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
203 lines
8.2 KiB
TypeScript
203 lines
8.2 KiB
TypeScript
// End-to-end coverage for scripts/run-bun-tests.mjs (#15785): spawns the REAL
|
|
// wrapper process, which spawns real children through the ELIZA_BUN_TEST_BIN
|
|
// seam (scripts/__fixtures__/stub-bun-runner.mjs emitting the verbatim #15785
|
|
// panic output). Exercises the full classify → capture → retry → exit-code
|
|
// pipeline with real processes on any platform.
|
|
import { describe, expect, test } from "bun:test";
|
|
import { spawnSync } from "node:child_process";
|
|
import { existsSync, mkdtempSync, readdirSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
|
|
const scriptsDir = import.meta.dir;
|
|
const wrapperPath = path.join(scriptsDir, "run-bun-tests.mjs");
|
|
const stubPath = path.join(scriptsDir, "__fixtures__", "stub-bun-runner.mjs");
|
|
|
|
const QUARANTINED_SUITE = "src/lib/services/tenant-db/tenant-db-placement-claimer.test.ts";
|
|
|
|
interface WrapperRun {
|
|
status: number | null;
|
|
stdout: string;
|
|
stderr: string;
|
|
merged: string;
|
|
invocations: { argv: string[] }[];
|
|
crashDir: string;
|
|
crashCaptures: string[];
|
|
}
|
|
|
|
function runWrapper({
|
|
plan,
|
|
mainMode = "pass",
|
|
env = {},
|
|
args = [],
|
|
}: {
|
|
plan?: string[];
|
|
mainMode?: "pass" | "fail";
|
|
env?: Record<string, string>;
|
|
args?: string[];
|
|
}): WrapperRun {
|
|
const stateDir = mkdtempSync(path.join(tmpdir(), "run-bun-tests-e2e-"));
|
|
const crashDir = path.join(stateDir, "crash-captures");
|
|
const result = spawnSync(process.execPath, [wrapperPath, ...args], {
|
|
cwd: scriptsDir,
|
|
encoding: "utf8",
|
|
timeout: 120_000,
|
|
maxBuffer: 32 * 1024 * 1024,
|
|
env: {
|
|
...process.env,
|
|
ELIZA_WIN_PGLITE_QUARANTINE: "1",
|
|
ELIZA_BUN_TEST_BIN: process.execPath,
|
|
ELIZA_BUN_TEST_BIN_ARGS: JSON.stringify([stubPath]),
|
|
ELIZA_PGLITE_CRASH_DIR: crashDir,
|
|
STUB_STATE_DIR: stateDir,
|
|
STUB_QUARANTINE_PLAN: JSON.stringify(plan ?? ["pass"]),
|
|
STUB_MAIN_MODE: mainMode,
|
|
...env,
|
|
},
|
|
});
|
|
const invocationsFile = path.join(stateDir, "invocations.jsonl");
|
|
const invocations = existsSync(invocationsFile)
|
|
? readFileSync(invocationsFile, "utf8")
|
|
.trim()
|
|
.split("\n")
|
|
.filter(Boolean)
|
|
.map((line) => JSON.parse(line) as { argv: string[] })
|
|
: [];
|
|
const crashCaptures = existsSync(crashDir)
|
|
? readdirSync(crashDir).map((file) => path.join(crashDir, file))
|
|
: [];
|
|
return {
|
|
status: result.status,
|
|
stdout: result.stdout ?? "",
|
|
stderr: result.stderr ?? "",
|
|
merged: `${result.stdout ?? ""}${result.stderr ?? ""}`,
|
|
invocations,
|
|
crashDir,
|
|
crashCaptures,
|
|
};
|
|
}
|
|
|
|
const isMainPassInvocation = (argv: string[]) =>
|
|
argv.some((arg) => arg.startsWith("--path-ignore-patterns="));
|
|
|
|
describe("run-bun-tests wrapper e2e (#15785 quarantine + crash retry)", () => {
|
|
test("native crash then pass: retries the quarantined pass, captures the panic, exits 0", () => {
|
|
const run = runWrapper({ plan: ["crash", "pass"] });
|
|
expect(run.status).toBe(0);
|
|
|
|
const mainPasses = run.invocations.filter((i) => isMainPassInvocation(i.argv));
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
expect(mainPasses).toHaveLength(1);
|
|
expect(quarantinePasses).toHaveLength(2);
|
|
|
|
// Main pass excludes the quarantined suite: ignore-pattern flag present,
|
|
// suite NOT passed as a positional file.
|
|
const mainArgv = mainPasses[0].argv;
|
|
expect(mainArgv).toContain(`--path-ignore-patterns=${QUARANTINED_SUITE}`);
|
|
expect(mainArgv.filter((arg) => !arg.startsWith("-")).includes(QUARANTINED_SUITE)).toBe(false);
|
|
|
|
// Quarantine pass runs exactly the quarantined suite as a positional file.
|
|
expect(quarantinePasses[0].argv).toContain(QUARANTINED_SUITE);
|
|
|
|
// The crash was captured for the upstream Bun report and flagged loudly.
|
|
expect(run.crashCaptures).toHaveLength(1);
|
|
const capture = readFileSync(run.crashCaptures[0], "utf8");
|
|
expect(capture).toContain("panic(main thread): Illegal instruction");
|
|
expect(capture).toContain("issue #15785");
|
|
expect(capture).toContain("attempt: 1/3");
|
|
expect(run.merged).toContain("NATIVE CRASH in quarantined PGlite pass");
|
|
expect(run.merged).toContain("PASSED on attempt 2/3");
|
|
expect(run.merged).toContain("bun-pglite-crash-upstream-report.md");
|
|
}, 60_000);
|
|
|
|
test("genuine test failure: fails immediately with NO retry and NO crash capture", () => {
|
|
const run = runWrapper({ plan: ["fail", "pass"] });
|
|
expect(run.status).toBe(1);
|
|
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
// One attempt only — a real assertion failure must stay loud (#13620).
|
|
expect(quarantinePasses).toHaveLength(1);
|
|
expect(run.crashCaptures).toHaveLength(0);
|
|
expect(run.merged).toContain("GENUINE test failure");
|
|
expect(run.merged).not.toContain("retrying quarantined suites");
|
|
}, 60_000);
|
|
|
|
test("persistent native crash: bounded attempts, then the run fails with the crash exit code", () => {
|
|
const run = runWrapper({
|
|
plan: ["crash", "crash", "crash", "crash", "crash"],
|
|
});
|
|
expect(run.status).toBe(3);
|
|
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
expect(quarantinePasses).toHaveLength(3); // DEFAULT_MAX_QUARANTINE_ATTEMPTS
|
|
expect(run.crashCaptures).toHaveLength(3);
|
|
expect(run.merged).toContain("crashed natively on all 3 attempt(s)");
|
|
}, 60_000);
|
|
|
|
test("crash-silent (exit 3, no markers, no summary) is treated as a native crash and retried", () => {
|
|
const run = runWrapper({ plan: ["crash-silent", "pass"] });
|
|
expect(run.status).toBe(0);
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
expect(quarantinePasses).toHaveLength(2);
|
|
expect(run.crashCaptures).toHaveLength(1);
|
|
expect(readFileSync(run.crashCaptures[0], "utf8")).toContain("native-crash exit code 3");
|
|
}, 60_000);
|
|
|
|
test("main-pass failure is not retried and fails the run even when the quarantined pass is green", () => {
|
|
const run = runWrapper({ plan: ["pass"], mainMode: "fail" });
|
|
expect(run.status).toBe(1);
|
|
expect(run.merged).toContain("main pass FAILED");
|
|
// Both passes still ran (one failure does not mask the other).
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
expect(quarantinePasses.length).toBeGreaterThanOrEqual(1);
|
|
}, 60_000);
|
|
|
|
test("quarantine off (ELIZA_WIN_PGLITE_QUARANTINE=0): single legacy bun test --isolate invocation", () => {
|
|
const run = runWrapper({
|
|
plan: ["pass"],
|
|
env: { ELIZA_WIN_PGLITE_QUARANTINE: "0" },
|
|
});
|
|
expect(run.status).toBe(0);
|
|
expect(run.invocations).toHaveLength(1);
|
|
const argv = run.invocations[0].argv;
|
|
expect(argv[0]).toBe("test");
|
|
expect(argv).toContain("--isolate");
|
|
expect(argv.some((arg) => arg.startsWith("--path-ignore-patterns="))).toBe(false);
|
|
expect(argv).not.toContain(QUARANTINED_SUITE);
|
|
}, 60_000);
|
|
|
|
test("a stale quarantine list fails loudly before running anything (#13620: no silent zero-suite pass)", () => {
|
|
const run = runWrapper({
|
|
plan: ["pass"],
|
|
env: {
|
|
ELIZA_PGLITE_QUARANTINE_SUITES: JSON.stringify(["src/does-not-exist/renamed-away.test.ts"]),
|
|
},
|
|
});
|
|
expect(run.status).toBe(1);
|
|
expect(run.merged).toContain("not found on disk");
|
|
expect(run.invocations).toHaveLength(0);
|
|
}, 60_000);
|
|
|
|
test("watchdog kills a wedged quarantined pass and retries it (the #15785 wedge lasted ~64 minutes)", () => {
|
|
const run = runWrapper({
|
|
plan: ["hang", "pass"],
|
|
env: { ELIZA_PGLITE_QUARANTINE_TIMEOUT_MS: "3000" },
|
|
});
|
|
expect(run.status).toBe(0);
|
|
const quarantinePasses = run.invocations.filter((i) => !isMainPassInvocation(i.argv));
|
|
expect(quarantinePasses).toHaveLength(2);
|
|
expect(run.crashCaptures).toHaveLength(1);
|
|
expect(readFileSync(run.crashCaptures[0], "utf8")).toContain("watchdog kill");
|
|
expect(run.merged).toContain("watchdog: child exceeded 3000ms");
|
|
}, 60_000);
|
|
|
|
test("passthrough args reach both passes", () => {
|
|
const run = runWrapper({ plan: ["pass"], args: ["--timeout", "120000"] });
|
|
expect(run.status).toBe(0);
|
|
for (const invocation of run.invocations) {
|
|
expect(invocation.argv).toContain("--timeout");
|
|
expect(invocation.argv).toContain("120000");
|
|
}
|
|
}, 60_000);
|
|
});
|