perf(test): unit-test crabbox provider aliases

This commit is contained in:
Peter Steinberger
2026-07-06 14:52:53 -04:00
parent a7c4a0471d
commit 2723628dd9
7 changed files with 162 additions and 137 deletions
+1
View File
@@ -169,6 +169,7 @@
"!docs/**/*.jpg",
"!docs/**/*.png",
"src/agents/templates/",
"scripts/crabbox-wrapper-providers.mjs",
"scripts/crabbox-wrapper.mjs",
"patches/",
"skills/",
+93
View File
@@ -0,0 +1,93 @@
const providerAliases = new Map([
["blacksmith", "blacksmith-testbox"],
["cf", "cloudflare"],
["container", "local-container"],
["docker", "local-container"],
["exe", "exe-dev"],
["exedev", "exe-dev"],
["google", "gcp"],
["google-cloud", "gcp"],
["local-docker", "local-container"],
["namespace", "namespace-devbox"],
["namespace-devboxes", "namespace-devbox"],
["rail", "railway"],
["railwayapp", "railway"],
["run-pod", "runpod"],
["runpodio", "runpod"],
["sem", "semaphore"],
["static", "ssh"],
["static-ssh", "ssh"],
["tensorlake-sbx", "tensorlake"],
["tl", "tensorlake"],
]);
// Crabbox providerHelpAll can omit Tensorlake even when the binary accepts it.
const providerHelpOmissions = new Set(["tensorlake"]);
export function canonicalProviderName(provider) {
return providerAliases.get(provider) ?? provider;
}
function addProviderNames(names, text) {
for (const name of text
.replace(/\s+\(default\b.*$/u, "")
.split(/\s*(?:,|\||\bor\b)\s*/u)
.map((s) => s.trim())
.filter(Boolean)) {
if (/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
names.add(name);
}
}
}
function providerListContinuation(line, previousText) {
const match = line.match(
/^\s*((?:or\s+)?[a-z0-9][a-z0-9-]*(?:\s*(?:,|\||\bor\b)\s*(?:or\s+)?[a-z0-9][a-z0-9-]*)*\s*(?:,|\|)?)(?:\s+\(default\b.*)?\s*$/u,
);
if (!match) {
return "";
}
if (/[,|]\s*$/u.test(previousText) || /[,|]|\bor\b|\(default\b/u.test(line)) {
return match[1];
}
return "";
}
export function parseProvidersFromHelp(text) {
const names = new Set();
const lines = text.split(/\r?\n/u);
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
const providerMatch = line.match(/provider:\s*([a-z0-9][a-z0-9, -]*)(?:\s*\(default\b|$)/u);
if (providerMatch) {
let providerText = providerMatch[1];
while (!/\(default\b/u.test(lines[index]) && index + 1 < lines.length) {
const continuation = providerListContinuation(lines[index + 1], providerText);
if (!continuation) {
break;
}
index += 1;
providerText = `${providerText} ${continuation}`;
}
addProviderNames(names, providerText);
continue;
}
const flagMatch = line.match(
/^\s+-{1,2}provider(?:[=\s]+)([a-z0-9][a-z0-9|, -]*)(?:\s{2,}|\s+\(|$)/u,
);
if (flagMatch && /[,|]|\bor\b/u.test(flagMatch[1])) {
addProviderNames(names, flagMatch[1]);
}
}
return [...names];
}
export function isProviderAdvertised(provider, advertisedProviders) {
const canonicalProvider = canonicalProviderName(provider);
return (
advertisedProviders.includes(provider) ||
advertisedProviders.includes(canonicalProvider) ||
providerHelpOmissions.has(canonicalProvider)
);
}
+8 -92
View File
@@ -19,6 +19,11 @@ import {
import { homedir, tmpdir } from "node:os";
import { delimiter, dirname, extname, isAbsolute, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
canonicalProviderName,
isProviderAdvertised,
parseProvidersFromHelp,
} from "./crabbox-wrapper-providers.mjs";
import { resolvePathEnvKey, resolveWindowsCmdExePath } from "./windows-cmd-helpers.mjs";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -660,7 +665,7 @@ function shouldRequireBrokeredAws(commandArgs, providerName) {
if (process.env.OPENCLAW_CRABBOX_ALLOW_DIRECT_AWS === "1") {
return false;
}
const canonicalProvider = providerAliases.get(providerName) ?? providerName;
const canonicalProvider = canonicalProviderName(providerName);
if (canonicalProvider !== "aws") {
return false;
}
@@ -2057,7 +2062,7 @@ function isAwsMacosRemoteTarget(commandArgs, providerName) {
}
function isBrokeredWsl2RemoteTarget(commandArgs, providerName) {
const canonicalProvider = providerAliases.get(providerName) ?? providerName;
const canonicalProvider = canonicalProviderName(providerName);
return (
commandArgs[0] === "run" &&
(canonicalProvider === "aws" || canonicalProvider === "azure") &&
@@ -3152,99 +3157,10 @@ function injectFullCheckoutLeaseReclaim(commandArgs) {
const version = checkedOutput(binary, ["--version"]);
const help = checkedOutput(binary, ["run", "--help"]);
const providerAliases = new Map([
["blacksmith", "blacksmith-testbox"],
["cf", "cloudflare"],
["container", "local-container"],
["docker", "local-container"],
["exe", "exe-dev"],
["exedev", "exe-dev"],
["google", "gcp"],
["google-cloud", "gcp"],
["local-docker", "local-container"],
["namespace", "namespace-devbox"],
["namespace-devboxes", "namespace-devbox"],
["rail", "railway"],
["railwayapp", "railway"],
["run-pod", "runpod"],
["runpodio", "runpod"],
["sem", "semaphore"],
["static", "ssh"],
["static-ssh", "ssh"],
["tensorlake-sbx", "tensorlake"],
["tl", "tensorlake"],
]);
// Crabbox providerHelpAll can omit Tensorlake even when the binary accepts it.
const providerHelpOmissions = new Set(["tensorlake"]);
function addProviderNames(names, text) {
for (const name of text
.replace(/\s+\(default\b.*$/u, "")
.split(/\s*(?:,|\||\bor\b)\s*/u)
.map((s) => s.trim())
.filter(Boolean)) {
if (/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
names.add(name);
}
}
}
function providerListContinuation(line, previousText) {
const match = line.match(
/^\s*((?:or\s+)?[a-z0-9][a-z0-9-]*(?:\s*(?:,|\||\bor\b)\s*(?:or\s+)?[a-z0-9][a-z0-9-]*)*\s*(?:,|\|)?)(?:\s+\(default\b.*)?\s*$/u,
);
if (!match) {
return "";
}
if (/[,|]\s*$/u.test(previousText) || /[,|]|\bor\b|\(default\b/u.test(line)) {
return match[1];
}
return "";
}
function parseProvidersFromHelp(text) {
const names = new Set();
const lines = text.split(/\r?\n/u);
for (let index = 0; index < lines.length; index += 1) {
const line = lines[index];
const providerMatch = line.match(/provider:\s*([a-z0-9][a-z0-9, -]*)(?:\s*\(default\b|$)/u);
if (providerMatch) {
let providerText = providerMatch[1];
while (!/\(default\b/u.test(lines[index]) && index + 1 < lines.length) {
const continuation = providerListContinuation(lines[index + 1], providerText);
if (!continuation) {
break;
}
index += 1;
providerText = `${providerText} ${continuation}`;
}
addProviderNames(names, providerText);
continue;
}
const flagMatch = line.match(
/^\s+-{1,2}provider(?:[=\s]+)([a-z0-9][a-z0-9|, -]*)(?:\s{2,}|\s+\(|$)/u,
);
if (flagMatch && /[,|]|\bor\b/u.test(flagMatch[1])) {
addProviderNames(names, flagMatch[1]);
}
}
return [...names];
}
function isProviderAdvertised(provider, advertisedProviders) {
const canonicalProvider = providerAliases.get(provider) ?? provider;
return (
advertisedProviders.includes(provider) ||
advertisedProviders.includes(canonicalProvider) ||
providerHelpOmissions.has(canonicalProvider)
);
}
const providers = parseProvidersFromHelp(help.text);
const displayBinary = binary === "crabbox" ? "crabbox" : relative(repoRoot, binary);
const provider = selectedProvider(args, providers);
const canonicalProvider = providerAliases.get(provider) ?? provider;
const canonicalProvider = canonicalProviderName(provider);
const commandProviderValue = commandProvider(args);
let normalizedArgs = ensureAwsMacOnDemandMarket(
ensureNativeWindowsHydrateJob(ensureAzureWindowsProvider(args, provider, providers)),
+1
View File
@@ -671,6 +671,7 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
["scripts/tsconfig.json", ["test/scripts/oxlint-config.test.ts"]],
["scripts/build-all.mjs", ["test/scripts/build-all.test.ts"]],
["scripts/build-stamp.mjs", ["src/infra/build-stamp.test.ts"]],
["scripts/crabbox-wrapper-providers.mjs", ["test/scripts/crabbox-wrapper.test.ts"]],
["scripts/crabbox-wrapper.mjs", ["test/scripts/crabbox-wrapper.test.ts"]],
["scripts/github/barnacle-auto-response.mjs", ["test/scripts/barnacle-auto-response.test.ts"]],
["scripts/changed-lanes.mjs", ["test/scripts/changed-lanes.test.ts"]],
+11
View File
@@ -72,6 +72,17 @@ describe("package manager build policy", () => {
expect(packageJson.files).toContain("THIRD_PARTY_NOTICES.md");
});
it("includes the Crabbox wrapper runtime modules in the published root package", () => {
const packageJson = readJson("package.json") as RootPackageJson;
expect(packageJson.files).toEqual(
expect.arrayContaining([
"scripts/crabbox-wrapper.mjs",
"scripts/crabbox-wrapper-providers.mjs",
]),
);
});
it("keeps npm shrinkwrap aligned with workspace overrides", () => {
const workspace = parse(
fs.readFileSync("pnpm-workspace.yaml", "utf8"),
+39 -41
View File
@@ -15,6 +15,11 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import {
canonicalProviderName,
isProviderAdvertised,
parseProvidersFromHelp,
} from "../../scripts/crabbox-wrapper-providers.mjs";
const tempDirs: string[] = [];
const repoRoot = process.cwd();
@@ -588,24 +593,24 @@ describe("scripts/crabbox-wrapper", () => {
"",
].join("\n");
const advertisedProviderAliases = [
"blacksmith",
"cf",
"container",
"docker",
"exe",
"exedev",
"google",
"google-cloud",
"local-docker",
"namespace",
"namespace-devboxes",
"rail",
"railwayapp",
"run-pod",
"runpodio",
"sem",
"static",
"static-ssh",
["blacksmith", "blacksmith-testbox"],
["cf", "cloudflare"],
["container", "local-container"],
["docker", "local-container"],
["exe", "exe-dev"],
["exedev", "exe-dev"],
["google", "gcp"],
["google-cloud", "gcp"],
["local-docker", "local-container"],
["namespace", "namespace-devbox"],
["namespace-devboxes", "namespace-devbox"],
["rail", "railway"],
["railwayapp", "railway"],
["run-pod", "runpod"],
["runpodio", "runpod"],
["sem", "semaphore"],
["static", "ssh"],
["static-ssh", "ssh"],
];
beforeAll(() => {
runWrapper("provider: aws\n", ["--version"]);
@@ -3077,18 +3082,12 @@ describe("scripts/crabbox-wrapper", () => {
});
it.each(advertisedProviderAliases)(
"accepts Crabbox provider alias %s when its canonical provider is advertised",
(alias) => {
const result = runWrapper(advertisedProviderAliasHelp, [
"run",
"--provider",
alias,
"--",
"echo ok",
]);
"canonicalizes Crabbox provider alias %s to %s",
(alias, canonical) => {
const advertisedProviders = parseProvidersFromHelp(advertisedProviderAliasHelp);
expect(result.status, alias).toBe(0);
expect(parseFakeCrabboxOutput(result).args).toContain(alias);
expect(canonicalProviderName(alias)).toBe(canonical);
expect(isProviderAdvertised(alias, advertisedProviders)).toBe(true);
},
);
@@ -3099,11 +3098,9 @@ describe("scripts/crabbox-wrapper", () => {
"",
].join("\n");
const advertisedProviders = parseProvidersFromHelp(helpText);
for (const provider of ["tensorlake", "tl", "tensorlake-sbx"]) {
const result = runWrapper(helpText, ["run", "--provider", provider, "--", "echo ok"]);
expect(result.status, provider).toBe(0);
expect(parseFakeCrabboxOutput(result).args).toContain(provider);
expect(isProviderAdvertised(provider, advertisedProviders), provider).toBe(true);
}
});
@@ -3132,15 +3129,16 @@ describe("scripts/crabbox-wrapper", () => {
});
it("parses provider choices from the --provider flag help format", () => {
const result = runWrapper(
"Usage: crabbox run [options]\n --provider hetzner|aws|local-container|blacksmith-testbox|cloudflare\n",
["run", "--provider", "aws", "--", "echo ok"],
);
const helpText =
"Usage: crabbox run [options]\n --provider hetzner|aws|local-container|blacksmith-testbox|cloudflare\n";
expect(result.status).toBe(0);
expect(result.stderr).toContain(
"providers=hetzner,aws,local-container,blacksmith-testbox,cloudflare",
);
expect(parseProvidersFromHelp(helpText)).toEqual([
"hetzner",
"aws",
"local-container",
"blacksmith-testbox",
"cloudflare",
]);
});
it("uses a temporary full checkout for clean sparse Blacksmith syncs", () => {
+9 -4
View File
@@ -1189,10 +1189,15 @@ describe("scripts/test-projects changed-target routing", () => {
});
it("keeps Crabbox runner script edits on their regression tests", () => {
expect(resolveChangedTestTargetPlan(["scripts/crabbox-wrapper.mjs"])).toEqual({
mode: "targets",
targets: ["test/scripts/crabbox-wrapper.test.ts"],
});
for (const scriptPath of [
"scripts/crabbox-wrapper.mjs",
"scripts/crabbox-wrapper-providers.mjs",
]) {
expect(resolveChangedTestTargetPlan([scriptPath]), scriptPath).toEqual({
mode: "targets",
targets: ["test/scripts/crabbox-wrapper.test.ts"],
});
}
});
it("keeps build stamp script edits on the build stamp regression test", () => {