Files
dev/third_party/pi/packages/agent/test/harness/memory-session-repo.test.ts
alexion 4def357d09 chore(vendor): import pi 0.85.1
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
2026-09-14 10:48:47 -04:00

140 lines
5.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { BACKGROUND_CONTEXT } from "../../src/harness/context.ts";
import * as sessionWrites from "../../src/harness/session/commit.ts";
import { MemorySessionRepo } from "../../src/harness/session/index.ts";
import * as storedValues from "../../src/harness/session/values.ts";
const NOW = 1_700_000_000_000;
function uuidTimestamp(id: string): number {
return Number.parseInt(id.replaceAll("-", "").slice(0, 12), 16);
}
describe("MemorySessionRepo metadata", () => {
it("uses its injected clock for generated session identity and metadata", async () => {
const repo = new MemorySessionRepo({ now: () => NOW });
const session = await repo.create({}, BACKGROUND_CONTEXT);
expect(session.metadata.createdAt).toBe(NOW);
expect(uuidTimestamp(session.metadata.id)).toBe(NOW);
await Promise.all([session.close(BACKGROUND_CONTEXT), repo.close(BACKGROUND_CONTEXT)]);
});
it("returns a fresh facade after close while retaining one session and storage", async () => {
const repo = new MemorySessionRepo({ now: () => NOW });
const first = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
const firstBranch = await first.createBranch("main", null, BACKGROUND_CONTEXT);
const admittedWrite = first.setName("preserved", BACKGROUND_CONTEXT);
await expect(repo.open(first.metadata, BACKGROUND_CONTEXT)).rejects.toThrow("already open");
await Promise.all([admittedWrite, first.close(BACKGROUND_CONTEXT)]);
await expect(first.getName(BACKGROUND_CONTEXT)).rejects.toThrow("Session is closed");
await expect(first.scanBranch({ start: "entry" }, BACKGROUND_CONTEXT)).rejects.toThrow("Session is closed");
await expect(firstBranch.getTipId(BACKGROUND_CONTEXT)).rejects.toThrow("Session is closed");
const second = await repo.open(first.metadata, BACKGROUND_CONTEXT);
expect(second).not.toBe(first);
expect(await second.getName(BACKGROUND_CONTEXT)).toBe("preserved");
await second.close(BACKGROUND_CONTEXT);
await repo.close(BACKGROUND_CONTEXT);
});
it("waits for an explicit mutation before closing its facade", async () => {
const repo = new MemorySessionRepo({ now: () => NOW });
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
const mutation = await session.beginMutation(BACKGROUND_CONTEXT);
let closed = false;
const closing = session.close(BACKGROUND_CONTEXT).then(() => {
closed = true;
});
await Promise.resolve();
expect(closed).toBe(false);
await mutation.end(BACKGROUND_CONTEXT);
await closing;
expect(closed).toBe(true);
const reopened = await repo.open(session.metadata, BACKGROUND_CONTEXT);
await Promise.all([reopened.close(BACKGROUND_CONTEXT), repo.close(BACKGROUND_CONTEXT)]);
});
it("rejects an explicit scope that had not acquired before facade close", async () => {
const repo = new MemorySessionRepo({ now: () => NOW });
const session = await repo.create({ id: "session" }, BACKGROUND_CONTEXT);
const first = await session.beginMutation(BACKGROUND_CONTEXT);
const secondPromise = session.beginMutation(BACKGROUND_CONTEXT);
void secondPromise.catch(() => {});
let closed = false;
const closing = session.close(BACKGROUND_CONTEXT).then(() => {
closed = true;
});
await first.end(BACKGROUND_CONTEXT);
await expect(secondPromise).rejects.toThrow("Session is closed");
await closing;
expect(closed).toBe(true);
const reopened = await repo.open(session.metadata, BACKGROUND_CONTEXT);
await Promise.all([reopened.close(BACKGROUND_CONTEXT), repo.close(BACKGROUND_CONTEXT)]);
});
it("captures fork options before waiting for its snapshot boundary", async () => {
const repo = new MemorySessionRepo({ now: () => NOW });
const source = await repo.create({ id: "source" }, BACKGROUND_CONTEXT);
const rootId = "00000000-0000-7000-8000-000000000001";
const childId = "00000000-0000-7000-8000-000000000002";
const mutation = await source.beginMutation(BACKGROUND_CONTEXT);
const commit = mutation.commit(
[
sessionWrites.insertEntry({
id: rootId,
parentId: null,
type: "custom",
customType: "root",
}),
sessionWrites.insertEntry({
id: childId,
parentId: rootId,
type: "custom",
customType: "child",
}),
storedValues.setValue(storedValues.branchTip("main"), childId),
storedValues.setValue(storedValues.laneConfig("main"), {
model: { provider: "test", modelId: "test" },
thinkingLevel: "off",
activeToolNames: [],
}),
storedValues.setValue(storedValues.laneState("main"), {
currentOperationId: null,
lastOperationId: null,
inbox: [],
}),
],
BACKGROUND_CONTEXT,
);
const options = {
id: "fork",
scope: "branch" as const,
branch: "main",
entryId: childId,
position: "before" as "before" | "at",
};
const fork = repo.fork(source.metadata, options, BACKGROUND_CONTEXT);
options.entryId = rootId;
options.position = "at";
await commit;
await mutation.end(BACKGROUND_CONTEXT);
await expect(
source.scanBranch({ start: childId, order: "oldestFirst" }, BACKGROUND_CONTEXT),
).resolves.toMatchObject([{ id: rootId }, { id: childId }]);
const forked = await fork;
const forkedMain = await forked.branch("main", BACKGROUND_CONTEXT);
expect(await forkedMain?.getTipId(BACKGROUND_CONTEXT)).toBe(rootId);
await Promise.all([
source.close(BACKGROUND_CONTEXT),
forked.close(BACKGROUND_CONTEXT),
repo.close(BACKGROUND_CONTEXT),
]);
});
});