import { describe, it, expect } from "vitest"; import { computeAlertWindow } from "../services/invoice-alerts"; import { utcMidnightOfLocalDay } from "../utils/date"; // Pure tests — no DB rows are touched. due_date is @db.Date: Prisma truncates // a Date used in a WHERE filter to its UTC date part, so the alert window // boundaries must be UTC midnights of the LOCAL calendar day. The old code // used local midnights (= 22:00/23:00 UTC of the PREVIOUS day), which shifted // the [today, today+3] due_date window a day early — the "splatnost za 3 dny" // advance alert (due == today+3) could then never fire. All assertions below // are timezone-independent (inputs are built from local components). describe("utcMidnightOfLocalDay", () => { it("preserves the LOCAL calendar day shortly after local midnight (the night window)", () => { // 00:30 local on 2098-07-01 — in Prague (UTC+2) this instant is // 2098-06-30T22:30Z, i.e. its UTC date part is the PREVIOUS day, which is // exactly what Prisma would truncate a bare Date to. const justAfterMidnight = new Date(2098, 6, 1, 0, 30, 0); expect(utcMidnightOfLocalDay(justAfterMidnight).getTime()).toBe( Date.UTC(2098, 6, 1), ); }); it("returns UTC midnight of the local day for a mid-day instant", () => { const noon = new Date(2098, 0, 15, 12, 0, 0); expect(utcMidnightOfLocalDay(noon).getTime()).toBe(Date.UTC(2098, 0, 15)); }); }); describe("computeAlertWindow", () => { it("builds [today, today+3] as UTC midnights of the LOCAL day, with matching strings", () => { // 00:30 local — the window where the old local-midnight computation // produced yesterday's UTC date and the whole window slid a day early. const now = new Date(2098, 6, 1, 0, 30, 0); const w = computeAlertWindow(now); expect(w.today.getTime()).toBe(Date.UTC(2098, 6, 1)); expect(w.in3days.getTime()).toBe(Date.UTC(2098, 6, 4)); expect(w.todayStr).toBe("2098-07-01"); expect(w.in3daysStr).toBe("2098-07-04"); }); it("rolls the +3-day bound over a month boundary", () => { const now = new Date(2098, 0, 30, 8, 0, 0); // 2098-01-30 const w = computeAlertWindow(now); expect(w.in3days.getTime()).toBe(Date.UTC(2098, 1, 2)); // 2098-02-02 expect(w.in3daysStr).toBe("2098-02-02"); }); });