chore: Fix types in tests 11/N.

This commit is contained in:
cpojer
2026-02-17 11:17:32 +09:00
parent 058eb85762
commit e02feaff83
6 changed files with 87 additions and 86 deletions

View File

@@ -92,7 +92,7 @@ describe("hooks mapping", () => {
], ],
}); });
expect(result?.ok).toBe(true); expect(result?.ok).toBe(true);
if (result?.ok) { if (result?.ok && result.action?.kind === "agent") {
expect(result.action.kind).toBe("agent"); expect(result.action.kind).toBe("agent");
expect(result.action.message).toBe("Subject: Hello"); expect(result.action.message).toBe("Subject: Hello");
} }
@@ -146,11 +146,9 @@ describe("hooks mapping", () => {
}); });
expect(result?.ok).toBe(true); expect(result?.ok).toBe(true);
if (result?.ok) { if (result?.ok && result.action?.kind === "wake") {
expect(result.action.kind).toBe("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); expect(result?.ok).toBe(true);
if (result?.ok) { if (result?.ok && result.action?.kind === "agent") {
expect(result.action.kind).toBe("agent"); expect(result.action.kind).toBe("agent");
expect(result.action.message).toBe("Override subject: Hello"); expect(result.action.message).toBe("Override subject: Hello");
} }

View File

@@ -113,7 +113,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
const res = await postChatCompletions(port, request.body, request.headers); const res = await postChatCompletions(port, request.body, request.headers);
expect(res.status).toBe(200); expect(res.status).toBe(200);
expect(agentCommand).toHaveBeenCalledTimes(1); 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( expect((opts as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch(
request.matcher, request.matcher,
); );
@@ -181,7 +181,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
); );
expect(res.status).toBe(200); 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( expect((opts as { sessionKey?: string } | undefined)?.sessionKey).toBe(
"agent:beta:openai:custom", "agent:beta:openai:custom",
); );
@@ -197,7 +197,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); 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( expect((opts as { sessionKey?: string } | undefined)?.sessionKey ?? "").toContain(
"openai-user:alice", "openai-user:alice",
); );
@@ -220,7 +220,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); 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"); expect((opts as { message?: string } | undefined)?.message).toBe("hello\nworld");
await res.text(); await res.text();
} }
@@ -238,7 +238,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); 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 ?? ""; const message = (opts as { message?: string } | undefined)?.message ?? "";
expect(message).toContain(HISTORY_CONTEXT_MARKER); expect(message).toContain(HISTORY_CONTEXT_MARKER);
expect(message).toContain("User: Hello, who are you?"); expect(message).toContain("User: Hello, who are you?");
@@ -259,7 +259,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); 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 ?? ""; const message = (opts as { message?: string } | undefined)?.message ?? "";
expect(message).not.toContain(HISTORY_CONTEXT_MARKER); expect(message).not.toContain(HISTORY_CONTEXT_MARKER);
expect(message).not.toContain(CURRENT_MESSAGE_MARKER); expect(message).not.toContain(CURRENT_MESSAGE_MARKER);
@@ -278,7 +278,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); expect(res.status).toBe(200);
const [opts] = agentCommand.mock.calls[0] ?? []; const opts = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
const extraSystemPrompt = const extraSystemPrompt =
(opts as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; (opts as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? "";
expect(extraSystemPrompt).toBe("You are a helpful assistant."); expect(extraSystemPrompt).toBe("You are a helpful assistant.");
@@ -298,7 +298,7 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
}); });
expect(res.status).toBe(200); 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 ?? ""; const message = (opts as { message?: string } | undefined)?.message ?? "";
expect(message).toContain(HISTORY_CONTEXT_MARKER); expect(message).toContain(HISTORY_CONTEXT_MARKER);
expect(message).toContain("User: What's the weather?"); expect(message).toContain("User: What's the weather?");
@@ -389,12 +389,12 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
try { try {
{ {
agentCommand.mockReset(); agentCommand.mockReset();
agentCommand.mockImplementationOnce(async (opts: unknown) => { agentCommand.mockImplementationOnce((async (opts: unknown) => {
const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; const runId = (opts as { runId?: string } | undefined)?.runId ?? "";
emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } });
emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } });
return { payloads: [{ text: "hello" }] } as never; return { payloads: [{ text: "hello" }] } as never;
}); }) as never);
const res = await postChatCompletions(port, { const res = await postChatCompletions(port, {
stream: true, stream: true,
@@ -422,12 +422,12 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
{ {
agentCommand.mockReset(); agentCommand.mockReset();
agentCommand.mockImplementationOnce(async (opts: unknown) => { agentCommand.mockImplementationOnce((async (opts: unknown) => {
const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; const runId = (opts as { runId?: string } | undefined)?.runId ?? "";
emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } });
emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "hi" } });
return { payloads: [{ text: "hihi" }] } as never; return { payloads: [{ text: "hihi" }] } as never;
}); }) as never);
const repeatedRes = await postChatCompletions(port, { const repeatedRes = await postChatCompletions(port, {
stream: true, stream: true,

View File

@@ -158,7 +158,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
{ "x-openclaw-agent-id": "beta" }, { "x-openclaw-agent-id": "beta" },
); );
expect(resHeader.status).toBe(200); 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( expect((optsHeader as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch(
/^agent:beta:/, /^agent:beta:/,
); );
@@ -167,7 +167,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
mockAgentOnce([{ text: "hello" }]); mockAgentOnce([{ text: "hello" }]);
const resModel = await postResponses(port, { model: "openclaw:beta", input: "hi" }); const resModel = await postResponses(port, { model: "openclaw:beta", input: "hi" });
expect(resModel.status).toBe(200); 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( expect((optsModel as { sessionKey?: string } | undefined)?.sessionKey ?? "").toMatch(
/^agent:beta:/, /^agent:beta:/,
); );
@@ -180,7 +180,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
input: "hi", input: "hi",
}); });
expect(resUser.status).toBe(200); 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( expect((optsUser as { sessionKey?: string } | undefined)?.sessionKey ?? "").toContain(
"openresponses-user:alice", "openresponses-user:alice",
); );
@@ -192,7 +192,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
input: "hello world", input: "hello world",
}); });
expect(resString.status).toBe(200); 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"); expect((optsString as { message?: string } | undefined)?.message).toBe("hello world");
await ensureResponseConsumed(resString); await ensureResponseConsumed(resString);
@@ -202,7 +202,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
input: [{ type: "message", role: "user", content: "hello there" }], input: [{ type: "message", role: "user", content: "hello there" }],
}); });
expect(resArray.status).toBe(200); 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"); expect((optsArray as { message?: string } | undefined)?.message).toBe("hello there");
await ensureResponseConsumed(resArray); await ensureResponseConsumed(resArray);
@@ -216,7 +216,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
], ],
}); });
expect(resSystemDeveloper.status).toBe(200); expect(resSystemDeveloper.status).toBe(200);
const [optsSystemDeveloper] = agentCommand.mock.calls[0] ?? []; const optsSystemDeveloper = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
const extraSystemPrompt = const extraSystemPrompt =
(optsSystemDeveloper as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? (optsSystemDeveloper as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ??
""; "";
@@ -231,7 +231,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
instructions: "Always respond in French.", instructions: "Always respond in French.",
}); });
expect(resInstructions.status).toBe(200); expect(resInstructions.status).toBe(200);
const [optsInstructions] = agentCommand.mock.calls[0] ?? []; const optsInstructions = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
const instructionPrompt = const instructionPrompt =
(optsInstructions as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; (optsInstructions as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? "";
expect(instructionPrompt).toContain("Always respond in French."); expect(instructionPrompt).toContain("Always respond in French.");
@@ -248,7 +248,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
], ],
}); });
expect(resHistory.status).toBe(200); 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 ?? ""; const historyMessage = (optsHistory as { message?: string } | undefined)?.message ?? "";
expect(historyMessage).toContain(HISTORY_CONTEXT_MARKER); expect(historyMessage).toContain(HISTORY_CONTEXT_MARKER);
expect(historyMessage).toContain("User: Hello, who are you?"); expect(historyMessage).toContain("User: Hello, who are you?");
@@ -266,7 +266,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
], ],
}); });
expect(resFunctionOutput.status).toBe(200); expect(resFunctionOutput.status).toBe(200);
const [optsFunctionOutput] = agentCommand.mock.calls[0] ?? []; const optsFunctionOutput = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
const functionOutputMessage = const functionOutputMessage =
(optsFunctionOutput as { message?: string } | undefined)?.message ?? ""; (optsFunctionOutput as { message?: string } | undefined)?.message ?? "";
expect(functionOutputMessage).toContain("Sunny, 70F."); expect(functionOutputMessage).toContain("Sunny, 70F.");
@@ -295,7 +295,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
], ],
}); });
expect(resInputFile.status).toBe(200); 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 inputFileMessage = (optsInputFile as { message?: string } | undefined)?.message ?? "";
const inputFilePrompt = const inputFilePrompt =
(optsInputFile as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? ""; (optsInputFile as { extraSystemPrompt?: string } | undefined)?.extraSystemPrompt ?? "";
@@ -316,7 +316,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
tool_choice: "none", tool_choice: "none",
}); });
expect(resToolNone.status).toBe(200); expect(resToolNone.status).toBe(200);
const [optsToolNone] = agentCommand.mock.calls[0] ?? []; const optsToolNone = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
expect( expect(
(optsToolNone as { clientTools?: unknown[] } | undefined)?.clientTools, (optsToolNone as { clientTools?: unknown[] } | undefined)?.clientTools,
).toBeUndefined(); ).toBeUndefined();
@@ -339,9 +339,9 @@ describe("OpenResponses HTTP API (e2e)", () => {
tool_choice: { type: "function", function: { name: "get_time" } }, tool_choice: { type: "function", function: { name: "get_time" } },
}); });
expect(resToolChoice.status).toBe(200); expect(resToolChoice.status).toBe(200);
const [optsToolChoice] = agentCommand.mock.calls[0] ?? []; const optsToolChoice = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
const clientTools = const clientTools =
(optsToolChoice as { clientTools?: Array<{ function?: { name?: string } }> }) (optsToolChoice as { clientTools?: Array<{ function?: { name?: string } }> } | undefined)
?.clientTools ?? []; ?.clientTools ?? [];
expect(clientTools).toHaveLength(1); expect(clientTools).toHaveLength(1);
expect(clientTools[0]?.function?.name).toBe("get_time"); expect(clientTools[0]?.function?.name).toBe("get_time");
@@ -368,7 +368,7 @@ describe("OpenResponses HTTP API (e2e)", () => {
max_output_tokens: 123, max_output_tokens: 123,
}); });
expect(resMaxTokens.status).toBe(200); expect(resMaxTokens.status).toBe(200);
const [optsMaxTokens] = agentCommand.mock.calls[0] ?? []; const optsMaxTokens = (agentCommand.mock.calls[0] as unknown[] | undefined)?.[0];
expect( expect(
(optsMaxTokens as { streamParams?: { maxTokens?: number } } | undefined)?.streamParams (optsMaxTokens as { streamParams?: { maxTokens?: number } } | undefined)?.streamParams
?.maxTokens, ?.maxTokens,
@@ -433,12 +433,12 @@ describe("OpenResponses HTTP API (e2e)", () => {
const port = enabledPort; const port = enabledPort;
try { try {
agentCommand.mockReset(); agentCommand.mockReset();
agentCommand.mockImplementationOnce(async (opts: unknown) => { agentCommand.mockImplementationOnce((async (opts: unknown) => {
const runId = (opts as { runId?: string } | undefined)?.runId ?? ""; const runId = (opts as { runId?: string } | undefined)?.runId ?? "";
emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "he" } });
emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } }); emitAgentEvent({ runId, stream: "assistant", data: { delta: "llo" } });
return { payloads: [{ text: "hello" }] } as never; return { payloads: [{ text: "hello" }] } as never;
}); }) as never);
const resDelta = await postResponses(port, { const resDelta = await postResponses(port, {
stream: true, stream: true,

View File

@@ -77,7 +77,7 @@ describe("gateway server hooks", () => {
}); });
expect(resAgentModel.status).toBe(202); expect(resAgentModel.status).toBe(202);
await waitForSystemEvent(); 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 } }; job?: { payload?: { model?: string } };
}; };
expect(call?.job?.payload?.model).toBe("openai/gpt-4.1-mini"); expect(call?.job?.payload?.model).toBe("openai/gpt-4.1-mini");
@@ -98,7 +98,7 @@ describe("gateway server hooks", () => {
}); });
expect(resAgentWithId.status).toBe(202); expect(resAgentWithId.status).toBe(202);
await waitForSystemEvent(); await waitForSystemEvent();
const routedCall = cronIsolatedRun.mock.calls[0]?.[0] as { const routedCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as {
job?: { agentId?: string }; job?: { agentId?: string };
}; };
expect(routedCall?.job?.agentId).toBe("hooks"); expect(routedCall?.job?.agentId).toBe("hooks");
@@ -119,7 +119,7 @@ describe("gateway server hooks", () => {
}); });
expect(resAgentUnknown.status).toBe(202); expect(resAgentUnknown.status).toBe(202);
await waitForSystemEvent(); await waitForSystemEvent();
const fallbackCall = cronIsolatedRun.mock.calls[0]?.[0] as { const fallbackCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as {
job?: { agentId?: string }; job?: { agentId?: string };
}; };
expect(fallbackCall?.job?.agentId).toBe("main"); expect(fallbackCall?.job?.agentId).toBe("main");
@@ -250,7 +250,9 @@ describe("gateway server hooks", () => {
}); });
expect(defaultRoute.status).toBe(202); expect(defaultRoute.status).toBe(202);
await waitForSystemEvent(); 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"); expect(defaultCall?.sessionKey).toBe("hook:ingress");
drainSystemEvents(resolveMainKey()); drainSystemEvents(resolveMainKey());
@@ -266,7 +268,9 @@ describe("gateway server hooks", () => {
}); });
expect(mappedOk.status).toBe(202); expect(mappedOk.status).toBe(202);
await waitForSystemEvent(); 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"); expect(mappedCall?.sessionKey).toBe("hook:mapped:42");
drainSystemEvents(resolveMainKey()); drainSystemEvents(resolveMainKey());
@@ -328,7 +332,7 @@ describe("gateway server hooks", () => {
}); });
expect(resNoAgent.status).toBe(202); expect(resNoAgent.status).toBe(202);
await waitForSystemEvent(); await waitForSystemEvent();
const noAgentCall = cronIsolatedRun.mock.calls[0]?.[0] as { const noAgentCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as {
job?: { agentId?: string }; job?: { agentId?: string };
}; };
expect(noAgentCall?.job?.agentId).toBeUndefined(); expect(noAgentCall?.job?.agentId).toBeUndefined();
@@ -349,7 +353,7 @@ describe("gateway server hooks", () => {
}); });
expect(resAllowed.status).toBe(202); expect(resAllowed.status).toBe(202);
await waitForSystemEvent(); await waitForSystemEvent();
const allowedCall = cronIsolatedRun.mock.calls[0]?.[0] as { const allowedCall = (cronIsolatedRun.mock.calls[0] as unknown[] | undefined)?.[0] as {
job?: { agentId?: string }; job?: { agentId?: string };
}; };
expect(allowedCall?.job?.agentId).toBe("hooks"); expect(allowedCall?.job?.agentId).toBe("hooks");

