import { describe, expect, it } from "vitest" ;
import {
collectErrorGraphCandidates,
detectErrorKind,
extractErrorCode,
formatErrorMessage,
formatUncaughtError,
hasErrnoCode,
isErrno,
readErrorName,
} from "./errors.js" ;
function createCircularObject() {
const circular: { self?: unknown } = {};
circular.self = circular;
return circular;
}
describe("error helpers" , () => {
it.each([
{ value: { code: "EADDRINUSE" }, expected: "EADDRINUSE" },
{ value: { code: 429 }, expected: "429" },
{ value: { code: false }, expected: undefined },
{ value: "boom" , expected: undefined },
])("extracts error codes from %j" , ({ value, expected }) => {
expect(extractErrorCode(value)).toBe(expected);
});
it.each([
{ value: { name: "AbortError" }, expected: "AbortError" },
{ value: { name: 42 }, expected: "" },
{ value: null , expected: "" },
])("reads error names from %j" , ({ value, expected }) => {
expect(readErrorName(value)).toBe(expected);
});
it("walks nested error graphs once in breadth-first order" , () => {
const leaf = { name: "leaf" };
const child = { name: "child" } as {
name: string;
cause?: unknown;
errors?: unknown[];
};
const root = { name: "root" , cause: child, errors: [leaf, child] };
child.cause = root;
expect(
collectErrorGraphCandidates(root, (current) => [
current.cause,
...((current as { errors?: unknown[] }).errors ?? []),
]),
).toEqual([root, child, leaf]);
expect(collectErrorGraphCandidates(null )).toEqual([]);
});
it("matches errno-shaped errors by code" , () => {
const err = Object.assign(new Error("busy" ), { code: "EADDRINUSE" });
expect(isErrno(err)).toBe(true );
expect(hasErrnoCode(err, "EADDRINUSE" )).toBe(true );
expect(hasErrnoCode(err, "ENOENT" )).toBe(false );
expect(isErrno("busy" )).toBe(false );
});
it.each([
{ value: 123 n, expected: "123" },
{ value: false , expected: "false" },
{ value: createCircularObject(), expected: "[object Object]" },
])("formats error messages for case %#" , ({ value, expected }) => {
expect(formatErrorMessage(value)).toBe(expected);
});
it("traverses .cause chain to include nested error messages" , () => {
const rootCause = new Error("ECONNRESET" );
const httpError = Object.assign(new Error("Network request for 'sendMessage' failed!" ), {
cause: rootCause,
});
const formatted = formatErrorMessage(httpError);
expect(formatted).toContain("Network request for 'sendMessage' failed!" );
expect(formatted).toContain("ECONNRESET" );
});
it("handles circular .cause references without infinite loop" , () => {
const a: Error & { cause?: unknown } = new Error("error A" );
const b: Error & { cause?: unknown } = new Error("error B" );
a.cause = b;
b.cause = a;
const formatted = formatErrorMessage(a);
expect(formatted).toBe("error A | error B" );
});
it("redacts sensitive tokens from formatted error messages" , () => {
const token = "sk-abcdefghijklmnopqrstuv" ;
const formatted = formatErrorMessage(new Error(`Authorization: Bearer ${token}`));
expect(formatted).toContain("Authorization: Bearer" );
expect(formatted).not.toContain(token);
});
it.each([
{
value: new Error("Unhandled stop reason: refusal_policy" ),
expected: "refusal" ,
},
{
value: Object.assign(new Error("request timed out" ), { code: "ETIMEDOUT" }),
expected: "timeout" ,
},
{
value: Object.assign(new Error("Too many requests" ), { code: 429 }),
expected: "rate_limit" ,
},
{
value: new Error("context_window exceeded with too many tokens" ),
expected: "context_length" ,
},
{
value: new Error("plain provider failure" ),
expected: undefined,
},
{
value: undefined,
expected: undefined,
},
] as const )("detects error kind for case %#" , ({ value, expected }) => {
expect(detectErrorKind(value)).toBe(expected);
});
it("uses message-only formatting for INVALID_CONFIG and stack formatting otherwise" , () => {
const invalidConfig = Object.assign(new Error("TOKEN=sk-abcdefghijklmnopqrstuv" ), {
code: "INVALID_CONFIG" ,
stack: "Error: TOKEN=sk-abcdefghijklmnopqrstuv\n at ignored" ,
});
expect(formatUncaughtError(invalidConfig)).not.toContain("at ignored" );
const uncaught = new Error("boom" );
uncaught.stack = "Error: Authorization: Bearer sk-abcdefghijklmnopqrstuv\n at runTask" ;
const formatted = formatUncaughtError(uncaught);
expect(formatted).toContain("at runTask" );
expect(formatted).not.toContain("sk-abcdefghijklmnopqrstuv" );
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland