feat: publish and synchronize workspace drafts

This commit is contained in:
2026-08-04 06:47:10 +02:00
parent 6b40fa0cc5
commit b75b3af28e
9 changed files with 545 additions and 16 deletions
+22 -1
View File
@@ -1,7 +1,17 @@
import { expect, test } from "vitest";
import { http, HttpResponse } from "msw";
import { server } from "../test/msw";
import { importWorkspace } from "./workspaces";
import { asWorkspaceConflict, importWorkspace, publishWorkspace, type CanonicalWorkspace } from "./workspaces";
const workspace: CanonicalWorkspace = {
workspace: { schema_version: 2, id: "psd-clinical", name: "PSD Clinical", language: "en" },
dwh: { engine: "postgres", database: "clinical", schema: "datawarehouse", supported_transports: ["postgres_direct"] },
semantic_index: {
vector_store: { engine: "pgvector", database: "vectors", schema: "public", collection: "clinical", dimensions: 768, distance: "cosine", supported_transports: ["pgvector_direct"] },
embedding: { provider: "ollama_compatible", model: "nomic-embed-text", dimensions: 768 },
},
llm_policy: { allowed: ["zai/glm-5.2"] },
};
test("uploads a workspace bundle without JSON content type", async () => {
let contentType: string | null = null;
@@ -17,3 +27,14 @@ test("uploads a workspace bundle without JSON content type", async () => {
// that header untouched; a real browser adds multipart/form-data + boundary.
expect(contentType ?? "").not.toMatch(/application\/json/i);
});
test("rejects a conflict payload that attempts to surface a secret field", async () => {
server.use(http.post("http://localhost:8787/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 })));
const error = await publishWorkspace({ action: "update", workspace, baseCommit: "a".repeat(40), baseBlob: "b".repeat(40) }).catch((cause: unknown) => cause);
expect(asWorkspaceConflict(error)).toBeUndefined();
});