153 lines
8.3 KiB
TypeScript
153 lines
8.3 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import type { GiteaConfig, GiteaMirrorRepository } from "./platform-infra-gitea-config";
|
|
import { renderMirrorBootstrap } from "./platform-infra-gitea-render";
|
|
import { pacBootstrapYamlMatchFailure, projectPacBootstrapResult, renderPacBootstrap, resolvePacBootstrapMirrorRepository } from "./platform-infra-pipelines-as-code-bootstrap";
|
|
import { runPlatformInfraPipelinesAsCodeCommand } from "./platform-infra-pipelines-as-code";
|
|
|
|
const mirror: GiteaMirrorRepository = {
|
|
key: "widgets-nc01",
|
|
targetId: "NC01",
|
|
upstream: { repository: "acme/widgets", cloneUrl: "https://github.com/acme/widgets.git", branch: "main", visibility: "public" },
|
|
gitea: { owner: "mirrors", name: "acme-widgets", mirrorMode: "controlled-push", publicRead: true, readUrl: "http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/acme-widgets.git" },
|
|
gitops: { branch: "main", flushDisposition: "not-a-gitops-branch" },
|
|
snapshot: { prefix: "refs/unidesk/snapshots/widgets-main-nc01" },
|
|
legacyGitMirror: null,
|
|
};
|
|
|
|
function fixtureConfig(repositories: GiteaMirrorRepository[]): GiteaConfig {
|
|
return { sourceAuthority: { repositories } } as unknown as GiteaConfig;
|
|
}
|
|
|
|
test("PaC bootstrap uses one exact YAML-owned Gitea repository", () => {
|
|
const selected = resolvePacBootstrapMirrorRepository(fixtureConfig([mirror]), "nc01", {
|
|
id: "widgets-nc01",
|
|
owner: "mirrors",
|
|
repo: "acme-widgets",
|
|
cloneUrl: `${mirror.gitea.readUrl}/`,
|
|
sourceBranch: "main",
|
|
});
|
|
expect(selected.key).toBe("widgets-nc01");
|
|
const releaseMirror = { ...mirror, key: "widgets-release-nc01", upstream: { ...mirror.upstream, branch: "release" } };
|
|
expect(resolvePacBootstrapMirrorRepository(fixtureConfig([mirror, releaseMirror]), "NC01", {
|
|
id: "widgets-release-nc01",
|
|
owner: "mirrors",
|
|
repo: "acme-widgets",
|
|
cloneUrl: mirror.gitea.readUrl,
|
|
sourceBranch: "release",
|
|
}).key).toBe("widgets-release-nc01");
|
|
expect(() => resolvePacBootstrapMirrorRepository(fixtureConfig([]), "NC01", {
|
|
id: "widgets-nc01",
|
|
owner: "mirrors",
|
|
repo: "acme-widgets",
|
|
cloneUrl: mirror.gitea.readUrl,
|
|
sourceBranch: "main",
|
|
})).toThrow("no exact match");
|
|
expect(() => resolvePacBootstrapMirrorRepository(fixtureConfig([mirror, { ...mirror }]), "NC01", {
|
|
id: "widgets-nc01",
|
|
owner: "mirrors",
|
|
repo: "acme-widgets",
|
|
cloneUrl: mirror.gitea.readUrl,
|
|
sourceBranch: "main",
|
|
})).toThrow("2 exact matches");
|
|
});
|
|
|
|
test("Gitea mirror bootstrap defaults to a visible dry-run instead of an empty refusal", () => {
|
|
const rendered = renderMirrorBootstrap({
|
|
ok: true,
|
|
action: "platform-infra-gitea-mirror-bootstrap",
|
|
mode: "dry-run",
|
|
mutation: false,
|
|
target: { id: "NC01" },
|
|
repositories: [{ key: "widgets-nc01", giteaOwner: "mirrors", giteaRepo: "acme-widgets" }],
|
|
blockers: [],
|
|
next: { confirm: "bun scripts/cli.ts platform-infra gitea mirror bootstrap --target NC01 --repo widgets-nc01 --confirm" },
|
|
}).renderedText;
|
|
expect(rendered).toContain("MODE MUTATION TARGET");
|
|
expect(rendered).toContain("widgets-nc01");
|
|
expect(rendered).toContain("--repo widgets-nc01 --confirm");
|
|
expect(rendered).not.toContain("missing-confirm");
|
|
});
|
|
|
|
test("PaC apply checks the Gitea repository before dry-run return and cluster mutation", () => {
|
|
const remote = readFileSync(resolve(import.meta.dir, "platform-infra-pipelines-as-code-remote.sh"), "utf8");
|
|
const action = remote.indexOf("apply_action() {");
|
|
const preflight = remote.indexOf("if ! repository_preflight", action);
|
|
const dryRun = remote.indexOf('if [ "$UNIDESK_PAC_DRY_RUN" = "1" ]', action);
|
|
const release = remote.indexOf(" apply_release", action);
|
|
expect(action).toBeGreaterThanOrEqual(0);
|
|
expect(preflight).toBeGreaterThan(action);
|
|
expect(dryRun).toBeGreaterThan(preflight);
|
|
expect(release).toBeGreaterThan(dryRun);
|
|
expect(remote).toContain('"error":"gitea-repository-missing"');
|
|
});
|
|
|
|
test("platform bootstrap help advertises the composite entry before low-level apply", async () => {
|
|
const help = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help", "platform-bootstrap"]);
|
|
const usage = (help as Record<string, unknown>).usage as string[];
|
|
expect(usage[0]).toContain("pipelines-as-code bootstrap");
|
|
expect(usage).toContain("bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target <NODE> --consumer <consumer> --confirm");
|
|
});
|
|
|
|
test("PaC bootstrap projects typed stages and compact JSON", () => {
|
|
const result = {
|
|
ok: true,
|
|
action: "platform-infra-pipelines-as-code-bootstrap",
|
|
mutation: false,
|
|
mode: "dry-run",
|
|
target: { id: "NC01" },
|
|
consumer: { id: "widgets", node: "NC01", lane: "v01" },
|
|
repository: { id: "widgets", namespace: "ci" },
|
|
mirrorRepository: { key: mirror.key, upstreamRepository: mirror.upstream.repository, upstreamBranch: mirror.upstream.branch, owner: mirror.gitea.owner, repo: mirror.gitea.name },
|
|
githubPreflight: { ok: true, mutation: false, repositories: [{ key: mirror.key, code: "github-upstream-available", ok: true }] },
|
|
giteaBootstrap: { ok: true, mutation: false, mode: "dry-run", blockers: [] },
|
|
pipelinesAsCodeApply: { ok: true, mutation: false, mode: "dry-run", remote: { ok: true, preflight: { repositoryExists: true } } },
|
|
};
|
|
const projection = projectPacBootstrapResult(result);
|
|
expect(projection.ok).toBe(true);
|
|
expect(JSON.stringify(projection)).not.toContain("pipelinesAsCodeApply");
|
|
expect((projection.stages as Record<string, unknown>[]).map((item) => item.id)).toEqual(["gitea-init", "pac-controller", "tekton-consumer", "gitops-argo"]);
|
|
const rendered = renderPacBootstrap(result).renderedText;
|
|
expect(rendered).toContain("PRECONDITIONS");
|
|
expect(rendered).toContain("yaml-repository-exact-match");
|
|
expect(rendered).toContain("confirm:");
|
|
});
|
|
|
|
test("PaC bootstrap returns fix-yaml next for zero YAML matches", () => {
|
|
const result = pacBootstrapYamlMatchFailure("NC01", "widgets", new Error("cannot resolve repository: no exact match"));
|
|
expect(projectPacBootstrapResult(result)).toBe(result);
|
|
expect((result.blockers as Record<string, unknown>[])[0]?.code).toBe("yaml-repository-no-match");
|
|
expect(result.next).toEqual({
|
|
kind: "fix-yaml",
|
|
command: "检查 config/platform-infra/gitea.yaml 与 config/platform-infra/pipelines-as-code.yaml 的 target、owner/name 和 read URL 精确匹配",
|
|
});
|
|
});
|
|
|
|
test("PaC bootstrap returns fix-yaml next for multiple YAML matches", () => {
|
|
const result = pacBootstrapYamlMatchFailure("NC01", "widgets", new Error("cannot resolve repository: 2 exact matches"));
|
|
expect(result.mutation).toBe(false);
|
|
expect((result.blockers as Record<string, unknown>[])[0]?.code).toBe("yaml-repository-multiple-matches");
|
|
expect((result.next as Record<string, unknown>).kind).toBe("fix-yaml");
|
|
});
|
|
|
|
test("PaC bootstrap distinguishes GitHub access failure from Gitea and PaC stages", () => {
|
|
const projection = projectPacBootstrapResult({
|
|
ok: false,
|
|
action: "platform-infra-pipelines-as-code-bootstrap",
|
|
mutation: false,
|
|
mode: "dry-run",
|
|
target: { id: "NC01" },
|
|
consumer: { id: "widgets", node: "NC01", lane: "v01" },
|
|
repository: { id: "widgets", namespace: "ci" },
|
|
mirrorRepository: { key: mirror.key, upstreamRepository: mirror.upstream.repository, upstreamBranch: mirror.upstream.branch, owner: mirror.gitea.owner, repo: mirror.gitea.name },
|
|
githubPreflight: { ok: false, mutation: false, repositories: [{ key: mirror.key, code: "github-repository-not-found-or-no-access", errorTail: "repository unavailable" }] },
|
|
giteaBootstrap: { ok: false, mutation: false, mode: "skipped", githubPreflight: { ok: false, repositories: [{ code: "github-repository-not-found-or-no-access", errorTail: "repository unavailable" }] } },
|
|
pipelinesAsCodeApply: { ok: false, mutation: false, mode: "skipped" },
|
|
});
|
|
expect((projection.blockers as Record<string, unknown>[])).toEqual([{ code: "github-repository-not-found-or-no-access", stage: "github-upstream", reason: "repository unavailable" }]);
|
|
expect((projection.preconditions as Record<string, unknown>[])[2]).toMatchObject({ ok: null, code: "gitea-bootstrap-skipped" });
|
|
expect((projection.stages as Record<string, unknown>[]).map((item) => item.state)).toEqual(["skipped", "skipped", "skipped", "skipped"]);
|
|
expect((projection.next as Record<string, unknown>).kind).toBe("read-only-status");
|
|
});
|