142 lines
3.9 KiB
TypeScript
142 lines
3.9 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
createDiagnosticsAdapter,
|
|
getLastBootEvidence,
|
|
noOpDiagnostics,
|
|
recordBootFailure,
|
|
} from "../../src/adapters/diagnostics/bounded-diagnostics.ts";
|
|
import {
|
|
projectDiagnosticRecord,
|
|
safeErrorKind,
|
|
} from "../../src/contracts/diagnostics.ts";
|
|
|
|
describe("structured diagnostics contract", () => {
|
|
it("projects only registered, bounded context with deterministic time", () => {
|
|
const projected = projectDiagnosticRecord(
|
|
{
|
|
level: "info",
|
|
eventId: "route.changed",
|
|
context: { route_id: "APP_HOME", build_id: "build-a" },
|
|
},
|
|
() => 0,
|
|
);
|
|
|
|
expect(projected).toEqual({
|
|
success: true,
|
|
record: {
|
|
level: "info",
|
|
eventId: "route.changed",
|
|
timestamp: "1970-01-01T00:00:00.000Z",
|
|
context: { route_id: "APP_HOME", build_id: "build-a" },
|
|
},
|
|
});
|
|
expect(
|
|
projectDiagnosticRecord({
|
|
level: "error",
|
|
eventId: "ui.render.failed",
|
|
context: { raw_url: "https://example.test/private?token=secret" },
|
|
}),
|
|
).toEqual({ success: false, reason: "unknown-context" });
|
|
expect(
|
|
projectDiagnosticRecord({
|
|
level: "error",
|
|
eventId: "ui.render.failed",
|
|
context: { route_id: "private value with whitespace" },
|
|
}),
|
|
).toEqual({ success: false, reason: "invalid-context" });
|
|
});
|
|
|
|
it("bounds evidence and isolates throwing sinks and hostile error objects", () => {
|
|
const sink = vi.fn(() => {
|
|
throw new Error("sink-secret");
|
|
});
|
|
const adapter = createDiagnosticsAdapter({
|
|
maxEntries: 1,
|
|
now: () => 0,
|
|
sink,
|
|
});
|
|
const circular: Record<string, unknown> = { name: "TypeError" };
|
|
circular.self = circular;
|
|
const hostile = new Proxy(
|
|
{},
|
|
{
|
|
get() {
|
|
throw new Error("private getter");
|
|
},
|
|
ownKeys() {
|
|
throw new Error("private keys");
|
|
},
|
|
},
|
|
);
|
|
|
|
expect(safeErrorKind(circular)).toBe("TYPE_ERROR");
|
|
expect(safeErrorKind(hostile)).toBe("UNKNOWN_FAILURE");
|
|
expect(
|
|
projectDiagnosticRecord({
|
|
level: "error",
|
|
eventId: "ui.render.failed",
|
|
context: hostile,
|
|
}),
|
|
).toEqual({ success: false, reason: "serialization-failure" });
|
|
expect(() =>
|
|
adapter.record({
|
|
level: "info",
|
|
eventId: "route.changed",
|
|
context: { route_id: "APP_HOME" },
|
|
}),
|
|
).not.toThrow();
|
|
adapter.record({
|
|
level: "warn",
|
|
eventId: "cache.operation.failed",
|
|
context: { operation: "query", error_kind: "UNKNOWN_FAILURE" },
|
|
});
|
|
|
|
expect(adapter.entries()).toHaveLength(1);
|
|
expect(adapter.entries()[0]?.eventId).toBe("cache.operation.failed");
|
|
expect(adapter.dropped()).toEqual({
|
|
"queue-full": 1,
|
|
"sink-failure": 2,
|
|
});
|
|
expect(JSON.stringify(adapter.entries())).not.toMatch(
|
|
/sink-secret|private getter/,
|
|
);
|
|
});
|
|
|
|
it("creates safe pre-mount boot evidence and supports a true no-op", () => {
|
|
const circular: Record<string, unknown> = {
|
|
name: "TypeError",
|
|
token: "credential-value",
|
|
stack: "private-stack",
|
|
};
|
|
circular.self = circular;
|
|
|
|
expect(() =>
|
|
recordBootFailure(
|
|
circular,
|
|
{
|
|
buildId: "build-a",
|
|
configSchemaVersion: "1",
|
|
supportReference: "support-private",
|
|
},
|
|
() => 0,
|
|
),
|
|
).not.toThrow();
|
|
const evidence = getLastBootEvidence();
|
|
expect(evidence?.diagnostic?.eventId).toBe("app.boot.failed");
|
|
expect(evidence?.telemetry).toMatchObject({
|
|
eventName: "app.boot.failed",
|
|
timestamp: "1970-01-01T00:00:00.000Z",
|
|
});
|
|
expect(JSON.stringify(evidence)).not.toMatch(
|
|
/credential-value|private-stack|support-private/,
|
|
);
|
|
expect(() =>
|
|
noOpDiagnostics.record({
|
|
level: "error",
|
|
eventId: "app.boot.failed",
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
});
|