test(sessions): make unread checks portable

This commit is contained in:
Federico Jaramillo Martinez
2026-07-22 23:36:28 +02:00
parent e70b3d6bb9
commit ced3261651
2 changed files with 19 additions and 15 deletions
@@ -1,6 +1,6 @@
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { join, resolve } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { PiSessionService, type PiAgentSession } from "./piSessionService.js";
import {
@@ -22,6 +22,9 @@ import {
} from "./sessionUnreadStore.js";
const TEST_AGENT_DIR = "/tmp/pi-web-test-agent";
// Unread identities are persisted after platform-native cwd canonicalization.
const WORKSPACE_CWD = resolve("/workspace");
const FEATURE_CWD = resolve("/workspace-feature");
const tempRoots: string[] = [];
afterEach(async () => {
@@ -50,11 +53,11 @@ describe("PiSessionService daemon-owned unread state", () => {
const secondSnapshot = await service.unreadCatalog();
const current = secondSnapshot.sessions[0];
expect(current).toMatchObject({ sessionId: "session-1", cwd: "/workspace", completionOrder: 2 });
expect(current).toMatchObject({ sessionId: "session-1", cwd: WORKSPACE_CWD, completionOrder: 2 });
expect(unreadEvents(hub).map((event) => event.catalogRevision)).toEqual([1, 2]);
const staleSnapshot = await service.acknowledgeUnread("session-1", {
cwd: "/workspace",
cwd: WORKSPACE_CWD,
catalogId: "catalog-test",
throughCompletionOrder: 1,
});
@@ -62,7 +65,7 @@ describe("PiSessionService daemon-owned unread state", () => {
expect(unreadEvents(hub).map((event) => event.catalogRevision)).toEqual([1, 2]);
const acknowledged = await service.acknowledgeUnread("session-1", {
cwd: "/workspace",
cwd: WORKSPACE_CWD,
catalogId: "catalog-test",
throughCompletionOrder: current?.completionOrder ?? 0,
});
@@ -100,7 +103,7 @@ describe("PiSessionService daemon-owned unread state", () => {
finishBash?.();
await Promise.resolve();
expect((await service.unreadCatalog()).sessions).toMatchObject([{ sessionId: "session-1", cwd: "/workspace", completionOrder: 1 }]);
expect((await service.unreadCatalog()).sessions).toMatchObject([{ sessionId: "session-1", cwd: WORKSPACE_CWD, completionOrder: 1 }]);
} finally {
await service.dispose();
}
@@ -258,13 +261,13 @@ describe("PiSessionService daemon-owned unread state", () => {
initial.session.isStreaming = false;
expect((await service.unreadCatalog()).sessions).toEqual([]);
completeStoreWork(unreadStore, "session-1", "/workspace");
completeStoreWork(unreadStore, "session-1", WORKSPACE_CWD);
const beforeReload = (await service.unreadCatalog()).sessions[0];
await service.reload(sessionRef("session-1"));
const afterReload = (await service.unreadCatalog()).sessions[0];
expect(beforeReload).toBeDefined();
expect(afterReload).toMatchObject({ sessionId: "session-1", cwd: "/workspace" });
expect(afterReload).toMatchObject({ sessionId: "session-1", cwd: WORKSPACE_CWD });
expect(afterReload?.completionOrder).toBeGreaterThan(beforeReload?.completionOrder ?? 0);
} finally {
await service.dispose();
@@ -273,7 +276,7 @@ describe("PiSessionService daemon-owned unread state", () => {
it("clears stale unread when a runtime rebind changes logical session identity", async () => {
const unreadStore = new SessionUnreadStore({ createCatalogId: () => "catalog-test" });
completeStoreWork(unreadStore, "session-old", "/workspace");
completeStoreWork(unreadStore, "session-old", WORKSPACE_CWD);
const original = fakeRuntime("session-old");
let rebindSession: ((session: PiAgentSession) => Promise<void>) | undefined;
original.runtime.setRebindSession = (callback) => { rebindSession = callback; };
@@ -302,7 +305,7 @@ describe("PiSessionService daemon-owned unread state", () => {
it("cleans unread state through archive, restore, delete, and cwd reconciliation", async () => {
const unreadStore = new SessionUnreadStore({ createCatalogId: () => "catalog-test" });
for (const sessionId of ["archive-me", "restore-me", "delete-me", "orphan"]) {
completeStoreWork(unreadStore, sessionId, "/workspace");
completeStoreWork(unreadStore, sessionId, WORKSPACE_CWD);
}
const archived = new Map([
["restore-me", { sessionId: "restore-me", cwd: "/workspace", archivedAt: "2026-07-01T00:00:00.000Z", archivePath: "/archive/restore-me.jsonl" }],
@@ -348,7 +351,7 @@ describe("PiSessionService daemon-owned unread state", () => {
await service.deleteArchived(sessionRef("delete-me"));
expect((await service.unreadCatalog()).sessions.map((summary) => summary.sessionId)).toEqual(["orphan"]);
await service.list("/workspace");
await service.list(WORKSPACE_CWD);
expect((await service.unreadCatalog()).sessions).toEqual([]);
} finally {
await service.dispose();
@@ -404,7 +407,7 @@ describe("PiSessionService daemon-owned unread state", () => {
completeRuntimeWork(child);
expect((await service.unreadCatalog()).sessions).toContainEqual(expect.objectContaining({
sessionId: "child-1",
cwd: "/workspace-feature",
cwd: FEATURE_CWD,
}));
} finally {
await service.dispose();
@@ -420,7 +423,7 @@ describe("PiSessionService daemon-owned unread state", () => {
await writeFile(childFile, `${JSON.stringify({ type: "session", version: 3, id: "child-1", timestamp: "2026-01-01T00:00:00.000Z", cwd: "/workspace-feature", parentSession: parentFile })}\n`, "utf8");
const unreadStore = new SessionUnreadStore({ createCatalogId: () => "catalog-test" });
completeStoreWork(unreadStore, "child-1", "/workspace-feature");
completeStoreWork(unreadStore, "child-1", FEATURE_CWD);
const hub = new CapturingSessionEventHub();
const parentManager = fakeSessionManager("/workspace", {
getEntries: () => [{
@@ -452,7 +455,7 @@ describe("PiSessionService daemon-owned unread state", () => {
]);
expect((await service.unreadCatalog()).sessions).toEqual([]);
expect(unreadEvents(hub).at(-1)).toMatchObject({ sessionId: "child-1", cwd: "/workspace-feature", unread: null });
expect(unreadEvents(hub).at(-1)).toMatchObject({ sessionId: "child-1", cwd: FEATURE_CWD, unread: null });
} finally {
await service.dispose();
}
@@ -466,7 +469,7 @@ describe("PiSessionService daemon-owned unread state", () => {
await writeFile(parentFile, `${JSON.stringify({ type: "session", version: 3, id: "parent-1", timestamp: "2026-01-01T00:00:00.000Z", cwd: "/workspace" })}\n`, "utf8");
const unreadStore = new SessionUnreadStore({ createCatalogId: () => "catalog-test" });
completeStoreWork(unreadStore, "child-1", "/workspace-feature");
completeStoreWork(unreadStore, "child-1", FEATURE_CWD);
const parentManager = fakeSessionManager("/workspace", {
getEntries: () => [{
type: "custom",
@@ -464,7 +464,8 @@ describe("FileSessionUnreadPersistence", () => {
nextCompletionOrder: 1,
sessions: [{ sessionId: "session-1", cwd: "/repo", completionOrder: 1 }],
});
expect((await stat(filePath)).mode & 0o777).toBe(0o600);
// Windows reports synthesized POSIX mode bits and ignores the requested file mode.
if (process.platform !== "win32") expect((await stat(filePath)).mode & 0o777).toBe(0o600);
expect((await readdir(join(root, "state"))).filter((name) => name.endsWith(".tmp"))).toEqual([]);
const reloaded = new SessionUnreadStore({ persistence, createCatalogId: () => "unused-catalog" });