mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-23 07:28:22 +00:00
* test(update-cli): deslop x7402 tests * test(update-cli): deslop x7402 tests * test(update-cli): deslop x7402 tests * test(outbound): deslop x7405 tests * test(outbound): deslop x7405 tests * test(memory-core): deslop x7380 tests * test(gateway): deslop x7407 tests * test(gateway): deslop x7407 tests * test(gateway): deslop x7407 tests * test(cron): deslop x7406 tests * test(cron): deslop x7406 tests * test(cron): deslop x7406 tests * test(plugins): deslop x7411 tests * test(plugins): deslop x7411 tests * test(channels): deslop x7378 tests * test(system-agent): deslop x7412 tests * test(system-agent): deslop x7412 tests * test(system-agent): deslop x7412 tests * test(agents): deslop x7415 tests * test(agents): deslop x7415 tests * test(plugins): deslop x7413 tests * test(plugins): further deslop x7413 tests * test(plugins): consolidate x7413 artifact fixtures * test(sqlite): deslop x7410 tests * test(sqlite): deslop x7410 tests * test(sqlite): deslop x7410 tests * test(agents): deslop x7409 tests * test(agents): deslop x7409 tests * test(agents): deslop x7409 tests * test(gateway): keep retained config scoping fixture explicit * test: repair CI fixtures and retain guarded mock factories * test: retain current fixture contracts in campaign replay * test(system-agent): reuse complete config snapshot fixture * test(agents): retain messaging target tool identity assertion
521 lines
18 KiB
TypeScript
521 lines
18 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { runCiGitStep } from "./ci-git-owner.test-support.js";
|
|
|
|
// Plugin publication jobs run on Ubuntu. Native Job Object and POSIX owner
|
|
// behavior stays in the shared ci-platform-checkout suites.
|
|
const posixIt = it.skipIf(process.platform === "win32");
|
|
const sha = "a".repeat(40);
|
|
const workflowSha = "b".repeat(40);
|
|
const releaseTag = "v2026.8.1";
|
|
const alphaBranch = "tideclaw/alpha/2026-08-30-1200Z";
|
|
const packageDir = "extensions/fixture";
|
|
const packageJson = '{"name":"@openclaw/fixture","version":"2026.8.33"}\n';
|
|
const monthlyBranch = "extended-stable/2026.8.33";
|
|
const mainMiss = { "merge-base --is-ancestor HEAD origin/main": { code: 1 } };
|
|
const releaseMiss = {
|
|
...mainMiss,
|
|
"for-each-ref --format=%(refname) refs/remotes/origin/release": { code: 0, output: "" },
|
|
};
|
|
const fetchBranches = [
|
|
"fetch",
|
|
"--no-tags",
|
|
"origin",
|
|
"+refs/heads/main:refs/remotes/origin/main",
|
|
"+refs/heads/release/*:refs/remotes/origin/release/*",
|
|
];
|
|
const protectedReleaseEnv = {
|
|
RELEASE_PUBLISH_RUN_ATTEMPT: "2",
|
|
RELEASE_PUBLISH_RUN_ID: "123",
|
|
RELEASE_TAG: releaseTag,
|
|
TARGET_SHA: sha,
|
|
WORKFLOW_REF: "refs/tags/release-publish/abcdef123456-2",
|
|
WORKFLOW_SHA: workflowSha,
|
|
};
|
|
const preflightEnv = {
|
|
PREFLIGHT_ONLY: "true",
|
|
NPM_DIST_TAG: "extended-stable",
|
|
PUBLISH_SCOPE: "all-publishable",
|
|
SOURCE_REF: sha,
|
|
};
|
|
|
|
type PluginMode = keyof typeof modes;
|
|
type RunOptions = Partial<Parameters<typeof runCiGitStep>[0]>;
|
|
|
|
function modeConfig(
|
|
registry: "npm" | "clawhub",
|
|
step: string,
|
|
env: Record<string, string> = {},
|
|
job = `preview_plugins_${registry}`,
|
|
) {
|
|
return { workflow: { file: `.github/workflows/plugin-${registry}-release.yml`, job, step }, env };
|
|
}
|
|
|
|
const modes = {
|
|
"clawhub-resolve": modeConfig("clawhub", "Resolve checked-out ref", {
|
|
RELEASE_TAG: "",
|
|
TARGET_REF: "",
|
|
}),
|
|
"clawhub-oidc": modeConfig("clawhub", "Validate OIDC source matches workflow ref", {
|
|
DRY_RUN: "false",
|
|
RELEASE_PUBLISH_RUN_ATTEMPT: "",
|
|
RELEASE_PUBLISH_RUN_ID: "",
|
|
RELEASE_TAG: "",
|
|
TARGET_SHA: sha,
|
|
WORKFLOW_REF: "refs/heads/main",
|
|
WORKFLOW_SHA: sha,
|
|
}),
|
|
"clawhub-trust": modeConfig("clawhub", "Validate ref is on a trusted publish branch", {
|
|
TRUSTED_PUBLISH_BRANCH: "main",
|
|
}),
|
|
"npm-resolve": modeConfig("npm", "Resolve checked-out ref"),
|
|
"npm-trust": modeConfig("npm", "Validate ref is on a trusted publish branch", {
|
|
RELEASE_CANDIDATE_BRANCH: "",
|
|
NPM_DIST_TAG: "default",
|
|
PREFLIGHT_ONLY: "false",
|
|
// Ancestry cases isolate Git policy; tag admission is asserted with it enabled.
|
|
REQUIRE_NPM_PUBLISH_ENVIRONMENT: "false",
|
|
PREPARED_ARTIFACT: "",
|
|
PUBLISH_SCOPE: "selected",
|
|
RELEASE_PLUGINS: "",
|
|
RELEASE_PUBLISH_RUN_ATTEMPT: "",
|
|
RELEASE_PUBLISH_RUN_ID: "",
|
|
SOURCE_REF: sha,
|
|
TRUSTED_PUBLISHER_PREFLIGHT: "false",
|
|
WORKFLOW_REF: "refs/heads/main",
|
|
WORKFLOW_SHA: workflowSha,
|
|
}),
|
|
"npm-preflight-read": modeConfig(
|
|
"npm",
|
|
"Read exact npm preflight source package",
|
|
{ EXTENSION_ID: "fixture", PACKAGE_DIR: packageDir, SOURCE_SHA: sha },
|
|
"verify_plugin_npm_preflight",
|
|
),
|
|
"npm-publish-read": modeConfig(
|
|
"npm",
|
|
"Read exact npm publication source package",
|
|
{ EXTENSION_ID: "fixture", PACKAGE_DIR: packageDir, TARGET_SHA: sha },
|
|
"publish_plugins_npm",
|
|
),
|
|
};
|
|
|
|
function pluginRun(signal: AbortSignal, mode: PluginMode, options: RunOptions = {}) {
|
|
const selected = modes[mode];
|
|
return runCiGitStep({
|
|
signal,
|
|
workflow: selected.workflow,
|
|
fetchResults: [],
|
|
...options,
|
|
env: { ...selected.env, ...options.env },
|
|
revisions: { HEAD: sha, ...options.revisions },
|
|
});
|
|
}
|
|
|
|
function gitCommands(report: Awaited<ReturnType<typeof pluginRun>>) {
|
|
return report.commands.filter(({ tool }) => tool === "git").map(({ args }) => args);
|
|
}
|
|
|
|
posixIt.for([
|
|
["clawhub-resolve", [fetchBranches, ["rev-parse", "HEAD"]], `sha=${sha}\n`],
|
|
["npm-resolve", [["rev-parse", "HEAD"]], `sha=${sha}\n`],
|
|
["clawhub-trust", [["merge-base", "--is-ancestor", "HEAD", "origin/main"]], ""],
|
|
["npm-trust", [fetchBranches, ["merge-base", "--is-ancestor", "HEAD", "origin/main"]], ""],
|
|
] as const)(
|
|
"%s drains every Git tree before success or output",
|
|
{ timeout: 55_000 },
|
|
async ([mode, commands, output], { signal }) => {
|
|
const report = await pluginRun(signal, mode);
|
|
expect(report.code, report.output).toBe(0);
|
|
expect(gitCommands(report)).toEqual(commands);
|
|
expect(report.githubOutput).toBe(output);
|
|
expect(report.readyAttempts).toHaveLength(commands.length);
|
|
if (output) {
|
|
expect(report.boundaries.some(({ name }) => name === "output")).toBe(true);
|
|
}
|
|
},
|
|
);
|
|
|
|
posixIt.for(
|
|
(["npm-preflight-read", "npm-publish-read"] as const).flatMap((mode) =>
|
|
[0, 23].map((code) => ({ mode, code })),
|
|
),
|
|
)(
|
|
"$mode admits exact source bytes only after a successful show (exit $code)",
|
|
{ timeout: 55_000 },
|
|
async ({ mode, code }, { signal }) => {
|
|
const sourceRef = mode === "npm-preflight-read" ? "SOURCE_SHA" : "TARGET_SHA";
|
|
const output = code ? "{partial" : packageJson;
|
|
const report = await pluginRun(signal, mode, {
|
|
commandResults: { [`show ${sha}:${packageDir}/package.json`]: { code, output } },
|
|
});
|
|
expect(report, report.output).toMatchObject({
|
|
code,
|
|
pluginSourcePackage: code ? "" : packageJson,
|
|
});
|
|
expect(gitCommands(report)).toEqual([
|
|
["fetch", "--no-tags", "--depth=1", "--filter=blob:none", "origin", sha],
|
|
["show", `${sha}:${packageDir}/package.json`],
|
|
]);
|
|
expect(modes[mode].env[sourceRef]).toBe(sha);
|
|
},
|
|
);
|
|
|
|
posixIt.for(
|
|
(["npm-preflight-read", "npm-publish-read"] as const).flatMap((mode) =>
|
|
([23, 125, "hang"] as const).map((failure) => ({ failure, mode })),
|
|
),
|
|
)(
|
|
"$mode fetch failure $failure stops before source package readback",
|
|
{ timeout: 55_000 },
|
|
async ({ failure, mode }, { signal }) => {
|
|
const report = await pluginRun(signal, mode, { fetchResults: [failure] });
|
|
expect(report.code, report.output).toBe(failure === "hang" ? 124 : failure);
|
|
expect(gitCommands(report).at(-1)?.[0]).toBe("fetch");
|
|
expect(report.pluginSourcePackage).toBe("");
|
|
},
|
|
);
|
|
|
|
posixIt.for([1, 125, 143])(
|
|
"ClawHub resolves origin fallback after safely drained ordinary local probe failure %s",
|
|
{ timeout: 55_000 },
|
|
async (code, { signal }) => {
|
|
const report = await pluginRun(signal, "clawhub-resolve", {
|
|
env: { TARGET_REF: "release/fixture" },
|
|
commandResults: {
|
|
"rev-parse --verify --quiet release/fixture^{commit}": { code, output: "" },
|
|
},
|
|
revisions: { "origin/release/fixture^{commit}": sha },
|
|
});
|
|
expect(report.code, report.output).toBe(0);
|
|
expect(gitCommands(report)).toEqual([
|
|
fetchBranches,
|
|
["rev-parse", "--verify", "--quiet", "release/fixture^{commit}"],
|
|
["rev-parse", "--verify", "--quiet", "origin/release/fixture^{commit}"],
|
|
["rev-parse", "origin/release/fixture^{commit}"],
|
|
["checkout", "--detach", sha],
|
|
["rev-parse", "HEAD"],
|
|
]);
|
|
expect(report.githubOutput).toBe(`sha=${sha}\n`);
|
|
},
|
|
);
|
|
|
|
posixIt(
|
|
"ClawHub release tags retain their second bounded fetch",
|
|
async ({ signal }) => {
|
|
const report = await pluginRun(signal, "clawhub-resolve", {
|
|
env: { RELEASE_TAG: releaseTag },
|
|
});
|
|
expect(report.code, report.output).toBe(0);
|
|
expect(report.fetches.map(({ args }) => args)).toEqual([
|
|
fetchBranches,
|
|
["fetch", "--no-tags", "origin", `+refs/tags/${releaseTag}:refs/tags/${releaseTag}`],
|
|
]);
|
|
},
|
|
55_000,
|
|
);
|
|
|
|
posixIt.for([0, 1, 125])(
|
|
"ClawHub protected tooling binds the exact peeled tag (lookup exit %s)",
|
|
{ timeout: 55_000 },
|
|
async (code, { signal }) => {
|
|
const report = await pluginRun(signal, "clawhub-oidc", {
|
|
env: protectedReleaseEnv,
|
|
...(code
|
|
? { commandResults: { [`rev-parse ${releaseTag}^{commit}`]: { code } } }
|
|
: { revisions: { [`${releaseTag}^{commit}`]: sha } }),
|
|
});
|
|
expect(report.code, report.output).toBe(code ? 1 : 0);
|
|
expect(gitCommands(report)).toEqual([["rev-parse", `${releaseTag}^{commit}`]]);
|
|
expect(report.output).toContain(
|
|
code
|
|
? "Plugin ClawHub OIDC publish target is not bound to protected tooling and the exact release tag."
|
|
: "Protected release tooling is publishing the exact immutable release tag target.",
|
|
);
|
|
},
|
|
);
|
|
|
|
posixIt.for(["clawhub-trust", "npm-trust"] as const)(
|
|
"%s accepts a release branch only after successful enumeration",
|
|
{ timeout: 55_000 },
|
|
async (mode, { signal }) => {
|
|
const ref = "refs/remotes/origin/release/2026.8.1";
|
|
const report = await pluginRun(signal, mode, {
|
|
commandResults: {
|
|
...mainMiss,
|
|
"for-each-ref --format=%(refname) refs/remotes/origin/release": {
|
|
code: 0,
|
|
output: `${ref}\n`,
|
|
},
|
|
[`merge-base --is-ancestor HEAD ${ref}`]: { code: 0 },
|
|
},
|
|
});
|
|
expect(report.code, report.output).toBe(0);
|
|
expect(gitCommands(report).at(-1)).toEqual(["merge-base", "--is-ancestor", "HEAD", ref]);
|
|
},
|
|
);
|
|
|
|
posixIt.for([false, true])(
|
|
"npm-trust rejects retired alpha ancestry (preflight=%s)",
|
|
{ timeout: 55_000 },
|
|
async (preflight, { signal }) => {
|
|
const report = await pluginRun(signal, "npm-trust", {
|
|
env: {
|
|
WORKFLOW_REF: `refs/heads/${alphaBranch}`,
|
|
...(preflight ? { PREFLIGHT_ONLY: "true", SOURCE_REF: sha } : {}),
|
|
},
|
|
...(preflight ? { revisions: { [`${sha}^{commit}`]: sha } } : {}),
|
|
commandResults: releaseMiss,
|
|
});
|
|
expect(report.code, report.output).toBe(1);
|
|
expect(report.output).toContain(
|
|
preflight
|
|
? "Plugin npm preflight target must be reachable from main or release/*."
|
|
: "Plugin npm publishes must target a commit reachable from main or release/*.",
|
|
);
|
|
if (preflight) {
|
|
expect(report.fetches).toHaveLength(1);
|
|
expect(report.output).not.toContain("matching Tideclaw alpha branch");
|
|
} else {
|
|
expect(report.fetches.some(({ args }) => args.join(" ").includes(alphaBranch))).toBe(false);
|
|
}
|
|
},
|
|
);
|
|
|
|
posixIt(
|
|
"clawhub-trust rejects a retired Tideclaw alpha branch before ancestry admission",
|
|
async ({ signal }) => {
|
|
const report = await pluginRun(signal, "clawhub-trust", {
|
|
env: { TRUSTED_PUBLISH_BRANCH: alphaBranch },
|
|
commandResults: {
|
|
...releaseMiss,
|
|
[`merge-base --is-ancestor HEAD refs/remotes/origin/${alphaBranch}`]: { code: 0 },
|
|
},
|
|
});
|
|
expect(report.code, report.output).toBe(1);
|
|
expect(report.output).toContain("Alpha releases are retired;");
|
|
expect(report.fetches).toEqual([]);
|
|
},
|
|
55_000,
|
|
);
|
|
|
|
posixIt.for([
|
|
["canonical branch", `refs/heads/${monthlyBranch}`, sha, 0],
|
|
["main branch", "refs/heads/main", sha, 0],
|
|
["moved canonical tip", "refs/heads/main", "c".repeat(40), 1],
|
|
["untrusted workflow branch", "refs/heads/topic", sha, 1],
|
|
["same-name main tag", "refs/tags/main", sha, 1],
|
|
] as const)(
|
|
"npm extended-stable preflight: %s",
|
|
{ timeout: 55_000 },
|
|
async ([_name, workflowRef, branchSha, code], { signal }) => {
|
|
const branch = monthlyBranch;
|
|
const report = await pluginRun(signal, "npm-trust", {
|
|
env: { ...preflightEnv, WORKFLOW_REF: workflowRef },
|
|
revisions: {
|
|
[`${sha}^{commit}`]: sha,
|
|
...(code === 0 ? { [`refs/heads/${branch}`]: sha } : {}),
|
|
[`refs/remotes/origin/${branch}`]: branchSha,
|
|
},
|
|
});
|
|
expect(report.code, report.output).toBe(code);
|
|
if (code === 0) {
|
|
expect(report.fetches.map(({ args }) => args)).toEqual([
|
|
["fetch", "--no-tags", "origin", `+refs/heads/${branch}:refs/remotes/origin/${branch}`],
|
|
]);
|
|
// Preflight checks its exact source before the extended-stable tip.
|
|
expect(gitCommands(report).filter(([operation]) => operation === "rev-parse")).toHaveLength(
|
|
6,
|
|
);
|
|
} else {
|
|
expect(report.output).toContain("Extended-stable plugin");
|
|
}
|
|
},
|
|
);
|
|
|
|
const candidateAdmissionCases: Array<{
|
|
name: string;
|
|
env?: RunOptions["env"];
|
|
commands?: RunOptions["commandResults"];
|
|
code?: number;
|
|
message?: string;
|
|
}> = [
|
|
{ name: "qualified protected tooling", code: 0 },
|
|
{
|
|
name: "wrong candidate month",
|
|
env: { RELEASE_CANDIDATE_BRANCH: "extended-stable/2026.7.33" },
|
|
message: "release_candidate_branch must be extended-stable/2026.8.33",
|
|
},
|
|
{
|
|
name: "mutable main tooling",
|
|
env: { WORKFLOW_REF: "refs/heads/main" },
|
|
message: "require a protected release-publish/<sha12>-<n> tooling tag",
|
|
},
|
|
{
|
|
name: "tooling outside main",
|
|
commands: { [`merge-base --is-ancestor ${workflowSha} origin/main`]: { code: 1 } },
|
|
message: "workflow revision is not reachable from current main",
|
|
},
|
|
{
|
|
name: "candidate outside monthly branch",
|
|
commands: {
|
|
[`merge-base --is-ancestor HEAD refs/remotes/origin/${monthlyBranch}`]: { code: 1 },
|
|
},
|
|
message: "target must be reachable from extended-stable/2026.8.33",
|
|
},
|
|
{
|
|
name: "tooling ancestry Git failure",
|
|
commands: { [`merge-base --is-ancestor ${workflowSha} origin/main`]: { code: 23 } },
|
|
code: 23,
|
|
},
|
|
{
|
|
name: "preflight candidate override",
|
|
env: { PREFLIGHT_ONLY: "true", REQUIRE_NPM_PUBLISH_ENVIRONMENT: "false" },
|
|
message: "preflight must not include release_candidate_branch",
|
|
},
|
|
{
|
|
name: "non-extended candidate override",
|
|
env: { NPM_DIST_TAG: "default" },
|
|
message: "release_candidate_branch is only valid for extended-stable publication",
|
|
},
|
|
];
|
|
|
|
posixIt.for(candidateAdmissionCases)(
|
|
"npm canonical candidate admission: $name",
|
|
{ timeout: 55_000 },
|
|
async ({ env, commands = {}, code = 1, message }, { signal }) => {
|
|
const report = await pluginRun(signal, "npm-trust", {
|
|
env: {
|
|
NPM_DIST_TAG: "extended-stable",
|
|
PUBLISH_SCOPE: "all-publishable",
|
|
RELEASE_CANDIDATE_BRANCH: "extended-stable/2026.8.33",
|
|
REQUIRE_NPM_PUBLISH_ENVIRONMENT: "true",
|
|
WORKFLOW_REF: `refs/tags/release-publish/${workflowSha.slice(0, 12)}-123`,
|
|
...env,
|
|
},
|
|
revisions: { [`${sha}^{commit}`]: sha },
|
|
commandResults: commands,
|
|
});
|
|
expect(report.code, report.output).toBe(code);
|
|
if (message) {
|
|
expect(report.output).toContain(message);
|
|
}
|
|
if (code === 0) {
|
|
expect(gitCommands(report).slice(-2)).toEqual([
|
|
["merge-base", "--is-ancestor", workflowSha, "origin/main"],
|
|
["merge-base", "--is-ancestor", "HEAD", "refs/remotes/origin/extended-stable/2026.8.33"],
|
|
]);
|
|
}
|
|
},
|
|
);
|
|
|
|
posixIt.for(
|
|
(["clawhub-trust", "npm-trust"] as const).flatMap((mode) =>
|
|
["merge-base", "for-each-ref"].map((operation) => ({ mode, operation })),
|
|
),
|
|
)(
|
|
"$mode treats $operation failure as terminal",
|
|
{ timeout: 55_000 },
|
|
async ({ mode, operation }, { signal }) => {
|
|
const report = await pluginRun(signal, mode, {
|
|
commandResults:
|
|
operation === "merge-base"
|
|
? { "merge-base --is-ancestor HEAD origin/main": { code: 23 } }
|
|
: {
|
|
...mainMiss,
|
|
"for-each-ref --format=%(refname) refs/remotes/origin/release": { code: 23 },
|
|
},
|
|
});
|
|
expect(report.code, report.output).toBe(23);
|
|
expect(gitCommands(report).at(-1)?.[0]).toBe(operation);
|
|
if (operation === "merge-base") {
|
|
expect(gitCommands(report)).toHaveLength(mode === "npm-trust" ? 2 : 1);
|
|
}
|
|
expect(report.fetches).toHaveLength(mode === "npm-trust" ? 1 : 0);
|
|
},
|
|
);
|
|
|
|
function terminalCase(
|
|
mode: PluginMode,
|
|
operation: string,
|
|
options: Pick<RunOptions, "env" | "commandResults" | "revisions"> = {},
|
|
match = `^${operation} `,
|
|
) {
|
|
return { mode, operation, match, ...options };
|
|
}
|
|
const sourcePackage = {
|
|
commandResults: { [`show ${sha}:${packageDir}/package.json`]: { code: 0, output: packageJson } },
|
|
};
|
|
const terminalCases = [
|
|
terminalCase("clawhub-resolve", "fetch"),
|
|
terminalCase(
|
|
"clawhub-resolve",
|
|
"rev-parse",
|
|
{
|
|
env: { TARGET_REF: "release/fixture" },
|
|
revisions: { "origin/release/fixture^{commit}": sha },
|
|
},
|
|
"^rev-parse --verify --quiet ",
|
|
),
|
|
terminalCase("clawhub-resolve", "checkout", {
|
|
env: { TARGET_REF: sha },
|
|
revisions: { [`${sha}^{commit}`]: sha },
|
|
}),
|
|
terminalCase("clawhub-oidc", "rev-parse", {
|
|
env: {
|
|
...protectedReleaseEnv,
|
|
RELEASE_PUBLISH_RUN_ATTEMPT: "1",
|
|
RELEASE_PUBLISH_RUN_ID: "1",
|
|
WORKFLOW_REF: "refs/tags/release-publish/abcdef123456-1",
|
|
},
|
|
}),
|
|
terminalCase("clawhub-trust", "merge-base"),
|
|
terminalCase("clawhub-trust", "for-each-ref", { commandResults: mainMiss }),
|
|
terminalCase("npm-resolve", "rev-parse"),
|
|
terminalCase("npm-trust", "fetch"),
|
|
terminalCase("npm-trust", "merge-base"),
|
|
terminalCase("npm-trust", "for-each-ref", { commandResults: mainMiss }),
|
|
terminalCase("npm-preflight-read", "fetch"),
|
|
terminalCase("npm-preflight-read", "show", sourcePackage),
|
|
terminalCase("npm-publish-read", "fetch"),
|
|
terminalCase("npm-publish-read", "show", sourcePackage),
|
|
];
|
|
|
|
posixIt.for(
|
|
terminalCases.flatMap((entry) =>
|
|
(["cleanup-failure", "cancel"] as const).map((failure) =>
|
|
Object.assign({}, entry, { failure }),
|
|
),
|
|
),
|
|
)(
|
|
"$mode $operation $failure fences every later Git/output/consumer boundary",
|
|
{ timeout: 55_000 },
|
|
async ({ commandResults, env, failure, match, mode, operation, revisions }, { signal }) => {
|
|
const report = await pluginRun(signal, mode, {
|
|
commandResults,
|
|
env,
|
|
revisions,
|
|
gitFault: { match, code: failure },
|
|
});
|
|
expect(report.code, report.output).toBe(failure === "cancel" ? 143 : 125);
|
|
expect(gitCommands(report).at(-1)?.[0]).toBe(operation);
|
|
expect(
|
|
gitCommands(report).filter((args) => new RegExp(match).test(args.join(" "))),
|
|
).toHaveLength(1);
|
|
expect(report.githubOutput).toBe("");
|
|
expect(report.commands.some(({ tool }) => ["node", "pnpm"].includes(tool))).toBe(false);
|
|
},
|
|
);
|
|
|
|
posixIt.for(
|
|
(["clawhub-resolve", "npm-resolve", "npm-preflight-read", "npm-publish-read"] as const).flatMap(
|
|
(mode) => (["owner", "python", "git"] as const).map((setupFailure) => ({ mode, setupFailure })),
|
|
),
|
|
)(
|
|
"$mode setup failure $setupFailure cannot publish or consume Git output",
|
|
{ timeout: 55_000 },
|
|
async ({ mode, setupFailure }, { signal }) => {
|
|
const report = await pluginRun(signal, mode, { setupFailure });
|
|
expect(report.code, report.output).not.toBe(0);
|
|
expect(report.githubOutput).toBe("");
|
|
expect(report.commands.some(({ tool }) => ["node", "pnpm"].includes(tool))).toBe(false);
|
|
},
|
|
);
|