import { readFileSync } from "node:fs"; import { join } from "node:path"; import { SuaveplanError } from "@suaveplan/error"; import { describe, expect, expectTypeOf, it } from "@suaveplan/testing/runner"; import { TPM_STRUCT_CODES, TpmStructError, type TpmStructErrorCode, tpmStructError, } from "./error-codes.js"; const SRC_ROOT = join(import.meta.dirname, ".."); describe("TPM_STRUCT_CODES", () => { it("contains exactly the 10 documented codes", { timeout: 5000 }, () => { const keys = Object.keys(TPM_STRUCT_CODES).sort(); expect(keys).toEqual([ "ALG_UNSUPPORTED", "ATTEST_TYPE_UNSUPPORTED", "CURVE_UNSUPPORTED", "MAGIC_INVALID", "MALFORMED", "NAME_ALG_NULL", "OBJECT_ATTRIBUTES_RESERVED_NONZERO", "PARAMETERS_INVALID", "TRUNCATED", "TYPE_MISMATCH", ]); for (const k of keys) { expect(TPM_STRUCT_CODES[k as keyof typeof TPM_STRUCT_CODES]).toBe( `TPM_STRUCT.${k}` ); } }); it("every TPM_STRUCT code reachable from a production path", { timeout: 5000, }, () => { const sources = readSrcSources(SRC_ROOT); for (const code of Object.values(TPM_STRUCT_CODES)) { const reason = (code as string).split(".")[1] as string; const found = sources.some(({ body }) => body.includes(reason)); expect(found, `code ${code} not referenced in src/`).toBe(true); } }); it("literal union pins to registry keys", { timeout: 5000 }, () => { expectTypeOf().toEqualTypeOf< | "TPM_STRUCT.MAGIC_INVALID" | "TPM_STRUCT.TRUNCATED" | "TPM_STRUCT.ALG_UNSUPPORTED" | "TPM_STRUCT.CURVE_UNSUPPORTED" | "TPM_STRUCT.TYPE_MISMATCH" | "TPM_STRUCT.MALFORMED" | "TPM_STRUCT.NAME_ALG_NULL" | "TPM_STRUCT.ATTEST_TYPE_UNSUPPORTED" | "TPM_STRUCT.PARAMETERS_INVALID" | "TPM_STRUCT.OBJECT_ATTRIBUTES_RESERVED_NONZERO" >(); }); }); describe("TpmStructError", () => { it("instances carry the TPM_STRUCT.* code literal", { timeout: 5000, }, () => { const err = tpmStructError(TPM_STRUCT_CODES.MAGIC_INVALID, "test"); expect(err).toBeInstanceOf(TpmStructError); expect(err).toBeInstanceOf(SuaveplanError); expect(err.code).toBe("TPM_STRUCT.MAGIC_INVALID"); expect(err.message).toContain("TPM_STRUCT.MAGIC_INVALID"); }); it("threads structured detail through the cause chain", { timeout: 5000, }, () => { const err = tpmStructError(TPM_STRUCT_CODES.TRUNCATED, "short", { cursor: 5, requested: 4, }); const cause = ( err as unknown as { cause: Error & { detail?: Record }; } ).cause; expect(cause).toBeInstanceOf(Error); expect(cause?.detail).toEqual({ cursor: 5, requested: 4 }); }); it("constructor without options has no cause", { timeout: 5000 }, () => { const err = new TpmStructError(TPM_STRUCT_CODES.MALFORMED, "no opts"); expect(err.code).toBe("TPM_STRUCT.MALFORMED"); expect((err as unknown as { cause?: unknown }).cause).toBeUndefined(); }); it("constructor accepts an Error cause directly", { timeout: 5000, }, () => { const inner = new Error("inner"); const err = new TpmStructError(TPM_STRUCT_CODES.MALFORMED, "outer", { cause: inner, }); expect((err as unknown as { cause: Error }).cause).toBe(inner); }); it("constructor silently drops non-Error causes", { timeout: 5000, }, () => { const err = new TpmStructError(TPM_STRUCT_CODES.MALFORMED, "outer", { cause: "string-not-error", }); expect((err as unknown as { cause?: unknown }).cause).toBeUndefined(); }); it("tpmStructError without detail has no cause", { timeout: 5000, }, () => { const err = tpmStructError(TPM_STRUCT_CODES.MALFORMED, "bare"); expect((err as unknown as { cause?: unknown }).cause).toBeUndefined(); }); }); type SrcSource = { path: string; body: string }; function readSrcSources(root: string): SrcSource[] { const out: SrcSource[] = []; // biome lint hint: this is a test helper; readdirSync is the simplest walker 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__" || entry.name === "__integration__" ) { 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; }