282 lines
10 KiB
TypeScript
282 lines
10 KiB
TypeScript
import { http, HttpResponse } from "msw";
|
|
import { server } from "../test/msw";
|
|
import { canonicalWorkspaceFixture, workspaceRevisionFixture, workspaceSummaryFixture } from "../test/workspace-fixtures";
|
|
import { createSession, getMe, listSessions, prewarmRuntime, resumeSession } from "./sessions";
|
|
import {
|
|
renameSession, setSessionGroup, archiveSession, unarchiveSession,
|
|
deleteSession, getSessionDocuments,
|
|
} from "./sessions";
|
|
import { workspacePreferences } from "../workspaces/preferences";
|
|
|
|
test("createSession seeds ephemeral selections and posts them", async () => {
|
|
localStorage.clear();
|
|
let body: unknown = null;
|
|
server.use(
|
|
http.get("/api/settings", () => HttpResponse.json({
|
|
workspace: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
})),
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", {
|
|
displayName: "PSD Clinical",
|
|
revision: workspaceRevisionFixture("psd-clinical"),
|
|
}),
|
|
}])),
|
|
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("psd-clinical"),
|
|
revision: { id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot" },
|
|
})),
|
|
http.post("/api/sessions", async ({ request }) => {
|
|
body = await request.json();
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
expect(await createSession({ question: "q" })).toEqual({ id: "s1" });
|
|
expect(body).toEqual({
|
|
question: "q", workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
expect(workspacePreferences.load()).toEqual({
|
|
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
expect(localStorage.getItem("thothii.workspace-registry.v1.preferences")).toBeNull();
|
|
});
|
|
|
|
test("createSession does not POST when a selected summary aliases another workspace name", async () => {
|
|
workspacePreferences.save({
|
|
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
let posted = false;
|
|
server.use(
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", {
|
|
displayName: "PSD Clinical",
|
|
revision: workspaceRevisionFixture("psd-clinical"),
|
|
}),
|
|
name: "other-workspace",
|
|
}])),
|
|
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("psd-clinical"),
|
|
revision: {
|
|
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
|
|
},
|
|
})),
|
|
http.post("/api/sessions", () => {
|
|
posted = true;
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
await expect(createSession({ question: "q" })).rejects.toMatchObject({
|
|
message: "Could not load workspace registry. Please retry.",
|
|
});
|
|
expect(posted).toBe(false);
|
|
});
|
|
|
|
test.each([
|
|
["historical state", { state: "operational" }],
|
|
["an unknown revision field", { generation: 1 }],
|
|
["a malformed revision", { commit: "not-a-commit" }],
|
|
])("createSession does not POST when the selected summary revision has %s", async (_case, revisionPatch) => {
|
|
workspacePreferences.save({
|
|
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
const revision = {
|
|
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40),
|
|
snapshotPath: "/snapshot", ...revisionPatch,
|
|
};
|
|
let posted = false;
|
|
server.use(
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", {
|
|
displayName: "PSD Clinical",
|
|
revision: revision as any,
|
|
}),
|
|
}])),
|
|
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("psd-clinical"),
|
|
revision: {
|
|
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
|
|
},
|
|
})),
|
|
http.post("/api/sessions", () => {
|
|
posted = true;
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
await expect(createSession({ question: "q" })).rejects.toMatchObject({
|
|
message: "Could not load workspace registry. Please retry.",
|
|
});
|
|
expect(posted).toBe(false);
|
|
});
|
|
|
|
test("createSession replaces a stale ephemeral workspace with the current installation default", async () => {
|
|
workspacePreferences.save({
|
|
workspaceId: "retired-workspace", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
let body: unknown;
|
|
server.use(
|
|
http.get("/api/settings", () => HttpResponse.json({ workspace: "psd-clinical" })),
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", {
|
|
displayName: "PSD Clinical",
|
|
revision: workspaceRevisionFixture("psd-clinical"),
|
|
}),
|
|
}])),
|
|
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({
|
|
workspace: canonicalWorkspaceFixture("psd-clinical"),
|
|
revision: {
|
|
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40), snapshotPath: "/snapshot",
|
|
},
|
|
})),
|
|
http.post("/api/sessions", async ({ request }) => {
|
|
body = await request.json();
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
await expect(createSession({ question: "q" })).resolves.toEqual({ id: "s1" });
|
|
expect(body).toEqual({
|
|
question: "q", workspaceId: "psd-clinical",
|
|
provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
expect(workspacePreferences.load()).toEqual({
|
|
workspaceId: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
});
|
|
|
|
test("createSession preserves a local legacy selection when the registry is empty", async () => {
|
|
workspacePreferences.save({
|
|
workspaceId: "legacy-workspace", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
let body: unknown;
|
|
server.use(
|
|
http.get("/api/workspaces", () => HttpResponse.json([])),
|
|
http.post("/api/sessions", async ({ request }) => {
|
|
body = await request.json();
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
await expect(createSession({ question: "q" })).resolves.toEqual({ id: "s1" });
|
|
expect(body).toEqual({
|
|
question: "q", workspaceId: "legacy-workspace",
|
|
provider: "zai", model: "glm-5.2", thinking: "low",
|
|
});
|
|
});
|
|
|
|
test("createSession rejects a workspace summary that omits the canonical revision", async () => {
|
|
localStorage.clear();
|
|
let posted = false;
|
|
server.use(
|
|
http.get("/api/settings", () => HttpResponse.json({
|
|
workspace: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
|
})),
|
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
|
...workspaceSummaryFixture("psd-clinical", {
|
|
displayName: "PSD Clinical",
|
|
configurationState: "configuration_required",
|
|
}),
|
|
}])),
|
|
http.post("/api/sessions", async () => {
|
|
posted = true;
|
|
return HttpResponse.json({ id: "s1" });
|
|
}),
|
|
);
|
|
|
|
await expect(createSession({ question: "q" })).rejects.toMatchObject({
|
|
message: "Could not load workspace registry. Please retry.",
|
|
});
|
|
expect(posted).toBe(false);
|
|
});
|
|
|
|
test.each([202, 204])("prewarmRuntime accepts a body-less %s response", async (status) => {
|
|
let called = false;
|
|
server.use(
|
|
http.post("/api/runtime/prewarm", () => {
|
|
called = true;
|
|
return new HttpResponse(null, { status });
|
|
}),
|
|
);
|
|
await expect(prewarmRuntime()).resolves.toBeUndefined();
|
|
expect(called).toBe(true);
|
|
});
|
|
|
|
test("listSessions defaults to the current user's scope", async () => {
|
|
let scope: string | null = null;
|
|
server.use(http.get("/api/sessions", ({ request }) => {
|
|
scope = new URL(request.url).searchParams.get("scope");
|
|
return HttpResponse.json([{ id: "s1", status: "open", question: "q", summary: null, created_at: "t", updated_at: null, author: null }]);
|
|
}));
|
|
const rows = await listSessions();
|
|
expect(rows[0].id).toBe("s1");
|
|
expect(scope).toBe("mine");
|
|
});
|
|
|
|
test("listSessions requests the selected administrator scope", async () => {
|
|
let scope: string | null = null;
|
|
server.use(http.get("/api/sessions", ({ request }) => {
|
|
scope = new URL(request.url).searchParams.get("scope");
|
|
return HttpResponse.json([]);
|
|
}));
|
|
await listSessions("all");
|
|
expect(scope).toBe("all");
|
|
});
|
|
|
|
test("getMe fetches the typed authenticated principal", async () => {
|
|
server.use(http.get("/api/me", () =>
|
|
HttpResponse.json({ issuer: "portal", subject: "42", displayName: "Alice", isAdmin: true }),
|
|
));
|
|
await expect(getMe()).resolves.toEqual({
|
|
issuer: "portal", subject: "42", displayName: "Alice", isAdmin: true,
|
|
});
|
|
});
|
|
|
|
test("resumeSession returns the typed runtime disposition", async () => {
|
|
server.use(http.post("/api/sessions/s1/resume", () =>
|
|
HttpResponse.json({ id: "s1", alreadyActive: false })));
|
|
|
|
const result: { id: string; alreadyActive: boolean } = await resumeSession("s1");
|
|
expect(result).toEqual({ id: "s1", alreadyActive: false });
|
|
});
|
|
|
|
test("renameSession POSTs {name}", async () => {
|
|
let body: unknown = null;
|
|
server.use(http.post("/api/sessions/s1/rename", async ({ request }) => {
|
|
body = await request.json();
|
|
return new HttpResponse(null, { status: 204 });
|
|
}));
|
|
await renameSession("s1", "Nome");
|
|
expect(body).toEqual({ name: "Nome" });
|
|
});
|
|
|
|
test("setSessionGroup POSTs {group}", async () => {
|
|
let body: unknown = null;
|
|
server.use(http.post("/api/sessions/s1/group", async ({ request }) => {
|
|
body = await request.json();
|
|
return new HttpResponse(null, { status: 204 });
|
|
}));
|
|
await setSessionGroup("s1", "G");
|
|
expect(body).toEqual({ group: "G" });
|
|
});
|
|
|
|
test("archive / unarchive / delete hit the right verbs+paths", async () => {
|
|
const hits: string[] = [];
|
|
server.use(
|
|
http.post("/api/sessions/s1/archive", () => { hits.push("archive"); return new HttpResponse(null, { status: 204 }); }),
|
|
http.post("/api/sessions/s1/unarchive", () => { hits.push("unarchive"); return new HttpResponse(null, { status: 204 }); }),
|
|
http.delete("/api/sessions/s1", () => { hits.push("delete"); return new HttpResponse(null, { status: 204 }); }),
|
|
);
|
|
await archiveSession("s1");
|
|
await unarchiveSession("s1");
|
|
await deleteSession("s1");
|
|
expect(hits).toEqual(["archive", "unarchive", "delete"]);
|
|
});
|
|
|
|
test("getSessionDocuments GETs the array", async () => {
|
|
server.use(http.get("/api/sessions/s1/documents", () =>
|
|
HttpResponse.json([{ phase: "—", key: "question", title: "t", format: "text", content: "q" }]),
|
|
));
|
|
const docs = await getSessionDocuments("s1");
|
|
expect(docs[0].key).toBe("question");
|
|
});
|