test(background-agent): remove forbidden assertions in manager tests

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-05-01 02:11:38 +09:00
parent c90be7f9a6
commit d737f2e214
+74 -71
View File
@@ -185,6 +185,10 @@ function createMockTask(overrides: Partial<BackgroundTask> & { id: string; paren
}
}
function cast<T>(value: unknown): T {
return value as T
}
function createBackgroundManager(): BackgroundManager {
const client = {
session: {
@@ -212,56 +216,56 @@ function createBackgroundManagerWithOptions(options: Partial<ConstructorParamete
}
function getConcurrencyManager(manager: BackgroundManager): ConcurrencyManager {
return (manager as unknown as { concurrencyManager: ConcurrencyManager }).concurrencyManager
return (cast<{ concurrencyManager: ConcurrencyManager }>(manager)).concurrencyManager
}
function getTaskMap(manager: BackgroundManager): Map<string, BackgroundTask> {
return (manager as unknown as { tasks: Map<string, BackgroundTask> }).tasks
return (cast<{ tasks: Map<string, BackgroundTask> }>(manager)).tasks
}
function getPendingByParent(manager: BackgroundManager): Map<string, Set<string>> {
return (manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent
return (cast<{ pendingByParent: Map<string, Set<string>> }>(manager)).pendingByParent
}
function getPendingNotifications(manager: BackgroundManager): Map<string, string[]> {
return (manager as unknown as { pendingNotifications: Map<string, string[]> }).pendingNotifications
return (cast<{ pendingNotifications: Map<string, string[]> }>(manager)).pendingNotifications
}
function getCompletionTimers(manager: BackgroundManager): Map<string, ReturnType<typeof setTimeout>> {
return (manager as unknown as { completionTimers: Map<string, ReturnType<typeof setTimeout>> }).completionTimers
return (cast<{ completionTimers: Map<string, ReturnType<typeof setTimeout>> }>(manager)).completionTimers
}
function getRootDescendantCounts(manager: BackgroundManager): Map<string, number> {
return (manager as unknown as { rootDescendantCounts: Map<string, number> }).rootDescendantCounts
return (cast<{ rootDescendantCounts: Map<string, number> }>(manager)).rootDescendantCounts
}
function getPreStartDescendantReservations(manager: BackgroundManager): Set<string> {
return (manager as unknown as { preStartDescendantReservations: Set<string> }).preStartDescendantReservations
return (cast<{ preStartDescendantReservations: Set<string> }>(manager)).preStartDescendantReservations
}
function getQueuesByKey(
manager: BackgroundManager
): Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>> {
return (manager as unknown as {
return (cast<{
queuesByKey: Map<string, Array<{ task: BackgroundTask; input: import("./types").LaunchInput }>>
}).queuesByKey
}>(manager)).queuesByKey
}
async function processKeyForTest(manager: BackgroundManager, key: string): Promise<void> {
return (manager as unknown as { processKey: (key: string) => Promise<void> }).processKey(key)
return (cast<{ processKey: (key: string) => Promise<void> }>(manager)).processKey(key)
}
function pruneStaleTasksAndNotificationsForTest(manager: BackgroundManager): void {
;(manager as unknown as { pruneStaleTasksAndNotifications: () => void }).pruneStaleTasksAndNotifications()
;(cast<{ pruneStaleTasksAndNotifications: () => void }>(manager)).pruneStaleTasksAndNotifications()
}
async function tryCompleteTaskForTest(manager: BackgroundManager, task: BackgroundTask): Promise<boolean> {
return (manager as unknown as { tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean> })
return (cast<{ tryCompleteTask: (task: BackgroundTask, source: string) => Promise<boolean> }>(manager))
.tryCompleteTask(task, "test")
}
function stubNotifyParentSession(manager: BackgroundManager): void {
;(manager as unknown as { notifyParentSession: () => Promise<void> }).notifyParentSession = async () => {}
;(cast<{ notifyParentSession: () => Promise<void> }>(manager)).notifyParentSession = async () => {}
}
async function flushBackgroundNotifications(): Promise<void> {
@@ -272,9 +276,9 @@ async function flushBackgroundNotifications(): Promise<void> {
function createToastRemoveTaskTracker(): { removeTaskCalls: string[]; resetToastManager: () => void } {
_resetTaskToastManagerForTesting()
const toastManager = initTaskToastManager({
const toastManager = initTaskToastManager(cast<PluginInput["client"]>({
tui: { showToast: async () => {} },
} as unknown as PluginInput["client"])
}))
const removeTaskCalls: string[] = []
const originalRemoveTask = toastManager.removeTask.bind(toastManager)
toastManager.removeTask = (taskId: string): void => {
@@ -311,22 +315,22 @@ describe("BackgroundManager session.error fallback hydration", () => {
fallbackChain: undefined,
})
let capturedFallbackChain: BackgroundTask["fallbackChain"]
;(manager as unknown as {
;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (retryTask) => {
}>(manager)).tryFallbackRetry = async (retryTask) => {
capturedFallbackChain = retryTask.fallbackChain
return true
}
//#when
await (manager as unknown as {
await (cast<{
handleSessionErrorEvent: (args: {
task: BackgroundTask
errorInfo: { name?: string; message?: string }
errorName: string | undefined
errorMessage: string | undefined
}) => Promise<void>
}).handleSessionErrorEvent({
}>(manager)).handleSessionErrorEvent({
task,
errorInfo: {
name: "APIError",
@@ -362,23 +366,23 @@ describe("BackgroundManager prompt rejection fallback routing", () => {
}
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager)
;(manager as unknown as {
;(cast<{
reserveSubagentSpawn: () => Promise<{
spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number }
descendantCount: number
commit: () => number
rollback: () => void
}>
}).reserveSubagentSpawn = async () => ({
}>(manager)).reserveSubagentSpawn = async () => ({
spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 },
descendantCount: 1,
commit: () => 1,
rollback: () => {},
})
const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = []
;(manager as unknown as {
;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (task, errorInfo, source) => {
}>(manager)).tryFallbackRetry = async (task, errorInfo, source) => {
retried.push({ taskId: task.id, errorInfo, source })
task.status = "pending"
task.error = undefined
@@ -441,9 +445,9 @@ describe("BackgroundManager prompt rejection fallback routing", () => {
}
getTaskMap(manager).set(task.id, task)
const retried: Array<{ taskId: string; errorInfo: { name?: string; message?: string }; source: string }> = []
;(manager as unknown as {
;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async (retryTask, errorInfo, source) => {
}>(manager)).tryFallbackRetry = async (retryTask, errorInfo, source) => {
retried.push({ taskId: retryTask.id, errorInfo, source })
retryTask.status = "pending"
retryTask.error = undefined
@@ -505,9 +509,9 @@ describe("BackgroundManager retry observability", () => {
}).queuePendingNotification = queuePendingNotification
//#when
await (manager as unknown as {
await (cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry(task, {
}>(manager)).tryFallbackRetry(task, {
name: "APIError",
message: "Forbidden: Selected provider is forbidden",
}, "promptAsync.launch")
@@ -591,12 +595,12 @@ describe("BackgroundManager retry observability", () => {
}
//#when
await (manager as unknown as {
await (cast<{
startTask: (queueItem: RetryReadyQueueItem) => Promise<void>
}).startTask(item)
}>(manager)).startTask(item)
//#then
const notifications = queuePendingNotification.mock.calls.map((call) => call[1])
const notifications = cast<Array<[string | undefined, string]>>(queuePendingNotification.mock.calls).map((call) => call[1])
const retryReadyNotification = notifications.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]"))
const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(tmpdir()).toString("base64url")}/session/ses_retry_created`
expect(retryReadyNotification).toBeDefined()
@@ -667,14 +671,14 @@ describe("BackgroundManager retry observability", () => {
}
//#when
await (manager as unknown as {
await (cast<{
startTask: (queueItem: { task: BackgroundTask; input: typeof taskInput; attemptID: string }) => Promise<void>
}).startTask({ task, input: taskInput, attemptID: "att_retry_ready_parent_dir" })
}>(manager)).startTask({ task, input: taskInput, attemptID: "att_retry_ready_parent_dir" })
//#then
const retryReadyNotification = queuePendingNotification.mock.calls
.map((call) => call[1])
.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]"))
const retryReadyNotification = cast<Array<[string | undefined, string]>>(queuePendingNotification.mock.calls)
.map((call) => call[1])
.find((notification) => notification.includes("[BACKGROUND TASK RETRY SESSION READY]"))
const expectedRetryLink = `http://127.0.0.1:4096/${Buffer.from(parentDirectory).toString("base64url")}/session/ses_retry_created_parent_dir`
expect(retryReadyNotification).toBeDefined()
expect(retryReadyNotification).toContain(expectedRetryLink)
@@ -1293,7 +1297,7 @@ describe("BackgroundManager.notifyParentSession - dynamic message lookup", () =>
getPendingByParent(manager).set("session-parent", new Set([task.id, "still-running"]))
//#when
await (manager as unknown as { notifyParentSession: (value: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (value: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1449,7 +1453,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id, "task-remaining"]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1491,7 +1495,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1531,7 +1535,7 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1589,7 +1593,7 @@ describe("BackgroundManager.notifyParentSession - notifications toggle", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1642,7 +1646,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1683,7 +1687,7 @@ describe("BackgroundManager.notifyParentSession - variant propagation", () => {
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(task)
//#then
@@ -1935,7 +1939,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
getTaskMap(manager).set(task.id, task)
getQueuesByKey(manager).set(concurrencyKey, [{ task, input }])
;(manager as unknown as { startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }).startTask = async (item) => {
;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }>(manager)).startTask = async (item) => {
item.task.concurrencyKey = concurrencyKey
throw new Error("startTask failed after assigning concurrencyKey")
}
@@ -1972,7 +1976,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
getTaskMap(manager).set(task.id, task)
getQueuesByKey(manager).set(concurrencyKey, [{ task, input }])
;(manager as unknown as { startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }).startTask = async (item) => {
;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void> }>(manager)).startTask = async (item) => {
item.task.status = "running"
item.task.sessionId = "ses_zombie_child"
item.task.startedAt = new Date()
@@ -2957,9 +2961,9 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
getPreStartDescendantReservations(manager).add(task.id)
stubNotifyParentSession(manager)
;(manager as unknown as {
;(cast<{
startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise<void>
}).startTask = async () => {
}>(manager)).startTask = async () => {
throw new Error("session create failed")
}
@@ -3455,7 +3459,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
parentMessageId: "parent-message",
}
const task1 = await manager.launch(input)
await manager.launch(input)
const task2 = await manager.launch(input)
await new Promise(resolve => setTimeout(resolve, 50))
@@ -3509,7 +3513,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
parentMessageId: "parent-message",
}
const task1 = await manager.launch(input)
await manager.launch(input)
const task2 = await manager.launch(input)
const task3 = await manager.launch(input)
await new Promise(resolve => setTimeout(resolve, 100))
@@ -3517,9 +3521,9 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
// when - cancel middle task
const cancelledTask2 = manager.getTask(task2.id)
expect(cancelledTask2?.status).toBe("pending")
manager.cancelPendingTask(task2.id)
const afterCancel = manager.getTask(task2.id)
expect(afterCancel?.status).toBe("cancelled")
@@ -4645,13 +4649,13 @@ describe("BackgroundManager.handleEvent - session.error", () => {
const mockVerifySessionExists = (manager: BackgroundManager, sessionExists: boolean): void => {
verifySessionExistsSpy?.mockRestore()
verifySessionExistsSpy = spyOn(
manager as unknown as { verifySessionExists: (sessionID: string) => Promise<boolean> },
cast<{ verifySessionExists: (sessionID: string) => Promise<boolean> }>(manager),
"verifySessionExists",
).mockResolvedValue(sessionExists)
}
const stubProcessKey = (manager: BackgroundManager) => {
;(manager as unknown as { processKey: (key: string) => Promise<void> }).processKey = async () => {}
;(cast<{ processKey: (key: string) => Promise<void> }>(manager)).processKey = async () => {}
}
const createRetryTask = (manager: BackgroundManager, input: {
@@ -4802,7 +4806,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({
type: "session.error",
properties: {
sessionId: "ses_unknown",
sessionID: "ses_unknown",
error: { name: "UnknownError", message: "Model not found" },
},
})
@@ -4833,7 +4837,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({
type: "session.error",
properties: {
sessionId: task.sessionId,
sessionID: task.sessionId,
error: {
name: "UnknownError",
message: "Out of memory",
@@ -4873,7 +4877,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.handleEvent({
type: "session.error",
properties: {
sessionId: task.sessionId,
sessionID: task.sessionId,
error: {
name: "UnknownError",
message: "Out of memory",
@@ -5115,7 +5119,7 @@ describe("BackgroundManager queue processing - error tasks are skipped", () => {
}
let startCalled = false
;(manager as unknown as { startTask: (item: unknown) => Promise<void> }).startTask = async () => {
;(cast<{ startTask: (item: unknown) => Promise<void> }>(manager)).startTask = async () => {
startCalled = true
}
@@ -5302,13 +5306,13 @@ describe("BackgroundManager.completionTimers - Memory Leak Fix", () => {
}
getTaskMap(manager).set(taskA.id, taskA)
getTaskMap(manager).set(taskB.id, taskB)
;(manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent.set(
;(cast<{ pendingByParent: Map<string, Set<string>> }>(manager)).pendingByParent.set(
"parent-session",
new Set([taskA.id, taskB.id])
)
// when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(taskA)
// then
@@ -5316,7 +5320,7 @@ describe("BackgroundManager.completionTimers - Memory Leak Fix", () => {
expect(completionTimers.size).toBe(1)
// when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager))
.notifyParentSession(taskB)
// then
@@ -5423,7 +5427,6 @@ describe("BackgroundManager.handleEvent - early session.idle deferral", () => {
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager)
const remainingMs = 1200
const task: BackgroundTask = {
id: "task-early-idle",
sessionId: sessionID,
@@ -5910,7 +5913,7 @@ describe("BackgroundManager regression fixes - resume and aborted notification",
getPendingByParent(manager).set(task.parentSessionId, new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> }).notifyParentSession(task)
await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise<void> }>(manager)).notifyParentSession(task)
//#then
expect(getCompletionTimers(manager).has(task.id)).toBe(true)
@@ -5953,7 +5956,7 @@ describe("BackgroundManager - tool permission spread order", () => {
}
//#when
await (manager as unknown as { startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> })
await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }>(manager))
.startTask({ task, input })
//#then
@@ -6001,7 +6004,7 @@ describe("BackgroundManager - tool permission spread order", () => {
}
//#when
await (manager as unknown as { startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> })
await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise<void> }>(manager))
.startTask({ task, input })
//#then
@@ -6107,14 +6110,14 @@ describe("BackgroundManager.launch - attempt state initialization", () => {
test("newly launched task has attempt state with attemptNumber 1 and currentAttemptID pointing at it", async () => {
//#given
const manager = createBackgroundManager()
;(manager as unknown as {
;(cast<{
reserveSubagentSpawn: () => Promise<{
spawnContext: { rootSessionID: string; parentDepth: number; childDepth: number }
descendantCount: number
commit: () => number
rollback: () => void
}>
}).reserveSubagentSpawn = async () => ({
}>(manager)).reserveSubagentSpawn = async () => ({
spawnContext: { rootSessionID: "parent-session", parentDepth: 0, childDepth: 1 },
descendantCount: 1,
commit: () => 1,
@@ -6210,9 +6213,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
}
//#when
await (manager as unknown as {
await (cast<{
startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void>
}).startTask({ task, input, attemptID: "attempt-2" })
}>(manager)).startTask({ task, input, attemptID: "attempt-2" })
//#then
const activeAttempt = task.attempts?.find((attempt) => attempt.attemptId === "attempt-2")
@@ -6324,9 +6327,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
}
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
stubNotifyParentSession(manager)
;(manager as unknown as {
;(cast<{
tryFallbackRetry: (task: BackgroundTask, errorInfo: { name?: string; message?: string }, source: string) => Promise<boolean>
}).tryFallbackRetry = async () => false
}>(manager)).tryFallbackRetry = async () => false
const task: BackgroundTask = {
id: "task-stale-prompt-error",
status: "pending",
@@ -6358,9 +6361,9 @@ describe("BackgroundManager attempt lifecycle bindings", () => {
model: task.model,
}
await (manager as unknown as {
await (cast<{
startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput; attemptID: string }) => Promise<void>
}).startTask({ task, input, attemptID: "attempt-1" })
}>(manager)).startTask({ task, input, attemptID: "attempt-1" })
task.attempts = [
{