import { readFileSync } from "node:fs"; import { join } from "node:path"; import { SuaveplanError } from "@suaveplan/error"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { SRP_CODES, SrpError, srpError } from "./errors.js"; const SRC_ROOT = join(import.meta.dirname, ".."); describe("SRP_CODES", () => { it("contains exactly the 6 documented codes", { timeout: 5000 }, () => { const keys = Object.keys(SRP_CODES).sort(); expect(keys).toEqual([ "AUTH_FAILED", "GROUP_UNSUPPORTED", "HASH_UNSUPPORTED", "INVALID_PARAMS", "MUTUAL_AUTH_FAILED", "PUBLIC_KEY_INVALID", ]); for (const k of keys) { expect(SRP_CODES[k as keyof typeof SRP_CODES]).toBe(`SRP.${k}`); } }); it("every SRP code reachable from a production path", { timeout: 5000, }, () => { // Recursively read every .ts under src/ excluding test files, fixtures, // and *.types.ts. For each registered code, assert at least one // production file mentions the code literal. const sources = readSrcSources(SRC_ROOT); for (const code of Object.values(SRP_CODES)) { const reason = code.split(".")[1]; const found = sources.some(({ body }) => body.includes(reason!)); expect(found).toBe(true); } }); }); describe("SrpError", () => { it("instances carry the SRP.* code literal", { timeout: 5000 }, () => { const err = srpError(SRP_CODES.AUTH_FAILED, "test"); expect(err).toBeInstanceOf(SrpError); expect(err).toBeInstanceOf(SuaveplanError); expect(err.code).toBe("SRP.AUTH_FAILED"); expect(err.message).toContain("SRP.AUTH_FAILED"); }); it("srpError threads through Error cause", { timeout: 5000 }, () => { const cause = new Error("inner"); const err = srpError(SRP_CODES.INVALID_PARAMS, "outer", { cause }); expect((err as unknown as { cause: Error }).cause).toBe(cause); }); it("SrpError constructor uses default empty options", { timeout: 5000, }, () => { const err = new SrpError(SRP_CODES.INVALID_PARAMS, "no opts"); expect(err.code).toBe("SRP.INVALID_PARAMS"); }); it("srpError called without options argument", { timeout: 5000 }, () => { const err = srpError(SRP_CODES.AUTH_FAILED, "no opts"); expect(err.code).toBe("SRP.AUTH_FAILED"); }); it("srpError ignores non-Error cause silently", { timeout: 5000 }, () => { const err = srpError(SRP_CODES.INVALID_PARAMS, "outer", { cause: "string", }); expect(err.code).toBe("SRP.INVALID_PARAMS"); }); }); type SrcSource = { path: string; body: string }; function readSrcSources(root: string): SrcSource[] { const out: SrcSource[] = []; const fs = require("node:fs") as typeof import("node:fs"); function walk(dir: string): void { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name); if (entry.isDirectory()) { if ( entry.name === "__fixtures__" || entry.name === "__benchmarks__" ) { continue; } walk(full); } else if ( entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".types.ts") && !entry.name.endsWith(".bench.ts") ) { out.push({ path: full, body: readFileSync(full, "utf8") }); } } } walk(root); return out; }