249 lines
8.0 KiB
TypeScript
249 lines
8.0 KiB
TypeScript
declare const Deno: {
|
|
test(name: string, fn: () => void | Promise<void>): void;
|
|
readTextFile(path: URL): Promise<string>;
|
|
};
|
|
|
|
function assert(condition: unknown, message: string): asserts condition {
|
|
if (!condition) throw new Error(message);
|
|
}
|
|
|
|
Deno.test("Runtime Settings routes validate unknown JSON through the shared Runtime parser", async () => {
|
|
const [listLoader, detailLoader] = await Promise.all([
|
|
Deno.readTextFile(
|
|
new URL(
|
|
"../src/routes/w/[workspaceId]/settings/runtimes/+page.ts",
|
|
import.meta.url,
|
|
),
|
|
),
|
|
Deno.readTextFile(
|
|
new URL(
|
|
"../src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.ts",
|
|
import.meta.url,
|
|
),
|
|
),
|
|
]);
|
|
|
|
assert(
|
|
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",
|
|
);
|
|
for (const source of [listLoader, detailLoader]) {
|
|
assert(
|
|
!source.includes("loadJson<"),
|
|
"Runtime loaders must not cast response JSON to a handwritten DTO",
|
|
);
|
|
}
|
|
});
|
|
|
|
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(
|
|
"../src/routes/w/[workspaceId]/settings/runtimes/+page.svelte",
|
|
import.meta.url,
|
|
),
|
|
);
|
|
|
|
assert(
|
|
page.includes(
|
|
"/settings/runtimes/${encodeURIComponent(runtime.runtime_id)}",
|
|
),
|
|
"Runtime name should link to canonical detail",
|
|
);
|
|
assert(
|
|
page.includes("testRuntime(runtime)"),
|
|
"connection Test should remain available",
|
|
);
|
|
assert(page.includes("Add Runtime"), "Add Runtime should remain available");
|
|
assert(
|
|
page.includes("data.workspace.permissions.manage_runtimes"),
|
|
"Add Runtime should be hidden from non-owners",
|
|
);
|
|
assert(
|
|
!page.includes("deleteRuntime"),
|
|
"inline Runtime delete logic must be removed",
|
|
);
|
|
assert(
|
|
!page.includes(">Delete</button>"),
|
|
"inline Runtime delete control must be removed",
|
|
);
|
|
});
|
|
|
|
Deno.test("Runtime detail keeps trust controls owner-only and conflict-safe", async () => {
|
|
const page = await Deno.readTextFile(
|
|
new URL(
|
|
"../src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte",
|
|
import.meta.url,
|
|
),
|
|
);
|
|
|
|
const ownerGate = page.indexOf("data.workspace.permissions.manage_runtimes");
|
|
const reveal = page.indexOf("Reveal public key");
|
|
const verifiedKey = page.indexOf("{#if verifiedTrust}");
|
|
const mutation = page.indexOf('id="runtime-public-key-input"');
|
|
const metadataStart = page.indexOf("async function saveRuntimeMetadata");
|
|
const trustStart = page.indexOf("async function saveTrustKey");
|
|
const metadataMutation = page.slice(metadataStart, trustStart);
|
|
assert(ownerGate >= 0, "Runtime trust controls should use manage_runtimes");
|
|
assert(
|
|
verifiedKey >= 0 && verifiedKey < mutation &&
|
|
page.slice(verifiedKey, mutation).includes("{:else}"),
|
|
"verified Runtime public key must be read-only while unverified trust keeps key input",
|
|
);
|
|
assert(
|
|
metadataMutation.includes("updateRemoteRuntime(") &&
|
|
metadataMutation.includes("display_name: normalizedDisplayName") &&
|
|
metadataMutation.includes("endpoint: normalizedEndpoint"),
|
|
"mutable Runtime settings should use the metadata-only update request",
|
|
);
|
|
assert(
|
|
!metadataMutation.includes("publicKey") &&
|
|
!metadataMutation.includes("public_bundle") &&
|
|
!metadataMutation.includes("public_key"),
|
|
"verified Runtime metadata updates must not carry public key authority",
|
|
);
|
|
assert(
|
|
page.includes("Current fingerprint"),
|
|
"current fingerprint must be explicit",
|
|
);
|
|
assert(
|
|
page.includes("Replacement fingerprint"),
|
|
"replacement fingerprint must be previewed before confirmation",
|
|
);
|
|
assert(
|
|
page.includes("!runtime.management.built_in"),
|
|
"Runtime trust controls should be hidden for the built-in Runtime",
|
|
);
|
|
assert(
|
|
ownerGate < reveal && ownerGate < mutation,
|
|
"owner gate should wrap key controls",
|
|
);
|
|
|
|
for (
|
|
const token of [
|
|
"Create Workspace trust",
|
|
"Replace trusted key",
|
|
"Reactivate with this key",
|
|
"Revoke Workspace trust",
|
|
"Workspace trust only; this does not delete the Runtime process, Workers, or Workdirs.",
|
|
"await revokeRuntimeTrustKey(",
|
|
"await removeRemoteRuntime(",
|
|
"new RuntimeRemovalAttempt()",
|
|
"runtimeRemovalAttempt.operationId()",
|
|
"operation_id: operationId",
|
|
"expected_binding_revision: trust.revision",
|
|
"The Backend removes Workspace trust and this Runtime registration as one guarded operation.",
|
|
"deleteRuntimeConfirmation.trim() !== data.runtimeId",
|
|
"Delete Runtime registration",
|
|
"Remove Runtime registration",
|
|
"This does not stop the Runtime process",
|
|
"RuntimeTrustConflictError",
|
|
"RuntimeTrustRouteFence",
|
|
"routeFence.enter(data.runtimeId)",
|
|
"showPublicKey = false",
|
|
"revealedPublicKey = null",
|
|
"saveRuntimeMetadata",
|
|
"Runtime settings",
|
|
"Edit Runtime",
|
|
"This verified key is read-only",
|
|
"publicKey = ''",
|
|
"requestError = null",
|
|
"successMessage = null",
|
|
"isCurrentRoute(operation)",
|
|
"revealRuntimeTrustKey",
|
|
"await reloadAuthority()",
|
|
"busyAction !== null",
|
|
"Workdirs",
|
|
"Recent trust audit",
|
|
]
|
|
) {
|
|
assert(page.includes(token), `Runtime detail should include ${token}`);
|
|
}
|
|
for (
|
|
const token of [
|
|
"fingerprintConfirmation",
|
|
"revokeFingerprintConfirmation",
|
|
"Confirm current fingerprint",
|
|
"deleteRemoteRuntime",
|
|
"if (data.runtimeDetail.trust_key.status !== 'revoked')",
|
|
"Revoke trust and delete registration",
|
|
]
|
|
) {
|
|
assert(!page.includes(token), `Runtime detail must not require ${token}`);
|
|
}
|
|
});
|
|
|
|
Deno.test("Runtime detail uses flat sections instead of nested cards", async () => {
|
|
const [page, css] = await Promise.all([
|
|
Deno.readTextFile(
|
|
new URL(
|
|
"../src/routes/w/[workspaceId]/settings/runtimes/[runtimeId]/+page.svelte",
|
|
import.meta.url,
|
|
),
|
|
),
|
|
Deno.readTextFile(
|
|
new URL("../src/lib/workspace/styles/settings.css", import.meta.url),
|
|
),
|
|
]);
|
|
|
|
assert(
|
|
!page.includes('class="card"') && !page.includes("settings-card"),
|
|
"Runtime detail should not add card nesting",
|
|
);
|
|
assert(
|
|
css.includes(".runtime-detail-section") &&
|
|
css.includes("border-top: 1px solid var(--line)"),
|
|
"Runtime detail hierarchy should use flat section separators",
|
|
);
|
|
});
|