import { describe, it, expect } from 'vitest' import * as fc from 'fast-check' import { parseCSV, generateCSV } from '../src/controllers/specDataIO' // CSV column headers matching spec_data table fields const CSV_HEADERS = [ 'model_name', 'fineness', 'main_stone', 'ring_size', 'gold_total_weight', 'gold_net_weight', 'loss', 'gold_loss', 'gold_price', 'gold_value', 'main_stone_count', 'main_stone_weight', 'main_stone_unit_price', 'main_stone_amount', 'side_stone_count', 'side_stone_weight', 'side_stone_unit_price', 'side_stone_amount', 'accessory_amount', 'processing_fee', 'setting_fee', 'total_labor_cost', 'total_price', ] const STRING_FIELDS = ['model_name', 'fineness', 'main_stone', 'ring_size'] // Generate a safe string that won't break CSV parsing (no newlines) const safeStringArb = fc.stringOf( fc.char().filter((c) => c !== '\n' && c !== '\r'), { minLength: 0, maxLength: 20 } ) // Generate a valid spec data row const specDataRowArb: fc.Arbitrary> = fc.record( Object.fromEntries( CSV_HEADERS.map((h) => [ h, STRING_FIELDS.includes(h) ? safeStringArb : fc.float({ min: 0, max: 99999, noNaN: true }).map((n) => Math.round(n * 100) / 100), ]) ) ) // Feature: jewelry-mall, Property 6: 规格数据导入导出 Round-Trip // **Validates: Requirements 8.3** describe('Property 6: 规格数据导入导出 Round-Trip', () => { it('导出后再导入应得到等价数据', () => { fc.assert( fc.property( fc.array(specDataRowArb, { minLength: 1, maxLength: 10 }), (rows) => { const csv = generateCSV(rows) const parsed = parseCSV(csv) expect(parsed.length).toBe(rows.length) for (let i = 0; i < rows.length; i++) { for (const h of CSV_HEADERS) { if (STRING_FIELDS.includes(h)) { // CSV round-trip trims whitespace, which is expected behavior expect(parsed[i][h]).toBe(String(rows[i][h]).trim()) } else { expect(Number(parsed[i][h])).toBeCloseTo(Number(rows[i][h]), 2) } } } } ), { numRuns: 100 } ) }) })