Files
boss/local-agent/heartbeat-project-snapshot.test.mjs
2026-06-08 12:22:50 +08:00

105 lines
2.9 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import {
resolveHeartbeatProjectsFromSnapshot,
runHeartbeatProjectDiscoveryWithTimeout,
storeHeartbeatProjectsSnapshot,
} from "./heartbeat-project-snapshot.mjs";
test("active master task heartbeat reuses the last discovered project snapshot", () => {
const runtime = {
masterTaskBusy: true,
lastHeartbeatProjectsSnapshot: {
projects: ["test"],
projectCandidates: [{ threadId: "thread-test", folderName: "test" }],
guiConnected: true,
capturedAt: "2026-06-07T10:39:00.000Z",
},
};
const result = resolveHeartbeatProjectsFromSnapshot({
config: {
projects: ["static"],
projectCandidates: [{ threadId: "static-thread", folderName: "static" }],
},
runtime,
});
assert.deepEqual(result, {
shouldUseSnapshot: true,
projects: ["test"],
projectCandidates: [{ threadId: "thread-test", folderName: "test" }],
guiConnected: true,
});
});
test("active master task heartbeat falls back to static projects without a snapshot", () => {
const result = resolveHeartbeatProjectsFromSnapshot({
config: {
projects: ["static"],
projectCandidates: [{ threadId: "static-thread", folderName: "static" }],
},
runtime: {
masterTaskBusy: true,
},
});
assert.deepEqual(result, {
shouldUseSnapshot: true,
projects: ["static"],
projectCandidates: [{ threadId: "static-thread", folderName: "static" }],
guiConnected: false,
});
});
test("idle heartbeat does not use the cached project snapshot", () => {
const result = resolveHeartbeatProjectsFromSnapshot({
config: {
projects: ["static"],
},
runtime: {
masterTaskBusy: false,
lastHeartbeatProjectsSnapshot: {
projects: ["cached"],
projectCandidates: [],
},
},
});
assert.equal(result.shouldUseSnapshot, false);
});
test("project snapshot stores only lightweight fields", () => {
const runtime = {};
storeHeartbeatProjectsSnapshot(runtime, {
projects: ["test"],
projectCandidates: [{ threadId: "thread-test", folderName: "test" }],
guiConnected: true,
privateField: "should-not-store",
});
assert.deepEqual(Object.keys(runtime.lastHeartbeatProjectsSnapshot).sort(), [
"capturedAt",
"guiConnected",
"projectCandidates",
"projects",
]);
assert.equal(runtime.lastHeartbeatProjectsSnapshot.privateField, undefined);
});
test("project discovery timeout falls back instead of blocking heartbeat", async () => {
const result = await runHeartbeatProjectDiscoveryWithTimeout({
timeoutMs: 10,
fallback: { projects: ["cached"], projectCandidates: [], guiConnected: false },
discover: () => new Promise(() => {}),
});
assert.equal(result.timedOut, true);
assert.deepEqual(result.value, {
projects: ["cached"],
projectCandidates: [],
guiConnected: false,
});
});