test(background-agent): add stale detection unit tests
This commit is contained in:
parent
1b6037bbdf
commit
638842966f
@ -1060,3 +1060,253 @@ describe("BackgroundManager process cleanup", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
||||||
|
test("should NOT interrupt task running less than 30 seconds (min runtime guard)", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-1",
|
||||||
|
sessionID: "session-1",
|
||||||
|
parentSessionID: "parent-1",
|
||||||
|
parentMessageID: "msg-1",
|
||||||
|
description: "Test task",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 20_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 0,
|
||||||
|
lastUpdate: new Date(Date.now() - 200_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.status).toBe("running")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should NOT interrupt task with recent lastUpdate", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-2",
|
||||||
|
sessionID: "session-2",
|
||||||
|
parentSessionID: "parent-2",
|
||||||
|
parentMessageID: "msg-2",
|
||||||
|
description: "Test task",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 60_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 5,
|
||||||
|
lastUpdate: new Date(Date.now() - 30_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.status).toBe("running")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should interrupt task with stale lastUpdate (> 3min)", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-3",
|
||||||
|
sessionID: "session-3",
|
||||||
|
parentSessionID: "parent-3",
|
||||||
|
parentMessageID: "msg-3",
|
||||||
|
description: "Stale task",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 300_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 2,
|
||||||
|
lastUpdate: new Date(Date.now() - 200_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.status).toBe("cancelled")
|
||||||
|
expect(task.error).toContain("Stale timeout")
|
||||||
|
expect(task.error).toContain("3min")
|
||||||
|
expect(task.completedAt).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should respect custom staleTimeoutMs config", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 60_000 })
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-4",
|
||||||
|
sessionID: "session-4",
|
||||||
|
parentSessionID: "parent-4",
|
||||||
|
parentMessageID: "msg-4",
|
||||||
|
description: "Custom timeout task",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 120_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 1,
|
||||||
|
lastUpdate: new Date(Date.now() - 90_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.status).toBe("cancelled")
|
||||||
|
expect(task.error).toContain("Stale timeout")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should release concurrency before abort", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-5",
|
||||||
|
sessionID: "session-5",
|
||||||
|
parentSessionID: "parent-5",
|
||||||
|
parentMessageID: "msg-5",
|
||||||
|
description: "Concurrency test",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 300_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 1,
|
||||||
|
lastUpdate: new Date(Date.now() - 200_000),
|
||||||
|
},
|
||||||
|
concurrencyKey: "test-agent",
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.concurrencyKey).toBeUndefined()
|
||||||
|
expect(task.status).toBe("cancelled")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should handle multiple stale tasks in same poll cycle", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
|
||||||
|
const task1: BackgroundTask = {
|
||||||
|
id: "task-6",
|
||||||
|
sessionID: "session-6",
|
||||||
|
parentSessionID: "parent-6",
|
||||||
|
parentMessageID: "msg-6",
|
||||||
|
description: "Stale 1",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 300_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 1,
|
||||||
|
lastUpdate: new Date(Date.now() - 200_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const task2: BackgroundTask = {
|
||||||
|
id: "task-7",
|
||||||
|
sessionID: "session-7",
|
||||||
|
parentSessionID: "parent-7",
|
||||||
|
parentMessageID: "msg-7",
|
||||||
|
description: "Stale 2",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 400_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 2,
|
||||||
|
lastUpdate: new Date(Date.now() - 250_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task1.id, task1)
|
||||||
|
manager["tasks"].set(task2.id, task2)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task1.status).toBe("cancelled")
|
||||||
|
expect(task2.status).toBe("cancelled")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should use default timeout when config not provided", async () => {
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput)
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-8",
|
||||||
|
sessionID: "session-8",
|
||||||
|
parentSessionID: "parent-8",
|
||||||
|
parentMessageID: "msg-8",
|
||||||
|
description: "Default timeout",
|
||||||
|
prompt: "Test",
|
||||||
|
agent: "test-agent",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 300_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 1,
|
||||||
|
lastUpdate: new Date(Date.now() - 200_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
manager["tasks"].set(task.id, task)
|
||||||
|
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
expect(task.status).toBe("cancelled")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user