Files
boss/tests/device-heartbeat-event-noise.test.ts
2026-06-08 12:22:50 +08:00

169 lines
5.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let readState: (typeof import("../src/lib/boss-data"))["readState"];
let upsertDeviceHeartbeat: (typeof import("../src/lib/boss-data"))["upsertDeviceHeartbeat"];
let subscribeBossEvents: (typeof import("../src/lib/boss-events"))["subscribeBossEvents"];
async function setup() {
if (runtimeRoot) return;
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-device-heartbeat-noise-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const [data, events] = await Promise.all([
import("../src/lib/boss-data.ts"),
import("../src/lib/boss-events.ts"),
]);
readState = data.readState;
upsertDeviceHeartbeat = data.upsertDeviceHeartbeat;
subscribeBossEvents = events.subscribeBossEvents;
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
function buildHeartbeatPayload(deviceId: string, projectCandidates: Array<{
folderName: string;
threadId: string;
threadDisplayName: string;
codexFolderRef: string;
codexThreadRef: string;
lastActiveAt: string;
recentAssistantMessages?: Array<{
messageId: string;
body: string;
sentAt: string;
phase?: "commentary" | "final_answer";
}>;
}>) {
return {
deviceId,
token: `${deviceId}-token`,
name: "Mac Studio",
avatar: "M",
account: "krisolo",
status: "online" as const,
quota5h: 68,
quota7d: 81,
projects: [],
endpoint: "mac://kris.local",
projectCandidates,
};
}
const bossThreadCandidate = {
folderName: "boss",
threadId: "thread-boss-main",
threadDisplayName: "Boss开发主线程",
codexFolderRef: "/Users/kris/code/boss",
codexThreadRef: "thread-boss-main",
lastActiveAt: "2026-04-10T10:00:00.000Z",
};
test("unchanged device heartbeats do not publish conversation refresh events", async () => {
await setup();
const heartbeat = buildHeartbeatPayload("noise-device-a", [bossThreadCandidate]);
await upsertDeviceHeartbeat(heartbeat);
await upsertDeviceHeartbeat(heartbeat);
const primedState = await readState();
const primedDraft = primedState.deviceImportDrafts.find((draft) => draft.deviceId === "noise-device-a");
assert.equal(primedDraft?.status, "applied");
const events: Array<{ event: string; payload: { note?: string; deviceId?: string } }> = [];
const unsubscribe = subscribeBossEvents((event, payload) => {
events.push({ event, payload });
});
await upsertDeviceHeartbeat(heartbeat);
unsubscribe();
assert.deepEqual(events.map((event) => event.event), ["devices.updated"]);
});
test("assistant observations refresh conversation metadata without publishing message refresh events", async () => {
await setup();
const deviceId = "noise-device-assistant-observation";
const heartbeat = buildHeartbeatPayload(deviceId, [bossThreadCandidate]);
await upsertDeviceHeartbeat(heartbeat);
await upsertDeviceHeartbeat(heartbeat);
const observedHeartbeat = buildHeartbeatPayload(deviceId, [
{
...bossThreadCandidate,
lastActiveAt: "2026-04-10T10:02:00.000Z",
recentAssistantMessages: [
{
messageId: "codex-thread:thread-boss-main:2026-04-10T10:02:00.000Z:final",
body: "桌面线程的最终回复不应作为手机聊天消息刷新。",
sentAt: "2026-04-10T10:02:00.000Z",
phase: "final_answer",
},
],
},
]);
const firstEvents: Array<{ event: string; payload: { projectId?: string } }> = [];
const unsubscribeFirst = subscribeBossEvents((event, payload) => {
firstEvents.push({ event, payload });
});
await upsertDeviceHeartbeat(observedHeartbeat);
unsubscribeFirst();
assert.equal(firstEvents.some((event) => event.event === "project.messages.updated"), false);
assert.deepEqual(
firstEvents.map((event) => event.event),
["devices.updated", "conversation.updated"],
);
const repeatedEvents: Array<{ event: string; payload: { projectId?: string } }> = [];
const unsubscribeRepeated = subscribeBossEvents((event, payload) => {
repeatedEvents.push({ event, payload });
});
await upsertDeviceHeartbeat(observedHeartbeat);
unsubscribeRepeated();
assert.deepEqual(repeatedEvents.map((event) => event.event), ["devices.updated"]);
});
test("device heartbeats publish one conversation refresh when import candidates change", async () => {
await setup();
const nextThreadCandidate = {
folderName: "boss",
threadId: "thread-boss-review",
threadDisplayName: "Boss回归线程",
codexFolderRef: "/Users/kris/code/boss",
codexThreadRef: "thread-boss-review",
lastActiveAt: "2026-04-10T10:01:00.000Z",
};
await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate]));
await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate]));
const events: Array<{ event: string; payload: { note?: string; deviceId?: string } }> = [];
const unsubscribe = subscribeBossEvents((event, payload) => {
events.push({ event, payload });
});
await upsertDeviceHeartbeat(buildHeartbeatPayload("noise-device-b", [bossThreadCandidate, nextThreadCandidate]));
unsubscribe();
assert.deepEqual(
events.map((event) => [event.event, event.payload.deviceId, event.payload.note]),
[
["devices.updated", "noise-device-b", undefined],
["conversation.updated", "noise-device-b", "device_import.updated"],
],
);
});