test: drop planner fixtures and coverage

This commit is contained in:
Peter Steinberger
2026-04-03 12:42:26 +01:00
parent d21d859ded
commit c80c1cf56f
16 changed files with 132 additions and 5889 deletions

View File

@@ -1,84 +0,0 @@
{
"config": "vitest.extensions.config.ts",
"generatedAt": "2026-04-03T04:18:33.578Z",
"defaultMinDeltaKb": 1048576,
"lane": "extensions, extensions-batch-*",
"files": {
"extensions/feishu/src/bot.test.ts": {
"deltaKb": 2516582,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/nextcloud-talk/src/setup.test.ts": {
"deltaKb": 2474639,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/acpx/src/runtime.test.ts": {
"deltaKb": 1824522,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/browser/src/node-host/invoke-browser.test.ts": {
"deltaKb": 1719665,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/msteams/src/graph-members.test.ts": {
"deltaKb": 1646264,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/msteams/src/token.test.ts": {
"deltaKb": 1625293,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/msteams/src/graph-messages.test.ts": {
"deltaKb": 1625293,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/bluebubbles/src/send.test.ts": {
"deltaKb": 1625293,
"sources": ["gh-job-69804189668:extensions-batch-19-shard-6"]
},
"extensions/googlechat/src/approval-auth.test.ts": {
"deltaKb": 1614807,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/diffs/src/tool.test.ts": {
"deltaKb": 1604321,
"sources": ["checks-fast-extensions:2026-04-03"]
},
"extensions/memory-core/src/memory/mmr.test.ts": {
"deltaKb": 1572864,
"sources": ["gh-job-69804189668:extensions-batch-2-shard-6"]
},
"extensions/diffs/src/config.test.ts": {
"deltaKb": 1572864,
"sources": ["gh-job-69804189668:extensions-batch-20-shard-6"]
},
"extensions/voice-call/src/webhook-security.test.ts": {
"deltaKb": 1541407,
"sources": ["gh-job-69804189666:extensions-batch-5-shard-1"]
},
"extensions/nostr/src/nostr-bus.fuzz.test.ts": {
"deltaKb": 1509949,
"sources": ["gh-job-69804189668:extensions-batch-11-shard-6"]
},
"extensions/memory-lancedb/index.test.ts": {
"deltaKb": 1499464,
"sources": ["gh-job-69804189668:extensions-batch-12-shard-6"]
},
"extensions/google/provider-models.test.ts": {
"deltaKb": 1478492,
"sources": ["gh-job-69804189681:extensions-batch-11-shard-5"]
},
"extensions/fal/image-generation-provider.test.ts": {
"deltaKb": 1447035,
"sources": ["gh-job-69804189668:extensions-batch-13-shard-6"]
},
"extensions/matrix/src/matrix/format.test.ts": {
"deltaKb": 1447035,
"sources": ["gh-job-69804189668:extensions-batch-23-shard-6"]
},
"extensions/minimax/model-definitions.test.ts": {
"deltaKb": 1447035,
"sources": ["gh-job-69804189676:extensions-batch-15-shard-4"]
}
}
}

View File

@@ -1,62 +0,0 @@
{
"config": "vitest.unit.config.ts",
"generatedAt": "2026-03-24T17:44:58.770Z",
"defaultMinDeltaKb": 262144,
"lane": "unit-fast, unit-*",
"files": {
"src/infra/outbound/channel-resolution.test.ts": {
"deltaKb": 1111491,
"sources": ["openclaw-test-memory-trace:unit-heavy-2"]
},
"src/cron/isolated-agent/run.skill-filter.test.ts": {
"deltaKb": 1069548,
"sources": ["openclaw-test-memory-trace:unit-run.skill-filter-memory-isolated"]
},
"src/infra/outbound/deliver.test.ts": {
"deltaKb": 1059062,
"sources": ["openclaw-deliver-mem:unit"]
},
"src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts": {
"deltaKb": 1048576,
"sources": [
"openclaw-test-memory-trace:unit-isolated-agent.uses-last-non-empty-agent-text-as-memory-isolated"
]
},
"src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts": {
"deltaKb": 1026355,
"sources": [
"openclaw-test-memory-trace:unit-isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true-memory-isolated"
]
},
"src/cron/isolated-agent/run.cron-model-override.test.ts": {
"deltaKb": 946790,
"sources": ["openclaw-test-memory-trace:unit-run.cron-model-override-memory-isolated"]
},
"src/channels/plugins/plugins-core.test.ts": {
"deltaKb": 792269,
"sources": ["openclaw-plugins-core-mem:unit"]
},
"src/plugins/manifest-registry.test.ts": {
"deltaKb": 684749,
"sources": ["openclaw-manifest-registry-mem:unit"]
},
"src/plugins/install.test.ts": {
"deltaKb": 510874,
"sources": ["openclaw-test-memory-trace:unit-install-memory-isolated"]
},
"ui/src/ui/views/chat.test.ts": {
"deltaKb": 509338,
"sources": ["openclaw-test-memory-trace:unit-chat-memory-isolated"]
},
"src/infra/provider-usage.auth.normalizes-keys.test.ts": {
"deltaKb": 494080,
"sources": [
"openclaw-test-memory-trace:unit-provider-usage.auth.normalizes-keys-memory-isolated"
]
},
"src/plugins/conversation-binding.test.ts": {
"deltaKb": 485786,
"sources": ["openclaw-test-memory-trace:unit-conversation-binding-memory-isolated"]
}
}
}

View File

@@ -1,280 +0,0 @@
{
"base": {
"threadPinned": [
{
"file": "src/commands/backup.test.ts",
"reason": "Measured ~16% faster under threads than forks on base config after removing process.chdir() from the test."
},
{
"file": "src/auto-reply/reply/commands-acp/install-hints.test.ts",
"reason": "Measured ~35% faster under threads than forks on base config after removing process.chdir() from the test."
},
{
"file": "src/agents/pi-hooks/compaction-safeguard.test.ts",
"reason": "Measured ~27% faster under threads than forks on base config after removing process.chdir() from the test."
},
{
"file": "src/auto-reply/reply/followup-runner.test.ts",
"reason": "This followup-runner suite is green under direct base-thread runs but can hang when targeted through the wrapper's default base fork lane; pin it to base threads so wrapper-targeted reruns match the stable raw-threads behavior."
}
]
},
"channels": {
"isolatedPrefixes": ["extensions/discord/src/monitor/"],
"isolated": [
{
"file": "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts",
"reason": "This Discord preflight suite hoists conversation-runtime ACP binding mocks; keep it in its own forked channel lane so shared workers do not reuse monitor modules with incompatible runtime stubs."
},
{
"file": "extensions/discord/src/monitor/monitor.threading-utils.test.ts",
"reason": "This Discord threading utility suite exercises gateway and presence caches plus auto-thread helpers; keep it in its own forked channel lane so shared workers cannot inherit mutated monitor module state."
},
{
"file": "extensions/discord/src/monitor/monitor.agent-components.test.ts",
"reason": "This Discord agent-components suite hoists security and conversation runtime mocks for DM component authorization; keep it in its own forked channel lane so shared workers do not inherit incompatible Discord runtime stubs."
},
{
"file": "extensions/discord/src/monitor/native-command.model-picker.test.ts",
"reason": "This Discord model-picker interaction suite relies on isolated native-command fallback state and DM channel enums; keep it in its own forked channel lane so shared workers do not reuse polluted command runtime modules."
},
{
"file": "extensions/discord/src/monitor/native-command.plugin-dispatch.test.ts",
"reason": "This Discord native plugin-dispatch suite hoists conversation and reply runtime mocks for slash-command routing; keep it in its own forked channel lane so shared workers do not reuse mismatched native-command modules."
},
{
"file": "extensions/discord/src/monitor/agent-components.wildcard.test.ts",
"reason": "This Discord wildcard component suite is green alone but can inherit polluted component-registration parser state from the shared channel lane; keep it in its own forked lane for deterministic CI."
},
{
"file": "extensions/discord/src/send.creates-thread.test.ts",
"reason": "This Discord send/create-thread suite hoists web-media mocks and retries against rate-limit seams; keep it isolated so shared channel workers do not leak send-module state into its retry assertions."
},
{
"file": "extensions/discord/src/components.test.ts",
"reason": "This Discord component builder suite is green alone but can inherit polluted component constructor state from the shared channel lane; keep it in its own forked lane for deterministic CI."
},
{
"file": "extensions/discord/src/api.test.ts",
"reason": "This Discord API retry suite is green alone but can inherit timer or fetch retry contamination from the shared channel lane; keep it in its own forked lane for deterministic CI."
},
{
"file": "extensions/discord/src/monitor/provider.skill-dedupe.test.ts",
"reason": "This Discord provider skill-dedupe suite depends on agent-component classes during provider import; keep it isolated so shared channel workers do not reuse partially mocked component modules."
},
{
"file": "extensions/discord/src/monitor/monitor.test.ts",
"reason": "This Discord monitor interaction suite imports agent-component classes directly and is green alone but can inherit partially mocked Discord component constructors from the shared channel lane."
},
{
"file": "extensions/whatsapp/src/inbound.test.ts",
"reason": "This WhatsApp inbound helper suite is green alone but can inherit polluted web-message parsing state from the shared channel lane; keep it in its own forked lane for deterministic CI."
},
{
"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."
},
{
"file": "src/browser/server-context.remote-tab-ops.test.ts",
"reason": "This browser remote-tab aggregate wrapper measured ~1.03 GiB RSS growth locally even after explicit Playwright teardown; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
},
{
"file": "extensions/telegram/src/bot.create-telegram-bot.test.ts",
"reason": "This Telegram bot bootstrap suite is green alone but can inherit stale command-registration spies and bot runtime state from the shared channel lane; keep it isolated for deterministic command assertions."
},
{
"file": "extensions/telegram/src/bot.test.ts",
"reason": "This Telegram bot runtime suite measured ~819.9 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."
},
{
"file": "extensions/telegram/src/webhook.test.ts",
"reason": "This Telegram webhook suite hoists grammY bot and InputFile mocks; keep it in its own forked channel lane so shared workers do not reuse telegram runtime modules with mismatched constructors."
},
{
"file": "extensions/slack/src/action-runtime.test.ts",
"reason": "This Slack action runtime suite measured ~766.2 MiB RSS growth locally; keep it in its own forked channel lane so the shared channels worker can recycle immediately after the hotspot file."
}
]
},
"extensions": {
"isolated": [
{
"file": "extensions/matrix/src/matrix/sdk.test.ts",
"reason": "This suite hoists a matrix-js-sdk module mock that can leak into later Matrix extension files when they share a non-isolated worker."
},
{
"file": "extensions/matrix/src/matrix/client/file-sync-store.test.ts",
"reason": "Matrix sdk.test.ts hoists a matrix-js-sdk module mock; keep the sync-store persistence regression in its own forked lane so non-isolated extension workers stay deterministic."
},
{
"file": "extensions/matrix/src/matrix/client.test.ts",
"reason": "This Matrix client suite measured about 736.9 MiB RSS growth in the shared extensions lane during heapsnapshot tracing; keep it isolated so the shared Matrix extension worker can recycle instead of retaining Matrix runtime state."
},
{
"file": "extensions/matrix/src/matrix/monitor/startup.test.ts",
"reason": "This Matrix startup suite was missing from the extension timing snapshot and repeated heapsnapshots in the shared extensions lane retained large Vite SSR transform strings plus system / Context growth; keep it isolated so the shared Matrix extension worker can recycle immediately after startup coverage."
},
{
"file": "extensions/matrix/src/matrix/monitor/events.test.ts",
"reason": "This Matrix monitor events suite pulls in the verification-routing graph and, when paired in the shared extensions lane, spends ~23s in import time while pushing the worker toward ~1 GiB RSS; keep it isolated so the shared lane can recycle instead of retaining the Matrix monitor runtime graph."
},
{
"file": "extensions/nextcloud-talk/src/monitor.replay.test.ts",
"reason": "The replay-handling regression is green alone but can inherit disturbed global stream/Response state from the shared extensions lane, so keep it in its own forked lane for deterministic CI."
},
{
"file": "extensions/duckduckgo/src/ddg-search-provider.test.ts",
"reason": "This provider test hoists a client mock and imports the provider lazily; keep it in its own forked lane so shared extension workers do not reuse a previously cached module under test."
},
{
"file": "extensions/browser/src/browser/chrome.test.ts",
"reason": "This Chrome CDP helper suite opens mock WebSocket servers and stubs global fetch state; keep it isolated so shared extension workers do not inherit stale browser probe state from neighboring files."
},
{
"file": "extensions/firecrawl/src/firecrawl-scrape-tool.test.ts",
"reason": "This scrape-tool suite hoists client mocks and imports the tool lazily; keep it in its own forked lane so shared extension workers cannot bypass the mocked client via cached modules."
},
{
"file": "extensions/firecrawl/src/firecrawl-search-provider.test.ts",
"reason": "This provider suite hoists the Firecrawl client mock and imports lazily; keep it isolated so shared extension workers do not reuse stale provider modules."
},
{
"file": "extensions/firecrawl/src/firecrawl-search-tool.test.ts",
"reason": "This tool suite hoists the Firecrawl client mock and imports lazily; keep it isolated so shared extension workers cannot leak cached implementations across files."
},
{
"file": "extensions/feishu/src/monitor.acp-init-failure.lifecycle.test.ts",
"reason": "This Feishu websocket lifecycle suite can stall only in the shared extensions swarm when reconnect state bleeds across files; keep it isolated so ACP init-failure teardown stays deterministic."
},
{
"file": "extensions/feishu/src/monitor.bot-menu.lifecycle.test.ts",
"reason": "This Feishu websocket lifecycle suite can stall only in the shared extensions swarm when reconnect state bleeds across files; keep it isolated so bot-menu lifecycle teardown stays deterministic."
},
{
"file": "extensions/feishu/src/monitor.broadcast.reply-once.lifecycle.test.ts",
"reason": "This Feishu websocket lifecycle suite can stall only in the shared extensions swarm when reconnect state bleeds across files; keep it isolated so broadcast reply-once teardown stays deterministic."
},
{
"file": "extensions/feishu/src/monitor.card-action.lifecycle.test.ts",
"reason": "This Feishu websocket lifecycle suite shares the same reconnect helpers and module state; keep it isolated with the other Feishu lifecycle files so card-action runs do not inherit cross-file websocket state."
},
{
"file": "extensions/feishu/src/monitor.reply-once.lifecycle.test.ts",
"reason": "This Feishu websocket lifecycle suite can stall only in the shared extensions swarm when reconnect state bleeds across files; keep it isolated so reply-once teardown stays deterministic."
},
{
"file": "extensions/line/src/download.test.ts",
"reason": "This LINE media-download suite depends on hoisted SDK mocks; run it in its own forked lane so shared extension workers do not fall back to the real SDK client."
},
{
"file": "extensions/line/src/monitor.lifecycle.test.ts",
"reason": "This LINE lifecycle suite hoists runtime and route-registration mocks; keep it isolated so shared extension workers do not reuse cached monitor modules with real dependencies."
},
{
"file": "extensions/line/src/probe.test.ts",
"reason": "This LINE probe suite depends on hoisted SDK mocks; keep it isolated so shared extension workers do not reuse cached probe modules with the real SDK client."
},
{
"file": "extensions/line/src/send.test.ts",
"reason": "This LINE send suite hoists SDK and account/token mocks; keep it isolated so shared extension workers do not reuse cached send modules that bypass those mocks."
},
{
"file": "extensions/telegram/src/fetch.test.ts",
"reason": "This Telegram transport suite is extension-owned and still measures high RSS locally; keep it in its own forked extensions lane so the shared worker can recycle immediately after the hotspot."
},
{
"file": "extensions/tavily/src/tavily-extract-tool.test.ts",
"reason": "This extract-tool suite hoists the Tavily client mock and imports lazily; keep it isolated so shared extension workers do not reuse cached tool modules and hit real API-key resolution."
},
{
"file": "extensions/tavily/src/tavily-search-provider.test.ts",
"reason": "This provider suite hoists the Tavily client mock and imports lazily; keep it isolated so shared extension workers do not reuse stale provider modules."
},
{
"file": "extensions/tavily/src/tavily-search-tool.test.ts",
"reason": "This search-tool suite hoists the Tavily client mock and imports lazily; keep it isolated so shared extension workers do not bypass the mocked client via cached modules."
},
{
"file": "extensions/xai/provider.contract.test.ts",
"reason": "This xAI provider contract suite validates the bundled provider registration produced by defineSingleProviderPluginEntry; keep it in its own forked extension lane so shared workers cannot poison that registry with hoisted plugin-sdk mocks from unrelated extension files."
},
{
"file": "extensions/xai/web-search-provider.contract.test.ts",
"reason": "This xAI web-search contract suite validates the bundled Grok web-search registration from the same xAI plugin entry; keep it isolated so shared extension workers do not reuse hoisted plugin-sdk web-search mocks from unrelated extension files."
}
]
},
"unit": {
"isolated": [
{
"file": "src/cli/qr-dashboard.integration.test.ts",
"reason": "This CLI integration suite hoists runtime/config mocks and resets module state between qr and dashboard command imports; keep it in its own forked lane so shared unit-fast workers stay deterministic."
},
{
"file": "src/config/config.legacy-config-detection.rejects-routing-allowfrom.test.ts",
"reason": "This config validation hotspot passes when rerun directly but can time out inside the shared unit-fast swarm under local and CI load; keep it in its own forked lane so schema validation stays deterministic."
},
{
"file": "src/config/plugin-auto-enable.test.ts",
"reason": "This plugin auto-enable suite validates config schema and plugin metadata across many cases; keep it isolated so the shared unit-fast batches do not starve its longer validation loop."
},
{
"file": "src/infra/matrix-legacy-crypto.test.ts",
"reason": "This Matrix legacy crypto migration suite touches temp-home filesystem fixtures and can time out when it shares the busy unit-fast lane; keep it isolated for deterministic migration timing."
},
{
"file": "src/infra/state-migrations.test.ts",
"reason": "This legacy state migration suite builds multi-surface fixture trees and passes when rerun directly; keep it isolated so shared unit-fast pressure does not push it past the timeout budget."
},
{
"file": "src/plugins/commands.test.ts",
"reason": "This plugin command suite contains a broad parameterized binding matrix and can time out only under the congested unit-fast lane; keep it isolated so command binding coverage remains deterministic."
},
{
"file": "src/plugin-sdk/package-contract-guardrails.test.ts",
"reason": "This packed-artifact contract suite shells out through npm pack/install and passes alone but can overrun the shared unit-fast lane when it competes with other package-heavy unit batches."
},
{
"file": "src/secrets/runtime.coverage.test.ts",
"reason": "This secrets runtime coverage sweep exercises every active registry target and passes in direct reruns; keep it isolated so the shared unit-fast lane does not starve its filesystem and config setup."
},
{
"file": "src/secrets/runtime.test.ts",
"reason": "This secrets runtime snapshot suite can time out only inside the shared unit-fast swarm; keep it isolated so secret-resolution setup remains deterministic."
},
{
"file": "src/security/audit.test.ts",
"reason": "This security audit suite runs a wide config matrix and direct reruns stay green; keep it isolated so shared unit-fast congestion does not push its longest cases past the timeout budget."
}
],
"threadPinned": []
}
}

