fix(todo-continuation-enforcer): expose prune interval for cleanup
Prune interval created inside hook was not exposed for disposal, preventing cleanup on plugin unload. - Add dispose() method that clears the prune interval - Export dispose in hook return type Tests: 2 pass, 6 expects
This commit is contained in:
@@ -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<typeof createTodoContinuationEnforcer>[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<Parameters<typeof clearInterval>[0]> = []
|
||||||
|
globalThis.clearInterval = ((timer?: Parameters<typeof clearInterval>[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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -56,5 +56,6 @@ export function createTodoContinuationEnforcer(
|
|||||||
markRecovering,
|
markRecovering,
|
||||||
markRecoveryComplete,
|
markRecoveryComplete,
|
||||||
cancelAllCountdowns,
|
cancelAllCountdowns,
|
||||||
|
dispose: () => sessionStateStore.shutdown(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface TodoContinuationEnforcer {
|
|||||||
markRecovering: (sessionID: string) => void
|
markRecovering: (sessionID: string) => void
|
||||||
markRecoveryComplete: (sessionID: string) => void
|
markRecoveryComplete: (sessionID: string) => void
|
||||||
cancelAllCountdowns: () => void
|
cancelAllCountdowns: () => void
|
||||||
|
dispose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Todo {
|
export interface Todo {
|
||||||
|
|||||||
Reference in New Issue
Block a user