mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-29 19:01:44 +00:00
perf: speed up channel test runs
This commit is contained in:
@@ -23,6 +23,7 @@ This doc is a “how we test” guide:
|
||||
Most days:
|
||||
|
||||
- Full gate (expected before push): `pnpm build && pnpm check && pnpm test`
|
||||
- Faster local full-suite run on a roomy machine: `pnpm test:max`
|
||||
|
||||
When you touch tests or want extra confidence:
|
||||
|
||||
@@ -54,9 +55,8 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
- Should be fast and stable
|
||||
- Scheduler note:
|
||||
- `pnpm test` now keeps a small checked-in behavioral manifest for true pool/isolation overrides and a separate timing snapshot for the slowest unit files.
|
||||
- Shared unit coverage now defaults to `threads`, while the manifest keeps the measured fork-only exceptions and heavy singleton lanes explicit.
|
||||
- The shared extension lane still defaults to `threads`; the wrapper keeps explicit fork-only exceptions in `test/fixtures/test-parallel.behavior.json` when a file cannot safely share a non-isolated worker.
|
||||
- The channel suite (`vitest.channels.config.ts`) now also defaults to `threads`; the March 22, 2026 direct full-suite control run passed clean without channel-specific fork exceptions.
|
||||
- Shared unit, extension, channel, and gateway runs all stay on Vitest `forks`.
|
||||
- The wrapper keeps measured fork-isolated exceptions and heavy singleton lanes explicit in `test/fixtures/test-parallel.behavior.json`.
|
||||
- The wrapper peels the heaviest measured files into dedicated lanes instead of relying on a growing hand-maintained exclusion list.
|
||||
- Refresh the timing snapshot with `pnpm test:perf:update-timings` after major suite shape changes.
|
||||
- Embedded runner note:
|
||||
@@ -72,15 +72,16 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
sufficient substitute for those integration paths.
|
||||
- Pool note:
|
||||
- Base Vitest config still defaults to `forks`.
|
||||
- Unit wrapper lanes default to `threads`, with explicit manifest fork-only exceptions.
|
||||
- Extension scoped config defaults to `threads`.
|
||||
- Channel scoped config defaults to `threads`.
|
||||
- Unit, channel, extension, and gateway wrapper lanes all default to `forks`.
|
||||
- Unit, channel, and extension configs default to `isolate: false` for faster file startup.
|
||||
- `pnpm test` also passes `--isolate=false` at the wrapper level.
|
||||
- Opt back into Vitest file isolation with `OPENCLAW_TEST_ISOLATE=1 pnpm test`.
|
||||
- `OPENCLAW_TEST_NO_ISOLATE=0` or `OPENCLAW_TEST_NO_ISOLATE=false` also force isolated runs.
|
||||
- Fast-local iteration note:
|
||||
- `pnpm test:changed` runs the wrapper with `--changed origin/main`.
|
||||
- `pnpm test:changed:max` keeps the same changed-file filter but uses the wrapper's aggressive local planner profile.
|
||||
- `pnpm test:max` exposes that same planner profile for a full local run.
|
||||
- On Node 25, the normal local profile keeps top-level lane parallelism off; `pnpm test:max` re-enables it. On Node 22/24 LTS, normal local runs can also use top-level lane parallelism.
|
||||
- The base Vitest config marks the wrapper manifests/config files as `forceRerunTriggers` so changed-mode reruns stay correct when scheduler inputs change.
|
||||
- Vitest's filesystem module cache is now enabled by default for Node-side test reruns.
|
||||
- Opt out with `OPENCLAW_VITEST_FS_MODULE_CACHE=0` or `OPENCLAW_VITEST_FS_MODULE_CACHE=false` if you suspect stale transform cache behavior.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const transcribeFirstAudioMock = vi.fn();
|
||||
const DEFAULT_MODEL = "anthropic/claude-opus-4-5";
|
||||
@@ -9,7 +9,8 @@ vi.mock("./media-understanding.runtime.js", () => ({
|
||||
transcribeFirstAudio: (...args: unknown[]) => transcribeFirstAudioMock(...args),
|
||||
}));
|
||||
|
||||
let buildTelegramMessageContextForTest: typeof import("./bot-message-context.test-harness.js").buildTelegramMessageContextForTest;
|
||||
const { buildTelegramMessageContextForTest } =
|
||||
await import("./bot-message-context.test-harness.js");
|
||||
|
||||
async function buildGroupVoiceContext(params: {
|
||||
messageId: number;
|
||||
@@ -75,12 +76,6 @@ function expectAudioPlaceholderRendered(ctx: Awaited<ReturnType<typeof buildGrou
|
||||
}
|
||||
|
||||
describe("buildTelegramMessageContext audio transcript body", () => {
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({ buildTelegramMessageContextForTest } =
|
||||
await import("./bot-message-context.test-harness.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
transcribeFirstAudioMock.mockReset();
|
||||
});
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
let buildTelegramMessageContextForTest: typeof import("./bot-message-context.test-harness.js").buildTelegramMessageContextForTest;
|
||||
let clearRuntimeConfigSnapshot: typeof import("../../../src/config/config.js").clearRuntimeConfigSnapshot;
|
||||
let setRuntimeConfigSnapshot: typeof import("../../../src/config/config.js").setRuntimeConfigSnapshot;
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({ buildTelegramMessageContextForTest } = await import("./bot-message-context.test-harness.js"));
|
||||
({ clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } =
|
||||
await import("../../../src/config/config.js"));
|
||||
});
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
const { buildTelegramMessageContextForTest } =
|
||||
await import("./bot-message-context.test-harness.js");
|
||||
const { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } =
|
||||
await import("../../../src/config/config.js");
|
||||
|
||||
beforeEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
|
||||
@@ -10,6 +10,8 @@ const {
|
||||
telegramBotDepsForTest,
|
||||
telegramBotRuntimeForTest,
|
||||
} = harness;
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
@@ -136,10 +138,7 @@ async function queueChannelPostAlbum(
|
||||
}
|
||||
|
||||
describe("createTelegramBot channel_post media", () => {
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
const { createTelegramBot: createTelegramBotBase, setTelegramBotRuntimeForTest } =
|
||||
await import("./bot.js");
|
||||
beforeAll(() => {
|
||||
createTelegramBot = (opts) =>
|
||||
createTelegramBotBase({
|
||||
...opts,
|
||||
@@ -150,8 +149,7 @@ describe("createTelegramBot channel_post media", () => {
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const { setTelegramBotRuntimeForTest } = await import("./bot.js");
|
||||
beforeEach(() => {
|
||||
setTelegramBotRuntimeForTest(
|
||||
telegramBotRuntimeForTest as unknown as Parameters<typeof setTelegramBotRuntimeForTest>[0],
|
||||
);
|
||||
|
||||
@@ -34,13 +34,15 @@ const {
|
||||
throttlerSpy,
|
||||
useSpy,
|
||||
} = harness;
|
||||
let resolveTelegramFetch: typeof import("./fetch.js").resolveTelegramFetch;
|
||||
let setTelegramBotRuntimeForTest: typeof import("./bot.js").setTelegramBotRuntimeForTest;
|
||||
let createTelegramBotBase: typeof import("./bot.js").createTelegramBot;
|
||||
const { resolveTelegramFetch } = await import("./fetch.js");
|
||||
const {
|
||||
createTelegramBot: createTelegramBotBase,
|
||||
getTelegramSequentialKey,
|
||||
setTelegramBotRuntimeForTest,
|
||||
} = await import("./bot.js");
|
||||
let createTelegramBot: (
|
||||
opts: Parameters<typeof import("./bot.js").createTelegramBot>[0],
|
||||
) => ReturnType<typeof import("./bot.js").createTelegramBot>;
|
||||
let getTelegramSequentialKey: typeof import("./bot.js").getTelegramSequentialKey;
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
const loadWebMedia = getLoadWebMediaMock();
|
||||
@@ -81,15 +83,6 @@ describe("createTelegramBot", () => {
|
||||
beforeAll(() => {
|
||||
process.env.TZ = "UTC";
|
||||
});
|
||||
beforeAll(async () => {
|
||||
vi.resetModules();
|
||||
({ resolveTelegramFetch } = await import("./fetch.js"));
|
||||
({
|
||||
createTelegramBot: createTelegramBotBase,
|
||||
getTelegramSequentialKey,
|
||||
setTelegramBotRuntimeForTest,
|
||||
} = await import("./bot.js"));
|
||||
});
|
||||
afterAll(() => {
|
||||
process.env.TZ = ORIGINAL_TZ;
|
||||
});
|
||||
|
||||
@@ -705,6 +705,7 @@
|
||||
"test:auth:compat": "vitest run --config vitest.gateway.config.ts src/gateway/server.auth.compat-baseline.test.ts src/gateway/client.test.ts src/gateway/reconnect-gating.test.ts src/gateway/protocol/connect-error-details.test.ts",
|
||||
"test:build:singleton": "node scripts/test-built-plugin-singleton.mjs",
|
||||
"test:changed": "pnpm test -- --changed origin/main",
|
||||
"test:changed:max": "node scripts/test-parallel.mjs --profile max --changed origin/main",
|
||||
"test:channels": "node scripts/test-parallel.mjs --surface channels",
|
||||
"test:contracts": "pnpm test:contracts:channels && pnpm test:contracts:plugins",
|
||||
"test:contracts:channels": "OPENCLAW_TEST_PROFILE=serial pnpm test -- src/channels/plugins/contracts",
|
||||
@@ -735,6 +736,7 @@
|
||||
"test:install:e2e:openai": "OPENCLAW_E2E_MODELS=openai bash scripts/test-install-sh-e2e-docker.sh",
|
||||
"test:install:smoke": "bash scripts/test-install-sh-docker.sh",
|
||||
"test:live": "OPENCLAW_LIVE_TEST=1 vitest run --config vitest.live.config.ts",
|
||||
"test:max": "node scripts/test-parallel.mjs --profile max",
|
||||
"test:parallels:linux": "bash scripts/e2e/parallels-linux-smoke.sh",
|
||||
"test:parallels:macos": "bash scripts/e2e/parallels-macos-smoke.sh",
|
||||
"test:parallels:npm-update": "bash scripts/e2e/parallels-npm-update-smoke.sh",
|
||||
|
||||
20
test/fixtures/test-parallel.behavior.json
vendored
20
test/fixtures/test-parallel.behavior.json
vendored
@@ -74,6 +74,22 @@
|
||||
"file": "extensions/whatsapp/src/inbound.media.test.ts",
|
||||
"reason": "This WhatsApp inbound media suite is green alone but can inherit polluted media and inbound parsing state from the shared channel lane; keep it in its own forked lane for deterministic CI."
|
||||
},
|
||||
{
|
||||
"file": "extensions/whatsapp/src/monitor-inbox.streams-inbound-messages.test.ts",
|
||||
"reason": "This WhatsApp monitor inbox stream suite is green alone but can fail after the shared channel lane reuses inbound parsing state; keep it in its own forked lane for deterministic reruns and to trim the serial shared channels batch."
|
||||
},
|
||||
{
|
||||
"file": "extensions/whatsapp/src/monitor-inbox.captures-media-path-image-messages.test.ts",
|
||||
"reason": "This WhatsApp inbox media-path suite spends about two seconds in assertions and grows worker RSS near 0.9 GiB alone; keep it in its own forked lane so the shared channels batch shrinks and top-level concurrency can overlap the hotspot."
|
||||
},
|
||||
{
|
||||
"file": "extensions/whatsapp/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts",
|
||||
"reason": "This WhatsApp inbox allow-from rejection suite is a heavy shared hotspot with about two seconds of test work and high worker RSS; keep it in its own forked lane so it can overlap instead of extending the serial shared channels batch."
|
||||
},
|
||||
{
|
||||
"file": "extensions/whatsapp/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test.ts",
|
||||
"reason": "This WhatsApp inbox allow-from acceptance suite is another heavy shared hotspot with about two seconds of test work and high worker RSS; keep it in its own forked lane so it can overlap instead of extending the serial shared channels batch."
|
||||
},
|
||||
{
|
||||
"file": "src/browser/chrome.test.ts",
|
||||
"reason": "This Chrome helper suite is green alone but can inherit stale fetch, websocket, or timer state from the shared channel lane; keep it isolated so its CDP timeout assertions stay deterministic."
|
||||
@@ -94,6 +110,10 @@
|
||||
"file": "extensions/telegram/src/fetch.test.ts",
|
||||
"reason": "This Telegram transport suite measured ~759.3 MiB RSS growth locally; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
|
||||
},
|
||||
{
|
||||
"file": "extensions/telegram/src/sendchataction-401-backoff.test.ts",
|
||||
"reason": "This Telegram send-chat-action backoff suite hoists infra-runtime sleep mocks and remains a relatively heavy shared hotspot; keep it isolated so top-level concurrency can overlap it instead of extending the shared channels batch."
|
||||
},
|
||||
{
|
||||
"file": "extensions/telegram/src/monitor.test.ts",
|
||||
"reason": "This Telegram monitor suite measured ~748.4 MiB RSS growth locally; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
|
||||
|
||||
Reference in New Issue
Block a user