mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 19:01:44 +00:00
136 lines
4.1 KiB
TypeScript
136 lines
4.1 KiB
TypeScript
import { EventEmitter } from "node:events";
|
||
import { PassThrough } from "node:stream";
|
||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
import { importFreshModule } from "../helpers/import-fresh.js";
|
||
|
||
afterEach(() => {
|
||
vi.useRealTimers();
|
||
vi.restoreAllMocks();
|
||
vi.resetModules();
|
||
});
|
||
|
||
describe("test planner executor", () => {
|
||
it("falls back to child exit when close never arrives", async () => {
|
||
vi.useRealTimers();
|
||
const stdout = new PassThrough();
|
||
const stderr = new PassThrough();
|
||
const fakeChild = Object.assign(new EventEmitter(), {
|
||
stdout,
|
||
stderr,
|
||
pid: 12345,
|
||
kill: vi.fn(),
|
||
});
|
||
const spawnMock = vi.fn(() => {
|
||
setTimeout(() => {
|
||
fakeChild.emit("exit", 0, null);
|
||
}, 0);
|
||
return fakeChild;
|
||
});
|
||
vi.doMock("node:child_process", () => ({
|
||
spawn: spawnMock,
|
||
}));
|
||
|
||
const { executePlan, createExecutionArtifacts } = await importFreshModule<
|
||
typeof import("../../scripts/test-planner/executor.mjs")
|
||
>(import.meta.url, "../../scripts/test-planner/executor.mjs?scope=exit-fallback");
|
||
const artifacts = createExecutionArtifacts({ OPENCLAW_TEST_CLOSE_GRACE_MS: "10" });
|
||
const executePromise = executePlan(
|
||
{
|
||
failurePolicy: "fail-fast",
|
||
passthroughMetadataOnly: true,
|
||
passthroughOptionArgs: [],
|
||
runtimeCapabilities: { isWindowsCi: false, isCI: false, isWindows: false },
|
||
},
|
||
{
|
||
env: { OPENCLAW_TEST_CLOSE_GRACE_MS: "10" },
|
||
artifacts,
|
||
},
|
||
);
|
||
|
||
await expect(executePromise).resolves.toMatchObject({
|
||
exitCode: 0,
|
||
summary: {
|
||
failedRunCount: 0,
|
||
},
|
||
});
|
||
expect(spawnMock).toHaveBeenCalledTimes(1);
|
||
|
||
artifacts.cleanupTempArtifacts();
|
||
});
|
||
|
||
it("collects failures across planned units when failure policy is collect-all", async () => {
|
||
vi.useRealTimers();
|
||
const children = [1, 2].map((pid, index) => {
|
||
const stdout = new PassThrough();
|
||
const stderr = new PassThrough();
|
||
return Object.assign(new EventEmitter(), {
|
||
stdout,
|
||
stderr,
|
||
pid,
|
||
kill: vi.fn(),
|
||
index,
|
||
});
|
||
});
|
||
let childIndex = 0;
|
||
const spawnMock = vi.fn(() => {
|
||
const child = children[childIndex];
|
||
childIndex += 1;
|
||
setTimeout(() => {
|
||
child.stdout.write(
|
||
child.index === 0
|
||
? " ❯ src/alpha.test.ts (1 test | 1 failed)\n"
|
||
: " ❯ src/beta.test.ts (1 test | 1 failed)\n",
|
||
);
|
||
child.emit("exit", 1, null);
|
||
child.emit("close", 1, null);
|
||
}, 0);
|
||
return child;
|
||
});
|
||
vi.doMock("node:child_process", () => ({
|
||
spawn: spawnMock,
|
||
}));
|
||
|
||
const { executePlan, createExecutionArtifacts } = await importFreshModule<
|
||
typeof import("../../scripts/test-planner/executor.mjs")
|
||
>(import.meta.url, "../../scripts/test-planner/executor.mjs?scope=collect-all");
|
||
const artifacts = createExecutionArtifacts({});
|
||
const report = await executePlan(
|
||
{
|
||
failurePolicy: "collect-all",
|
||
passthroughMetadataOnly: false,
|
||
passthroughOptionArgs: [],
|
||
targetedUnits: [],
|
||
parallelUnits: [
|
||
{ id: "unit-a", args: ["vitest", "run", "src/alpha.test.ts"] },
|
||
{ id: "unit-b", args: ["vitest", "run", "src/beta.test.ts"] },
|
||
],
|
||
serialUnits: [],
|
||
serialPrefixUnits: [],
|
||
shardCount: 1,
|
||
shardIndexOverride: null,
|
||
topLevelSingleShardAssignments: new Map(),
|
||
runtimeCapabilities: { isWindowsCi: false, isCI: false, isWindows: false },
|
||
topLevelParallelEnabled: false,
|
||
topLevelParallelLimit: 1,
|
||
deferredRunConcurrency: 1,
|
||
passthroughRequiresSingleRun: false,
|
||
},
|
||
{
|
||
env: {},
|
||
artifacts,
|
||
},
|
||
);
|
||
|
||
expect(spawnMock).toHaveBeenCalledTimes(2);
|
||
expect(report.exitCode).toBe(1);
|
||
expect(report.summary.failedRunCount).toBe(2);
|
||
expect(report.summary.failedTestFileCount).toBe(2);
|
||
expect(report.results.map((result) => result.classification)).toEqual([
|
||
"test-failure",
|
||
"test-failure",
|
||
]);
|
||
|
||
artifacts.cleanupTempArtifacts();
|
||
});
|
||
});
|