mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:40:44 +00:00
test: share auto reply trigger fixtures
This commit is contained in:
@@ -24,6 +24,14 @@ type GetReplyFromConfig = typeof import("./reply.js").getReplyFromConfig;
|
||||
|
||||
const TEST_PRIMARY_PROFILE_ID = "openai-codex:primary@example.test";
|
||||
const TEST_SECONDARY_PROFILE_ID = "openai-codex:secondary@example.test";
|
||||
const TEST_TIME_ZONE = "America/Chicago";
|
||||
const TELEGRAM_DIRECT_MESSAGE = {
|
||||
From: "telegram:111",
|
||||
To: "telegram:111",
|
||||
ChatType: "direct",
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
} as const;
|
||||
|
||||
vi.mock("./reply/agent-runner.runtime.js", () => ({
|
||||
runReplyAgent: async (params: {
|
||||
@@ -115,6 +123,111 @@ function mockEmbeddedOkPayload() {
|
||||
return mockRunEmbeddedPiAgentOk("ok");
|
||||
}
|
||||
|
||||
function mockRunEmbeddedPiAgentText(text: string, durationMs: number) {
|
||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
runEmbeddedPiAgentMock.mockResolvedValue({
|
||||
payloads: [{ text }],
|
||||
meta: {
|
||||
durationMs,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
return runEmbeddedPiAgentMock;
|
||||
}
|
||||
|
||||
async function writeDailyMemoryNotes(
|
||||
workspaceDir: string,
|
||||
notes: Array<{ stamp: string; text: string }>,
|
||||
) {
|
||||
const memoryDir = join(workspaceDir, "memory");
|
||||
await fs.mkdir(memoryDir, { recursive: true });
|
||||
for (const note of notes) {
|
||||
await fs.writeFile(join(memoryDir, `${note.stamp}.md`), note.text, "utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
async function seedTargetSession(storePath: string, targetSessionKey: string) {
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify({
|
||||
[targetSessionKey]: {
|
||||
sessionId: "session-target",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function makeNativeTelegramCommandMessage(params: {
|
||||
body: string;
|
||||
slashSessionKey: string;
|
||||
targetSessionKey: string;
|
||||
}) {
|
||||
return {
|
||||
Body: params.body,
|
||||
...TELEGRAM_DIRECT_MESSAGE,
|
||||
SessionKey: params.slashSessionKey,
|
||||
CommandSource: "native",
|
||||
CommandTargetSessionKey: params.targetSessionKey,
|
||||
CommandAuthorized: true,
|
||||
};
|
||||
}
|
||||
|
||||
function makeTelegramSessionMessage(body: string, sessionKey: string) {
|
||||
return {
|
||||
Body: body,
|
||||
...TELEGRAM_DIRECT_MESSAGE,
|
||||
SessionKey: sessionKey,
|
||||
};
|
||||
}
|
||||
|
||||
function makeAuthorizedSmsCommandMessage(body: string) {
|
||||
return {
|
||||
Body: body,
|
||||
From: "+1003",
|
||||
To: "+2000",
|
||||
CommandAuthorized: true,
|
||||
};
|
||||
}
|
||||
|
||||
function makeStartupContextCfg(home: string, startupContext?: { applyOn: Array<"new" | "reset"> }) {
|
||||
const cfg = makeCfg(home);
|
||||
cfg.agents ??= {};
|
||||
cfg.agents.defaults ??= {};
|
||||
cfg.agents.defaults.userTimezone = TEST_TIME_ZONE;
|
||||
if (startupContext) {
|
||||
cfg.agents.defaults.startupContext = startupContext;
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
async function runAuthorizedSmsCommand(body: string, cfg: ReturnType<typeof makeCfg>) {
|
||||
return await getReplyFromConfig(makeAuthorizedSmsCommandMessage(body), {}, cfg);
|
||||
}
|
||||
|
||||
async function expectNextRunUsesTargetSession(
|
||||
params: {
|
||||
cfg: ReturnType<typeof makeCfg>;
|
||||
targetSessionKey: string;
|
||||
runEmbeddedPiAgentMock: ReturnType<typeof getRunEmbeddedPiAgentMock>;
|
||||
},
|
||||
expected: Record<string, unknown>,
|
||||
) {
|
||||
mockRunEmbeddedPiAgentText("ok", 5);
|
||||
|
||||
await getReplyFromConfig(
|
||||
makeTelegramSessionMessage("hi", params.targetSessionKey),
|
||||
{},
|
||||
params.cfg,
|
||||
);
|
||||
|
||||
expect(params.runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(params.runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toEqual(
|
||||
expect.objectContaining(expected),
|
||||
);
|
||||
}
|
||||
|
||||
async function writeStoredModelOverride(cfg: ReturnType<typeof makeCfg>): Promise<void> {
|
||||
await fs.writeFile(
|
||||
requireSessionStorePath(cfg),
|
||||
@@ -264,50 +377,21 @@ describe("trigger handling", () => {
|
||||
it("prepends runtime-loaded daily memory context on bare /new", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const workspaceDir = join(home, "openclaw");
|
||||
const timeZone = "America/Chicago";
|
||||
const nowMs = Date.now();
|
||||
const todayStamp = formatDateStampForZone(nowMs, timeZone);
|
||||
const yesterdayStamp = formatDateStampForZone(nowMs - 24 * 60 * 60 * 1000, timeZone);
|
||||
await fs.mkdir(join(workspaceDir, "memory"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
join(workspaceDir, "memory", `${todayStamp}.md`),
|
||||
"today startup note",
|
||||
"utf-8",
|
||||
);
|
||||
await fs.writeFile(
|
||||
join(workspaceDir, "memory", `${yesterdayStamp}.md`),
|
||||
"yesterday startup note",
|
||||
"utf-8",
|
||||
);
|
||||
const todayStamp = formatDateStampForZone(nowMs, TEST_TIME_ZONE);
|
||||
const yesterdayStamp = formatDateStampForZone(nowMs - 24 * 60 * 60 * 1000, TEST_TIME_ZONE);
|
||||
await writeDailyMemoryNotes(workspaceDir, [
|
||||
{ stamp: todayStamp, text: "today startup note" },
|
||||
{ stamp: yesterdayStamp, text: "yesterday startup note" },
|
||||
]);
|
||||
|
||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
runEmbeddedPiAgentMock.mockResolvedValue({
|
||||
payloads: [{ text: "hello" }],
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
const runEmbeddedPiAgentMock = mockRunEmbeddedPiAgentText("hello", 1);
|
||||
|
||||
const cfg = makeCfg(home);
|
||||
cfg.agents ??= {};
|
||||
cfg.agents.defaults ??= {};
|
||||
cfg.agents.defaults.userTimezone = timeZone;
|
||||
const cfg = makeStartupContextCfg(home);
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "/new",
|
||||
From: "+1003",
|
||||
To: "+2000",
|
||||
CommandAuthorized: true,
|
||||
},
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
const res = await runAuthorizedSmsCommand("/new", cfg);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toBe("hello");
|
||||
expect(maybeReplyText(res)).toBe("hello");
|
||||
const prompt = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0]?.prompt ?? "";
|
||||
expect(prompt).toContain("[Startup context loaded by runtime]");
|
||||
expect(prompt).toContain(`[Untrusted daily memory: memory/${todayStamp}.md]`);
|
||||
@@ -321,47 +405,19 @@ describe("trigger handling", () => {
|
||||
it("treats normalized /RESET as reset for startupContext.applyOn", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const workspaceDir = join(home, "openclaw");
|
||||
const timeZone = "America/Chicago";
|
||||
const nowMs = Date.now();
|
||||
const todayStamp = formatDateStampForZone(nowMs, timeZone);
|
||||
await fs.mkdir(join(workspaceDir, "memory"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
join(workspaceDir, "memory", `${todayStamp}.md`),
|
||||
"reset startup note",
|
||||
"utf-8",
|
||||
);
|
||||
const todayStamp = formatDateStampForZone(nowMs, TEST_TIME_ZONE);
|
||||
await writeDailyMemoryNotes(workspaceDir, [
|
||||
{ stamp: todayStamp, text: "reset startup note" },
|
||||
]);
|
||||
|
||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
runEmbeddedPiAgentMock.mockResolvedValue({
|
||||
payloads: [{ text: "hello" }],
|
||||
meta: {
|
||||
durationMs: 1,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
const runEmbeddedPiAgentMock = mockRunEmbeddedPiAgentText("hello", 1);
|
||||
|
||||
const cfg = makeCfg(home);
|
||||
cfg.agents ??= {};
|
||||
cfg.agents.defaults ??= {};
|
||||
cfg.agents.defaults.userTimezone = timeZone;
|
||||
cfg.agents.defaults.startupContext = {
|
||||
applyOn: ["reset"],
|
||||
};
|
||||
const cfg = makeStartupContextCfg(home, { applyOn: ["reset"] });
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "/RESET",
|
||||
From: "+1003",
|
||||
To: "+2000",
|
||||
CommandAuthorized: true,
|
||||
},
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
const res = await runAuthorizedSmsCommand("/RESET", cfg);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toBe("hello");
|
||||
expect(maybeReplyText(res)).toBe("hello");
|
||||
const prompt = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0]?.prompt ?? "";
|
||||
expect(prompt).toContain(`[Untrusted daily memory: memory/${todayStamp}.md]`);
|
||||
expect(prompt).toContain("reset startup note");
|
||||
@@ -594,77 +650,35 @@ describe("trigger handling", () => {
|
||||
cfg.session = { ...cfg.session, store: join(home, "native-model.sessions.json") };
|
||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
const storePath = cfg.session?.store;
|
||||
if (!storePath) {
|
||||
throw new Error("missing session store path");
|
||||
}
|
||||
const storePath = requireSessionStorePath(cfg);
|
||||
const slashSessionKey = "telegram:slash:111";
|
||||
const targetSessionKey = MAIN_SESSION_KEY;
|
||||
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify({
|
||||
[targetSessionKey]: {
|
||||
sessionId: "session-target",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
await seedTargetSession(storePath, targetSessionKey);
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: "/model openai/gpt-4.1-mini",
|
||||
From: "telegram:111",
|
||||
To: "telegram:111",
|
||||
ChatType: "direct",
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
SessionKey: slashSessionKey,
|
||||
CommandSource: "native",
|
||||
CommandTargetSessionKey: targetSessionKey,
|
||||
CommandAuthorized: true,
|
||||
},
|
||||
makeNativeTelegramCommandMessage({
|
||||
body: "/model openai/gpt-4.1-mini",
|
||||
slashSessionKey,
|
||||
targetSessionKey,
|
||||
}),
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Model set to openai/gpt-4.1-mini");
|
||||
expect(maybeReplyText(res)).toContain("Model set to openai/gpt-4.1-mini");
|
||||
|
||||
const store = loadSessionStore(storePath);
|
||||
expect(store[targetSessionKey]?.providerOverride).toBe("openai");
|
||||
expect(store[targetSessionKey]?.modelOverride).toBe("gpt-4.1-mini");
|
||||
expect(store[slashSessionKey]).toBeUndefined();
|
||||
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
runEmbeddedPiAgentMock.mockResolvedValue({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
|
||||
await getReplyFromConfig(
|
||||
await expectNextRunUsesTargetSession(
|
||||
{ cfg, targetSessionKey, runEmbeddedPiAgentMock },
|
||||
{
|
||||
Body: "hi",
|
||||
From: "telegram:111",
|
||||
To: "telegram:111",
|
||||
ChatType: "direct",
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
SessionKey: targetSessionKey,
|
||||
},
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai",
|
||||
model: "gpt-4.1-mini",
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -675,10 +689,7 @@ describe("trigger handling", () => {
|
||||
cfg.session = { ...cfg.session, store: join(home, "native-model-auth.sessions.json") };
|
||||
const runEmbeddedPiAgentMock = getRunEmbeddedPiAgentMock();
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
const storePath = cfg.session?.store;
|
||||
if (!storePath) {
|
||||
throw new Error("missing session store path");
|
||||
}
|
||||
const storePath = requireSessionStorePath(cfg);
|
||||
const authDir = join(home, ".openclaw", "agents", "main", "agent");
|
||||
await fs.mkdir(authDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
@@ -720,72 +731,33 @@ describe("trigger handling", () => {
|
||||
const slashSessionKey = "telegram:slash:111";
|
||||
const targetSessionKey = MAIN_SESSION_KEY;
|
||||
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify({
|
||||
[targetSessionKey]: {
|
||||
sessionId: "session-target",
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
await seedTargetSession(storePath, targetSessionKey);
|
||||
|
||||
const res = await getReplyFromConfig(
|
||||
{
|
||||
Body: `/model openai-codex/gpt-5.4@${TEST_SECONDARY_PROFILE_ID}`,
|
||||
From: "telegram:111",
|
||||
To: "telegram:111",
|
||||
ChatType: "direct",
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
SessionKey: slashSessionKey,
|
||||
CommandSource: "native",
|
||||
CommandTargetSessionKey: targetSessionKey,
|
||||
CommandAuthorized: true,
|
||||
},
|
||||
makeNativeTelegramCommandMessage({
|
||||
body: `/model openai-codex/gpt-5.4@${TEST_SECONDARY_PROFILE_ID}`,
|
||||
slashSessionKey,
|
||||
targetSessionKey,
|
||||
}),
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain(`Auth profile set to ${TEST_SECONDARY_PROFILE_ID}`);
|
||||
expect(maybeReplyText(res)).toContain(`Auth profile set to ${TEST_SECONDARY_PROFILE_ID}`);
|
||||
|
||||
const store = loadSessionStore(storePath);
|
||||
expect(store[targetSessionKey]?.authProfileOverride).toBe(TEST_SECONDARY_PROFILE_ID);
|
||||
expect(store[targetSessionKey]?.authProfileOverrideSource).toBe("user");
|
||||
expect(store[slashSessionKey]).toBeUndefined();
|
||||
|
||||
runEmbeddedPiAgentMock.mockReset();
|
||||
runEmbeddedPiAgentMock.mockResolvedValue({
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
|
||||
await getReplyFromConfig(
|
||||
await expectNextRunUsesTargetSession(
|
||||
{ cfg, targetSessionKey, runEmbeddedPiAgentMock },
|
||||
{
|
||||
Body: "hi",
|
||||
From: "telegram:111",
|
||||
To: "telegram:111",
|
||||
ChatType: "direct",
|
||||
Provider: "telegram",
|
||||
Surface: "telegram",
|
||||
SessionKey: targetSessionKey,
|
||||
},
|
||||
{},
|
||||
cfg,
|
||||
);
|
||||
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
provider: "openai-codex",
|
||||
model: "gpt-5.4",
|
||||
authProfileId: TEST_SECONDARY_PROFILE_ID,
|
||||
authProfileIdSource: "user",
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user