/* @vitest-environment node */ import { describe, it, expect } from "vitest"; import { jsonError, withErrorHandling, badRequest, json } from "./errors.js"; it("json sets Cache-Control: no-store by default (RHL-006)", async () => { const res = json({ ok: true }, 200); expect(res.headers.get("Cache-Control")).toBe("no-store"); }); describe("lib/api/errors", () => { it("jsonError returns the standardized error shape without details", async () => { const res = jsonError(401, "AUTH_UNAUTHENTICATED", "Unauthorized"); expect(res.status).toBe(401); expect(await res.json()).toEqual({ error: { message: "Unauthorized", code: "AUTH_UNAUTHENTICATED" }, }); }); it("jsonError includes details when provided", async () => { const res = jsonError( 400, "VALIDATION_MISSING_PARAM", "Missing required route parameter(s)", { params: ["branch"] } ); expect(res.status).toBe(400); expect(await res.json()).toEqual({ error: { message: "Missing required route parameter(s)", code: "VALIDATION_MISSING_PARAM", details: { params: ["branch"] }, }, }); }); it("withErrorHandling converts ApiError into a standardized response", async () => { // The wrapped handler throws an expected error (ApiError). // The wrapper must convert it into { error: { message, code } } with status 400. const handler = withErrorHandling(async () => { throw badRequest("VALIDATION_TEST", "Bad Request"); }); const res = await handler(); expect(res.status).toBe(400); expect(await res.json()).toEqual({ error: { message: "Bad Request", code: "VALIDATION_TEST" }, }); }); it("withErrorHandling converts unknown errors into a safe 500 response", async () => { // Unknown errors must never leak internal messages/stacks. // We always return a generic 500 payload. const handler = withErrorHandling(async () => { throw new Error("boom"); }); const res = await handler(); expect(res.status).toBe(500); expect(await res.json()).toEqual({ error: { message: "Internal server error", code: "INTERNAL_SERVER_ERROR", }, }); }); });