Files
2026-07-13 13:32:57 +08:00

92 lines
3.1 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { recordSpanException } from "../src/v3/otel/utils.js";
import type { Span } from "@opentelemetry/api";
function createMockSpan() {
return {
recordException: vi.fn(),
setStatus: vi.fn(),
} as unknown as Span & {
recordException: ReturnType<typeof vi.fn>;
setStatus: ReturnType<typeof vi.fn>;
};
}
describe("recordSpanException", () => {
it("records Error instances with truncated message and stack", () => {
const span = createMockSpan();
const error = new Error("x".repeat(5_000));
recordSpanException(span, error);
expect(span.recordException).toHaveBeenCalledTimes(1);
const recorded = (span.recordException as any).mock.calls[0][0] as Error;
expect(recorded).toBeInstanceOf(Error);
expect(recorded.message.length).toBeLessThan(1100);
});
it("records string errors with truncation", () => {
const span = createMockSpan();
recordSpanException(span, "x".repeat(10_000));
const recorded = (span.recordException as any).mock.calls[0][0] as string;
expect(typeof recorded).toBe("string");
expect(recorded.length).toBeLessThan(5_100);
expect(recorded).toContain("...[truncated]");
});
it("does not throw on circular references", () => {
const span = createMockSpan();
const circular: any = { foo: "bar" };
circular.self = circular;
expect(() => recordSpanException(span, circular)).not.toThrow();
expect(span.recordException).toHaveBeenCalledTimes(1);
expect(span.setStatus).toHaveBeenCalledTimes(1);
});
it("does not throw on BigInt values", () => {
const span = createMockSpan();
const error = { count: BigInt(123) };
expect(() => recordSpanException(span, error)).not.toThrow();
expect(span.recordException).toHaveBeenCalledTimes(1);
});
it("handles symbol values (JSON.stringify returns undefined)", () => {
const span = createMockSpan();
const sym = Symbol("test");
expect(() => recordSpanException(span, sym)).not.toThrow();
expect(span.recordException).toHaveBeenCalledTimes(1);
const recorded = (span.recordException as any).mock.calls[0][0] as string;
expect(typeof recorded).toBe("string");
expect(recorded).toContain("Symbol");
});
it("handles function values (JSON.stringify returns undefined)", () => {
const span = createMockSpan();
const fn = () => "test";
expect(() => recordSpanException(span, fn)).not.toThrow();
expect(span.recordException).toHaveBeenCalledTimes(1);
});
it("handles undefined (JSON.stringify returns undefined)", () => {
const span = createMockSpan();
expect(() => recordSpanException(span, undefined)).not.toThrow();
expect(span.recordException).toHaveBeenCalledTimes(1);
const recorded = (span.recordException as any).mock.calls[0][0] as string;
expect(typeof recorded).toBe("string");
});
it("always calls setStatus ERROR", () => {
const span = createMockSpan();
recordSpanException(span, new Error("test"));
recordSpanException(span, "string");
recordSpanException(span, { obj: true });
expect(span.setStatus).toHaveBeenCalledTimes(3);
});
});