import { expect, test } from "bun:test"; import { createServerIssue, issueFromError } from "../../src/server/issues.ts"; test("server issues have stable fingerprints and retain actionable error context", () => { const input = { ruleId: "server/query", category: "server" as const, severity: "warning" as const, title: "Slow query", message: "Query exceeded budget", pathname: "/orders", source: { file: "app/api/orders.ts", line: 12, column: 4 }, }; const first = createServerIssue(input); const second = createServerIssue(input); expect(first.id).not.toBe(second.id); expect(first.fingerprint).toBe(second.fingerprint); expect(first.metadata?.pathname).toBe("/orders"); const error = issueFromError(new Error("database offline"), { pathname: "/orders" }); expect(error.severity).toBe("error"); expect(error.message).toBe("database offline"); expect(error.metadata?.stack).toContain("database offline"); expect(error.recommendation).toContain("stack trace"); }); test("non-Error throws are normalized without losing caller context", () => { const issue = issueFromError( { reason: "offline" }, { ruleId: "server/dependency", title: "Dependency failed", pathname: "/health", source: { file: "app/api/health.ts", line: 4 }, }, ); expect(issue.ruleId).toBe("server/dependency"); expect(issue.title).toBe("Dependency failed"); expect(issue.message).toBe("[object Object]"); expect(issue.fingerprint).toContain("app/api/health.ts:4:0"); });