import { describe, it, expect } from "vitest"; import { RecordingTracer } from "@/instrumentation/recording-tracer"; describe("RecordingTracer", () => { it("records every startSpan call with name, op, attributes, status, durationMs", async () => { const tracer = new RecordingTracer(); await tracer.startSpan( { name: "blog.getArticles", op: "use-case", attributes: { limit: 10 } }, async (span) => { span.setAttribute("count", 3); span.setStatus("ok"); return undefined; }, ); expect(tracer.spans).toHaveLength(1); const s = tracer.spans[0]!; expect(s.name).toBe("blog.getArticles"); expect(s.op).toBe("use-case"); expect(s.attributes).toMatchObject({ limit: 10, count: 3 }); expect(s.status).toBe("ok"); expect(typeof s.durationMs).toBe("number"); expect(s.durationMs).toBeGreaterThanOrEqual(0); }); it("records error status when fn throws", async () => { const tracer = new RecordingTracer(); await expect( tracer.startSpan({ name: "x" }, async () => { throw new Error("boom"); }), ).rejects.toThrow("boom"); expect(tracer.spans).toHaveLength(1); expect(tracer.spans[0]!.status).toBe("error"); expect(tracer.spans[0]!.statusMessage).toBe("boom"); }); it("records error status when set explicitly via span.setStatus", async () => { const tracer = new RecordingTracer(); await tracer.startSpan({ name: "x" }, async (span) => { span.setStatus("error", "validation failed"); return undefined; }); expect(tracer.spans[0]!.status).toBe("error"); expect(tracer.spans[0]!.statusMessage).toBe("validation failed"); }); it("reset() clears recorded spans", async () => { const tracer = new RecordingTracer(); await tracer.startSpan({ name: "x" }, async () => undefined); expect(tracer.spans).toHaveLength(1); tracer.reset(); expect(tracer.spans).toHaveLength(0); }); it("findSpan returns first matching span by name", async () => { const tracer = new RecordingTracer(); await tracer.startSpan({ name: "a" }, async () => undefined); await tracer.startSpan({ name: "b" }, async () => undefined); expect(tracer.findSpan("b")).toBeDefined(); expect(tracer.findSpan("missing")).toBeUndefined(); }); it("nested spans are recorded in order (children appear after parent end)", async () => { const tracer = new RecordingTracer(); await tracer.startSpan({ name: "parent" }, async () => { await tracer.startSpan({ name: "child" }, async () => undefined); }); expect(tracer.spans.map((s) => s.name)).toEqual(["child", "parent"]); }); });