import { describe, it, expect } from 'vitest' import * as fc from 'fast-check' import { calculateRing, validateInput } from './calculator' import type { RingCalculatorInput } from '../types/calculator' // 生成有效的非负数输入 const validInputArb: fc.Arbitrary = fc.record({ goldWeight: fc.float({ min: 0, max: 1000, noNaN: true }), mainStoneWeight: fc.float({ min: 0, max: 100, noNaN: true }), sideStoneWeight: fc.float({ min: 0, max: 100, noNaN: true }), lossRate: fc.float({ min: 0, max: 10, noNaN: true }), moldGoldPrice: fc.float({ min: 0, max: 10000, noNaN: true }), mainStoneUnitPrice: fc.float({ min: 0, max: 100000, noNaN: true }), sideStoneUnitPrice: fc.float({ min: 0, max: 100000, noNaN: true }), sideStoneCount: fc.integer({ min: 0, max: 1000 }), microSettingFee: fc.float({ min: 0, max: 1000, noNaN: true }), mainStoneSettingFee: fc.float({ min: 0, max: 100000, noNaN: true }), threeDFee: fc.float({ min: 0, max: 100000, noNaN: true }), basicLaborCost: fc.float({ min: 0, max: 100000, noNaN: true }), otherCost: fc.float({ min: 0, max: 100000, noNaN: true }), }) // Feature: jewelry-mall, Property 4: 钻戒计算器公式正确性 // **Validates: Requirements 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8** describe('Property 4: 钻戒计算器公式正确性', () => { it('所有输出字段应符合设计文档公式', () => { fc.assert( fc.property(validInputArb, (input) => { const result = calculateRing(input) // 4.2 净金重 = 金重 - 主石重×0.2 - 副石重×0.2 const expectedNetGold = input.goldWeight - input.mainStoneWeight * 0.2 - input.sideStoneWeight * 0.2 expect(result.netGoldWeight).toBeCloseTo(expectedNetGold, 10) // 4.3 含耗重 = 净金重 × 损耗 const expectedWeightWithLoss = expectedNetGold * input.lossRate expect(result.weightWithLoss).toBeCloseTo(expectedWeightWithLoss, 10) // 4.4 金值 = 含耗重 × 倒模金价 const expectedGoldValue = expectedWeightWithLoss * input.moldGoldPrice expect(result.goldValue).toBeCloseTo(expectedGoldValue, 5) // 4.5 主石总价 = 主石重 × 主石单价 const expectedMainStone = input.mainStoneWeight * input.mainStoneUnitPrice expect(result.mainStoneTotal).toBeCloseTo(expectedMainStone, 10) // 4.6 副石总价 = 副石重 × 副石单价 const expectedSideStone = input.sideStoneWeight * input.sideStoneUnitPrice expect(result.sideStoneTotal).toBeCloseTo(expectedSideStone, 10) // 4.7 微镶总价 = 副石粒数 × 微镶费 const expectedMicroSetting = input.sideStoneCount * input.microSettingFee expect(result.microSettingTotal).toBeCloseTo(expectedMicroSetting, 10) // 4.8 总价 = 金值 + 主石总价 + 副石总价 + 主石镶费 + 微镶总价 + 3D起板费 + 基本工费 + 其他费用 const expectedTotal = expectedGoldValue + expectedMainStone + expectedSideStone + input.mainStoneSettingFee + expectedMicroSetting + input.threeDFee + input.basicLaborCost + input.otherCost expect(result.totalPrice).toBeCloseTo(expectedTotal, 5) }), { numRuns: 100 }, ) }) }) // 单元测试:边界情况和非法输入 // Requirements: 4.2-4.8 describe('calculateRing 单元测试', () => { const zeroInput: RingCalculatorInput = { goldWeight: 0, mainStoneWeight: 0, sideStoneWeight: 0, lossRate: 0, moldGoldPrice: 0, mainStoneUnitPrice: 0, sideStoneUnitPrice: 0, sideStoneCount: 0, microSettingFee: 0, mainStoneSettingFee: 0, threeDFee: 0, basicLaborCost: 0, otherCost: 0, } it('所有输入为 0 时,所有输出应为 0', () => { const result = calculateRing(zeroInput) expect(result.netGoldWeight).toBe(0) expect(result.weightWithLoss).toBe(0) expect(result.goldValue).toBe(0) expect(result.mainStoneTotal).toBe(0) expect(result.sideStoneTotal).toBe(0) expect(result.microSettingTotal).toBe(0) expect(result.totalPrice).toBe(0) }) it('仅金重有值时,净金重等于金重', () => { const input = { ...zeroInput, goldWeight: 10, lossRate: 1.05, moldGoldPrice: 500 } const result = calculateRing(input) expect(result.netGoldWeight).toBe(10) expect(result.weightWithLoss).toBeCloseTo(10.5, 10) expect(result.goldValue).toBeCloseTo(5250, 5) expect(result.totalPrice).toBeCloseTo(5250, 5) }) it('极大值输入不应抛出异常', () => { const input: RingCalculatorInput = { goldWeight: 999999, mainStoneWeight: 999999, sideStoneWeight: 999999, lossRate: 999999, moldGoldPrice: 999999, mainStoneUnitPrice: 999999, sideStoneUnitPrice: 999999, sideStoneCount: 999999, microSettingFee: 999999, mainStoneSettingFee: 999999, threeDFee: 999999, basicLaborCost: 999999, otherCost: 999999, } expect(() => calculateRing(input)).not.toThrow() }) it('负数金重应被拒绝', () => { expect(() => calculateRing({ ...zeroInput, goldWeight: -1 })).toThrow('金重不能为负数') }) it('负数主石重应被拒绝', () => { expect(() => calculateRing({ ...zeroInput, mainStoneWeight: -0.5 })).toThrow('主石重不能为负数') }) it('负数损耗应被拒绝', () => { expect(() => calculateRing({ ...zeroInput, lossRate: -1 })).toThrow('损耗不能为负数') }) it('负数其他费用应被拒绝', () => { expect(() => calculateRing({ ...zeroInput, otherCost: -100 })).toThrow('其他费用不能为负数') }) })