describe('Recipe Cost Parity Test', () => { // Verify recipe cost formula: cost = sum(bottle_price / drop_count * drops) let oilsMap = {} let testRecipes = [] before(() => { cy.request('/api/oils').then(res => { res.body.forEach(oil => { oilsMap[oil.name] = { bottle_price: oil.bottle_price, drop_count: oil.drop_count, ppd: oil.drop_count ? oil.bottle_price / oil.drop_count : 0, retail_price: oil.retail_price } }) }) cy.request('/api/recipes').then(res => { testRecipes = res.body.slice(0, 20) }) }) it('oil data has correct structure (137+ oils)', () => { expect(Object.keys(oilsMap).length).to.be.gte(100) const lav = oilsMap['薰衣草'] expect(lav).to.exist expect(lav.bottle_price).to.be.gt(0) expect(lav.drop_count).to.be.gt(0) }) it('price-per-drop matches formula for common oils', () => { const checks = ['薰衣草', '乳香', '茶树', '柠檬', '椒样薄荷'] checks.forEach(name => { const oil = oilsMap[name] if (oil) { const expected = oil.bottle_price / oil.drop_count expect(oil.ppd).to.be.closeTo(expected, 0.0001) } }) }) it('calculates cost for each of first 20 recipes', () => { testRecipes.forEach(recipe => { let cost = 0 recipe.ingredients.forEach(ing => { const oil = oilsMap[ing.oil_name] if (oil) cost += oil.ppd * ing.drops }) expect(cost).to.be.gte(0) }) }) it('retail price >= wholesale for oils that have it', () => { Object.entries(oilsMap).forEach(([name, oil]) => { if (oil.retail_price && oil.retail_price > 0) { expect(oil.retail_price).to.be.gte(oil.bottle_price) } }) }) it('no recipe has all-zero cost', () => { let zeroCostCount = 0 testRecipes.forEach(recipe => { let cost = 0 recipe.ingredients.forEach(ing => { const oil = oilsMap[ing.oil_name] if (oil) cost += oil.ppd * ing.drops }) if (cost === 0) zeroCostCount++ }) expect(zeroCostCount).to.be.lt(testRecipes.length) }) it('cost formula is consistent: two calculation methods agree', () => { testRecipes.forEach(recipe => { const costs = recipe.ingredients.map(ing => { const oil = oilsMap[ing.oil_name] return oil ? oil.ppd * ing.drops : 0 }) const fromMap = costs.reduce((a, b) => a + b, 0) const fromReduce = recipe.ingredients.reduce((s, ing) => { const oil = oilsMap[ing.oil_name] return s + (oil ? oil.ppd * ing.drops : 0) }, 0) expect(fromMap).to.be.closeTo(fromReduce, 0.001) }) }) })