feat: support workspace-managed SSH repository access
This commit is contained in:
@@ -2,6 +2,7 @@ import {
|
||||
parseRepositoryAccessProjection,
|
||||
parseRepositorySshCredentials,
|
||||
parseRepositorySshHostTrusts,
|
||||
parseRepositorySshPublicKey,
|
||||
RepositoryAccessSchemaError,
|
||||
} from "../../src/lib/workspace/api/repository-access.ts";
|
||||
|
||||
@@ -59,6 +60,22 @@ const hostTrust = {
|
||||
|
||||
Deno.test("Repository Access parsers accept generated response contracts", () => {
|
||||
assertEquals(parseRepositorySshCredentials([credential]), [credential]);
|
||||
assertEquals(
|
||||
parseRepositorySshPublicKey({
|
||||
credential_id: "deploy-key",
|
||||
current_revision: 2,
|
||||
public_key_algorithm: "ssh-ed25519",
|
||||
public_key_fingerprint: "SHA256:credential",
|
||||
public_key: "ssh-ed25519 AAAA",
|
||||
}),
|
||||
{
|
||||
credential_id: "deploy-key",
|
||||
current_revision: 2,
|
||||
public_key_algorithm: "ssh-ed25519",
|
||||
public_key_fingerprint: "SHA256:credential",
|
||||
public_key: "ssh-ed25519 AAAA",
|
||||
},
|
||||
);
|
||||
assertEquals(parseRepositorySshHostTrusts([hostTrust]), [hostTrust]);
|
||||
assertEquals(
|
||||
parseRepositoryAccessProjection({
|
||||
|
||||
@@ -28,6 +28,7 @@ test("Repository Access Web code consumes workspace-api generated DTOs", () => {
|
||||
assert(
|
||||
loaderSource.includes("parseRepositorySshCredentials") &&
|
||||
loaderSource.includes("parseRepositorySshHostTrusts") &&
|
||||
loaderSource.includes("parseRepositorySshPublicKey") &&
|
||||
loaderSource.includes("parseRepositoryAccessProjection"),
|
||||
"loader should validate unknown JSON before exposing generated DTOs to Svelte",
|
||||
);
|
||||
@@ -66,6 +67,29 @@ test("Repository Access renders the shared access projection fields", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("Repository Access generates and copies selectable public keys", () => {
|
||||
for (
|
||||
const token of [
|
||||
"/credentials/generate",
|
||||
"/public-key",
|
||||
"Generate Repository SSH credential",
|
||||
"navigator.clipboard.writeText",
|
||||
"publicKeys[credential.credential_id]",
|
||||
"workspace-default",
|
||||
"always offered during SSH clone",
|
||||
]
|
||||
) {
|
||||
assert(
|
||||
source.includes(token),
|
||||
`missing generated public key flow ${token}`,
|
||||
);
|
||||
}
|
||||
assert(
|
||||
source.includes("binding.credential_id"),
|
||||
"Repository bindings should identify the selected credential",
|
||||
);
|
||||
});
|
||||
|
||||
test("Repository credential submissions clear write-only fields in finally blocks", () => {
|
||||
const createStart = source.indexOf("async function createCredential()");
|
||||
const rotateStart = source.indexOf("async function rotateCredential(");
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import { assert, assertEquals } from "jsr:@std/assert";
|
||||
import type { WorkspaceRuntimeResource } from "../src/lib/generated/workspace-api.ts";
|
||||
import { parseRepositorySshConnectionProbeResponse } from "../src/lib/workspace/api/workspace-model.ts";
|
||||
import { repositorySshProbeRuntimes } from "../src/lib/workspace/repositories/ssh-connection.ts";
|
||||
|
||||
const root = new URL("../", import.meta.url);
|
||||
const pageSource = await Deno.readTextFile(
|
||||
new URL(
|
||||
"./src/routes/w/[workspaceId]/repositories/[repositoryKey]/+page.svelte",
|
||||
root,
|
||||
),
|
||||
);
|
||||
const loaderSource = await Deno.readTextFile(
|
||||
new URL(
|
||||
"./src/routes/w/[workspaceId]/repositories/[repositoryKey]/+page.ts",
|
||||
root,
|
||||
),
|
||||
);
|
||||
|
||||
Deno.test("Repository SSH probe parser preserves the confirmation contract", () => {
|
||||
const response = {
|
||||
workspace_id: "workspace-a",
|
||||
repository_key: "main",
|
||||
runtime_id: "runtime-a",
|
||||
hostname: "example.test",
|
||||
port: 22,
|
||||
trust_state: "untrusted" as const,
|
||||
host_trust_id: "tofu-example.test-22",
|
||||
expected_host_trust_revision: null,
|
||||
candidates: [
|
||||
{
|
||||
algorithm: "ssh-ed25519",
|
||||
host_key: "ssh-ed25519 AAAA",
|
||||
fingerprint: "SHA256:host",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assertEquals(parseRepositorySshConnectionProbeResponse(response), response);
|
||||
});
|
||||
|
||||
Deno.test("Repository SSH probe offers configured remote Runtimes regardless of worker-style status", () => {
|
||||
const configured = {
|
||||
runtime_id: "arcadia",
|
||||
label: "Arcadia",
|
||||
kind: "remote_worker_runtime",
|
||||
status: "idle",
|
||||
diagnostics: [],
|
||||
management: {
|
||||
endpoint_configured: true,
|
||||
endpoint_display: "https://arcadia.example",
|
||||
binding: {
|
||||
state: "verified",
|
||||
},
|
||||
},
|
||||
} as unknown as WorkspaceRuntimeResource;
|
||||
const embedded = {
|
||||
...configured,
|
||||
runtime_id: "embedded-worker-runtime",
|
||||
kind: "embedded_worker_runtime",
|
||||
} as unknown as WorkspaceRuntimeResource;
|
||||
const revoked = {
|
||||
...configured,
|
||||
runtime_id: "revoked",
|
||||
management: {
|
||||
...configured.management,
|
||||
binding: { state: "revoked" },
|
||||
},
|
||||
} as unknown as WorkspaceRuntimeResource;
|
||||
const unbound = {
|
||||
...configured,
|
||||
runtime_id: "unbound",
|
||||
management: {
|
||||
...configured.management,
|
||||
binding: undefined,
|
||||
},
|
||||
} as unknown as WorkspaceRuntimeResource;
|
||||
|
||||
assertEquals(
|
||||
repositorySshProbeRuntimes([embedded, configured, revoked, unbound]).map((
|
||||
runtime,
|
||||
) => runtime.runtime_id),
|
||||
["arcadia"],
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("Repository SSH connection test requires an explicit host-key confirmation", () => {
|
||||
for (
|
||||
const token of [
|
||||
"Check SSH connection",
|
||||
"selectedRuntimeId",
|
||||
"candidate.fingerprint",
|
||||
"Confirm and trust selected host key",
|
||||
"expected_host_trust_revision",
|
||||
"requestConnectionTest('POST'",
|
||||
"requestConnectionTest('PUT'",
|
||||
]
|
||||
) {
|
||||
assert(pageSource.includes(token), `missing SSH connection flow ${token}`);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("Repository detail loads configured Workspace Runtimes for the connection test", () => {
|
||||
assert(loaderSource.includes('workspaceApiPath(workspaceId, "/runtimes")'));
|
||||
assert(loaderSource.includes("parseWorkspaceRuntimeList"));
|
||||
});
|
||||
Reference in New Issue
Block a user