feat: pin sessions to workspace revisions
This commit is contained in:
@@ -603,6 +603,54 @@ test("POST /sessions/:id/resume returns a sanitized error when its retained revi
|
||||
expect(response.json()).toMatchObject({ code: "workspace_revision_unavailable" });
|
||||
});
|
||||
|
||||
test("a pruned pin blocks Resume but not active or mutation lifecycle routes", async () => {
|
||||
const activePath = "/registry/snapshots/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/b-workspace.yaml";
|
||||
const prunedError = "cannot read /registry/snapshots/secret-pruned-revision/b-workspace.yaml";
|
||||
const calls: string[] = [];
|
||||
const readPinned = vi.fn(async () => { throw new Error(prunedError); });
|
||||
let active: any = { bridge: { respond: () => true } };
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
thtRunner: {
|
||||
sessionShow: async (_id: string, workspace: string) => {
|
||||
expect(workspace).toBe(activePath);
|
||||
return {
|
||||
id: "pruned", status: "open", archived: false,
|
||||
workspace_id: "b-workspace", workspace_revision: "b".repeat(40),
|
||||
};
|
||||
},
|
||||
closeSession: async (_id: string, workspace: string) => { calls.push(`close:${workspace}`); },
|
||||
deleteSession: async (_id: string, workspace: string) => { calls.push(`delete:${workspace}`); },
|
||||
} as any,
|
||||
mgr: {
|
||||
get: () => active,
|
||||
teardownIfCurrent: (_id: string, expected: any) => {
|
||||
if (active !== expected) return false;
|
||||
active = undefined;
|
||||
return true;
|
||||
},
|
||||
} as any,
|
||||
workspaceRegistry: {
|
||||
list: async () => [{
|
||||
id: "b-workspace", commit: "a".repeat(40), blob: "a".repeat(40), snapshotPath: activePath, state: "operational",
|
||||
}],
|
||||
readPinned,
|
||||
} as any,
|
||||
});
|
||||
|
||||
expect((await app.inject({ method: "POST", url: "/sessions/pruned/response", payload: { ui_response: {} } })).statusCode)
|
||||
.toBe(204);
|
||||
expect((await app.inject({ method: "POST", url: "/sessions/pruned/close" })).statusCode).toBe(200);
|
||||
expect((await app.inject({ method: "DELETE", url: "/sessions/pruned" })).statusCode).toBe(204);
|
||||
expect(calls).toEqual([`close:${activePath}`, `delete:${activePath}`]);
|
||||
expect(readPinned).not.toHaveBeenCalled();
|
||||
|
||||
const resume = await app.inject({ method: "POST", url: "/sessions/pruned/resume" });
|
||||
expect(resume.statusCode).toBe(409);
|
||||
expect(resume.body).not.toContain(prunedError);
|
||||
expect(resume.json()).toMatchObject({ code: "workspace_revision_unavailable" });
|
||||
expect(readPinned).toHaveBeenCalledWith("b-workspace", "b".repeat(40));
|
||||
});
|
||||
|
||||
test("POST /sessions/:id/resume refuses a pinned finalized session before reading its snapshot", async () => {
|
||||
const readPinned = vi.fn(async () => { throw new Error("must not resolve"); });
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
|
||||
Reference in New Issue
Block a user