import { describe, expect, it } from "vitest"; import { ERROR_REGISTRY, createFailure, kindForStatus, normalizeUnknownFailure, } from "../../src/contracts/errors.js"; describe("frontend failure classification", () => { it("defines all 26 stable error kinds with the seven contract fields", () => { expect(Object.keys(ERROR_REGISTRY)).toHaveLength(31); for (const definition of Object.values(ERROR_REGISTRY)) { expect(definition).toEqual( expect.objectContaining({ kind: expect.any(String), defaultRetryable: expect.any(Boolean), severity: expect.any(String), userMessageKey: expect.any(String), action: expect.any(String), telemetryEvent: expect.any(String), redaction: expect.any(Array), }), ); } }); it.each([ [401, "AUTH_REQUIRED"], [403, "FORBIDDEN"], [404, "NOT_FOUND"], [409, "CONFLICT"], [422, "VALIDATION_REJECTED"], [418, "UNKNOWN_CLIENT_FAILURE"], [429, "RATE_LIMITED"], [503, "SERVER_FAILURE"], ])("maps HTTP %i to %s", (status, kind) => { expect(kindForStatus(status)).toBe(kind); }); it("projects only allowlisted safe fields", () => { const untrustedDetails = { code: "TEMPORARY", httpStatus: 503, requestId: "request-1", stack: "must not leak", body: "must not leak", authorization: "Bearer secret", }; const result = createFailure( "SERVER_FAILURE", "LIST_ENTITIES", 0, untrustedDetails, ); expect(result).toMatchObject({ kind: "SERVER_FAILURE", code: "TEMPORARY", httpStatus: 503, requestId: "request-1", }); expect(JSON.stringify(result)).not.toMatch(/stack|body|Bearer|secret/); }); it("normalizes any thrown value without leaking it", () => { const secret = { token: "sensitive", nested: { rawBody: "private" } }; const result = normalizeUnknownFailure(secret); expect(result).toMatchObject({ kind: "UNKNOWN_FAILURE", causeClass: "object", }); expect(JSON.stringify(result)).not.toMatch(/sensitive|private|token|rawBody/); }); });