fix(FN-2200): log stale-session eviction failures
- Replace the silent stale-session eviction catch with a terminal error log in setupTerminalWebSocket - Add regression tests that assert evictStaleSessions runs on each 60s interval tick - Verify eviction errors are logged without stopping future ticks and interval cleanup runs on server close
This commit is contained in:
@@ -310,6 +310,8 @@ export class DevServerProcessManager extends EventEmitter {
|
||||
}
|
||||
|
||||
private async handleClose(code: number): Promise<void> {
|
||||
this.clearTimers();
|
||||
|
||||
const updated = await this.store.updateState({
|
||||
status: "stopped",
|
||||
exitCode: code,
|
||||
@@ -318,7 +320,6 @@ export class DevServerProcessManager extends EventEmitter {
|
||||
});
|
||||
|
||||
this.childProcess = null;
|
||||
this.clearTimers();
|
||||
this.resolveClosePromise?.(updated);
|
||||
this.resolveClosePromise = null;
|
||||
this.closePromise = null;
|
||||
@@ -326,6 +327,8 @@ export class DevServerProcessManager extends EventEmitter {
|
||||
}
|
||||
|
||||
private async handleFailure(error: Error): Promise<void> {
|
||||
this.clearTimers();
|
||||
|
||||
const updated = await this.store.updateState({
|
||||
status: "failed",
|
||||
stoppedAt: new Date().toISOString(),
|
||||
@@ -333,7 +336,6 @@ export class DevServerProcessManager extends EventEmitter {
|
||||
});
|
||||
|
||||
this.childProcess = null;
|
||||
this.clearTimers();
|
||||
this.resolveClosePromise?.(updated);
|
||||
this.resolveClosePromise = null;
|
||||
this.closePromise = null;
|
||||
|
||||
@@ -503,6 +503,74 @@ describe("Terminal WebSocket heartbeat", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Terminal stale-session eviction", () => {
|
||||
let app: ReturnType<typeof express>;
|
||||
let server: http.Server;
|
||||
let store: TaskStore;
|
||||
let consoleErrorSpy: ReturnType<typeof vi.spyOn>;
|
||||
|
||||
beforeEach(() => {
|
||||
app = express();
|
||||
server = http.createServer(app);
|
||||
store = createMockStore();
|
||||
vi.useFakeTimers();
|
||||
mockTerminalService.evictStaleSessions.mockReset().mockReturnValue(0);
|
||||
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("calls evictStaleSessions on each 60s interval tick", () => {
|
||||
setupTerminalWebSocket(app, server, store);
|
||||
|
||||
vi.advanceTimersByTime(60_000);
|
||||
expect(mockTerminalService.evictStaleSessions).toHaveBeenCalledTimes(1);
|
||||
|
||||
vi.advanceTimersByTime(60_000);
|
||||
expect(mockTerminalService.evictStaleSessions).toHaveBeenCalledTimes(2);
|
||||
expect(consoleErrorSpy).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Stale session eviction failed"),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("logs error and continues when evictStaleSessions throws", () => {
|
||||
mockTerminalService.evictStaleSessions.mockImplementation(() => {
|
||||
throw new Error("simulated eviction failure");
|
||||
});
|
||||
|
||||
setupTerminalWebSocket(app, server, store);
|
||||
|
||||
vi.advanceTimersByTime(60_000);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalled();
|
||||
const failureCall = consoleErrorSpy.mock.calls.find(
|
||||
(call) => typeof call[0] === "string" && call[0].includes("[terminal] Stale session eviction failed:"),
|
||||
);
|
||||
expect(failureCall).toBeDefined();
|
||||
expect(failureCall?.[0]).toEqual(expect.stringContaining("[terminal] Stale session eviction failed:"));
|
||||
expect(failureCall?.[1]).toBeInstanceOf(Error);
|
||||
expect((failureCall?.[1] as Error).message).toContain("simulated eviction failure");
|
||||
|
||||
vi.advanceTimersByTime(60_000);
|
||||
expect(mockTerminalService.evictStaleSessions).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("stops eviction interval when server closes", () => {
|
||||
setupTerminalWebSocket(app, server, store);
|
||||
|
||||
server.emit("close");
|
||||
|
||||
vi.advanceTimersByTime(120_000);
|
||||
expect(mockTerminalService.evictStaleSessions).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Scoped Scheduling Resolver Regression Tests
|
||||
* ===========================================
|
||||
|
||||
@@ -1051,8 +1051,8 @@ export function setupTerminalWebSocket(
|
||||
const staleEvictionInterval = setInterval(() => {
|
||||
try {
|
||||
defaultTerminalService.evictStaleSessions();
|
||||
} catch {
|
||||
// Ignore errors during periodic eviction
|
||||
} catch (err) {
|
||||
console.error("[terminal] Stale session eviction failed:", err);
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user