Files
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

406 lines
13 KiB
TypeScript

// Exercises tests test task pool.test automation behavior with deterministic script fixtures.
import { describe, expect, test } from "bun:test";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import {
isParallelSafeTask,
normalizeConcurrency,
parseShardSpec,
partitionTasks,
runPool,
SERIALIZE_PACKAGES,
taskBelongsToShard,
} from "../lib/test-task-pool.mjs";
describe("isParallelSafeTask", () => {
test("plain `test` script in the pr lane is parallel-safe", () => {
expect(
isParallelSafeTask({
scriptName: "test",
lane: "pr",
packageName: "@elizaos/core",
}),
).toBe(true);
});
test("extra-script lanes (e2e/integration/...) are never parallel-safe", () => {
for (const scriptName of [
"test:e2e",
"test:integration",
"test:playwright",
"test:ui",
"test:live",
]) {
expect(
isParallelSafeTask({
scriptName,
lane: "pr",
packageName: "@elizaos/core",
}),
).toBe(false);
}
});
test("any lane other than pr forces serial (real-API / shared DB)", () => {
expect(
isParallelSafeTask({
scriptName: "test",
lane: "post-merge",
packageName: "@elizaos/core",
}),
).toBe(false);
});
test("denylisted packages stay serial even for their `test` script", () => {
for (const packageName of SERIALIZE_PACKAGES) {
expect(
isParallelSafeTask({ scriptName: "test", lane: "pr", packageName }),
).toBe(false);
}
});
test("denylist matches the packages the root test:plugins sweep pulls out", () => {
expect(SERIALIZE_PACKAGES.has("@elizaos/plugin-personal-assistant")).toBe(
true,
);
expect(SERIALIZE_PACKAGES.has("@elizaos/plugin-agent-orchestrator")).toBe(
true,
);
expect(SERIALIZE_PACKAGES.has("@elizaos/plugin-sql")).toBe(true);
});
});
describe("partitionTasks", () => {
test("splits into parallel/serial buckets preserving order", () => {
const tasks = [
{ packageName: "@elizaos/core", scriptName: "test" },
{ packageName: "@elizaos/core", scriptName: "test:e2e" },
{ packageName: "@elizaos/plugin-sql", scriptName: "test" },
{ packageName: "@elizaos/agent", scriptName: "test" },
];
const { parallel, serial } = partitionTasks(tasks, "pr");
expect(parallel.map((t) => t.packageName)).toEqual([
"@elizaos/core",
"@elizaos/agent",
]);
expect(serial.map((t) => `${t.packageName}#${t.scriptName}`)).toEqual([
"@elizaos/core#test:e2e",
"@elizaos/plugin-sql#test",
]);
});
test("post-merge lane puts everything in the serial bucket", () => {
const tasks = [
{ packageName: "@elizaos/core", scriptName: "test" },
{ packageName: "@elizaos/agent", scriptName: "test" },
];
const { parallel, serial } = partitionTasks(tasks, "post-merge");
expect(parallel).toHaveLength(0);
expect(serial).toHaveLength(2);
});
});
describe("runPool", () => {
test("preserves result order regardless of completion order", async () => {
const results = await runPool(
[30, 10, 20, 0, 5],
async (ms, i) => {
await new Promise((r) => setTimeout(r, ms));
return i;
},
3,
);
expect(results.map((r) => (r.ok ? r.value : null))).toEqual([
0, 1, 2, 3, 4,
]);
});
test("never runs more than `concurrency` workers at once", async () => {
let inFlight = 0;
let maxInFlight = 0;
await runPool(
Array.from({ length: 20 }, (_, i) => i),
async () => {
inFlight++;
maxInFlight = Math.max(maxInFlight, inFlight);
await new Promise((r) => setTimeout(r, 5));
inFlight--;
},
4,
);
expect(maxInFlight).toBeLessThanOrEqual(4);
expect(maxInFlight).toBeGreaterThan(1);
});
test("captures thrown errors as { ok: false } without aborting siblings", async () => {
const results = await runPool(
[1, 2, 3, 4],
async (n) => {
if (n % 2 === 0) {
throw new Error(`boom ${n}`);
}
return n;
},
2,
);
expect(results[0]).toEqual({ ok: true, value: 1 });
expect(results[1].ok).toBe(false);
expect(results[2]).toEqual({ ok: true, value: 3 });
expect(results[3].ok).toBe(false);
// All four ran — a thrown worker does not cancel the rest of the pool.
expect(results.every((r) => r !== undefined)).toBe(true);
});
test("empty input resolves to an empty array", async () => {
expect(await runPool([], async () => 1, 4)).toEqual([]);
});
test("concurrency is clamped to at least 1 and at most item count", async () => {
const results = await runPool([1, 2], async (n) => n, 99);
expect(results.map((r) => r.value)).toEqual([1, 2]);
});
});
describe("process supervision boundary", () => {
test("documents why dev supervisors and the test pool stay split", () => {
const doc = readFileSync(
new URL("../process-supervision.md", import.meta.url),
"utf8",
);
expect(doc).toContain("keep the seams separate");
expect(doc).toContain("api-supervisor.mjs");
expect(doc).toContain("dev-all.mjs");
expect(doc).toContain("run-all-tests.mjs");
expect(doc).toContain("long-lived single-child supervisor");
expect(doc).toContain("bounded batch runner");
expect(doc).toContain("digest.readUInt32BE(0)");
});
});
describe("normalizeConcurrency", () => {
test("defaults to 1 (fully serial) for empty/invalid input", () => {
for (const value of [undefined, null, "", "abc", "0", "-3", 0, -1]) {
expect(normalizeConcurrency(value)).toBe(1);
}
});
test("parses positive integers from string or number", () => {
expect(normalizeConcurrency("4")).toBe(4);
expect(normalizeConcurrency(8)).toBe(8);
expect(normalizeConcurrency("3.9")).toBe(3);
});
});
describe("parseShardSpec", () => {
test("parses a valid N/M spec", () => {
expect(parseShardSpec("2/4")).toEqual({ index: 2, total: 4 });
expect(parseShardSpec("1/1")).toEqual({ index: 1, total: 1 });
expect(parseShardSpec("4/4")).toEqual({ index: 4, total: 4 });
});
test("returns null for absent specs", () => {
expect(parseShardSpec("")).toBeNull();
expect(parseShardSpec(undefined)).toBeNull();
expect(parseShardSpec(null)).toBeNull();
});
test("returns null for malformed or out-of-range specs", () => {
for (const bad of [
"3", // missing /M
"a/b", // non-numeric
"0/4", // index < 1
"5/4", // index > total
"2/0", // total <= 0
"-1/4", // negative index
"1/2/3", // too many parts
"/4", // empty index
"2/", // empty total
]) {
expect(parseShardSpec(bad)).toBeNull();
}
});
});
describe("taskBelongsToShard", () => {
test("everything belongs when there is no shard config", () => {
expect(taskBelongsToShard("packages/core", null)).toBe(true);
});
test("membership is deterministic for the same key + config", () => {
const cfg = { index: 2, total: 5 };
const a = taskBelongsToShard("packages/app-core", cfg);
const b = taskBelongsToShard("packages/app-core", cfg);
expect(a).toBe(b);
});
test("a single shard (M=1) owns every task", () => {
const cfg = { index: 1, total: 1 };
for (const key of [
"packages/core",
"plugins/plugin-openai",
"packages/ui",
]) {
expect(taskBelongsToShard(key, cfg)).toBe(true);
}
});
test("every task lands in exactly one shard, across many M (partition invariant)", () => {
const keys = Array.from({ length: 300 }, (_, i) => `packages/pkg-${i}`);
for (const total of [2, 3, 4, 5, 8, 13]) {
for (const key of keys) {
const owners = [];
for (let index = 1; index <= total; index++) {
if (taskBelongsToShard(key, { index, total })) {
owners.push(index);
}
}
// Each key is claimed by precisely one shard — no gaps, no overlaps.
expect(owners).toHaveLength(1);
}
}
});
test("shards are reasonably balanced (no shard is wildly over/under-loaded)", () => {
const total = 4;
const keys = Array.from({ length: 800 }, (_, i) => `plugins/plugin-${i}`);
const counts = new Array(total).fill(0);
for (const key of keys) {
for (let index = 1; index <= total; index++) {
if (taskBelongsToShard(key, { index, total })) {
counts[index - 1]++;
}
}
}
expect(counts.reduce((a, b) => a + b, 0)).toBe(keys.length);
// Even split would be 200/shard; allow generous slack for hash variance.
for (const count of counts) {
expect(count).toBeGreaterThan(120);
expect(count).toBeLessThan(280);
}
});
});
describe("CI plugin sharding contract", () => {
const rootPackageJson = JSON.parse(
readFileSync(new URL("../../../package.json", import.meta.url), "utf8"),
);
const testWorkflow = readFileSync(
new URL("../../../.github/workflows/test.yml", import.meta.url),
"utf8",
);
const qualityWorkflow = readFileSync(
new URL("../../../.github/workflows/quality.yml", import.meta.url),
"utf8",
);
test("root test:plugins uses the shard-aware cross-package runner", () => {
const script = rootPackageJson.scripts["test:plugins"];
expect(script).toContain("run-all-tests.mjs");
expect(script).toContain("TEST_PACKAGE_FILTER='\\(plugins/'");
expect(script).toContain("TEST_SCRIPT_FILTER='^test$'");
expect(script).toContain("--only=test");
expect(script).toContain("--no-cloud");
expect(script).toContain("--concurrency=3");
});
test("Tests workflow shards plugin tests and keeps a stable aggregate check", () => {
expect(testWorkflow).toMatch(
/plugin-tests:\s+name:\s+Plugin Tests \(\$\{\{ matrix\.shard \}\}\/4\)[\s\S]*?strategy:[\s\S]*?fail-fast:\s+false[\s\S]*?matrix:[\s\S]*?shard:\s+\[1,\s*2,\s*3,\s*4\]/,
);
expect(testWorkflow).toMatch(/TEST_SHARD:\s+\$\{\{ matrix\.shard \}\}\/4/);
expect(testWorkflow).toMatch(
/plugin-tests-status:\s+name:\s+Plugin Tests[\s\S]*?needs:[\s\S]*?-\s+plugin-tests/,
);
expect(testWorkflow).toMatch(
/ci-ok:[\s\S]*?needs:[\s\S]*?-\s+plugin-tests-status/,
);
});
test("Quality workflow gates develop PRs with static scans and lint", () => {
expect(qualityWorkflow).toMatch(
/develop-static-gate:[\s\S]*?Prompt secret scan[\s\S]*?check:secrets[\s\S]*?UI determinism self-test[\s\S]*?audit:ui-determinism:self-test[\s\S]*?UI determinism gate[\s\S]*?audit:ui-determinism/,
);
expect(qualityWorkflow).toMatch(
/develop-lint-gate:[\s\S]*?install-command: bun install[\s\S]*?Run lint[\s\S]*?bun run lint/,
);
expect(qualityWorkflow).not.toMatch(
/develop-static-gate:[\s\S]*?Run lint[\s\S]*?bun run lint[\s\S]*?develop-lint-gate:/,
);
});
});
describe("run-all-tests plan mode", () => {
const runnerPath = new URL("../run-all-tests.mjs", import.meta.url);
function runPlan(args: string[], env: Record<string, string> = {}) {
return spawnSync(process.execPath, [runnerPath.pathname, ...args], {
cwd: new URL("../../..", import.meta.url).pathname,
encoding: "utf8",
env: {
...process.env,
TEST_LANE: "pr",
TEST_PACKAGE_FILTER: "",
TEST_SCRIPT_FILTER: "",
TEST_SHARD: "",
TEST_START_AT: "",
...env,
},
});
}
test("prints a JSON inventory without preparing services or starting package tests", () => {
const result = runPlan(
[
"--plan=json",
"--only=test",
"--no-cloud",
"--filter=^@elizaos/core \\(packages/core\\)#test$",
],
// If plan mode regresses and prepares PostgreSQL or spawns package
// scripts, this stripped PATH makes the side effect visible.
{ PATH: "" },
);
expect(result.status).toBe(0);
expect(result.stderr).toBe("");
expect(result.stdout).not.toContain("[eliza-test] START");
expect(result.stdout).not.toContain("PostgreSQL");
const plan = JSON.parse(result.stdout);
expect(plan.summary).toMatchObject({
lane: "pr",
only: "test",
noCloud: true,
taskCount: 1,
cloudStep: false,
});
expect(plan.tasks).toEqual([
{
packageName: "@elizaos/core",
relativeDir: "packages/core",
scriptName: "test",
label: "@elizaos/core (packages/core)#test",
parallelSafe: true,
},
]);
expect(plan.cloudStep).toBeNull();
});
test("bare --plan prints text and keeps the cloud step visible", () => {
const result = runPlan([
"--plan",
"--only=test",
"--filter=^@elizaos/core \\(packages/core\\)#test$",
]);
expect(result.status).toBe(0);
expect(result.stdout).toContain("[eliza-test] PLAN lane=pr only=test");
expect(result.stdout).toContain("[eliza-test] PLAN cloud-step=yes");
expect(result.stdout).toContain(
"[eliza-test] PLAN parallel @elizaos/core (packages/core)#test",
);
expect(result.stdout).not.toContain("[eliza-test] START");
});
});