diff --git a/frontend/src/__tests__/kitCost.test.js b/frontend/src/__tests__/kitCost.test.js index fcf91c3..f04acfe 100644 --- a/frontend/src/__tests__/kitCost.test.js +++ b/frontend/src/__tests__/kitCost.test.js @@ -251,3 +251,80 @@ describe('Recipe Matching', () => { expect(canMakeRecipe(kit, recipe)).toBe(false) }) }) + +// --------------------------------------------------------------------------- +// Accessory Values — PR32 +// --------------------------------------------------------------------------- +describe('Accessory Values', () => { + it('芳香调理 has no accessories', () => { + const kit = KITS.find(k => k.id === 'aroma') + expect(kit.accessoryValue).toBeUndefined() + }) + + it('家庭医生 accessories = 475 (香薰机375 + 木盒100)', () => { + const kit = KITS.find(k => k.id === 'family') + expect(kit.accessoryValue).toBe(475) + }) + + it('居家呵护 accessories = 585 (香薰机375 + 竹木架210)', () => { + const kit = KITS.find(k => k.id === 'home3988') + expect(kit.accessoryValue).toBe(585) + }) + + it('全精油 accessories = 795 (香薰机375 + 竹木架210×2)', () => { + const kit = KITS.find(k => k.id === 'full') + expect(kit.accessoryValue).toBe(795) + }) +}) + +// --------------------------------------------------------------------------- +// Discount Rate Calculation — PR32 +// --------------------------------------------------------------------------- +describe('Discount Rate', () => { + function calcDiscountRate(kit) { + const resolved = kit.oils.map(resolveOilName) + const bc = kit.bottleCount || {} + let totalBP = 0 + for (const name of resolved) { + const meta = oils[name] + totalBP += meta ? meta.bottlePrice * (bc[name] || 1) : 0 + } + const totalValue = totalBP + (kit.accessoryValue || 0) + return totalValue > 0 ? Math.min(kit.price / totalValue, 1) : 1 + } + + it('all kits have discount rate < 1 (套装比单买便宜)', () => { + for (const kit of KITS) { + const rate = calcDiscountRate(kit) + expect(rate).toBeLessThan(1) + expect(rate).toBeGreaterThan(0) + } + }) + + it('全精油 discount rate ≈ 0.69', () => { + const kit = KITS.find(k => k.id === 'full') + const rate = calcDiscountRate(kit) + expect(rate).toBeGreaterThan(0.65) + expect(rate).toBeLessThan(0.75) + }) + + it('larger kits have better discounts', () => { + const rates = KITS.map(k => ({ id: k.id, rate: calcDiscountRate(k), count: k.oils.length })) + rates.sort((a, b) => a.count - b.count) + // Generally larger kits should have lower discount rate (better deal) + // At minimum, the largest kit should have a lower rate than the smallest + expect(rates[rates.length - 1].rate).toBeLessThanOrEqual(rates[0].rate) + }) + + it('kit cost per recipe should be less than original cost', () => { + for (const kit of KITS) { + const perDrop = calcKitPerDrop(kit) + const recipes = prodData.recipes.filter(r => canMakeRecipe(kit, r)) + for (const r of recipes.slice(0, 5)) { + const kitCost = calcRecipeCostWithKit(perDrop, r) + const originalCost = calcOriginalCost(r) + expect(kitCost).toBeLessThanOrEqual(originalCost + 0.01) + } + } + }) +})