fix: harden Pi management readiness
This commit is contained in:
@@ -5,7 +5,7 @@ import { apiFetch } from "./client";
|
||||
test("body-less POST omits content-type (avoids Fastify empty-body 400)", async () => {
|
||||
let contentType: string | null = "unset";
|
||||
server.use(
|
||||
http.post("http://localhost:8787/sessions/s1/resume", ({ request }) => {
|
||||
http.post("/api/sessions/s1/resume", ({ request }) => {
|
||||
contentType = request.headers.get("content-type");
|
||||
return HttpResponse.json({ id: "s1" });
|
||||
}),
|
||||
@@ -17,7 +17,7 @@ test("body-less POST omits content-type (avoids Fastify empty-body 400)", async
|
||||
test("POST with a body sends application/json content-type", async () => {
|
||||
let contentType: string | null = null;
|
||||
server.use(
|
||||
http.post("http://localhost:8787/sessions/s1/steer", ({ request }) => {
|
||||
http.post("/api/sessions/s1/steer", ({ request }) => {
|
||||
contentType = request.headers.get("content-type");
|
||||
return new HttpResponse(null, { status: 204 });
|
||||
}),
|
||||
|
||||
@@ -14,7 +14,7 @@ test("Pi management client calls only the sanctioned sanitized endpoints", async
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", ({ request }) => {
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname });
|
||||
return HttpResponse.json({ ready: true, version: "0.80.3", config: {}, checkedAt: "2026-08-05T10:00:00.000Z" });
|
||||
return HttpResponse.json({ ready: true, version: "0.80.3", credentials: "present", config: {}, checkedAt: "2026-08-05T10:00:00.000Z" });
|
||||
}),
|
||||
http.get("/api/pi-management/options", ({ request }) => {
|
||||
calls.push({ method: request.method, path: new URL(request.url).pathname });
|
||||
@@ -34,7 +34,7 @@ test("Pi management client calls only the sanctioned sanitized endpoints", async
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(getPiManagementStatus()).resolves.toMatchObject({ version: "0.80.3", ready: true });
|
||||
await expect(getPiManagementStatus()).resolves.toMatchObject({ version: "0.80.3", ready: true, credentials: "present" });
|
||||
await expect(getPiManagementOptions()).resolves.toMatchObject({ providers: ["zai"] });
|
||||
await expect(savePiManagementConfig({ provider: "zai", model: "glm-5.2", reasoning: "high" })).resolves.toMatchObject({ reasoning: "high" });
|
||||
await expect(runPiManagementTest()).resolves.toMatchObject({ ready: true });
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface PiInstallationConfig {
|
||||
export interface PiManagementStatus {
|
||||
version?: string;
|
||||
ready: boolean;
|
||||
credentials: "present" | "missing";
|
||||
config: Partial<PiInstallationConfig>;
|
||||
checkedAt: string;
|
||||
message?: string;
|
||||
|
||||
@@ -10,13 +10,13 @@ test("createSession migrates legacy selections and POSTs browser preferences", a
|
||||
localStorage.clear();
|
||||
let body: unknown = null;
|
||||
server.use(
|
||||
http.get("http://localhost:8787/settings", () => HttpResponse.json({
|
||||
http.get("/api/settings", () => HttpResponse.json({
|
||||
workspace: "psd-clinical", provider: "zai", model: "glm-5.2", thinking: "low",
|
||||
})),
|
||||
http.get("http://localhost:8787/workspaces", () => HttpResponse.json([{
|
||||
http.get("/api/workspaces", () => HttpResponse.json([{
|
||||
id: "psd-clinical", name: "psd-clinical", file: "psd-clinical.yaml", displayName: "PSD Clinical",
|
||||
}])),
|
||||
http.post("http://localhost:8787/sessions", async ({ request }) => {
|
||||
http.post("/api/sessions", async ({ request }) => {
|
||||
body = await request.json();
|
||||
return HttpResponse.json({ id: "s1" });
|
||||
}),
|
||||
@@ -33,7 +33,7 @@ test("createSession migrates legacy selections and POSTs browser preferences", a
|
||||
test.each([202, 204])("prewarmRuntime accepts a body-less %s response", async (status) => {
|
||||
let called = false;
|
||||
server.use(
|
||||
http.post("http://localhost:8787/runtime/prewarm", () => {
|
||||
http.post("/api/runtime/prewarm", () => {
|
||||
called = true;
|
||||
return new HttpResponse(null, { status });
|
||||
}),
|
||||
@@ -44,7 +44,7 @@ test.each([202, 204])("prewarmRuntime accepts a body-less %s response", async (s
|
||||
|
||||
test("listSessions defaults to the current user's scope", async () => {
|
||||
let scope: string | null = null;
|
||||
server.use(http.get("http://localhost:8787/sessions", ({ request }) => {
|
||||
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 }]);
|
||||
}));
|
||||
@@ -55,7 +55,7 @@ test("listSessions defaults to the current user's scope", async () => {
|
||||
|
||||
test("listSessions requests the selected administrator scope", async () => {
|
||||
let scope: string | null = null;
|
||||
server.use(http.get("http://localhost:8787/sessions", ({ request }) => {
|
||||
server.use(http.get("/api/sessions", ({ request }) => {
|
||||
scope = new URL(request.url).searchParams.get("scope");
|
||||
return HttpResponse.json([]);
|
||||
}));
|
||||
@@ -64,7 +64,7 @@ test("listSessions requests the selected administrator scope", async () => {
|
||||
});
|
||||
|
||||
test("getMe fetches the typed authenticated principal", async () => {
|
||||
server.use(http.get("http://localhost:8787/me", () =>
|
||||
server.use(http.get("/api/me", () =>
|
||||
HttpResponse.json({ issuer: "portal", subject: "42", displayName: "Alice", isAdmin: true }),
|
||||
));
|
||||
await expect(getMe()).resolves.toEqual({
|
||||
@@ -73,7 +73,7 @@ test("getMe fetches the typed authenticated principal", async () => {
|
||||
});
|
||||
|
||||
test("resumeSession returns the typed runtime disposition", async () => {
|
||||
server.use(http.post("http://localhost:8787/sessions/s1/resume", () =>
|
||||
server.use(http.post("/api/sessions/s1/resume", () =>
|
||||
HttpResponse.json({ id: "s1", alreadyActive: false })));
|
||||
|
||||
const result: { id: string; alreadyActive: boolean } = await resumeSession("s1");
|
||||
@@ -82,7 +82,7 @@ test("resumeSession returns the typed runtime disposition", async () => {
|
||||
|
||||
test("renameSession POSTs {name}", async () => {
|
||||
let body: unknown = null;
|
||||
server.use(http.post("http://localhost:8787/sessions/s1/rename", async ({ request }) => {
|
||||
server.use(http.post("/api/sessions/s1/rename", async ({ request }) => {
|
||||
body = await request.json();
|
||||
return new HttpResponse(null, { status: 204 });
|
||||
}));
|
||||
@@ -92,7 +92,7 @@ test("renameSession POSTs {name}", async () => {
|
||||
|
||||
test("setSessionGroup POSTs {group}", async () => {
|
||||
let body: unknown = null;
|
||||
server.use(http.post("http://localhost:8787/sessions/s1/group", async ({ request }) => {
|
||||
server.use(http.post("/api/sessions/s1/group", async ({ request }) => {
|
||||
body = await request.json();
|
||||
return new HttpResponse(null, { status: 204 });
|
||||
}));
|
||||
@@ -103,9 +103,9 @@ test("setSessionGroup POSTs {group}", async () => {
|
||||
test("archive / unarchive / delete hit the right verbs+paths", async () => {
|
||||
const hits: string[] = [];
|
||||
server.use(
|
||||
http.post("http://localhost:8787/sessions/s1/archive", () => { hits.push("archive"); return new HttpResponse(null, { status: 204 }); }),
|
||||
http.post("http://localhost:8787/sessions/s1/unarchive", () => { hits.push("unarchive"); return new HttpResponse(null, { status: 204 }); }),
|
||||
http.delete("http://localhost:8787/sessions/s1", () => { hits.push("delete"); return new HttpResponse(null, { status: 204 }); }),
|
||||
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");
|
||||
@@ -114,7 +114,7 @@ test("archive / unarchive / delete hit the right verbs+paths", async () => {
|
||||
});
|
||||
|
||||
test("getSessionDocuments GETs the array", async () => {
|
||||
server.use(http.get("http://localhost:8787/sessions/s1/documents", () =>
|
||||
server.use(http.get("/api/sessions/s1/documents", () =>
|
||||
HttpResponse.json([{ phase: "—", key: "question", title: "t", format: "text", content: "q" }]),
|
||||
));
|
||||
const docs = await getSessionDocuments("s1");
|
||||
|
||||
@@ -15,7 +15,7 @@ const workspace: CanonicalWorkspace = {
|
||||
|
||||
test("uploads a workspace bundle without JSON content type", async () => {
|
||||
let contentType: string | null = null;
|
||||
server.use(http.post("http://localhost:8787/workspaces/import", ({ request }) => {
|
||||
server.use(http.post("/api/workspaces/import", ({ request }) => {
|
||||
contentType = request.headers.get("content-type");
|
||||
return HttpResponse.json({ draft: { workspace: {} } });
|
||||
}));
|
||||
@@ -29,7 +29,7 @@ test("uploads a workspace bundle without JSON content type", async () => {
|
||||
});
|
||||
|
||||
test("rejects a conflict payload that attempts to surface a secret field", async () => {
|
||||
server.use(http.post("http://localhost:8787/workspaces/publish", () => HttpResponse.json({
|
||||
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
|
||||
code: "workspace_conflict", message: "Workspace changed in the registry.", fields: ["dwh.password"],
|
||||
base: { ...workspace, dwh: { ...workspace.dwh, password: "secret" } }, local: workspace, remote: workspace,
|
||||
}, { status: 409 })));
|
||||
@@ -73,7 +73,7 @@ const diagnosticsWorkspace: CanonicalWorkspace = {
|
||||
};
|
||||
|
||||
test.each(optionalDiagnosticsConflictFields)("accepts optional diagnostics conflict branch %s", async (field) => {
|
||||
server.use(http.post("http://localhost:8787/workspaces/publish", () => HttpResponse.json({
|
||||
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
|
||||
code: "workspace_conflict", message: "Workspace changed in the registry.", fields: [field],
|
||||
expected: { commit: "a".repeat(40), blob: "b".repeat(40) },
|
||||
actual: { commit: "c".repeat(40), blob: "d".repeat(40) },
|
||||
@@ -99,7 +99,7 @@ test.each(diagnosticConflictFields)("accepts canonical diagnostic conflict leaf
|
||||
embedding: { method: "GET", path: "/models", auth: "none", response: { model: "model", dimensions: "dimensions" } },
|
||||
},
|
||||
};
|
||||
server.use(http.post("http://localhost:8787/workspaces/publish", () => HttpResponse.json({
|
||||
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
|
||||
code: "workspace_conflict", message: "Workspace changed in the registry.",
|
||||
fields: [field],
|
||||
expected: { commit: "a".repeat(40), blob: "b".repeat(40) },
|
||||
|
||||
Reference in New Issue
Block a user