diff --git a/src/features/task-toast-manager/manager.test.ts b/src/features/task-toast-manager/manager.test.ts index f6a24111a..796bb9ad2 100644 --- a/src/features/task-toast-manager/manager.test.ts +++ b/src/features/task-toast-manager/manager.test.ts @@ -1,17 +1,20 @@ -import { describe, test, expect, beforeEach, mock } from "bun:test" -import { TaskToastManager } from "./manager" +declare const require: (name: string) => any +const { describe, test, expect, beforeEach, afterEach, mock } = require("bun:test") import type { ConcurrencyManager } from "../background-agent/concurrency" +type TaskToastManagerClass = typeof import("./manager").TaskToastManager + describe("TaskToastManager", () => { + let TaskToastManager: TaskToastManagerClass let mockClient: { tui: { showToast: ReturnType } } - let toastManager: TaskToastManager + let toastManager: InstanceType let mockConcurrencyManager: ConcurrencyManager - beforeEach(() => { + beforeEach(async () => { mockClient = { tui: { showToast: mock(() => Promise.resolve()), @@ -20,10 +23,18 @@ describe("TaskToastManager", () => { mockConcurrencyManager = { getConcurrencyLimit: mock(() => 5), } as unknown as ConcurrencyManager + + const mod = await import("./manager") + TaskToastManager = mod.TaskToastManager + // eslint-disable-next-line @typescript-eslint/no-explicit-any toastManager = new TaskToastManager(mockClient as any, mockConcurrencyManager) }) + afterEach(() => { + mock.restore() + }) + describe("skills in toast message", () => { test("should display skills when provided", () => { // given - a task with skills diff --git a/src/features/task-toast-manager/manager.ts b/src/features/task-toast-manager/manager.ts index 5ae8dc6d5..5115fae79 100644 --- a/src/features/task-toast-manager/manager.ts +++ b/src/features/task-toast-manager/manager.ts @@ -217,3 +217,7 @@ export function initTaskToastManager( instance = new TaskToastManager(client, concurrencyManager) return instance } + +export function _resetTaskToastManagerForTesting(): void { + instance = null +} diff --git a/src/tools/delegate-task/category-resolver.test.ts b/src/tools/delegate-task/category-resolver.test.ts index febfe3f16..f0b8d2daa 100644 --- a/src/tools/delegate-task/category-resolver.test.ts +++ b/src/tools/delegate-task/category-resolver.test.ts @@ -1,8 +1,24 @@ -import { describe, test, expect } from "bun:test" +declare const require: (name: string) => any +const { describe, test, expect, beforeEach, afterEach, spyOn, mock } = require("bun:test") import { resolveCategoryExecution } from "./category-resolver" import type { ExecutorContext } from "./executor-types" +import * as connectedProvidersCache from "../../shared/connected-providers-cache" describe("resolveCategoryExecution", () => { + let connectedProvidersSpy: ReturnType | undefined + let providerModelsSpy: ReturnType | undefined + + beforeEach(() => { + mock.restore() + connectedProvidersSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(null) + providerModelsSpy = spyOn(connectedProvidersCache, "readProviderModelsCache").mockReturnValue(null) + }) + + afterEach(() => { + connectedProvidersSpy?.mockRestore() + providerModelsSpy?.mockRestore() + }) + const createMockExecutorContext = (): ExecutorContext => ({ client: {} as any, manager: {} as any, diff --git a/src/tools/delegate-task/sync-continuation.test.ts b/src/tools/delegate-task/sync-continuation.test.ts index 80a6f9cb5..58ffd58cf 100644 --- a/src/tools/delegate-task/sync-continuation.test.ts +++ b/src/tools/delegate-task/sync-continuation.test.ts @@ -1,9 +1,9 @@ -declare const require: (name: string) => any -const { describe, test, expect, beforeEach, afterEach, mock } = require("bun:test") +const { describe, test, expect, beforeEach, afterEach, mock, spyOn } = require("bun:test") describe("executeSyncContinuation - toast cleanup error paths", () => { let removeTaskCalls: string[] = [] let addTaskCalls: any[] = [] + let resetToastManager: (() => void) | null = null beforeEach(() => { //#given - configure fast timing for all tests @@ -19,19 +19,21 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { removeTaskCalls = [] addTaskCalls = [] - //#given - mock task-toast-manager module - const mockToastManager = { - addTask: (task: any) => { addTaskCalls.push(task) }, - removeTask: (id: string) => { removeTaskCalls.push(id) }, - } + //#given - initialize real task toast manager (avoid global module mocks) + const { initTaskToastManager, _resetTaskToastManagerForTesting } = require("../../features/task-toast-manager/manager") + _resetTaskToastManagerForTesting() + resetToastManager = _resetTaskToastManagerForTesting - const mockGetTaskToastManager = () => mockToastManager + const toastManager = initTaskToastManager({ + tui: { showToast: mock(() => Promise.resolve()) }, + }) - mock.module("../../features/task-toast-manager/index.ts", () => ({ - getTaskToastManager: mockGetTaskToastManager, - TaskToastManager: class {}, - initTaskToastManager: () => mockToastManager, - })) + spyOn(toastManager, "addTask").mockImplementation((task: any) => { + addTaskCalls.push(task) + }) + spyOn(toastManager, "removeTask").mockImplementation((id: string) => { + removeTaskCalls.push(id) + }) //#given - mock other dependencies mock.module("./sync-session-poller.ts", () => ({ @@ -48,7 +50,9 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { const { __resetTimingConfig } = require("./timing") __resetTimingConfig() - mock.restore() + mock.restore() + resetToastManager?.() + resetToastManager = null }) test("removes toast when fetchSyncResult throws", async () => { @@ -294,14 +298,9 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { }) test("no crash when toastManager is null", async () => { - //#given - mock task-toast-manager module to return null - const mockGetTaskToastManager = () => null - - mock.module("../../features/task-toast-manager/index.ts", () => ({ - getTaskToastManager: mockGetTaskToastManager, - TaskToastManager: class {}, - initTaskToastManager: () => null, - })) + //#given - reset toast manager instance to null + const { _resetTaskToastManagerForTesting } = require("../../features/task-toast-manager/manager") + _resetTaskToastManagerForTesting() const mockClient = { session: { diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index 923a57c04..65818f447 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -1132,27 +1132,50 @@ describe("sisyphus-task", () => { launch: async () => mockTask, } + let messagesCallCount = 0 + const mockClient = { - session: { - prompt: async () => ({ data: {} }), - promptAsync: async () => ({ data: {} }), - messages: async () => ({ - data: [ - { - info: { id: "msg_001", role: "user", time: { created: Date.now() } }, - parts: [{ type: "text", text: "Continue the task" }], - }, - { - info: { id: "msg_002", role: "assistant", time: { created: Date.now() + 1 }, finish: "end_turn" }, - parts: [{ type: "text", text: "This is the continued task result" }], - }, - ], - }), - status: async () => ({ data: { "ses_continue_test": { type: "idle" } } }), - }, - config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, - app: { - agents: async () => ({ data: [] }), + session: { + prompt: async () => ({ data: {} }), + promptAsync: async () => ({ data: {} }), + messages: async () => { + messagesCallCount++ + const now = Date.now() + + const beforeContinuation = [ + { + info: { id: "msg_001", role: "user", time: { created: now } }, + parts: [{ type: "text", text: "Previous context" }], + }, + { + info: { id: "msg_002", role: "assistant", time: { created: now + 1 }, finish: "end_turn" }, + parts: [{ type: "text", text: "Previous result" }], + }, + ] + + if (messagesCallCount === 1) { + return { data: beforeContinuation } + } + + return { + data: [ + ...beforeContinuation, + { + info: { id: "msg_003", role: "user", time: { created: now + 2 } }, + parts: [{ type: "text", text: "Continue the task" }], + }, + { + info: { id: "msg_004", role: "assistant", time: { created: now + 3 }, finish: "end_turn" }, + parts: [{ type: "text", text: "This is the continued task result" }], + }, + ], + } + }, + status: async () => ({ data: { "ses_continue_test": { type: "idle" } } }), + }, + config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) }, + app: { + agents: async () => ({ data: [] }), }, }