fix: harden workspace activation and snapshot retention
This commit is contained in:
@@ -30,6 +30,7 @@ const defaultWorkspaceRegistry = {
|
||||
|
||||
function buildApp(config: Parameters<typeof buildRealApp>[0], deps: Record<string, unknown> = {}) {
|
||||
return buildRealApp(config, {
|
||||
workspaceRuntimeSupport: () => true,
|
||||
...deps,
|
||||
workspaceRegistry: { ...defaultWorkspaceRegistry, ...(deps.workspaceRegistry as object | undefined) },
|
||||
} as any);
|
||||
@@ -324,6 +325,100 @@ test("creates a session from the active immutable workspace revision", async ()
|
||||
}));
|
||||
});
|
||||
|
||||
test("rejects an SSH-only workspace before persisting or starting a session", async () => {
|
||||
const sessionNew = vi.fn(async () => ({ id: "must-not-exist" }));
|
||||
const ensure = vi.fn(async () => ({ ok: true }));
|
||||
const createFor = vi.fn();
|
||||
const abort = vi.fn(async () => {});
|
||||
const markPersisted = vi.fn(async () => {});
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
thtRunner: { sessionNew, searchPack: async () => {} } as any,
|
||||
readiness: { ensure } as any,
|
||||
mgr: { get: () => undefined, createFor } as any,
|
||||
getSettings: () => ({ workspace: "ssh-workspace" }) as any,
|
||||
workspaceRuntimeSupport: vi.fn(() => false),
|
||||
workspaceRegistry: {
|
||||
acquireSessionRevision: vi.fn(async () => ({
|
||||
workspace: {
|
||||
workspace: { schema_version: 2, id: "ssh-workspace", name: "SSH", language: "en" },
|
||||
dwh: {
|
||||
engine: "postgres", database: "postgres", schema: "public",
|
||||
supported_transports: ["ssh_tunnel"],
|
||||
},
|
||||
semantic_index: {
|
||||
vector_store: {
|
||||
engine: "pgvector", database: "postgres", schema: "vectors",
|
||||
collection: "documents", dimensions: 768, distance: "cosine",
|
||||
supported_transports: ["ssh_tunnel"],
|
||||
},
|
||||
embedding: {
|
||||
provider: "ollama_compatible", model: "nomic-embed-text", dimensions: 768,
|
||||
},
|
||||
},
|
||||
llm_policy: { allowed: ["zai/glm-5.2"] },
|
||||
},
|
||||
revision: {
|
||||
id: "ssh-workspace", commit: "a".repeat(40), blob: "b".repeat(40),
|
||||
snapshotPath: `/data/workspace-registry/snapshots/${"a".repeat(40)}/ssh-workspace.yaml`,
|
||||
state: "operational",
|
||||
},
|
||||
abort,
|
||||
markPersisted,
|
||||
})),
|
||||
} as any,
|
||||
});
|
||||
|
||||
const response = await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
|
||||
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response.json()).toMatchObject({ code: "workspace_not_activatable" });
|
||||
expect(ensure).not.toHaveBeenCalled();
|
||||
expect(sessionNew).not.toHaveBeenCalled();
|
||||
expect(createFor).not.toHaveBeenCalled();
|
||||
expect(abort).toHaveBeenCalledOnce();
|
||||
expect(markPersisted).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("hands a revision lease to retention only after the session manifest is durable", async () => {
|
||||
const persisted = deferred<{ id: string }>();
|
||||
const markPersisted = vi.fn(async () => {});
|
||||
const abort = vi.fn(async () => {});
|
||||
const acquireSessionRevision = vi.fn(async () => ({
|
||||
workspace: { llm_policy: { allowed: ["zai/glm-5.2"] } },
|
||||
revision: {
|
||||
id: "leased", commit: "a".repeat(40), blob: "b".repeat(40),
|
||||
snapshotPath: `/data/workspace-registry/snapshots/${"a".repeat(40)}/leased.yaml`,
|
||||
state: "operational",
|
||||
},
|
||||
markPersisted,
|
||||
abort,
|
||||
}));
|
||||
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
|
||||
thtRunner: { sessionNew: () => persisted.promise, searchPack: async () => {} } as any,
|
||||
readiness: { ensure: async () => ({ ok: true }) } as any,
|
||||
mgr: {
|
||||
get: () => undefined,
|
||||
createFor: () => ({ bridge: { onClientEvent: () => {} } }),
|
||||
configure: async () => {},
|
||||
start: () => {},
|
||||
} as any,
|
||||
getSettings: () => ({ workspace: "leased", provider: "zai", model: "glm-5.2" }) as any,
|
||||
listModels: async () => [{ provider: "zai", id: "glm-5.2", name: "GLM", reasoning: true }],
|
||||
workspaceRuntimeSupport: () => true,
|
||||
workspaceRegistry: { acquireSessionRevision } as any,
|
||||
});
|
||||
|
||||
const request = app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
expect(markPersisted).not.toHaveBeenCalled();
|
||||
expect(abort).not.toHaveBeenCalled();
|
||||
|
||||
persisted.resolve({ id: "leased-session" });
|
||||
expect((await request).statusCode).toBe(200);
|
||||
expect(markPersisted).toHaveBeenCalledOnce();
|
||||
expect(abort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("creates a session from the configured default workspace revision when workspaceId is omitted", async () => {
|
||||
const sessionNew = vi.fn(async () => ({ id: "default-pinned" }));
|
||||
const registry = {
|
||||
|
||||
Reference in New Issue
Block a user