import { expect, test } from "bun:test"; import { spawnSync } from "node:child_process"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { readGiteaWebhookGitOpsDelivery, renderGiteaWebhookSyncDesiredManifest } from "./platform-infra-gitea"; import { renderPlatformInfraGiteaApplication } from "../native/cicd/publish-platform-infra-gitea-gitops.mjs"; const root = resolve(import.meta.dir, "../.."); test("existing NC01 Repository can bootstrap the independent desired publisher without new RBAC", () => { const pipeline = Bun.YAML.parse(readFileSync(resolve(root, ".tekton/platform-infra-gitea-nc01-pac.yaml"), "utf8")) as any; expect(pipeline.metadata.annotations).toMatchObject({ "pipelinesascode.tekton.dev/on-event": "[push]", "pipelinesascode.tekton.dev/on-target-branch": "[master]", }); expect(pipeline.metadata.annotations["pipelinesascode.tekton.dev/on-cel-expression"]).toContain("node == 'NC01'"); expect(pipeline.spec.taskRunTemplate.serviceAccountName).toBe("default"); const script = pipeline.spec.pipelineSpec.tasks[0].taskSpec.steps[0].script as string; expect(script).toContain("$SOURCE_SNAPSHOT_PREFIX/$SOURCE_COMMIT"); expect(script).toContain("publish-platform-infra-gitea-gitops.mjs"); expect(script).not.toContain("build-unidesk-host-image"); expect(script).not.toContain("todo-note"); const pac = Bun.YAML.parse(readFileSync(resolve(root, "config/platform-infra/pipelines-as-code.yaml"), "utf8")) as any; const consumer = pac.consumers.find((item: any) => item.id === "platform-infra-gitea-nc01"); expect(consumer).toMatchObject({ repositoryRef: "sentinel-nc01-v03", node: "NC01", namespace: "devops-infra", pipeline: "platform-infra-gitea-nc01-pac", closeoutGitOpsMirrorFlush: false, }); expect(consumer.argoBootstrap).toBeUndefined(); }); test("owning YAML renders one child Application and the complete durable bridge desired state", () => { const delivery = readGiteaWebhookGitOpsDelivery(); expect(delivery.enabled).toBe(true); expect(delivery.bootstrapApplicationPath).toStartWith("deploy/gitops/unidesk-host/"); expect(delivery.desiredManifestPath).toStartWith("deploy/gitops/platform-infra/gitea-nc01/"); const application = renderPlatformInfraGiteaApplication(delivery); expect(application).toContain("kind: Application"); expect(application).toContain("name: platform-infra-gitea-nc01"); expect(application).toContain("path: deploy/gitops/platform-infra/gitea-nc01"); expect(application).toContain("prune: true"); expect(application).toContain("selfHeal: true"); const manifest = renderGiteaWebhookSyncDesiredManifest("NC01"); expect(manifest).toContain("kind: ServiceAccount"); expect(manifest).toContain("kind: PersistentVolumeClaim"); expect(manifest).toContain("strategy:\n type: Recreate"); expect(manifest).toContain("serviceAccountName: gitea-github-sync"); expect(manifest).toContain("name: UNIDESK_GITEA_WEBHOOK_RESPONSE_BUDGET_MS\n value: \"6500\""); expect(manifest).toContain("name: UNIDESK_GITEA_WEBHOOK_WORKER_ATTEMPT_TIMEOUT_MS\n value: \"120000\""); const candidateConfig = manifest.indexOf("name: gitea-github-sync-candidate\n namespace: devops-infra\n annotations:"); const candidateJob = manifest.indexOf("kind: Job\nmetadata:\n name: gitea-github-sync-candidate"); const deployment = manifest.indexOf("kind: Deployment\nmetadata:\n name: gitea-github-sync"); expect(candidateConfig).toBeGreaterThanOrEqual(0); expect(candidateJob).toBeGreaterThan(candidateConfig); expect(deployment).toBeGreaterThan(candidateJob); expect(manifest).toContain("argocd.argoproj.io/hook: PreSync"); expect(manifest).toContain('argocd.argoproj.io/sync-wave: "-2"'); expect(manifest).toContain('argocd.argoproj.io/sync-wave: "-1"'); expect(manifest).toContain("node /etc/gitea-github-sync-candidate/entrypoint.mjs &"); expect(manifest).toContain('import { startServer } from "./server.mjs";'); expect(manifest).toContain("- /etc/gitea-github-sync/entrypoint.mjs"); expect(manifest).toContain("mountPath: /etc/gitea-github-sync-candidate"); expect(manifest).toContain("name: candidate-inbox\n emptyDir: {}"); expect(manifest).toContain('gate: "gitea-github-sync-candidate-startup"'); const documents = manifest.split(/^---\s*$/mu).map((item) => item.trim()).filter(Boolean).map((item) => Bun.YAML.parse(item) as any); expect(documents.map((item) => item.kind)).toEqual([ "ConfigMap", "Job", "ServiceAccount", "ConfigMap", "PersistentVolumeClaim", "Service", "Deployment", "Role", "RoleBinding", "Role", "RoleBinding", "Role", "RoleBinding", "ValidatingAdmissionPolicy", "ValidatingAdmissionPolicyBinding", "ServiceAccount", "ConfigMap", "Deployment", ]); const pacReadOnlyNamespaces = documents .filter((item) => item.kind === "Role" && item.metadata?.name === "unidesk-pac-consumer-runner") .map((item) => item.metadata.namespace) .sort(); expect(pacReadOnlyNamespaces).toEqual(["agentrun-ci", "devops-infra", "selfmedia-ci"]); const candidate = documents.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "gitea-github-sync-candidate"); const activeConfig = documents.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "gitea-github-sync-config"); const bridge = documents.find((item) => item.kind === "Deployment" && item.metadata?.name === "gitea-github-sync"); expect(candidate.data["hook-topology.json"]).toBeUndefined(); expect(candidate.data["platform_infra_gitea_hook_reconciler.py"]).toBeUndefined(); expect(activeConfig.data["hook-topology.json"]).toContain('"ownerTargetId": "NC01"'); expect(activeConfig.data["hook-topology.json"]).toContain('"targetId": "NC01"'); expect(activeConfig.data["hook-topology.json"]).toContain('"lookbackSeconds": 259200'); expect(activeConfig.data["hook-topology.json"]).toContain('"desiredHooks"'); expect(activeConfig.data["platform_infra_gitea_hook_reconciler.py"]).toContain("github-hook-topology-reconciled"); expect(bridge.spec.template.spec.containers.map((item: any) => item.name)).toEqual([ "github-to-gitea-sync", "github-hook-topology-reconciler", ]); const candidateContainer = documents.find((item) => item.kind === "Job" && item.metadata?.name === "gitea-github-sync-candidate").spec.template.spec.containers[0]; expect(candidateContainer.env.map((item: any) => item.name)).not.toContain("UNIDESK_GITEA_HOOK_RECOVERY_STATE_PATH"); const stableBridge = bridge.spec.template.spec.containers[0]; expect(stableBridge.env.filter((item: any) => item.name === "UNIDESK_GITEA_HOOK_RECOVERY_STATE_PATH")).toHaveLength(1); const reconciler = bridge.spec.template.spec.containers[1]; expect(reconciler.volumeMounts).toContainEqual({ name: "durable-inbox", mountPath: "/var/lib/gitea-github-sync" }); const sourcePath = resolve(root, "scripts/src/platform_infra_gitea_hook_reconciler.py"); const syntax = spawnSync("python3", ["-c", "import pathlib,sys; compile(pathlib.Path(sys.argv[1]).read_text(), sys.argv[1], 'exec')", sourcePath], { encoding: "utf8" }); expect(syntax.status).toBe(0); expect(syntax.stderr).toBe(""); const recoveryTests = spawnSync("python3", [resolve(root, "scripts/src/platform_infra_gitea_hook_reconciler.test.py")], { encoding: "utf8" }); expect(recoveryTests.status).toBe(0); expect(recoveryTests.stderr).toContain("OK"); });