test: add integration tests for webhook hook cleanup
This commit is contained in:
parent
113bab034d
commit
45f2fbf95d
173
src/gateway/hooks-cleanup.integration.test.ts
Normal file
173
src/gateway/hooks-cleanup.integration.test.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("./hook-run-registry.store.js", () => ({
|
||||
loadHookRunRegistryFromDisk: vi.fn(() => new Map()),
|
||||
saveHookRunRegistryToDisk: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./call.js", () => ({
|
||||
callGateway: vi.fn().mockResolvedValue({ ok: true }),
|
||||
}));
|
||||
|
||||
describe("webhook hook cleanup integration", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("sweeper deletes session when cleanupAtMs is reached", async () => {
|
||||
const { callGateway } = await import("./call.js");
|
||||
const mockCallGateway = vi.mocked(callGateway);
|
||||
|
||||
const { registerHookRun, markHookRunComplete, clearHookRuns } =
|
||||
await import("./hook-run-registry.js");
|
||||
clearHookRuns();
|
||||
|
||||
// Register a hook run with immediate cleanup
|
||||
registerHookRun({
|
||||
runId: "test-run",
|
||||
sessionKey: "hook:test:123",
|
||||
jobName: "Test",
|
||||
cleanup: "delete",
|
||||
cleanupDelayMinutes: 0,
|
||||
});
|
||||
|
||||
// Mark complete (sets cleanupAtMs to now and cleanupHandled to true)
|
||||
markHookRunComplete("test-run");
|
||||
|
||||
// Advance time past the sweeper interval
|
||||
await vi.advanceTimersByTimeAsync(61_000);
|
||||
|
||||
// Verify sessions.delete was called
|
||||
expect(mockCallGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "sessions.delete",
|
||||
params: { key: "hook:test:123", deleteTranscript: true },
|
||||
}),
|
||||
);
|
||||
|
||||
clearHookRuns();
|
||||
});
|
||||
|
||||
it("does not delete session when cleanup=keep", async () => {
|
||||
const { callGateway } = await import("./call.js");
|
||||
const mockCallGateway = vi.mocked(callGateway);
|
||||
|
||||
const { registerHookRun, clearHookRuns } = await import("./hook-run-registry.js");
|
||||
clearHookRuns();
|
||||
|
||||
// Register with cleanup=keep (should be no-op)
|
||||
registerHookRun({
|
||||
runId: "test-run",
|
||||
sessionKey: "hook:test:123",
|
||||
jobName: "Test",
|
||||
cleanup: "keep",
|
||||
cleanupDelayMinutes: 0,
|
||||
});
|
||||
|
||||
// Advance time
|
||||
await vi.advanceTimersByTimeAsync(120_000);
|
||||
|
||||
// Verify sessions.delete was NOT called
|
||||
expect(mockCallGateway).not.toHaveBeenCalled();
|
||||
|
||||
clearHookRuns();
|
||||
});
|
||||
|
||||
it("respects cleanupDelayMinutes", async () => {
|
||||
const { callGateway } = await import("./call.js");
|
||||
const mockCallGateway = vi.mocked(callGateway);
|
||||
|
||||
const { registerHookRun, markHookRunComplete, clearHookRuns } =
|
||||
await import("./hook-run-registry.js");
|
||||
clearHookRuns();
|
||||
|
||||
// Register with 5 minute delay
|
||||
registerHookRun({
|
||||
runId: "test-run",
|
||||
sessionKey: "hook:test:123",
|
||||
jobName: "Test",
|
||||
cleanup: "delete",
|
||||
cleanupDelayMinutes: 5,
|
||||
});
|
||||
|
||||
markHookRunComplete("test-run");
|
||||
|
||||
// Advance 2 minutes - should NOT delete yet
|
||||
await vi.advanceTimersByTimeAsync(2 * 60 * 1000);
|
||||
expect(mockCallGateway).not.toHaveBeenCalled();
|
||||
|
||||
// Advance past 5 minutes total (need to trigger sweeper at 6 min mark)
|
||||
await vi.advanceTimersByTimeAsync(4 * 60 * 1000);
|
||||
expect(mockCallGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "sessions.delete",
|
||||
}),
|
||||
);
|
||||
|
||||
clearHookRuns();
|
||||
});
|
||||
|
||||
it("retries on RPC failure", async () => {
|
||||
const { callGateway } = await import("./call.js");
|
||||
const mockCallGateway = vi.mocked(callGateway);
|
||||
mockCallGateway.mockRejectedValueOnce(new Error("RPC failed"));
|
||||
mockCallGateway.mockResolvedValueOnce({ ok: true });
|
||||
|
||||
const { registerHookRun, markHookRunComplete, getHookRun, clearHookRuns } =
|
||||
await import("./hook-run-registry.js");
|
||||
clearHookRuns();
|
||||
|
||||
registerHookRun({
|
||||
runId: "test-run",
|
||||
sessionKey: "hook:test:123",
|
||||
jobName: "Test",
|
||||
cleanup: "delete",
|
||||
cleanupDelayMinutes: 0,
|
||||
});
|
||||
|
||||
markHookRunComplete("test-run");
|
||||
|
||||
// First sweep - RPC fails, entry stays
|
||||
await vi.advanceTimersByTimeAsync(61_000);
|
||||
expect(mockCallGateway).toHaveBeenCalledTimes(1);
|
||||
expect(getHookRun("test-run")).toBeDefined();
|
||||
|
||||
// Second sweep - RPC succeeds, entry removed
|
||||
await vi.advanceTimersByTimeAsync(60_000);
|
||||
expect(mockCallGateway).toHaveBeenCalledTimes(2);
|
||||
expect(getHookRun("test-run")).toBeUndefined();
|
||||
|
||||
clearHookRuns();
|
||||
});
|
||||
|
||||
it("does not delete session when cleanup=undefined", async () => {
|
||||
const { callGateway } = await import("./call.js");
|
||||
const mockCallGateway = vi.mocked(callGateway);
|
||||
|
||||
const { registerHookRun, clearHookRuns } = await import("./hook-run-registry.js");
|
||||
clearHookRuns();
|
||||
|
||||
// Register with cleanup=undefined (should be no-op)
|
||||
registerHookRun({
|
||||
runId: "test-run",
|
||||
sessionKey: "hook:test:123",
|
||||
jobName: "Test",
|
||||
cleanup: undefined,
|
||||
cleanupDelayMinutes: undefined,
|
||||
});
|
||||
|
||||
// Advance time
|
||||
await vi.advanceTimersByTimeAsync(120_000);
|
||||
|
||||
// Verify sessions.delete was NOT called
|
||||
expect(mockCallGateway).not.toHaveBeenCalled();
|
||||
|
||||
clearHookRuns();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user