diff --git a/src/cli/config-manager/npm-dist-tags.test.ts b/src/cli/config-manager/npm-dist-tags.test.ts index 3de417290..db592c363 100644 --- a/src/cli/config-manager/npm-dist-tags.test.ts +++ b/src/cli/config-manager/npm-dist-tags.test.ts @@ -13,12 +13,12 @@ describe("fetchNpmDistTags", () => { test("returns dist-tags on success", async () => { //#given - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ latest: "3.13.1", beta: "3.14.0-beta.1" }), } as Response) - ) as unknown as typeof fetch + )) //#when const result = await fetchNpmDistTags("oh-my-openagent") @@ -29,7 +29,7 @@ describe("fetchNpmDistTags", () => { test("returns null on network failure", async () => { //#given - globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch + globalThis.fetch = testCoerce(mock(() => Promise.reject(new Error("Network error")))) //#when const result = await fetchNpmDistTags("oh-my-openagent") @@ -40,12 +40,12 @@ describe("fetchNpmDistTags", () => { test("returns null on non-ok response", async () => { //#given - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: false, status: 404, } as Response) - ) as unknown as typeof fetch + )) //#when const result = await fetchNpmDistTags("oh-my-openagent") diff --git a/src/cli/config-manager/opencode-binary.test.ts b/src/cli/config-manager/opencode-binary.test.ts index 27171db5b..2bc2f8b3f 100644 --- a/src/cli/config-manager/opencode-binary.test.ts +++ b/src/cli/config-manager/opencode-binary.test.ts @@ -92,12 +92,12 @@ describe("getOpenCodeVersion (installer)", () => { }), ) - const immediateSetTimeout = ((handler: TimerHandler) => { + const immediateSetTimeout = testCoerce(((handler: TimerHandler) => { if (typeof handler === "function") { handler() } - return 1 as unknown as ReturnType - }) as unknown as typeof globalThis.setTimeout + return testCoerce>(1) + })) const setTimeoutSpy = spyOn(globalThis, "setTimeout").mockImplementation(immediateSetTimeout) const result = await getOpenCodeVersion() @@ -124,12 +124,12 @@ describe("getOpenCodeVersion (installer)", () => { }), ) - const immediateSetTimeout = ((handler: TimerHandler) => { + const immediateSetTimeout = testCoerce(((handler: TimerHandler) => { if (typeof handler === "function") { handler() } - return 1 as unknown as ReturnType - }) as unknown as typeof globalThis.setTimeout + return testCoerce>(1) + })) const setTimeoutSpy = spyOn(globalThis, "setTimeout").mockImplementation(immediateSetTimeout) const result = await getOpenCodeVersion() diff --git a/src/cli/config-manager/plugin-name-with-version.test.ts b/src/cli/config-manager/plugin-name-with-version.test.ts index 7da003338..ca1a92f4d 100644 --- a/src/cli/config-manager/plugin-name-with-version.test.ts +++ b/src/cli/config-manager/plugin-name-with-version.test.ts @@ -13,12 +13,12 @@ describe("getPluginNameWithVersion", () => { test("returns the canonical latest tag when current version matches latest", async () => { //#given - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ latest: "3.13.1", beta: "3.14.0-beta.1" }), } as Response) - ) as unknown as typeof fetch + )) //#when const result = await getPluginNameWithVersion("3.13.1") @@ -29,7 +29,7 @@ describe("getPluginNameWithVersion", () => { test("preserves the canonical prerelease channel when fetch fails", async () => { //#given - globalThis.fetch = mock(() => Promise.reject(new Error("Network error"))) as unknown as typeof fetch + globalThis.fetch = testCoerce(mock(() => Promise.reject(new Error("Network error")))) //#when const result = await getPluginNameWithVersion("3.14.0-beta.1") @@ -40,12 +40,12 @@ describe("getPluginNameWithVersion", () => { test("returns the canonical bare package name for stable fallback", async () => { //#given - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: false, status: 404, } as Response) - ) as unknown as typeof fetch + )) //#when const result = await getPluginNameWithVersion("3.13.1") diff --git a/src/cli/install.test.ts b/src/cli/install.test.ts index 61bcf645f..b8e858ba1 100644 --- a/src/cli/install.test.ts +++ b/src/cli/install.test.ts @@ -57,12 +57,12 @@ describe("install CLI - binary check behavior", () => { getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue(null) // given mock npm fetch - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ latest: "3.0.0" }), } as Response) - ) as unknown as typeof fetch + )) const args: InstallArgs = { tui: false, @@ -92,12 +92,12 @@ describe("install CLI - binary check behavior", () => { getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue(null) // given mock npm fetch - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ latest: "3.0.0" }), } as Response) - ) as unknown as typeof fetch + )) const args: InstallArgs = { tui: false, @@ -131,12 +131,12 @@ describe("install CLI - binary check behavior", () => { getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0") // given mock npm fetch - globalThis.fetch = mock(() => + globalThis.fetch = testCoerce(mock(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ latest: "3.0.0" }), } as Response) - ) as unknown as typeof fetch + )) const args: InstallArgs = { tui: false, diff --git a/src/cli/run/completion-continuation.test.ts b/src/cli/run/completion-continuation.test.ts index 6fd553527..f859923d3 100644 --- a/src/cli/run/completion-continuation.test.ts +++ b/src/cli/run/completion-continuation.test.ts @@ -26,7 +26,7 @@ function createTempDir(): string { function createMockContext(directory: string): RunContext { return { - client: { + client: testCoerce({ session: { todo: mock(() => Promise.resolve({ data: [] })), children: mock(() => Promise.resolve({ data: [] })), @@ -39,7 +39,7 @@ function createMockContext(directory: string): RunContext { })), messages: mock(async () => ({ data: [] })), }, - } as unknown as RunContext["client"], + }), sessionID: "test-session", directory, abortController: new AbortController(), @@ -155,17 +155,17 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "child-session" setSessionAgent("child-session", "atlas") - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "child-session" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "child-session" ? [{ info: { agent: "atlas", providerID: "openai", modelID: "gpt-5.4" } }] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -187,13 +187,13 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "lineage-only-session" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "lineage-only-session" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async () => ({ data: [] })) as unknown as RunContext["client"]["session"]["messages"] + }))) + ctx.client.session.messages = testCoerce(mock(async () => ({ data: [] }))) const { checkCompletionConditions } = await import("./completion") @@ -218,17 +218,17 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "mismatch-subagent-session" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "mismatch-subagent-session" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "mismatch-subagent-session" ? [{ info: { agent: "sisyphus-junior", providerID: "openai", modelID: "gpt-5.4" } }] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -253,17 +253,17 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "appended-mismatch-session" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "appended-mismatch-session" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "appended-mismatch-session" ? [{ info: { agent: "sisyphus-junior", providerID: "openai", modelID: "gpt-5.4" } }] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -288,14 +288,14 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_appended_descendant" - ctx.client.session.get = mock(async () => { + ctx.client.session.get = testCoerce(mock(async () => { throw new Error("session lookup failed") - }) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + })) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "ses_appended_descendant" ? [{ info: { agent: "atlas", providerID: "openai", modelID: "gpt-5.4" } }] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -317,12 +317,12 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_direct_child" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "ses_direct_child" ? "ses_parent" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -347,12 +347,12 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_direct_tracked" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -374,9 +374,9 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_unknown_child" - ctx.client.session.get = mock(async () => { + ctx.client.session.get = testCoerce(mock(async () => { throw new Error("lineage unavailable") - }) as unknown as RunContext["client"]["session"]["get"] + })) const { checkCompletionConditions } = await import("./completion") @@ -401,17 +401,17 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_direct_child" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "ses_direct_child" ? "ses_root_tracked" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "ses_direct_child" ? [{ info: { agent: "sisyphus-junior", providerID: "openai", modelID: "gpt-5.4" } }] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -437,20 +437,20 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_child_after_compaction" setSessionAgent("ses_child_after_compaction", "atlas") - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "ses_child_after_compaction" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "ses_child_after_compaction" ? [ { info: { agent: "atlas", providerID: "openai", modelID: "gpt-5.4" } }, { info: { agent: "compaction", providerID: "openai", modelID: "gpt-5.4" } }, ] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -472,13 +472,13 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_sqlite_descendant" - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "ses_sqlite_descendant" ? "root-session" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async ({ path }: { path: { id: string } }) => ({ + }))) + ctx.client.session.messages = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: path.id === "ses_sqlite_descendant" ? [ { id: "msg_0001", info: { agent: "atlas", providerID: "openai", modelID: "gpt-5.4", time: { created: 100 } } }, @@ -486,7 +486,7 @@ describe("checkCompletionConditions continuation coverage", () => { { id: "msg_0002", info: { agent: "sisyphus-junior", providerID: "openai", modelID: "gpt-5.4", time: { created: 100 } } }, ] : [], - })) as unknown as RunContext["client"]["session"]["messages"] + }))) const { checkCompletionConditions } = await import("./completion") @@ -512,13 +512,13 @@ describe("checkCompletionConditions continuation coverage", () => { const ctx = createMockContext(directory) ctx.sessionID = "ses_appended_child" setSessionAgent("ses_appended_child", "atlas") - ctx.client.session.get = mock(async ({ path }: { path: { id: string } }) => ({ + ctx.client.session.get = testCoerce(mock(async ({ path }: { path: { id: string } }) => ({ data: { id: path.id, parentID: path.id === "ses_appended_child" ? "ses_root_tracked" : undefined, }, - })) as unknown as RunContext["client"]["session"]["get"] - ctx.client.session.messages = mock(async () => ({ data: [] })) as unknown as RunContext["client"]["session"]["messages"] + }))) + ctx.client.session.messages = testCoerce(mock(async () => ({ data: [] }))) const { checkCompletionConditions } = await import("./completion") diff --git a/src/cli/run/completion-verbose-logging.test.ts b/src/cli/run/completion-verbose-logging.test.ts index ff9adfcf4..80db21e65 100644 --- a/src/cli/run/completion-verbose-logging.test.ts +++ b/src/cli/run/completion-verbose-logging.test.ts @@ -13,7 +13,7 @@ const createMockContext = (overrides: { } = overrides return { - client: { + client: testCoerce({ session: { todo: mock(() => Promise.resolve({ data: [] })), children: mock((opts: { path: { id: string } }) => @@ -21,7 +21,7 @@ const createMockContext = (overrides: { ), status: mock(() => Promise.resolve({ data: statuses })), }, - } as unknown as RunContext["client"], + }), sessionID: "test-session", directory: "/test", abortController: new AbortController(), diff --git a/src/cli/run/completion.test.ts b/src/cli/run/completion.test.ts index 1537d318d..56916b9a0 100644 --- a/src/cli/run/completion.test.ts +++ b/src/cli/run/completion.test.ts @@ -13,7 +13,7 @@ const createMockContext = (overrides: { } = overrides return { - client: { + client: testCoerce({ session: { todo: mock(() => Promise.resolve({ data: todo })), children: mock((opts: { path: { id: string } }) => @@ -21,7 +21,7 @@ const createMockContext = (overrides: { ), status: mock(() => Promise.resolve({ data: statuses })), }, - } as unknown as RunContext["client"], + }), sessionID: "test-session", directory: "/test", abortController: new AbortController(), diff --git a/src/cli/run/event-handlers.test.ts b/src/cli/run/event-handlers.test.ts index b6687cf7d..eae1911a4 100644 --- a/src/cli/run/event-handlers.test.ts +++ b/src/cli/run/event-handlers.test.ts @@ -23,7 +23,7 @@ describe("handleSessionStatus", () => { } //#when - handleSessionStatus called with idle status - handleSessionStatus(ctx, payload as any, state) + handleSessionStatus(ctx, testCoerce(payload), state) //#then - state.mainSessionIdle === true expect(state.mainSessionIdle).toBe(true) @@ -44,7 +44,7 @@ describe("handleSessionStatus", () => { } //#when - handleSessionStatus called with busy status - handleSessionStatus(ctx, payload as any, state) + handleSessionStatus(ctx, testCoerce(payload), state) //#then - state.mainSessionIdle === false expect(state.mainSessionIdle).toBe(false) @@ -65,7 +65,7 @@ describe("handleSessionStatus", () => { } //#when - handleSessionStatus called with different session ID - handleSessionStatus(ctx, payload as any, state) + handleSessionStatus(ctx, testCoerce(payload), state) //#then - state.mainSessionIdle remains unchanged expect(state.mainSessionIdle).toBe(true) @@ -86,7 +86,7 @@ describe("handleSessionStatus", () => { } //#when - handleSessionStatus called with camelCase sessionId - handleSessionStatus(ctx, payload as any, state) + handleSessionStatus(ctx, testCoerce(payload), state) //#then - state.mainSessionIdle === true expect(state.mainSessionIdle).toBe(true) @@ -114,7 +114,7 @@ describe("handleMessagePartUpdated", () => { } //#when - handleMessagePartUpdated(ctx, payload as any, state) + handleMessagePartUpdated(ctx, testCoerce(payload), state) //#then expect(state.hasReceivedMeaningfulWork).toBe(true) @@ -142,7 +142,7 @@ describe("handleMessagePartUpdated", () => { } //#when - handleMessagePartUpdated(ctx, payload as any, state) + handleMessagePartUpdated(ctx, testCoerce(payload), state) //#then expect(state.hasReceivedMeaningfulWork).toBe(false) @@ -170,7 +170,7 @@ describe("handleMessagePartUpdated", () => { } //#when - handleMessagePartUpdated(ctx, payload as any, state) + handleMessagePartUpdated(ctx, testCoerce(payload), state) //#then expect(state.currentTool).toBe("read") @@ -200,7 +200,7 @@ describe("handleMessagePartUpdated", () => { } //#when - handleMessagePartUpdated(ctx, payload as any, state) + handleMessagePartUpdated(ctx, testCoerce(payload), state) //#then expect(state.currentTool).toBeNull() @@ -225,7 +225,7 @@ describe("handleMessagePartUpdated", () => { } //#when - handleMessagePartUpdated(ctx, payload as any, state) + handleMessagePartUpdated(ctx, testCoerce(payload), state) //#then expect(state.hasReceivedMeaningfulWork).toBe(true) @@ -243,7 +243,7 @@ describe("handleMessagePartUpdated", () => { handleMessageUpdated( ctx, - { + testCoerce({ type: "message.updated", properties: { info: { @@ -254,7 +254,7 @@ describe("handleMessagePartUpdated", () => { modelID: "claude-sonnet-4-6", }, }, - } as any, + }), state, ) state.messageStartedAtById["msg_1"] = 1000 @@ -262,7 +262,7 @@ describe("handleMessagePartUpdated", () => { // when handleMessagePartUpdated( ctx, - { + testCoerce({ type: "message.part.updated", properties: { part: { @@ -274,13 +274,13 @@ describe("handleMessagePartUpdated", () => { time: { end: 1 }, }, }, - } as any, + }), state, ) handleMessagePartUpdated( ctx, - { + testCoerce({ type: "message.part.updated", properties: { part: { @@ -292,7 +292,7 @@ describe("handleMessagePartUpdated", () => { time: { end: 2 }, }, }, - } as any, + }), state, ) @@ -323,7 +323,7 @@ describe("handleTuiToast", () => { } //#when - handleTuiToast(ctx, payload as any, state) + handleTuiToast(ctx, testCoerce(payload), state) //#then expect(state.mainSessionError).toBe(true) @@ -344,7 +344,7 @@ describe("handleTuiToast", () => { } //#when - handleTuiToast(ctx, payload as any, state) + handleTuiToast(ctx, testCoerce(payload), state) //#then expect(state.mainSessionError).toBe(false) diff --git a/src/cli/run/integration.test.ts b/src/cli/run/integration.test.ts index c2b019e62..19fb1c254 100644 --- a/src/cli/run/integration.test.ts +++ b/src/cli/run/integration.test.ts @@ -56,14 +56,14 @@ function createMockWriteStream(): MockWriteStream { const createMockClient = ( getResult?: { error?: unknown; data?: { id: string } } -): OpencodeClient => ({ +): OpencodeClient => (testCoerce({ session: { get: mock((opts: { path: { id: string } }) => Promise.resolve(getResult ?? { data: { id: opts.path.id } }) ), create: mock(() => Promise.resolve({ data: { id: "new-session-id" } })), }, -} as unknown as OpencodeClient) +})) describe("integration: --json mode", () => { it("emits valid RunResult JSON to stdout", () => { @@ -78,8 +78,8 @@ describe("integration: --json mode", () => { summary: "Test summary", } const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) // when @@ -103,8 +103,8 @@ describe("integration: --json mode", () => { const mockStdout = createMockWriteStream() const mockStderr = createMockWriteStream() const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) manager.redirectToStderr() @@ -272,8 +272,8 @@ describe("integration: option combinations", () => { summary: "Test completed", } const jsonManager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) jsonManager.redirectToStderr() spawnSpy.mockClear() diff --git a/src/cli/run/json-output.test.ts b/src/cli/run/json-output.test.ts index d932af3c5..58bf96c05 100644 --- a/src/cli/run/json-output.test.ts +++ b/src/cli/run/json-output.test.ts @@ -31,8 +31,8 @@ describe("createJsonOutputManager", () => { it("causes stdout writes to go to stderr", () => { // given const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) manager.redirectToStderr() @@ -49,8 +49,8 @@ describe("createJsonOutputManager", () => { it("reverses the redirect", () => { // given const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) manager.redirectToStderr() @@ -75,8 +75,8 @@ describe("createJsonOutputManager", () => { summary: "Test summary", } const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) // when @@ -98,8 +98,8 @@ describe("createJsonOutputManager", () => { summary: "Test summary", } const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) // when @@ -126,8 +126,8 @@ describe("createJsonOutputManager", () => { summary: "Test", } const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) manager.redirectToStderr() @@ -148,8 +148,8 @@ describe("createJsonOutputManager", () => { it("work correctly", () => { // given const manager = createJsonOutputManager({ - stdout: mockStdout as unknown as NodeJS.WriteStream, - stderr: mockStderr as unknown as NodeJS.WriteStream, + stdout: testCoerce(mockStdout), + stderr: testCoerce(mockStderr), }) // when diff --git a/src/cli/run/poll-for-completion.test.ts b/src/cli/run/poll-for-completion.test.ts index 670c6ba05..4d6529373 100644 --- a/src/cli/run/poll-for-completion.test.ts +++ b/src/cli/run/poll-for-completion.test.ts @@ -15,7 +15,7 @@ const createMockContext = (overrides: { } = overrides return { - client: { + client: testCoerce({ session: { todo: mock(() => Promise.resolve({ data: todo })), children: mock((opts: { path: { id: string } }) => @@ -23,7 +23,7 @@ const createMockContext = (overrides: { ), status: mock(() => Promise.resolve({ data: statuses })), }, - } as unknown as RunContext["client"], + }), sessionID: "test-session", directory: "/test", abortController: new AbortController(), @@ -124,7 +124,7 @@ describe("pollForCompletion", () => { let todoCallCount = 0 let busyInserted = false - ;(ctx.client.session as any).todo = mock(async () => { + ;(testCoerce(ctx.client.session)).todo = mock(async () => { todoCallCount++ if (todoCallCount === 1 && !busyInserted) { busyInserted = true @@ -133,10 +133,10 @@ describe("pollForCompletion", () => { } return { data: [] } }) - ;(ctx.client.session as any).children = mock(() => + ;(testCoerce(ctx.client.session)).children = mock(() => Promise.resolve({ data: [] }) ) - ;(ctx.client.session as any).status = mock(() => + ;(testCoerce(ctx.client.session)).status = mock(() => Promise.resolve({ data: {} }) ) @@ -322,17 +322,17 @@ describe("pollForCompletion", () => { const abortController = new AbortController() let pollTick = 0 - ;(ctx.client.session as any).todo = mock(async () => { + ;(testCoerce(ctx.client.session)).todo = mock(async () => { pollTick++ if (pollTick === 2) { eventState.currentTool = "task" } return { data: [] } }) - ;(ctx.client.session as any).children = mock(() => + ;(testCoerce(ctx.client.session)).children = mock(() => Promise.resolve({ data: [] }) ) - ;(ctx.client.session as any).status = mock(() => + ;(testCoerce(ctx.client.session)).status = mock(() => Promise.resolve({ data: {} }) ) diff --git a/src/cli/run/session-resolver.test.ts b/src/cli/run/session-resolver.test.ts index 7b4338f11..f56a17280 100644 --- a/src/cli/run/session-resolver.test.ts +++ b/src/cli/run/session-resolver.test.ts @@ -10,7 +10,7 @@ const createMockClient = (overrides: { } = {}): OpencodeClient => { const { getResult, createResults = [] } = overrides let createCallIndex = 0 - return { + return testCoerce({ session: { get: mock((opts: { path: { id: string } }) => Promise.resolve(getResult ?? { data: { id: opts.path.id } }) @@ -22,7 +22,7 @@ const createMockClient = (overrides: { return Promise.resolve(result) }), }, - } as unknown as OpencodeClient + }) } describe("resolveSession", () => { diff --git a/src/cli/run/timestamp-output.test.ts b/src/cli/run/timestamp-output.test.ts index 48b8a02bb..988eb7374 100644 --- a/src/cli/run/timestamp-output.test.ts +++ b/src/cli/run/timestamp-output.test.ts @@ -87,7 +87,7 @@ describe("createTimestampedStdoutController", () => { it("prefixes stdout writes when enabled", () => { // given const stdout = createMockWriteStream() - const controller = createTimestampedStdoutController(stdout as unknown as NodeJS.WriteStream) + const controller = createTimestampedStdoutController(testCoerce(stdout)) // when controller.enable() @@ -101,7 +101,7 @@ describe("createTimestampedStdoutController", () => { it("restores original write function", () => { // given const stdout = createMockWriteStream() - const controller = createTimestampedStdoutController(stdout as unknown as NodeJS.WriteStream) + const controller = createTimestampedStdoutController(testCoerce(stdout)) controller.enable() // when @@ -118,7 +118,7 @@ describe("createTimestampedStdoutController", () => { it("supports Uint8Array chunks and encoding", () => { // given const stdout = createMockWriteStream() - const controller = createTimestampedStdoutController(stdout as unknown as NodeJS.WriteStream) + const controller = createTimestampedStdoutController(testCoerce(stdout)) // when controller.enable()