From 3f03cdea56ab7a486664e47d98695cc24a681845 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 23 Feb 2026 13:57:34 +0000 Subject: [PATCH] test: optimize redundant suites for faster runtime --- extensions/msteams/src/attachments.test.ts | 4 + ...ne-commands-strips-it-before-agent.test.ts | 300 +++++++++--------- src/gateway/openresponses-http.ts | 72 +---- src/gateway/openresponses-parity.test.ts | 4 +- src/gateway/openresponses-prompt.ts | 70 ++++ ...s-media-file-path-no-file-download.test.ts | 59 ++++ ...udes-location-text-ctx-fields-pins.test.ts | 88 ----- 7 files changed, 288 insertions(+), 309 deletions(-) create mode 100644 src/gateway/openresponses-prompt.ts delete mode 100644 src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts diff --git a/extensions/msteams/src/attachments.test.ts b/extensions/msteams/src/attachments.test.ts index f33541cb8d3..42470e370b6 100644 --- a/extensions/msteams/src/attachments.test.ts +++ b/extensions/msteams/src/attachments.test.ts @@ -2,6 +2,10 @@ import type { PluginRuntime } from "openclaw/plugin-sdk"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { setMSTeamsRuntime } from "./runtime.js"; +vi.mock("openclaw/plugin-sdk", () => ({ + isPrivateIpAddress: () => false, +})); + /** Mock DNS resolver that always returns a public IP (for anti-SSRF validation in tests). */ const publicResolveFn = async () => ({ address: "13.107.136.10" }); diff --git a/src/auto-reply/reply.triggers.trigger-handling.handles-inline-commands-strips-it-before-agent.test.ts b/src/auto-reply/reply.triggers.trigger-handling.handles-inline-commands-strips-it-before-agent.test.ts index 45fda184b47..ff9521c9799 100644 --- a/src/auto-reply/reply.triggers.trigger-handling.handles-inline-commands-strips-it-before-agent.test.ts +++ b/src/auto-reply/reply.triggers.trigger-handling.handles-inline-commands-strips-it-before-agent.test.ts @@ -234,16 +234,11 @@ describe("trigger handling", () => { }); }); - it("drops top-level restricted commands for unauthorized senders", async () => { + it("enforces top-level command auth but keeps inline text for unauthorized senders", async () => { await withTempHome(async (home) => { for (const command of ["/status", "/whoami"] as const) { await expectUnauthorizedCommandDropped(home, command); } - }); - }); - - it("keeps inline commands for unauthorized senders", async () => { - await withTempHome(async (home) => { for (const command of ["/status", "/help"] as const) { const runEmbeddedPiAgentMock = mockEmbeddedOk(); const res = await runInlineUnauthorizedCommand({ @@ -305,109 +300,115 @@ describe("trigger handling", () => { }); }); - it("rejects elevated toggles when disabled", async () => { + it("enforces elevated toggles across enabled and mention scenarios", async () => { await withTempHome(async (home) => { - const cfg = makeWhatsAppElevatedCfg(home, { elevatedEnabled: false }); + const isolateStore = (cfg: ReturnType, label: string) => { + cfg.session = { ...cfg.session, store: join(home, `${label}.sessions.json`) }; + return cfg; + }; - const res = await getReplyFromConfig( - { - Body: "/elevated on", - From: "+1000", - To: "+2000", - Provider: "whatsapp", - SenderE164: "+1000", - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toContain("tools.elevated.enabled"); + { + const cfg = isolateStore(makeWhatsAppElevatedCfg(home, { elevatedEnabled: false }), "off"); + const res = await getReplyFromConfig( + { + Body: "/elevated on", + From: "+1000", + To: "+2000", + Provider: "whatsapp", + SenderE164: "+1000", + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("tools.elevated.enabled"); - const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8"); - const store = JSON.parse(storeRaw) as Record; - expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBeUndefined(); - }); - }); + const storeRaw = await fs.readFile(requireSessionStorePath(cfg), "utf-8"); + const store = JSON.parse(storeRaw) as Record; + expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBeUndefined(); + } - it("allows elevated off in groups without mention", async () => { - await withTempHome(async (home) => { - const cfg = makeWhatsAppElevatedCfg(home, { requireMentionInGroups: false }); + { + const cfg = isolateStore( + makeWhatsAppElevatedCfg(home, { requireMentionInGroups: false }), + "group-off", + ); + const res = await getReplyFromConfig( + { + Body: "/elevated off", + From: "whatsapp:group:123@g.us", + To: "whatsapp:+2000", + Provider: "whatsapp", + SenderE164: "+1000", + CommandAuthorized: true, + ChatType: "group", + WasMentioned: false, + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("Elevated mode disabled."); + const store = await readSessionStore(cfg); + expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("off"); + } - const res = await getReplyFromConfig( - { - Body: "/elevated off", - From: "whatsapp:group:123@g.us", - To: "whatsapp:+2000", - Provider: "whatsapp", - SenderE164: "+1000", - CommandAuthorized: true, - ChatType: "group", - WasMentioned: false, - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toContain("Elevated mode disabled."); + { + const cfg = isolateStore( + makeWhatsAppElevatedCfg(home, { requireMentionInGroups: true }), + "group-on", + ); + const res = await getReplyFromConfig( + { + Body: "/elevated on", + From: "whatsapp:group:123@g.us", + To: "whatsapp:+2000", + Provider: "whatsapp", + SenderE164: "+1000", + CommandAuthorized: true, + ChatType: "group", + WasMentioned: true, + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("Elevated mode set to ask"); + const store = await readSessionStore(cfg); + expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("on"); + } - const store = await readSessionStore(cfg); - expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("off"); - }); - }); - - it("allows elevated directive in groups when mentioned", async () => { - await withTempHome(async (home) => { - const cfg = makeWhatsAppElevatedCfg(home, { requireMentionInGroups: true }); - - const res = await getReplyFromConfig( - { - Body: "/elevated on", - From: "whatsapp:group:123@g.us", - To: "whatsapp:+2000", - Provider: "whatsapp", - SenderE164: "+1000", - CommandAuthorized: true, - ChatType: "group", - WasMentioned: true, - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toContain("Elevated mode set to ask"); - - const store = await readSessionStore(cfg); - expect(store["agent:main:whatsapp:group:123@g.us"]?.elevatedLevel).toBe("on"); - }); - }); - - it("ignores elevated directive in groups when not mentioned", async () => { - await withTempHome(async (home) => { - getRunEmbeddedPiAgentMock().mockResolvedValue({ - payloads: [{ text: "ok" }], - meta: { - durationMs: 1, - agentMeta: { sessionId: "s", provider: "p", model: "m" }, - }, - }); - const cfg = makeWhatsAppElevatedCfg(home, { requireMentionInGroups: false }); - - const res = await getReplyFromConfig( - { - Body: "/elevated on", - From: "whatsapp:group:123@g.us", - To: "whatsapp:+2000", - Provider: "whatsapp", - SenderE164: "+1000", - ChatType: "group", - WasMentioned: false, - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toBeUndefined(); - expect(getRunEmbeddedPiAgentMock()).not.toHaveBeenCalled(); + { + const cfg = isolateStore( + makeWhatsAppElevatedCfg(home, { requireMentionInGroups: false }), + "group-ignore", + ); + const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock(); + runEmbeddedPiAgentMock.mockClear(); + runEmbeddedPiAgentMock.mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { + durationMs: 1, + agentMeta: { sessionId: "s", provider: "p", model: "m" }, + }, + }); + const res = await getReplyFromConfig( + { + Body: "/elevated on", + From: "whatsapp:group:123@g.us", + To: "whatsapp:+2000", + Provider: "whatsapp", + SenderE164: "+1000", + ChatType: "group", + WasMentioned: false, + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toBeUndefined(); + expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled(); + } }); }); @@ -439,56 +440,57 @@ describe("trigger handling", () => { }); }); - it("uses tools.elevated.allowFrom.discord for elevated approval", async () => { + it("handles discord elevated allowlist and override behavior", async () => { await withTempHome(async (home) => { - const cfg = makeCfg(home); - cfg.tools = { elevated: { allowFrom: { discord: ["123"] } } }; + { + const cfg = makeCfg(home); + cfg.session = { ...cfg.session, store: join(home, "discord-allow.sessions.json") }; + cfg.tools = { elevated: { allowFrom: { discord: ["123"] } } }; - const res = await getReplyFromConfig( - { - Body: "/elevated on", - From: "discord:123", - To: "user:123", - Provider: "discord", - SenderName: "Peter Steinberger", - SenderUsername: "steipete", - SenderTag: "steipete", - CommandAuthorized: true, - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toContain("Elevated mode set to ask"); + const res = await getReplyFromConfig( + { + Body: "/elevated on", + From: "discord:123", + To: "user:123", + Provider: "discord", + SenderName: "Peter Steinberger", + SenderUsername: "steipete", + SenderTag: "steipete", + CommandAuthorized: true, + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("Elevated mode set to ask"); + const store = await readSessionStore(cfg); + expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBe("on"); + } - const store = await readSessionStore(cfg); - expect(store[MAIN_SESSION_KEY]?.elevatedLevel).toBe("on"); - }); - }); + { + const cfg = makeCfg(home); + cfg.session = { ...cfg.session, store: join(home, "discord-deny.sessions.json") }; + cfg.tools = { + elevated: { + allowFrom: { discord: [] }, + }, + }; - it("treats explicit discord elevated allowlist as override", async () => { - await withTempHome(async (home) => { - const cfg = makeCfg(home); - cfg.tools = { - elevated: { - allowFrom: { discord: [] }, - }, - }; - - const res = await getReplyFromConfig( - { - Body: "/elevated on", - From: "discord:123", - To: "user:123", - Provider: "discord", - SenderName: "steipete", - }, - {}, - cfg, - ); - const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toContain("tools.elevated.allowFrom.discord"); - expect(getRunEmbeddedPiAgentMock()).not.toHaveBeenCalled(); + const res = await getReplyFromConfig( + { + Body: "/elevated on", + From: "discord:123", + To: "user:123", + Provider: "discord", + SenderName: "steipete", + }, + {}, + cfg, + ); + const text = Array.isArray(res) ? res[0]?.text : res?.text; + expect(text).toContain("tools.elevated.allowFrom.discord"); + expect(getRunEmbeddedPiAgentMock()).not.toHaveBeenCalled(); + } }); }); diff --git a/src/gateway/openresponses-http.ts b/src/gateway/openresponses-http.ts index 791fdb5e68f..ab1a4a5e0d0 100644 --- a/src/gateway/openresponses-http.ts +++ b/src/gateway/openresponses-http.ts @@ -30,10 +30,6 @@ import { } from "../media/input-files.js"; import { defaultRuntime } from "../runtime.js"; import { resolveAssistantStreamDeltaText } from "./agent-event-assistant-text.js"; -import { - buildAgentMessageFromConversationEntries, - type ConversationEntry, -} from "./agent-prompt.js"; import type { AuthRateLimiter } from "./auth-rate-limit.js"; import type { ResolvedGatewayAuth } from "./auth.js"; import { sendJson, setSseHeaders, writeDone } from "./http-common.js"; @@ -41,14 +37,13 @@ import { handleGatewayPostJsonEndpoint } from "./http-endpoint-helpers.js"; import { resolveAgentIdForRequest, resolveSessionKey } from "./http-utils.js"; import { CreateResponseBodySchema, - type ContentPart, type CreateResponseBody, - type ItemParam, type OutputItem, type ResponseResource, type StreamingEvent, type Usage, } from "./open-responses.schema.js"; +import { buildAgentPrompt } from "./openresponses-prompt.js"; type OpenResponsesHttpOptions = { auth: ResolvedGatewayAuth; @@ -67,24 +62,6 @@ function writeSseEvent(res: ServerResponse, event: StreamingEvent) { res.write(`data: ${JSON.stringify(event)}\n\n`); } -function extractTextContent(content: string | ContentPart[]): string { - if (typeof content === "string") { - return content; - } - return content - .map((part) => { - if (part.type === "input_text") { - return part.text; - } - if (part.type === "output_text") { - return part.text; - } - return ""; - }) - .filter(Boolean) - .join("\n"); -} - type ResolvedResponsesLimits = { maxBodyBytes: number; maxUrlParts: number; @@ -172,52 +149,7 @@ function applyToolChoice(params: { return { tools }; } -export function buildAgentPrompt(input: string | ItemParam[]): { - message: string; - extraSystemPrompt?: string; -} { - if (typeof input === "string") { - return { message: input }; - } - - const systemParts: string[] = []; - const conversationEntries: ConversationEntry[] = []; - - for (const item of input) { - if (item.type === "message") { - const content = extractTextContent(item.content).trim(); - if (!content) { - continue; - } - - if (item.role === "system" || item.role === "developer") { - systemParts.push(content); - continue; - } - - const normalizedRole = item.role === "assistant" ? "assistant" : "user"; - const sender = normalizedRole === "assistant" ? "Assistant" : "User"; - - conversationEntries.push({ - role: normalizedRole, - entry: { sender, body: content }, - }); - } else if (item.type === "function_call_output") { - conversationEntries.push({ - role: "tool", - entry: { sender: `Tool:${item.call_id}`, body: item.output }, - }); - } - // Skip reasoning and item_reference for prompt building (Phase 1) - } - - const message = buildAgentMessageFromConversationEntries(conversationEntries); - - return { - message, - extraSystemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : undefined, - }; -} +export { buildAgentPrompt } from "./openresponses-prompt.js"; function resolveOpenResponsesSessionKey(params: { req: IncomingMessage; diff --git a/src/gateway/openresponses-parity.test.ts b/src/gateway/openresponses-parity.test.ts index 1f4212ab0a6..3e4b2dc535b 100644 --- a/src/gateway/openresponses-parity.test.ts +++ b/src/gateway/openresponses-parity.test.ts @@ -12,7 +12,7 @@ let InputFileContentPartSchema: typeof import("./open-responses.schema.js").Inpu let ToolDefinitionSchema: typeof import("./open-responses.schema.js").ToolDefinitionSchema; let CreateResponseBodySchema: typeof import("./open-responses.schema.js").CreateResponseBodySchema; let OutputItemSchema: typeof import("./open-responses.schema.js").OutputItemSchema; -let buildAgentPrompt: typeof import("./openresponses-http.js").buildAgentPrompt; +let buildAgentPrompt: typeof import("./openresponses-prompt.js").buildAgentPrompt; describe("OpenResponses Feature Parity", () => { beforeAll(async () => { @@ -23,7 +23,7 @@ describe("OpenResponses Feature Parity", () => { CreateResponseBodySchema, OutputItemSchema, } = await import("./open-responses.schema.js")); - ({ buildAgentPrompt } = await import("./openresponses-http.js")); + ({ buildAgentPrompt } = await import("./openresponses-prompt.js")); }); describe("Schema Validation", () => { diff --git a/src/gateway/openresponses-prompt.ts b/src/gateway/openresponses-prompt.ts new file mode 100644 index 00000000000..fad2d4787e8 --- /dev/null +++ b/src/gateway/openresponses-prompt.ts @@ -0,0 +1,70 @@ +import { + buildAgentMessageFromConversationEntries, + type ConversationEntry, +} from "./agent-prompt.js"; +import type { ContentPart, ItemParam } from "./open-responses.schema.js"; + +function extractTextContent(content: string | ContentPart[]): string { + if (typeof content === "string") { + return content; + } + return content + .map((part) => { + if (part.type === "input_text") { + return part.text; + } + if (part.type === "output_text") { + return part.text; + } + return ""; + }) + .filter(Boolean) + .join("\n"); +} + +export function buildAgentPrompt(input: string | ItemParam[]): { + message: string; + extraSystemPrompt?: string; +} { + if (typeof input === "string") { + return { message: input }; + } + + const systemParts: string[] = []; + const conversationEntries: ConversationEntry[] = []; + + for (const item of input) { + if (item.type === "message") { + const content = extractTextContent(item.content).trim(); + if (!content) { + continue; + } + + if (item.role === "system" || item.role === "developer") { + systemParts.push(content); + continue; + } + + const normalizedRole = item.role === "assistant" ? "assistant" : "user"; + const sender = normalizedRole === "assistant" ? "Assistant" : "User"; + + conversationEntries.push({ + role: normalizedRole, + entry: { sender, body: content }, + }); + } else if (item.type === "function_call_output") { + conversationEntries.push({ + role: "tool", + entry: { sender: `Tool:${item.call_id}`, body: item.output }, + }); + } + // Skip reasoning and item_reference for prompt building (Phase 1) + } + + const message = buildAgentMessageFromConversationEntries(conversationEntries); + + return { + message, + extraSystemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : undefined, + }; +} diff --git a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts index 88e9f29590e..47448cd0a6d 100644 --- a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts +++ b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts @@ -209,6 +209,65 @@ describe("telegram inbound media", () => { fetchSpy.mockRestore(); }); + + it("captures pin and venue location payload fields", async () => { + const { handler, replySpy } = await createBotHandler(); + + const cases = [ + { + message: { + chat: { id: 42, type: "private" as const }, + message_id: 5, + caption: "Meet here", + date: 1736380800, + location: { + latitude: 48.858844, + longitude: 2.294351, + horizontal_accuracy: 12, + }, + }, + assert: (payload: Record) => { + expect(payload.Body).toContain("Meet here"); + expect(payload.Body).toContain("48.858844"); + expect(payload.LocationLat).toBe(48.858844); + expect(payload.LocationLon).toBe(2.294351); + expect(payload.LocationSource).toBe("pin"); + expect(payload.LocationIsLive).toBe(false); + }, + }, + { + message: { + chat: { id: 42, type: "private" as const }, + message_id: 6, + date: 1736380800, + venue: { + title: "Eiffel Tower", + address: "Champ de Mars, Paris", + location: { latitude: 48.858844, longitude: 2.294351 }, + }, + }, + assert: (payload: Record) => { + expect(payload.Body).toContain("Eiffel Tower"); + expect(payload.LocationName).toBe("Eiffel Tower"); + expect(payload.LocationAddress).toBe("Champ de Mars, Paris"); + expect(payload.LocationSource).toBe("place"); + }, + }, + ] as const; + + for (const testCase of cases) { + replySpy.mockClear(); + await handler({ + message: testCase.message, + me: { username: "openclaw_bot" }, + getFile: async () => ({ file_path: "unused" }), + }); + + expect(replySpy).toHaveBeenCalledTimes(1); + const payload = replySpy.mock.calls[0][0] as Record; + testCase.assert(payload); + } + }); }); describe("telegram media groups", () => { diff --git a/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts b/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts deleted file mode 100644 index 2bc104fba34..00000000000 --- a/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { onSpy } from "./bot.media.e2e-harness.js"; - -let handler: (ctx: Record) => Promise; -let replySpy: ReturnType; - -beforeAll(async () => { - const { createTelegramBot } = await import("./bot.js"); - const replyModule = await import("../auto-reply/reply.js"); - replySpy = (replyModule as unknown as { __replySpy: ReturnType }).__replySpy; - - onSpy.mockClear(); - createTelegramBot({ token: "tok" }); - const registeredHandler = onSpy.mock.calls.find((call) => call[0] === "message")?.[1] as ( - ctx: Record, - ) => Promise; - expect(registeredHandler).toBeDefined(); - handler = registeredHandler; -}); - -beforeEach(() => { - replySpy.mockClear(); -}); - -function expectSingleReplyPayload(replySpy: ReturnType) { - expect(replySpy).toHaveBeenCalledTimes(1); - return replySpy.mock.calls[0][0] as Record; -} - -describe("telegram inbound media", () => { - const _INBOUND_MEDIA_TEST_TIMEOUT_MS = process.platform === "win32" ? 30_000 : 20_000; - it( - "includes location text and ctx fields for pins", - async () => { - await handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 5, - caption: "Meet here", - date: 1736380800, - location: { - latitude: 48.858844, - longitude: 2.294351, - horizontal_accuracy: 12, - }, - }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "unused" }), - }); - - const payload = expectSingleReplyPayload(replySpy); - expect(payload.Body).toContain("Meet here"); - expect(payload.Body).toContain("48.858844"); - expect(payload.LocationLat).toBe(48.858844); - expect(payload.LocationLon).toBe(2.294351); - expect(payload.LocationSource).toBe("pin"); - expect(payload.LocationIsLive).toBe(false); - }, - _INBOUND_MEDIA_TEST_TIMEOUT_MS, - ); - - it( - "captures venue fields for named places", - async () => { - await handler({ - message: { - chat: { id: 42, type: "private" }, - message_id: 6, - date: 1736380800, - venue: { - title: "Eiffel Tower", - address: "Champ de Mars, Paris", - location: { latitude: 48.858844, longitude: 2.294351 }, - }, - }, - me: { username: "openclaw_bot" }, - getFile: async () => ({ file_path: "unused" }), - }); - - const payload = expectSingleReplyPayload(replySpy); - expect(payload.Body).toContain("Eiffel Tower"); - expect(payload.LocationName).toBe("Eiffel Tower"); - expect(payload.LocationAddress).toBe("Champ de Mars, Paris"); - expect(payload.LocationSource).toBe("place"); - }, - _INBOUND_MEDIA_TEST_TIMEOUT_MS, - ); -});