feat: complete Workspace Runtime management flow

This commit is contained in:
2026-09-09 09:38:26 +09:00
parent d0999326bd
commit 3df611636b
6 changed files with 312 additions and 119 deletions
@@ -44,6 +44,48 @@ Deno.test("Runtime Settings routes validate unknown JSON through the shared Runt
}
});
Deno.test("Runtime registration presents the complete multi-Workspace trust sequence", async () => {
const page = await Deno.readTextFile(
new URL(
"../src/routes/w/[workspaceId]/settings/runtimes/+page.svelte",
import.meta.url,
),
);
for (
const token of [
"1. Trust this Workspace on the Runtime",
"Provision Workspace identity",
"provisionWorkspaceSigningIdentity(data.workspaceId)",
"Existing Workspace trust entries are preserved.",
"--fs-root",
"--fs-runtime-dir",
"2. Verify the Runtime identity",
"yoi-runtime identity show --json",
"3. Register the connection",
"Register Runtime",
"Run Test to complete authenticated verification.",
]
) {
assert(
page.includes(token),
`Runtime registration should include ${token}`,
);
}
assert(
page.includes(
"data.signingIdentity?.identity.state === 'pending_provisioning'",
) &&
page.includes("Loading Workspace public identity…"),
"pending identity must have a dedicated provisioning state before loading fallback",
);
assert(
!page.includes("fingerprintConfirmation") &&
!page.includes("Confirm Runtime fingerprint"),
"Runtime registration must not require retyping a fingerprint",
);
});
Deno.test("Runtime list links to canonical detail and has no inline delete action", async () => {
const page = await Deno.readTextFile(
new URL(
@@ -111,22 +153,26 @@ Deno.test("Runtime detail keeps trust controls owner-only and conflict-safe", as
"Create Workspace trust",
"Replace trusted key",
"Reactivate with this key",
"Confirm current fingerprint",
"Revoke Workspace trust",
"Workspace trust only; this does not delete the Runtime process, Workers, or Workdirs.",
"await revokeRuntimeTrustKey(",
"deleteRemoteRuntime(data.workspaceId, operation.runtimeId)",
"Revoke trust and delete registration",
"trust.status !== 'revoked'",
"deleteRuntimeConfirmation.trim() !== data.runtimeId",
"Delete Runtime registration",
"Delete registration",
"This does not stop the Runtime process",
"RuntimeTrustConflictError",
"RuntimeTrustRouteFence",
"routeFence.enter(data.runtimeId)",
"showPublicKey = false",
"revealedPublicKey = null",
"publicKey = ''",
"fingerprintConfirmation = ''",
"revokeFingerprintConfirmation = ''",
"requestError = null",
"successMessage = null",
"isCurrentRoute(operation)",
"revealRuntimeTrustKey",
"revokeFingerprintConfirmation.trim() !== trust.fingerprint",
"await reloadAuthority()",
"busyAction !== null",
"Workdirs",
@@ -135,6 +181,15 @@ Deno.test("Runtime detail keeps trust controls owner-only and conflict-safe", as
) {
assert(page.includes(token), `Runtime detail should include ${token}`);
}
for (
const token of [
"fingerprintConfirmation",
"revokeFingerprintConfirmation",
"Confirm current fingerprint",
]
) {
assert(!page.includes(token), `Runtime detail must not require ${token}`);
}
});
Deno.test("Runtime detail uses flat sections instead of nested cards", async () => {
@@ -4,6 +4,7 @@ declare const Deno: {
import {
createRemoteRuntime,
deleteRemoteRuntime,
parseRuntimeTrustConflict,
parseRuntimeTrustKeyRevealResponse,
parseWorkspaceRuntimeDetail,
@@ -272,6 +273,24 @@ Deno.test("mismatched revoke fingerprint never sends a request", async () => {
assert(requests === 0, "mismatched fingerprint sent a revoke request");
});
Deno.test("Runtime registration delete uses the Workspace-scoped resource route", async () => {
let requestedUrl = "";
let requestedMethod = "";
const fetchImpl = ((input: string | URL | Request, init?: RequestInit) => {
requestedUrl = String(input);
requestedMethod = init?.method ?? "GET";
return Promise.resolve(new Response(null, { status: 204 }));
}) as typeof fetch;
await deleteRemoteRuntime("workspace a", "runtime/a", fetchImpl);
assert(
requestedUrl === "/api/w/workspace%20a/runtimes/runtime%2Fa",
`unexpected delete URL: ${requestedUrl}`,
);
assert(requestedMethod === "DELETE", "Runtime delete must use DELETE");
});
Deno.test("Runtime route fence rejects a delayed reveal from the prior Runtime", async () => {
const fence = new RuntimeTrustRouteFence();
fence.enter("runtime-a");