test(anthropic-recovery): cover recovery hook timer disposal

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-04 15:39:28 +09:00
parent 2b55f65c36
commit 5e8a0ccf84
2 changed files with 48 additions and 1 deletions
@@ -75,6 +75,7 @@ export function createMockContext(): PluginInput {
export function setupDelayedTimeoutMocks(): {
createUntrackedTimeout: () => ReturnType<typeof setTimeout>
runScheduledTimeout: (index: number) => void
restore: () => void
getClearTimeoutCalls: () => Array<ReturnType<typeof setTimeout>>
getScheduledTimeouts: () => Array<ReturnType<typeof setTimeout>>
@@ -83,6 +84,7 @@ export function setupDelayedTimeoutMocks(): {
const originalClearTimeout = globalThis.clearTimeout
const clearTimeoutCalls: Array<ReturnType<typeof setTimeout>> = []
const scheduledTimeouts: Array<ReturnType<typeof setTimeout>> = []
const scheduledCallbacks: Array<() => void> = []
function createTimeoutHandle(): ReturnType<typeof setTimeout> {
const timeoutID = originalSetTimeout(() => {}, 60_000)
@@ -90,9 +92,10 @@ export function setupDelayedTimeoutMocks(): {
return timeoutID
}
globalThis.setTimeout = ((_: () => void, _delay?: number) => {
globalThis.setTimeout = ((callback: () => void, _delay?: number) => {
const timeoutID = createTimeoutHandle()
scheduledTimeouts.push(timeoutID)
scheduledCallbacks.push(callback)
return timeoutID
}) as typeof setTimeout
@@ -103,6 +106,9 @@ export function setupDelayedTimeoutMocks(): {
return {
createUntrackedTimeout: createTimeoutHandle,
runScheduledTimeout: (index: number) => {
scheduledCallbacks[index]?.()
},
restore: () => {
globalThis.setTimeout = originalSetTimeout
globalThis.clearTimeout = originalClearTimeout
@@ -94,4 +94,45 @@ describe("createAnthropicContextWindowLimitRecoveryHook", () => {
}
})
test("#given active pending and retry timers #when dispose is called #then it clears both timer maps", async () => {
//#given
const { createUntrackedTimeout, getClearTimeoutCalls, getScheduledTimeouts, restore, runScheduledTimeout } =
setupDelayedTimeoutMocks()
executeCompactMock.mockImplementationOnce(async (...args: Parameters<typeof executeCompactMock>) => {
const sessionID = args[0]
const autoCompactState = args[2]
autoCompactState.retryTimerBySession.set(sessionID, createUntrackedTimeout())
})
const hook = createRecoveryHook()
try {
await hook.event({
event: {
type: "session.error",
properties: { sessionID: "session-retry", error: "prompt is too long" },
},
})
await hook.event({
event: {
type: "session.error",
properties: { sessionID: "session-pending", error: "prompt is too long" },
},
})
runScheduledTimeout(0)
const [retryTimer, pendingTimer] = getScheduledTimeouts()
//#when
hook.dispose()
//#then
expect(getClearTimeoutCalls()).toEqual(expect.arrayContaining([retryTimer, pendingTimer]))
} finally {
restore()
}
})
})