























@@ -41,6 +41,25 @@ describe("plugin gateway gauntlet helpers", () => {
4141await fs.writeFile(path.join(dir, fileName), source, "utf8");
4242}
434344+function minimalQaSuiteSummary(metrics: Record<string, number>) {
45+return {
46+counts: { failed: 0, passed: 1, total: 1 },
47+ metrics,
48+run: {
49+concurrency: 1,
50+fastMode: false,
51+finishedAt: "2026-05-30T00:00:01.000Z",
52+primaryModel: "mock-openai/gpt-5.5",
53+primaryModelName: "gpt-5.5",
54+primaryProvider: "mock-openai",
55+providerMode: "mock-openai",
56+scenarioIds: ["channel-chat-baseline"],
57+startedAt: "2026-05-30T00:00:00.000Z",
58+},
59+scenarios: [{ name: "channel-chat-baseline", status: "pass", steps: [] }],
60+};
61+}
62+4463it("stops parsing options after the argument terminator", () => {
4564expect(parseArgs(["--plugin", "telegram", "--", "--plugin", "discord"])).toMatchObject({
4665pluginIds: ["telegram"],
@@ -645,6 +664,9 @@ describe("plugin gateway gauntlet helpers", () => {
645664646665it("carries bounded build ids into QA run-node chunks", async () => {
647666const outputDir = path.join(repoRoot, "artifacts");
667+const qaSummaryJson = JSON.stringify(
668+minimalQaSuiteSummary({ gatewayCpuCoreRatio: 0, wallMs: 1 }),
669+);
648670await writeManifest("alpha", "openclaw.plugin.json", JSON.stringify({ id: "alpha" }));
649671await fs.writeFile(path.join(repoRoot, "extensions", "alpha", "index.ts"), "export {};\n");
650672await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
@@ -657,7 +679,7 @@ describe("plugin gateway gauntlet helpers", () => {
657679"const outputDir = path.resolve(process.cwd(), process.argv[outputArgIndex + 1]);",
658680"fs.mkdirSync(outputDir, { recursive: true });",
659681'fs.writeFileSync(path.join(outputDir, "env.txt"), process.env.OPENCLAW_BUNDLED_PLUGIN_BUILD_IDS ?? "", "utf8");',
660-'fs.writeFileSync(path.join(outputDir, "qa-suite-summary.json"), JSON.stringify({ metrics: { wallMs: 1, gatewayCpuCoreRatio: 0 } }), "utf8");',
682+`fs.writeFileSync(path.join(outputDir, "qa-suite-summary.json"), ${JSON.stringify(qaSummaryJson)}, "utf8");`,
661683].join("\n"),
662684"utf8",
663685);
@@ -689,4 +711,125 @@ describe("plugin gateway gauntlet helpers", () => {
689711fs.readFile(path.join(outputDir, "qa-suite", "chunk-00", "env.txt"), "utf8"),
690712).resolves.toBe("alpha,qa-channel,qa-lab,qa-matrix");
691713});
714+715+it("fails successful QA chunks that do not write the requested summary", async () => {
716+const outputDir = path.join(repoRoot, "artifacts");
717+await writeManifest("alpha", "openclaw.plugin.json", JSON.stringify({ id: "alpha" }));
718+await fs.writeFile(path.join(repoRoot, "extensions", "alpha", "index.ts"), "export {};\n");
719+await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
720+await fs.writeFile(
721+path.join(repoRoot, "scripts", "run-node.mjs"),
722+[
723+'import fs from "node:fs";',
724+'import path from "node:path";',
725+'const outputArgIndex = process.argv.indexOf("--output-dir");',
726+"const outputDir = path.resolve(process.cwd(), process.argv[outputArgIndex + 1]);",
727+"fs.mkdirSync(outputDir, { recursive: true });",
728+'console.log("fake QA completed without writing qa-suite-summary.json");',
729+].join("\n"),
730+"utf8",
731+);
732+733+const result = spawnSync(
734+process.execPath,
735+[
736+path.resolve("scripts/check-plugin-gateway-gauntlet.mjs"),
737+"--repo-root",
738+repoRoot,
739+"--output-dir",
740+outputDir,
741+"--skip-prebuild",
742+"--skip-lifecycle",
743+"--skip-slash-help",
744+"--plugin",
745+"alpha",
746+"--qa-scenario",
747+"channel-chat-baseline",
748+],
749+{
750+cwd: path.resolve("."),
751+encoding: "utf8",
752+},
753+);
754+755+expect(result.status, result.stdout).toBe(1);
756+expect(result.stdout).toContain("diagnostic=qa-summary-missing");
757+const summary = JSON.parse(
758+await fs.readFile(path.join(outputDir, "plugin-gateway-gauntlet-summary.json"), "utf8"),
759+);
760+expect(summary.failures).toEqual([
761+expect.objectContaining({
762+diagnosticFailure: "qa-summary-missing",
763+phase: "qa:rpc",
764+pluginId: "alpha",
765+status: 0,
766+}),
767+]);
768+expect(summary.rows[0]).toEqual(
769+expect.objectContaining({
770+diagnosticFailure: "qa-summary-missing",
771+qaSummaryPath: path.join(outputDir, "qa-suite", "chunk-00", "qa-suite-summary.json"),
772+}),
773+);
774+expect(summary.isolatedRunRootPreserved).toBe(true);
775+await fs.rm(summary.isolatedRunRoot, { recursive: true, force: true });
776+});
777+778+it("fails successful QA chunks that write unusable summary JSON", async () => {
779+const outputDir = path.join(repoRoot, "artifacts");
780+await writeManifest("alpha", "openclaw.plugin.json", JSON.stringify({ id: "alpha" }));
781+await fs.writeFile(path.join(repoRoot, "extensions", "alpha", "index.ts"), "export {};\n");
782+await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true });
783+await fs.writeFile(
784+path.join(repoRoot, "scripts", "run-node.mjs"),
785+[
786+'import fs from "node:fs";',
787+'import path from "node:path";',
788+'const outputArgIndex = process.argv.indexOf("--output-dir");',
789+"const outputDir = path.resolve(process.cwd(), process.argv[outputArgIndex + 1]);",
790+"fs.mkdirSync(outputDir, { recursive: true });",
791+'fs.writeFileSync(path.join(outputDir, "qa-suite-summary.json"), "{}", "utf8");',
792+].join("\n"),
793+"utf8",
794+);
795+796+const result = spawnSync(
797+process.execPath,
798+[
799+path.resolve("scripts/check-plugin-gateway-gauntlet.mjs"),
800+"--repo-root",
801+repoRoot,
802+"--output-dir",
803+outputDir,
804+"--skip-prebuild",
805+"--skip-lifecycle",
806+"--skip-slash-help",
807+"--plugin",
808+"alpha",
809+"--qa-scenario",
810+"channel-chat-baseline",
811+],
812+{
813+cwd: path.resolve("."),
814+encoding: "utf8",
815+},
816+);
817+818+expect(result.status, result.stdout).toBe(1);
819+expect(result.stdout).toContain("diagnostic=qa-summary-invalid");
820+const summary = JSON.parse(
821+await fs.readFile(path.join(outputDir, "plugin-gateway-gauntlet-summary.json"), "utf8"),
822+);
823+expect(summary.failures).toEqual([
824+expect.objectContaining({
825+diagnosticDetail: "QA suite summary missing scenarios array",
826+diagnosticFailure: "qa-summary-invalid",
827+phase: "qa:rpc",
828+pluginId: "alpha",
829+status: 0,
830+}),
831+]);
832+expect(summary.isolatedRunRootPreserved).toBe(true);
833+await fs.rm(summary.isolatedRunRoot, { recursive: true, force: true });
834+});
692835});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。