Compare commits

..
Author SHA1 Message Date
paseo-bot[bot]andgszep 70cd36d732 test(app): retain host registry startup diagnosis
Record the independently reproduced registry deletion with injected invalid storage. Valid storage survives startup; the macOS update trigger in #3817 remains unverified.

Co-authored-by: gszep <contact@gszep.com>
2026-10-18 18:35:02 +02:00
2 changed files with 46 additions and 57 deletions
@@ -4440,3 +4440,49 @@ describe("HostRuntimeStore initial connection hint bootstrap", () => {
expect(store.getHosts()).toHaveLength(0);
});
});
// Diagnostic coverage for #3817. Corrupt storage is injected explicitly;
// these tests do not reproduce the macOS 0.5.1 -> 0.5.2 updater.
describe("host registry preservation during startup (#3817 diagnostic)", () => {
it("loads a valid saved remote host without changing its registry", async () => {
const host = makeHost();
const raw = JSON.stringify([host]);
const storage = createMemoryHostRuntimeStorage({
"@paseo:daemon-registry": raw,
"@paseo:e2e": "1",
});
const store = createAppearanceStore(storage);
try {
await store.boot();
expect(store.getHosts().map((entry) => entry.serverId)).toEqual([host.serverId]);
expect(await storage.getItem("@paseo:daemon-registry")).toBe(raw);
} finally {
store.syncHosts([]);
}
});
it.each([
["malformed JSON", "[{"],
[
"an unknown field beside a valid host",
JSON.stringify([
makeHost(),
{ ...makeHost({ serverId: "srv_other" }), unexpectedField: true },
]),
],
])("preserves saved bytes when startup encounters %s", async (_name, raw) => {
const storage = createMemoryHostRuntimeStorage({
"@paseo:daemon-registry": raw,
"@paseo:e2e": "1",
});
const store = createAppearanceStore(storage);
try {
await store.boot();
expect(store.getHostRegistryStatus()).toBe("ready");
expect(store.getHosts()).toEqual([]);
expect(await storage.getItem("@paseo:daemon-registry")).toBe(raw);
} finally {
store.syncHosts([]);
}
});
});
@@ -1,57 +0,0 @@
import { expect, test } from "vitest";
import { createDaemonTestContext } from "../test-utils/index.js";
import { DaemonClient } from "../test-utils/daemon-client.js";
import type { SessionOutboundMessage } from "../messages.js";
test("deleting an agent removes it from every connected agent subscription", async () => {
const ctx = await createDaemonTestContext();
const observer = new DaemonClient({ url: `ws://127.0.0.1:${ctx.daemon.port}/ws` });
const updates: SessionOutboundMessage[] = [];
const requestorUpdates: SessionOutboundMessage[] = [];
try {
await observer.connect();
const agent = await ctx.client.createAgent({
provider: "codex",
cwd: ctx.daemon.staticDir,
title: "Cross-client deletion",
});
const snapshot = await observer.fetchAgents({ subscribe: {} });
expect(snapshot.entries.some((entry) => entry.agent.id === agent.id)).toBe(true);
observer.subscribeRawMessages((message) => updates.push(message));
ctx.client.subscribeRawMessages((message) => requestorUpdates.push(message));
await ctx.client.deleteAgent(agent.id);
// The record is gone, and a fresh query sees that. An existing subscriber
// must learn the same fact without reconnecting or fetching another list.
const afterDelete = await ctx.client.fetchAgents();
expect(afterDelete.entries.some((entry) => entry.agent.id === agent.id)).toBe(false);
expect(
requestorUpdates.some(
(message) =>
message.type === "agent_update" &&
message.payload.kind === "remove" &&
message.payload.agentId === agent.id,
),
).toBe(true);
await expect(observer.fetchAgentTimeline(agent.id, { direction: "tail" })).rejects.toThrow(
"Agent not found",
);
await expect
.poll(
() =>
updates.some(
(message) =>
message.type === "agent_update" &&
message.payload.kind === "remove" &&
message.payload.agentId === agent.id,
),
{ timeout: 2_000 },
)
.toBe(true);
} finally {
await observer.close();
await ctx.cleanup();
}
});