430 lines
25 KiB
TypeScript
430 lines
25 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { readFileSync } from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
import { resolve } from "node:path";
|
|
|
|
import { resolveAgentRunLaneTarget } from "./agentrun-lanes";
|
|
import { renderAgentRunPipelineManifest } from "./agentrun-manifests";
|
|
import { renderPlatformInfraGiteaDesiredFragments } from "./platform-infra-gitea-desired-fragments";
|
|
import { pacConsumerRbacDesiredIdentity, renderPacConsumerRbacDesiredFragment } from "./platform-infra-pac-consumer-rbac";
|
|
import { parsePacAdmissionContract, readPacAdmissionContract, renderPacAdmissionDesiredFragment } from "./platform-infra-pac-provenance";
|
|
import { pacDebugFixtureDisclosure } from "./platform-infra-pipelines-as-code";
|
|
|
|
const root = resolve(import.meta.dir, "../..");
|
|
const evaluator = createRequire(import.meta.url)("../native/cicd/pac-status-evaluator.cjs") as {
|
|
evaluatePacAdmissionState: (input: Record<string, unknown>) => Record<string, any>;
|
|
evaluatePacStatus: (input: Record<string, unknown>) => Record<string, any>;
|
|
extractPacArtifactEvidence: (records: unknown[], logText: string) => Record<string, any>;
|
|
normalizeGitopsRevisionRelation: (input: Record<string, unknown>) => Record<string, any>;
|
|
taskTerminalRecord: (taskRun: Record<string, unknown>) => Record<string, any> | null;
|
|
runPacStatusFixtureChecks: () => { ok: boolean; checks: Array<{ id: string; ok: boolean }> };
|
|
};
|
|
|
|
function documents(value: string): any[] {
|
|
return value.split(/^---\s*$/mu).map((item) => item.trim()).filter(Boolean).map((item) => Bun.YAML.parse(item));
|
|
}
|
|
|
|
test("admission contract rejects malformed required declarations instead of silently downgrading", () => {
|
|
const source = Bun.YAML.parse(readFileSync(resolve(root, "config/platform-infra/pipelines-as-code.yaml"), "utf8")) as any;
|
|
const malformedRequired = structuredClone(source);
|
|
malformedRequired.consumers.find((item: any) => item.deliveryProvenance !== undefined).deliveryProvenance.required = "true";
|
|
expect(() => parsePacAdmissionContract(malformedRequired)).toThrow("deliveryProvenance.required must be boolean true or false");
|
|
|
|
const sharedDefault = structuredClone(source);
|
|
sharedDefault.consumers.find((item: any) => item.deliveryProvenance !== undefined).deliveryProvenance.executionServiceAccountName = "default";
|
|
expect(() => parsePacAdmissionContract(sharedDefault)).toThrow("must not reserve the shared default ServiceAccount");
|
|
|
|
const duplicatedServiceAccount = structuredClone(source);
|
|
const duplicatedRequiredConsumers = duplicatedServiceAccount.consumers.filter((item: any) => item.deliveryProvenance !== undefined);
|
|
duplicatedRequiredConsumers[1].deliveryProvenance.executionServiceAccountName = duplicatedRequiredConsumers[0].deliveryProvenance.executionServiceAccountName;
|
|
expect(() => parsePacAdmissionContract(duplicatedServiceAccount)).toThrow("executionServiceAccountName must be unique per target");
|
|
|
|
const denyingAdmission = structuredClone(source);
|
|
denyingAdmission.deliveryProvenance.admission.failurePolicy = "Fail";
|
|
denyingAdmission.deliveryProvenance.admission.validationActions = ["Deny"];
|
|
expect(() => parsePacAdmissionContract(denyingAdmission)).toThrow("validationActions must be exactly [Warn, Audit]");
|
|
});
|
|
|
|
test("admission queue transition contract rejects absent sources, unknown Tekton states, and duplicate transitions", () => {
|
|
const source = Bun.YAML.parse(readFileSync(resolve(root, "config/platform-infra/pipelines-as-code.yaml"), "utf8")) as any;
|
|
const absentSource = structuredClone(source);
|
|
absentSource.deliveryProvenance.queueTransition.allowed[0].fromSpecStatus = "absent";
|
|
expect(() => parsePacAdmissionContract(absentSource)).toThrow("fromSpecStatus must be a known Tekton v1 PipelineRun spec status");
|
|
|
|
const unknownTarget = structuredClone(source);
|
|
unknownTarget.deliveryProvenance.queueTransition.allowed[0].toSpecStatus = "UnknownStatus";
|
|
expect(() => parsePacAdmissionContract(unknownTarget)).toThrow("toSpecStatus must be a known Tekton v1 PipelineRun spec status or absent");
|
|
|
|
const duplicate = structuredClone(source);
|
|
duplicate.deliveryProvenance.queueTransition.allowed.push(structuredClone(duplicate.deliveryProvenance.queueTransition.allowed[0]));
|
|
expect(() => parsePacAdmissionContract(duplicate)).toThrow("deliveryProvenance.queueTransition.allowed must be unique");
|
|
});
|
|
|
|
test("versioned admission desired state reports controller proof and candidate drift without denying delivery", () => {
|
|
const contract = readPacAdmissionContract();
|
|
const owningYaml = Bun.YAML.parse(readFileSync(resolve(root, "config/platform-infra/pipelines-as-code.yaml"), "utf8")) as any;
|
|
expect(owningYaml.deliveryProvenance.terminalRoles).toBeUndefined();
|
|
expect(contract.policyName).toEndWith("-v2");
|
|
expect(contract.bindingName).toEndWith("-v2");
|
|
expect(contract.queueControllerUsername).toBe("system:serviceaccount:pipelines-as-code:pipelines-as-code-watcher");
|
|
expect(contract.allowedQueueTransitions).toEqual([{
|
|
fromSpecStatus: "PipelineRunPending",
|
|
toSpecStatus: "absent",
|
|
fromState: "queued",
|
|
toState: "started",
|
|
}]);
|
|
expect(contract.child.enabled).toBe(false);
|
|
expect(contract.consumers.map((item) => item.id)).toEqual(["agentrun-nc01-v02", "platform-infra-sub2rank-nc01", "selfmedia-nc01", "selfmedia-production-nc01", "pikaoa-test-nc01"]);
|
|
const items = documents(renderPacAdmissionDesiredFragment("NC01"));
|
|
expect(items.map((item) => item.kind)).toEqual(["ValidatingAdmissionPolicy", "ValidatingAdmissionPolicyBinding"]);
|
|
const policy = items[0];
|
|
const expressions = policy.spec.validations.map((item: any) => item.expression).join("\n");
|
|
const messages = policy.spec.validations.map((item: any) => item.message).join("\n");
|
|
expect(policy.spec.failurePolicy).toBe("Ignore");
|
|
expect(policy.spec.matchConstraints.matchPolicy).toBe("Equivalent");
|
|
expect(items[1].spec.validationActions).toEqual(["Warn", "Audit"]);
|
|
expect(policy.metadata.annotations["argocd.argoproj.io/sync-wave"]).toBe("0");
|
|
expect(items[1].metadata.annotations["argocd.argoproj.io/sync-wave"]).toBe("1");
|
|
expect(expressions).toContain("request.userInfo.username");
|
|
expect(expressions).toContain("spec.pipelineRef.name");
|
|
expect(expressions).toContain("spec.params == oldObject.spec.params");
|
|
expect(expressions).toContain("object.spec.pipelineRef == oldObject.spec.pipelineRef");
|
|
expect(expressions).not.toContain("object.spec == oldObject.spec");
|
|
expect(expressions).toContain("taskRunTemplate.serviceAccountName == oldObject.spec.taskRunTemplate.serviceAccountName");
|
|
expect(expressions).toContain("object.metadata.name.startsWith");
|
|
expect(expressions).toContain('object.metadata.labels["tekton.dev/pipeline"]');
|
|
expect(expressions).toContain("agentrun-nc01-v02-tekton-runner");
|
|
expect(expressions).toContain("sub2rank-nc01-tekton-runner");
|
|
expect(expressions).not.toContain('serviceAccountName == "default"');
|
|
expect(expressions).toContain("selfmedia-nc01-tekton-runner");
|
|
expect(messages).toContain("execution ServiceAccount");
|
|
expect(expressions).toContain(contract.child.parentUidAnnotation);
|
|
expect(expressions).toContain("oldObject");
|
|
expect(JSON.stringify(items)).not.toContain('"kind":"ServiceAccount"');
|
|
const specGuard = policy.spec.validations.find((item: any) => item.message.includes("PipelineRun spec fields are immutable"));
|
|
const queueGuard = policy.spec.validations.find((item: any) => item.message.includes("YAML-declared queue transition"));
|
|
const oldRun = { spec: { pipelineSpec: { tasks: [{ name: "plan-artifacts" }] }, workspaces: [], timeouts: { pipeline: "1h" } } };
|
|
const mutatedRun = structuredClone(oldRun);
|
|
mutatedRun.spec.pipelineSpec.tasks[0].name = "forged-task";
|
|
for (const field of ["managedBy", "params", "pipelineRef", "taskRunSpecs", "taskRunTemplate", "timeouts", "workspaces"]) {
|
|
expect(specGuard.expression).toContain(`has(object.spec.${field})`);
|
|
expect(specGuard.expression).toContain(`object.spec.${field} == oldObject.spec.${field}`);
|
|
}
|
|
expect(specGuard.expression).toContain("has(dyn(object.spec).pipelineSpec)");
|
|
expect(specGuard.expression).toContain("has(dyn(oldObject.spec).pipelineSpec)");
|
|
expect(specGuard.expression).toContain("dyn(object.spec).pipelineSpec == dyn(oldObject.spec).pipelineSpec");
|
|
expect(specGuard.expression).not.toContain("has(object.spec.pipelineSpec)");
|
|
expect(specGuard.expression).not.toContain("object.spec.pipelineSpec == oldObject.spec.pipelineSpec");
|
|
expect(specGuard.expression).not.toContain("pipelinesascode.tekton.dev/state");
|
|
expect(mutatedRun.spec).not.toEqual(oldRun.spec);
|
|
expect(queueGuard.expression).toContain('request.userInfo.username == "system:serviceaccount:pipelines-as-code:pipelines-as-code-watcher"');
|
|
expect(queueGuard.expression).toContain('oldObject.spec.status == "PipelineRunPending"');
|
|
expect(queueGuard.expression).toContain("!has(object.spec.status)");
|
|
expect(queueGuard.expression).toContain('oldObject.metadata.labels["pipelinesascode.tekton.dev/state"] == "queued"');
|
|
expect(queueGuard.expression).toContain('object.metadata.labels["pipelinesascode.tekton.dev/state"] == "started"');
|
|
expect(queueGuard.expression).toContain('oldObject.metadata.annotations["unidesk.ai/pac-admission-provenance"] == "admission-pac-v2:agentrun-nc01-v02"');
|
|
expect(queueGuard.expression).toContain('object.metadata.annotations["unidesk.ai/pac-admission-provenance"] == "admission-pac-v2:agentrun-nc01-v02"');
|
|
expect(queueGuard.expression).toContain("oldObject.metadata.name.startsWith");
|
|
expect(queueGuard.expression).toContain("object.metadata.name.startsWith");
|
|
expect(queueGuard.expression).toContain("oldObject.spec.taskRunTemplate.serviceAccountName");
|
|
expect(queueGuard.expression).toContain("object.spec.taskRunTemplate.serviceAccountName");
|
|
expect(queueGuard.expression.match(/admission-pac-v2:agentrun-nc01-v02/gu)).toHaveLength(2);
|
|
expect(queueGuard.expression.match(/pipelines-as-code-watcher/gu)).toHaveLength(1);
|
|
expect(queueGuard.expression.match(/agentrun-nc01-v02-tekton-runner/gu)).toHaveLength(2);
|
|
expect(queueGuard.expression.match(/sub2rank-nc01-tekton-runner/gu)).toHaveLength(2);
|
|
expect(queueGuard.expression.match(/selfmedia-nc01-tekton-runner/gu)).toHaveLength(2);
|
|
expect(queueGuard.expression).not.toContain("admission-pac-v1:");
|
|
expect(queueGuard.expression).not.toContain('== "Cancelled"');
|
|
});
|
|
|
|
test("required consumer default RBAC has one automatic desired owner per namespace and no Tekton mutation verbs", () => {
|
|
const rbac = documents(renderPacConsumerRbacDesiredFragment("NC01"));
|
|
expect(rbac.map((item) => item.kind)).toEqual(["Role", "RoleBinding", "Role", "RoleBinding", "Role", "RoleBinding", "Role", "RoleBinding"]);
|
|
expect(rbac.filter((item) => item.kind === "Role").map((item) => item.metadata.namespace)).toEqual(["agentrun-ci", "devops-infra", "selfmedia-ci", "pikaoa-ci"]);
|
|
for (const role of rbac.filter((item) => item.kind === "Role")) {
|
|
expect(role.metadata.annotations["argocd.argoproj.io/sync-wave"]).toBe("-1");
|
|
expect(role.metadata.annotations["unidesk.ai/effective-config-sha256"]).toBe(pacConsumerRbacDesiredIdentity("NC01").configSha256);
|
|
const verbs = role.rules.flatMap((rule: any) => rule.verbs);
|
|
for (const verb of ["create", "patch", "update", "delete", "deletecollection"]) expect(verbs).not.toContain(verb);
|
|
}
|
|
const desiredKinds = documents(renderPlatformInfraGiteaDesiredFragments("NC01")).map((item) => item.kind);
|
|
expect(desiredKinds).toEqual([
|
|
"Role",
|
|
"RoleBinding",
|
|
"Role",
|
|
"RoleBinding",
|
|
"Role",
|
|
"RoleBinding",
|
|
"Role",
|
|
"RoleBinding",
|
|
"ValidatingAdmissionPolicy",
|
|
"ValidatingAdmissionPolicyBinding",
|
|
"ServiceAccount",
|
|
"ConfigMap",
|
|
"Deployment",
|
|
]);
|
|
});
|
|
|
|
test("live admission evaluator reports exact desired drift as non-blocking warnings", () => {
|
|
const contract = readPacAdmissionContract();
|
|
const admission = documents(renderPacAdmissionDesiredFragment("NC01"));
|
|
const rbac = documents(renderPacConsumerRbacDesiredFragment("NC01"));
|
|
const policy = { ...admission[0], metadata: { ...admission[0].metadata, uid: "policy-uid", generation: 2, creationTimestamp: "2026-01-01T00:00:00Z" }, status: { observedGeneration: 2, typeChecking: { expressionWarnings: [] } } };
|
|
const binding = { ...admission[1], metadata: { ...admission[1].metadata, uid: "binding-uid", creationTimestamp: "2026-01-01T00:00:01Z" } };
|
|
const expected = {
|
|
targetId: "NC01",
|
|
policyName: contract.policyName,
|
|
bindingName: contract.bindingName,
|
|
version: contract.version,
|
|
controllerIdentity: contract.controllerUsername,
|
|
configSha256: policy.metadata.annotations["unidesk.ai/effective-config-sha256"],
|
|
rbacConfigSha256: rbac[0].metadata.annotations["unidesk.ai/effective-config-sha256"],
|
|
};
|
|
const input = { policy, binding, role: rbac[0], roleBinding: rbac[1], namespace: "agentrun-ci", defaultCanMutate: false, expected };
|
|
expect(evaluator.evaluatePacAdmissionState(input)).toMatchObject({ ready: true, blocking: false, warning: false, activeAfter: "2026-01-01T00:00:01Z", defaultCanMutate: false });
|
|
expect(evaluator.evaluatePacAdmissionState({
|
|
...input,
|
|
policy: {
|
|
...policy,
|
|
status: {
|
|
...policy.status,
|
|
typeChecking: {
|
|
expressionWarnings: [{
|
|
fieldRef: "spec.validations[7].expression",
|
|
warning: "undefined field 'pipelineSpec'",
|
|
}],
|
|
},
|
|
},
|
|
},
|
|
})).toMatchObject({ ready: false, blocking: false, warning: true, reasons: expect.arrayContaining(["policy-expression-warning"]) });
|
|
const apiDefaultedPolicy = {
|
|
...policy,
|
|
spec: {
|
|
...policy.spec,
|
|
matchConstraints: {
|
|
...policy.spec.matchConstraints,
|
|
namespaceSelector: {},
|
|
objectSelector: {},
|
|
resourceRules: policy.spec.matchConstraints.resourceRules.map((rule: any) => ({ ...rule, scope: "*" })),
|
|
},
|
|
},
|
|
};
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, policy: apiDefaultedPolicy })).toMatchObject({
|
|
ready: true,
|
|
liveSpecSha256: expected.configSha256,
|
|
});
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, defaultCanMutate: true })).toMatchObject({ ready: false, blocking: false, warning: true, reasons: expect.arrayContaining(["default-serviceaccount-can-mutate-tekton-runs"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, defaultCanMutate: null })).toMatchObject({ ready: false, blocking: false, warning: true, defaultCanMutate: null, reasons: expect.arrayContaining(["default-serviceaccount-mutation-probe-unavailable"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, role: { ...rbac[0], rules: [rbac[0].rules[0]] } })).toMatchObject({ ready: false, blocking: false, warning: true, reasons: expect.arrayContaining(["default-role-rules-mismatch"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, expected: { ...expected, configSha256: `sha256:${"0".repeat(64)}` } })).toMatchObject({ ready: false, blocking: false, warning: true, reasons: expect.arrayContaining(["policy-config-sha256-mismatch"]) });
|
|
const mutatedPolicy = {
|
|
...policy,
|
|
spec: {
|
|
...policy.spec,
|
|
validations: policy.spec.validations.map((item: any, index: number) => index === 0 ? { ...item, expression: "true" } : item),
|
|
},
|
|
};
|
|
expect(evaluator.evaluatePacAdmissionState({ ...input, policy: mutatedPolicy })).toMatchObject({
|
|
ready: false,
|
|
reasons: expect.arrayContaining(["admission-live-spec-sha256-mismatch"]),
|
|
});
|
|
expect(evaluator.evaluatePacAdmissionState({
|
|
...input,
|
|
policy: { ...policy, spec: { ...policy.spec, matchConditions: [{ name: "disable-enforcement", expression: "false" }] } },
|
|
})).toMatchObject({ ready: false, reasons: expect.arrayContaining(["admission-live-spec-sha256-mismatch"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({
|
|
...input,
|
|
binding: { ...binding, spec: { ...binding.spec, matchResources: { namespaceSelector: { matchLabels: { disabled: "true" } } } } },
|
|
})).toMatchObject({ ready: false, reasons: expect.arrayContaining(["admission-live-spec-sha256-mismatch"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({
|
|
...input,
|
|
policy: {
|
|
...apiDefaultedPolicy,
|
|
spec: {
|
|
...apiDefaultedPolicy.spec,
|
|
matchConstraints: {
|
|
...apiDefaultedPolicy.spec.matchConstraints,
|
|
namespaceSelector: { matchLabels: { disabled: "true" } },
|
|
},
|
|
},
|
|
},
|
|
})).toMatchObject({ ready: false, reasons: expect.arrayContaining(["admission-live-spec-sha256-mismatch"]) });
|
|
expect(evaluator.evaluatePacAdmissionState({
|
|
...input,
|
|
policy: {
|
|
...apiDefaultedPolicy,
|
|
spec: {
|
|
...apiDefaultedPolicy.spec,
|
|
matchConstraints: {
|
|
...apiDefaultedPolicy.spec.matchConstraints,
|
|
resourceRules: apiDefaultedPolicy.spec.matchConstraints.resourceRules.map((rule: any) => ({ ...rule, scope: "Namespaced" })),
|
|
},
|
|
},
|
|
},
|
|
})).toMatchObject({ ready: false, reasons: expect.arrayContaining(["admission-live-spec-sha256-mismatch"]) });
|
|
});
|
|
|
|
test("AgentRun owning renderer emits ordered terminal roles over the shared workspace without changing build and publish steps", () => {
|
|
const spec = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }).spec;
|
|
const pipeline = renderAgentRunPipelineManifest(spec) as any;
|
|
const tasks = pipeline.spec.tasks;
|
|
expect(tasks.map((item: any) => item.name)).toEqual(["plan-artifacts", "collect-artifacts", "gitops-promote"]);
|
|
expect(tasks[0].runAfter).toBeUndefined();
|
|
expect(tasks[1].runAfter).toEqual(["plan-artifacts"]);
|
|
expect(tasks[2].runAfter).toEqual(["collect-artifacts"]);
|
|
for (const task of tasks) expect(task.workspaces).toEqual([{ name: "source", workspace: "source" }]);
|
|
expect(tasks[0].taskSpec.steps.map((item: any) => item.name)).toEqual(["source-checkout", "probe-env-image"]);
|
|
expect(tasks[1].taskSpec.steps.map((item: any) => item.name)).toEqual(["build-env-image"]);
|
|
expect(tasks[2].taskSpec.steps.map((item: any) => item.name)).toEqual(["publish-gitops"]);
|
|
expect(tasks[0].taskSpec.steps[1].script).toContain('"event":"pac-delivery-plan"');
|
|
expect(tasks[1].taskSpec.steps[0].script).toContain("buildctl-daemonless.sh");
|
|
expect(tasks[2].taskSpec.steps[0].script).toContain("gitops-publish");
|
|
});
|
|
|
|
test("AgentRun rendered delivery plan and real TaskRun terminals close the status evaluator gate", () => {
|
|
const spec = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }).spec;
|
|
const pipeline = renderAgentRunPipelineManifest(spec) as any;
|
|
const tasks = pipeline.spec.tasks as any[];
|
|
const sourceCommit = "c".repeat(40);
|
|
const gitopsCommit = "b".repeat(40);
|
|
const digest = `sha256:${"a".repeat(64)}`;
|
|
const planPrintf = String(tasks[0].taskSpec.steps[1].script)
|
|
.split("\n")
|
|
.find((line) => line.startsWith("printf '{\"event\":\"pac-delivery-plan\""));
|
|
expect(planPrintf).toBeDefined();
|
|
const planResult = Bun.spawnSync({
|
|
cmd: ["sh", "-c", `build_services='["agentrun-mgr"]'; reused_services='[]'; ${planPrintf!.replaceAll("$(params.revision)", sourceCommit)}`],
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
expect(planResult.exitCode).toBe(0);
|
|
const plan = JSON.parse(planResult.stdout.toString().trim());
|
|
expect(plan).toMatchObject({ event: "pac-delivery-plan", sourceCommitId: sourceCommit, buildServices: ["agentrun-mgr"] });
|
|
|
|
const terminals = tasks.map((task) => evaluator.taskTerminalRecord({
|
|
apiVersion: "tekton.dev/v1",
|
|
kind: "TaskRun",
|
|
metadata: {
|
|
name: `fixture-${task.name}`,
|
|
labels: { "tekton.dev/pipelineTask": task.name },
|
|
},
|
|
status: {
|
|
conditions: [{ type: "Succeeded", status: "True", reason: "Succeeded" }],
|
|
results: [],
|
|
},
|
|
}));
|
|
expect(terminals).not.toContain(null);
|
|
const publish = {
|
|
ok: true,
|
|
status: "succeeded",
|
|
phase: "gitops-publish",
|
|
gitopsCommit,
|
|
sourceCommit,
|
|
imageStatus: "built",
|
|
digest,
|
|
valuesPrinted: false,
|
|
};
|
|
const artifact = evaluator.extractPacArtifactEvidence([plan, ...terminals, publish], "");
|
|
expect(artifact.sourceObservation).toMatchObject({
|
|
contract: "pac-delivery-plan-v1",
|
|
mode: "delivery",
|
|
valid: true,
|
|
terminal: {
|
|
planArtifacts: "succeeded",
|
|
collectArtifacts: "succeeded",
|
|
gitopsPromote: "succeeded",
|
|
},
|
|
terminalSources: {
|
|
planArtifacts: { markerSource: "pac-delivery-plan-structured-log" },
|
|
},
|
|
});
|
|
expect(evaluator.evaluatePacStatus({
|
|
sourceCommit,
|
|
artifact,
|
|
registryPresent: true,
|
|
argo: {
|
|
sync: "Synced",
|
|
health: "Healthy",
|
|
revision: gitopsCommit,
|
|
revisionRelation: { relation: "exact", expectedRevision: gitopsCommit, observedRevision: gitopsCommit },
|
|
},
|
|
runtime: { image: `registry/agentrun-mgr@${digest}`, digest, replicas: 1, readyReplicas: 1 },
|
|
})).toMatchObject({ ok: true, code: "pac-ready-gitops-exact" });
|
|
});
|
|
|
|
test("provenance fixtures keep outer PaC delivery eligible while reporting admission warnings", () => {
|
|
const result = evaluator.runPacStatusFixtureChecks();
|
|
expect(result.ok).toBe(true);
|
|
for (const id of ["gitops-only-registry-not-configured", "image-registry-missing-fails-closed", "equal-revisions-ignore-owning-git-fetch-failure", "different-revisions-fetch-failure-remains-unknown", "admission-provenance-verified", "pre-bootstrap-run-warning-only", "marker-mismatch-warning-only", "candidate-label-warning-only", "candidate-pipeline-ref-warning-only", "service-account-mismatch-warning-only", "policy-identity-mismatch-warning-only"]) {
|
|
expect(result.checks.find((item) => item.id === id)?.ok).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("equal GitOps revisions stay exact when the supplemental owning Git fetch fails", () => {
|
|
const revision = "b".repeat(40);
|
|
const raw = {
|
|
relation: "unknown",
|
|
expectedRevision: revision,
|
|
observedRevision: revision,
|
|
proof: "unavailable",
|
|
reason: "owning-git-fetch-failed",
|
|
fetchOk: false,
|
|
};
|
|
const normalized = evaluator.normalizeGitopsRevisionRelation(raw);
|
|
expect(normalized).toMatchObject({
|
|
relation: "exact",
|
|
proof: "direct-revision-equality",
|
|
reason: "revisions-equal",
|
|
contextReason: "owning-git-fetch-failed",
|
|
warnings: [{
|
|
warning: true,
|
|
blocking: false,
|
|
code: "pac-owning-git-fetch-failed",
|
|
mutation: false,
|
|
valuesPrinted: false,
|
|
}],
|
|
valuesPrinted: false,
|
|
});
|
|
expect(evaluator.normalizeGitopsRevisionRelation(normalized)).toEqual(normalized);
|
|
const remote = readFileSync(resolve(root, "scripts/src/platform-infra-pipelines-as-code-remote.sh"), "utf8");
|
|
expect(remote).toContain("normalizeGitopsRevisionRelation({");
|
|
});
|
|
|
|
test("history evaluates admission and default RBAC in each consumer namespace", () => {
|
|
const remote = readFileSync(resolve(root, "scripts/src/platform-infra-pipelines-as-code-remote.sh"), "utf8");
|
|
expect(remote).toContain("consumer.admissionState = admissionStateForConsumer(consumer)");
|
|
expect(remote).toContain("kubectlJson(['-n', consumer.namespace, 'get', 'role', 'unidesk-pac-consumer-runner'");
|
|
expect(remote).toContain("kubectlJson(['-n', consumer.namespace, 'get', 'rolebinding', 'unidesk-pac-consumer-runner-default'");
|
|
expect(remote).toContain("defaultCanMutate(consumer.namespace)");
|
|
expect(remote).toContain("for verb in create patch update delete deletecollection; do");
|
|
expect(remote).toContain('timeout "$UNIDESK_PAC_WAIT_TIMEOUT_SECONDS" kubectl auth can-i');
|
|
expect(remote).toContain("for (const verb of ['create', 'patch', 'update', 'delete', 'deletecollection'])");
|
|
const defaultMutationProbe = remote.slice(remote.indexOf("function defaultCanMutate(namespace)"), remote.indexOf("function admissionStateForConsumer(consumer)"));
|
|
expect(defaultMutationProbe).toContain("timeout: waitTimeoutSeconds * 1000");
|
|
expect(defaultMutationProbe).not.toContain("timeout: 12000");
|
|
expect(remote).toContain("'delivery plan structured evidence is missing'");
|
|
expect(remote).toContain("'delivery plan source commit does not match the selected PipelineRun'");
|
|
expect(remote).not.toContain("'g14-ci-plan structured evidence is missing'");
|
|
expect(remote).not.toContain("for (const consumer of consumers) consumer.admissionState = admissionState");
|
|
expect(remote).toContain("consumer_bootstrap_state()");
|
|
expect(remote).toContain('get serviceaccount "$UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME"');
|
|
expect(remote).toContain('get rolebinding "$UNIDESK_PAC_RUNNER_ROLE_BINDING_NAME"');
|
|
expect(remote).toContain('get secret "$UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME"');
|
|
expect(remote).toContain("sa.automountServiceAccountToken === false");
|
|
expect(remote).toContain("const expectedKeys = ['password', 'url', 'username']");
|
|
expect(remote).toContain("passwordDecoded: false");
|
|
expect(remote).not.toContain("Buffer.from(argoSecret.data.password");
|
|
expect(remote).not.toContain("--from-literal=password=");
|
|
});
|
|
|
|
test("id-specific PaC debug keeps fixture failures but does not dump every passing check", () => {
|
|
const passing = Array.from({ length: 23 }, (_, index) => ({ id: `pass-${index}`, ok: true }));
|
|
expect(pacDebugFixtureDisclosure(passing, "agentrun-run")).toMatchObject({
|
|
fixtureGate: { ok: true, total: 23, passed: 23, failed: 0, disclosure: "failed-checks-only-for-id-specific-debug" },
|
|
checks: [],
|
|
});
|
|
const withFailure = [...passing, { id: "failed", ok: false }];
|
|
expect(pacDebugFixtureDisclosure(withFailure, "agentrun-run").checks).toEqual([{ id: "failed", ok: false }]);
|
|
expect(pacDebugFixtureDisclosure(passing, null).checks).toHaveLength(23);
|
|
});
|