diff --git a/src/gateway/hooks-mapping.test.ts b/src/gateway/hooks-mapping.test.ts index b12eae05a5..e9ba5a5725 100644 --- a/src/gateway/hooks-mapping.test.ts +++ b/src/gateway/hooks-mapping.test.ts @@ -92,7 +92,7 @@ describe("hooks mapping", () => { ], }); expect(result?.ok).toBe(true); - if (result?.ok) { + if (result?.ok && result.action?.kind === "agent") { expect(result.action.kind).toBe("agent"); expect(result.action.message).toBe("Subject: Hello"); } @@ -146,11 +146,9 @@ describe("hooks mapping", () => { }); expect(result?.ok).toBe(true); - if (result?.ok) { + if (result?.ok && result.action?.kind === "wake") { expect(result.action.kind).toBe("wake"); - if (result.action.kind === "wake") { - expect(result.action.text).toBe("Ping Ada"); - } + expect(result.action.text).toBe("Ping Ada"); } }); @@ -259,7 +257,7 @@ describe("hooks mapping", () => { ], }); expect(result?.ok).toBe(true); - if (result?.ok) { + if (result?.ok && result.action?.kind === "agent") { expect(result.action.kind).toBe("agent"); expect(result.action.message).toBe("Override subject: Hello"); } diff --git a/src/gateway/openai-http.e2e.test.ts b/src/gateway/openai-http.e2e.test.ts index d7f095b217..36a0ee5fec 100644 --- a/src/gateway/openai-http.e2e.test.ts +++ b/src/gateway/openai-http.e2e.test.ts @@ -113,7 +113,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { const res = await postChatCompletions(port, request.body, request.headers); expect(res.status).toBe(200); expect(agentCommand).toHaveBeenCalledTimes(1); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((opts as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch( request.matcher, ); @@ -181,7 +181,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { ); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((opts as { sessionKey?: string } | undefined)?.sessionKey).toBe( "agent:beta:openai:custom", ); @@ -197,7 +197,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((opts as { sessionKey?: string } | undefined)?.sessionKey ?? "").toContain( "openai-user:alice", ); @@ -220,7 +220,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((opts as { message?: string } | undefined)?.message).toBe("hello\nworld"); await res.text(); } @@ -238,7 +238,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const message = (opts as { message?: string } | undefined)?.message ?? ""; expect(message).toContain(HISTORY_CONTEXT_MARKER); expect(message).toContain("User: Hello, who are you?"); @@ -259,7 +259,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const message = (opts as { message?: string } | undefined)?.message ?? ""; expect(message).not.toContain(HISTORY_CONTEXT_MARKER); expect(message).not.toContain(CURRENT_MESSAGE_MARKER); @@ -278,7 +278,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const extraSystemPrompt = (opts as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; expect(extraSystemPrompt).toBe("You are a helpful assistant."); @@ -298,7 +298,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { }); expect(res.status).toBe(200); - const [opts] = agentCommand.mock.calls[0] ?? []; + const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const message = (opts as { message?: string } | undefined)?.message ?? ""; expect(message).toContain(HISTORY_CONTEXT_MARKER); expect(message).toContain("User: What's the weather?"); @@ -389,12 +389,12 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { try { { agentCommand.mockReset(); - agentCommand.mockImplementationOnce(async (opts: unknown) => { + agentCommand.mockImplementationOnce((async (opts: unknown) => { const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } }); return { payloads: [{ text: "hello" }] } as never; - }); + }) as never); const res = await postChatCompletions(port, { stream: true, @@ -422,12 +422,12 @@ describe("OpenAI-compatible HTTP API (e2e)", () => { { agentCommand.mockReset(); - agentCommand.mockImplementationOnce(async (opts: unknown) => { + agentCommand.mockImplementationOnce((async (opts: unknown) => { const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } }); return { payloads: [{ text: "hihi" }] } as never; - }); + }) as never); const repeatedRes = await postChatCompletions(port, { stream: true, diff --git a/src/gateway/openresponses-http.e2e.test.ts b/src/gateway/openresponses-http.e2e.test.ts index 9747280d69..702e8630e2 100644 --- a/src/gateway/openresponses-http.e2e.test.ts +++ b/src/gateway/openresponses-http.e2e.test.ts @@ -158,7 +158,7 @@ describe("OpenResponses HTTP API (e2e)", () => { { "x-openclaw-agent-id": "beta" }, ); expect(resHeader.status).toBe(200); - const [optsHeader] = agentCommand.mock.calls[0] ?? []; + const optsHeader = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((optsHeader as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch( /^agent:beta:/, ); @@ -167,7 +167,7 @@ describe("OpenResponses HTTP API (e2e)", () => { mockAgentOnce([{ text: "hello" }]); const resModel = await postResponses(port, { model: "openclaw:beta", input: "hi" }); expect(resModel.status).toBe(200); - const [optsModel] = agentCommand.mock.calls[0] ?? []; + const optsModel = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((optsModel as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch( /^agent:beta:/, ); @@ -180,7 +180,7 @@ describe("OpenResponses HTTP API (e2e)", () => { input: "hi", }); expect(resUser.status).toBe(200); - const [optsUser] = agentCommand.mock.calls[0] ?? []; + const optsUser = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((optsUser as { sessionKey?: string } | undefined)?.sessionKey ?? "").toContain( "openresponses-user:alice", ); @@ -192,7 +192,7 @@ describe("OpenResponses HTTP API (e2e)", () => { input: "hello world", }); expect(resString.status).toBe(200); - const [optsString] = agentCommand.mock.calls[0] ?? []; + const optsString = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((optsString as { message?: string } | undefined)?.message).toBe("hello world"); await ensureResponseConsumed(resString); @@ -202,7 +202,7 @@ describe("OpenResponses HTTP API (e2e)", () => { input: [{ type: "message", role: "user", content: "hello there" }], }); expect(resArray.status).toBe(200); - const [optsArray] = agentCommand.mock.calls[0] ?? []; + const optsArray = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect((optsArray as { message?: string } | undefined)?.message).toBe("hello there"); await ensureResponseConsumed(resArray); @@ -216,7 +216,7 @@ describe("OpenResponses HTTP API (e2e)", () => { ], }); expect(resSystemDeveloper.status).toBe(200); - const [optsSystemDeveloper] = agentCommand.mock.calls[0] ?? []; + const optsSystemDeveloper = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const extraSystemPrompt = (optsSystemDeveloper as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; @@ -231,7 +231,7 @@ describe("OpenResponses HTTP API (e2e)", () => { instructions: "Always respond in French.", }); expect(resInstructions.status).toBe(200); - const [optsInstructions] = agentCommand.mock.calls[0] ?? []; + const optsInstructions = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const instructionPrompt = (optsInstructions as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; expect(instructionPrompt).toContain("Always respond in French."); @@ -248,7 +248,7 @@ describe("OpenResponses HTTP API (e2e)", () => { ], }); expect(resHistory.status).toBe(200); - const [optsHistory] = agentCommand.mock.calls[0] ?? []; + const optsHistory = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const historyMessage = (optsHistory as { message?: string } | undefined)?.message ?? ""; expect(historyMessage).toContain(HISTORY_CONTEXT_MARKER); expect(historyMessage).toContain("User: Hello, who are you?"); @@ -266,7 +266,7 @@ describe("OpenResponses HTTP API (e2e)", () => { ], }); expect(resFunctionOutput.status).toBe(200); - const [optsFunctionOutput] = agentCommand.mock.calls[0] ?? []; + const optsFunctionOutput = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const functionOutputMessage = (optsFunctionOutput as { message?: string } | undefined)?.message ?? ""; expect(functionOutputMessage).toContain("Sunny, 70F."); @@ -295,7 +295,7 @@ describe("OpenResponses HTTP API (e2e)", () => { ], }); expect(resInputFile.status).toBe(200); - const [optsInputFile] = agentCommand.mock.calls[0] ?? []; + const optsInputFile = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const inputFileMessage = (optsInputFile as { message?: string } | undefined)?.message ?? ""; const inputFilePrompt = (optsInputFile as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; @@ -316,7 +316,7 @@ describe("OpenResponses HTTP API (e2e)", () => { tool_choice: "none", }); expect(resToolNone.status).toBe(200); - const [optsToolNone] = agentCommand.mock.calls[0] ?? []; + const optsToolNone = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect( (optsToolNone as { clientTools?: unknown[] } | undefined)?.clientTools, ).toBeUndefined(); @@ -339,9 +339,9 @@ describe("OpenResponses HTTP API (e2e)", () => { tool_choice: { type: "function", function: { name: "get_time" } }, }); expect(resToolChoice.status).toBe(200); - const [optsToolChoice] = agentCommand.mock.calls[0] ?? []; + const optsToolChoice = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; const clientTools = - (optsToolChoice as { clientTools?: Array<{ function?: { name?: string } }> }) + (optsToolChoice as { clientTools?: Array<{ function?: { name?: string } }> } | undefined) ?.clientTools ?? []; expect(clientTools).toHaveLength(1); expect(clientTools[0]?.function?.name).toBe("get_time"); @@ -368,7 +368,7 @@ describe("OpenResponses HTTP API (e2e)", () => { max_output_tokens: 123, }); expect(resMaxTokens.status).toBe(200); - const [optsMaxTokens] = agentCommand.mock.calls[0] ?? []; + const optsMaxTokens = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0]; expect( (optsMaxTokens as { streamParams?: { maxTokens?: number } } | undefined)?.streamParams ?.maxTokens, @@ -433,12 +433,12 @@ describe("OpenResponses HTTP API (e2e)", () => { const port = enabledPort; try { agentCommand.mockReset(); - agentCommand.mockImplementationOnce(async (opts: unknown) => { + agentCommand.mockImplementationOnce((async (opts: unknown) => { const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } }); return { payloads: [{ text: "hello" }] } as never; - }); + }) as never); const resDelta = await postResponses(port, { stream: true, diff --git a/src/gateway/server.hooks.e2e.test.ts b/src/gateway/server.hooks.e2e.test.ts index 7e0a8a484d..149246af06 100644 --- a/src/gateway/server.hooks.e2e.test.ts +++ b/src/gateway/server.hooks.e2e.test.ts @@ -77,7 +77,7 @@ describe("gateway server hooks", () => { }); expect(resAgentModel.status).toBe(202); await waitForSystemEvent(); - const call = cronIsolatedRun.mock.calls[0]?.[0] as { + const call = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as { job?: { payload?: { model?: string } }; }; expect(call?.job?.payload?.model).toBe("openai/gpt-4.1-mini"); @@ -98,7 +98,7 @@ describe("gateway server hooks", () => { }); expect(resAgentWithId.status).toBe(202); await waitForSystemEvent(); - const routedCall = cronIsolatedRun.mock.calls[0]?.[0] as { + const routedCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as { job?: { agentId?: string }; }; expect(routedCall?.job?.agentId).toBe("hooks"); @@ -119,7 +119,7 @@ describe("gateway server hooks", () => { }); expect(resAgentUnknown.status).toBe(202); await waitForSystemEvent(); - const fallbackCall = cronIsolatedRun.mock.calls[0]?.[0] as { + const fallbackCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as { job?: { agentId?: string }; }; expect(fallbackCall?.job?.agentId).toBe("main"); @@ -250,7 +250,9 @@ describe("gateway server hooks", () => { }); expect(defaultRoute.status).toBe(202); await waitForSystemEvent(); - const defaultCall = cronIsolatedRun.mock.calls[0]?.[0] as { sessionKey?: string } | undefined; + const defaultCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as + | { sessionKey?: string } + | undefined; expect(defaultCall?.sessionKey).toBe("hook:ingress"); drainSystemEvents(resolveMainKey()); @@ -266,7 +268,9 @@ describe("gateway server hooks", () => { }); expect(mappedOk.status).toBe(202); await waitForSystemEvent(); - const mappedCall = cronIsolatedRun.mock.calls[0]?.[0] as { sessionKey?: string } | undefined; + const mappedCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as + | { sessionKey?: string } + | undefined; expect(mappedCall?.sessionKey).toBe("hook:mapped:42"); drainSystemEvents(resolveMainKey()); @@ -328,7 +332,7 @@ describe("gateway server hooks", () => { }); expect(resNoAgent.status).toBe(202); await waitForSystemEvent(); - const noAgentCall = cronIsolatedRun.mock.calls[0]?.[0] as { + const noAgentCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as { job?: { agentId?: string }; }; expect(noAgentCall?.job?.agentId).toBeUndefined(); @@ -349,7 +353,7 @@ describe("gateway server hooks", () => { }); expect(resAllowed.status).toBe(202); await waitForSystemEvent(); - const allowedCall = cronIsolatedRun.mock.calls[0]?.[0] as { + const allowedCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as { job?: { agentId?: string }; }; expect(allowedCall?.job?.agentId).toBe("hooks"); diff --git a/src/signal/monitor/event-handler.mention-gating.test.ts b/src/signal/monitor/event-handler.mention-gating.test.ts index 678bcf7837..6a38799c8d 100644 --- a/src/signal/monitor/event-handler.mention-gating.test.ts +++ b/src/signal/monitor/event-handler.mention-gating.test.ts @@ -1,14 +1,20 @@ import { describe, expect, it, vi } from "vitest"; import { buildDispatchInboundCaptureMock } from "../../../test/helpers/dispatch-inbound-capture.js"; import type { MsgContext } from "../../auto-reply/templating.js"; +import type { OpenClawConfig } from "../../config/types.js"; import { createBaseSignalEventHandlerDeps } from "./event-handler.test-harness.js"; -let capturedCtx: MsgContext | undefined; +type SignalMsgContext = MsgContext & { + Body?: string; + WasMentioned?: boolean; +}; + +let capturedCtx: SignalMsgContext | undefined; vi.mock("../../auto-reply/dispatch.js", async (importOriginal) => { const actual = await importOriginal(); return buildDispatchInboundCaptureMock(actual, (ctx) => { - capturedCtx = ctx as MsgContext; + capturedCtx = ctx as SignalMsgContext; }); }); @@ -51,10 +57,7 @@ function createMentionGatedHistoryHandler() { const groupHistories = new Map(); const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: true } } } }, - }, + cfg: createSignalConfig({ requireMention: true }), historyLimit: 5, groupHistories, }), @@ -62,6 +65,20 @@ function createMentionGatedHistoryHandler() { return { handler, groupHistories }; } +function createSignalConfig(params: { requireMention: boolean; mentionPattern?: string }) { + return { + messages: { + inbound: { debounceMs: 0 }, + groupChat: { mentionPatterns: [params.mentionPattern ?? "@bot"] }, + }, + channels: { + signal: { + groups: { "*": { requireMention: params.requireMention } }, + }, + }, + } as unknown as OpenClawConfig; +} + async function expectSkippedGroupHistory(opts: GroupEventOpts, expectedBody: string) { capturedCtx = undefined; const { handler, groupHistories } = createMentionGatedHistoryHandler(); @@ -78,10 +95,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: true } } } }, - }, + cfg: createSignalConfig({ requireMention: true }), }), ); @@ -93,10 +107,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: true } } } }, - }, + cfg: createSignalConfig({ requireMention: true }), }), ); @@ -109,10 +120,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: false } } } }, - }, + cfg: createSignalConfig({ requireMention: false }), }), ); @@ -147,10 +155,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: true } } } }, - }, + cfg: createSignalConfig({ requireMention: true }), }), ); @@ -162,10 +167,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } }, - channels: { signal: { groups: { "*": { requireMention: false } } } }, - }, + cfg: createSignalConfig({ requireMention: false }), }), ); @@ -194,10 +196,7 @@ describe("signal mention gating", () => { capturedCtx = undefined; const handler = createSignalEventHandler( createBaseSignalEventHandlerDeps({ - cfg: { - messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@123e4567"] } }, - channels: { signal: { groups: { "*": { requireMention: true } } } }, - }, + cfg: createSignalConfig({ requireMention: true, mentionPattern: "@123e4567" }), }), ); diff --git a/src/telegram/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts b/src/telegram/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts index ce10b1b694..90d0a88018 100644 --- a/src/telegram/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts +++ b/src/telegram/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts @@ -36,7 +36,7 @@ async function createBotHandlerWithOptions(options: { }> { const { createTelegramBot } = await import("./bot.js"); const replyModule = await import("../auto-reply/reply.js"); - const replySpy = replyModule.__replySpy as unknown as ReturnType; + const replySpy = (replyModule as { __replySpy: ReturnType }).__replySpy; onSpy.mockReset(); replySpy.mockReset(); @@ -49,8 +49,8 @@ async function createBotHandlerWithOptions(options: { testTimings: TELEGRAM_TEST_TIMINGS, ...(options.proxyFetch ? { proxyFetch: options.proxyFetch } : {}), runtime: { - log: runtimeLog, - error: runtimeError, + log: runtimeLog as (...data: unknown[]) => void, + error: runtimeError as (...data: unknown[]) => void, exit: () => { throw new Error("exit"); }, @@ -67,23 +67,23 @@ function mockTelegramFileDownload(params: { contentType: string; bytes: Uint8Array; }): ReturnType { - return vi.spyOn(globalThis, "fetch" as never).mockResolvedValueOnce({ + return vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({ ok: true, status: 200, statusText: "OK", headers: { get: () => params.contentType }, arrayBuffer: async () => params.bytes.buffer, - } as Response); + } as unknown as Response); } function mockTelegramPngDownload(): ReturnType { - return vi.spyOn(globalThis, "fetch" as never).mockResolvedValue({ + return vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, status: 200, statusText: "OK", headers: { get: () => "image/png" }, arrayBuffer: async () => new Uint8Array([0x89, 0x50, 0x4e, 0x47]).buffer, - } as Response); + } as unknown as Response); } beforeEach(() => { @@ -147,7 +147,7 @@ describe("telegram inbound media", () => { it("prefers proxyFetch over global fetch", async () => { const runtimeLog = vi.fn(); const runtimeError = vi.fn(); - const globalFetchSpy = vi.spyOn(globalThis, "fetch" as never).mockImplementation(() => { + const globalFetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(async () => { throw new Error("global fetch should not be called"); }); const proxyFetch = vi.fn().mockResolvedValueOnce({ @@ -156,7 +156,7 @@ describe("telegram inbound media", () => { statusText: "OK", headers: { get: () => "image/jpeg" }, arrayBuffer: async () => new Uint8Array([0xff, 0xd8, 0xff]).buffer, - } as Response); + } as unknown as Response); const { handler } = await createBotHandlerWithOptions({ proxyFetch: proxyFetch as unknown as typeof fetch, @@ -190,7 +190,7 @@ describe("telegram inbound media", () => { runtimeLog, runtimeError, }); - const fetchSpy = vi.spyOn(globalThis, "fetch" as never); + const fetchSpy = vi.spyOn(globalThis, "fetch"); await handler({ message: { @@ -385,13 +385,13 @@ describe("telegram stickers", () => { cachedAt: "2026-01-20T10:00:00.000Z", }); - const fetchSpy = vi.spyOn(globalThis, "fetch" as never).mockResolvedValueOnce({ + const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({ ok: true, status: 200, statusText: "OK", headers: { get: () => "image/webp" }, arrayBuffer: async () => new Uint8Array([0x52, 0x49, 0x46, 0x46]).buffer, - } as Response); + } as unknown as Response); await handler({ message: { @@ -435,7 +435,7 @@ describe("telegram stickers", () => { "skips animated stickers (TGS format)", async () => { const { handler, replySpy, runtimeError } = await createBotHandler(); - const fetchSpy = vi.spyOn(globalThis, "fetch" as never); + const fetchSpy = vi.spyOn(globalThis, "fetch"); await handler({ message: { @@ -473,7 +473,7 @@ describe("telegram stickers", () => { "skips video stickers (WEBM format)", async () => { const { handler, replySpy, runtimeError } = await createBotHandler(); - const fetchSpy = vi.spyOn(globalThis, "fetch" as never); + const fetchSpy = vi.spyOn(globalThis, "fetch"); await handler({ message: { @@ -520,7 +520,7 @@ describe("telegram text fragments", () => { async () => { const { createTelegramBot } = await import("./bot.js"); const replyModule = await import("../auto-reply/reply.js"); - const replySpy = replyModule.__replySpy as unknown as ReturnType; + const replySpy = (replyModule as { __replySpy: ReturnType }).__replySpy; onSpy.mockReset(); replySpy.mockReset();