test: cover repository access loader failures
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"dev": "deno run -A npm:vite@7.2.7 dev",
|
||||
"dev:backend": "cd ../.. && cargo run -p yoi-workspace-server --bin yoi-server -- serve --listen 127.0.0.1:8787",
|
||||
"check": "deno run -A npm:@sveltejs/kit@2.49.4 sync && deno run -A npm:svelte-check@4.3.4 --tsconfig ./tsconfig.json",
|
||||
"test": "deno test --allow-read=src,test --allow-env=LOG,VSCODE_TEXTMATE_DEBUG src/lib/workspace/auth/model.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts",
|
||||
"test": "deno test --allow-read=src,test --allow-env=LOG,VSCODE_TEXTMATE_DEBUG,NODE_ENV src/lib/workspace/auth/model.test.ts src/lib/workspace/api/http.test.ts src/lib/workspace/header/breadcrumb-model.test.ts src/lib/workspace/console/chat-submit.test.ts src/lib/workspace/console/composer-command.test.ts src/lib/workspace/console/composer-completion.test.ts src/lib/workspace/console/markdown.test.ts test/console/ansi.test.ts src/lib/workspace/console/model.test.ts src/lib/workspace/console/tasks.test.ts test/ticket-detail-route-reuse.test.ts src/lib/workspace/console/worker-console.ui.test.ts src/lib/workspace/settings/model.test.ts src/lib/workspace/sidebar/override-stack.test.ts src/lib/workspace/sidebar/workers.test.ts src/lib/workspace/sidebar/workspace-switcher.test.ts src/lib/workspace/sidebar/worker-subscription.test.ts src/lib/workspace/sidebar/worker-launch.test.ts src/lib/workspace/tickets/merge-request-resources.test.ts src/lib/workspace/tickets/ticket-panel.test.ts test/merge-request-status.test.ts test/config-source/decodal-grammar.test.ts test/config-source/editor-state.test.ts test/config-source/fixed-schema-wrapper.test.ts test/config-source/toolchain.test.ts test/config-source/wasm-parity.test.ts test/repository-access/api.test.ts test/repository-access/loader.test.ts test/repository-access/ui.test.ts",
|
||||
"build": "deno run -A npm:vite@7.2.7 build",
|
||||
"preview": "deno run -A npm:vite@7.2.7 preview"
|
||||
},
|
||||
|
||||
@@ -1,33 +1,114 @@
|
||||
import { loadRepositoryAccessJson } from "../../src/lib/workspace/api/repository-access-loader.ts";
|
||||
import { RepositoryAccessSchemaError } from "../../src/lib/workspace/api/repository-access.ts";
|
||||
|
||||
Deno.test("Repository Access loader maps missing permission to a bounded unavailable error", async () => {
|
||||
let requests = 0;
|
||||
type HttpFailure = { status?: number; body?: { message?: string } };
|
||||
|
||||
async function captureHttpFailure(
|
||||
run: () => Promise<unknown>,
|
||||
expectedStatus: number,
|
||||
expectedMessage: string,
|
||||
): Promise<HttpFailure> {
|
||||
try {
|
||||
await loadRepositoryAccessJson(
|
||||
await run();
|
||||
} catch (error) {
|
||||
const failure = error as HttpFailure;
|
||||
if (failure.status !== expectedStatus) {
|
||||
throw new Error(
|
||||
`expected bounded ${expectedStatus}, got ${String(failure.status)}`,
|
||||
);
|
||||
}
|
||||
if (failure.body?.message !== expectedMessage) {
|
||||
throw new Error(
|
||||
`unexpected bounded error: ${JSON.stringify(failure.body)}`,
|
||||
);
|
||||
}
|
||||
return failure;
|
||||
}
|
||||
throw new Error(`expected bounded ${expectedStatus} error`);
|
||||
}
|
||||
|
||||
for (const status of [401, 403]) {
|
||||
Deno.test(`Repository Access loader maps ${status} to bounded permission unavailable`, async () => {
|
||||
let requests = 0;
|
||||
await captureHttpFailure(
|
||||
() =>
|
||||
loadRepositoryAccessJson(
|
||||
() => {
|
||||
requests += 1;
|
||||
return Promise.resolve(new Response(null, { status: 403 }));
|
||||
return Promise.resolve(new Response(null, { status }));
|
||||
},
|
||||
"/api/w/workspace-1/settings/repository-access/credentials",
|
||||
"/api/w/workspace-1/settings/repository-access",
|
||||
(value) => value,
|
||||
),
|
||||
403,
|
||||
"Repository Access is unavailable for this account.",
|
||||
);
|
||||
} catch (error) {
|
||||
const failure = error as { status?: number; body?: { message?: string } };
|
||||
if (failure.status !== 403) {
|
||||
throw new Error(`expected bounded 403, got ${String(failure.status)}`);
|
||||
}
|
||||
if (
|
||||
failure.body?.message !==
|
||||
"Repository Access is unavailable for this account."
|
||||
) {
|
||||
throw new Error(
|
||||
`unexpected permission error: ${JSON.stringify(failure.body)}`,
|
||||
);
|
||||
}
|
||||
if (requests !== 1) {
|
||||
throw new Error(`expected one bounded request, got ${requests}`);
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
Deno.test("Repository Access loader maps invalid JSON to safe bounded 502", async () => {
|
||||
const upstreamSecret = "private-key-must-not-leak";
|
||||
const failure = await captureHttpFailure(
|
||||
() =>
|
||||
loadRepositoryAccessJson(
|
||||
() =>
|
||||
Promise.resolve(
|
||||
new Response(upstreamSecret, {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
),
|
||||
"/api/w/workspace-1/settings/repository-access/credentials",
|
||||
(value) => value,
|
||||
),
|
||||
502,
|
||||
"Repository Access returned an invalid JSON response.",
|
||||
);
|
||||
if (JSON.stringify(failure.body).includes(upstreamSecret)) {
|
||||
throw new Error("invalid JSON error exposed upstream response content");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("Repository Access loader maps schema mismatch to explicit bounded 502", async () => {
|
||||
const failure = await captureHttpFailure(
|
||||
() =>
|
||||
loadRepositoryAccessJson(
|
||||
() => Promise.resolve(Response.json({ stale: true })),
|
||||
"/api/w/workspace-1/settings/repository-access/credentials",
|
||||
() => {
|
||||
throw new RepositoryAccessSchemaError("credentials", "an array");
|
||||
},
|
||||
),
|
||||
502,
|
||||
"Repository Access response schema mismatch at credentials: expected an array",
|
||||
);
|
||||
if (!failure.body?.message?.includes("credentials")) {
|
||||
throw new Error("schema mismatch error omitted the failing response path");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("Repository Access loader never exposes failed upstream response bodies", async () => {
|
||||
const upstreamSecret = "secret-ref-must-not-leak";
|
||||
const failure = await captureHttpFailure(
|
||||
() =>
|
||||
loadRepositoryAccessJson(
|
||||
() =>
|
||||
Promise.resolve(
|
||||
Response.json(
|
||||
{ message: upstreamSecret, secret_ref: upstreamSecret },
|
||||
{ status: 500 },
|
||||
),
|
||||
),
|
||||
"/api/w/workspace-1/settings/repository-access/host-trusts",
|
||||
(value) => value,
|
||||
),
|
||||
502,
|
||||
"Repository Access request failed with status 500.",
|
||||
);
|
||||
if (JSON.stringify(failure.body).includes(upstreamSecret)) {
|
||||
throw new Error("bounded upstream error exposed response content");
|
||||
}
|
||||
throw new Error("expected Repository Access permission error");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user