import { describe, expect, test } from "bun:test"; import { spawnSync } from "node:child_process"; import { readFileSync } from "node:fs"; import { createRequire } from "node:module"; import { runAgentRunCommand, agentRunHelpText } from "./agentrun/entry"; import { cicdHelp as branchFollowerHelp, runCicdCommand as runBranchFollowerCommand } from "./cicd-branch-follower"; import { branchFollowerReadOnlyNext } from "./cicd-branch-follower-authority"; import { cicdGiteaActionsPocHelp, runGiteaActionsPocCommand } from "./cicd-gitea-actions-poc"; import { unsupported as unsupportedAgentRunCommand } from "./agentrun/utils"; import { PAC_AUTOMATIC_DELIVERY_REFERENCE, decideCicdDeliveryMutation, gitRepositoryIdentity, pacReadOnlyNext, readCicdDeliveryAuthorityCatalog, resolveCicdDeliveryAuthority, resolveCicdDeliveryAuthorityFromCatalog, type CicdDeliveryAuthorityCatalog, } from "./cicd-delivery-authority"; import { hwlabNodeHelp } from "./hwlab-node-help"; import { runHwlabNodeCommand } from "./hwlab-node/entry"; import { guardedSentinelDeliveryAction, runWebProbeSentinelCommand, webProbeSentinelDeliveryAuthorityFromCatalog, } from "./hwlab-node-web-sentinel-cicd"; import { parseNodeScopedDelegatedOptions } from "./hwlab-node/plan"; import { parseNodeWebProbeSentinelOptions } from "./hwlab-node/web-probe-observe"; import { nodeRuntimeVisiblePipelineRunDiagnostics, summarizeNodeRuntimeControlPlaneStatus } from "./hwlab-node/render"; import { renderControlPlaneResult, renderPublishCurrentResult } from "./hwlab-node-web-sentinel-cicd-shared"; import { resolvePacHistoryConsumerSelection, runPlatformInfraPipelinesAsCodeCommand } from "./platform-infra-pipelines-as-code"; import { giteaHelp, runPlatformInfraGiteaCommand } from "./platform-infra-gitea"; import { platformInfraHelp } from "./platform-infra/entry"; import { authorizeSentinelPacInternalPublish, readSentinelPacInternalPublishCapability, type SentinelPacInternalPublishCapability, } from "./sentinel-pac-execution-context"; import type { BranchFollowerRegistry, FollowerSpec, ParsedOptions } from "./cicd-types"; const mutationCommand = /\b(?:apply|closeout|trigger-current|refresh|sync|flush|webhook-test)\b/u; const pacEvaluator = createRequire(import.meta.url)("../native/cicd/pac-status-evaluator.cjs") as { classifyPacPipelineRun: (consumer: Record, item: Record) => Record; }; describe("YAML 组合的 CI/CD delivery authority", () => { test("组合 PaC 与 Gitea YAML,覆盖所有实际 consumer 且 id 唯一", () => { const catalog = readCicdDeliveryAuthorityCatalog(); expect(catalog.consumers).toHaveLength(8); expect(new Set(catalog.consumers.map((item) => item.consumerId)).size).toBe(8); for (const consumerId of ["agentrun-nc01-v02", "hwlab-nc01-v03", "sentinel-nc01-v03", "unidesk-host", "platform-infra-gitea-nc01"]) { const authority = resolveCicdDeliveryAuthority({ consumerId }); expect(authority.kind).toBe("pac-pr-merge"); expect(authority.mutationHintsAllowed).toBe(false); } }); test("repository identity 从通用 Git remote 解析,不依赖仓库特例", () => { expect(gitRepositoryIdentity("git@github.com:acme/widgets.git")).toBe("acme/widgets"); expect(gitRepositoryIdentity("https://example.test/acme/widgets.git")).toBe("acme/widgets"); expect(gitRepositoryIdentity("not-a-repository")).toBeNull(); }); test("未知、冲突和歧义 authority 均 fail-closed", () => { expect(resolveCicdDeliveryAuthority({ node: "NC01", lane: "v03" })).toMatchObject({ kind: "unknown", reason: "authority-ambiguous", mutationHintsAllowed: false, }); expect(resolveCicdDeliveryAuthority({ consumerId: "hwlab-nc01-v03", node: "JD01" })).toMatchObject({ kind: "unknown", reason: "authority-identity-mismatch", mutationHintsAllowed: false, }); expect(resolveCicdDeliveryAuthority({ declaredSourceAuthorityMode: "giteaSnapshot", declaredConfigRef: "fixture.yaml" })).toMatchObject({ kind: "unknown", reason: "authority-not-declared", mutationHintsAllowed: false, }); expect(resolveCicdDeliveryAuthority({ node: "NC01", lane: "nc01-v02", sourceRepository: "acme/drifted", sourceBranch: "v0.2", declaredSourceAuthorityMode: "gitMirrorSnapshot", declaredConfigRef: "config/agentrun.yaml", })).toMatchObject({ kind: "unknown", reason: "authority-identity-mismatch", mutationHintsAllowed: false, }); }); test("只有显式 legacy-manual authority 允许旧 mutation 实现", () => { const catalog: CicdDeliveryAuthorityCatalog = { consumers: [{ consumerId: "arbitrary-pac", node: "NODE-A", lane: "lane-a", namespace: "ci-a", pipelineRunPrefix: "widgets-a-", repositoryRef: "repo-a", sourceRepository: "acme/widgets", sourceBranch: "main", sourceSnapshotPrefix: "refs/unidesk/snapshots/widgets/main", giteaOwner: "mirrors", giteaRepository: "acme-widgets", giteaRepositoryUrl: "https://gitea.example.test/mirrors/acme-widgets", pacControllerVersion: "v0.48.0", configRefs: ["pac.yaml", "gitea.yaml"], }], configRefs: ["pac.yaml", "gitea.yaml"], }; const legacy = resolveCicdDeliveryAuthorityFromCatalog(catalog, { node: "NODE-B", lane: "lane-b", sourceRepository: "acme/legacy", sourceBranch: "release", declaredSourceAuthorityMode: "gitMirrorSnapshot", declaredConfigRef: "legacy-lanes.yaml", }); expect(legacy).toMatchObject({ kind: "legacy-manual", mutationHintsAllowed: true }); expect(decideCicdDeliveryMutation(legacy, { command: "fixture trigger-current", statusCommand: "fixture status", action: "trigger-current", node: "NODE-B", lane: "lane-b", })).toMatchObject({ allowed: true }); }); test("只读 Next 只有 status/history 和稳定自动链修复引用", () => { const next = pacReadOnlyNext("NC01", "hwlab-nc01-v03"); expect(Object.keys(next).sort()).toEqual(["fixAutomaticDelivery", "history", "status", "valuesPrinted"]); expect(JSON.stringify(next)).not.toMatch(mutationCommand); expect(PAC_AUTOMATIC_DELIVERY_REFERENCE).toBe("docs/reference/platform-infra.md#gitea-与-pipelines-as-code-边界"); expect(PAC_AUTOMATIC_DELIVERY_REFERENCE).not.toContain("issues/"); }); }); describe("migrated CLI 执行 guard 与提示清理", () => { test("AgentRun migrated trigger/refresh/mirror mutation 在远端调用前结构化拒绝", async () => { const commands = [ ["control-plane", "trigger-current", "--node", "NC01", "--lane", "nc01-v02", "--confirm"], ["control-plane", "refresh", "--node", "NC01", "--lane", "nc01-v02", "--confirm"], ["git-mirror", "sync", "--node", "NC01", "--lane", "nc01-v02", "--confirm", "--wait"], ["git-mirror", "flush", "--node", "NC01", "--lane", "nc01-v02", "--confirm", "--wait"], ]; for (const args of commands) { const result = await runAgentRunCommand({} as never, args) as Record; expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false }); expect(JSON.stringify(result.next)).not.toMatch(mutationCommand); } }); test("HWLAB migrated delivery mutation 在异步 job 创建前结构化拒绝", async () => { const commands = [ ["control-plane", "trigger-current", "--node", "NC01", "--lane", "v03", "--confirm"], ["control-plane", "refresh", "--node", "NC01", "--lane", "v03", "--confirm"], ["control-plane", "sync", "--node", "NC01", "--lane", "v03", "--confirm"], ["control-plane", "source-workspace", "sync", "--node", "NC01", "--lane", "v03", "--confirm"], ["git-mirror", "sync", "--node", "NC01", "--lane", "v03", "--confirm", "--wait"], ["git-mirror", "flush", "--node", "NC01", "--lane", "v03", "--confirm", "--wait"], ]; for (const args of commands) { const result = await runHwlabNodeCommand({} as never, args) as Record; expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false }); expect(JSON.stringify(result.next)).not.toMatch(mutationCommand); } }); test("Web sentinel migrated delivery mutation 在 state load 和异步 job 前拒绝", () => { const spec = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]).spec; const sourceOverride = { sourceCommit: null, sourceStageRef: null, sourceMirrorCommit: null, sourceAuthority: null }; const commands = [ { kind: "image", action: "build", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, sourceOverride }, { kind: "control-plane", action: "trigger-current", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, rerun: false, manualRecovery: false, reason: null, sourceOverride }, { kind: "publish", action: "publish-current", node: "NC01", lane: "v03", sentinelId: null, dryRun: true, confirm: false, wait: false, timeoutSeconds: 30, rerun: false, manualRecovery: true, reason: "explicit recovery", sourceOverride }, ]; for (const options of commands) { const result = runWebProbeSentinelCommand(spec, options as never) as unknown as Record; expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false }); expect(Object.keys(result.next as Record).sort()).toEqual([ "controlPlaneStatus", "fixAutomaticDelivery", "history", "pacHistory", "pacStatus", "status", ]); expect(JSON.stringify(result.next)).not.toMatch(mutationCommand); } }); test("Web sentinel 稳定 PaC consumer 漂移或缺失时保持 unknown,不降级为 legacy", () => { const spec = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]).spec; const catalog = readCicdDeliveryAuthorityCatalog(); const consumerId = "sentinel-nc01-v03"; const drifted: CicdDeliveryAuthorityCatalog = { ...catalog, consumers: catalog.consumers.map((consumer) => consumer.consumerId === consumerId ? { ...consumer, node: "DRIFTED" } : consumer), }; const missing: CicdDeliveryAuthorityCatalog = { ...catalog, consumers: catalog.consumers.filter((consumer) => consumer.consumerId !== consumerId), }; for (const fixture of [drifted, missing]) { const authority = webProbeSentinelDeliveryAuthorityFromCatalog(spec, fixture); expect(authority).toMatchObject({ kind: "unknown", mutationHintsAllowed: false }); const decision = decideCicdDeliveryMutation(authority, { command: "web-probe sentinel publish-current", statusCommand: "web-probe sentinel control-plane status", action: "sentinel-publish-current", node: spec.nodeId, lane: spec.lane, }); expect(decision).toMatchObject({ allowed: false }); if (!decision.allowed) expect(JSON.stringify(decision.result.next)).not.toMatch(mutationCommand); } }); test("Web sentinel 内部 publish capability 默认关闭,启用后仍因 admission provenance 缺失而拒绝", () => { const authority = resolveCicdDeliveryAuthority({ consumerId: "sentinel-nc01-v03" }); expect(authority.kind).toBe("pac-pr-merge"); const options = { kind: "publish", action: "publish-current", node: "NC01", lane: "v03", sentinelId: "nc01-web-probe-sentinel", dryRun: false, confirm: true, wait: true, timeoutSeconds: 180, rerun: false, manualRecovery: false, reason: null, internalExecution: "pac-controller", sourceOverride: { sourceCommit: "a".repeat(40), sourceStageRef: null, sourceMirrorCommit: null, sourceAuthority: "gitea-snapshot" }, } as const; const capability = readSentinelPacInternalPublishCapability(); expect(capability).toMatchObject({ enabled: false, admissionProvenance: "unavailable" }); expect(authorizeSentinelPacInternalPublish(options, authority, capability)).toMatchObject({ allowed: false, reason: "capability-disabled", }); const enabled: SentinelPacInternalPublishCapability = { ...capability, enabled: true }; expect(authorizeSentinelPacInternalPublish(options, authority, enabled)).toMatchObject({ allowed: false, reason: "admission-provenance-unavailable", }); expect(authorizeSentinelPacInternalPublish({ ...options, internalExecution: null }, authority, enabled)).toMatchObject({ allowed: false, reason: "operator-entrypoint", }); }); test("Web sentinel 保持旧自动入口,不把 default SA、Pod env 或可写 metadata 当 creator proof", () => { const pacYaml = readFileSync("config/platform-infra/pipelines-as-code.yaml", "utf8"); expect(pacYaml).toContain("sentinelInternalPublish:"); expect(pacYaml).toContain("enabled: false"); expect(pacYaml).toContain("issues/1769"); expect(pacYaml).toContain("service_account: default"); for (const node of ["jd01", "nc01"] as const) { const source = readFileSync(`.tekton/web-probe-sentinel-${node}-pac.yaml`, "utf8"); expect(source).toContain("serviceAccountName: default"); expect(source).toContain("web-probe sentinel publish-current \\"); expect(source).not.toContain("pac-publish-current"); expect(source).not.toContain("UNIDESK_PAC_INTERNAL_ENTRYPOINT"); expect(source).not.toContain("UNIDESK_PAC_POD_UID"); } const parsed = parseNodeWebProbeSentinelOptions([ "pac-publish-current", "--node", "NC01", "--lane", "v03", "--sentinel", "nc01-web-probe-sentinel", "--confirm", "--wait", "--source-commit", "a".repeat(40), "--source-stage-ref", `refs/unidesk/snapshots/gitea-actions/unidesk-master-nc01/${"a".repeat(40)}`, "--source-authority", "gitea-snapshot", ]); expect(parsed.sentinel).toMatchObject({ kind: "publish", internalExecution: "pac-controller", manualRecovery: false }); }); test("PaC 外层事件与内层 deterministic publish 分开归类,只有外层可驱动 delivery", () => { const consumer = { repository: "sentinel-nc01-v03", pipelineRunPrefix: "hwlab-web-probe-sentinel-nc01-", pipeline: "hwlab-web-probe-sentinel-nc01" }; const outer = { metadata: { name: "hwlab-web-probe-sentinel-nc01-9h49j", labels: { "app.kubernetes.io/managed-by": "pipelinesascode.tekton.dev", "pipelinesascode.tekton.dev/event-type": "push", "pipelinesascode.tekton.dev/repository": "sentinel-nc01-v03", }, annotations: { "pipelinesascode.tekton.dev/git-provider": "gitea" }, }, }; const inner = { metadata: { name: "hwlab-web-probe-sentinel-nc01-web-probe-sentinel-b125fb358d46", labels: { "app.kubernetes.io/part-of": "hwlab-web-probe-sentinel", "unidesk.ai/ci-system": "tekton", }, annotations: { "unidesk.ai/source-authority": "gitea-snapshot", "unidesk.ai/publish-gitops": "true", }, }, }; expect(pacEvaluator.classifyPacPipelineRun(consumer, outer)).toMatchObject({ deliveryClass: "outer-pac-event", deliveryAuthorityEligible: true, admissionProvenanceVerified: false, metadataObservationOnly: true, }); expect(pacEvaluator.classifyPacPipelineRun(consumer, inner)).toMatchObject({ deliveryClass: "inner-deterministic-publish", deliveryAuthorityEligible: false, parentPipelineRun: null, parentRelation: "unproven", }); const catalog = readCicdDeliveryAuthorityCatalog(); for (const consumerId of ["agentrun-nc01-v02", "hwlab-nc01-v03", "sentinel-nc01-v03", "unidesk-host"]) { const configured = catalog.consumers.find((item) => item.consumerId === consumerId); if (configured === undefined) throw new Error(`missing classification fixture ${consumerId}`); const classified = pacEvaluator.classifyPacPipelineRun({ repository: configured.repositoryRef, pipelineRunPrefix: configured.pipelineRunPrefix, }, { metadata: { name: `${configured.pipelineRunPrefix}-outer-fixture`, labels: { "app.kubernetes.io/managed-by": "pipelinesascode.tekton.dev", "pipelinesascode.tekton.dev/event-type": "push", "pipelinesascode.tekton.dev/repository": configured.repositoryRef, }, annotations: { "pipelinesascode.tekton.dev/git-provider": "gitea" }, }, }); expect(classified).toMatchObject({ deliveryClass: "outer-pac-event", deliveryAuthorityEligible: true }); } }); test("Web sentinel 业务观测和仪表盘动作不被 delivery guard 误阻断", () => { for (const options of [ { kind: "dashboard", action: "trigger" }, { kind: "validate", action: "validate" }, { kind: "maintenance", action: "status" }, { kind: "control-plane", action: "status" }, { kind: "image", action: "status" }, ]) { expect(guardedSentinelDeliveryAction(options as never)).toBeNull(); } }); test("HWLAB degraded summary 不再返回 flush 或 triggerCurrent", () => { const scoped = parseNodeScopedDelegatedOptions("control-plane", ["status", "--node", "NC01", "--lane", "v03"]); const summary = summarizeNodeRuntimeControlPlaneStatus({ ok: false, degradedReason: "git-mirror-pending-flush" }, scoped); expect(summary.nextAction).toBe("bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01 --consumer hwlab-nc01-v03"); expect(summary.next).not.toHaveProperty("triggerCurrent"); expect(summary.next).not.toHaveProperty("gitMirrorFlush"); expect(JSON.stringify(summary.next)).not.toMatch(mutationCommand); }); test("HWLAB migrated PipelineRun diagnostics 不泄漏 rerun Next 或文本提示", () => { const authority = resolveCicdDeliveryAuthority({ consumerId: "hwlab-nc01-v03" }); const diagnostics = nodeRuntimeVisiblePipelineRunDiagnostics({ failureSummary: { nextAction: "choose controlled rerun" }, next: { rerun: "bun scripts/cli.ts hwlab nodes control-plane trigger-current --confirm --rerun" }, }, authority); expect(diagnostics?.next).toBeNull(); expect(JSON.stringify(diagnostics)).not.toContain("trigger-current"); expect(JSON.stringify(diagnostics)).not.toContain("controlled rerun"); expect(JSON.stringify(diagnostics)).toContain("不得人工 rerun"); }); test("默认 help 只暴露观察入口,legacy/platform 写入口只在 scoped help", async () => { const agentRunDefault = agentRunHelpText(["control-plane", "--help"]); const hwlabDefault = JSON.stringify(hwlabNodeHelp()); const pacDefault = JSON.stringify(await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help"])); const giteaDefault = JSON.stringify(giteaHelp()); expect(agentRunDefault).not.toMatch(mutationCommand); expect(hwlabDefault).not.toMatch(mutationCommand); expect(pacDefault).not.toMatch(mutationCommand); expect(giteaDefault).not.toMatch(mutationCommand); expect(agentRunHelpText(["control-plane", "legacy-cicd", "--help"])).toContain("trigger-current"); expect(JSON.stringify(hwlabNodeHelp("legacy-cicd"))).toContain("git-mirror flush"); expect(JSON.stringify(await runPlatformInfraPipelinesAsCodeCommand({} as never, ["help", "platform-bootstrap"]))).toContain("pipelines-as-code apply"); expect(JSON.stringify(giteaHelp("platform-bootstrap"))).toContain("platform-infra gitea apply"); }); test("所有 PaC consumer 与 AgentRun/HWLAB plan 均只返回 authority-aware 只读 Next", async () => { const results = [ await runAgentRunCommand({} as never, ["control-plane", "plan", "--node", "NC01", "--lane", "nc01-v02", "--raw"]), await runHwlabNodeCommand({} as never, ["control-plane", "plan", "--node", "NC01", "--lane", "v03", "--raw"]), ...await Promise.all(readCicdDeliveryAuthorityCatalog().consumers.map((consumer) => ( runPlatformInfraPipelinesAsCodeCommand({} as never, ["plan", "--target", consumer.node, "--consumer", consumer.consumerId, "--raw"]) ))), ] as Record[]; for (const result of results) { const next = result.next as Record; expect(Object.keys(next).sort()).toEqual(["fixAutomaticDelivery", "history", "status", "valuesPrinted"]); expect(JSON.stringify(next)).not.toMatch(mutationCommand); } }); test("Gitea 与 mirror 默认 plan 只返回只读 Next", async () => { const plans = [ await runPlatformInfraGiteaCommand({} as never, ["plan", "--target", "NC01", "--raw"]), await runPlatformInfraGiteaCommand({} as never, ["mirror", "plan", "--target", "NC01", "--raw"]), ] as Record[]; for (const plan of plans) { expect(plan.mutation).toBe(false); expect(JSON.stringify(plan.next)).not.toMatch(mutationCommand); expect(JSON.stringify(plan.next)).toContain("status"); expect(JSON.stringify(plan.next)).toContain(PAC_AUTOMATIC_DELIVERY_REFERENCE); } }); test("PaC history detail 只按唯一 consumer 生成 Next,四类前缀无默认 consumer 回退", () => { const ids = ["sentinel-nc01-v03", "unidesk-host", "agentrun-nc01-v02", "hwlab-nc01-v03"]; const consumers = readCicdDeliveryAuthorityCatalog().consumers.filter((consumer) => ids.includes(consumer.consumerId)).map((consumer) => ({ id: consumer.consumerId, node: consumer.node, pipelineRunPrefix: consumer.pipelineRunPrefix, })); for (const consumer of consumers) { const detailId = `${consumer.pipelineRunPrefix}-fixture`; expect(resolvePacHistoryConsumerSelection(consumers, "NC01", null, detailId)).toEqual({ selectedConsumerIds: [consumer.id], nextConsumerId: consumer.id, }); } expect(() => resolvePacHistoryConsumerSelection(consumers, "NC01", null, "no-matching-pipelinerun")).toThrow("matched 0"); expect(() => resolvePacHistoryConsumerSelection([ { id: "a", node: "NC01", pipelineRunPrefix: "overlap-" }, { id: "b", node: "NC01", pipelineRunPrefix: "overlap-run" }, ], "NC01", null, "overlap-run-1")).toThrow("matched 2"); expect(() => resolvePacHistoryConsumerSelection(consumers, "NC01", "hwlab-nc01-v03", "hwlab-web-probe-sentinel-nc01-fixture")).toThrow("belongs to sentinel-nc01-v03"); }); test("PaC、HWLAB scoped help 的真实 CLI 路由可达且默认入口不泄漏 delivery mutation", () => { const pacDefault = runCliJson(["platform-infra", "pipelines-as-code", "--help"]); const pacBootstrap = runCliJson(["platform-infra", "pipelines-as-code", "help", "platform-bootstrap"]); const pacCompatibility = runCliJson(["platform-infra", "pipelines-as-code", "help", "compatibility-diagnostics"]); const hwlabPlatform = runCliJson(["hwlab", "nodes", "control-plane", "platform-maintenance", "--help"]); const hwlabLegacy = runCliJson(["hwlab", "nodes", "control-plane", "legacy-cicd", "--help"]); expect(JSON.stringify(pacDefault.data)).not.toMatch(mutationCommand); expect(JSON.stringify(pacBootstrap.data)).toContain("pipelines-as-code apply"); expect(JSON.stringify(pacCompatibility.data)).not.toMatch(/webhook-test|trigger-current|\bsync\b|\bflush\b|apply --/u); expect((hwlabPlatform.data as Record).command).toBe("hwlab nodes control-plane platform-maintenance"); expect((hwlabLegacy.data as Record).command).toBe("hwlab nodes control-plane legacy-cicd"); }); test("PaC webhook-test 已从公开路由与远端执行器移除", async () => { const result = await runPlatformInfraPipelinesAsCodeCommand({} as never, ["webhook-test", "--target", "NC01", "--confirm"]) as Record; expect(result).toMatchObject({ ok: false, error: "unsupported-platform-infra-pipelines-as-code-command" }); expect(JSON.stringify(result.help)).not.toContain("webhook-test"); expect(readFileSync("scripts/src/platform-infra-pipelines-as-code-remote.sh", "utf8")).not.toContain("webhook-test"); }); test("Gitea webhook-test 已从公开路由与远端执行器移除", async () => { const result = await runPlatformInfraGiteaCommand({} as never, ["mirror", "webhook", "test", "--target", "NC01", "--confirm"]) as Record; expect(result).toMatchObject({ ok: false, error: "unsupported-platform-infra-gitea-mirror-webhook-command" }); expect(readFileSync("scripts/src/platform-infra-gitea-remote.sh", "utf8")).not.toContain("mirror-webhook-test"); }); test("Sentinel migrated 文本输出只渲染实际只读 status/history/fix,不打印空 mutation 标签", () => { const next = { status: "bun scripts/cli.ts web-probe sentinel control-plane status --node NC01 --lane v03", history: "bun scripts/cli.ts platform-infra pipelines-as-code history --target NC01 --consumer sentinel-nc01-v03", fixAutomaticDelivery: PAC_AUTOMATIC_DELIVERY_REFERENCE, }; for (const renderedText of [ renderPublishCurrentResult({ command: "fixture publish", ok: false, next }), renderControlPlaneResult({ command: "fixture status", ok: false, next }), ]) { expect(renderedText).toContain("status:"); expect(renderedText).toContain("history:"); expect(renderedText).toContain("fix-automatic-delivery:"); expect(renderedText).not.toMatch(/pac-closeout:|manual-recovery:|trigger-current:|apply:|sync:|flush:/u); } }); test("retired branch-follower 写操作和默认 cleanup 在任何远端执行前结构化拒绝", async () => { const commands = [ ["branch-follower", "apply", "--confirm", "--json"], ["branch-follower", "run-once", "--follower", "hwlab-jd01-v03", "--confirm", "--wait", "--json"], ["branch-follower", "job", "--follower", "agentrun-jd01-v02", "--job", "image-build", "--json"], ["branch-follower", "gate", "--follower", "hwlab-jd01-v03", "--gate", "runtime-closeout", "--confirm", "--json"], ["branch-follower", "debug-step", "--follower", "web-probe-sentinel-master", "--step", "state-write", "--confirm", "--json"], ["branch-follower", "cleanup-state", "--all", "--confirm", "--json"], ]; for (const args of commands) { const rendered = await runBranchFollowerCommand(null, args); const payload = JSON.parse(rendered.renderedText) as Record; expect(payload).toMatchObject({ ok: false, mutation: false }); expect(JSON.stringify(payload.next)).not.toMatch(mutationCommand); } const forged = await runBranchFollowerCommand(null, ["branch-follower", "legacy-cicd", "apply", "--config", "/tmp/forged-legacy.yaml", "--confirm", "--json"]); expect(JSON.parse(forged.renderedText)).toMatchObject({ ok: false, mutation: false, mode: "noncanonical-branch-follower-config-mutation-blocked", }); expect(Object.keys((JSON.parse(forged.renderedText) as { next: Record }).next).sort()).toEqual([ "fixAutomaticDelivery", "status", "valuesPrinted", ]); await expect(runBranchFollowerCommand(null, ["branch-follower", "plan", "--json"])).rejects.toThrow("default surface is read-only"); }); test("branch-follower unknown 与多 node scope 不猜测 JD01 PaC 入口", () => { const options = { followerId: null } as ParsedOptions; const unknownRegistry = { followers: [{ id: "unknown-fixture", deliveryAuthority: "unknown", source: { repository: "acme/unknown", branch: "main" }, target: { node: "NODE-X", lane: "lane-x" }, } as FollowerSpec], } as BranchFollowerRegistry; const unknownNext = branchFollowerReadOnlyNext(unknownRegistry, options); expect(Object.keys(unknownNext).sort()).toEqual(["events", "fixAutomaticDelivery", "logs", "status", "valuesPrinted"]); expect(JSON.stringify(unknownNext)).not.toContain("JD01"); const catalog = readCicdDeliveryAuthorityCatalog(); const consumers = ["hwlab-jd01-v03", "hwlab-nc01-v03"].map((id) => { const consumer = catalog.consumers.find((item) => item.consumerId === id); if (consumer === undefined) throw new Error(`missing fixture consumer ${id}`); return { id: `${id}-retired-follower`, deliveryAuthority: "retired-readonly", source: { repository: consumer.sourceRepository, branch: consumer.sourceBranch }, target: { node: consumer.node, lane: consumer.lane }, } as FollowerSpec; }); const multiNodeNext = branchFollowerReadOnlyNext({ followers: consumers } as BranchFollowerRegistry, options); expect(Object.keys(multiNodeNext).sort()).toEqual(["events", "fixAutomaticDelivery", "logs", "status", "valuesPrinted"]); expect(JSON.stringify(multiNodeNext)).not.toContain("--target JD01"); }); test("retired branch-follower 与 Gitea Actions POC 默认帮助只读", async () => { expect(JSON.stringify(branchFollowerHelp())).not.toMatch(mutationCommand); expect(JSON.stringify(cicdGiteaActionsPocHelp())).not.toMatch(mutationCommand); await expect(runGiteaActionsPocCommand(null, ["apply"])).rejects.toThrow("intentionally unavailable"); }); test("platform-infra 默认 delivery 帮助和 AgentRun 失败帮助不暴露人工推进", () => { const platformHelp = platformInfraHelp() as { usage: string[] }; const deliveryUsage = platformHelp.usage.filter((line) => line.includes("gitea") || line.includes("pipelines-as-code")); expect(JSON.stringify(deliveryUsage)).not.toMatch(mutationCommand); expect(deliveryUsage).toContain("bun scripts/cli.ts platform-infra gitea mirror webhook status --target NC01"); expect(deliveryUsage).toContain("bun scripts/cli.ts platform-infra pipelines-as-code help platform-bootstrap"); const agentRunOperations = unsupportedAgentRunCommand(["unexpected"]).renderedText.split("Operations:")[1] ?? ""; expect(agentRunOperations).not.toMatch(mutationCommand); const root = runCliJson(["--help"]); expect(JSON.stringify(root)).not.toMatch(/platform-infra gitea apply|platform-infra gitea mirror sync/u); }); }); function runCliJson(args: string[]): { ok: boolean; command: string; data: unknown } { const result = spawnSync("bun", ["scripts/cli.ts", ...args], { cwd: process.cwd(), encoding: "utf8", timeout: 15_000, }); if (result.status !== 0) throw new Error(`CLI ${args.join(" ")} failed: ${result.stderr}`); return JSON.parse(result.stdout) as { ok: boolean; command: string; data: unknown }; }