import { describe, it, expect } from "vitest"; import { readReceiptOutput } from "../src/tasks.js"; /** * Regression: kvitto-skanning kraschade på "svar matchar inte schema … invalid * value" när Gemini läste kvitto-enheter som "st", "kg", "förp". Enheten ska nu * normaliseras till en giltig kod eller null – aldrig fälla hela svaret. */ function receiptWith(unit: unknown) { return { storeName: "ICA", purchaseDate: "2026-08-19", lines: [ { rawText: "Rad", normalizedName: "Vara", canonicalIngredientId: null, quantity: 1, unit, unitPriceMinor: null, totalPriceMinor: 1500, isDiscount: false, confidence: 0.8, }, ], totalMinor: 1500, discountTotalMinor: null, confidence: 0.8, }; } describe("kvitto: robust enhetstolkning", () => { const cases: Array<[unknown, string | null]> = [ ["st", "COUNT"], ["styck", "COUNT"], ["kg", "KILOGRAM"], ["g", "GRAM"], ["GRAM", "GRAM"], ["dl", "DECILITER"], ["ml", "MILLILITER"], ["l", "LITER"], ["förp", "PACKAGE"], ["paket", "PACKAGE"], ["påse", "PACKAGE"], ["burk", "CAN"], ["kg.", "KILOGRAM"], // med skiljetecken [" St ", "COUNT"], // whitespace + versal ["xyz", null], // okänd → null (kraschar inte) [null, null], ]; for (const [input, expected] of cases) { it(`"${String(input)}" → ${expected}`, () => { const result = readReceiptOutput.safeParse(receiptWith(input)); expect(result.success).toBe(true); if (result.success) { expect(result.data.lines[0]!.unit).toBe(expected); } }); } it("ett okänt enhetsvärde fäller inte hela kvittot", () => { const result = readReceiptOutput.safeParse(receiptWith("2-pack")); expect(result.success).toBe(true); }); });