diff --git a/src/hooks/todo-continuation-enforcer/dispose.test.ts b/src/hooks/todo-continuation-enforcer/dispose.test.ts new file mode 100644 index 000000000..5423c8068 --- /dev/null +++ b/src/hooks/todo-continuation-enforcer/dispose.test.ts @@ -0,0 +1,101 @@ +declare module "bun:test" { + export interface Matchers { + toBeDefined(): void + toBeUndefined(): void + toHaveLength(expected: number): void + } +} + +import { afterAll, afterEach, describe, expect, it, mock } from "bun:test" + +import * as actualSessionStateModule from "./session-state" +import type { SessionStateStore } from "./session-state" + +let createdSessionStateStore: SessionStateStore | undefined +const createActualSessionStateStore = actualSessionStateModule.createSessionStateStore + +const mockModule = mock as typeof mock & { + module: (specifier: string, factory: () => unknown) => void +} + +mockModule.module("./session-state", () => ({ + ...actualSessionStateModule, + createSessionStateStore: () => { + const sessionStateStore = createActualSessionStateStore() + createdSessionStateStore = sessionStateStore + return sessionStateStore + }, +})) + +const { createTodoContinuationEnforcer } = await import(".") + +type PluginInput = Parameters[0] + +function createMockPluginInput(): PluginInput { + return { + directory: "/tmp/test", + } as PluginInput +} + +function getCreatedSessionStateStore(): SessionStateStore { + if (!createdSessionStateStore) { + throw new Error("expected session state store to be created") + } + + return createdSessionStateStore +} + +describe("todo-continuation-enforcer dispose", () => { + afterEach(() => { + createdSessionStateStore?.shutdown() + createdSessionStateStore = undefined + }) + + afterAll(() => { + mockModule.module("./session-state", () => actualSessionStateModule) + }) + + it("#given todo-continuation-enforcer created #when dispose exists on return value #then it is a function", () => { + // given + const enforcer = createTodoContinuationEnforcer(createMockPluginInput()) + + // when + const { dispose } = enforcer + + // then + expect(typeof dispose).toBe("function") + + enforcer.dispose() + }) + + it("#given enforcer with active session states #when dispose is called #then internal session state store is shut down", () => { + // given + const originalClearInterval = globalThis.clearInterval + const clearIntervalCalls: Array[0]> = [] + globalThis.clearInterval = ((timer?: Parameters[0]) => { + clearIntervalCalls.push(timer) + return originalClearInterval(timer) + }) as typeof clearInterval + + try { + const enforcer = createTodoContinuationEnforcer(createMockPluginInput()) + const sessionStateStore = getCreatedSessionStateStore() + + enforcer.markRecovering("session-1") + enforcer.markRecovering("session-2") + + expect(sessionStateStore.getExistingState("session-1")).toBeDefined() + expect(sessionStateStore.getExistingState("session-2")).toBeDefined() + + // when + enforcer.dispose() + + // then + expect(clearIntervalCalls).toHaveLength(1) + expect(sessionStateStore.getExistingState("session-1")).toBeUndefined() + expect(sessionStateStore.getExistingState("session-2")).toBeUndefined() + } finally { + globalThis.clearInterval = originalClearInterval + } + }) +}) diff --git a/src/hooks/todo-continuation-enforcer/index.ts b/src/hooks/todo-continuation-enforcer/index.ts index b7e376563..edeba85b5 100644 --- a/src/hooks/todo-continuation-enforcer/index.ts +++ b/src/hooks/todo-continuation-enforcer/index.ts @@ -56,5 +56,6 @@ export function createTodoContinuationEnforcer( markRecovering, markRecoveryComplete, cancelAllCountdowns, + dispose: () => sessionStateStore.shutdown(), } } diff --git a/src/hooks/todo-continuation-enforcer/types.ts b/src/hooks/todo-continuation-enforcer/types.ts index 2221a14c0..c801ab128 100644 --- a/src/hooks/todo-continuation-enforcer/types.ts +++ b/src/hooks/todo-continuation-enforcer/types.ts @@ -13,6 +13,7 @@ export interface TodoContinuationEnforcer { markRecovering: (sessionID: string) => void markRecoveryComplete: (sessionID: string) => void cancelAllCountdowns: () => void + dispose: () => void } export interface Todo {