127 lines
4.4 KiB
TypeScript
127 lines
4.4 KiB
TypeScript
import { expect, spyOn, test } from "bun:test";
|
|
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { ci, apiKeyRequest, runCi } from "../../command/ci";
|
|
import { provideContext } from "../../lib/context";
|
|
import { writeSession } from "../../lib/session";
|
|
import { resolveTrustIdentity } from "../../lib/trust";
|
|
|
|
test("CI requester supplies an API key without session authentication", async () => {
|
|
const fetch = spyOn(globalThis, "fetch").mockResolvedValue(
|
|
new Response(JSON.stringify({ ok: true }), { status: 200 }),
|
|
);
|
|
try {
|
|
await apiKeyRequest("ci-secret")("/workspaces/shop/resources/plan", {
|
|
headers: { "x-kuber-trust-project": "shop" },
|
|
});
|
|
const [, init] = fetch.mock.calls[0]!;
|
|
const headers = new Headers(init?.headers);
|
|
expect(headers.get("authorization")).toBe("Bearer ci-secret");
|
|
expect(headers.get("x-kuber-trust-project")).toBe("shop");
|
|
} finally {
|
|
fetch.mockRestore();
|
|
}
|
|
});
|
|
|
|
test("CI command requires an API key before loading project context", async () => {
|
|
const previous = process.env.KUBER_API_KEY;
|
|
delete process.env.KUBER_API_KEY;
|
|
try {
|
|
await expect(
|
|
ci.run!({
|
|
args: { apiKey: undefined, build: false, trust: false },
|
|
} as never),
|
|
).rejects.toThrow("KUBER_API_KEY or --api-key is required");
|
|
} finally {
|
|
if (previous === undefined) delete process.env.KUBER_API_KEY;
|
|
else process.env.KUBER_API_KEY = previous;
|
|
}
|
|
});
|
|
|
|
test("CI trust grant and deployment pipeline use the API key requester", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "kuber-ci-"));
|
|
const previousCwd = process.cwd();
|
|
const previousConfigHome = process.env.XDG_CONFIG_HOME;
|
|
const previousRuntimeDirectory = process.env.XDG_RUNTIME_DIR;
|
|
const calls: Array<{
|
|
path: string;
|
|
method: string | undefined;
|
|
body: string | undefined;
|
|
headers: Headers;
|
|
}> = [];
|
|
const fetch = spyOn(globalThis, "fetch").mockImplementation((async (
|
|
input,
|
|
init,
|
|
) => {
|
|
const url = new URL(input.toString());
|
|
const path = url.pathname.replace("/api/v2", "");
|
|
calls.push({
|
|
path,
|
|
method: init?.method,
|
|
body: typeof init?.body === "string" ? init.body : undefined,
|
|
headers: new Headers(init?.headers),
|
|
});
|
|
if (path === "/workspaces/shop")
|
|
return new Response(
|
|
JSON.stringify({
|
|
metadata: { name: "shop", uid: "workspace", resourceVersion: "1" },
|
|
}),
|
|
);
|
|
if (path.endsWith("/plan"))
|
|
return new Response(JSON.stringify({ desired: [], stale: [] }));
|
|
if (path === "/snapshots/negotiate")
|
|
return new Response(
|
|
JSON.stringify({ workspace: "sha256:abc", missing: [], ready: true }),
|
|
);
|
|
return new Response(JSON.stringify({ resourcesAdopted: 0 }));
|
|
}) as typeof globalThis.fetch);
|
|
|
|
try {
|
|
process.env.XDG_CONFIG_HOME = join(root, "config");
|
|
process.env.XDG_RUNTIME_DIR = join(root, "runtime");
|
|
await writeSession(
|
|
{
|
|
token: "session-secret",
|
|
expiresAt: "2030-01-01T00:00:00Z",
|
|
user: { username: "ci", roles: [] },
|
|
},
|
|
true,
|
|
);
|
|
await writeFile(join(root, "compose.yml"), "services: {}\n");
|
|
await writeFile(
|
|
join(root, ".kuberrc.ts"),
|
|
'export default { project: "shop" };\n',
|
|
);
|
|
const git = Bun.spawn(["git", "init", "-q", root]);
|
|
expect(await git.exited).toBe(0);
|
|
const fingerprint = (await resolveTrustIdentity("shop", root)).fingerprint;
|
|
process.chdir(root);
|
|
await provideContext(() => runCi(false, true, "ci-key"));
|
|
|
|
expect(calls[0]?.path).toBe("/workspaces/shop/trust");
|
|
expect(calls[0]?.method).toBe("POST");
|
|
expect(JSON.parse(calls[0]?.body ?? "")).toEqual({ fingerprint });
|
|
expect(calls.some(({ path }) => path.endsWith("/resources/plan"))).toBe(
|
|
true,
|
|
);
|
|
expect(calls.some(({ path }) => path.endsWith("/resources/apply"))).toBe(
|
|
true,
|
|
);
|
|
expect(
|
|
calls.every(
|
|
({ headers }) => headers.get("authorization") === "Bearer ci-key",
|
|
),
|
|
).toBe(true);
|
|
} finally {
|
|
fetch.mockRestore();
|
|
process.chdir(previousCwd);
|
|
if (previousConfigHome === undefined) delete process.env.XDG_CONFIG_HOME;
|
|
else process.env.XDG_CONFIG_HOME = previousConfigHome;
|
|
if (previousRuntimeDirectory === undefined)
|
|
delete process.env.XDG_RUNTIME_DIR;
|
|
else process.env.XDG_RUNTIME_DIR = previousRuntimeDirectory;
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|