79 lines
4.8 KiB
TypeScript
79 lines
4.8 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { GiteaClient } from "../src/integrations/gitea/client.js";
|
|
import { assertSuccessfulActionRuns, exactHeadActionRuns } from "../src/integrations/gitea/policy.js";
|
|
import type { GiteaActionRun } from "../src/integrations/gitea/types.js";
|
|
|
|
const branch = "codex/native-ci";
|
|
const sha = "a".repeat(40);
|
|
const context = "CI / build-test (pull_request)";
|
|
const run = (id: number, overrides: Partial<GiteaActionRun> = {}): GiteaActionRun => ({
|
|
id, run_number: id, display_title: "Managed validation / full", path: ".gitea/workflows/managed-validation.yml",
|
|
event: "pull_request", head_sha: sha, head_branch: "", status: "completed",
|
|
conclusion: "success", html_url: `https://gitea.example/runs/${id}`, ...overrides,
|
|
});
|
|
|
|
describe("native PR validation discovery", () => {
|
|
it("discovers an empty branch by exact SHA and checks available PR identity", () => {
|
|
const matching = run(1, { pull_requests: [{ number: 30, head: { ref: branch, sha } }] });
|
|
expect(exactHeadActionRuns([
|
|
matching, run(2), run(3, { head_sha: "stale" }),
|
|
run(4, { head_branch: "other" }),
|
|
run(5, { pull_requests: [{ number: 31, head: { ref: "other", sha } }] }),
|
|
run(6, { event: "workflow_dispatch" }),
|
|
], branch, sha)).toEqual([matching, run(2)]);
|
|
expect(assertSuccessfulActionRuns([matching], branch, sha)).toEqual([matching]);
|
|
});
|
|
|
|
it.each(["pending", "queued", "waiting", "in_progress"])("does not authorize %s", (status) => {
|
|
expect(() => assertSuccessfulActionRuns([run(1, { status, conclusion: undefined })], branch, sha)).toThrow("FEATURE_DELIVERY_FULL_VALIDATION_REQUIRED");
|
|
});
|
|
|
|
it.each(["cancelled", "canceled", "failure"])("does not authorize a %s job that never started", (conclusion) => {
|
|
expect(() => assertSuccessfulActionRuns([run(1, { conclusion })], branch, sha)).toThrow("FEATURE_DELIVERY_FULL_VALIDATION_REQUIRED");
|
|
});
|
|
|
|
it("uses the newest attempt of each workflow and preserves all evidence", () => {
|
|
const failure = run(2, { conclusion: "failure" });
|
|
expect(() => assertSuccessfulActionRuns([failure, run(1, { status: "pending" })], branch, sha)).toThrow();
|
|
const success = run(3);
|
|
expect(assertSuccessfulActionRuns([failure, success], branch, sha)).toEqual([failure, success]);
|
|
expect(() => assertSuccessfulActionRuns([success, run(4, { status: "queued" })], branch, sha)).toThrow();
|
|
});
|
|
|
|
it("never lets dispatch success replace PR failure even for the same workflow path", () => {
|
|
expect(() => assertSuccessfulActionRuns([
|
|
run(1, { conclusion: "failure" }),
|
|
run(2, { event: "workflow_dispatch", head_branch: branch }),
|
|
], branch, sha)).toThrow("FEATURE_DELIVERY_CI_NOT_SUCCESSFUL");
|
|
});
|
|
|
|
it("queries by exact SHA without the upstream branch filter", async () => {
|
|
const fetcher = vi.fn(async () => new Response(JSON.stringify({ total_count: 1, workflow_runs: [run(1)] })));
|
|
await new GiteaClient("https://gitea.example", "test-only", fetcher as typeof fetch).listActionRuns("Jens", "Repo", { branch, headSha: sha });
|
|
const url = new URL(String(fetcher.mock.calls[0]![0]));
|
|
expect(url.searchParams.get("head_sha")).toBe(sha);
|
|
expect(url.searchParams.has("branch")).toBe(false);
|
|
});
|
|
|
|
it.each([
|
|
[[{ context, status: "failure" }, { context, status: "pending" }], "GITEA_REQUIRED_CHECKS_FAILED"],
|
|
[[{ context: "CI / build-test (workflow_dispatch)", status: "success" }], "GITEA_REQUIRED_CHECKS_PENDING"],
|
|
[[{ context: `${context} extra`, status: "success" }], "GITEA_REQUIRED_CHECKS_PENDING"],
|
|
[[{ context, status: "pending" }], "GITEA_REQUIRED_CHECKS_PENDING"],
|
|
] as const)("requires the exact latest protected status %#", async (statuses, error) => {
|
|
const fetcher = vi.fn(async (url: URL | RequestInfo) => new Response(JSON.stringify(String(url).includes("branch_protections")
|
|
? { enable_status_check: true, status_check_contexts: [context] } : { statuses })));
|
|
const client = new GiteaClient("https://gitea.example", "test-only", fetcher as typeof fetch);
|
|
await expect(client.waitForRequiredPullRequestChecks("Jens", "Repo", "main", sha, { timeoutMs: 0 })).rejects.toMatchObject({ code: error });
|
|
expect(String(fetcher.mock.calls[1]![0])).toContain(`/commits/${sha}/status`);
|
|
});
|
|
|
|
it("accepts a newer native success after an old native failure", async () => {
|
|
const fetcher = vi.fn(async (url: URL | RequestInfo) => new Response(JSON.stringify(String(url).includes("branch_protections")
|
|
? { enable_status_check: true, status_check_contexts: [context] }
|
|
: { statuses: [{ context, status: "success" }, { context, status: "failure" }] })));
|
|
await expect(new GiteaClient("https://gitea.example", "test-only", fetcher as typeof fetch)
|
|
.waitForRequiredPullRequestChecks("Jens", "Repo", "main", sha, { timeoutMs: 0 })).resolves.toMatchObject({ state: "success" });
|
|
});
|
|
});
|