fix: preserve workspace evidence in browser drafts
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { http, HttpResponse } from "msw";
|
||||
import { server } from "../test/msw";
|
||||
import { asWorkspaceConflict, importWorkspace, publishWorkspace, type CanonicalWorkspace } from "./workspaces";
|
||||
import {
|
||||
asWorkspaceConflict, getWorkspace, importWorkspace, publishWorkspace, validateWorkspace,
|
||||
type CanonicalWorkspace,
|
||||
} from "./workspaces";
|
||||
|
||||
const workspace: CanonicalWorkspace = {
|
||||
workspace: { schema_version: 3, id: "psd-clinical", name: "PSD Clinical", language: "en" },
|
||||
@@ -13,6 +16,27 @@ const workspace: CanonicalWorkspace = {
|
||||
llm_policy: { allowed: ["zai/glm-5.2"] },
|
||||
};
|
||||
|
||||
const evidenceWorkspace = {
|
||||
...workspace,
|
||||
evidence: {
|
||||
source: {
|
||||
type: "filesystem",
|
||||
uri: "workspace-content/psd-clinical/evidence",
|
||||
patterns: ["**/*.md"],
|
||||
max_bytes: 10 * 1024 * 1024,
|
||||
},
|
||||
policy: { max_chunk_chars: 4_000, retain_published_generations: 3 },
|
||||
},
|
||||
} satisfies CanonicalWorkspace;
|
||||
|
||||
const revision = {
|
||||
id: "psd-clinical",
|
||||
commit: "a".repeat(40),
|
||||
blob: "b".repeat(40),
|
||||
snapshotPath: "workspaces/psd-clinical.yaml",
|
||||
state: "operational" as const,
|
||||
};
|
||||
|
||||
test("uploads a workspace bundle without JSON content type", async () => {
|
||||
let contentType: string | null = null;
|
||||
server.use(http.post("/api/workspaces/import", ({ request }) => {
|
||||
@@ -107,3 +131,116 @@ test("rejects a conflict payload that attempts to surface removed vector transpo
|
||||
|
||||
expect(asWorkspaceConflict(error)).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
test("sanitizes read and validate responses while preserving Evidence", async () => {
|
||||
server.use(
|
||||
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({ workspace: evidenceWorkspace, revision })),
|
||||
http.post("/api/workspaces/validate", () => HttpResponse.json({ workspace: evidenceWorkspace, contract: {} })),
|
||||
);
|
||||
|
||||
const read = await getWorkspace("psd-clinical");
|
||||
const validated = await validateWorkspace(evidenceWorkspace);
|
||||
|
||||
expect(read.workspace.evidence).toEqual(evidenceWorkspace.evidence);
|
||||
expect(read.workspace).not.toBe(evidenceWorkspace);
|
||||
expect(validated.workspace.evidence).toEqual(evidenceWorkspace.evidence);
|
||||
});
|
||||
|
||||
test("rejects malformed workspace API responses instead of exposing unknown Evidence fields", async () => {
|
||||
const malformed = {
|
||||
...evidenceWorkspace,
|
||||
evidence: { ...evidenceWorkspace.evidence, signed_urls_file: "/run/secrets/urls" },
|
||||
};
|
||||
server.use(
|
||||
http.get("/api/workspaces/psd-clinical", () => HttpResponse.json({ workspace: malformed, revision })),
|
||||
http.post("/api/workspaces/validate", () => HttpResponse.json({ workspace: malformed, contract: {} })),
|
||||
);
|
||||
|
||||
await expect(getWorkspace("psd-clinical")).rejects.toThrow();
|
||||
await expect(validateWorkspace(evidenceWorkspace)).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("publishes Evidence without mutating or dropping it from the request", async () => {
|
||||
let sent: unknown;
|
||||
server.use(http.post("/api/workspaces/publish", async ({ request }) => {
|
||||
sent = await request.json();
|
||||
return HttpResponse.json({ revision });
|
||||
}));
|
||||
|
||||
await publishWorkspace({
|
||||
action: "update", workspace: evidenceWorkspace,
|
||||
baseCommit: revision.commit, baseBlob: revision.blob,
|
||||
});
|
||||
|
||||
expect(sent).toMatchObject({ workspace: { evidence: evidenceWorkspace.evidence } });
|
||||
expect(evidenceWorkspace.evidence.source.patterns).toEqual(["**/*.md"]);
|
||||
});
|
||||
|
||||
const evidenceConflictFields = [
|
||||
"evidence",
|
||||
"evidence.source",
|
||||
"evidence.source.type",
|
||||
"evidence.source.uri",
|
||||
"evidence.source.patterns",
|
||||
"evidence.source.max_bytes",
|
||||
"evidence.source.uris",
|
||||
"evidence.source.authentication",
|
||||
"evidence.source.connect_timeout_ms",
|
||||
"evidence.source.read_timeout_ms",
|
||||
"evidence.source.max_redirects",
|
||||
"evidence.source.allow_private_hosts",
|
||||
"evidence.source.max_cache_bytes",
|
||||
"evidence.source.endpoint_url",
|
||||
"evidence.source.region",
|
||||
"evidence.source.credentials",
|
||||
"evidence.source.trusted_endpoint",
|
||||
"evidence.source.allow_private_endpoint",
|
||||
"evidence.source.allow_insecure_endpoint",
|
||||
"evidence.source.max_objects",
|
||||
"evidence.source.max_pages",
|
||||
"evidence.source.page_size",
|
||||
"evidence.policy",
|
||||
"evidence.policy.max_chunk_chars",
|
||||
"evidence.policy.retain_published_generations",
|
||||
] as const;
|
||||
|
||||
test.each(evidenceConflictFields)("accepts canonical Evidence conflict field %s", async (field) => {
|
||||
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) },
|
||||
base: evidenceWorkspace,
|
||||
local: evidenceWorkspace,
|
||||
remote: evidenceWorkspace,
|
||||
}, { status: 409 })));
|
||||
|
||||
const error = await publishWorkspace({
|
||||
action: "update", workspace: evidenceWorkspace,
|
||||
baseCommit: "a".repeat(40), baseBlob: "b".repeat(40),
|
||||
}).catch((cause: unknown) => cause);
|
||||
|
||||
expect(asWorkspaceConflict(error)).toMatchObject({ fields: [field] });
|
||||
});
|
||||
|
||||
test("rejects unknown Evidence conflict paths", async () => {
|
||||
server.use(http.post("/api/workspaces/publish", () => HttpResponse.json({
|
||||
code: "workspace_conflict",
|
||||
message: "Workspace changed in the registry.",
|
||||
fields: ["evidence.source.signed_url"],
|
||||
expected: { commit: "a".repeat(40), blob: "b".repeat(40) },
|
||||
actual: { commit: "c".repeat(40), blob: "d".repeat(40) },
|
||||
base: evidenceWorkspace,
|
||||
local: evidenceWorkspace,
|
||||
remote: evidenceWorkspace,
|
||||
}, { status: 409 })));
|
||||
|
||||
const error = await publishWorkspace({
|
||||
action: "update", workspace: evidenceWorkspace,
|
||||
baseCommit: "a".repeat(40), baseBlob: "b".repeat(40),
|
||||
}).catch((cause: unknown) => cause);
|
||||
|
||||
expect(asWorkspaceConflict(error)).toBeUndefined();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user