import { describe, expect, spyOn, test } from "bun:test"; import { notFound, redirect } from "@tanstack/react-router"; import { clientSafeError } from "./client-safe-error"; import { HttpError } from "./errors"; describe("clientSafeError (DEPLOY-P8)", () => { test("unknown errors become a generic 500 and are logged", () => { const log = spyOn(console, "error").mockImplementation(() => undefined); const masked = clientSafeError(new Error("D1_ERROR: no such column secret_hash")); expect(masked).toBeInstanceOf(HttpError); expect(masked instanceof HttpError && masked.status).toBe(500); expect(masked instanceof HttpError && masked.message).toBe("Internal error"); expect(log).toHaveBeenCalled(); log.mockRestore(); }); test("5xx HttpErrors lose their internal message", () => { const log = spyOn(console, "error").mockImplementation(() => undefined); const result = clientSafeError(new HttpError(500, "D1 table x missing")); expect(result instanceof HttpError && [result.status, result.message]).toEqual([500, "Internal error"]); log.mockRestore(); }); test("deliberate errors and router control flow pass through", () => { const http = new HttpError(403, "Nope"); expect(clientSafeError(http)).toBe(http); const nf = notFound(); expect(clientSafeError(nf)).toBe(nf); const to = redirect({ to: "/" }); expect(clientSafeError(to)).toBe(to); }); test("validation failures become a 400 with the first issue's message", () => { const error = Object.assign(new Error("[...]"), { name: "ZodError", issues: [{ message: "Too long" }] }); const result = clientSafeError(error); expect(result instanceof HttpError && [result.status, result.message]).toEqual([400, "Too long"]); }); test("validator failures serialized into a plain Error message also become a 400", () => { const issues = [{ code: "invalid_value", path: ["acceptTerms"], message: "Accept the Terms to continue" }]; const result = clientSafeError(new Error(JSON.stringify(issues))); expect(result instanceof HttpError && [result.status, result.message]).toEqual([ 400, "Accept the Terms to continue", ]); }); test("a plain Error that merely looks like JSON stays a masked 500", () => { const spy = spyOn(console, "error").mockImplementation(() => undefined); try { const result = clientSafeError(new Error('[{"secret":"x"}]')); expect(result instanceof HttpError && [result.status, result.message]).toEqual([500, "Internal error"]); } finally { spy.mockRestore(); } }); });