View File

@@ -1,14 +1,20 @@
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { buildDispatchInboundCaptureMock } from "../../../test/helpers/dispatch-inbound-capture.js"; import { buildDispatchInboundCaptureMock } from "../../../test/helpers/dispatch-inbound-capture.js";
import type { MsgContext } from "../../auto-reply/templating.js"; import type { MsgContext } from "../../auto-reply/templating.js";
import type { OpenClawConfig } from "../../config/types.js";
import { createBaseSignalEventHandlerDeps } from "./event-handler.test-harness.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) => { vi.mock("../../auto-reply/dispatch.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../auto-reply/dispatch.js")>(); const actual = await importOriginal<typeof import("../../auto-reply/dispatch.js")>();
return buildDispatchInboundCaptureMock(actual, (ctx) => { return buildDispatchInboundCaptureMock(actual, (ctx) => {
capturedCtx = ctx as MsgContext; capturedCtx = ctx as SignalMsgContext;
}); });
}); });
@@ -51,10 +57,7 @@ function createMentionGatedHistoryHandler() {
const groupHistories = new Map(); const groupHistories = new Map();
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: true }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: true } } } },
},
historyLimit: 5, historyLimit: 5,
groupHistories, groupHistories,
}), }),
@@ -62,6 +65,20 @@ function createMentionGatedHistoryHandler() {
return { handler, groupHistories }; 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) { async function expectSkippedGroupHistory(opts: GroupEventOpts, expectedBody: string) {
capturedCtx = undefined; capturedCtx = undefined;
const { handler, groupHistories } = createMentionGatedHistoryHandler(); const { handler, groupHistories } = createMentionGatedHistoryHandler();
@@ -78,10 +95,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: true }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: true } } } },
},
}), }),
); );
@@ -93,10 +107,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: true }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: true } } } },
},
}), }),
); );
@@ -109,10 +120,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: false }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: false } } } },
},
}), }),
); );
@@ -147,10 +155,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: true }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: true } } } },
},
}), }),
); );
@@ -162,10 +167,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: false }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@bot"] } },
channels: { signal: { groups: { "*": { requireMention: false } } } },
},
}), }),
); );
@@ -194,10 +196,7 @@ describe("signal mention gating", () => {
capturedCtx = undefined; capturedCtx = undefined;
const handler = createSignalEventHandler( const handler = createSignalEventHandler(
createBaseSignalEventHandlerDeps({ createBaseSignalEventHandlerDeps({
cfg: { cfg: createSignalConfig({ requireMention: true, mentionPattern: "@123e4567" }),
messages: { inbound: { debounceMs: 0 }, groupChat: { mentionPatterns: ["@123e4567"] } },
channels: { signal: { groups: { "*": { requireMention: true } } } },
},
}), }),
); );

View File

@@ -36,7 +36,7 @@ async function createBotHandlerWithOptions(options: {
}> { }> {
const { createTelegramBot } = await import("./bot.js"); const { createTelegramBot } = await import("./bot.js");
const replyModule = await import("../auto-reply/reply.js"); const replyModule = await import("../auto-reply/reply.js");
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>; const replySpy = (replyModule as { __replySpy: ReturnType<typeof vi.fn> }).__replySpy;
onSpy.mockReset(); onSpy.mockReset();
replySpy.mockReset(); replySpy.mockReset();
@@ -49,8 +49,8 @@ async function createBotHandlerWithOptions(options: {
testTimings: TELEGRAM_TEST_TIMINGS, testTimings: TELEGRAM_TEST_TIMINGS,
...(options.proxyFetch ? { proxyFetch: options.proxyFetch } : {}), ...(options.proxyFetch ? { proxyFetch: options.proxyFetch } : {}),
runtime: { runtime: {
log: runtimeLog, log: runtimeLog as (...data: unknown[]) => void,
error: runtimeError, error: runtimeError as (...data: unknown[]) => void,
exit: () => { exit: () => {
throw new Error("exit"); throw new Error("exit");
}, },
@@ -67,23 +67,23 @@ function mockTelegramFileDownload(params: {
contentType: string; contentType: string;
bytes: Uint8Array; bytes: Uint8Array;
}): ReturnType<typeof vi.spyOn> { }): ReturnType<typeof vi.spyOn> {
return vi.spyOn(globalThis, "fetch" as never).mockResolvedValueOnce({ return vi.spyOn(globalThis, "fetch").mockResolvedValueOnce({
ok: true, ok: true,
status: 200, status: 200,
statusText: "OK", statusText: "OK",
headers: { get: () => params.contentType }, headers: { get: () => params.contentType },
arrayBuffer: async () => params.bytes.buffer, arrayBuffer: async () => params.bytes.buffer,
} as Response); } as unknown as Response);
} }
function mockTelegramPngDownload(): ReturnType<typeof vi.spyOn> { function mockTelegramPngDownload(): ReturnType<typeof vi.spyOn> {
return vi.spyOn(globalThis, "fetch" as never).mockResolvedValue({ return vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true, ok: true,
status: 200, status: 200,
statusText: "OK", statusText: "OK",
headers: { get: () => "image/png" }, headers: { get: () => "image/png" },
arrayBuffer: async () => new Uint8Array([0x89, 0x50, 0x4e, 0x47]).buffer, arrayBuffer: async () => new Uint8Array([0x89, 0x50, 0x4e, 0x47]).buffer,
} as Response); } as unknown as Response);
} }
beforeEach(() => { beforeEach(() => {
@@ -147,7 +147,7 @@ describe("telegram inbound media", () => {
it("prefers proxyFetch over global fetch", async () => { it("prefers proxyFetch over global fetch", async () => {
const runtimeLog = vi.fn(); const runtimeLog = vi.fn();
const runtimeError = 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"); throw new Error("global fetch should not be called");
}); });
const proxyFetch = vi.fn().mockResolvedValueOnce({ const proxyFetch = vi.fn().mockResolvedValueOnce({
@@ -156,7 +156,7 @@ describe("telegram inbound media", () => {
statusText: "OK", statusText: "OK",
headers: { get: () => "image/jpeg" }, headers: { get: () => "image/jpeg" },
arrayBuffer: async () => new Uint8Array([0xff, 0xd8, 0xff]).buffer, arrayBuffer: async () => new Uint8Array([0xff, 0xd8, 0xff]).buffer,
} as Response); } as unknown as Response);
const { handler } = await createBotHandlerWithOptions({ const { handler } = await createBotHandlerWithOptions({
proxyFetch: proxyFetch as unknown as typeof fetch, proxyFetch: proxyFetch as unknown as typeof fetch,
@@ -190,7 +190,7 @@ describe("telegram inbound media", () => {
runtimeLog, runtimeLog,
runtimeError, runtimeError,
}); });
const fetchSpy = vi.spyOn(globalThis, "fetch" as never); const fetchSpy = vi.spyOn(globalThis, "fetch");
await handler({ await handler({
message: { message: {
@@ -385,13 +385,13 @@ describe("telegram stickers", () => {
cachedAt: "2026-01-20T10:00:00.000Z", 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, ok: true,
status: 200, status: 200,
statusText: "OK", statusText: "OK",
headers: { get: () => "image/webp" }, headers: { get: () => "image/webp" },
arrayBuffer: async () => new Uint8Array([0x52, 0x49, 0x46, 0x46]).buffer, arrayBuffer: async () => new Uint8Array([0x52, 0x49, 0x46, 0x46]).buffer,
} as Response); } as unknown as Response);
await handler({ await handler({
message: { message: {
@@ -435,7 +435,7 @@ describe("telegram stickers", () => {
"skips animated stickers (TGS format)", "skips animated stickers (TGS format)",
async () => { async () => {
const { handler, replySpy, runtimeError } = await createBotHandler(); const { handler, replySpy, runtimeError } = await createBotHandler();
const fetchSpy = vi.spyOn(globalThis, "fetch" as never); const fetchSpy = vi.spyOn(globalThis, "fetch");
await handler({ await handler({
message: { message: {
@@ -473,7 +473,7 @@ describe("telegram stickers", () => {
"skips video stickers (WEBM format)", "skips video stickers (WEBM format)",
async () => { async () => {
const { handler, replySpy, runtimeError } = await createBotHandler(); const { handler, replySpy, runtimeError } = await createBotHandler();
const fetchSpy = vi.spyOn(globalThis, "fetch" as never); const fetchSpy = vi.spyOn(globalThis, "fetch");
await handler({ await handler({
message: { message: {
@@ -520,7 +520,7 @@ describe("telegram text fragments", () => {
async () => { async () => {
const { createTelegramBot } = await import("./bot.js"); const { createTelegramBot } = await import("./bot.js");
const replyModule = await import("../auto-reply/reply.js"); const replyModule = await import("../auto-reply/reply.js");
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>; const replySpy = (replyModule as { __replySpy: ReturnType<typeof vi.fn> }).__replySpy;
onSpy.mockReset(); onSpy.mockReset();
replySpy.mockReset(); replySpy.mockReset();