View File

@@ -1,853 +0,0 @@
{
"config": "vitest.channels.config.ts",
"generatedAt": "2026-03-31T10:45:00Z",
"defaultDurationMs": 3000,
"files": {
"extensions/discord/src/account-inspect.test.ts": {
"durationMs": 3
},
"extensions/discord/src/accounts.test.ts": {
"durationMs": 3
},
"extensions/discord/src/actions/runtime.moderation.authz.test.ts": {
"durationMs": 6
},
"extensions/discord/src/actions/runtime.presence.test.ts": {
"durationMs": 7
},
"extensions/discord/src/actions/runtime.test.ts": {
"durationMs": 14
},
"extensions/discord/src/api.test.ts": {
"durationMs": 9
},
"extensions/discord/src/audit.test.ts": {
"durationMs": 7
},
"extensions/discord/src/channel-actions.test.ts": {
"durationMs": 5
},
"extensions/discord/src/channel.test.ts": {
"durationMs": 950
},
"extensions/discord/src/chunk.test.ts": {
"durationMs": 5
},
"extensions/discord/src/client.test.ts": {
"durationMs": 4
},
"extensions/discord/src/components.test.ts": {
"durationMs": 400
},
"extensions/discord/src/directory-live.test.ts": {
"durationMs": 6
},
"extensions/discord/src/draft-chunking.test.ts": {
"durationMs": 3
},
"extensions/discord/src/draft-stream.test.ts": {
"durationMs": 5
},
"extensions/discord/src/gateway-logging.test.ts": {
"durationMs": 6
},
"extensions/discord/src/group-policy.test.ts": {
"durationMs": 3
},
"extensions/discord/src/mentions.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor.gateway.test.ts": {
"durationMs": 6
},
"extensions/discord/src/monitor.test.ts": {
"durationMs": 71
},
"extensions/discord/src/monitor.tool-result.sends-status-replies-responseprefix.test.ts": {
"durationMs": 701
},
"extensions/discord/src/monitor/acp-bind-here.integration.test.ts": {
"durationMs": 15200
},
"extensions/discord/src/monitor/agent-components.wildcard.test.ts": {
"durationMs": 547
},
"extensions/discord/src/monitor/auto-presence.test.ts": {
"durationMs": 6
},
"extensions/discord/src/monitor/commands.test.ts": {
"durationMs": 3
},
"extensions/discord/src/monitor/dm-command-auth.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor/dm-command-decision.test.ts": {
"durationMs": 6
},
"extensions/discord/src/monitor/exec-approvals.test.ts": {
"durationMs": 11300
},
"extensions/discord/src/monitor/gateway-error-guard.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor/inbound-context.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor/inbound-job.test.ts": {
"durationMs": 9
},
"extensions/discord/src/monitor/listeners.test.ts": {
"durationMs": 1300
},
"extensions/discord/src/monitor/message-handler.bot-self-filter.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/message-handler.inbound-context.test.ts": {
"durationMs": 9
},
"extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts": {
"durationMs": 11
},
"extensions/discord/src/monitor/message-handler.preflight.test.ts": {
"durationMs": 4100
},
"extensions/discord/src/monitor/message-handler.process.test.ts": {
"durationMs": 5700
},
"extensions/discord/src/monitor/message-handler.queue.test.ts": {
"durationMs": 3900
},
"extensions/discord/src/monitor/message-utils.test.ts": {
"durationMs": 6000
},
"extensions/discord/src/monitor/model-picker-preferences.test.ts": {
"durationMs": 14
},
"extensions/discord/src/monitor/model-picker.test.ts": {
"durationMs": 16
},
"extensions/discord/src/monitor/monitor.agent-components.test.ts": {
"durationMs": 13
},
"extensions/discord/src/monitor/monitor.test.ts": {
"durationMs": 19800
},
"extensions/discord/src/monitor/monitor.threading-utils.test.ts": {
"durationMs": 10
},
"extensions/discord/src/monitor/native-command-context.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/native-command.commands-allowfrom.test.ts": {
"durationMs": 17
},
"extensions/discord/src/monitor/native-command.model-picker.test.ts": {
"durationMs": 770
},
"extensions/discord/src/monitor/native-command.options.test.ts": {
"durationMs": 1100
},
"extensions/discord/src/monitor/native-command.plugin-dispatch.test.ts": {
"durationMs": 2000
},
"extensions/discord/src/monitor/presence.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor/provider.allowlist.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/provider.lifecycle.test.ts": {
"durationMs": 39
},
"extensions/discord/src/monitor/provider.proxy.test.ts": {
"durationMs": 13
},
"extensions/discord/src/monitor/provider.rest-proxy.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/provider.skill-dedupe.test.ts": {
"durationMs": 954
},
"extensions/discord/src/monitor/provider.test.ts": {
"durationMs": 5100
},
"extensions/discord/src/monitor/reply-delivery.test.ts": {
"durationMs": 6000
},
"extensions/discord/src/monitor/route-resolution.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/startup-status.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/thread-bindings.discord-api.test.ts": {
"durationMs": 1100
},
"extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts": {
"durationMs": 42
},
"extensions/discord/src/monitor/thread-bindings.persona.test.ts": {
"durationMs": 4
},
"extensions/discord/src/monitor/thread-bindings.shared-state.test.ts": {
"durationMs": 6
},
"extensions/discord/src/monitor/thread-session-close.test.ts": {
"durationMs": 130
},
"extensions/discord/src/monitor/threading.auto-thread.test.ts": {
"durationMs": 7
},
"extensions/discord/src/monitor/threading.parent-info.test.ts": {
"durationMs": 5
},
"extensions/discord/src/monitor/threading.starter.test.ts": {
"durationMs": 3
},
"extensions/discord/src/normalize.test.ts": {
"durationMs": 3
},
"extensions/discord/src/outbound-adapter.interactive-order.test.ts": {
"durationMs": 4
},
"extensions/discord/src/outbound-adapter.test.ts": {
"durationMs": 2300
},
"extensions/discord/src/pluralkit.test.ts": {
"durationMs": 3
},
"extensions/discord/src/probe.intents.test.ts": {
"durationMs": 4
},
"extensions/discord/src/probe.parse-token.test.ts": {
"durationMs": 3
},
"extensions/discord/src/resolve-allowlist-common.test.ts": {
"durationMs": 4
},
"extensions/discord/src/resolve-channels.test.ts": {
"durationMs": 9
},
"extensions/discord/src/resolve-users.test.ts": {
"durationMs": 10
},
"extensions/discord/src/send.components.test.ts": {
"durationMs": 511
},
"extensions/discord/src/send.creates-thread.test.ts": {
"durationMs": 4600
},
"extensions/discord/src/send.permissions.authz.test.ts": {
"durationMs": 8
},
"extensions/discord/src/send.sends-basic-channel-messages.test.ts": {
"durationMs": 5300
},
"extensions/discord/src/send.typing.test.ts": {
"durationMs": 5
},
"extensions/discord/src/send.webhook-activity.test.ts": {
"durationMs": 19380
},
"extensions/discord/src/session-key-normalization.test.ts": {
"durationMs": 3
},
"extensions/discord/src/setup-account-state.test.ts": {
"durationMs": 4
},
"extensions/discord/src/shared-interactive.test.ts": {
"durationMs": 3
},
"extensions/discord/src/status-issues.test.ts": {
"durationMs": 4
},
"extensions/discord/src/subagent-hooks.test.ts": {
"durationMs": 6
},
"extensions/discord/src/targets.test.ts": {
"durationMs": 8
},
"extensions/discord/src/token.test.ts": {
"durationMs": 5
},
"extensions/discord/src/voice-message.test.ts": {
"durationMs": 207
},
"extensions/discord/src/voice/command.test.ts": {
"durationMs": 4
},
"extensions/imessage/src/channel.outbound.test.ts": {
"durationMs": 10
},
"extensions/imessage/src/group-policy.test.ts": {
"durationMs": 3
},
"extensions/imessage/src/monitor.gating.test.ts": {
"durationMs": 9
},
"extensions/imessage/src/monitor.shutdown.unhandled-rejection.test.ts": {
"durationMs": 4
},
"extensions/imessage/src/monitor/deliver.test.ts": {
"durationMs": 220
},
"extensions/imessage/src/monitor/inbound-processing.test.ts": {
"durationMs": 10
},
"extensions/imessage/src/monitor/loop-rate-limiter.test.ts": {
"durationMs": 5
},
"extensions/imessage/src/monitor/monitor-provider.echo-cache.test.ts": {
"durationMs": 4
},
"extensions/imessage/src/monitor/reflection-guard.test.ts": {
"durationMs": 5
},
"extensions/imessage/src/monitor/sanitize-outbound.test.ts": {
"durationMs": 4
},
"extensions/imessage/src/monitor/self-chat-cache.test.ts": {
"durationMs": 6
},
"extensions/imessage/src/probe.test.ts": {
"durationMs": 3
},
"extensions/imessage/src/send.test.ts": {
"durationMs": 11
},
"extensions/imessage/src/setup-allow-from.test.ts": {
"durationMs": 3
},
"extensions/imessage/src/targets.test.ts": {
"durationMs": 7
},
"extensions/signal/src/channel.outbound.test.ts": {
"durationMs": 4
},
"extensions/signal/src/channel.test.ts": {
"durationMs": 4
},
"extensions/signal/src/client.test.ts": {
"durationMs": 8
},
"extensions/signal/src/format.chunking.test.ts": {
"durationMs": 18
},
"extensions/signal/src/format.links.test.ts": {
"durationMs": 10
},
"extensions/signal/src/format.test.ts": {
"durationMs": 13
},
"extensions/signal/src/format.visual.test.ts": {
"durationMs": 9
},
"extensions/signal/src/identity.test.ts": {
"durationMs": 4
},
"extensions/signal/src/monitor.test.ts": {
"durationMs": 3
},
"extensions/signal/src/monitor.tool-result.autostart.test.ts": {
"durationMs": 7
},
"extensions/signal/src/monitor.tool-result.pairs-uuid-only-senders-uuid-allowlist-entry.test.ts": {
"durationMs": 379
},
"extensions/signal/src/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts": {
"durationMs": 129
},
"extensions/signal/src/monitor/access-policy.test.ts": {
"durationMs": 6
},
"extensions/signal/src/monitor/event-handler.inbound-context.test.ts": {
"durationMs": 1300
},
"extensions/signal/src/monitor/event-handler.mention-gating.test.ts": {
"durationMs": 1900
},
"extensions/signal/src/probe.test.ts": {
"durationMs": 4
},
"extensions/signal/src/send-reactions.test.ts": {
"durationMs": 62
},
"extensions/signal/src/setup-allow-from.test.ts": {
"durationMs": 5
},
"extensions/slack/src/accounts.test.ts": {
"durationMs": 3
},
"extensions/slack/src/action-runtime.test.ts": {
"durationMs": 15
},
"extensions/slack/src/actions.blocks.test.ts": {
"durationMs": 7
},
"extensions/slack/src/actions.download-file.test.ts": {
"durationMs": 838
},
"extensions/slack/src/actions.read.test.ts": {
"durationMs": 5
},
"extensions/slack/src/blocks-fallback.test.ts": {
"durationMs": 3
},
"extensions/slack/src/blocks-input.test.ts": {
"durationMs": 3
},
"extensions/slack/src/channel-migration.test.ts": {
"durationMs": 4
},
"extensions/slack/src/channel.test.ts": {
"durationMs": 12
},
"extensions/slack/src/client.test.ts": {
"durationMs": 8
},
"extensions/slack/src/draft-stream.test.ts": {
"durationMs": 5
},
"extensions/slack/src/format.test.ts": {
"durationMs": 12
},
"extensions/slack/src/group-policy.test.ts": {
"durationMs": 5
},
"extensions/slack/src/http/registry.test.ts": {
"durationMs": 5
},
"extensions/slack/src/interactive-replies.test.ts": {
"durationMs": 3
},
"extensions/slack/src/message-action-dispatch.test.ts": {
"durationMs": 4
},
"extensions/slack/src/message-actions.test.ts": {
"durationMs": 3
},
"extensions/slack/src/modal-metadata.test.ts": {
"durationMs": 4
},
"extensions/slack/src/monitor.test.ts": {
"durationMs": 4
},
"extensions/slack/src/monitor.threading.missing-thread-ts.test.ts": {
"durationMs": 570
},
"extensions/slack/src/monitor.tool-result.test.ts": {
"durationMs": 4300
},
"extensions/slack/src/monitor/allow-list.test.ts": {
"durationMs": 4
},
"extensions/slack/src/monitor/auth.test.ts": {
"durationMs": 981
},
"extensions/slack/src/monitor/context.test.ts": {
"durationMs": 3
},
"extensions/slack/src/monitor/events/channels.test.ts": {
"durationMs": 504
},
"extensions/slack/src/monitor/events/interactions.test.ts": {
"durationMs": 5100
},
"extensions/slack/src/monitor/events/members.test.ts": {
"durationMs": 1200
},
"extensions/slack/src/monitor/events/message-subtype-handlers.test.ts": {
"durationMs": 4
},
"extensions/slack/src/monitor/events/messages.test.ts": {
"durationMs": 1700
},
"extensions/slack/src/monitor/events/pins.test.ts": {
"durationMs": 1200
},
"extensions/slack/src/monitor/events/reactions.test.ts": {
"durationMs": 2200
},
"extensions/slack/src/monitor/media.test.ts": {
"durationMs": 74
},
"extensions/slack/src/monitor/message-handler.app-mention-race.test.ts": {
"durationMs": 753
},
"extensions/slack/src/monitor/message-handler.debounce-key.test.ts": {
"durationMs": 3
},
"extensions/slack/src/monitor/message-handler.test.ts": {
"durationMs": 967
},
"extensions/slack/src/monitor/message-handler/dispatch.streaming.test.ts": {
"durationMs": 3
},
"extensions/slack/src/monitor/message-handler/prepare.test.ts": {
"durationMs": 68
},
"extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts": {
"durationMs": 24
},
"extensions/slack/src/monitor/monitor.test.ts": {
"durationMs": 42
},
"extensions/slack/src/monitor/provider.auth-errors.test.ts": {
"durationMs": 4
},
"extensions/slack/src/monitor/provider.interop.test.ts": {
"durationMs": 3
},
"extensions/slack/src/monitor/provider.reconnect.test.ts": {
"durationMs": 5
},
"extensions/slack/src/monitor/replies.test.ts": {
"durationMs": 713
},
"extensions/slack/src/monitor/slash.test.ts": {
"durationMs": 12500
},
"extensions/slack/src/outbound-adapter.test.ts": {
"durationMs": 4630
},
"extensions/slack/src/probe.test.ts": {
"durationMs": 8
},
"extensions/slack/src/resolve-allowlist-common.test.ts": {
"durationMs": 3
},
"extensions/slack/src/resolve-channels.test.ts": {
"durationMs": 3
},
"extensions/slack/src/resolve-users.test.ts": {
"durationMs": 4
},
"extensions/slack/src/send.blocks.test.ts": {
"durationMs": 21
},
"extensions/slack/src/send.upload.test.ts": {
"durationMs": 10010
},
"extensions/slack/src/sent-thread-cache.test.ts": {
"durationMs": 78
},
"extensions/slack/src/shared-interactive.test.ts": {
"durationMs": 4
},
"extensions/slack/src/stream-mode.test.ts": {
"durationMs": 4
},
"extensions/slack/src/targets.test.ts": {
"durationMs": 100
},
"extensions/slack/src/threading-tool-context.test.ts": {
"durationMs": 4
},
"extensions/slack/src/threading.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/accounts.test.ts": {
"durationMs": 3
},
"extensions/whatsapp/src/accounts.whatsapp-auth.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/action-runtime.test.ts": {
"durationMs": 6
},
"extensions/whatsapp/src/active-listener.test.ts": {
"durationMs": 11
},
"extensions/whatsapp/src/auto-reply.broadcast-groups.combined.test.ts": {
"durationMs": 120
},
"extensions/whatsapp/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts": {
"durationMs": 1500
},
"extensions/whatsapp/src/auto-reply.web-auto-reply.last-route.test.ts": {
"durationMs": 434
},
"extensions/whatsapp/src/auto-reply/deliver-reply.test.ts": {
"durationMs": 10260
},
"extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts": {
"durationMs": 1700
},
"extensions/whatsapp/src/auto-reply/monitor/group-members.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/auto-reply/monitor/process-message.inbound-context.test.ts": {
"durationMs": 2500
},
"extensions/whatsapp/src/auto-reply/web-auto-reply-monitor.test.ts": {
"durationMs": 12
},
"extensions/whatsapp/src/auto-reply/web-auto-reply-utils.test.ts": {
"durationMs": 12
},
"extensions/whatsapp/src/channel.directory.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/channel.outbound.test.ts": {
"durationMs": 335
},
"extensions/whatsapp/src/channel.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/group-policy.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/inbound.media.test.ts": {
"durationMs": 627
},
"extensions/whatsapp/src/inbound.test.ts": {
"durationMs": 4400
},
"extensions/whatsapp/src/inbound/access-control.test.ts": {
"durationMs": 1600
},
"extensions/whatsapp/src/inbound/media.node.test.ts": {
"durationMs": 90
},
"extensions/whatsapp/src/inbound/send-api.test.ts": {
"durationMs": 732
},
"extensions/whatsapp/src/login-qr.test.ts": {
"durationMs": 6
},
"extensions/whatsapp/src/login.coverage.test.ts": {
"durationMs": 8
},
"extensions/whatsapp/src/login.test.ts": {
"durationMs": 350
},
"extensions/whatsapp/src/logout.test.ts": {
"durationMs": 7
},
"extensions/whatsapp/src/media.test.ts": {
"durationMs": 4600
},
"extensions/whatsapp/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test.ts": {
"durationMs": 1400
},
"extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts": {
"durationMs": 999
},
"extensions/whatsapp/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts": {
"durationMs": 2100
},
"extensions/whatsapp/src/monitor-inbox.captures-media-path-image-messages.test.ts": {
"durationMs": 3200
},
"extensions/whatsapp/src/monitor-inbox.streams-inbound-messages.test.ts": {
"durationMs": 1600
},
"extensions/whatsapp/src/normalize-target.test.ts": {
"durationMs": 5
},
"extensions/whatsapp/src/outbound-adapter.poll.test.ts": {
"durationMs": 17860
},
"extensions/whatsapp/src/outbound-adapter.sendpayload.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/reconnect.test.ts": {
"durationMs": 4
},
"extensions/whatsapp/src/resolve-outbound-target.test.ts": {
"durationMs": 18
},
"extensions/whatsapp/src/resolve-target.test.ts": {
"durationMs": 1600
},
"extensions/whatsapp/src/send.test.ts": {
"durationMs": 2700
},
"extensions/whatsapp/src/session.test.ts": {
"durationMs": 479
},
"extensions/whatsapp/src/setup-surface.test.ts": {
"durationMs": 1400
},
"extensions/whatsapp/src/status-issues.test.ts": {
"durationMs": 4
},
"src/browser/bridge-server.auth.test.ts": {
"durationMs": 94
},
"src/browser/browser-utils.test.ts": {
"durationMs": 7
},
"src/browser/cdp-proxy-bypass.test.ts": {
"durationMs": 119
},
"src/browser/cdp-timeouts.test.ts": {
"durationMs": 3
},
"src/browser/cdp.test.ts": {
"durationMs": 39
},
"src/browser/chrome-mcp.snapshot.test.ts": {
"durationMs": 3
},
"src/browser/chrome-mcp.test.ts": {
"durationMs": 7
},
"src/browser/chrome.default-browser.test.ts": {
"durationMs": 5
},
"src/browser/chrome.launch-args.test.ts": {
"durationMs": 3
},
"src/browser/chrome.test.ts": {
"durationMs": 324
},
"src/browser/client-fetch.loopback-auth.test.ts": {
"durationMs": 101
},
"src/browser/client.test.ts": {
"durationMs": 10
},
"src/browser/config.test.ts": {
"durationMs": 8
},
"src/browser/control-auth.auto-token.test.ts": {
"durationMs": 85
},
"src/browser/control-auth.test.ts": {
"durationMs": 4
},
"src/browser/navigation-guard.test.ts": {
"durationMs": 16
},
"src/browser/paths.test.ts": {
"durationMs": 11
},
"src/browser/profiles-service.test.ts": {
"durationMs": 32
},
"src/browser/profiles.test.ts": {
"durationMs": 7
},
"src/browser/proxy-files.test.ts": {
"durationMs": 11
},
"src/browser/pw-role-snapshot.test.ts": {
"durationMs": 5
},
"src/browser/pw-session.connections.test.ts": {
"durationMs": 55
},
"src/browser/pw-session.create-page.navigation-guard.test.ts": {
"durationMs": 6
},
"src/browser/pw-session.get-page-for-targetid.extension-fallback.test.ts": {
"durationMs": 9
},
"src/browser/pw-session.page-cdp.test.ts": {
"durationMs": 3
},
"src/browser/pw-session.test.ts": {
"durationMs": 6
},
"src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts": {
"durationMs": 610
},
"src/browser/pw-tools-core.interactions.batch.test.ts": {
"durationMs": 12
},
"src/browser/pw-tools-core.interactions.evaluate.abort.test.ts": {
"durationMs": 122
},
"src/browser/pw-tools-core.interactions.set-input-files.test.ts": {
"durationMs": 127
},
"src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts": {
"durationMs": 383
},
"src/browser/pw-tools-core.screenshots-element-selector.test.ts": {
"durationMs": 1000
},
"src/browser/pw-tools-core.snapshot.navigate-guard.test.ts": {
"durationMs": 11
},
"src/browser/pw-tools-core.waits-next-download-saves-it.test.ts": {
"durationMs": 677
},
"src/browser/routes/agent.existing-session.test.ts": {
"durationMs": 19
},
"src/browser/routes/agent.shared.test.ts": {
"durationMs": 3
},
"src/browser/routes/agent.snapshot.plan.test.ts": {
"durationMs": 4
},
"src/browser/routes/agent.snapshot.test.ts": {
"durationMs": 1600
},
"src/browser/routes/agent.storage.test.ts": {
"durationMs": 3
},
"src/browser/routes/basic.existing-session.test.ts": {
"durationMs": 221
},
"src/browser/routes/dispatcher.abort.test.ts": {
"durationMs": 8
},
"src/browser/screenshot.test.ts": {
"durationMs": 108
},
"src/browser/server-context.ensure-browser-available.waits-for-cdp-ready.test.ts": {
"durationMs": 8
},
"src/browser/server-context.existing-session.test.ts": {
"durationMs": 67
},
"src/browser/server-context.hot-reload-profiles.test.ts": {
"durationMs": 11
},
"src/browser/server-context.loopback-direct-ws.test.ts": {
"durationMs": 8
},
"src/browser/server-context.remote-profile-tab-ops.test.ts": {
"durationMs": 740
},
"src/browser/server-context.remote-tab-ops.test.ts": {
"durationMs": 1500
},
"src/browser/server-context.reset.test.ts": {
"durationMs": 14
},
"src/browser/server-context.tab-selection-state.test.ts": {
"durationMs": 115
},
"src/browser/server-lifecycle.test.ts": {
"durationMs": 9
},
"src/browser/server.agent-contract-form-layout-act-commands.test.ts": {
"durationMs": 282
},
"src/browser/server.agent-contract-snapshot-endpoints.test.ts": {
"durationMs": 982
},
"src/browser/server.auth-fail-closed.test.ts": {
"durationMs": 63
},
"src/browser/server.auth-token-gates-http.test.ts": {
"durationMs": 20
},
"src/browser/server.evaluate-disabled-does-not-block-storage.test.ts": {
"durationMs": 122
},
"src/browser/server.post-tabs-open-profile-unknown-returns-404.test.ts": {
"durationMs": 159
},
"src/browser/session-tab-registry.test.ts": {
"durationMs": 4
},
"src/browser/url-pattern.test.ts": {
"durationMs": 3
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
import { describe, expect, it } from "vitest";
import { buildWorkflowManifest } from "../../scripts/ci-write-manifest-outputs.mjs";
describe("buildWorkflowManifest", () => {
it("builds static CI matrices from scope env", () => {
const manifest = buildWorkflowManifest({
GITHUB_EVENT_NAME: "pull_request",
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_DOCS_CHANGED: "false",
OPENCLAW_CI_RUN_NODE: "true",
OPENCLAW_CI_RUN_MACOS: "true",
OPENCLAW_CI_RUN_ANDROID: "true",
OPENCLAW_CI_RUN_WINDOWS: "true",
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false",
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "true",
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[{"extension":"discord"}]}',
});
expect(manifest.run_checks).toBe(true);
expect(manifest.checks_fast_matrix).toEqual({
include: [
{ check_name: "checks-fast-bundled", runtime: "node", task: "bundled" },
{ check_name: "checks-fast-extensions", runtime: "node", task: "extensions" },
{
check_name: "checks-fast-contracts-protocol",
runtime: "node",
task: "contracts-protocol",
},
],
});
expect(manifest.checks_matrix).toEqual({
include: [
{ check_name: "checks-node-test", runtime: "node", task: "test" },
{ check_name: "checks-node-channels", runtime: "node", task: "channels" },
],
});
expect(manifest.checks_windows_matrix).toEqual({
include: [{ check_name: "checks-windows-node-test", runtime: "node", task: "test" }],
});
expect(manifest.extension_fast_matrix).toEqual({
include: [{ check_name: "extension-fast-discord", extension: "discord" }],
});
expect(manifest.android_matrix).toHaveProperty("include");
expect(manifest.macos_node_matrix).toEqual({
include: [{ check_name: "macos-node", runtime: "node", task: "test" }],
});
});
it("includes the push-only compat lane on pushes", () => {
const manifest = buildWorkflowManifest({
GITHUB_EVENT_NAME: "push",
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_DOCS_CHANGED: "false",
OPENCLAW_CI_RUN_NODE: "true",
});
expect(manifest.checks_matrix).toEqual({
include: [
{ check_name: "checks-node-test", runtime: "node", task: "test" },
{ check_name: "checks-node-channels", runtime: "node", task: "channels" },
{
check_name: "checks-node-compat-node22",
runtime: "node",
task: "compat-node22",
node_version: "22.x",
cache_key_suffix: "node22",
},
],
});
});
it("suppresses heavy jobs for docs-only changes", () => {
const manifest = buildWorkflowManifest({
OPENCLAW_CI_DOCS_ONLY: "true",
OPENCLAW_CI_DOCS_CHANGED: "true",
OPENCLAW_CI_RUN_NODE: "true",
OPENCLAW_CI_RUN_WINDOWS: "true",
});
expect(manifest.run_checks).toBe(false);
expect(manifest.run_checks_windows).toBe(false);
expect(manifest.run_check_docs).toBe(true);
});
it("builds install-smoke outputs separately", () => {
const manifest = buildWorkflowManifest(
{
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_RUN_CHANGED_SMOKE: "true",
},
"install-smoke",
);
expect(manifest).toEqual({
docs_only: false,
run_install_smoke: true,
});
});
});

View File

@@ -5,7 +5,6 @@ import {
detectChangedExtensionIds,
listAvailableExtensionIds,
listChangedExtensionIds,
partitionExtensionTestFiles,
resolveExtensionTestPlan,
} from "../../scripts/test-extension.mjs";
import { bundledPluginFile, bundledPluginRoot } from "../helpers/bundled-plugin-paths.js";
@@ -49,19 +48,6 @@ describe("scripts/test-extension.mjs", () => {
);
});
it("splits channel monitor files into isolated runs", () => {
const plan = resolveExtensionTestPlan({ targetArg: "discord", cwd: process.cwd() });
expect(plan.config).toBe("vitest.channels.config.ts");
expect(plan.isolatedTestFiles).toContain(
bundledPluginFile("discord", "src/monitor/provider.test.ts"),
);
expect(plan.sharedTestFiles).toContain(bundledPluginFile("discord", "src/channel.test.ts"));
expect(plan.sharedTestFiles).not.toContain(
bundledPluginFile("discord", "src/monitor/provider.test.ts"),
);
});
it("resolves provider extensions onto the extensions vitest config", () => {
const plan = resolveExtensionTestPlan({ targetArg: "firecrawl", cwd: process.cwd() });
@@ -72,21 +58,6 @@ describe("scripts/test-extension.mjs", () => {
).toBe(true);
});
it("applies exact isolated files for non-channel extensions", () => {
const { isolatedTestFiles, sharedTestFiles } = partitionExtensionTestFiles({
config: "vitest.extensions.config.ts",
testFiles: [
bundledPluginFile("firecrawl", "src/firecrawl-scrape-tool.test.ts"),
bundledPluginFile("firecrawl", "src/index.test.ts"),
],
});
expect(isolatedTestFiles).toEqual([
bundledPluginFile("firecrawl", "src/firecrawl-scrape-tool.test.ts"),
]);
expect(sharedTestFiles).toEqual([bundledPluginFile("firecrawl", "src/index.test.ts")]);
});
it("includes paired src roots when they contain tests", () => {
const plan = resolveExtensionTestPlan({ targetArg: "line", cwd: process.cwd() });

View File

@@ -1,590 +0,0 @@
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
parseCompletedTestFileLines,
parseMemoryTraceSummaryLines,
parseMemoryValueKb,
} from "../../scripts/test-parallel-memory.mjs";
import {
appendCapturedOutput,
hasFatalTestRunOutput,
resolveTestRunExitCode,
} from "../../scripts/test-parallel-utils.mjs";
import { loadTestCatalog } from "../../scripts/test-planner/catalog.mjs";
import { bundledPluginFile } from "../helpers/bundled-plugin-paths.js";
const clearPlannerShardEnv = (env) => {
const nextEnv = { ...env };
delete nextEnv.OPENCLAW_TEST_SHARDS;
delete nextEnv.OPENCLAW_TEST_SHARD_INDEX;
delete nextEnv.OPENCLAW_TEST_FORCE_THREADS;
delete nextEnv.OPENCLAW_TEST_FORCE_FORKS;
delete nextEnv.OPENCLAW_TEST_DISABLE_THREAD_EXPANSION;
delete nextEnv.OPENCLAW_TEST_SHOW_POOL_DECISION;
delete nextEnv.OPENCLAW_TEST_PROFILE;
delete nextEnv.OPENCLAW_TEST_WORKERS;
delete nextEnv.OPENCLAW_TEST_SKIP_DEFAULT;
delete nextEnv.OPENCLAW_TEST_INCLUDE_EXTENSIONS;
delete nextEnv.OPENCLAW_TEST_INCLUDE_CHANNELS;
delete nextEnv.OPENCLAW_TEST_INCLUDE_GATEWAY;
delete nextEnv.OPENCLAW_TEST_UNIT_FAST_BATCH_TARGET_MS;
return nextEnv;
};
const sharedTargetedChannelProxyFiles = (() => {
const catalog = loadTestCatalog();
return catalog.allKnownTestFiles
.filter((file) => {
const classification = catalog.classifyTestFile(file);
return classification.surface === "channels" && !classification.isolated;
})
.slice(0, 100);
})();
const sharedTargetedUnitProxyFiles = (() => {
const catalog = loadTestCatalog();
return catalog.allKnownTestFiles
.filter((file) => {
const classification = catalog.classifyTestFile(file);
return classification.surface === "unit" && !classification.isolated;
})
.slice(0, 100);
})();
const targetedChannelProxyFiles = [
...sharedTargetedChannelProxyFiles,
bundledPluginFile("discord", "src/monitor/message-handler.preflight.acp-bindings.test.ts"),
bundledPluginFile("discord", "src/monitor/monitor.agent-components.test.ts"),
bundledPluginFile("whatsapp", "src/monitor-inbox.streams-inbound-messages.test.ts"),
];
const targetedUnitProxyFiles = [
...sharedTargetedUnitProxyFiles,
"src/cli/qr-dashboard.integration.test.ts",
];
const REPO_ROOT = path.resolve(import.meta.dirname, "../..");
const HIGH_MEMORY_LOCAL_PLANNER_ENV = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "12",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
} as const;
function createPlannerEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
return {
...clearPlannerShardEnv(process.env),
...overrides,
};
}
function createLocalPlannerEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
return createPlannerEnv({
CI: "",
GITHUB_ACTIONS: "",
OPENCLAW_TEST_LOAD_AWARE: "0",
...overrides,
});
}
function createHighMemoryLocalPlannerEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.ProcessEnv {
return createLocalPlannerEnv({
...HIGH_MEMORY_LOCAL_PLANNER_ENV,
...overrides,
});
}
function runPlannerPlan(args: string[], envOverrides: NodeJS.ProcessEnv = {}): string {
return execFileSync("node", ["scripts/test-parallel.mjs", ...args], {
cwd: REPO_ROOT,
env: createPlannerEnv(envOverrides),
encoding: "utf8",
stdio: ["pipe", "pipe", "pipe"],
});
}
function runHighMemoryLocalMultiSurfacePlan(): string {
return runPlannerPlan(
["--plan", "--surface", "unit", "--surface", "extensions", "--surface", "channels"],
createHighMemoryLocalPlannerEnv(),
);
}
function getPlanLines(output: string, prefix: string): string[] {
return output
.split("\n")
.map((line) => line.trim())
.filter((line) => line.startsWith(prefix));
}
function getTargetedChannelPlanLines(output: string): string[] {
return output
.split("\n")
.map((line) => line.trim())
.filter(
(line) =>
line.startsWith("channels-") &&
line.includes("filters=") &&
line.includes("surface=channels"),
);
}
function parseNumericPlanField(line: string, key: string): number {
const match = line.match(new RegExp(`\\b${key}=(\\d+)\\b`));
if (!match) {
throw new Error(`missing ${key} in plan line: ${line}`);
}
return Number(match[1]);
}
function runManifestOutputWriter(workflow: string, envOverrides: NodeJS.ProcessEnv = {}): string {
const outputPath = path.join(os.tmpdir(), `openclaw-${workflow}-output-${Date.now()}.txt`);
try {
execFileSync("node", ["scripts/ci-write-manifest-outputs.mjs", "--workflow", workflow], {
cwd: REPO_ROOT,
env: createPlannerEnv({
GITHUB_OUTPUT: outputPath,
...envOverrides,
}),
encoding: "utf8",
});
return fs.readFileSync(outputPath, "utf8");
} finally {
fs.rmSync(outputPath, { force: true });
}
}
describe("scripts/test-parallel fatal output guard", () => {
it("fails a zero exit when V8 reports an out-of-memory fatal", () => {
const output = [
"FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory",
"node::OOMErrorHandler(char const*, v8::OOMDetails const&)",
"[test-parallel] done unit-fast code=0 elapsed=210.9s",
].join("\n");
expect(hasFatalTestRunOutput(output)).toBe(true);
expect(resolveTestRunExitCode({ code: 0, signal: null, output })).toBe(1);
});
it("keeps a clean zero exit green", () => {
expect(
resolveTestRunExitCode({
code: 0,
signal: null,
output: "Test Files 3 passed (3)",
}),
).toBe(0);
});
it("preserves explicit non-zero exits", () => {
expect(resolveTestRunExitCode({ code: 2, signal: null, output: "" })).toBe(2);
});
it("fails even when the fatal line scrolls out of the retained tail", () => {
const fatalLine = "FATAL ERROR: Ineffective mark-compacts near heap limit";
const output = appendCapturedOutput(fatalLine, "x".repeat(250_000), 200_000);
expect(hasFatalTestRunOutput(output)).toBe(false);
expect(resolveTestRunExitCode({ code: 0, signal: null, output, fatalSeen: true })).toBe(1);
});
it("keeps only the tail of captured output", () => {
const output = appendCapturedOutput("", "abc", 5);
expect(appendCapturedOutput(output, "defg", 5)).toBe("cdefg");
});
});
describe("scripts/test-parallel memory trace parsing", () => {
it("extracts completed test file lines from colored Vitest output", () => {
const output = [
"\u001B[32m✓\u001B[39m src/config/doc-baseline.test.ts \u001B[2m(\u001B[22m\u001B[2m8 tests\u001B[22m\u001B[2m)\u001B[22m\u001B[33m 46424\u001B[2mms\u001B[22m\u001B[39m",
" \u001B[32m✓\u001B[39m src/infra/restart.test.ts (5 tests) 4.2s",
].join("\n");
expect(parseCompletedTestFileLines(output)).toEqual([
{
file: "src/config/doc-baseline.test.ts",
durationMs: 46_424,
},
{
file: "src/infra/restart.test.ts",
durationMs: 4_200,
},
]);
});
it("ignores non-file summary lines", () => {
expect(
parseCompletedTestFileLines(
[
" Test Files 2 passed (2)",
" Tests 30 passed (30)",
"[test-parallel] done unit code=0 elapsed=68.8s",
].join("\n"),
),
).toEqual([]);
});
it("parses memory trace summary lines and hotspot deltas", () => {
const summaries = parseMemoryTraceSummaryLines(
[
"2026-03-20T04:32:18.7721466Z [test-parallel][mem] summary unit-fast files=360 peak=13.22GiB totalDelta=6.69GiB peakAt=poll top=src/config/schema.help.quality.test.ts:1.06GiB, src/infra/update-runner.test.ts:+463.6MiB",
].join("\n"),
);
expect(summaries).toHaveLength(1);
expect(summaries[0]).toEqual({
lane: "unit-fast",
files: 360,
peakRssKb: parseMemoryValueKb("13.22GiB"),
totalDeltaKb: parseMemoryValueKb("6.69GiB"),
peakAt: "poll",
top: [
{
file: "src/config/schema.help.quality.test.ts",
deltaKb: parseMemoryValueKb("1.06GiB"),
},
{
file: "src/infra/update-runner.test.ts",
deltaKb: parseMemoryValueKb("+463.6MiB"),
},
],
});
});
it("parses memory trace summaries from gh run job logs", () => {
const summaries = parseMemoryTraceSummaryLines(
[
"checks-fast-extensions-6\tRun extensions (node)\t2026-04-03T04:07:10.5924943Z [test-parallel][mem] summary extensions-batch-22-shard-6 files=15 peak=2.66GiB totalDelta=+470.5MiB peakAt=poll top=extensions/microsoft-foundry/index.test.ts:+1.35GiB, extensions/acpx/src/service.test.ts:+212.1MiB",
].join("\n"),
);
expect(summaries).toEqual([
{
lane: "extensions-batch-22-shard-6",
files: 15,
peakRssKb: parseMemoryValueKb("2.66GiB"),
totalDeltaKb: parseMemoryValueKb("+470.5MiB"),
peakAt: "poll",
top: [
{
file: "extensions/microsoft-foundry/index.test.ts",
deltaKb: parseMemoryValueKb("+1.35GiB"),
},
{
file: "extensions/acpx/src/service.test.ts",
deltaKb: parseMemoryValueKb("+212.1MiB"),
},
],
},
]);
});
});
describe("scripts/test-parallel lane planning", () => {
it("keeps serial profile on split unit lanes instead of one giant unit worker", () => {
const output = runPlannerPlan(["--plan"], {
OPENCLAW_TEST_PROFILE: "serial",
});
expect(output).toContain("unit-fast");
expect(output).not.toContain("unit filters=all maxWorkers=1");
});
it("recycles default local unit-fast runs into bounded batches", () => {
const output = runPlannerPlan(["--plan"], {
CI: "",
OPENCLAW_TEST_UNIT_FAST_LANES: "1",
OPENCLAW_TEST_UNIT_FAST_BATCH_TARGET_MS: "1",
});
expect(output).toContain("unit-fast-batch-");
expect(output).not.toContain("unit-fast filters=all maxWorkers=");
});
it("keeps legacy base-pinned targeted reruns on dedicated forks lanes", () => {
const output = runPlannerPlan([
"--plan",
"--files",
"src/auto-reply/reply/followup-runner.test.ts",
]);
expect(output).toContain("base-pinned-followup-runner");
expect(output).not.toContain("base-followup-runner");
});
it("reports capability-derived output for mid-memory local macOS hosts", () => {
const output = runPlannerPlan(
["--plan", "--surface", "unit", "--surface", "extensions"],
createLocalPlannerEnv({
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
}),
);
expect(output).toContain("mode=local intent=normal memoryBand=mid");
expect(output).toMatch(/unit-fast(?:-batch-\d+)? filters=\d+ maxWorkers=/);
expect(output).toMatch(/extensions(?:-batch-\d+)? filters=\d+ maxWorkers=/);
});
it("uses higher shared extension worker counts on high-memory local hosts", () => {
const highMemoryOutput = runPlannerPlan(
["--plan", "--surface", "extensions"],
createHighMemoryLocalPlannerEnv(),
);
const midMemoryOutput = runPlannerPlan(
["--plan", "--surface", "extensions"],
createLocalPlannerEnv({
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
}),
);
const midSharedBatches = getPlanLines(midMemoryOutput, "extensions-batch-");
const highSharedBatches = getPlanLines(highMemoryOutput, "extensions-batch-");
expect(midSharedBatches.length).toBeGreaterThan(0);
expect(highSharedBatches.length).toBeGreaterThan(0);
expect(midSharedBatches.every((line) => /filters=\d+ maxWorkers=3/.test(line))).toBe(true);
expect(highSharedBatches.every((line) => /filters=\d+ maxWorkers=5/.test(line))).toBe(true);
expect(highSharedBatches.length).toBeLessThanOrEqual(midSharedBatches.length);
});
it("starts isolated channel lanes before shared extension batches on high-memory local hosts", () => {
const output = runHighMemoryLocalMultiSurfacePlan();
const firstChannelIsolated = output.indexOf(
"message-handler.preflight.acp-bindings-channels-isolated",
);
const firstExtensionBatch = output.indexOf("extensions-batch-1");
const firstChannelBatch = output.indexOf("channels-batch-1");
expect(firstChannelIsolated).toBeGreaterThanOrEqual(0);
expect(firstExtensionBatch).toBeGreaterThan(firstChannelIsolated);
expect(firstChannelBatch).toBeGreaterThan(firstExtensionBatch);
expect(output).toMatch(/channels-batch-1 filters=\d+ maxWorkers=5/);
});
it("uses coarser unit-fast batching for high-memory local multi-surface runs", () => {
const output = runHighMemoryLocalMultiSurfacePlan();
expect(output).toContain("unit-fast-batch-4");
expect(output).not.toContain("unit-fast-batch-5");
});
it("uses earlier targeted channel batching on high-memory local hosts", () => {
const output = runPlannerPlan(
[
"--plan",
"--surface",
"channels",
...targetedChannelProxyFiles.flatMap((file) => ["--files", file]),
],
createHighMemoryLocalPlannerEnv(),
);
const channelBatchLines = getPlanLines(output, "channels-batch-");
const channelBatchFilterCounts = channelBatchLines.map((line) =>
parseNumericPlanField(line, "filters"),
);
const targetedChannelPlanLines = getTargetedChannelPlanLines(output);
expect(channelBatchLines.length).toBeGreaterThanOrEqual(4);
expect(channelBatchLines.every((line) => line.includes("maxWorkers=5"))).toBe(true);
expect(Math.max(...channelBatchFilterCounts)).toBeLessThan(30);
expect(
targetedChannelPlanLines.reduce(
(sum, line) => sum + parseNumericPlanField(line, "filters"),
0,
),
).toBe(targetedChannelProxyFiles.length);
});
it("uses targeted unit batching on high-memory local hosts", () => {
const output = runPlannerPlan(
[
"--plan",
"--surface",
"unit",
...targetedUnitProxyFiles.flatMap((file) => ["--files", file]),
],
createHighMemoryLocalPlannerEnv(),
);
const unitBatchLines = getPlanLines(output, "unit-batch-");
const unitBatchFilterCounts = unitBatchLines.map((line) =>
parseNumericPlanField(line, "filters"),
);
expect(unitBatchLines.length).toBeGreaterThanOrEqual(3);
expect(unitBatchLines.every((line) => line.includes("maxWorkers=6"))).toBe(true);
expect(Math.max(...unitBatchFilterCounts)).toBeLessThan(40);
expect(unitBatchFilterCounts.reduce((sum, count) => sum + count, 0)).toBe(
sharedTargetedUnitProxyFiles.length,
);
expect(output).toContain("unit-qr-dashboard.integration-isolated filters=1 maxWorkers=2");
});
it("explains targeted file ownership and execution policy", () => {
const output = runPlannerPlan(["--explain", "src/auto-reply/reply/followup-runner.test.ts"]);
expect(output).toContain("surface=base");
expect(output).toContain("reasons=base-surface,base-pinned-manifest");
expect(output).toContain("pool=forks");
});
it("routes targeted contract tests through the contracts config", () => {
const output = runPlannerPlan([
"--explain",
"src/channels/plugins/contracts/registry-backed.contract.test.ts",
]);
expect(output).toContain("surface=contracts");
expect(output).toContain("vitest.contracts.config.ts");
expect(output).not.toContain("vitest.unit.config.ts");
});
it("routes telegram plugin coverage through the extensions config", () => {
const output = runPlannerPlan([
"--explain",
bundledPluginFile("telegram", "src/bot.create-telegram-bot.test.ts"),
]);
expect(output).toContain("surface=extensions");
expect(output).toContain("extensions-surface");
expect(output).toContain("vitest.extensions.config.ts");
expect(output).not.toContain("vitest.channels.config.ts");
});
it("prints the planner-backed CI manifest as JSON", () => {
const output = runPlannerPlan(["--ci-manifest"], {
GITHUB_EVENT_NAME: "pull_request",
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_DOCS_CHANGED: "false",
OPENCLAW_CI_RUN_NODE: "true",
OPENCLAW_CI_RUN_MACOS: "true",
OPENCLAW_CI_RUN_ANDROID: "false",
OPENCLAW_CI_RUN_WINDOWS: "true",
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false",
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false",
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}',
});
const manifest = JSON.parse(output);
expect(manifest.jobs.checks.enabled).toBe(true);
expect(manifest.jobs.macosNode.enabled).toBe(true);
expect(manifest.jobs.checksWindows.enabled).toBe(true);
});
it("writes CI workflow outputs in ci mode", () => {
const outputs = runManifestOutputWriter("ci", {
GITHUB_EVENT_NAME: "pull_request",
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_DOCS_CHANGED: "false",
OPENCLAW_CI_RUN_NODE: "true",
OPENCLAW_CI_RUN_MACOS: "true",
OPENCLAW_CI_RUN_ANDROID: "true",
OPENCLAW_CI_RUN_WINDOWS: "true",
OPENCLAW_CI_RUN_SKILLS_PYTHON: "false",
OPENCLAW_CI_HAS_CHANGED_EXTENSIONS: "false",
OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX: '{"include":[]}',
});
expect(outputs).toContain("run_build_artifacts=true");
expect(outputs).toContain("run_checks_windows=true");
expect(outputs).toContain("run_macos_node=true");
expect(outputs).toContain("android_matrix=");
});
it("writes install-smoke outputs in install-smoke mode", () => {
const outputs = runManifestOutputWriter("install-smoke", {
OPENCLAW_CI_DOCS_ONLY: "false",
OPENCLAW_CI_RUN_CHANGED_SMOKE: "true",
});
expect(outputs).toContain("run_install_smoke=true");
expect(outputs).not.toContain("run_checks=");
});
it("passes through vitest --mode values that are not wrapper runtime overrides", () => {
const output = runPlannerPlan(
["--plan", "--mode", "development", "src/infra/outbound/deliver.test.ts"],
createLocalPlannerEnv({
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "16",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
}),
);
expect(output).toContain("mode=local intent=normal memoryBand=high");
expect(output).toContain("unit-deliver-isolated filters=1");
});
it("prints collect-all failure policy in planner output for wrapper-native flag", () => {
const output = runPlannerPlan(["--plan", "--collect-failures", "--surface", "unit"]);
expect(output).toContain("failurePolicy=collect-all");
});
it("maps --bail=0 to collect-all failure policy in planner output", () => {
const output = runPlannerPlan(["--plan", "--surface", "unit", "--", "--bail=0"]);
expect(output).toContain("failurePolicy=collect-all");
});
it("rejects wrapper-level positive --bail values", () => {
expect(() => runPlannerPlan(["--plan", "--surface", "unit", "--", "--bail=2"])).toThrowError(
/Unsupported wrapper-level --bail value/u,
);
});
it("rejects removed machine-name profiles", () => {
expect(() => runPlannerPlan(["--plan", "--profile", "macmini"])).toThrowError(
/Unsupported test profile "macmini"/u,
);
});
it("rejects unknown explicit surface names", () => {
expect(() => runPlannerPlan(["--plan", "--surface", "channel"])).toThrowError(
/Unsupported --surface value\(s\): channel/u,
);
});
it("supports the explicit contracts surface", () => {
const output = runPlannerPlan(["--plan", "--surface", "contracts"]);
expect(output).toContain("contracts filters=all");
expect(output).toContain("surface=contracts");
});
it("rejects wrapper --files values that look like options", () => {
expect(() => runPlannerPlan(["--plan", "--files", "--config"])).toThrowError(
/Invalid --files value/u,
);
});
it("rejects missing --profile values", () => {
expect(() => runPlannerPlan(["--plan", "--profile"])).toThrowError(/Invalid --profile value/u);
});
it("rejects missing --surface values", () => {
expect(() => runPlannerPlan(["--plan", "--surface"])).toThrowError(/Invalid --surface value/u);
});
it("rejects missing --explain values", () => {
expect(() => runPlannerPlan(["--explain"])).toThrowError(/Invalid --explain value/u);
});
it("rejects explicit existing files that are not known test files", () => {
const tempFilePath = path.join(os.tmpdir(), `openclaw-non-test-${Date.now()}.ts`);
fs.writeFileSync(tempFilePath, "export const notATest = true;\n", "utf8");
try {
expect(() => runPlannerPlan(["--plan", "--files", tempFilePath])).toThrowError(
/is not a known test file/u,
);
} finally {
fs.rmSync(tempFilePath, { force: true });
}
});
});

