test(auto-reply): speed up reply prompt tests

This commit is contained in:
Peter Steinberger
2026-04-06 18:33:37 +01:00
parent e6c1e9c64b
commit ff8f46884a
2 changed files with 62 additions and 148 deletions

View File

@@ -1,91 +1,30 @@
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
import type { OpenClawConfig } from "../config/config.js";
import {
createReplyRuntimeMocks,
installReplyRuntimeMocks,
makeEmbeddedTextResult,
resetReplyRuntimeMocks,
} from "./reply.test-harness.js";
let getReplyFromConfig: typeof import("./reply.js").getReplyFromConfig;
const agentMocks = createReplyRuntimeMocks();
installReplyRuntimeMocks(agentMocks);
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
return withTempHomeBase(
async (home) => {
agentMocks.runEmbeddedPiAgent.mockClear();
return await fn(home);
},
{
env: {
OPENCLAW_BUNDLED_SKILLS_DIR: (home) => path.join(home, "bundled-skills"),
},
prefix: "openclaw-media-note-",
},
);
}
function makeCfg(home: string) {
return {
agents: {
defaults: {
model: "anthropic/claude-opus-4-6",
workspace: path.join(home, "openclaw"),
},
},
channels: { whatsapp: { allowFrom: ["*"] } },
session: { store: path.join(home, "sessions.json") },
} as unknown as OpenClawConfig;
}
import { describe, expect, it } from "vitest";
import { finalizeInboundContext } from "./reply/inbound-context.js";
import { buildReplyPromptBodies } from "./reply/prompt-prelude.js";
describe("getReplyFromConfig media note plumbing", () => {
beforeEach(async () => {
vi.resetModules();
vi.stubEnv("OPENCLAW_TEST_FAST", "1");
resetReplyRuntimeMocks(agentMocks);
({ getReplyFromConfig } = await import("./reply.js"));
});
afterEach(() => {
vi.clearAllMocks();
});
it("includes all MediaPaths in the agent prompt", async () => {
await withTempHome(async (home) => {
let seenPrompt: string | undefined;
agentMocks.runEmbeddedPiAgent.mockImplementation(async (params) => {
seenPrompt = params.prompt;
return makeEmbeddedTextResult("ok");
});
const cfg = makeCfg(home);
const res = await getReplyFromConfig(
{
Body: "hello",
From: "+1001",
To: "+2000",
MediaPaths: ["/tmp/a.png", "/tmp/b.png"],
MediaUrls: ["/tmp/a.png", "/tmp/b.png"],
},
{},
cfg,
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toBe("ok");
expect(seenPrompt).toBeTruthy();
expect(seenPrompt).toContain("[media attached: 2 files]");
const idxA = seenPrompt?.indexOf("[media attached 1/2: /tmp/a.png");
const idxB = seenPrompt?.indexOf("[media attached 2/2: /tmp/b.png");
expect(typeof idxA).toBe("number");
expect(typeof idxB).toBe("number");
expect((idxA ?? -1) >= 0).toBe(true);
expect((idxB ?? -1) >= 0).toBe(true);
expect((idxA ?? 0) < (idxB ?? 0)).toBe(true);
it("includes all MediaPaths in the agent prompt", () => {
const sessionCtx = finalizeInboundContext({
Body: "hello",
BodyForAgent: "hello",
From: "+1001",
To: "+2000",
MediaPaths: ["/tmp/a.png", "/tmp/b.png"],
MediaUrls: ["/tmp/a.png", "/tmp/b.png"],
});
const prompt = buildReplyPromptBodies({
ctx: sessionCtx,
sessionCtx,
effectiveBaseBody: sessionCtx.BodyForAgent,
prefixedBody: sessionCtx.BodyForAgent,
}).prefixedCommandBody;
expect(prompt).toContain("[media attached: 2 files]");
const idxA = prompt.indexOf("[media attached 1/2: /tmp/a.png");
const idxB = prompt.indexOf("[media attached 2/2: /tmp/b.png");
expect(idxA).toBeGreaterThanOrEqual(0);
expect(idxB).toBeGreaterThanOrEqual(0);
expect(idxA).toBeLessThan(idxB);
expect(prompt).toContain("hello");
});
});

View File

@@ -1,67 +1,42 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
createReplyRuntimeMocks,
createTempHomeHarness,
installReplyRuntimeMocks,
makeEmbeddedTextResult,
makeReplyConfig,
resetReplyRuntimeMocks,
} from "./reply.test-harness.js";
let getReplyFromConfig: typeof import("./reply.js").getReplyFromConfig;
const agentMocks = createReplyRuntimeMocks();
const { withTempHome } = createTempHomeHarness({ prefix: "openclaw-rawbody-" });
installReplyRuntimeMocks(agentMocks);
import { describe, expect, it } from "vitest";
import { parseInlineDirectives } from "./reply/directive-handling.parse.js";
import { finalizeInboundContext } from "./reply/inbound-context.js";
import { buildInboundUserContextPrefix } from "./reply/inbound-meta.js";
import { buildReplyPromptBodies } from "./reply/prompt-prelude.js";
describe("RawBody directive parsing", () => {
beforeEach(async () => {
vi.resetModules();
vi.stubEnv("OPENCLAW_TEST_FAST", "1");
resetReplyRuntimeMocks(agentMocks);
({ getReplyFromConfig } = await import("./reply.js"));
});
afterEach(() => {
vi.clearAllMocks();
});
it("handles directives and history in the prompt", async () => {
await withTempHome(async (home) => {
agentMocks.runEmbeddedPiAgent.mockResolvedValue(makeEmbeddedTextResult("ok"));
const groupMessageCtx = {
Body: "/think:high status please",
BodyForAgent: "/think:high status please",
RawBody: "/think:high status please",
InboundHistory: [{ sender: "Peter", body: "hello", timestamp: 1700000000000 }],
From: "+1222",
To: "+1222",
ChatType: "group",
GroupSubject: "Ops",
SenderName: "Jake McInteer",
SenderE164: "+6421807830",
CommandAuthorized: true,
};
const res = await getReplyFromConfig(
groupMessageCtx,
{},
makeReplyConfig(home) as OpenClawConfig,
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toBe("ok");
expect(agentMocks.runEmbeddedPiAgent).toHaveBeenCalledOnce();
const prompt =
(agentMocks.runEmbeddedPiAgent.mock.calls[0]?.[0] as { prompt?: string } | undefined)
?.prompt ?? "";
expect(prompt).toContain("Chat history since last reply (untrusted, for context):");
expect(prompt).toContain('"sender": "Peter"');
expect(prompt).toContain('"body": "hello"');
expect(prompt).toContain("status please");
expect(prompt).not.toContain("/think:high");
it("handles directives and history in the prompt", () => {
const sessionCtx = finalizeInboundContext({
Body: "/think:high status please",
BodyForAgent: "/think:high status please",
BodyForCommands: "/think:high status please",
RawBody: "/think:high status please",
InboundHistory: [{ sender: "Peter", body: "hello", timestamp: 1700000000000 }],
From: "+1222",
To: "+1222",
ChatType: "group",
GroupSubject: "Ops",
SenderName: "Jake McInteer",
SenderE164: "+6421807830",
CommandAuthorized: true,
});
const directives = parseInlineDirectives(sessionCtx.BodyForCommands ?? "", {
allowStatusDirective: true,
});
const prefixedBody = [buildInboundUserContextPrefix(sessionCtx), directives.cleaned]
.filter(Boolean)
.join("\n\n");
const prompt = buildReplyPromptBodies({
ctx: sessionCtx,
sessionCtx: { ...sessionCtx, BodyStripped: directives.cleaned },
effectiveBaseBody: prefixedBody,
prefixedBody,
}).prefixedCommandBody;
expect(prompt).toContain("Chat history since last reply (untrusted, for context):");
expect(prompt).toContain('"sender": "Peter"');
expect(prompt).toContain('"body": "hello"');
expect(prompt).toContain("status please");
expect(prompt).not.toContain("/think:high");
});
});