64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
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<Record<string, string | number>> = 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 }
|
|
)
|
|
})
|
|
})
|