import { test, expect } from "bun:test"; import { createContext } from "@wrnexus/core"; import { createTracker, type ErrorEvent, type ErrorSink } from "../src/index.ts"; function collectingSink(): ErrorSink & { events: ErrorEvent[] } { const events: ErrorEvent[] = []; return { name: "collect", events, capture: (e) => void events.push(e) }; } test("capture fans out to sinks with context + timestamp", async () => { const sink = collectingSink(); const tracker = createTracker({ sinks: [sink], now: () => 123 }); await tracker.capture(new Error("boom"), { userId: "u1" }); expect(sink.events.length).toBe(1); expect(sink.events[0]!.error.message).toBe("boom"); expect(sink.events[0]!.context.userId).toBe("u1"); expect(sink.events[0]!.timestamp).toBe(123); }); test("non-Error values are wrapped", async () => { const sink = collectingSink(); const tracker = createTracker({ sinks: [sink] }); await tracker.capture("just a string"); expect(sink.events[0]!.error.message).toBe("just a string"); }); test("beforeSend can scrub or drop events", async () => { const sink = collectingSink(); const tracker = createTracker({ sinks: [sink], beforeSend: (e) => (e.context.secret ? null : e), // drop events tagged secret }); await tracker.capture(new Error("a"), { secret: true }); await tracker.capture(new Error("b")); expect(sink.events.map((e) => e.error.message)).toEqual(["b"]); }); test("middleware captures a thrown request error and re-throws it", async () => { const sink = collectingSink(); const tracker = createTracker({ sinks: [sink] }); const url = new URL("http://x/api/boom"); const ctx = createContext(new Request(url, { method: "POST" }), url); await expect( tracker.middleware()(ctx, () => { throw new Error("downstream"); }), ).rejects.toThrow("downstream"); expect(sink.events[0]!.context).toMatchObject({ method: "POST", path: "/api/boom" }); }); test("a failing sink never breaks capture", async () => { const bad: ErrorSink = { capture: () => { throw new Error("sink down"); }, }; const good = collectingSink(); const tracker = createTracker({ sinks: [bad, good] }); await tracker.capture(new Error("x")); // must not throw expect(good.events.length).toBe(1); });