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
This commit is contained in:
1 parent
5ebc7018c3
commit
4def357d09
1669 files changed
+397627
No files matched your search
@@ -0,0 +1,199 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { BACKGROUND_CONTEXT, type Context } from "../../src/harness/context.ts";
|
||||
import { NodeExecutionEnv } from "../../src/harness/env/nodejs.ts";
|
||||
import { JSONL_STORAGE_VERSION, JsonlSessionRepo } from "../../src/harness/session/jsonl/index.ts";
|
||||
import { sessionName, setValue } from "../../src/harness/session/values.ts";
|
||||
import { getOrThrow } from "../../src/harness/types.ts";
|
||||
import { createTempDir } from "./session-test-utils.ts";
|
||||
|
||||
const NOW = 1_700_000_000_000;
|
||||
|
||||
class AtomicPublicationNodeExecutionEnv extends NodeExecutionEnv {
|
||||
publication:
|
||||
| { sourcePath: string; destinationPath: string; destinationExisted: boolean; stagedContent: string }
|
||||
| undefined;
|
||||
|
||||
override async renameFile(sourcePath: string, destinationPath: string, context: Context) {
|
||||
const destinationExists = await super.exists(destinationPath, context);
|
||||
const stagedContent = await super.readTextFile(sourcePath, context);
|
||||
if (destinationExists.ok && stagedContent.ok) {
|
||||
this.publication = {
|
||||
sourcePath,
|
||||
destinationPath,
|
||||
destinationExisted: destinationExists.value,
|
||||
stagedContent: stagedContent.value,
|
||||
};
|
||||
}
|
||||
return super.renameFile(sourcePath, destinationPath, context);
|
||||
}
|
||||
}
|
||||
|
||||
describe("JsonlSessionRepo cwd-scoped lifecycle", () => {
|
||||
it("persists metadata and filters discovery by cwd", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const session = await repo.create(
|
||||
{ id: "child", cwd: "/workspace", parentSessionId: "parent" },
|
||||
BACKGROUND_CONTEXT,
|
||||
);
|
||||
const metadata = session.metadata;
|
||||
|
||||
expect(metadata).toMatchObject({
|
||||
id: "child",
|
||||
createdAt: NOW,
|
||||
storageVersion: JSONL_STORAGE_VERSION,
|
||||
cwd: "/workspace",
|
||||
parentSessionId: "parent",
|
||||
});
|
||||
expect(metadata.path).toContain("/sessions/--workspace--/");
|
||||
expect(metadata.path.endsWith("_child.jsonl")).toBe(true);
|
||||
expect(Number.isFinite(metadata.modifiedAt)).toBe(true);
|
||||
await session.close(BACKGROUND_CONTEXT);
|
||||
|
||||
expect(await repo.list({ cwd: "/other" }, BACKGROUND_CONTEXT)).toEqual([]);
|
||||
expect(await repo.list({ cwd: "/workspace" }, BACKGROUND_CONTEXT)).toEqual([metadata]);
|
||||
const firstLine = getOrThrow(
|
||||
await fileSystem.readTextLines(metadata.path, { maxLines: 1 }, BACKGROUND_CONTEXT),
|
||||
)[0];
|
||||
expect(JSON.parse(firstLine!)).toEqual({
|
||||
v: 4,
|
||||
kind: "header",
|
||||
id: "child",
|
||||
storageVersion: JSONL_STORAGE_VERSION,
|
||||
createdAt: NOW,
|
||||
cwd: "/workspace",
|
||||
parentSessionId: "parent",
|
||||
});
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("atomically publishes a branchless session header", async () => {
|
||||
const fileSystem = new AtomicPublicationNodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const session = await repo.create({ id: "session", cwd: "/workspace" }, BACKGROUND_CONTEXT);
|
||||
|
||||
const publication = fileSystem.publication;
|
||||
if (publication === undefined) throw new Error("Expected atomic session publication");
|
||||
expect(publication.destinationPath).toBe(session.metadata.path);
|
||||
expect(publication.destinationExisted).toBe(false);
|
||||
const lines = publication.stagedContent.trimEnd().split("\n");
|
||||
expect(lines).toHaveLength(1);
|
||||
expect(JSON.parse(lines[0]!)).toMatchObject({ kind: "header", id: "session" });
|
||||
expect(getOrThrow(await fileSystem.readTextFile(session.metadata.path, BACKGROUND_CONTEXT))).toBe(
|
||||
publication.stagedContent,
|
||||
);
|
||||
expect(getOrThrow(await fileSystem.exists(publication.sourcePath, BACKGROUND_CONTEXT))).toBe(false);
|
||||
|
||||
await session.close(BACKGROUND_CONTEXT);
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("keeps an explicit Session mutation through commit until end", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const session = await repo.create({ id: "session", cwd: "/workspace" }, BACKGROUND_CONTEXT);
|
||||
const mutation = await session.beginMutation(BACKGROUND_CONTEXT);
|
||||
let queuedStarted = false;
|
||||
const queued = session.mutate(() => {
|
||||
queuedStarted = true;
|
||||
}, BACKGROUND_CONTEXT);
|
||||
|
||||
const result = await mutation.commit([setValue(sessionName, "explicit")], BACKGROUND_CONTEXT);
|
||||
expect(result.seqs).toHaveLength(1);
|
||||
expect(queuedStarted).toBe(false);
|
||||
expect(await mutation.getValue(sessionName, BACKGROUND_CONTEXT)).toMatchObject({ value: "explicit" });
|
||||
await mutation.end(BACKGROUND_CONTEXT);
|
||||
await queued;
|
||||
expect(queuedStarted).toBe(true);
|
||||
|
||||
await session.close(BACKGROUND_CONTEXT);
|
||||
const reopened = await repo.open(session.metadata, BACKGROUND_CONTEXT);
|
||||
expect(await reopened.getName(BACKGROUND_CONTEXT)).toBe("explicit");
|
||||
await reopened.close(BACKGROUND_CONTEXT);
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("rejects unsupported storage versions without repairing a torn tail", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const session = await repo.create({ id: "future", cwd: "/workspace" }, BACKGROUND_CONTEXT);
|
||||
const metadata = session.metadata;
|
||||
await session.close(BACKGROUND_CONTEXT);
|
||||
|
||||
const lines = getOrThrow(await fileSystem.readTextFile(metadata.path, BACKGROUND_CONTEXT))
|
||||
.trimEnd()
|
||||
.split("\n");
|
||||
const unsupportedVersion = JSONL_STORAGE_VERSION + 1;
|
||||
lines[0] = JSON.stringify({ ...JSON.parse(lines[0]!), storageVersion: unsupportedVersion });
|
||||
const unsupportedContent = `${lines.join("\n")}\n{"kind":"entry"`;
|
||||
getOrThrow(await fileSystem.writeFile(metadata.path, unsupportedContent, BACKGROUND_CONTEXT));
|
||||
|
||||
await expect(repo.open(metadata, BACKGROUND_CONTEXT)).rejects.toThrow(
|
||||
`unsupported storage version ${unsupportedVersion}`,
|
||||
);
|
||||
expect(getOrThrow(await fileSystem.readTextFile(metadata.path, BACKGROUND_CONTEXT))).toBe(unsupportedContent);
|
||||
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("keeps fork destinations claimed until close and rejects deleting open sessions", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const source = await repo.create({ id: "source", cwd: "/workspace" }, BACKGROUND_CONTEXT);
|
||||
const fork = await repo.fork(source.metadata, { id: "fork", scope: "tree" }, BACKGROUND_CONTEXT);
|
||||
|
||||
await expect(repo.open(fork.metadata, BACKGROUND_CONTEXT)).rejects.toThrow("already open");
|
||||
await expect(repo.delete(fork.metadata, BACKGROUND_CONTEXT)).rejects.toThrow("open");
|
||||
await fork.close(BACKGROUND_CONTEXT);
|
||||
|
||||
const reopened = await repo.open(fork.metadata, BACKGROUND_CONTEXT);
|
||||
await reopened.close(BACKGROUND_CONTEXT);
|
||||
await repo.delete(fork.metadata, BACKGROUND_CONTEXT);
|
||||
await expect(repo.open(fork.metadata, BACKGROUND_CONTEXT)).rejects.toThrow("does not exist");
|
||||
|
||||
await source.close(BACKGROUND_CONTEXT);
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("rejects concurrent creates for the same working-directory id", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
repo.create({ id: "session", cwd: "/workspace" }, BACKGROUND_CONTEXT),
|
||||
repo.create({ id: "session", cwd: "/workspace" }, BACKGROUND_CONTEXT),
|
||||
]);
|
||||
|
||||
expect(results.filter((result) => result.status === "fulfilled")).toHaveLength(1);
|
||||
expect(results.filter((result) => result.status === "rejected")).toHaveLength(1);
|
||||
expect(await repo.list({ cwd: "/workspace" }, BACKGROUND_CONTEXT)).toHaveLength(1);
|
||||
for (const result of results) {
|
||||
if (result.status === "fulfilled") await result.value.close(BACKGROUND_CONTEXT);
|
||||
}
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
|
||||
it("allows the same id to be active in different working directories", async () => {
|
||||
const fileSystem = new NodeExecutionEnv({ cwd: createTempDir() });
|
||||
const repo = new JsonlSessionRepo({ fileSystem, sessionsRoot: "sessions", now: () => NOW });
|
||||
const first = await repo.create({ id: "shared", cwd: "/workspace-a" }, BACKGROUND_CONTEXT);
|
||||
const second = await repo.create({ id: "shared", cwd: "/workspace-b" }, BACKGROUND_CONTEXT);
|
||||
|
||||
expect(first.metadata.path).not.toBe(second.metadata.path);
|
||||
await expect(repo.create({ id: "shared", cwd: "/workspace-a" }, BACKGROUND_CONTEXT)).rejects.toThrow(
|
||||
"already exists",
|
||||
);
|
||||
expect((await repo.list(undefined, BACKGROUND_CONTEXT)).map(({ cwd, id }) => ({ cwd, id }))).toEqual([
|
||||
{ cwd: "/workspace-a", id: "shared" },
|
||||
{ cwd: "/workspace-b", id: "shared" },
|
||||
]);
|
||||
|
||||
await Promise.all([first.close(BACKGROUND_CONTEXT), second.close(BACKGROUND_CONTEXT)]);
|
||||
const reopened = await Promise.all([
|
||||
repo.open(first.metadata, BACKGROUND_CONTEXT),
|
||||
repo.open(second.metadata, BACKGROUND_CONTEXT),
|
||||
]);
|
||||
await Promise.all(reopened.map((session) => session.close(BACKGROUND_CONTEXT)));
|
||||
await repo.close(BACKGROUND_CONTEXT);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user