Files
elizaos--eliza/scripts/vast/run-certification.test.mjs
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

537 lines
17 KiB
JavaScript

/**
* Unit tests for the pure pieces of the vast.ai certification runner: offer
* filtering/sorting, onstart assembly + the 16 KB vast cap, the poll state
* machine's kill paths, budget guards, CLI parsing, log-marker/certification
* extraction, and dry-run secret redaction. No network, no vast account —
* everything here is a pure function imported from run-certification.mjs
* (the real-instance acceptance run is owner-gated on VAST_API_KEY).
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
API_KEY_ENV_VAR,
assertOnstartSize,
buildCreatePayload,
buildDryRunPlan,
buildOfferQuery,
buildOnstart,
CERT_BEGIN_MARKER,
CERT_END_MARKER,
createPollState,
detectMarker,
EXIT,
extractCertification,
FAILURE_MARKER,
filterAndSortOffers,
isRetryableOutcome,
ONSTART_MAX_BYTES,
PUSH_CMD_ENV_VAR,
parseCliArgs,
redactCreatePayload,
reducePoll,
SIGNING_KEY_ENV_VAR,
SUCCESS_MARKER,
selectAttemptOffers,
UsageError,
} from "./run-certification.mjs";
const SHA = "a".repeat(40);
function baseOpts(overrides = {}) {
return parseCliArgs(["--sha", SHA, "--dry-run"], {
[SIGNING_KEY_ENV_VAR]: undefined,
...overrides.env,
});
}
function offer(overrides = {}) {
return {
id: 1,
dph_total: 0.35,
reliability2: 0.995,
inet_down: 900,
num_gpus: 1,
gpu_name: "RTX 4090",
...overrides,
};
}
describe("buildOfferQuery", () => {
it("translates underscore GPU names and pins the acceptance filters", () => {
const query = buildOfferQuery(baseOpts());
assert.equal(query.gpu_name.eq, "RTX 4090");
assert.equal(query.num_gpus.eq, 1);
assert.equal(query.verified.eq, true);
assert.equal(query.rentable.eq, true);
assert.equal(query.reliability2.gt, 0.98);
assert.equal(query.inet_down.gt, 500);
assert.deepEqual(query.order, [["dph_total", "asc"]]);
assert.equal(query.type, "ask");
});
});
describe("filterAndSortOffers", () => {
it("sorts eligible offers cheapest-first with reliability tie-break", () => {
const { eligible } = filterAndSortOffers(
[
offer({ id: 1, dph_total: 0.4 }),
offer({ id: 2, dph_total: 0.3, reliability2: 0.99 }),
offer({ id: 3, dph_total: 0.3, reliability2: 0.999 }),
],
baseOpts(),
);
assert.deepEqual(
eligible.map((entry) => entry.id),
[3, 2, 1],
);
});
it("rejects offers over --max-dph with a reason (budget guard)", () => {
const { eligible, rejected } = filterAndSortOffers(
[offer({ id: 7, dph_total: 0.99 })],
baseOpts(),
);
assert.equal(eligible.length, 0);
assert.equal(rejected[0].id, 7);
assert.match(rejected[0].reason, /--max-dph/);
});
it("re-checks reliability, inet_down and num_gpus client-side", () => {
const { eligible, rejected } = filterAndSortOffers(
[
offer({ id: 1, reliability2: 0.9 }),
offer({ id: 2, inet_down: 100 }),
offer({ id: 3, num_gpus: 4 }),
offer({ id: 4 }),
],
baseOpts(),
);
assert.deepEqual(
eligible.map((entry) => entry.id),
[4],
);
assert.equal(rejected.length, 3);
});
it("falls back to legacy `reliability` and rejects offers missing both", () => {
const { eligible, rejected } = filterAndSortOffers(
[
offer({ id: 1, reliability2: undefined, reliability: 0.999 }),
offer({ id: 2, reliability2: undefined, reliability: undefined }),
],
baseOpts(),
);
assert.deepEqual(
eligible.map((entry) => entry.id),
[1],
);
assert.match(rejected[0].reason, /missing reliability/);
});
it("never assumes compliance for offers missing dph_total or id", () => {
const { eligible } = filterAndSortOffers(
[offer({ id: undefined }), offer({ dph_total: undefined })],
baseOpts(),
);
assert.equal(eligible.length, 0);
});
});
describe("selectAttemptOffers", () => {
it("caps attempts at --max-attempts, cheapest-first", () => {
const { eligible } = filterAndSortOffers(
[1, 2, 3, 4, 5].map((id) => offer({ id, dph_total: id / 10 })),
baseOpts(),
);
const attempts = selectAttemptOffers(eligible, { maxAttempts: 2 });
assert.deepEqual(
attempts.map((entry) => entry.id),
[1, 2],
);
});
});
describe("buildOnstart", () => {
it("clones the exact sha and runs the certification chain in order", () => {
const onstart = buildOnstart(baseOpts());
const cloneIndex = onstart.indexOf(`git fetch --depth 1 origin ${SHA}`);
const installIndex = onstart.indexOf("bun run install:light");
const bundleIndex = onstart.indexOf("bundle:create -- --tier full");
const rollupIndex = onstart.indexOf("certify:rollup");
const signIndex = onstart.indexOf("certify:sign");
assert.ok(
cloneIndex > -1 && installIndex > cloneIndex,
"clone then install",
);
assert.ok(bundleIndex > installIndex, "bundle after install");
assert.ok(rollupIndex > bundleIndex, "rollup after bundle");
assert.ok(signIndex > rollupIndex, "sign after rollup");
assert.ok(onstart.includes(SUCCESS_MARKER));
assert.ok(onstart.includes(CERT_BEGIN_MARKER));
assert.ok(onstart.includes("ELIZA_EVIDENCE_RUNNER=vast"));
});
it("never embeds secret material in the onstart text", () => {
const opts = {
...baseOpts(),
signingKey: "-----BEGIN PRIVATE KEY-----SECRETSECRET",
pushCmd: "aws s3 cp --secret-token hunter2",
apiKey: "vast-key-hunter3",
};
const onstart = buildOnstart(opts);
assert.ok(!onstart.includes("SECRETSECRET"));
assert.ok(!onstart.includes("hunter2"));
assert.ok(!onstart.includes("hunter3"));
// The push command is referenced by env var NAME only.
assert.ok(onstart.includes(`\${${PUSH_CMD_ENV_VAR}:-}`));
});
it("stays comfortably under the 16 KB vast cap with defaults", () => {
const bytes = assertOnstartSize(buildOnstart(baseOpts()));
assert.ok(
bytes < ONSTART_MAX_BYTES / 4,
`expected small onstart, got ${bytes} bytes`,
);
});
});
describe("assertOnstartSize", () => {
it("throws UsageError past 16 KB (multi-byte aware)", () => {
assert.throws(
() => assertOnstartSize("é".repeat(ONSTART_MAX_BYTES / 2 + 1)),
UsageError,
);
assert.throws(
() =>
assertOnstartSize(
buildOnstart({ ...baseOpts(), reviewerId: "x".repeat(17000) }),
),
/16384/,
);
});
it("accepts exactly 16 KB", () => {
assert.equal(
assertOnstartSize("x".repeat(ONSTART_MAX_BYTES)),
ONSTART_MAX_BYTES,
);
});
});
describe("buildCreatePayload / redactCreatePayload", () => {
it("injects signing key and push cmd via env only, destroy-safe defaults", () => {
const opts = { ...baseOpts(), signingKey: "PEMPEM", pushCmd: "push it" };
const payload = buildCreatePayload(opts, "onstart");
assert.equal(payload.env[SIGNING_KEY_ENV_VAR], "PEMPEM");
assert.equal(payload.env[PUSH_CMD_ENV_VAR], "push it");
assert.equal(payload.env.ELIZA_EVIDENCE_RUNNER, "vast");
assert.equal(payload.client_id, "me");
assert.equal(payload.image, "ghcr.io/elizaos/certification-gpu:latest");
assert.equal(payload.label, `eliza-certification-${SHA.slice(0, 12)}`);
});
it("redacts every env value, keeps the key names visible", () => {
const payload = buildCreatePayload(
{ ...baseOpts(), signingKey: "PEMPEM", pushCmd: "s3 cp secret" },
"onstart",
);
const redacted = redactCreatePayload(payload);
assert.deepEqual(
Object.values(redacted.env),
Object.keys(payload.env).map(() => "<redacted>"),
);
assert.ok(JSON.stringify(redacted).includes(SIGNING_KEY_ENV_VAR));
assert.ok(!JSON.stringify(redacted).includes("PEMPEM"));
});
});
describe("poll state machine", () => {
const config = { timeoutMs: 60 * 60_000, loadingTimeoutMs: 10 * 60_000 };
function run(snapshots, stateConfig = config) {
let state = createPollState(stateConfig);
for (const snapshot of snapshots) {
state = reducePoll(state, { marker: null, ...snapshot });
if (state.outcome) return state;
}
return state;
}
it("happy path: loading → running → success marker", () => {
const state = run([
{ actualStatus: "loading", elapsedMs: 60_000 },
{ actualStatus: "running", elapsedMs: 300_000 },
{ actualStatus: "running", elapsedMs: 900_000, marker: "success" },
]);
assert.deepEqual(state.outcome, { ok: true });
});
it("failure marker → ONSTART_FAILED even while still running", () => {
const state = run([
{ actualStatus: "running", elapsedMs: 300_000, marker: "failure" },
]);
assert.equal(state.outcome.ok, false);
assert.equal(state.outcome.code, EXIT.ONSTART_FAILED);
});
it("exited without success marker → ONSTART_FAILED", () => {
const state = run([
{ actualStatus: "running", elapsedMs: 300_000 },
{ actualStatus: "exited", elapsedMs: 600_000 },
]);
assert.equal(state.outcome.code, EXIT.ONSTART_FAILED);
assert.match(state.outcome.reason, /without the success marker/);
});
it("exited WITH success marker in the same tick is a success", () => {
const state = run([
{ actualStatus: "exited", elapsedMs: 600_000, marker: "success" },
]);
assert.deepEqual(state.outcome, { ok: true });
});
it("stuck in loading past the loading timeout → STUCK_LOADING", () => {
const state = run([
{ actualStatus: "loading", elapsedMs: 60_000 },
{ actualStatus: "loading", elapsedMs: 11 * 60_000 },
]);
assert.equal(state.outcome.code, EXIT.STUCK_LOADING);
});
it("blank/provisioning status counts as loading for the stuck check", () => {
const state = run([{ actualStatus: "", elapsedMs: 11 * 60_000 }]);
assert.equal(state.outcome.code, EXIT.STUCK_LOADING);
});
it("loading timeout does NOT fire once the instance has been running", () => {
const state = run([
{ actualStatus: "running", elapsedMs: 60_000 },
{ actualStatus: "loading", elapsedMs: 11 * 60_000 },
]);
assert.equal(state.outcome, null);
});
it("offline/unknown are debounced, then INSTANCE_LOST", () => {
const state = run([
{ actualStatus: "running", elapsedMs: 60_000 },
{ actualStatus: "offline", elapsedMs: 120_000 },
{ actualStatus: "unknown", elapsedMs: 180_000 },
{ actualStatus: "offline", elapsedMs: 240_000 },
]);
assert.equal(state.outcome.code, EXIT.INSTANCE_LOST);
});
it("a healthy poll resets the offline debounce", () => {
const state = run([
{ actualStatus: "offline", elapsedMs: 60_000 },
{ actualStatus: "offline", elapsedMs: 120_000 },
{ actualStatus: "running", elapsedMs: 180_000 },
{ actualStatus: "offline", elapsedMs: 240_000 },
{ actualStatus: "offline", elapsedMs: 300_000 },
]);
assert.equal(state.outcome, null);
assert.equal(state.consecutiveLost, 2);
});
it("hard wall-clock timeout → RUN_TIMEOUT", () => {
const state = run([{ actualStatus: "running", elapsedMs: 61 * 60_000 }]);
assert.equal(state.outcome.code, EXIT.RUN_TIMEOUT);
});
it("terminal state is sticky", () => {
let state = run([{ actualStatus: "running", elapsedMs: 61 * 60_000 }]);
state = reducePoll(state, {
actualStatus: "running",
elapsedMs: 62 * 60_000,
marker: "success",
});
assert.equal(state.outcome.code, EXIT.RUN_TIMEOUT);
});
});
describe("isRetryableOutcome", () => {
it("retries only host-side failures, never chain failures or timeouts", () => {
assert.ok(isRetryableOutcome(EXIT.STUCK_LOADING));
assert.ok(isRetryableOutcome(EXIT.INSTANCE_LOST));
assert.ok(isRetryableOutcome(EXIT.CREATE_FAILED));
assert.ok(!isRetryableOutcome(EXIT.ONSTART_FAILED));
assert.ok(!isRetryableOutcome(EXIT.RUN_TIMEOUT));
assert.ok(!isRetryableOutcome(EXIT.DEAD_API_KEY));
});
});
describe("VastApiError auth classification", () => {
it("maps vast's 404 + auth_error body to a dead key (live-API behavior)", async () => {
const { parseVastErrorCode, VastApiError } = await import(
"./run-certification.mjs"
);
// Verified against the live API: a dead key answers HTTP 404 with
// {"success":false,"error":"auth_error","msg":"Invalid user key"}.
const body =
'{"success":false,"error":"auth_error","msg":"Invalid user key"}';
assert.equal(parseVastErrorCode(body), "auth_error");
const authByCode = new VastApiError("x", {
status: 404,
errorCode: "auth_error",
});
assert.ok(authByCode.isAuthFailure);
const authByStatus = new VastApiError("x", { status: 401 });
assert.ok(authByStatus.isAuthFailure);
const plain404 = new VastApiError("x", { status: 404 });
assert.ok(!plain404.isAuthFailure);
});
it("returns undefined for HTML error pages, never throws", async () => {
const { parseVastErrorCode } = await import("./run-certification.mjs");
assert.equal(parseVastErrorCode("<html>404</html>"), undefined);
assert.equal(parseVastErrorCode('{"msg":"no error field"}'), undefined);
assert.equal(parseVastErrorCode(undefined), undefined);
});
});
describe("detectMarker / extractCertification", () => {
it("failure marker wins over a later success marker", () => {
assert.equal(
detectMarker(`${FAILURE_MARKER}: rollup\n${SUCCESS_MARKER}\n`),
"failure",
);
assert.equal(detectMarker(`all good\n${SUCCESS_MARKER}\n`), "success");
assert.equal(detectMarker("still going"), null);
assert.equal(detectMarker(""), null);
assert.equal(detectMarker(undefined), null);
});
it("extracts the LAST complete certification block and validates JSON", () => {
const first = JSON.stringify({ commit: "old" });
const second = JSON.stringify({ commit: SHA, verdicts: [] });
const logs = [
CERT_BEGIN_MARKER,
first,
CERT_END_MARKER,
"retry noise",
CERT_BEGIN_MARKER,
second,
CERT_END_MARKER,
SUCCESS_MARKER,
].join("\n");
assert.equal(extractCertification(logs), second);
});
it("returns null for truncated or unparseable blocks (never corrupt output)", () => {
assert.equal(extractCertification(`${CERT_BEGIN_MARKER}\n{"a": 1`), null);
assert.equal(
extractCertification(
`${CERT_BEGIN_MARKER}\n{"a": tru\n${CERT_END_MARKER}`,
),
null,
);
assert.equal(extractCertification("no markers"), null);
});
});
describe("parseCliArgs", () => {
it("requires --sha as hex and validates --tier", () => {
assert.throws(() => parseCliArgs(["--dry-run"], {}), /--sha is required/);
assert.throws(
() => parseCliArgs(["--sha", "not-hex!", "--dry-run"], {}),
/--sha/,
);
assert.throws(
() => parseCliArgs(["--sha", SHA, "--tier", "mega", "--dry-run"], {}),
/--tier must be one of/,
);
});
it("requires api + signing keys for real runs, but not for --dry-run", () => {
assert.throws(
() => parseCliArgs(["--sha", SHA], {}),
new RegExp(API_KEY_ENV_VAR),
);
assert.throws(
() => parseCliArgs(["--sha", SHA], { [API_KEY_ENV_VAR]: "k" }),
new RegExp(SIGNING_KEY_ENV_VAR),
);
const opts = parseCliArgs(["--sha", SHA, "--dry-run"], {});
assert.equal(opts.dryRun, true);
});
it("parses budget guards and rejects non-positive values", () => {
const opts = parseCliArgs(
[
"--sha",
SHA,
"--dry-run",
"--max-dph",
"0.45",
"--max-attempts",
"2",
"--timeout-minutes",
"90",
],
{},
);
assert.equal(opts.maxDph, 0.45);
assert.equal(opts.maxAttempts, 2);
assert.equal(opts.timeoutMinutes, 90);
assert.throws(
() =>
parseCliArgs(["--sha", SHA, "--dry-run", "--max-attempts", "0"], {}),
/--max-attempts/,
);
assert.throws(
() =>
parseCliArgs(["--sha", SHA, "--dry-run", "--max-dph", "banana"], {}),
/--max-dph/,
);
assert.throws(
() => parseCliArgs(["--sha", SHA, "--dry-run", "--unknown"], {}),
/unknown argument/,
);
});
it("reads keys and push cmd from the environment", () => {
const opts = parseCliArgs(["--sha", SHA], {
[API_KEY_ENV_VAR]: "api-key",
[SIGNING_KEY_ENV_VAR]: "pem",
[PUSH_CMD_ENV_VAR]: "rclone copy",
});
assert.equal(opts.apiKey, "api-key");
assert.equal(opts.signingKey, "pem");
assert.equal(opts.pushCmd, "rclone copy");
});
});
describe("buildDryRunPlan", () => {
it("summarizes budget worst-case and never leaks secret values", () => {
const opts = parseCliArgs(
[
"--sha",
SHA,
"--dry-run",
"--max-dph",
"0.5",
"--max-attempts",
"2",
"--timeout-minutes",
"60",
],
{
[SIGNING_KEY_ENV_VAR]: "-----BEGIN PRIVATE KEY-----LEAKME",
[API_KEY_ENV_VAR]: "vast-LEAKME",
[PUSH_CMD_ENV_VAR]: "s3 cp --token LEAKME",
},
);
const plan = buildDryRunPlan(opts);
assert.equal(plan.budget.worstCaseUsd, 1);
const serialized = JSON.stringify(plan);
assert.ok(!serialized.includes("LEAKME"));
assert.equal(
plan.secrets[SIGNING_KEY_ENV_VAR],
"present (create-env only)",
);
assert.ok(plan.onstart.includes(SHA));
assert.equal(plan.cleanup.retries, 3);
});
});