Files
pikasTech-unidesk/scripts/src/cicd-delivery-authority.test.ts
T
2026-07-11 11:42:16 +02:00

578 lines
30 KiB
TypeScript

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<string, unknown>, item: Record<string, unknown>) => Record<string, unknown>;
};
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<string, unknown>;
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<string, unknown>;
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<string, unknown>;
expect(result).toMatchObject({ ok: false, mode: "pac-pr-merge-mutation-blocked", mutation: false });
expect(Object.keys(result.next as Record<string, unknown>).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<string, unknown>[];
for (const result of results) {
const next = result.next as Record<string, unknown>;
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<string, unknown>[];
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<string, unknown>).command).toBe("hwlab nodes control-plane platform-maintenance");
expect((hwlabLegacy.data as Record<string, unknown>).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<string, unknown>;
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<string, unknown>;
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<string, unknown>;
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<string, unknown> }).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 };
}