fix: restore remote Runtime management contracts

This commit is contained in:
2026-09-09 00:26:04 +09:00
parent a072562034
commit 18fd6a1f5e
23 changed files with 1144 additions and 529 deletions
+35 -2
View File
@@ -26,10 +26,12 @@ function assertThrows<T extends Error>(
import {
fetchProfileSettings,
fetchWorkspaceMetadata,
fetchWorkspaceSigningIdentity,
parseProfileSettingsResponse,
parseWorkspaceMetadataSettingsResponse,
parseWorkspaceSigningIdentityResponse,
ProfileApiError,
provisionWorkspaceSigningIdentity,
updateWorkspaceMetadata,
} from "../src/lib/workspace/settings/profile-api.ts";
@@ -127,8 +129,8 @@ Deno.test("workspace metadata requests use generated DTO shapes", async () => {
"workspace 1",
);
assertEquals(requests.map((request) => request.url), [
"/api/w/workspace%201/settings/workspace",
"/api/w/workspace%201/settings/workspace",
"/api/w/workspace%201/settings",
"/api/w/workspace%201/settings",
]);
assertEquals(requests[1].init?.method, "PUT");
assertEquals(
@@ -140,6 +142,37 @@ Deno.test("workspace metadata requests use generated DTO shapes", async () => {
}
});
Deno.test("Workspace signing identity requests use flat settings routes", async () => {
const originalFetch = globalThis.fetch;
const requests: Array<{ url: string; init?: RequestInit }> = [];
globalThis.fetch = (input: string | URL | Request, init?: RequestInit) => {
requests.push({ url: String(input), init });
return Promise.resolve(Response.json({
identity: {
workspace_id: "workspace 1",
key_id: "workspace-signing-key",
algorithm: "ed25519",
revision: 1,
state: "pending_provisioning",
created_at: "2026-01-01T00:00:00Z",
},
}));
};
try {
await fetchWorkspaceSigningIdentity("workspace 1");
await provisionWorkspaceSigningIdentity("workspace 1");
assertEquals(requests.map((request) => request.url), [
"/api/w/workspace%201/settings/signing-identity",
"/api/w/workspace%201/settings/signing-identity/provision",
]);
assertEquals(requests[0].init, undefined);
assertEquals(requests[1].init?.method, "POST");
} finally {
globalThis.fetch = originalFetch;
}
});
Deno.test("profile settings parser rejects missing, mistyped, stale, and invalid provenance fields", () => {
const missing = profileSettingsFixture();
delete missing.profiles;
@@ -27,6 +27,11 @@ Deno.test("Runtime Settings routes validate unknown JSON through the shared Runt
listLoader.includes("parseWorkspaceRuntimeList(value)"),
"Runtime list loader should validate unknown JSON",
);
assert(
listLoader.includes('"/settings/signing-identity"') &&
!listLoader.includes("/settings/workspace"),
"Runtime list loader should use the canonical Workspace signing identity route",
);
assert(
detailLoader.includes("parseWorkspaceRuntimeDetail(value)"),
"Runtime detail loader should validate unknown JSON",
@@ -129,6 +129,33 @@ Deno.test("Runtime list and detail parsers return generated Runtime DTO shapes",
);
});
Deno.test("Runtime list parser accepts the built-in Runtime's internal binding", () => {
const embedded = runtime();
embedded.runtime_id = "embedded";
embedded.label = "Embedded Runtime";
embedded.kind = "embedded";
embedded.management.built_in = true;
embedded.management.endpoint_configured = false;
const binding = embedded.management.binding as Partial<
typeof embedded.management.binding
>;
delete binding.workspace_key_id;
delete binding.workspace_key_generation;
delete binding.verification;
const list = parseWorkspaceRuntimeList({
workspace_id: "workspace-a",
limit: 200,
items: [embedded],
source: "workspace-control-plane",
diagnostics: [],
});
assert(
list.items[0]?.management.binding?.connection_state === "verified",
"built-in Runtime binding was not preserved",
);
});
Deno.test("Runtime management parser rejects Workspace identity bindings without key metadata", () => {
const payload = detail();
const binding = payload.runtime.management.binding as Partial<
+1 -1
View File
@@ -190,7 +190,7 @@ Deno.test("Workspace deletion DTOs fail closed and preserve durable operation st
Deno.test("Workspace settings exposes owner-gated typed destructive confirmation", async () => {
const source = await Deno.readTextFile(
new URL(
"../src/routes/w/[workspaceId]/settings/workspace/+page.svelte",
"../src/routes/w/[workspaceId]/settings/+page.svelte",
import.meta.url,
),
);