From 43fa394b83bea10deb971ada8a2f0d21cab2dc3e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 19:55:22 +0100 Subject: [PATCH] test: share get-reply edge fixtures --- ...et-reply-directives.target-session.test.ts | 156 +++++++----------- ...ine-actions.skip-when-config-empty.test.ts | 74 ++++----- .../reply/get-reply-run.media-only.test.ts | 27 ++- 3 files changed, 103 insertions(+), 154 deletions(-) diff --git a/src/auto-reply/reply/get-reply-directives.target-session.test.ts b/src/auto-reply/reply/get-reply-directives.target-session.test.ts index 60b526a3d4f..b199a05576e 100644 --- a/src/auto-reply/reply/get-reply-directives.target-session.test.ts +++ b/src/auto-reply/reply/get-reply-directives.target-session.test.ts @@ -86,6 +86,62 @@ function parseInlineDirectivesForTest(body: string) { }; } +async function resolveHelloWithModelDefaults(params: { + defaultThinking: "off" | "low"; + defaultReasoning: "on"; +}) { + const resolveDefaultThinkingLevel = vi.fn(async () => params.defaultThinking); + const resolveDefaultReasoningLevel = vi.fn(async () => params.defaultReasoning); + mocks.createModelSelectionState.mockResolvedValueOnce({ + provider: "openai", + model: "gpt-4o-mini", + allowedModelKeys: new Set(), + allowedModelCatalog: [], + resetModelOverride: false, + resolveDefaultThinkingLevel, + resolveDefaultReasoningLevel, + }); + + const result = await resolveReplyDirectives({ + ctx: buildTestCtx({ + Body: "hello", + CommandBody: "hello", + }), + cfg: {}, + agentId: "main", + agentDir: "/tmp/main-agent", + workspaceDir: "/tmp", + agentCfg: {}, + sessionCtx: { + Body: "hello", + BodyStripped: "hello", + BodyForAgent: "hello", + CommandBody: "hello", + Provider: "whatsapp", + } as TemplateContext, + sessionEntry: makeSessionEntry(), + sessionStore: {}, + sessionKey: "agent:main:whatsapp:+2000", + storePath: "/tmp/sessions.json", + sessionScope: "per-sender", + groupResolution: undefined, + isGroup: false, + triggerBodyNormalized: "hello", + commandAuthorized: false, + defaultProvider: "openai", + defaultModel: "gpt-4o-mini", + aliasIndex: { byAlias: new Map(), byKey: new Map() }, + provider: "openai", + model: "gpt-4o-mini", + hasResolvedHeartbeatModelOverride: false, + typing: makeTypingController(), + opts: undefined, + skillFilter: undefined, + }); + + return { result, resolveDefaultReasoningLevel }; +} + vi.mock("../../agents/agent-scope.js", () => ({ listAgentEntries: vi.fn(() => []), })); @@ -356,53 +412,9 @@ describe("resolveReplyDirectives", () => { }); it("uses the model reasoning default when thinking is off", async () => { - const resolveDefaultThinkingLevel = vi.fn(async () => "off"); - const resolveDefaultReasoningLevel = vi.fn(async () => "on"); - mocks.createModelSelectionState.mockResolvedValueOnce({ - provider: "openai", - model: "gpt-4o-mini", - allowedModelKeys: new Set(), - allowedModelCatalog: [], - resetModelOverride: false, - resolveDefaultThinkingLevel, - resolveDefaultReasoningLevel, - }); - - const result = await resolveReplyDirectives({ - ctx: buildTestCtx({ - Body: "hello", - CommandBody: "hello", - }), - cfg: {}, - agentId: "main", - agentDir: "/tmp/main-agent", - workspaceDir: "/tmp", - agentCfg: {}, - sessionCtx: { - Body: "hello", - BodyStripped: "hello", - BodyForAgent: "hello", - CommandBody: "hello", - Provider: "whatsapp", - } as TemplateContext, - sessionEntry: makeSessionEntry(), - sessionStore: {}, - sessionKey: "agent:main:whatsapp:+2000", - storePath: "/tmp/sessions.json", - sessionScope: "per-sender", - groupResolution: undefined, - isGroup: false, - triggerBodyNormalized: "hello", - commandAuthorized: false, - defaultProvider: "openai", - defaultModel: "gpt-4o-mini", - aliasIndex: { byAlias: new Map(), byKey: new Map() }, - provider: "openai", - model: "gpt-4o-mini", - hasResolvedHeartbeatModelOverride: false, - typing: makeTypingController(), - opts: undefined, - skillFilter: undefined, + const { result, resolveDefaultReasoningLevel } = await resolveHelloWithModelDefaults({ + defaultThinking: "off", + defaultReasoning: "on", }); expect(result).toEqual({ @@ -416,53 +428,9 @@ describe("resolveReplyDirectives", () => { }); it("skips the model reasoning default when thinking is active", async () => { - const resolveDefaultThinkingLevel = vi.fn(async () => "low"); - const resolveDefaultReasoningLevel = vi.fn(async () => "on"); - mocks.createModelSelectionState.mockResolvedValueOnce({ - provider: "openai", - model: "gpt-4o-mini", - allowedModelKeys: new Set(), - allowedModelCatalog: [], - resetModelOverride: false, - resolveDefaultThinkingLevel, - resolveDefaultReasoningLevel, - }); - - const result = await resolveReplyDirectives({ - ctx: buildTestCtx({ - Body: "hello", - CommandBody: "hello", - }), - cfg: {}, - agentId: "main", - agentDir: "/tmp/main-agent", - workspaceDir: "/tmp", - agentCfg: {}, - sessionCtx: { - Body: "hello", - BodyStripped: "hello", - BodyForAgent: "hello", - CommandBody: "hello", - Provider: "whatsapp", - } as TemplateContext, - sessionEntry: makeSessionEntry(), - sessionStore: {}, - sessionKey: "agent:main:whatsapp:+2000", - storePath: "/tmp/sessions.json", - sessionScope: "per-sender", - groupResolution: undefined, - isGroup: false, - triggerBodyNormalized: "hello", - commandAuthorized: false, - defaultProvider: "openai", - defaultModel: "gpt-4o-mini", - aliasIndex: { byAlias: new Map(), byKey: new Map() }, - provider: "openai", - model: "gpt-4o-mini", - hasResolvedHeartbeatModelOverride: false, - typing: makeTypingController(), - opts: undefined, - skillFilter: undefined, + const { result, resolveDefaultReasoningLevel } = await resolveHelloWithModelDefaults({ + defaultThinking: "low", + defaultReasoning: "on", }); expect(result).toEqual({ diff --git a/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts b/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts index 51f8f4d375c..b16d1a39f95 100644 --- a/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts +++ b/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts @@ -116,6 +116,33 @@ async function expectInlineActionSkipped(params: { expect(handleCommandsMock).not.toHaveBeenCalled(); } +async function runInlineStatusAction(storePath?: string) { + const typing = createTypingController(); + const ctx = buildTestCtx({ + Body: "/status", + CommandBody: "/status", + }); + const result = await handleInlineActions( + createHandleInlineActionsInput({ + ctx, + typing, + cleanedBody: stripInlineStatus("/status").cleaned, + command: { + isAuthorizedSender: true, + rawBodyNormalized: "/status", + commandBodyNormalized: "/status", + }, + overrides: { + allowTextCommands: true, + inlineStatusRequested: true, + ...(storePath ? { storePath } : {}), + }, + }), + ); + + return { result, typing }; +} + describe("handleInlineActions", () => { beforeEach(() => { handleCommandsMock.mockReset(); @@ -235,28 +262,7 @@ describe("handleInlineActions", () => { }); it("does not run command handlers after replying to an inline status-only turn", async () => { - const typing = createTypingController(); - const ctx = buildTestCtx({ - Body: "/status", - CommandBody: "/status", - }); - - const result = await handleInlineActions( - createHandleInlineActionsInput({ - ctx, - typing, - cleanedBody: stripInlineStatus("/status").cleaned, - command: { - isAuthorizedSender: true, - rawBodyNormalized: "/status", - commandBodyNormalized: "/status", - }, - overrides: { - allowTextCommands: true, - inlineStatusRequested: true, - }, - }), - ); + const { result, typing } = await runInlineStatusAction(); expect(result).toEqual({ kind: "reply", reply: undefined }); expect(buildStatusReplyMock).toHaveBeenCalledTimes(1); @@ -270,29 +276,7 @@ describe("handleInlineActions", () => { }); it("preserves storePath when routing inline status through the shared status builder", async () => { - const typing = createTypingController(); - const ctx = buildTestCtx({ - Body: "/status", - CommandBody: "/status", - }); - - const result = await handleInlineActions( - createHandleInlineActionsInput({ - ctx, - typing, - cleanedBody: stripInlineStatus("/status").cleaned, - command: { - isAuthorizedSender: true, - rawBodyNormalized: "/status", - commandBodyNormalized: "/status", - }, - overrides: { - allowTextCommands: true, - inlineStatusRequested: true, - storePath: "/tmp/inline-status-store.json", - }, - }), - ); + const { result } = await runInlineStatusAction("/tmp/inline-status-store.json"); expect(result).toEqual({ kind: "reply", reply: undefined }); expect(buildStatusReplyMock).toHaveBeenCalledWith( diff --git a/src/auto-reply/reply/get-reply-run.media-only.test.ts b/src/auto-reply/reply/get-reply-run.media-only.test.ts index 3670bfbf964..098686f5f44 100644 --- a/src/auto-reply/reply/get-reply-run.media-only.test.ts +++ b/src/auto-reply/reply/get-reply-run.media-only.test.ts @@ -206,6 +206,15 @@ function baseParams( }; } +function ownerParams(): Parameters[0] { + const params = baseParams(); + params.command = { + ...(params.command as Record), + senderIsOwner: true, + } as never; + return params; +} + describe("runPreparedReply media-only handling", () => { beforeAll(async () => { ({ runPreparedReply } = await import("./get-reply-run.js")); @@ -752,11 +761,7 @@ describe("runPreparedReply media-only handling", () => { vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce( "System (untrusted): [t] External webhook payload.", ); - const params = baseParams(); - params.command = { - ...(params.command as Record), - senderIsOwner: true, - } as never; + const params = ownerParams(); await runPreparedReply(params); @@ -766,11 +771,7 @@ describe("runPreparedReply media-only handling", () => { it("keeps sender ownership when drained system events are trusted", async () => { vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce("System: [t] Trusted event."); - const params = baseParams(); - params.command = { - ...(params.command as Record), - senderIsOwner: true, - } as never; + const params = ownerParams(); await runPreparedReply(params); @@ -782,11 +783,7 @@ describe("runPreparedReply media-only handling", () => { vi.mocked(drainFormattedSystemEvents).mockResolvedValueOnce( "System: [t] Relay text mentions System (untrusted): but event is trusted.", ); - const params = baseParams(); - params.command = { - ...(params.command as Record), - senderIsOwner: true, - } as never; + const params = ownerParams(); await runPreparedReply(params);