141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import fs from "node:fs";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { onDiagnosticEvent, resetDiagnosticEventsForTest } from "../infra/diagnostic-events.js";
|
|
import {
|
|
diagnosticSessionStates,
|
|
getDiagnosticSessionStateCountForTest,
|
|
getDiagnosticSessionState,
|
|
pruneDiagnosticSessionStates,
|
|
resetDiagnosticSessionStateForTest,
|
|
} from "./diagnostic-session-state.js";
|
|
import {
|
|
logSessionStateChange,
|
|
resetDiagnosticStateForTest,
|
|
resolveStuckSessionWarnMs,
|
|
startDiagnosticHeartbeat,
|
|
} from "./diagnostic.js";
|
|
|
|
describe("diagnostic session state pruning", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
resetDiagnosticSessionStateForTest();
|
|
});
|
|
|
|
afterEach(() => {
|
|
resetDiagnosticSessionStateForTest();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("evicts stale idle session states", () => {
|
|
getDiagnosticSessionState({ sessionId: "stale-1" });
|
|
expect(getDiagnosticSessionStateCountForTest()).toBe(1);
|
|
|
|
vi.advanceTimersByTime(31 * 60 * 1000);
|
|
getDiagnosticSessionState({ sessionId: "fresh-1" });
|
|
|
|
expect(getDiagnosticSessionStateCountForTest()).toBe(1);
|
|
});
|
|
|
|
it("caps tracked session states to a bounded max", () => {
|
|
const now = Date.now();
|
|
for (let i = 0; i < 2001; i += 1) {
|
|
diagnosticSessionStates.set(`session-${i}`, {
|
|
sessionId: `session-${i}`,
|
|
lastActivity: now + i,
|
|
state: "idle",
|
|
queueDepth: 1,
|
|
});
|
|
}
|
|
pruneDiagnosticSessionStates(now + 2002, true);
|
|
|
|
expect(getDiagnosticSessionStateCountForTest()).toBe(2000);
|
|
});
|
|
|
|
it("reuses keyed session state when later looked up by sessionId", () => {
|
|
const keyed = getDiagnosticSessionState({
|
|
sessionId: "s1",
|
|
sessionKey: "agent:main:discord:channel:c1",
|
|
});
|
|
const bySessionId = getDiagnosticSessionState({ sessionId: "s1" });
|
|
|
|
expect(bySessionId).toBe(keyed);
|
|
expect(bySessionId.sessionKey).toBe("agent:main:discord:channel:c1");
|
|
expect(getDiagnosticSessionStateCountForTest()).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("logger import side effects", () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("does not mkdir at import time", async () => {
|
|
vi.useRealTimers();
|
|
vi.resetModules();
|
|
|
|
const mkdirSpy = vi.spyOn(fs, "mkdirSync");
|
|
|
|
await import("./logger.js");
|
|
|
|
expect(mkdirSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("stuck session diagnostics threshold", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
resetDiagnosticStateForTest();
|
|
resetDiagnosticEventsForTest();
|
|
});
|
|
|
|
afterEach(() => {
|
|
resetDiagnosticEventsForTest();
|
|
resetDiagnosticStateForTest();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("uses the configured diagnostics.stuckSessionWarnMs threshold", () => {
|
|
const events: Array<{ type: string }> = [];
|
|
const unsubscribe = onDiagnosticEvent((event) => {
|
|
events.push({ type: event.type });
|
|
});
|
|
try {
|
|
startDiagnosticHeartbeat({
|
|
diagnostics: {
|
|
enabled: true,
|
|
stuckSessionWarnMs: 30_000,
|
|
},
|
|
});
|
|
logSessionStateChange({ sessionId: "s1", sessionKey: "main", state: "processing" });
|
|
vi.advanceTimersByTime(61_000);
|
|
} finally {
|
|
unsubscribe();
|
|
}
|
|
|
|
expect(events.filter((event) => event.type === "session.stuck")).toHaveLength(1);
|
|
});
|
|
|
|
it("falls back to default threshold when config is absent", () => {
|
|
const events: Array<{ type: string }> = [];
|
|
const unsubscribe = onDiagnosticEvent((event) => {
|
|
events.push({ type: event.type });
|
|
});
|
|
try {
|
|
startDiagnosticHeartbeat();
|
|
logSessionStateChange({ sessionId: "s2", sessionKey: "main", state: "processing" });
|
|
vi.advanceTimersByTime(31_000);
|
|
} finally {
|
|
unsubscribe();
|
|
}
|
|
|
|
expect(events.filter((event) => event.type === "session.stuck")).toHaveLength(0);
|
|
});
|
|
|
|
it("uses default threshold for invalid values", () => {
|
|
expect(resolveStuckSessionWarnMs({ diagnostics: { stuckSessionWarnMs: -1 } })).toBe(120_000);
|
|
expect(resolveStuckSessionWarnMs({ diagnostics: { stuckSessionWarnMs: 0 } })).toBe(120_000);
|
|
expect(resolveStuckSessionWarnMs()).toBe(120_000);
|
|
});
|
|
});
|