diff --git a/src/gateway/server.cron.test.ts b/src/gateway/server.cron.test.ts index 0be7c97695d..76af3f1f7d2 100644 --- a/src/gateway/server.cron.test.ts +++ b/src/gateway/server.cron.test.ts @@ -581,6 +581,135 @@ describe("gateway server cron", () => { } }); + test("accepts implicit announce delivery when extra configured channels are disabled", async () => { + const { prevSkipCron } = await setupCronTestRun({ + tempPrefix: "openclaw-gw-cron-disabled-channel-ambiguity-", + cronEnabled: false, + }); + + await writeCronConfig({ + session: { + mainKey: "main", + }, + channels: { + telegram: { + botToken: "telegram-token", + }, + slack: { + enabled: false, + botToken: "xoxb-slack-token", + appToken: "xapp-slack-token", + }, + }, + }); + + const { server, ws } = await startServerWithClient(); + await connectOk(ws); + + try { + const addRes = await rpcReq(ws, "cron.add", { + name: "disabled extra channel", + enabled: true, + schedule: { kind: "every", everyMs: 60_000 }, + sessionTarget: "isolated", + wakeMode: "next-heartbeat", + payload: { kind: "agentTurn", message: "hello" }, + delivery: { mode: "announce" }, + }); + + if (!addRes.ok) { + throw new Error(addRes.error?.message ?? "cron.add failed"); + } + expect(addRes.ok).toBe(true); + } finally { + await cleanupCronTestRun({ ws, server, prevSkipCron }); + } + }); + + test("keeps delivery updates valid after gateway config changes the default agent", async () => { + const { prevSkipCron } = await setupCronTestRun({ + tempPrefix: "openclaw-gw-cron-main-default-agent-drift-", + cronEnabled: false, + }); + + await writeCronConfig({ + session: { + mainKey: "main", + }, + agents: { + list: [{ id: "ops", default: true }], + }, + channels: { + telegram: { + botToken: "telegram-token", + }, + }, + }); + + const { server, ws } = await startServerWithClient(); + await connectOk(ws); + + try { + const addRes = await rpcReq(ws, "cron.add", { + name: "main default agent drift", + enabled: true, + schedule: { kind: "every", everyMs: 60_000 }, + sessionTarget: "main", + agentId: "ops", + wakeMode: "next-heartbeat", + payload: { kind: "systemEvent", text: "hello" }, + }); + expect(addRes.ok).toBe(true); + const jobIdValue = (addRes.payload as { id?: unknown } | null)?.id; + const jobId = typeof jobIdValue === "string" ? jobIdValue : ""; + expect(jobId.length > 0).toBe(true); + + const { writeConfigFile } = await import("../config/config.js"); + await writeConfigFile({ + session: { + mainKey: "main", + }, + agents: { + list: [{ id: "main", default: true }, { id: "ops" }], + }, + channels: { + telegram: { + botToken: "telegram-token", + }, + }, + }); + + let agentIds: string[] = []; + for (let i = 0; i < 20; i += 1) { + const agentsRes = await rpcReq<{ agents?: Array<{ id?: string }> }>(ws, "agents.list", {}); + expect(agentsRes.ok).toBe(true); + agentIds = (agentsRes.payload?.agents ?? []) + .map((agent) => agent.id) + .filter((id): id is string => typeof id === "string"); + if (agentIds.includes("main") && agentIds.includes("ops")) { + break; + } + await yieldToEventLoop(); + } + expect(agentIds).toContain("main"); + expect(agentIds).toContain("ops"); + + const updateRes = await rpcReq(ws, "cron.update", { + id: jobId, + patch: { + delivery: { mode: "announce", channel: "telegram", to: "19098680" }, + }, + }); + + if (!updateRes.ok) { + throw new Error(updateRes.error?.message ?? "cron.update failed"); + } + expect(updateRes.ok).toBe(true); + } finally { + await cleanupCronTestRun({ ws, server, prevSkipCron }); + } + }); + test("rejects ambiguous announce delivery on add when multiple channels are configured", async () => { const { prevSkipCron } = await setupCronTestRun({ tempPrefix: "openclaw-gw-cron-ambiguous-delivery-add-",