import { types } from "pg"; import { beforeAll, describe, expect, it, vi } from "vitest"; // Warum es diesen Test gibt. // // lib/supabase/types.ts sagt für 16 Spalten `string` und für zwei `number`. // Der `pg`-Treiber liefert von Haus aus das Gegenteil: aus `date` wird ein // Date-Objekt, aus `numeric` eine Zeichenkette. // // Diese Abweichung ist für Typprüfer und Tests unsichtbar — die // Deklarationen beschreiben, was der Code glaubt, nicht was ankommt. tsc war // sauber, 187 Tests waren grün, und die Anwendung stürzte auf der ersten // Seite ab („a.date.localeCompare is not a function"). Der Absturz war noch // der freundliche Fall; ein Vergleich zwischen Date und Zeichenkette wirft // nicht, er liefert bloss das falsche Ergebnis. // // Geprüft wird deshalb die einzige Stelle, an der beides zusammenkommt: die // Parser, die lib/db/pool.ts beim Import registriert. vi.mock("server-only", () => ({})); beforeAll(async () => { // Der Import allein registriert die Parser — deshalb reicht er als // Vorbereitung, und deshalb würde ein Entfernen der Zeilen hier auffallen. await import("@/lib/db/pool"); }); const parse = (oid: number, raw: string) => types.getTypeParser(oid)(raw); describe("Typumwandlung des Treibers", () => { it("gibt `date` unverändert als Kalendertag zurück", () => { // Nicht `new Date(...)`: ein Kalendertag hat keine Zeitzone. Als // Date-Objekt bekäme er Mitternacht in der Zone des Servers, und ein // Geburtsdatum verschöbe sich beim Formatieren um einen Tag — in // Österreich immer, weil MEZ östlich von UTC liegt. expect(parse(1082, "1968-08-15")).toBe("1968-08-15"); }); it("gibt `numeric` als Zahl zurück", () => { // Die FTE-Kachel rechnet damit. Als Zeichenkette ergäbe die Summe // "744.4" + "0.5" = "744.40.5" statt 744.9 — ohne jede Fehlermeldung. expect(parse(1700, "744.4")).toBe(744.4); expect(parse(1700, "38.5")).toBe(38.5); }); it("gibt `timestamptz` als ISO-8601 mit Z zurück", () => { const v = parse(1184, "2026-08-03 07:55:46.659+00"); expect(v).toBe("2026-08-03T07:55:46.659Z"); // Muss für Date verwertbar bleiben — die Protokollansicht formatiert // damit. Die Rohform des Treibers („… +00") ist kein Format, das der // Standard kennt. expect(Number.isNaN(new Date(v as string).getTime())).toBe(false); }); it("lässt `int8` als Zeichenkette", () => { // Kommt nur aus count() und wird überall mit Number() gelesen. Als Zahl // geparst verlöre es jenseits von 2^53 stillschweigend an Genauigkeit. expect(parse(20, "852")).toBe("852"); }); it("sortiert ISO-Zeitstempel als Text chronologisch", () => { // Darauf beruht jedes orderBy im Anwendungscode, das auf Zeichenketten // arbeitet. Gilt nur, weil das Format feste Breite hat und in UTC steht. const frueher = parse(1184, "2026-08-03 07:55:46.659+00") as string; const spaeter = parse(1184, "2026-08-03 09:12:01.000+00") as string; expect(frueher.localeCompare(spaeter)).toBeLessThan(0); }); });