From d737f2e214e386b9c86dda9bacca29ab3f0a624b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 1 May 2026 02:11:38 +0900 Subject: [PATCH] test(background-agent): remove forbidden assertions in manager tests Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/features/background-agent/manager.test.ts | 145 +++++++++--------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/src/features/background-agent/manager.test.ts b/src/features/background-agent/manager.test.ts index a766b3bbc..04f261dd9 100644 --- a/src/features/background-agent/manager.test.ts +++ b/src/features/background-agent/manager.test.ts @@ -185,6 +185,10 @@ function createMockTask(overrides: Partial & { id: string; paren } } +function cast(value: unknown): T { + return value as T +} + function createBackgroundManager(): BackgroundManager { const client = { session: { @@ -212,56 +216,56 @@ function createBackgroundManagerWithOptions(options: Partial(manager)).concurrencyManager } function getTaskMap(manager: BackgroundManager): Map { - return (manager as unknown as { tasks: Map }).tasks + return (cast<{ tasks: Map }>(manager)).tasks } function getPendingByParent(manager: BackgroundManager): Map> { - return (manager as unknown as { pendingByParent: Map> }).pendingByParent + return (cast<{ pendingByParent: Map> }>(manager)).pendingByParent } function getPendingNotifications(manager: BackgroundManager): Map { - return (manager as unknown as { pendingNotifications: Map }).pendingNotifications + return (cast<{ pendingNotifications: Map }>(manager)).pendingNotifications } function getCompletionTimers(manager: BackgroundManager): Map> { - return (manager as unknown as { completionTimers: Map> }).completionTimers + return (cast<{ completionTimers: Map> }>(manager)).completionTimers } function getRootDescendantCounts(manager: BackgroundManager): Map { - return (manager as unknown as { rootDescendantCounts: Map }).rootDescendantCounts + return (cast<{ rootDescendantCounts: Map }>(manager)).rootDescendantCounts } function getPreStartDescendantReservations(manager: BackgroundManager): Set { - return (manager as unknown as { preStartDescendantReservations: Set }).preStartDescendantReservations + return (cast<{ preStartDescendantReservations: Set }>(manager)).preStartDescendantReservations } function getQueuesByKey( manager: BackgroundManager ): Map> { - return (manager as unknown as { + return (cast<{ queuesByKey: Map> - }).queuesByKey + }>(manager)).queuesByKey } async function processKeyForTest(manager: BackgroundManager, key: string): Promise { - return (manager as unknown as { processKey: (key: string) => Promise }).processKey(key) + return (cast<{ processKey: (key: string) => Promise }>(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 { - return (manager as unknown as { tryCompleteTask: (task: BackgroundTask, source: string) => Promise }) + return (cast<{ tryCompleteTask: (task: BackgroundTask, source: string) => Promise }>(manager)) .tryCompleteTask(task, "test") } function stubNotifyParentSession(manager: BackgroundManager): void { - ;(manager as unknown as { notifyParentSession: () => Promise }).notifyParentSession = async () => {} + ;(cast<{ notifyParentSession: () => Promise }>(manager)).notifyParentSession = async () => {} } async function flushBackgroundNotifications(): Promise { @@ -272,9 +276,9 @@ async function flushBackgroundNotifications(): Promise { function createToastRemoveTaskTracker(): { removeTaskCalls: string[]; resetToastManager: () => void } { _resetTaskToastManagerForTesting() - const toastManager = initTaskToastManager({ + const toastManager = initTaskToastManager(cast({ 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 - }).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 - }).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 - }).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 - }).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 - }).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 - }).startTask(item) + }>(manager)).startTask(item) //#then - const notifications = queuePendingNotification.mock.calls.map((call) => call[1]) + const notifications = cast>(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 - }).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>(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 }) + await (cast<{ notifyParentSession: (value: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }).startTask = async (item) => { + ;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise }>(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 }).startTask = async (item) => { + ;(cast<{ startTask: (item: { task: BackgroundTask; input: typeof input }) => Promise }>(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 - }).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 }, + cast<{ verifySessionExists: (sessionID: string) => Promise }>(manager), "verifySessionExists", ).mockResolvedValue(sessionExists) } const stubProcessKey = (manager: BackgroundManager) => { - ;(manager as unknown as { processKey: (key: string) => Promise }).processKey = async () => {} + ;(cast<{ processKey: (key: string) => Promise }>(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 }).startTask = async () => { + ;(cast<{ startTask: (item: unknown) => Promise }>(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> }).pendingByParent.set( + ;(cast<{ pendingByParent: Map> }>(manager)).pendingByParent.set( "parent-session", new Set([taskA.id, taskB.id]) ) // when - await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }).notifyParentSession(task) + await (cast<{ notifyParentSession: (task: BackgroundTask) => Promise }>(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 }) + await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise }>(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 }) + await (cast<{ startTask: (item: { task: BackgroundTask; input: import("./types").LaunchInput }) => Promise }>(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 - }).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 - }).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 - }).startTask({ task, input, attemptID: "attempt-1" }) + }>(manager)).startTask({ task, input, attemptID: "attempt-1" }) task.attempts = [ {