/** * Which failures explain themselves, and which ask to be reported. */ import { test } from "node:test"; import assert from "node:assert/strict"; import { CONTACT, NO_CARD, NO_GREETING, SuckerError, TOO_FEW_MESSAGES, TRUNCATED_CARD, isSuckerError, userFacingMessage, } from "../src/lib/errors.ts"; test("an explained failure is shown to the user as written", () => { for (const e of [NO_CARD, NO_GREETING, TOO_FEW_MESSAGES, TRUNCATED_CARD]) { assert.equal(userFacingMessage(e), e.message); assert.ok(!/dingus/i.test(e.message), "nobody is called a dingus for these"); } }); test("an unexplained failure owns it and asks to be reported", () => { const msg = userFacingMessage(new TypeError("Cannot read properties of undefined")); assert.match(msg, /not your fault/i); assert.ok(msg.includes(CONTACT), "must say where to report it"); // The internals must not leak into a user-facing reply. assert.ok(!msg.includes("Cannot read properties")); }); test("the truncated-card case keeps the context-size advice", () => { assert.match(TRUNCATED_CARD.message, /Context Size/); assert.ok(TRUNCATED_CARD.message.includes(CONTACT)); }); test("recognised across module boundaries, not just by identity", () => { assert.ok(isSuckerError(new SuckerError("x"))); // What a second copy of the class in another bundle would look like. assert.ok(isSuckerError({ name: "SuckerError", message: "x" })); assert.ok(!isSuckerError(new Error("x"))); assert.ok(!isSuckerError(null)); assert.ok(!isSuckerError("SuckerError")); });