From 9d8e923ddb9708ad279863456b2ac51a4bd54ecd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 19 Apr 2026 01:59:53 +0100 Subject: [PATCH] test: share heartbeat override fixture --- .../heartbeat-runner.model-override.test.ts | 196 +++++++----------- 1 file changed, 70 insertions(+), 126 deletions(-) diff --git a/src/infra/heartbeat-runner.model-override.test.ts b/src/infra/heartbeat-runner.model-override.test.ts index 637644aaa71..8887e2c81b9 100644 --- a/src/infra/heartbeat-runner.model-override.test.ts +++ b/src/infra/heartbeat-runner.model-override.test.ts @@ -17,6 +17,8 @@ type SeedSessionInput = { lastTo: string; updatedAt?: number; }; +type AgentDefaultsConfig = NonNullable["defaults"]>; +type HeartbeatConfig = NonNullable; async function withHeartbeatFixture( run: (ctx: { @@ -113,6 +115,56 @@ describe("runHeartbeatOnce – heartbeat model override", () => { }); } + async function expectPerAgentHeartbeatOverride(params: { + defaultsHeartbeat: Partial; + expectedOptions: Record; + heartbeat: Partial; + }): Promise { + await withHeartbeatFixture(async ({ tmpDir, storePath, replySpy, seedSession }) => { + const cfg: OpenClawConfig = { + agents: { + defaults: { + heartbeat: { + every: "30m", + ...params.defaultsHeartbeat, + }, + }, + list: [ + { id: "main", default: true }, + { + id: "ops", + workspace: tmpDir, + heartbeat: { + every: "5m", + target: "whatsapp", + ...params.heartbeat, + }, + }, + ], + }, + channels: { whatsapp: { allowFrom: ["*"] } }, + session: { store: storePath }, + }; + const sessionKey = resolveAgentMainSessionKey({ cfg, agentId: "ops" }); + const result = await runHeartbeatWithSeed({ + seedSession, + cfg, + agentId: "ops", + sessionKey, + replySpy, + }); + + expect(result.replySpy).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + isHeartbeat: true, + ...params.expectedOptions, + }), + cfg, + ); + }); + } + it("passes heartbeatModelOverride from defaults heartbeat config", async () => { const replyOpts = await runDefaultsHeartbeat({ model: "ollama/llama3.2:1b" }); expect(replyOpts).toEqual( @@ -211,140 +263,32 @@ describe("runHeartbeatOnce – heartbeat model override", () => { }); it("passes per-agent heartbeat model override (merged with defaults)", async () => { - await withHeartbeatFixture(async ({ tmpDir, storePath, replySpy, seedSession }) => { - const cfg: OpenClawConfig = { - agents: { - defaults: { - heartbeat: { - every: "30m", - model: "openai/gpt-5.4", - }, - }, - list: [ - { id: "main", default: true }, - { - id: "ops", - workspace: tmpDir, - heartbeat: { - every: "5m", - target: "whatsapp", - model: "ollama/llama3.2:1b", - }, - }, - ], - }, - channels: { whatsapp: { allowFrom: ["*"] } }, - session: { store: storePath }, - }; - const sessionKey = resolveAgentMainSessionKey({ cfg, agentId: "ops" }); - const result = await runHeartbeatWithSeed({ - seedSession, - cfg, - agentId: "ops", - sessionKey, - replySpy, - }); - - expect(result.replySpy).toHaveBeenCalledWith( - expect.any(Object), - expect.objectContaining({ - isHeartbeat: true, - heartbeatModelOverride: "ollama/llama3.2:1b", - }), - cfg, - ); + await expectPerAgentHeartbeatOverride({ + defaultsHeartbeat: { model: "openai/gpt-5.4" }, + heartbeat: { model: "ollama/llama3.2:1b" }, + expectedOptions: { + heartbeatModelOverride: "ollama/llama3.2:1b", + }, }); }); it("passes per-agent heartbeat lightContext override after merging defaults", async () => { - await withHeartbeatFixture(async ({ tmpDir, storePath, replySpy, seedSession }) => { - const cfg: OpenClawConfig = { - agents: { - defaults: { - heartbeat: { - every: "30m", - lightContext: false, - }, - }, - list: [ - { id: "main", default: true }, - { - id: "ops", - workspace: tmpDir, - heartbeat: { - every: "5m", - target: "whatsapp", - lightContext: true, - }, - }, - ], - }, - channels: { whatsapp: { allowFrom: ["*"] } }, - session: { store: storePath }, - }; - const sessionKey = resolveAgentMainSessionKey({ cfg, agentId: "ops" }); - const result = await runHeartbeatWithSeed({ - seedSession, - cfg, - agentId: "ops", - sessionKey, - replySpy, - }); - - expect(result.replySpy).toHaveBeenCalledWith( - expect.any(Object), - expect.objectContaining({ - isHeartbeat: true, - bootstrapContextMode: "lightweight", - }), - cfg, - ); + await expectPerAgentHeartbeatOverride({ + defaultsHeartbeat: { lightContext: false }, + heartbeat: { lightContext: true }, + expectedOptions: { + bootstrapContextMode: "lightweight", + }, }); }); it("passes per-agent heartbeat timeout override after merging defaults", async () => { - await withHeartbeatFixture(async ({ tmpDir, storePath, replySpy, seedSession }) => { - const cfg: OpenClawConfig = { - agents: { - defaults: { - heartbeat: { - every: "30m", - timeoutSeconds: 120, - }, - }, - list: [ - { id: "main", default: true }, - { - id: "ops", - workspace: tmpDir, - heartbeat: { - every: "5m", - target: "whatsapp", - timeoutSeconds: 45, - }, - }, - ], - }, - channels: { whatsapp: { allowFrom: ["*"] } }, - session: { store: storePath }, - }; - const sessionKey = resolveAgentMainSessionKey({ cfg, agentId: "ops" }); - const result = await runHeartbeatWithSeed({ - seedSession, - cfg, - agentId: "ops", - sessionKey, - replySpy, - }); - - expect(result.replySpy).toHaveBeenCalledWith( - expect.any(Object), - expect.objectContaining({ - isHeartbeat: true, - timeoutOverrideSeconds: 45, - }), - cfg, - ); + await expectPerAgentHeartbeatOverride({ + defaultsHeartbeat: { timeoutSeconds: 120 }, + heartbeat: { timeoutSeconds: 45 }, + expectedOptions: { + timeoutOverrideSeconds: 45, + }, }); });