/** * Reference pricing suite — proves the slim harness boots and exercises FareEngineService directly. * Also confirms two matrix findings against the running engine: * D1 — a promo with percentOff > 100 drives the total NEGATIVE (no clamp at 0). * C1 — a missing USD→ETB FX rate is silently substituted with 1.0 (fares collapse ~100x). */ import { FareEngineService } from "../src/modules/fare-engine/fare-engine.service"; import { createServiceHarness, ServiceHarness, } from "./setup/slim-app"; import { IDS, resetAndSeedCore, DISTANCE, USD_TO_ETB, } from "./fixtures/seed-core"; describe("Pricing — FareEngineService (slim harness)", () => { let harness: ServiceHarness; let fareEngine: FareEngineService; beforeAll(async () => { harness = await createServiceHarness(); fareEngine = harness.moduleRef.get(FareEngineService); }); afterAll(async () => { await harness?.close(); }); beforeEach(async () => { await resetAndSeedCore(harness.prisma); }); // nationality 'Ethiopian' → LOCAL seat class (3.00 ETB/km) and ETB billing (rate 1). const baseDto = () => ({ routeId: IDS.route, originStationId: IDS.stationA, destinationStationId: IDS.stationB, seatClassId: IDS.seatClassLocal, nationality: "Ethiopian", adultCount: 1, childCount: 0, }); it("boots and computes a positive baseline fare (A→B, local, 1 adult)", async () => { const result = await fareEngine.calculate(baseDto() as any); // 100km × 3.00 ETB/km × 1 × USD_TO_ETB(100) = 30000 minor (see seat-class formula). expect(result.totalMinor).toBeGreaterThan(0); expect(result.totalMinor).toBe(DISTANCE.B * 3 * USD_TO_ETB); }); it("D1 🔴 promo percentOff=150 produces a NEGATIVE total (no floor at 0)", async () => { await harness.prisma.promotion.create({ data: { title: "Overshoot", code: "OVER150", percentOff: 150, validUntil: new Date(Date.now() + 86_400_000), active: true, }, }); const result = await fareEngine.calculate({ ...baseDto(), promoCode: "OVER150", } as any); // Expected (correct) behavior: total clamped at >= 0. Actual: negative. expect(result.totalMinor).toBeLessThan(0); }); it("D2 🔴 fixed amountOffMinor larger than subtotal drives total NEGATIVE", async () => { const base = await fareEngine.calculate(baseDto() as any); // 30000 minor await harness.prisma.promotion.create({ data: { title: "Huge fixed", code: "FIXEDBIG", amountOffMinor: base.totalMinor + 10_000, validUntil: new Date(Date.now() + 86_400_000), active: true, }, }); const result = await fareEngine.calculate({ ...baseDto(), promoCode: "FIXEDBIG", } as any); expect(result.totalMinor).toBeLessThan(0); }); it("D4 🔴 promo with percentOff=0 is treated as FIXED (0 is falsy) and applies amountOffMinor", async () => { // A promo intended as '0% off' but also carrying a stray fixed amount: the falsy check // `promo.percentOff ? percent : amountOffMinor` wrongly applies the fixed discount. await harness.prisma.promotion.create({ data: { title: "Zero percent", code: "ZERO0", percentOff: 0, amountOffMinor: 5000, validUntil: new Date(Date.now() + 86_400_000), active: true, }, }); const base = await fareEngine.calculate(baseDto() as any); const withPromo = await fareEngine.calculate({ ...baseDto(), promoCode: "ZERO0", } as any); // A true 0% promo should not change the price; here it deducts the fixed 5000. expect(withPromo.totalMinor).toBe(base.totalMinor - 5000); }); it("C1 🔴 missing USD→ETB rate silently falls back to 1.0 (fare collapses ~100x)", async () => { const withRate = await fareEngine.calculate(baseDto() as any); // Remove the USD→ETB rate the seat-class formula multiplies by. await harness.prisma.currencyExchangeRate.deleteMany({ where: { fromCurrency: "USD", toCurrency: "ETB" }, }); const withoutRate = await fareEngine.calculate(baseDto() as any); // Correct behavior would be to reject/flag; instead the fare silently drops by the rate factor. expect(withoutRate.totalMinor).toBe(withRate.totalMinor / USD_TO_ETB); expect(withoutRate.totalMinor).toBeLessThan(withRate.totalMinor); }); });