mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 04:41:25 +00:00
fix(heartbeat): run when HEARTBEAT.md is missing
This commit is contained in:
@@ -1372,7 +1372,7 @@ describe("runHeartbeatOnce", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("skips heartbeat when HEARTBEAT.md does not exist (saves API calls)", async () => {
|
||||
it("runs heartbeat when HEARTBEAT.md does not exist", async () => {
|
||||
const tmpDir = await createCaseDir("openclaw-hb");
|
||||
const storePath = path.join(tmpDir, "sessions.json");
|
||||
const workspaceDir = path.join(tmpDir, "workspace");
|
||||
@@ -1409,7 +1409,7 @@ describe("runHeartbeatOnce", () => {
|
||||
),
|
||||
);
|
||||
|
||||
replySpy.mockResolvedValue({ text: "HEARTBEAT_OK" });
|
||||
replySpy.mockResolvedValue({ text: "Checked logs and PRs" });
|
||||
const sendWhatsApp = vi.fn().mockResolvedValue({
|
||||
messageId: "m1",
|
||||
toJid: "jid",
|
||||
@@ -1426,13 +1426,74 @@ describe("runHeartbeatOnce", () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Should skip - no HEARTBEAT.md means nothing actionable
|
||||
expect(res.status).toBe("skipped");
|
||||
if (res.status === "skipped") {
|
||||
expect(res.reason).toBe("no-heartbeat-file");
|
||||
}
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
expect(sendWhatsApp).not.toHaveBeenCalled();
|
||||
// Missing HEARTBEAT.md should still run so prompt/system instructions can drive work.
|
||||
expect(res.status).toBe("ran");
|
||||
expect(replySpy).toHaveBeenCalled();
|
||||
expect(sendWhatsApp).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
replySpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("runs heartbeat when HEARTBEAT.md read fails with a non-ENOENT error", async () => {
|
||||
const tmpDir = await createCaseDir("openclaw-hb");
|
||||
const storePath = path.join(tmpDir, "sessions.json");
|
||||
const workspaceDir = path.join(tmpDir, "workspace");
|
||||
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
|
||||
try {
|
||||
await fs.mkdir(workspaceDir, { recursive: true });
|
||||
// Simulate a read failure path (readFile on a directory returns EISDIR).
|
||||
await fs.mkdir(path.join(workspaceDir, "HEARTBEAT.md"), { recursive: true });
|
||||
|
||||
const cfg: OpenClawConfig = {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
heartbeat: { every: "5m", target: "whatsapp" },
|
||||
},
|
||||
},
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { store: storePath },
|
||||
};
|
||||
const sessionKey = resolveMainSessionKey(cfg);
|
||||
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
JSON.stringify(
|
||||
{
|
||||
[sessionKey]: {
|
||||
sessionId: "sid",
|
||||
updatedAt: Date.now(),
|
||||
lastChannel: "whatsapp",
|
||||
lastTo: "+1555",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
replySpy.mockResolvedValue({ text: "Checked logs and PRs" });
|
||||
const sendWhatsApp = vi.fn().mockResolvedValue({
|
||||
messageId: "m1",
|
||||
toJid: "jid",
|
||||
});
|
||||
|
||||
const res = await runHeartbeatOnce({
|
||||
cfg,
|
||||
deps: {
|
||||
sendWhatsApp,
|
||||
getQueueSize: () => 0,
|
||||
nowMs: () => 0,
|
||||
webAuthExists: async () => true,
|
||||
hasActiveWebListener: () => true,
|
||||
},
|
||||
});
|
||||
|
||||
// Read errors other than ENOENT should not disable heartbeat runs.
|
||||
expect(res.status).toBe("ran");
|
||||
expect(replySpy).toHaveBeenCalled();
|
||||
expect(sendWhatsApp).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
replySpy.mockRestore();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user