1import { describe, expect, it } from "vite-plus/test";
2
3import { safeErrorLogAttributes } from "./safeLog.ts";
4
5describe("safeErrorLogAttributes", () => {
6 it("keeps correlation and stack frames without serializing messages or nested causes", () => {
7 const cause = Object.assign(new Error("nested-cause-secret-sentinel"), {
8 traceId: "trace-safe-123",
9 });
10 const error = Object.assign(new Error("outer-error-secret-sentinel", { cause }), {
11 _tag: "ProjectRemovalError",
12 });
13 error.stack = [
14 "ProjectRemovalError: outer-error-secret-sentinel",
15 " at removeProject (https://user:password@example.com/project.ts?token=secret#fragment)",
16 ].join("\n");
17
18 const attributes = safeErrorLogAttributes(error);
19
20 expect(attributes).toMatchObject({
21 errorType: "error",
22 errorName: "Error",
23 errorTag: "ProjectRemovalError",
24 traceId: "trace-safe-123",
25 stack: " at removeProject (https://example.com/project.ts)",
26 });
27 const diagnosticText = Object.values(attributes).map(String).join("\n");
28 expect(diagnosticText).not.toContain("outer-error-secret-sentinel");
29 expect(diagnosticText).not.toContain("nested-cause-secret-sentinel");
30 expect(diagnosticText).not.toContain("user:password");
31 expect(diagnosticText).not.toContain("token=secret");
32 });
33
34 it("does not trust arbitrary object messages or tags", () => {
35 const attributes = safeErrorLogAttributes({
36 _tag: "payload-secret-sentinel",
37 message: "message-secret-sentinel",
38 cause: { traceId: "trace id with unsafe whitespace" },
39 });
40
41 expect(attributes).toEqual({ errorType: "object" });
42 });
43
44 it("skips an unsafe outer trace id when a nested safe trace id is available", () => {
45 const attributes = safeErrorLogAttributes({
46 traceId: "unsafe trace id",
47 cause: { traceId: "trace-safe-inner" },
48 });
49
50 expect(attributes).toEqual({
51 errorType: "object",
52 traceId: "trace-safe-inner",
53 });
54 });
55});