openclaw/test/scripts/openclaw-performance-git-lifecycle.test.ts
Peter Steinberger 2a53c32dc2
test(channels,infra,sdk): remove low-value tests (batch d016) (#158764)
* test(signal): deslop s080 tests

* test(node-host): deslop s085 tests

* test(imessage): deslop s084 tests

* test(scripts): deslop s075 tests

* test(flows): deslop s089 tests

* test(slack): deslop s087 tests

* test(mattermost): deslop s091 tests

* test(infra): deslop s086 tests

* test(plugin-sdk): deslop s090 tests

* test(voice-call): deslop s092 tests

* style(test): format batch d016 replay

* test(mattermost): preserve retained reaction regression

* test: retain independent contracts in batch d016

* test(plugin-sdk): avoid shadowed table labels

* test(node-host): remove unused runtime test type
2026-09-26 10:51:39 +00:00

405 lines
15 KiB
TypeScript

import { beforeAll, expect, it, vi } from "vitest";
import { runCiGitStep } from "./ci-git-owner.test-support.js";
import type { PerformanceFixtureOptions } from "./openclaw-performance-workflow.test-support.js";
// Each case owns its checkout and process trees. Overlap their real timeout and
// drain waits while keeping subprocess pressure bounded.
beforeAll(() => {
vi.setConfig({ maxConcurrency: 2 });
return () => vi.resetConfig();
});
// Performance jobs run on Ubuntu. Exercise their POSIX bodies here; the shared
// ci-platform-checkout suite owns native Windows Job Object proof.
const posixIt = it.skipIf(process.platform === "win32").concurrent;
const steps = {
target: ["resolve_target", "Resolve OpenClaw target ref"],
record: ["source_performance", "Record source performance revision"],
tested: ["kova", "Record tested revision"],
kova: ["kova", "Install OCM and Kova"],
baseline: ["source_performance", "Fetch previous source performance baseline"],
prepare: ["publish", "Prepare clawgrit report commit"],
publish: ["publish", "Publish to clawgrit reports"],
} as const;
function performanceRun(
mode: PerformanceFixtureOptions["mode"],
options: Partial<Parameters<typeof runCiGitStep>[0]> = {},
) {
const [job, step] = steps[mode];
return runCiGitStep({
workflow: { file: ".github/workflows/openclaw-performance.yml", job, step },
fetchResults: [],
performance: { mode },
...options,
});
}
// The previous semantic tests used short-lived stubs or replayed Git by hand.
// These actual workflow bodies must drain real parent/child/grandchild writers
// before every command, output, consumer and exit, while a sentinel stays alive.
posixIt.each(Object.keys(steps) as PerformanceFixtureOptions["mode"][])(
"Performance %s drains Git trees before every continuation",
async (mode) => {
const report = await performanceRun(mode);
expect(report.code, report.output).toBe(0);
expect(report.readyAttempts.length).toBeGreaterThan(0);
if (mode === "prepare") {
expect(report.githubOutput).toContain("ready=true\n");
}
if (mode === "publish") {
expect(report.pushes).toHaveLength(1);
expect(report.fetches).toHaveLength(0);
expect(report.githubSummary).toContain("### Clawgrit report published");
}
},
55_000,
);
posixIt.each([23, 125])(
"baseline ordinary fetch %s is advisory after extinction",
async (code) => {
const report = await performanceRun("baseline", { fetchResults: [code, code, code] });
expect(report.code, report.output).toBe(0);
expect(report.githubSummary).toBe(
"No previous source performance baseline could be fetched.\n",
);
expect(report.githubEnv).toBe("");
expect(report.commands.at(-1)?.args[0]).toBe("fetch");
},
55_000,
);
posixIt.each(["absent", "invalid", "trailing-newline"] as const)(
"baseline %s pointer preserves advisory result",
async (baseline) => {
const report = await performanceRun("baseline", {
performance: { mode: "baseline", baseline },
});
expect(report.code, report.output).toBe(0);
expect(report.githubSummary).toContain(
baseline === "absent"
? "No previous source performance baseline exists"
: "Previous source performance baseline pointer is invalid.",
);
expect(report.githubEnv).toBe("");
expect(report.checkouts).toHaveLength(0);
},
55_000,
);
posixIt.each(["ls-tree", "show"])(
"baseline %s failure never becomes absence or invalid JSON",
async (operation) => {
const report = await performanceRun("baseline", {
gitFault: { match: `^${operation} `, code: 128 },
});
expect(report.code, report.output).toBe(128);
expect(report.githubSummary).toBe("");
expect(report.githubEnv).toBe("");
expect(report.commands.at(-1)?.args[0]).toBe(operation);
},
55_000,
);
const terminalCases = [
...["rev-parse"].map((operation) => ({ mode: "target" as const, operation })),
{ mode: "record" as const, operation: "rev-parse" },
{ mode: "tested" as const, operation: "rev-parse" },
...["fetch", "checkout"].map((operation) => ({ mode: "kova" as const, operation })),
...["fetch", "ls-tree", "show", "sparse-checkout init", "sparse-checkout set", "checkout"].map(
(operation) => ({ mode: "baseline" as const, operation }),
),
...["fetch", "ls-tree", "diff"].map((operation) => ({ mode: "prepare" as const, operation })),
...[
"config",
"push",
"fetch",
"ls-tree",
"checkout",
"cherry-pick -X",
"cherry-pick --abort",
"rev-parse",
].map((operation) => ({ mode: "publish" as const, operation })),
];
// Every injected lifecycle failure must stop at its command, before later policy actions.
posixIt.each(
terminalCases.flatMap((entry) =>
(["cleanup-failure", "cancel"] as const).map((code) => ({
mode: entry.mode,
operation: entry.operation,
code,
})),
),
)(
"$mode $operation $code fences every later action",
async ({ mode, operation, code }) => {
const report = await performanceRun(mode, {
gitFault: { match: `^${operation}(?: |$)`, code },
...(mode === "publish" && operation !== "config" && operation !== "push"
? { pushResults: [23] }
: {}),
...(operation === "cherry-pick --abort"
? { gitFaults: [{ match: "^cherry-pick -X ", code: 23 }] }
: {}),
});
expect(report.code, report.output).toBe(code === "cancel" ? 143 : 125);
expect(report.commands.at(-1)?.args.join(" ")).toMatch(new RegExp(`^${operation}(?: |$)`));
expect(report.githubSummary).toBe("");
expect(report.githubOutput).not.toMatch(/ready=true|already_published=|report_url=/u);
expect(report.githubEnv).not.toContain("SOURCE_PERF_BASELINE_DIR=");
expect(report.output).not.toMatch(
/Unable to replay|Unable to refresh|No previous|Published report:/u,
);
const failedPush = mode === "publish" && operation === "push";
if (failedPush) {
expect(report.fetches).toHaveLength(0);
expect(report.output).not.toContain("fixture backoff:");
}
},
55_000,
);
posixIt.each(["hang", 23, 125] as const)(
"prepare initial fetch %s cannot reach checkout, commit or token readiness",
async (failure) => {
const report = await performanceRun("prepare", { fetchResults: [failure, failure, failure] });
expect(report.code, report.output).toBe(1);
expect(report.fetches).toHaveLength(3);
expect(report.fetches.every(({ args }) => args.includes("--depth=1"))).toBe(true);
expect(report.checkouts).toHaveLength(0);
expect(report.commands.at(-1)?.args[0]).toBe("fetch");
expect(report.githubOutput).toBe("ready=false\n");
expect(report.githubSummary).toBe("");
},
55_000,
);
posixIt(
"initial duplicate is verified before token or push",
async () => {
const report = await performanceRun("prepare", {
performance: { mode: "prepare", duplicate: true },
});
expect(report.code, report.output).toBe(0);
expect(report.githubOutput).toContain("already_published=true\n");
expect(report.githubOutput).toContain("ready=true\n");
expect(report.githubOutput).not.toContain("report_commit=");
expect(report.githubSummary).toContain("### Clawgrit report already published");
expect(report.pushes).toHaveLength(0);
expect(report.commands.some(({ args }) => args[0] === "commit")).toBe(false);
expect(
report.commands.every(
({ envProbe, args }) =>
!JSON.parse(envProbe!).token && JSON.parse(envProbe!).auth === (args[0] === "fetch"),
),
).toBe(true);
},
55_000,
);
posixIt.each([128, 125])(
"prepare duplicate inspection %s is terminal",
async (code) => {
const report = await performanceRun("prepare", { gitFault: { match: "^ls-tree ", code } });
expect(report.code, report.output).toBe(code);
expect(report.githubOutput).toBe("ready=false\n");
expect(report.commands.at(-1)?.args[0]).toBe("ls-tree");
},
55_000,
);
posixIt.each([0, 1, 125])(
"cached diff status %s commits only for ordinary 1",
async (code) => {
const report = await performanceRun("prepare", {
gitFault: { match: "^diff --cached --quiet$", code, output: "" },
});
expect(report.code, report.output).toBe(code > 1 ? code : 0);
expect(report.commands.filter(({ args }) => args[0] === "commit")).toHaveLength(
code === 1 ? 1 : 0,
);
expect(report.githubOutput.includes("ready=true\n")).toBe(code <= 1);
},
55_000,
);
posixIt.each([125, "hang"] as const)(
"ambiguous push %s reconciles only after extinction",
async (code) => {
const report = await performanceRun("publish", { pushResults: [code] });
expect(report.code, report.output).toBe(0);
expect(report.pushes).toHaveLength(2);
expect(report.fetches).toHaveLength(1);
expect(report.checkouts).toHaveLength(1);
expect(report.output.match(/fixture backoff: \d+/gu)).toEqual(["fixture backoff: 2"]);
expect(report.githubSummary).toContain("### Clawgrit report published");
expect(report.performance?.config).not.toContain("AUTHORIZATION");
for (const command of report.commands) {
const scope = JSON.parse(command.envProbe!);
expect(scope.token).toBe(false);
const authenticated = [
"push",
"fetch",
"ls-tree",
"checkout",
"cherry-pick",
"rev-parse",
].includes(command.args[0]!);
expect(scope.auth).toBe(authenticated);
if (scope.auth) {
expect(scope).toMatchObject({ count: "3", prompt: "0" });
}
if (command.args[0] === "push") {
expect(command.configuration).toContain("core.hooksPath=/dev/null");
}
}
expect(JSON.stringify(report)).not.toContain("fixture-performance-token");
expect(JSON.stringify(report)).not.toContain(
Buffer.from("x-access-token:fixture-performance-token").toString("base64"),
);
},
55_000,
);
posixIt.each([false, true])(
"five failed pushes always get five fetches (fetch fails=%s)",
async (fetchFails) => {
const report = await performanceRun("publish", {
pushResults: [23, 23, 23, 23, 23],
fetchResults: fetchFails ? [23, 23, 23, 23, 23] : [],
});
expect(report.code, report.output).toBe(1);
expect(report.pushes).toHaveLength(5);
expect(report.boundaries.filter(({ name }) => name === "backoff")).toHaveLength(5);
expect(report.fetches.map(({ args }) => args)).toEqual(
Array.from({ length: 5 }, () => ["fetch", "--depth=1", "origin", "main"]),
);
expect(report.output.match(/fixture backoff: \d+/gu)).toEqual(
[2, 4, 6, 8, 10].map((seconds) => `fixture backoff: ${seconds}`),
);
expect(report.checkouts).toHaveLength(fetchFails ? 0 : 4);
expect(report.commands.filter(({ args }) => args[0] === "cherry-pick")).toHaveLength(
fetchFails ? 0 : 4,
);
expect(report.output.match(/::warning::Unable to refresh/gu) ?? []).toHaveLength(
fetchFails ? 4 : 0,
);
expect(report.githubSummary).toContain("failed after 5 attempts.");
expect(report.githubSummary).not.toContain("Published report:");
},
55_000,
);
posixIt.each([1, 5])(
"remote duplicate after ambiguous attempt %s succeeds without replay",
async (attempt) => {
const report = await performanceRun("publish", {
performance: { mode: "publish", remoteDuplicateAttempt: attempt },
pushResults: Array.from({ length: attempt }, () => 124),
});
expect(report.code, report.output).toBe(0);
expect(report.pushes).toHaveLength(attempt);
expect(report.fetches).toHaveLength(attempt);
expect(report.checkouts).toHaveLength(attempt - 1);
expect(report.githubSummary).toContain("### Clawgrit report published");
},
55_000,
);
posixIt.each([23, 125])(
"ordinary cherry-pick and abort %s failures remain visible publish failure",
async (code) => {
const report = await performanceRun("publish", {
pushResults: [23],
gitFaults: [
{ match: "^cherry-pick -X ", code: 23 },
{ match: "^cherry-pick --abort$", code },
],
});
expect(report.code, report.output).toBe(1);
expect(report.commands.at(-1)?.args).toEqual(["cherry-pick", "--abort"]);
expect(report.pushes).toHaveLength(1);
expect(report.output).toContain(
"::error::Unable to replay the clawgrit report after a concurrent publish.",
);
expect(report.githubSummary).toBe("");
},
55_000,
);
posixIt.each(["owner", "python", "git"] as const)(
"prepare setup failure %s cannot publish readiness",
async (setupFailure) => {
const report = await performanceRun("prepare", { setupFailure });
expect(report.code, report.output).not.toBe(0);
expect(report.pushes).toHaveLength(0);
expect(report.githubOutput).not.toContain("ready=true");
},
55_000,
);
posixIt.each([125, "hang"] as const)(
"reconciliation fetch %s warns once and retries without replay",
async (code) => {
const report = await performanceRun("publish", { pushResults: [23], fetchResults: [code] });
expect(report.code, report.output).toBe(0);
expect(report.pushes).toHaveLength(2);
expect(report.fetches).toHaveLength(1);
expect(report.checkouts).toHaveLength(0);
expect(report.output.match(/::warning::Unable to refresh/gu)).toHaveLength(1);
},
55_000,
);
posixIt.each([125, 128])(
"remote duplicate read %s is terminal, never absence",
async (code) => {
const report = await performanceRun("publish", {
pushResults: [23],
gitFault: { match: "^ls-tree ", code },
});
expect(report.code, report.output).toBe(code);
expect(report.commands.at(-1)?.args[0]).toBe("ls-tree");
expect(report.githubSummary).toBe("");
expect(report.checkouts).toHaveLength(0);
},
55_000,
);
posixIt.each(["prepare", "publish"] as const)(
"%s cancellation during real TERM-resistant cleanup prevents continuation",
async (mode) => {
const report = await performanceRun(mode, {
cancelDuringCleanup: true,
cleanupCancelMatch: mode === "prepare" ? "^fetch " : "^push ",
...(mode === "prepare" ? { fetchResults: ["hang"] } : { pushResults: ["hang"] }),
});
expect(report.cancelledDuringCleanup).toBe(true);
expect(report.code, report.output).toBe(143);
expect(report.commands.at(-1)?.args[0]).toBe(mode === "prepare" ? "fetch" : "push");
expect(report.githubOutput).not.toContain("ready=true");
expect(report.githubSummary).toBe("");
expect(report.output).not.toContain("fixture backoff:");
},
55_000,
);
posixIt(
"cancellation during owned backoff prevents reconciliation fetch",
async () => {
const report = await performanceRun("publish", {
pushResults: [23],
realClock: true,
cooperativeTrees: true,
cancelDuringBackoff: true,
});
expect(report.code, report.output).toBe(143);
expect(report.pushes).toHaveLength(1);
expect(report.fetches).toHaveLength(0);
expect(report.githubSummary).toBe("");
expect(report.boundaries.some(({ name }) => name === "backoff-cancel")).toBe(true);
},
55_000,
);