import { describe, it, expect } from "vitest"; import { diffDays, parseCart, serializeCart, EMPTY_CART } from "@/lib/rental-cart"; describe("diffDays", () => { it("renvoie 0 pour mêmes dates", () => { expect(diffDays("2026-06-01", "2026-06-01")).toBe(0); }); it("compte 1 nuit entre J et J+1", () => { expect(diffDays("2026-06-01", "2026-06-02")).toBe(1); }); it("compte 7 jours sur une semaine", () => { expect(diffDays("2026-06-01", "2026-06-08")).toBe(7); }); it("ne renvoie pas de valeur négative si end < start", () => { expect(diffDays("2026-06-08", "2026-06-01")).toBe(0); }); }); describe("parseCart", () => { it("retourne EMPTY_CART pour null/undefined/garbage", () => { expect(parseCart(null)).toEqual(EMPTY_CART); expect(parseCart(undefined)).toEqual(EMPTY_CART); expect(parseCart("")).toEqual(EMPTY_CART); expect(parseCart("not json")).toEqual(EMPTY_CART); }); it("retourne EMPTY_CART quand le schéma est invalide", () => { expect(parseCart(JSON.stringify({ v: 99, items: [] }))).toEqual(EMPTY_CART); expect(parseCart(JSON.stringify({ v: 1, items: "nope" }))).toEqual(EMPTY_CART); }); it("accepte un panier valide", () => { const valid = { v: 1, items: [ { itemId: "abc", qty: 2, startDate: "2026-06-01", endDate: "2026-06-03" }, ], }; const out = parseCart(JSON.stringify(valid)); expect(out.items).toHaveLength(1); expect(out.items[0].qty).toBe(2); }); it("rejette une date au mauvais format", () => { const bad = { v: 1, items: [{ itemId: "abc", qty: 1, startDate: "1/6/2026", endDate: "2026-06-03" }], }; expect(parseCart(JSON.stringify(bad))).toEqual(EMPTY_CART); }); }); describe("serializeCart", () => { it("ajoute un updatedAt ISO", () => { const s = serializeCart({ v: 1, items: [] }); const parsed = JSON.parse(s); expect(parsed.v).toBe(1); expect(parsed.updatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/); }); it("roundtrip parse(serialize(x)) === x sur les items", () => { const cart = { v: 1 as const, items: [ { itemId: "k1", qty: 3, startDate: "2026-07-01", endDate: "2026-07-08" }, ], }; const round = parseCart(serializeCart(cart)); expect(round.items).toEqual(cart.items); }); }); // Calcul de commission (snapshot de la logique métier dans l'API checkout). // Ce test sert de garde-fou : si la formule change, faire évoluer aussi // `/api/rentals/checkout` (cf. commissionAmount = itemsTotal * pct / 100). describe("rental commission formula", () => { function commission(itemsTotal: number, pct: number): number { return Math.round((itemsTotal * pct) / 100 * 100) / 100; } it("0% commission System D", () => { expect(commission(120, 0)).toBe(0); }); it("15% sur 200€ = 30€", () => { expect(commission(200, 15)).toBe(30); }); it("arrondit au centime", () => { expect(commission(33.33, 15)).toBe(5); }); }); // Disponibilité : la quantité libre = totalQty - somme des qty bloquées // chevauchant la fenêtre. Snapshot de la logique de `getItemAvailability`. describe("rental availability arithmetic", () => { function availableQty(totalQty: number, blockedQtys: number[]): number { const used = blockedQtys.reduce((a, b) => a + b, 0); return Math.max(0, totalQty - used); } it("totalQty quand rien n'est bloqué", () => { expect(availableQty(5, [])).toBe(5); }); it("soustrait les blocages", () => { expect(availableQty(5, [2, 1])).toBe(2); }); it("ne renvoie jamais de valeur négative", () => { expect(availableQty(3, [5])).toBe(0); }); });