View File

@@ -1,195 +0,0 @@
import { EventEmitter } from "node:events";
import { PassThrough } from "node:stream";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createExecutionArtifacts, executePlan } from "../../scripts/test-planner/executor.mjs";
beforeEach(() => {
vi.spyOn(console, "log").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(process.stdout, "write").mockImplementation(() => true);
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
describe("test planner executor", () => {
it("falls back to child exit when close never arrives", async () => {
vi.useFakeTimers();
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;
});
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,
spawn: spawnMock,
},
);
await vi.runAllTimersAsync();
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.useFakeTimers();
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;
});
const artifacts = createExecutionArtifacts({});
const reportPromise = 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,
spawn: spawnMock,
},
);
await vi.runAllTimersAsync();
const report = await reportPromise;
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();
});
it("injects a valid localstorage file path into child NODE_OPTIONS", async () => {
vi.useFakeTimers();
const stdout = new PassThrough();
const stderr = new PassThrough();
const fakeChild = Object.assign(new EventEmitter(), {
stdout,
stderr,
pid: 123,
kill: vi.fn(),
});
let capturedEnv;
const spawnMock = vi.fn((_command, _args, options) => {
capturedEnv = options?.env;
setTimeout(() => {
fakeChild.emit("exit", 0, null);
fakeChild.emit("close", 0, null);
}, 0);
return fakeChild;
});
const artifacts = createExecutionArtifacts({
NODE_OPTIONS: "--max_old_space_size=4096 --localstorage-file",
});
const executePromise = executePlan(
{
failurePolicy: "fail-fast",
passthroughMetadataOnly: false,
passthroughOptionArgs: [],
targetedUnits: [],
parallelUnits: [{ id: "unit-a", args: ["vitest", "run", "src/alpha.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: {
NODE_OPTIONS: "--max_old_space_size=4096 --localstorage-file",
},
artifacts,
spawn: spawnMock,
},
);
await vi.runAllTimersAsync();
await expect(executePromise).resolves.toMatchObject({
exitCode: 0,
});
expect(spawnMock).toHaveBeenCalledTimes(1);
expect(capturedEnv?.NODE_OPTIONS).toContain("--max_old_space_size=4096");
expect(capturedEnv?.NODE_OPTIONS).toMatch(
/--localstorage-file=[^\s]+\.localstorage\.json(?:\s|$)/u,
);
expect(capturedEnv?.NODE_OPTIONS).not.toMatch(/(^|\s)--localstorage-file(?=\s|$)/u);
artifacts.cleanupTempArtifacts();
});
});

