Files
openclaw/src/infra/diagnostic-events.test.ts
2026-03-13 20:18:50 +00:00

122 lines
3.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
emitDiagnosticEvent,
isDiagnosticsEnabled,
onDiagnosticEvent,
resetDiagnosticEventsForTest,
} from "./diagnostic-events.js";
describe("diagnostic-events", () => {
beforeEach(() => {
resetDiagnosticEventsForTest();
});
afterEach(() => {
resetDiagnosticEventsForTest();
vi.restoreAllMocks();
});
it("emits monotonic seq and timestamps to subscribers", () => {
vi.spyOn(Date, "now").mockReturnValueOnce(111).mockReturnValueOnce(222);
const events: Array<{ seq: number; ts: number; type: string }> = [];
const stop = onDiagnosticEvent((event) => {
events.push({ seq: event.seq, ts: event.ts, type: event.type });
});
emitDiagnosticEvent({
type: "model.usage",
usage: { total: 1 },
});
emitDiagnosticEvent({
type: "session.state",
state: "processing",
});
stop();
expect(events).toEqual([
{ seq: 1, ts: 111, type: "model.usage" },
{ seq: 2, ts: 222, type: "session.state" },
]);
});
it("isolates listener failures and logs them", () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const seen: string[] = [];
onDiagnosticEvent(() => {
throw new Error("boom");
});
onDiagnosticEvent((event) => {
seen.push(event.type);
});
emitDiagnosticEvent({
type: "message.queued",
source: "telegram",
});
expect(seen).toEqual(["message.queued"]);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining("listener error type=message.queued seq=1: Error: boom"),
);
});
it("supports unsubscribe and full reset", () => {
const seen: string[] = [];
const stop = onDiagnosticEvent((event) => {
seen.push(event.type);
});
emitDiagnosticEvent({
type: "webhook.received",
channel: "telegram",
});
stop();
emitDiagnosticEvent({
type: "webhook.processed",
channel: "telegram",
});
expect(seen).toEqual(["webhook.received"]);
resetDiagnosticEventsForTest();
emitDiagnosticEvent({
type: "webhook.error",
channel: "telegram",
error: "failed",
});
expect(seen).toEqual(["webhook.received"]);
});
it("drops recursive emissions after the guard threshold", () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
let calls = 0;
onDiagnosticEvent(() => {
calls += 1;
emitDiagnosticEvent({
type: "queue.lane.enqueue",
lane: "main",
queueSize: calls,
});
});
emitDiagnosticEvent({
type: "queue.lane.enqueue",
lane: "main",
queueSize: 0,
});
expect(calls).toBe(101);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining(
"recursion guard tripped at depth=101, dropping type=queue.lane.enqueue",
),
);
});
it("requires an explicit true diagnostics flag", () => {
expect(isDiagnosticsEnabled()).toBe(false);
expect(isDiagnosticsEnabled({ diagnostics: { enabled: false } } as never)).toBe(false);
expect(isDiagnosticsEnabled({ diagnostics: { enabled: true } } as never)).toBe(true);
});
});