Scope folder realtime refreshes by device

This commit is contained in:
kris
2026-04-10 12:55:43 +08:00
parent d1e5a1ac5e
commit 0cba837ed3
11 changed files with 357 additions and 9 deletions

View File

@@ -0,0 +1,26 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
test("android folder activity refreshes on matching device-scoped conversation updates", async () => {
const source = await readFile(
new URL("../android/app/src/main/java/com/hyzq/boss/ConversationFolderActivity.java", import.meta.url),
"utf8",
);
assert.match(
source,
/private String folderDeviceId;/,
"expected folder activity to keep track of the folder device id for realtime filtering",
);
assert.match(
source,
/String payloadDeviceId = event\.payload\.optString\("deviceId", ""\)\.trim\(\);/,
"expected folder activity to inspect device-scoped realtime payloads",
);
assert.match(
source,
/payloadDeviceId\.equals\(folderDeviceId\)/,
"expected folder activity to reload when the event targets its device",
);
});

View File

@@ -83,6 +83,9 @@ exec "$@"
assert.equal(sshCalls.length, 2);
assert.match(sshCalls[0] ?? "", /sudo mkdir -p \/opt\/boss/);
assert.match(rsyncArgs, /--rsync-path=sudo rsync/);
assert.match(rsyncArgs, /--exclude \.project/);
assert.match(rsyncArgs, /--exclude \.classpath/);
assert.match(rsyncArgs, /--exclude \.settings/);
assert.match(sshCalls[1] ?? "", /bootstrap-server\.sh/);
assert.match(sshCalls[1] ?? "", /sudo chown -R ubuntu:ubuntu \/opt\/boss\/data \/opt\/boss\/public\/downloads/);
assert.match(sshCalls[1] ?? "", /npm install --omit=dev/);

View File

@@ -69,6 +69,7 @@ test("folder conversation page wires folder thread ids into realtime refresh", a
assert.match(folderPage, /<RealtimeRefresh/, "expected folder page to render RealtimeRefresh");
assert.match(folderPage, /projectIds=\{folder\.threads\.map\(\(thread\) => thread\.projectId\)\}/, "expected folder page to scope refreshes to folder thread project ids");
assert.match(folderPage, /deviceId=\{folder\.deviceId\}/, "expected folder page to scope device-only refreshes to the folder device");
assert.match(folderPage, /"conversation\.updated"/, "expected folder page to listen to conversation updates");
});

View File

@@ -39,6 +39,48 @@ test("shouldRefreshRealtimeEvent filters scoped conversation updates by project
);
});
test("shouldRefreshRealtimeEvent ignores scoped events that do not identify a project", () => {
assert.equal(
shouldRefreshRealtimeEvent({
eventType: "conversation.updated",
eventData: JSON.stringify({ deviceId: "mac-studio" }),
projectId: "project-a",
}),
false,
);
assert.equal(
shouldRefreshRealtimeEvent({
eventType: "project.context_risk.updated",
eventData: JSON.stringify({ deviceId: "mac-studio" }),
projectIds: ["project-a", "project-b"],
}),
false,
);
});
test("shouldRefreshRealtimeEvent can match scoped device events when page opts into device scope", () => {
assert.equal(
shouldRefreshRealtimeEvent({
eventType: "conversation.updated",
eventData: JSON.stringify({ deviceId: "mac-studio" }),
projectIds: ["project-a", "project-b"],
deviceId: "mac-studio",
}),
true,
);
assert.equal(
shouldRefreshRealtimeEvent({
eventType: "conversation.updated",
eventData: JSON.stringify({ deviceId: "windows-box" }),
projectIds: ["project-a", "project-b"],
deviceId: "mac-studio",
}),
false,
);
});
test("planThrottledRefresh coalesces bursts into one delayed refresh", () => {
const state = createRefreshThrottleState();