View File

@@ -1,989 +0,0 @@
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { loadTestCatalog } from "../../scripts/test-planner/catalog.mjs";
import {
createExecutionArtifacts,
createTempArtifactWriteStream,
resolvePnpmCommandInvocation,
resolveVitestFsModuleCachePath,
} from "../../scripts/test-planner/executor.mjs";
import {
buildCIExecutionManifest,
buildExecutionPlan,
explainExecutionTarget,
} from "../../scripts/test-planner/planner.mjs";
import {
loadChannelTimingManifest,
selectTimedHeavyFiles,
} from "../../scripts/test-runner-manifest.mjs";
import { bundledPluginFile } from "../helpers/bundled-plugin-paths.js";
const resolveTimedHeavyChannelFixture = () => {
const catalog = loadTestCatalog();
const timings = loadChannelTimingManifest();
const candidates = catalog.allKnownTestFiles.filter(
(file) =>
catalog.channelTestPrefixes.some((prefix) => file.startsWith(prefix)) &&
!catalog.channelIsolatedFileSet.has(file),
);
const [file] = selectTimedHeavyFiles({
candidates,
limit: 1,
minDurationMs: 12_000,
timings,
});
if (!file) {
throw new Error("No timed-heavy channel fixture found");
}
return file;
};
describe("test planner", () => {
it("builds a capability-aware plan for mid-memory local runs", () => {
const artifacts = createExecutionArtifacts({
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
OPENCLAW_TEST_LOAD_AWARE: "0",
});
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit", "extensions"],
passthroughArgs: [],
},
{
env: {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
OPENCLAW_TEST_LOAD_AWARE: "0",
},
platform: "darwin",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.runtimeCapabilities.runtimeProfileName).toBe("local-darwin");
expect(plan.failurePolicy).toBe("fail-fast");
expect(plan.runtimeCapabilities.memoryBand).toBe("mid");
expect(plan.executionBudget.unitSharedWorkers).toBe(4);
expect(plan.executionBudget.topLevelParallelLimitNoIsolate).toBe(8);
expect(plan.executionBudget.topLevelParallelLimitIsolated).toBe(3);
expect(plan.selectedUnits.some((unit) => unit.id.startsWith("unit-fast"))).toBe(true);
expect(plan.selectedUnits.some((unit) => unit.id.startsWith("extensions"))).toBe(true);
expect(plan.topLevelParallelLimit).toBe(8);
artifacts.cleanupTempArtifacts();
});
it("keeps bundled-plugin-dependent core tests off the default unit surface", () => {
const env = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
OPENCLAW_TEST_LOAD_AWARE: "0",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit", "bundled"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.selectedUnits.some((unit) => unit.surface === "bundled")).toBe(true);
expect(
plan.selectedUnits
.filter((unit) => unit.surface === "unit")
.every((unit) => unit.env?.OPENCLAW_DISABLE_BUNDLED_PLUGINS === "1"),
).toBe(true);
expect(plan.selectedUnits.find((unit) => unit.surface === "bundled")?.args).toContain(
"vitest.bundled.config.ts",
);
artifacts.cleanupTempArtifacts();
});
it("runs boundary inventory suites on the lean boundary config", () => {
const env = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
OPENCLAW_TEST_LOAD_AWARE: "0",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const boundaryUnit = plan.selectedUnits.find((unit) => unit.id === "unit-boundary");
expect(boundaryUnit).toBeTruthy();
expect(boundaryUnit?.args).toContain("vitest.boundary.config.ts");
expect(boundaryUnit?.env?.OPENCLAW_DISABLE_BUNDLED_PLUGINS).toBe("1");
expect(
plan.selectedUnits
.filter((unit) => unit.id.startsWith("unit-fast"))
.every((unit) => !unit.includeFiles?.includes("test/web-search-provider-boundary.test.ts")),
).toBe(true);
artifacts.cleanupTempArtifacts();
});
it("uses smaller shared extension batches on constrained local hosts", () => {
const env = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "8",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
OPENCLAW_TEST_LOAD_AWARE: "0",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["extensions"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const sharedExtensionBatches = plan.selectedUnits.filter((unit) =>
unit.id.startsWith("extensions-batch-"),
);
expect(plan.runtimeCapabilities.memoryBand).toBe("constrained");
expect(plan.executionBudget.extensionsBatchTargetMs).toBe(60_000);
expect(sharedExtensionBatches.length).toBeGreaterThan(3);
artifacts.cleanupTempArtifacts();
});
it("caps CI extension batch concurrency when multiple shared batches are scheduled", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "4",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "ci",
surfaces: ["extensions"],
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const sharedExtensionBatches = plan.selectedUnits.filter(
(unit) => unit.surface === "extensions" && !unit.isolate,
);
expect(plan.runtimeCapabilities.runtimeProfileName).toBe("ci-linux");
expect(plan.executionBudget.topLevelParallelLimitNoIsolate).toBe(4);
expect(sharedExtensionBatches.length).toBeGreaterThan(1);
expect(plan.topLevelParallelLimit).toBe(3);
artifacts.cleanupTempArtifacts();
});
it("auto-isolates timed-heavy extension suites in CI", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "4",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "ci",
surfaces: ["extensions"],
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const hotspotUnit = plan.selectedUnits.find((unit) => unit.id === "extensions-cli-isolated");
expect(hotspotUnit).toBeTruthy();
expect(hotspotUnit?.isolate).toBe(true);
expect(hotspotUnit?.reasons).toContain("extensions-timed-heavy");
artifacts.cleanupTempArtifacts();
});
it("auto-isolates memory-heavy extension suites in CI", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "4",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "ci",
surfaces: ["extensions"],
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const hotspotFile = bundledPluginFile("feishu", "src/bot.test.ts");
const hotspotUnit = plan.selectedUnits.find((unit) => unit.args.includes(hotspotFile));
expect(hotspotUnit).toBeTruthy();
expect(hotspotUnit?.isolate).toBe(true);
expect(hotspotUnit?.reasons).toContain("extensions-memory-heavy");
artifacts.cleanupTempArtifacts();
});
it("auto-isolates newly-seeded extension memory survivors in CI", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "4",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "ci",
surfaces: ["extensions"],
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const hotspotFile = bundledPluginFile("bluebubbles", "src/send.test.ts");
const hotspotUnit = plan.selectedUnits.find((unit) => unit.args.includes(hotspotFile));
expect(hotspotUnit).toBeTruthy();
expect(hotspotUnit?.isolate).toBe(true);
expect(hotspotUnit?.reasons).toContain("extensions-memory-heavy");
artifacts.cleanupTempArtifacts();
});
it("auto-isolates timed-heavy channel suites in CI", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "4",
OPENCLAW_TEST_HOST_MEMORY_GIB: "16",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "ci",
surfaces: ["channels"],
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const timedHeavyFile = resolveTimedHeavyChannelFixture();
const hotspotUnit = plan.selectedUnits.find(
(unit) => unit.id === `channels-${path.basename(timedHeavyFile, ".test.ts")}-isolated`,
);
expect(hotspotUnit).toBeTruthy();
expect(hotspotUnit?.isolate).toBe(true);
expect(hotspotUnit?.reasons).toContain("channels-timed-heavy");
artifacts.cleanupTempArtifacts();
});
it("scales down mid-tier local concurrency under saturated load", () => {
const artifacts = createExecutionArtifacts({
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
});
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit", "extensions"],
passthroughArgs: [],
},
{
env: {
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "10",
OPENCLAW_TEST_HOST_MEMORY_GIB: "64",
},
platform: "linux",
loadAverage: [11.5, 11.5, 11.5],
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.runtimeCapabilities.memoryBand).toBe("mid");
expect(plan.runtimeCapabilities.loadBand).toBe("saturated");
expect(plan.executionBudget.unitSharedWorkers).toBe(2);
expect(plan.executionBudget.unitIsolatedWorkers).toBe(1);
expect(plan.executionBudget.topLevelParallelLimitNoIsolate).toBe(4);
expect(plan.executionBudget.topLevelParallelLimitIsolated).toBe(1);
expect(plan.topLevelParallelLimit).toBe(4);
expect(plan.deferredRunConcurrency).toBe(1);
artifacts.cleanupTempArtifacts();
});
it("splits saturated high-memory local unit bursts into smaller shared batches", () => {
const env = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "16",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
loadAverage: [18, 18, 18],
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const sharedUnitBatches = plan.selectedUnits.filter(
(unit) => unit.surface === "unit" && !unit.isolate && unit.id.startsWith("unit-fast"),
);
const baselinePlan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
loadAverage: [1, 1, 1],
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const baselineSharedUnitBatches = baselinePlan.selectedUnits.filter(
(unit) => unit.surface === "unit" && !unit.isolate && unit.id.startsWith("unit-fast"),
);
expect(plan.runtimeCapabilities.memoryBand).toBe("high");
expect(plan.runtimeCapabilities.loadBand).toBe("saturated");
expect(sharedUnitBatches.length).toBeGreaterThan(baselineSharedUnitBatches.length);
expect(plan.executionBudget.unitIsolatedWorkers).toBe(1);
expect(plan.executionBudget.unitFastBatchTargetMs).toBe(22_500);
artifacts.cleanupTempArtifacts();
});
it("keeps full local unit runs phased when isolated and heavy lanes are present", () => {
const env = {
RUNNER_OS: "macOS",
OPENCLAW_TEST_HOST_CPU_COUNT: "16",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
OPENCLAW_TEST_LOAD_AWARE: "0",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
profile: null,
mode: "local",
surfaces: ["unit"],
passthroughArgs: [],
},
{
env,
platform: "darwin",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const sharedUnitBatches = plan.selectedUnits.filter(
(unit) => unit.surface === "unit" && !unit.isolate && unit.id.startsWith("unit-fast"),
);
expect(sharedUnitBatches.length).toBeGreaterThanOrEqual(4);
expect(plan.serialPrefixUnits.some((unit) => unit.serialPhase === "unit-fast")).toBe(true);
expect(plan.topLevelParallelLimit).toBe(3);
artifacts.cleanupTempArtifacts();
});
it("honors the max-profile top-level no-isolate cap without adding extra lanes", () => {
const artifacts = createExecutionArtifacts({
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "16",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
OPENCLAW_TEST_LOAD_AWARE: "0",
OPENCLAW_TEST_PROFILE: "max",
});
const plan = buildExecutionPlan(
{
profile: "max",
mode: "local",
surfaces: ["unit", "extensions"],
passthroughArgs: [],
},
{
env: {
RUNNER_OS: "Linux",
OPENCLAW_TEST_HOST_CPU_COUNT: "16",
OPENCLAW_TEST_HOST_MEMORY_GIB: "128",
OPENCLAW_TEST_LOAD_AWARE: "0",
OPENCLAW_TEST_PROFILE: "max",
},
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.runtimeCapabilities.intentProfile).toBe("max");
expect(plan.executionBudget.topLevelParallelLimitNoIsolate).toBe(8);
expect(plan.topLevelParallelLimit).toBe(8);
artifacts.cleanupTempArtifacts();
});
it("splits mixed targeted file selections across surfaces", () => {
const artifacts = createExecutionArtifacts({});
const plan = buildExecutionPlan(
{
mode: "local",
surfaces: [],
passthroughArgs: [
"src/auto-reply/reply/followup-runner.test.ts",
bundledPluginFile(
"discord",
"src/monitor/message-handler.preflight.acp-bindings.test.ts",
),
],
},
{
env: {},
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.targetedUnits).toHaveLength(2);
expect(
plan.targetedUnits
.map((unit) => unit.surface)
.toSorted((left, right) => left.localeCompare(right)),
).toEqual(["base", "channels"]);
artifacts.cleanupTempArtifacts();
});
it.each(["test/extension-test-boundary.test.ts", "test/web-search-provider-boundary.test.ts"])(
"routes targeted boundary inventories through the lean boundary config: %s",
(file) => {
const artifacts = createExecutionArtifacts({});
const plan = buildExecutionPlan(
{
mode: "local",
surfaces: [],
passthroughArgs: [file],
},
{
env: {},
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.targetedUnits).toHaveLength(1);
expect(plan.targetedUnits[0]?.surface).toBe("unit");
expect(plan.targetedUnits[0]?.args).toContain("vitest.boundary.config.ts");
artifacts.cleanupTempArtifacts();
},
);
it("normalizes --bail=0 into collect-all failure policy", () => {
const artifacts = createExecutionArtifacts({});
const plan = buildExecutionPlan(
{
mode: "local",
surfaces: ["unit"],
passthroughArgs: ["--bail=0"],
},
{
env: {},
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
expect(plan.failurePolicy).toBe("collect-all");
expect(plan.passthroughOptionArgs).not.toContain("--bail=0");
artifacts.cleanupTempArtifacts();
});
it("explains runtime truth using the same catalog and worker policy", () => {
const explanation = explainExecutionTarget(
{
mode: "local",
fileFilters: ["src/auto-reply/reply/followup-runner.test.ts"],
},
{
env: {},
},
);
expect(explanation.surface).toBe("base");
expect(explanation.pool).toBe("forks");
expect(explanation.reasons).toContain("base-pinned-manifest");
expect(explanation.intentProfile).toBe("normal");
});
it("routes synthetic bundled-plugin fixture tests through the bundled surface", () => {
const explanation = explainExecutionTarget(
{
mode: "local",
fileFilters: ["src/plugin-sdk/facade-runtime.test.ts"],
},
{
env: {},
},
);
expect(explanation.surface).toBe("bundled");
expect(explanation.args).toContain("vitest.bundled.config.ts");
});
it("uses hotspot-backed memory isolation when explaining unit tests", () => {
const explanation = explainExecutionTarget(
{
mode: "local",
fileFilters: ["src/infra/outbound/channel-resolution.test.ts"],
},
{
env: {
OPENCLAW_TEST_LOAD_AWARE: "0",
},
},
);
expect(explanation.isolate).toBe(true);
expect(explanation.reasons).toContain("unit-memory-isolated");
});
it("normalizes absolute explain targets before classification", () => {
const relativeExplanation = explainExecutionTarget(
{
mode: "local",
fileFilters: ["src/infra/outbound/channel-resolution.test.ts"],
},
{
env: {
OPENCLAW_TEST_LOAD_AWARE: "0",
},
},
);
const absoluteExplanation = explainExecutionTarget(
{
mode: "local",
fileFilters: [path.join(process.cwd(), "src/infra/outbound/channel-resolution.test.ts")],
},
{
env: {
OPENCLAW_TEST_LOAD_AWARE: "0",
},
},
);
expect(absoluteExplanation.file).toBe(relativeExplanation.file);
expect(absoluteExplanation.surface).toBe(relativeExplanation.surface);
expect(absoluteExplanation.pool).toBe(relativeExplanation.pool);
expect(absoluteExplanation.isolate).toBe(relativeExplanation.isolate);
expect(absoluteExplanation.reasons).toEqual(relativeExplanation.reasons);
});
it("explains timed-heavy extension suites as isolated", () => {
const explanation = explainExecutionTarget(
{
mode: "ci",
fileFilters: ["extensions/matrix/src/cli.test.ts"],
},
{
env: {
CI: "true",
GITHUB_ACTIONS: "true",
},
},
);
expect(explanation.surface).toBe("extensions");
expect(explanation.isolate).toBe(true);
expect(explanation.reasons).toContain("extensions-timed-heavy");
});
it("explains timed-heavy channel suites as isolated", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
};
const timedHeavyFile = resolveTimedHeavyChannelFixture();
const explanation = explainExecutionTarget(
{
mode: "ci",
fileFilters: [timedHeavyFile],
},
{
env,
},
);
expect(explanation.surface).toBe("channels");
expect(explanation.isolate).toBe(true);
expect(explanation.reasons).toContain("channels-timed-heavy");
});
it("does not leak default-plan shard assignments into targeted units with the same id", () => {
const artifacts = createExecutionArtifacts({});
const plan = buildExecutionPlan(
{
mode: "local",
fileFilters: ["src/cli/qr-dashboard.integration.test.ts"],
passthroughArgs: [],
},
{
env: {
OPENCLAW_TEST_SHARDS: "4",
OPENCLAW_TEST_SHARD_INDEX: "2",
OPENCLAW_TEST_LOAD_AWARE: "0",
},
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const targetedUnit = plan.targetedUnits.at(0);
const defaultUnitWithSameId = plan.allUnits.find((unit) => unit.id === targetedUnit?.id);
expect(targetedUnit).toBeTruthy();
expect(defaultUnitWithSameId).toBeTruthy();
const targetedUnitRecord = targetedUnit!;
const defaultUnitRecord = defaultUnitWithSameId as typeof targetedUnitRecord;
expect(defaultUnitRecord).not.toBe(targetedUnitRecord);
expect(plan.topLevelSingleShardAssignments.get(targetedUnitRecord)).toBeUndefined();
expect(plan.topLevelSingleShardAssignments.get(defaultUnitRecord)).toBeDefined();
artifacts.cleanupTempArtifacts();
});
it("pins the smallest CI include-file batches to fixed shards", () => {
const env = {
CI: "true",
GITHUB_ACTIONS: "true",
OPENCLAW_TEST_SHARDS: "4",
OPENCLAW_TEST_SHARD_INDEX: "1",
OPENCLAW_TEST_LOAD_AWARE: "0",
};
const artifacts = createExecutionArtifacts(env);
const plan = buildExecutionPlan(
{
mode: "ci",
passthroughArgs: [],
},
{
env,
platform: "linux",
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const shardableUnits = plan.parallelUnits.filter(
(unit) =>
unit.id.startsWith("unit-fast-") &&
Array.isArray(unit.includeFiles) &&
unit.includeFiles.length > 0,
);
const smallestIncludeCount = Math.min(
...shardableUnits.map((unit) => unit.includeFiles.length),
);
const smallestBatches = shardableUnits.filter(
(unit) => unit.includeFiles.length === smallestIncludeCount,
);
expect(smallestBatches.length).toBeGreaterThan(0);
expect(smallestBatches.every((unit) => typeof unit.fixedShardIndex === "number")).toBe(true);
expect(
smallestBatches.every((unit) => plan.topLevelSingleShardAssignments.get(unit) === undefined),
).toBe(true);
artifacts.cleanupTempArtifacts();
});
it("removes planner temp artifacts when cleanup runs after planning", () => {
const artifacts = createExecutionArtifacts({});
buildExecutionPlan(
{
mode: "local",
surfaces: ["unit"],
passthroughArgs: [],
},
{
env: {},
writeTempJsonArtifact: artifacts.writeTempJsonArtifact,
},
);
const artifactDir = artifacts.ensureTempArtifactDir();
expect(fs.existsSync(artifactDir)).toBe(true);
artifacts.cleanupTempArtifacts();
expect(fs.existsSync(artifactDir)).toBe(false);
});
it("keeps fd-backed artifact streams writable after temp cleanup", async () => {
const artifacts = createExecutionArtifacts({});
const artifactDir = artifacts.ensureTempArtifactDir();
const logPath = path.join(artifactDir, "lane.log");
const stream = createTempArtifactWriteStream(logPath);
stream.write("before cleanup\n");
artifacts.cleanupTempArtifacts();
await expect(
new Promise<void>((resolve, reject) => {
stream.on("error", reject);
stream.write("after cleanup\n");
stream.end(() => resolve());
}),
).resolves.toBeUndefined();
expect(fs.existsSync(artifactDir)).toBe(false);
});
it("builds a CI manifest with planner-owned shard counts and matrices", () => {
const manifest = buildCIExecutionManifest(
{
eventName: "pull_request",
docsOnly: false,
docsChanged: false,
runNode: true,
runMacos: true,
runAndroid: true,
runWindows: true,
runSkillsPython: false,
hasChangedExtensions: true,
changedExtensionsMatrix: { include: [{ extension: "discord" }] },
},
{
env: {},
},
);
expect(manifest.jobs.buildArtifacts.enabled).toBe(true);
expect(manifest.shardCounts.unit).toBe(4);
expect(manifest.shardCounts.channels).toBe(3);
expect(manifest.shardCounts.extensionFast).toBeGreaterThanOrEqual(4);
expect(manifest.shardCounts.extensionFast).toBeLessThanOrEqual(6);
expect(manifest.shardCounts.windows).toBe(6);
expect(manifest.shardCounts.macosNode).toBe(9);
expect(manifest.jobs.checks.matrix.include).toHaveLength(7);
expect(manifest.jobs.checksWindows.matrix.include).toHaveLength(6);
expect(manifest.jobs.macosNode.matrix.include).toHaveLength(9);
expect(manifest.jobs.checksFast.matrix.include).toHaveLength(
manifest.shardCounts.extensionFast + 2,
);
expect(manifest.requiredCheckNames).toContain("checks-fast-bundled");
expect(manifest.jobs.checksFast.matrix.include).toEqual(
expect.arrayContaining([
expect.objectContaining({
check_name: "checks-fast-bundled",
command: "pnpm test:bundled",
}),
]),
);
expect(
manifest.jobs.checksFast.matrix.include
.filter((entry) => entry.task === "extensions")
.every(
(entry) => typeof entry.shard_count === "number" && typeof entry.shard_index === "number",
),
).toBe(true);
expect(manifest.jobs.macosSwift.enabled).toBe(true);
expect(manifest.requiredCheckNames).toContain("macos-swift");
expect(manifest.requiredCheckNames).not.toContain("macos-swift-lint");
expect(manifest.requiredCheckNames).not.toContain("macos-swift-build");
expect(manifest.requiredCheckNames).not.toContain("macos-swift-test");
expect(manifest.jobs.extensionFast.matrix.include).toEqual([
{ check_name: "extension-fast-discord", extension: "discord" },
]);
});
it("suppresses heavy CI jobs in docs-only manifests", () => {
const manifest = buildCIExecutionManifest(
{
eventName: "pull_request",
docsOnly: true,
docsChanged: true,
runNode: false,
runMacos: false,
runAndroid: false,
runWindows: false,
runSkillsPython: false,
hasChangedExtensions: false,
},
{
env: {},
},
);
expect(manifest.jobs.buildArtifacts.enabled).toBe(false);
expect(manifest.jobs.checks.enabled).toBe(false);
expect(manifest.jobs.checksWindows.enabled).toBe(false);
expect(manifest.jobs.macosNode.enabled).toBe(false);
expect(manifest.jobs.checkDocs.enabled).toBe(true);
});
it("adds the push-only compat lane to push manifests", () => {
const manifest = buildCIExecutionManifest(
{
eventName: "push",
docsOnly: false,
docsChanged: false,
runNode: true,
runMacos: false,
runAndroid: false,
runWindows: false,
runSkillsPython: false,
hasChangedExtensions: false,
},
{
env: {},
},
);
expect(
manifest.jobs.checks.matrix.include.some((entry) => entry.task === "compat-node22"),
).toBe(true);
});
});
describe("resolvePnpmCommandInvocation", () => {
it("prefers the parent pnpm CLI path when npm_execpath points to pnpm", () => {
expect(
resolvePnpmCommandInvocation({
npmExecPath: "/opt/homebrew/lib/node_modules/corepack/dist/pnpm.cjs",
nodeExecPath: "/usr/local/bin/node",
platform: "linux",
}),
).toEqual({
command: "/usr/local/bin/node",
args: ["/opt/homebrew/lib/node_modules/corepack/dist/pnpm.cjs"],
});
});
it("falls back to cmd.exe mediation on Windows when npm_execpath is unavailable", () => {
expect(
resolvePnpmCommandInvocation({
npmExecPath: "",
platform: "win32",
comSpec: "C:\\Windows\\System32\\cmd.exe",
}),
).toEqual({
command: "C:\\Windows\\System32\\cmd.exe",
args: ["/d", "/s", "/c", "pnpm.cmd"],
});
});
});
describe("resolveVitestFsModuleCachePath", () => {
it("uses a lane-local cache path by default on non-Windows hosts", () => {
expect(
resolveVitestFsModuleCachePath({
cwd: "/repo",
env: {},
platform: "linux",
unitId: "unit-fast-1",
}),
).toBe("/repo/node_modules/.experimental-vitest-cache/unit-fast-1");
});
it("honors the requested Windows platform when building the cache path", () => {
expect(
resolveVitestFsModuleCachePath({
cwd: "/repo",
env: {
OPENCLAW_VITEST_FS_MODULE_CACHE: "1",
},
platform: "win32",
unitId: "unit-fast-1",
}),
).toBe("\\repo\\node_modules\\.experimental-vitest-cache\\unit-fast-1");
});
it("respects an explicit cache path override", () => {
expect(
resolveVitestFsModuleCachePath({
cwd: "/repo",
env: {
OPENCLAW_VITEST_FS_MODULE_CACHE_PATH: "/tmp/custom-vitest-cache",
},
platform: "linux",
unitId: "unit-fast-1",
}),
).toBe("/tmp/custom-vitest-cache");
});
it("does not force a cache path when the cache is disabled", () => {
expect(
resolveVitestFsModuleCachePath({
cwd: "/repo",
env: {
OPENCLAW_VITEST_FS_MODULE_CACHE: "0",
},
platform: "linux",
unitId: "unit-fast-1",
}),
).toBeUndefined();
});
});

View File

@@ -1,154 +0,0 @@
import { describe, expect, it } from "vitest";
import {
dedupeFilesPreserveOrder,
packFilesByDuration,
packFilesByDurationWithBaseLoads,
selectMemoryHeavyFiles,
selectTimedHeavyFiles,
selectUnitHeavyFileGroups,
} from "../../scripts/test-runner-manifest.mjs";
describe("scripts/test-runner-manifest timed selection", () => {
it("only selects known timed heavy files above the minimum", () => {
expect(
selectTimedHeavyFiles({
candidates: ["a.test.ts", "b.test.ts", "c.test.ts"],
limit: 3,
minDurationMs: 1000,
exclude: new Set(["c.test.ts"]),
timings: {
defaultDurationMs: 250,
files: {
"a.test.ts": { durationMs: 2500 },
"b.test.ts": { durationMs: 900 },
"c.test.ts": { durationMs: 5000 },
},
},
}),
).toEqual(["a.test.ts"]);
});
});
describe("scripts/test-runner-manifest memory selection", () => {
it("selects known memory hotspots above the minimum", () => {
expect(
selectMemoryHeavyFiles({
candidates: ["a.test.ts", "b.test.ts", "c.test.ts", "d.test.ts"],
limit: 3,
minDeltaKb: 256 * 1024,
exclude: new Set(["c.test.ts"]),
hotspots: {
files: {
"a.test.ts": { deltaKb: 600 * 1024 },
"b.test.ts": { deltaKb: 120 * 1024 },
"c.test.ts": { deltaKb: 900 * 1024 },
},
},
}),
).toEqual(["a.test.ts"]);
});
it("orders selected memory hotspots by descending retained heap", () => {
expect(
selectMemoryHeavyFiles({
candidates: ["a.test.ts", "b.test.ts", "c.test.ts"],
limit: 2,
minDeltaKb: 1,
hotspots: {
files: {
"a.test.ts": { deltaKb: 300 },
"b.test.ts": { deltaKb: 700 },
"c.test.ts": { deltaKb: 500 },
},
},
}),
).toEqual(["b.test.ts", "c.test.ts"]);
});
it("gives memory-heavy isolation precedence over timed-heavy buckets", () => {
expect(
selectUnitHeavyFileGroups({
candidates: ["overlap.test.ts", "memory-only.test.ts", "timed-only.test.ts"],
behaviorOverrides: new Set(),
timedLimit: 3,
timedMinDurationMs: 1000,
memoryLimit: 3,
memoryMinDeltaKb: 256 * 1024,
timings: {
defaultDurationMs: 250,
files: {
"overlap.test.ts": { durationMs: 5000 },
"timed-only.test.ts": { durationMs: 4200 },
},
},
hotspots: {
files: {
"overlap.test.ts": { deltaKb: 900 * 1024 },
"memory-only.test.ts": { deltaKb: 700 * 1024 },
},
},
}),
).toEqual({
memoryHeavyFiles: ["overlap.test.ts", "memory-only.test.ts"],
timedHeavyFiles: ["timed-only.test.ts"],
});
});
});
describe("dedupeFilesPreserveOrder", () => {
it("removes duplicates while keeping the first-seen order", () => {
expect(
dedupeFilesPreserveOrder([
"src/b.test.ts",
"src/a.test.ts",
"src/b.test.ts",
"src/c.test.ts",
"src/a.test.ts",
]),
).toEqual(["src/b.test.ts", "src/a.test.ts", "src/c.test.ts"]);
});
it("filters excluded files before deduping", () => {
expect(
dedupeFilesPreserveOrder(
["src/a.test.ts", "src/b.test.ts", "src/c.test.ts", "src/b.test.ts"],
new Set(["src/b.test.ts"]),
),
).toEqual(["src/a.test.ts", "src/c.test.ts"]);
});
});
describe("packFilesByDuration", () => {
it("packs heavier files into the lightest remaining bucket", () => {
const durationByFile = {
"src/a.test.ts": 100,
"src/b.test.ts": 90,
"src/c.test.ts": 20,
"src/d.test.ts": 10,
} satisfies Record<string, number>;
expect(
packFilesByDuration(Object.keys(durationByFile), 2, (file) => durationByFile[file] ?? 0),
).toEqual([
["src/a.test.ts", "src/d.test.ts"],
["src/b.test.ts", "src/c.test.ts"],
]);
});
it("accounts for existing shard load when packing new work", () => {
const durationByFile = {
"src/a.test.ts": 100,
"src/b.test.ts": 90,
"src/c.test.ts": 20,
} satisfies Record<string, number>;
expect(
packFilesByDurationWithBaseLoads(
Object.keys(durationByFile),
3,
(file) => durationByFile[file] ?? 0,
[0, 200, 10],
),
).toEqual([["src/a.test.ts", "src/c.test.ts"], [], ["src/b.test.ts"]]);
});
});

View File

@@ -1,101 +0,0 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { loadHotspotInputTexts } from "../../scripts/test-update-memory-hotspots-sources.mjs";
const tempFiles = [];
afterEach(() => {
for (const tempFile of tempFiles.splice(0)) {
try {
fs.unlinkSync(tempFile);
} catch {
// Ignore temp cleanup races in tests.
}
}
});
describe("test-update-memory-hotspots source loading", () => {
it("loads local log files with basename-derived source names", () => {
const tempLog = path.join(os.tmpdir(), `openclaw-hotspots-${Date.now()}.log`);
tempFiles.push(tempLog);
fs.writeFileSync(tempLog, "local log");
expect(loadHotspotInputTexts({ logPaths: [tempLog] })).toEqual([
{ sourceName: path.basename(tempLog, ".log"), text: "local log" },
]);
});
it("loads GitHub Actions job logs through gh", () => {
const execFileSyncImpl = vi.fn(() => "remote log");
expect(
loadHotspotInputTexts({
ghJobs: ["69804189668"],
execFileSyncImpl,
}),
).toEqual([{ sourceName: "gh-job-69804189668", text: "remote log" }]);
expect(execFileSyncImpl).toHaveBeenCalledWith(
"gh",
["run", "view", "--job", "69804189668", "--log"],
{
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
},
);
});
it("loads GitHub Actions run jobs and filters by job name", () => {
const execFileSyncImpl = vi.fn((command, args) => {
if (
command === "gh" &&
args[0] === "run" &&
args[1] === "view" &&
args[2] === "23933168654" &&
args[3] === "--json" &&
args[4] === "jobs"
) {
return JSON.stringify({
jobs: [
{ databaseId: 69804189668, name: "checks-fast-extensions-1" },
{ databaseId: 69804189669, name: "build-smoke" },
],
});
}
if (command === "gh" && args[0] === "run" && args[1] === "view" && args[2] === "--job") {
return `job-log-${args[3]}`;
}
throw new Error("unexpected gh call");
});
expect(
loadHotspotInputTexts({
ghRuns: ["23933168654"],
ghRunJobMatches: ["extensions"],
execFileSyncImpl,
}),
).toEqual([{ sourceName: "gh-job-69804189668", text: "job-log-69804189668" }]);
expect(execFileSyncImpl).toHaveBeenCalledWith(
"gh",
["run", "view", "23933168654", "--json", "jobs"],
{
encoding: "utf8",
maxBuffer: 8 * 1024 * 1024,
},
);
const jobLogCalls = execFileSyncImpl.mock.calls.filter(
(call) => call[0] === "gh" && call[1][2] === "--job",
);
expect(jobLogCalls).toEqual([
[
"gh",
["run", "view", "--job", "69804189668", "--log"],
{
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
},
],
]);
});
});

View File

@@ -1,18 +0,0 @@
import { describe, expect, it } from "vitest";
import { matchesHotspotSummaryLane } from "../../scripts/test-update-memory-hotspots-utils.mjs";
describe("test-update-memory-hotspots lane matching", () => {
it("matches the exact target lane", () => {
expect(matchesHotspotSummaryLane("unit-fast", "unit-fast")).toBe(true);
});
it("matches configured lane prefixes", () => {
expect(matchesHotspotSummaryLane("unit-chat-memory-isolated", "unit-fast", ["unit-"])).toBe(
true,
);
});
it("rejects unrelated lanes", () => {
expect(matchesHotspotSummaryLane("extensions", "unit-fast", ["unit-"])).toBe(false);
});
});

View File

@@ -1,32 +0,0 @@
import { describe, expect, it } from "vitest";
import {
loadChannelTimingManifest,
loadTestRunnerBehavior,
} from "../scripts/test-runner-manifest.mjs";
import { bundledPluginDirPrefix, bundledPluginFile } from "./helpers/bundled-plugin-paths.js";
describe("loadTestRunnerBehavior", () => {
it("loads channel isolated entries from the behavior manifest", () => {
const behavior = loadTestRunnerBehavior();
const files = behavior.channels.isolated.map((entry) => entry.file);
expect(files).toContain(
bundledPluginFile("discord", "src/monitor/message-handler.preflight.acp-bindings.test.ts"),
);
});
it("loads channel isolated prefixes from the behavior manifest", () => {
const behavior = loadTestRunnerBehavior();
expect(behavior.channels.isolatedPrefixes).toContain(
bundledPluginDirPrefix("discord", "src/monitor"),
);
});
it("loads channel timing metadata from the timing manifest", () => {
const timings = loadChannelTimingManifest();
expect(timings.config).toBe("vitest.channels.config.ts");
expect(Object.keys(timings.files).length).toBeGreaterThan(0);
});
});

View File

@@ -1,27 +1,8 @@
import { describe, expect, it } from "vitest";
import {
resolveExecutionBudget,
resolveRuntimeCapabilities,
} from "../scripts/test-planner/runtime-profile.mjs";
import baseConfig, { resolveLocalVitestMaxWorkers } from "../vitest.config.ts";
function resolveHighMemoryLocalRuntime() {
return resolveRuntimeCapabilities(
{
RUNNER_OS: "macOS",
},
{
cpuCount: 16,
totalMemoryBytes: 128 * 1024 ** 3,
platform: "darwin",
mode: "local",
loadAverage: [0.2, 0.2, 0.2],
},
);
}
describe("resolveLocalVitestMaxWorkers", () => {
it("derives a mid-tier local cap for 64 GiB hosts", () => {
it("derives a moderate local cap for 64 GiB hosts", () => {
expect(
resolveLocalVitestMaxWorkers(
{
@@ -30,8 +11,6 @@ describe("resolveLocalVitestMaxWorkers", () => {
{
cpuCount: 10,
totalMemoryBytes: 64 * 1024 ** 3,
platform: "darwin",
loadAverage: [0.1, 0.1, 0.1],
},
),
).toBe(4);
@@ -52,165 +31,42 @@ describe("resolveLocalVitestMaxWorkers", () => {
).toBe(2);
});
it("maps the legacy low profile to serial intent for compatibility", () => {
const runtime = resolveRuntimeCapabilities(
{
OPENCLAW_TEST_PROFILE: "low",
RUNNER_OS: "Linux",
},
{
cpuCount: 8,
totalMemoryBytes: 32 * 1024 ** 3,
platform: "linux",
mode: "local",
},
);
expect(runtime.intentProfile).toBe("serial");
it("respects the legacy OPENCLAW_TEST_WORKERS override too", () => {
expect(
resolveLocalVitestMaxWorkers(
{
OPENCLAW_TEST_WORKERS: "3",
},
{
cpuCount: 16,
totalMemoryBytes: 128 * 1024 ** 3,
},
),
).toBe(3);
});
it("classifies 64 GiB local macOS hosts as mid-memory capabilities", () => {
const runtime = resolveRuntimeCapabilities(
{
RUNNER_OS: "macOS",
},
{
cpuCount: 10,
totalMemoryBytes: 64 * 1024 ** 3,
platform: "darwin",
mode: "local",
loadAverage: [0.2, 0.2, 0.2],
},
);
expect(runtime.runtimeProfileName).toBe("local-darwin");
expect(runtime.memoryBand).toBe("mid");
expect(runtime.loadBand).toBe("idle");
it("keeps memory-constrained hosts conservative", () => {
expect(
resolveLocalVitestMaxWorkers(
{},
{
cpuCount: 16,
totalMemoryBytes: 16 * 1024 ** 3,
},
),
).toBe(2);
});
it("does not classify 64 GiB non-macOS hosts as constrained locals", () => {
const runtime = resolveRuntimeCapabilities(
{
RUNNER_OS: "Linux",
},
{
cpuCount: 16,
totalMemoryBytes: 64 * 1024 ** 3,
platform: "linux",
mode: "local",
loadAverage: [0.2, 0.2, 0.2],
},
);
expect(runtime.memoryBand).toBe("mid");
expect(runtime.runtimeProfileName).toBe("local-linux");
});
it("reduces local budgets when the host is busy", () => {
const runtime = resolveRuntimeCapabilities(
{
RUNNER_OS: "Linux",
},
{
cpuCount: 10,
totalMemoryBytes: 16 * 1024 ** 3,
platform: "linux",
mode: "local",
loadAverage: [9.5, 9.5, 9.5],
},
);
const budget = resolveExecutionBudget(runtime);
expect(runtime.memoryBand).toBe("constrained");
expect(runtime.loadBand).toBe("busy");
expect(budget.vitestMaxWorkers).toBe(1);
expect(budget.topLevelParallelLimit).toBe(1);
});
it("keeps 64 GiB hosts mid-tier but scales them down under saturation", () => {
const runtime = resolveRuntimeCapabilities(
{
RUNNER_OS: "Linux",
},
{
cpuCount: 10,
totalMemoryBytes: 64 * 1024 ** 3,
platform: "linux",
mode: "local",
loadAverage: [11.5, 11.5, 11.5],
},
);
const budget = resolveExecutionBudget(runtime);
expect(runtime.memoryBand).toBe("mid");
expect(runtime.loadBand).toBe("saturated");
expect(budget.vitestMaxWorkers).toBe(2);
expect(budget.unitIsolatedWorkers).toBe(1);
expect(budget.deferredRunConcurrency).toBe(1);
});
it("backs off isolated workers and shrinks unit batches on saturated high-memory locals", () => {
const runtime = resolveRuntimeCapabilities(
{
RUNNER_OS: "macOS",
},
{
cpuCount: 16,
totalMemoryBytes: 128 * 1024 ** 3,
platform: "darwin",
mode: "local",
loadAverage: [18, 18, 18],
},
);
const budget = resolveExecutionBudget(runtime);
expect(runtime.memoryBand).toBe("high");
expect(runtime.loadBand).toBe("saturated");
expect(budget.unitIsolatedWorkers).toBe(1);
expect(budget.unitFastBatchTargetMs).toBe(22_500);
});
it("keeps CI windows policy constrained independently of host load", () => {
const runtime = resolveRuntimeCapabilities(
{
CI: "true",
RUNNER_OS: "Windows",
},
{
cpuCount: 32,
totalMemoryBytes: 128 * 1024 ** 3,
platform: "win32",
mode: "ci",
loadAverage: [0, 0, 0],
},
);
const budget = resolveExecutionBudget(runtime);
expect(runtime.runtimeProfileName).toBe("ci-windows");
expect(budget.vitestMaxWorkers).toBe(2);
expect(budget.topLevelParallelLimit).toBe(2);
});
it("enables shared channel batching on high-memory local hosts", () => {
const runtime = resolveHighMemoryLocalRuntime();
const budget = resolveExecutionBudget(runtime);
expect(runtime.memoryBand).toBe("high");
expect(runtime.loadBand).toBe("idle");
expect(budget.channelsBatchTargetMs).toBe(30_000);
expect(budget.channelSharedWorkers).toBe(5);
expect(budget.deferredRunConcurrency).toBe(8);
expect(budget.topLevelParallelLimitNoIsolate).toBe(14);
});
it("uses a coarser shared extension batch target on high-memory local hosts", () => {
const runtime = resolveHighMemoryLocalRuntime();
const budget = resolveExecutionBudget(runtime);
expect(runtime.memoryBand).toBe("high");
expect(runtime.loadBand).toBe("idle");
expect(budget.extensionsBatchTargetMs).toBe(300_000);
expect(budget.extensionWorkers).toBe(5);
it("lets roomy hosts use a higher default cap", () => {
expect(
resolveLocalVitestMaxWorkers(
{},
{
cpuCount: 16,
totalMemoryBytes: 128 * 1024 ** 3,
},
),
).toBe(6);
});
});