multica/src/heartbeat/runner.test.ts

74 lines
2.4 KiB
TypeScript

import os from "node:os";
import path from "node:path";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { afterEach, describe, expect, it } from "vitest";
import { runHeartbeatOnce, setHeartbeatsEnabled } from "./runner.js";
type StubAgent = {
closed: boolean;
sessionId: string;
ensureInitialized: () => Promise<void>;
getMessages: () => Array<any>;
write: (content: string) => void;
waitForIdle: () => Promise<void>;
getHeartbeatConfig: () => { prompt?: string; ackMaxChars?: number; enabled?: boolean };
getPendingWrites: () => number;
getProfileDir: () => string | undefined;
};
function createStubAgent(opts?: {
profileDir?: string;
replyText?: string;
heartbeatEnabled?: boolean;
}): StubAgent {
const messages: Array<any> = [];
const replyText = opts?.replyText ?? "HEARTBEAT_OK";
return {
closed: false,
sessionId: "test-session",
ensureInitialized: async () => {},
getMessages: () => messages,
write: (content: string) => {
messages.push({ role: "user", content });
messages.push({ role: "assistant", content: [{ type: "text", text: replyText }] });
},
waitForIdle: async () => {},
getHeartbeatConfig: () =>
typeof opts?.heartbeatEnabled === "boolean"
? { enabled: opts.heartbeatEnabled }
: {},
getPendingWrites: () => 0,
getProfileDir: () => opts?.profileDir,
};
}
describe("heartbeat runner", () => {
afterEach(() => {
setHeartbeatsEnabled(true);
});
it("skips when no agent is available", async () => {
const result = await runHeartbeatOnce({ agent: null });
expect(result).toEqual({ status: "skipped", reason: "disabled" });
});
it("skips when heartbeat file is effectively empty", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "heartbeat-test-"));
try {
await writeFile(path.join(dir, "heartbeat.md"), "# keep empty\n", "utf-8");
const agent = createStubAgent({ profileDir: dir });
const result = await runHeartbeatOnce({ agent: agent as any });
expect(result).toEqual({ status: "skipped", reason: "empty-heartbeat-file" });
} finally {
await rm(dir, { recursive: true, force: true });
}
});
it("runs and returns ran for heartbeat acknowledgements", async () => {
const agent = createStubAgent({ replyText: "HEARTBEAT_OK" });
const result = await runHeartbeatOnce({ agent: agent as any, reason: "manual" });
expect(result.status).toBe("ran");
});
});