mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
test: stabilize alpha plugin prerelease gates
This commit is contained in:
@@ -1894,7 +1894,6 @@ describe("TelegramPollingSession", () => {
|
||||
});
|
||||
|
||||
it("keeps active spooled lanes blocked across isolated ingress restarts", async () => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
const abort = new AbortController();
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-telegram-spool-"));
|
||||
let releaseRegularTurn: (() => void) | undefined;
|
||||
@@ -1921,24 +1920,32 @@ describe("TelegramPollingSession", () => {
|
||||
},
|
||||
});
|
||||
|
||||
let workerTaskCalls = 0;
|
||||
const createWorker = vi.fn(() => ({
|
||||
onMessage: vi.fn(() => () => undefined),
|
||||
stop: vi.fn(async () => undefined),
|
||||
task: vi.fn(async () => {
|
||||
workerTaskCalls += 1;
|
||||
if (workerTaskCalls === 1) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve) => {
|
||||
abort.signal.addEventListener("abort", () => resolve(), { once: true });
|
||||
});
|
||||
}),
|
||||
}));
|
||||
const workerStops: Array<() => void> = [];
|
||||
const createWorker = vi.fn(() => {
|
||||
let stopWorker: (() => void) | undefined;
|
||||
const workerDone = new Promise<void>((resolve) => {
|
||||
stopWorker = resolve;
|
||||
});
|
||||
workerStops.push(() => {
|
||||
stopWorker?.();
|
||||
});
|
||||
return {
|
||||
onMessage: vi.fn(() => () => undefined),
|
||||
stop: vi.fn(async () => {
|
||||
stopWorker?.();
|
||||
}),
|
||||
task: vi.fn(async () => {
|
||||
await workerDone;
|
||||
}),
|
||||
};
|
||||
});
|
||||
const watchdogHarness = installPollingStallWatchdogHarness([0]);
|
||||
let runPromise: Promise<void> | undefined;
|
||||
|
||||
try {
|
||||
const session = createPollingSession({
|
||||
abortSignal: abort.signal,
|
||||
stallThresholdMs: 30_000,
|
||||
isolatedIngress: {
|
||||
enabled: true,
|
||||
spoolDir: tempDir,
|
||||
@@ -1947,14 +1954,15 @@ describe("TelegramPollingSession", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const runPromise = session.runUntilAbort();
|
||||
runPromise = session.runUntilAbort();
|
||||
await vi.waitFor(() => expect(handleUpdate).toHaveBeenCalledTimes(1));
|
||||
await vi.advanceTimersByTimeAsync(16_000);
|
||||
const watchdog = await watchdogHarness.waitForWatchdog();
|
||||
watchdogHarness.setNow(31_000);
|
||||
watchdog();
|
||||
await vi.waitFor(() => expect(createWorker).toHaveBeenCalledTimes(2));
|
||||
expect(handleUpdate).toHaveBeenCalledTimes(1);
|
||||
|
||||
releaseRegularTurn?.();
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
await vi.waitFor(async () =>
|
||||
expect(
|
||||
(await listTelegramSpooledUpdates({ spoolDir: tempDir })).map(
|
||||
@@ -1963,11 +1971,15 @@ describe("TelegramPollingSession", () => {
|
||||
).toEqual([]),
|
||||
);
|
||||
abort.abort();
|
||||
await vi.advanceTimersByTimeAsync(20_000);
|
||||
await runPromise;
|
||||
} finally {
|
||||
releaseRegularTurn?.();
|
||||
vi.useRealTimers();
|
||||
for (const stopWorker of workerStops) {
|
||||
stopWorker();
|
||||
}
|
||||
abort.abort();
|
||||
watchdogHarness.restore();
|
||||
await runPromise?.catch(() => undefined);
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,6 +9,9 @@ import { isDirectScriptRun, runVitestBatch } from "./lib/vitest-batch-runner.mjs
|
||||
|
||||
const FS_MODULE_CACHE_PATH_ENV_KEY = "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH";
|
||||
const PARALLEL_ENV_KEY = "OPENCLAW_EXTENSION_BATCH_PARALLEL";
|
||||
const TARGET_CHUNK_SIZE_ENV_KEY = "OPENCLAW_EXTENSION_BATCH_TARGET_CHUNK_SIZE";
|
||||
const TELEGRAM_VITEST_CONFIG = "test/vitest/vitest.extension-telegram.config.ts";
|
||||
const TELEGRAM_TARGET_CHUNK_SIZE = 40;
|
||||
const ALLOW_NO_TESTS_FLAG = "--allow-no-tests";
|
||||
const ALLOW_EMPTY_AFTER_EXCLUDE_FLAG = "--allow-empty-after-exclude";
|
||||
|
||||
@@ -130,8 +133,8 @@ export function parseExactVitestExcludePaths(vitestArgs) {
|
||||
return excludePaths;
|
||||
}
|
||||
|
||||
function resolveGroupTargets(group, exactExcludePaths) {
|
||||
if (exactExcludePaths.size === 0) {
|
||||
function resolveGroupTargets(group, exactExcludePaths, forceFileTargets = false) {
|
||||
if (exactExcludePaths.size === 0 && !forceFileTargets) {
|
||||
return group.roots;
|
||||
}
|
||||
|
||||
@@ -143,8 +146,28 @@ function resolveGroupTargets(group, exactExcludePaths) {
|
||||
return testFiles.filter((file) => !exactExcludePaths.has(file));
|
||||
}
|
||||
|
||||
function resolveGroupTargetChunkSize(group, env) {
|
||||
const override = parsePositiveInt(env[TARGET_CHUNK_SIZE_ENV_KEY]);
|
||||
if (override !== null) {
|
||||
return override;
|
||||
}
|
||||
return group.config === TELEGRAM_VITEST_CONFIG ? TELEGRAM_TARGET_CHUNK_SIZE : null;
|
||||
}
|
||||
|
||||
function chunkTargets(targets, chunkSize) {
|
||||
if (!chunkSize || targets.length <= chunkSize) {
|
||||
return [targets];
|
||||
}
|
||||
const chunks = [];
|
||||
for (let index = 0; index < targets.length; index += chunkSize) {
|
||||
chunks.push(targets.slice(index, index + chunkSize));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
async function runPlanGroup(group, params) {
|
||||
const targets = resolveGroupTargets(group, params.exactExcludePaths);
|
||||
const targetChunkSize = resolveGroupTargetChunkSize(group, params.env);
|
||||
const targets = resolveGroupTargets(group, params.exactExcludePaths, targetChunkSize !== null);
|
||||
if (targets.length === 0) {
|
||||
console.error(`[test-extension-batch] ${group.config}: no test files remain after excludes`);
|
||||
return params.allowEmptyAfterExclude ? 0 : 1;
|
||||
@@ -153,17 +176,29 @@ async function runPlanGroup(group, params) {
|
||||
console.log(
|
||||
`[test-extension-batch] ${group.config}: ${group.extensionIds.join(", ")} (${targets.length} targets)`,
|
||||
);
|
||||
return await params.runGroup({
|
||||
args: params.vitestArgs,
|
||||
config: group.config,
|
||||
env: createGroupEnv({
|
||||
baseEnv: params.env,
|
||||
group,
|
||||
groupIndex: params.groupIndex,
|
||||
useDedicatedCache: params.useDedicatedCache,
|
||||
}),
|
||||
targets,
|
||||
});
|
||||
const targetChunks = chunkTargets(targets, targetChunkSize);
|
||||
for (const [index, chunk] of targetChunks.entries()) {
|
||||
if (targetChunks.length > 1) {
|
||||
console.log(
|
||||
`[test-extension-batch] ${group.config}: chunk ${index + 1}/${targetChunks.length} (${chunk.length} targets)`,
|
||||
);
|
||||
}
|
||||
const exitCode = await params.runGroup({
|
||||
args: params.vitestArgs,
|
||||
config: group.config,
|
||||
env: createGroupEnv({
|
||||
baseEnv: params.env,
|
||||
group,
|
||||
groupIndex: params.groupIndex,
|
||||
useDedicatedCache: params.useDedicatedCache,
|
||||
}),
|
||||
targets: chunk,
|
||||
});
|
||||
if (exitCode !== 0) {
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export async function runExtensionBatchPlan(batchPlan, params = {}) {
|
||||
|
||||
@@ -675,6 +675,41 @@ describe("scripts/test-extension.mjs", () => {
|
||||
expect(runParams.targets).toContain("extensions/codex/src/app-server/client.test.ts");
|
||||
});
|
||||
|
||||
it("chunks large extension batch groups into separate Vitest processes", async () => {
|
||||
const runGroup = vi.fn<() => Promise<number>>().mockResolvedValue(0);
|
||||
const result = await runExtensionBatchPlan(
|
||||
{
|
||||
extensionCount: 1,
|
||||
extensionIds: ["telegram"],
|
||||
estimatedCost: 125,
|
||||
hasTests: true,
|
||||
planGroups: [
|
||||
{
|
||||
config: "test/vitest/vitest.extension-telegram.config.ts",
|
||||
estimatedCost: 125,
|
||||
extensionIds: ["telegram"],
|
||||
roots: [bundledPluginRoot("telegram")],
|
||||
testFileCount: 125,
|
||||
},
|
||||
],
|
||||
testFileCount: 125,
|
||||
},
|
||||
{
|
||||
env: { OPENCLAW_EXTENSION_BATCH_TARGET_CHUNK_SIZE: "50" },
|
||||
runGroup,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toBe(0);
|
||||
expect(runGroup).toHaveBeenCalledTimes(3);
|
||||
expect(runGroup.mock.calls.map(([params]) => params.targets)).toEqual([
|
||||
expect.arrayContaining([bundledPluginFile("telegram", "index.test.ts")]),
|
||||
expect.any(Array),
|
||||
expect.any(Array),
|
||||
]);
|
||||
expect(runGroup.mock.calls.map(([params]) => params.targets.length)).toEqual([50, 50, 25]);
|
||||
});
|
||||
|
||||
it("fails extension batch groups when exact excludes remove every test", async () => {
|
||||
const runGroup = vi.fn<() => Promise<number>>().mockResolvedValue(0);
|
||||
const result = await runExtensionBatchPlan(
|
||||
|
||||
Reference in New Issue
Block a user