From 1bfef1782541d78a3a3c673295f3bf867994907b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 24 Mar 2026 04:18:58 +0000 Subject: [PATCH] ci: balance shards and reuse pr artifacts --- .github/workflows/ci.yml | 30 +- scripts/test-parallel.mjs | 122 ++- scripts/test-runner-manifest.mjs | 57 +- test/fixtures/test-timings.channels.json | 1096 +++++++++++++++++++++ test/scripts/test-runner-manifest.test.ts | 18 + test/test-runner-manifest.test.ts | 12 +- 6 files changed, 1305 insertions(+), 30 deletions(-) create mode 100644 test/fixtures/test-timings.channels.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be1db16f9d7..779fd326cd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -162,7 +162,7 @@ jobs: # Build dist once for Node-relevant changes and share it with downstream jobs. build-artifacts: needs: [preflight] - if: github.event_name == 'push' && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' + if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' runs-on: blacksmith-16vcpu-ubuntu-2404 timeout-minutes: 20 steps: @@ -234,7 +234,7 @@ jobs: checks: needs: [preflight, build-artifacts] - if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success') + if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && needs.build-artifacts.result == 'success' runs-on: blacksmith-16vcpu-ubuntu-2404 timeout-minutes: 20 strategy: @@ -327,27 +327,19 @@ jobs: fi - name: Download dist artifact - if: github.event_name == 'push' && matrix.task == 'test' + if: matrix.task == 'test' uses: actions/download-artifact@v8 with: name: dist-build path: dist/ - name: Download A2UI bundle artifact - if: github.event_name == 'push' && (matrix.task == 'test' || matrix.task == 'channels') + if: matrix.task == 'test' || matrix.task == 'channels' uses: actions/download-artifact@v8 with: name: canvas-a2ui-bundle path: src/canvas-host/a2ui/ - - name: Build A2UI bundle - if: github.event_name != 'push' && (matrix.task == 'test' || matrix.task == 'channels') - run: pnpm canvas:a2ui:bundle - - - name: Build dist - if: github.event_name != 'push' && matrix.task == 'test' && matrix.runtime == 'node' - run: pnpm build - - name: Run ${{ matrix.task }} (${{ matrix.runtime }}) if: github.event_name != 'pull_request' || matrix.task != 'compat-node22' run: ${{ matrix.command }} @@ -592,7 +584,7 @@ jobs: checks-windows: needs: [preflight, build-artifacts] - if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_windows == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success') + if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_windows == 'true' && needs.build-artifacts.result == 'success' runs-on: blacksmith-32vcpu-windows-2025 timeout-minutes: 20 env: @@ -719,27 +711,19 @@ jobs: echo "OPENCLAW_TEST_SHARD_INDEX=${{ matrix.shard_index }}" >> "$GITHUB_ENV" - name: Download dist artifact - if: github.event_name == 'push' && matrix.task == 'test' + if: matrix.task == 'test' uses: actions/download-artifact@v8 with: name: dist-build path: dist/ - name: Download A2UI bundle artifact - if: github.event_name == 'push' && matrix.task == 'test' + if: matrix.task == 'test' uses: actions/download-artifact@v8 with: name: canvas-a2ui-bundle path: src/canvas-host/a2ui/ - - name: Build A2UI bundle (Windows) - if: github.event_name != 'push' && matrix.task == 'test' - run: pnpm canvas:a2ui:bundle - - - name: Build dist (Windows) - if: github.event_name != 'push' && matrix.task == 'test' - run: pnpm build - - name: Run ${{ matrix.task }} (${{ matrix.runtime }}) run: ${{ matrix.command }} diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index fc689bf836f..01dbdb4bd3a 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -17,11 +17,13 @@ import { } from "./test-parallel-utils.mjs"; import { dedupeFilesPreserveOrder, + loadChannelTimingManifest, loadUnitMemoryHotspotManifest, loadTestRunnerBehavior, loadUnitTimingManifest, selectUnitHeavyFileGroups, packFilesByDuration, + packFilesByDurationWithBaseLoads, } from "./test-runner-manifest.mjs"; // On Windows, `.cmd` launchers can fail with `spawn EINVAL` when invoked without a shell @@ -312,6 +314,7 @@ const inferTarget = (fileFilter) => { return { owner: "base", isolated }; }; const unitTimingManifest = loadUnitTimingManifest(); +const channelTimingManifest = loadChannelTimingManifest(); const unitMemoryHotspotManifest = loadUnitMemoryHotspotManifest(); const parseEnvNumber = (name, fallback) => { const parsed = Number.parseInt(process.env[name] ?? "", 10); @@ -385,6 +388,26 @@ const unitFastExcludedFiles = [ ]; const estimateUnitDurationMs = (file) => unitTimingManifest.files[file]?.durationMs ?? unitTimingManifest.defaultDurationMs; +const estimateChannelDurationMs = (file) => + channelTimingManifest.files[file]?.durationMs ?? channelTimingManifest.defaultDurationMs; +const resolveEntryTimingEstimator = (entry) => { + const configIndex = entry.args.findIndex((arg) => arg === "--config"); + const config = configIndex >= 0 ? (entry.args[configIndex + 1] ?? "") : ""; + if (config === "vitest.unit.config.ts") { + return estimateUnitDurationMs; + } + if (config === "vitest.channels.config.ts") { + return estimateChannelDurationMs; + } + return null; +}; +const estimateEntryFilesDurationMs = (entry, files) => { + const estimateDurationMs = resolveEntryTimingEstimator(entry); + if (!estimateDurationMs) { + return files.length * 1_000; + } + return files.reduce((totalMs, file) => totalMs + estimateDurationMs(file), 0); +}; const splitFilesByDurationBudget = (files, targetDurationMs, estimateDurationMs) => { if (!Number.isFinite(targetDurationMs) || targetDurationMs <= 0 || files.length <= 1) { return [files]; @@ -462,6 +485,11 @@ const unitFastEntries = unitFastBuckets.flatMap((files, index) => { .map((batch, batchIndex) => ({ name: recycledBatches.length === 1 ? laneName : `${laneName}-batch-${String(batchIndex + 1)}`, serialPhase: "unit-fast", + includeFiles: batch, + estimatedDurationMs: estimateEntryFilesDurationMs( + { args: ["vitest", "run", "--config", "vitest.unit.config.ts"] }, + batch, + ), env: { OPENCLAW_VITEST_INCLUDE_FILE: writeTempJsonArtifact( `vitest-unit-fast-include-${String(index + 1)}-${String(batchIndex + 1)}`, @@ -564,6 +592,7 @@ const baseRuns = [ ...extensionIsolatedEntries, { name: "extensions", + includeFiles: extensionSharedCandidateFiles, env: extensionSharedCandidateFiles.length > 0 ? { @@ -585,6 +614,11 @@ const baseRuns = [ })), { name: "channels", + includeFiles: channelSharedCandidateFiles, + estimatedDurationMs: estimateEntryFilesDurationMs( + { args: ["vitest", "run", "--config", "vitest.channels.config.ts"] }, + channelSharedCandidateFiles, + ), env: channelSharedCandidateFiles.length > 0 ? { @@ -772,6 +806,65 @@ const createPerFileTargetedEntry = (file) => { name: `${formatPerFileEntryName(owner, file)}${target.isolated ? "-isolated" : ""}`, }; }; +const rebuildEntryArgsWithFilters = (entryArgs, filters) => { + const baseArgs = entryArgs.slice(0, 2); + const { optionArgs } = parsePassthroughArgs(entryArgs.slice(2)); + return [...baseArgs, ...optionArgs, ...filters]; +}; +const createPinnedShardEntry = (entry, files, fixedShardIndex) => { + const nextEntry = { + ...entry, + name: `${entry.name}-shard-${String(fixedShardIndex)}`, + fixedShardIndex, + estimatedDurationMs: estimateEntryFilesDurationMs(entry, files), + }; + if (Array.isArray(entry.includeFiles) && entry.includeFiles.length > 0) { + return { + ...nextEntry, + includeFiles: files, + env: { + ...entry.env, + OPENCLAW_VITEST_INCLUDE_FILE: writeTempJsonArtifact( + `${sanitizeArtifactName(entry.name)}-shard-${String(fixedShardIndex)}-include`, + files, + ), + }, + args: rebuildEntryArgsWithFilters(entry.args, []), + }; + } + return { + ...nextEntry, + args: rebuildEntryArgsWithFilters(entry.args, files), + }; +}; +const expandEntryAcrossTopLevelShards = (entry) => { + if (configuredShardCount === null || shardCount <= 1 || entry.fixedShardIndex !== undefined) { + return [entry]; + } + const estimateDurationMs = resolveEntryTimingEstimator(entry); + if (!estimateDurationMs) { + return [entry]; + } + const candidateFiles = + Array.isArray(entry.includeFiles) && entry.includeFiles.length > 0 + ? entry.includeFiles + : getExplicitEntryFilters(entry.args); + if (candidateFiles.length <= 1) { + return [entry]; + } + const effectiveShardCount = Math.min(shardCount, Math.max(1, candidateFiles.length - 1)); + if (effectiveShardCount <= 1) { + return [entry]; + } + const buckets = packFilesByDurationWithBaseLoads( + candidateFiles, + effectiveShardCount, + estimateDurationMs, + ); + return buckets.flatMap((files, bucketIndex) => + files.length > 0 ? [createPinnedShardEntry(entry, files, bucketIndex + 1)] : [], + ); +}; const targetedEntries = (() => { if (passthroughFileFilters.length === 0) { return []; @@ -815,7 +908,13 @@ const targetedEntries = (() => { return [createTargetedEntry(owner, false, uniqueFilters)]; }).flat(); })(); +if (configuredShardCount !== null && shardCount > 1) { + runs = runs.flatMap((entry) => expandEntryAcrossTopLevelShards(entry)); +} const estimateTopLevelEntryDurationMs = (entry) => { + if (Number.isFinite(entry.estimatedDurationMs) && entry.estimatedDurationMs > 0) { + return entry.estimatedDurationMs; + } const filters = getExplicitEntryFilters(entry.args); if (filters.length === 0) { return unitTimingManifest.defaultDurationMs; @@ -841,6 +940,9 @@ const topLevelSingleShardAssignments = (() => { // Single-file and other non-shardable explicit lanes would otherwise run on // every shard. Assign them to one top-level shard instead. const entriesNeedingAssignment = runs.filter((entry) => { + if (entry.fixedShardIndex !== undefined) { + return false; + } const explicitFilterCount = countExplicitEntryFilters(entry.args); if (explicitFilterCount === null) { return false; @@ -850,10 +952,22 @@ const topLevelSingleShardAssignments = (() => { }); const assignmentMap = new Map(); - const buckets = packFilesByDuration( + const pinnedShardLoadsMs = Array.from({ length: shardCount }, () => 0); + for (const entry of runs) { + if (entry.fixedShardIndex === undefined) { + continue; + } + const shardArrayIndex = entry.fixedShardIndex - 1; + if (shardArrayIndex < 0 || shardArrayIndex >= pinnedShardLoadsMs.length) { + continue; + } + pinnedShardLoadsMs[shardArrayIndex] += estimateTopLevelEntryDurationMs(entry); + } + const buckets = packFilesByDurationWithBaseLoads( entriesNeedingAssignment, shardCount, estimateTopLevelEntryDurationMs, + pinnedShardLoadsMs, ); for (const [bucketIndex, bucket] of buckets.entries()) { for (const entry of bucket) { @@ -1363,6 +1477,12 @@ const runOnce = (entry, extraArgs = []) => }); const run = async (entry, extraArgs = []) => { + if (entry.fixedShardIndex !== undefined) { + if (shardIndexOverride !== null && shardIndexOverride !== entry.fixedShardIndex) { + return 0; + } + return runOnce(entry, extraArgs); + } const explicitFilterCount = countExplicitEntryFilters(entry.args); const topLevelAssignedShard = topLevelSingleShardAssignments.get(entry); if (topLevelAssignedShard !== undefined) { diff --git a/scripts/test-runner-manifest.mjs b/scripts/test-runner-manifest.mjs index c92fe444c04..f33b4193c24 100644 --- a/scripts/test-runner-manifest.mjs +++ b/scripts/test-runner-manifest.mjs @@ -2,6 +2,7 @@ import { normalizeTrackedRepoPath, tryReadJsonFile } from "./test-report-utils.m export const behaviorManifestPath = "test/fixtures/test-parallel.behavior.json"; export const unitTimingManifestPath = "test/fixtures/test-timings.unit.json"; +export const channelTimingManifestPath = "test/fixtures/test-timings.channels.json"; export const unitMemoryHotspotManifestPath = "test/fixtures/test-memory-hotspots.unit.json"; const defaultTimingManifest = { @@ -9,6 +10,11 @@ const defaultTimingManifest = { defaultDurationMs: 250, files: {}, }; +const defaultChannelTimingManifest = { + config: "vitest.channels.config.ts", + defaultDurationMs: 3000, + files: {}, +}; const defaultMemoryHotspotManifest = { config: "vitest.unit.config.ts", defaultMinDeltaKb: 256 * 1024, @@ -87,12 +93,12 @@ export function loadTestRunnerBehavior() { }; } -export function loadUnitTimingManifest() { - const raw = tryReadJsonFile(unitTimingManifestPath, defaultTimingManifest); +const loadTimingManifest = (manifestPath, fallbackManifest) => { + const raw = tryReadJsonFile(manifestPath, fallbackManifest); const defaultDurationMs = Number.isFinite(raw.defaultDurationMs) && raw.defaultDurationMs > 0 ? raw.defaultDurationMs - : defaultTimingManifest.defaultDurationMs; + : fallbackManifest.defaultDurationMs; const files = Object.fromEntries( Object.entries(raw.files ?? {}) .map(([file, value]) => { @@ -116,12 +122,19 @@ export function loadUnitTimingManifest() { ); return { - config: - typeof raw.config === "string" && raw.config ? raw.config : defaultTimingManifest.config, + config: typeof raw.config === "string" && raw.config ? raw.config : fallbackManifest.config, generatedAt: typeof raw.generatedAt === "string" ? raw.generatedAt : "", defaultDurationMs, files, }; +}; + +export function loadUnitTimingManifest() { + return loadTimingManifest(unitTimingManifestPath, defaultTimingManifest); +} + +export function loadChannelTimingManifest() { + return loadTimingManifest(channelTimingManifestPath, defaultChannelTimingManifest); } export function loadUnitMemoryHotspotManifest() { @@ -268,6 +281,40 @@ export function packFilesByDuration(files, bucketCount, estimateDurationMs) { return buckets.map((bucket) => bucket.files).filter((bucket) => bucket.length > 0); } +export function packFilesByDurationWithBaseLoads( + files, + bucketCount, + estimateDurationMs, + baseLoadsMs = [], +) { + const normalizedBucketCount = Math.max(0, Math.floor(bucketCount)); + if (normalizedBucketCount <= 0) { + return []; + } + + const buckets = Array.from({ length: normalizedBucketCount }, (_, index) => ({ + totalMs: + Number.isFinite(baseLoadsMs[index]) && baseLoadsMs[index] >= 0 + ? Math.round(baseLoadsMs[index]) + : 0, + files: [], + })); + + const sortedFiles = [...files].toSorted((left, right) => { + return estimateDurationMs(right) - estimateDurationMs(left); + }); + + for (const file of sortedFiles) { + const bucket = buckets.reduce((lightest, current) => + current.totalMs < lightest.totalMs ? current : lightest, + ); + bucket.files.push(file); + bucket.totalMs += estimateDurationMs(file); + } + + return buckets.map((bucket) => bucket.files); +} + export function dedupeFilesPreserveOrder(files, exclude = new Set()) { const result = []; const seen = new Set(); diff --git a/test/fixtures/test-timings.channels.json b/test/fixtures/test-timings.channels.json new file mode 100644 index 00000000000..f4efd016ff2 --- /dev/null +++ b/test/fixtures/test-timings.channels.json @@ -0,0 +1,1096 @@ +{ + "config": "vitest.channels.config.ts", + "generatedAt": "2026-03-24T04:15:05Z", + "defaultDurationMs": 3000, + "files": { + "extensions/telegram/src/bot.create-telegram-bot.test.ts": { + "durationMs": 13900 + }, + "extensions/telegram/src/bot-message-dispatch.test.ts": { + "durationMs": 12600 + }, + "extensions/discord/src/monitor/exec-approvals.test.ts": { + "durationMs": 11300 + }, + "extensions/telegram/src/sendchataction-401-backoff.test.ts": { + "durationMs": 10400 + }, + "extensions/telegram/src/bot.test.ts": { + "durationMs": 8500 + }, + "extensions/discord/src/monitor/message-utils.test.ts": { + "durationMs": 6000 + }, + "extensions/discord/src/monitor/reply-delivery.test.ts": { + "durationMs": 6000 + }, + "extensions/discord/src/monitor/message-handler.process.test.ts": { + "durationMs": 5700 + }, + "extensions/telegram/src/monitor.test.ts": { + "durationMs": 5600 + }, + "extensions/discord/src/send.sends-basic-channel-messages.test.ts": { + "durationMs": 5300 + }, + "extensions/discord/src/monitor/provider.test.ts": { + "durationMs": 5100 + }, + "extensions/slack/src/monitor/events/interactions.test.ts": { + "durationMs": 5100 + }, + "extensions/discord/src/send.creates-thread.test.ts": { + "durationMs": 4600 + }, + "extensions/whatsapp/src/media.test.ts": { + "durationMs": 4600 + }, + "extensions/whatsapp/src/inbound.test.ts": { + "durationMs": 4400 + }, + "extensions/slack/src/monitor.tool-result.test.ts": { + "durationMs": 4300 + }, + "extensions/discord/src/monitor/message-handler.preflight.test.ts": { + "durationMs": 4100 + }, + "extensions/discord/src/monitor/message-handler.queue.test.ts": { + "durationMs": 3900 + }, + "extensions/telegram/src/fetch.test.ts": { + "durationMs": 3500 + }, + "extensions/whatsapp/src/monitor-inbox.captures-media-path-image-messages.test.ts": { + "durationMs": 3200 + }, + "extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts": { + "durationMs": 3000 + }, + "extensions/telegram/src/webhook.test.ts": { + "durationMs": 2900 + }, + "extensions/slack/src/monitor/slash.test.ts": { + "durationMs": 2800 + }, + "extensions/whatsapp/src/send.test.ts": { + "durationMs": 2700 + }, + "extensions/whatsapp/src/auto-reply/monitor/process-message.inbound-context.test.ts": { + "durationMs": 2500 + }, + "extensions/discord/src/outbound-adapter.test.ts": { + "durationMs": 2300 + }, + "extensions/whatsapp/src/auto-reply/deliver-reply.test.ts": { + "durationMs": 2200 + }, + "extensions/slack/src/monitor/events/reactions.test.ts": { + "durationMs": 2200 + }, + "extensions/whatsapp/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test.ts": { + "durationMs": 2100 + }, + "extensions/telegram/src/bot-native-commands.session-meta.test.ts": { + "durationMs": 2100 + }, + "extensions/discord/src/monitor/native-command.plugin-dispatch.test.ts": { + "durationMs": 2000 + }, + "extensions/signal/src/monitor/event-handler.mention-gating.test.ts": { + "durationMs": 1900 + }, + "extensions/telegram/src/bot-native-commands.test.ts": { + "durationMs": 1700 + }, + "extensions/whatsapp/src/auto-reply/heartbeat-runner.test.ts": { + "durationMs": 1700 + }, + "extensions/slack/src/monitor/events/messages.test.ts": { + "durationMs": 1700 + }, + "extensions/whatsapp/src/monitor-inbox.streams-inbound-messages.test.ts": { + "durationMs": 1600 + }, + "extensions/whatsapp/src/resolve-target.test.ts": { + "durationMs": 1600 + }, + "extensions/telegram/src/bot-message-context.dm-threads.test.ts": { + "durationMs": 1600 + }, + "extensions/whatsapp/src/inbound/access-control.test.ts": { + "durationMs": 1600 + }, + "src/browser/routes/agent.snapshot.test.ts": { + "durationMs": 1600 + }, + "extensions/whatsapp/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts": { + "durationMs": 1500 + }, + "src/browser/server-context.remote-tab-ops.test.ts": { + "durationMs": 1500 + }, + "extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts": { + "durationMs": 1500 + }, + "extensions/whatsapp/src/setup-surface.test.ts": { + "durationMs": 1400 + }, + "extensions/whatsapp/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test.ts": { + "durationMs": 1400 + }, + "extensions/discord/src/monitor/listeners.test.ts": { + "durationMs": 1300 + }, + "extensions/signal/src/monitor/event-handler.inbound-context.test.ts": { + "durationMs": 1300 + }, + "extensions/telegram/src/bot-message-context.named-account-dm.test.ts": { + "durationMs": 1200 + }, + "extensions/slack/src/monitor/events/pins.test.ts": { + "durationMs": 1200 + }, + "extensions/slack/src/monitor/events/members.test.ts": { + "durationMs": 1200 + }, + "extensions/discord/src/monitor/thread-bindings.discord-api.test.ts": { + "durationMs": 1100 + }, + "extensions/discord/src/monitor/native-command.options.test.ts": { + "durationMs": 1100 + }, + "src/browser/pw-tools-core.screenshots-element-selector.test.ts": { + "durationMs": 1000 + }, + "extensions/telegram/src/bot-message-context.audio-transcript.test.ts": { + "durationMs": 1000 + }, + "extensions/whatsapp/src/monitor-inbox.append-upsert.test.ts": { + "durationMs": 999 + }, + "src/browser/server.agent-contract-snapshot-endpoints.test.ts": { + "durationMs": 982 + }, + "extensions/slack/src/monitor/auth.test.ts": { + "durationMs": 981 + }, + "extensions/telegram/src/bot-message-context.acp-bindings.test.ts": { + "durationMs": 980 + }, + "extensions/slack/src/monitor/message-handler.test.ts": { + "durationMs": 967 + }, + "extensions/discord/src/monitor/provider.skill-dedupe.test.ts": { + "durationMs": 954 + }, + "extensions/discord/src/channel.test.ts": { + "durationMs": 950 + }, + "extensions/slack/src/send.upload.test.ts": { + "durationMs": 923 + }, + "extensions/telegram/src/bot-native-commands.registry.test.ts": { + "durationMs": 842 + }, + "extensions/slack/src/actions.download-file.test.ts": { + "durationMs": 838 + }, + "extensions/discord/src/monitor/native-command.model-picker.test.ts": { + "durationMs": 770 + }, + "extensions/slack/src/monitor/message-handler.app-mention-race.test.ts": { + "durationMs": 753 + }, + "src/browser/server-context.remote-profile-tab-ops.test.ts": { + "durationMs": 740 + }, + "extensions/whatsapp/src/inbound/send-api.test.ts": { + "durationMs": 732 + }, + "extensions/slack/src/monitor/replies.test.ts": { + "durationMs": 713 + }, + "extensions/telegram/src/send.proxy.test.ts": { + "durationMs": 709 + }, + "extensions/telegram/src/dm-access.test.ts": { + "durationMs": 704 + }, + "extensions/discord/src/monitor.tool-result.sends-status-replies-responseprefix.test.ts": { + "durationMs": 701 + }, + "extensions/telegram/src/bot-message-context.thread-binding.test.ts": { + "durationMs": 695 + }, + "src/browser/pw-tools-core.waits-next-download-saves-it.test.ts": { + "durationMs": 677 + }, + "extensions/whatsapp/src/inbound.media.test.ts": { + "durationMs": 627 + }, + "extensions/telegram/src/probe.test.ts": { + "durationMs": 616 + }, + "src/browser/pw-tools-core.clamps-timeoutms-scrollintoview.test.ts": { + "durationMs": 610 + }, + "extensions/telegram/src/bot-message-context.dm-topic-threadid.test.ts": { + "durationMs": 581 + }, + "extensions/slack/src/monitor.threading.missing-thread-ts.test.ts": { + "durationMs": 570 + }, + "extensions/telegram/src/bot-native-commands.plugin-auth.test.ts": { + "durationMs": 563 + }, + "extensions/discord/src/monitor/agent-components.wildcard.test.ts": { + "durationMs": 547 + }, + "extensions/discord/src/send.components.test.ts": { + "durationMs": 511 + }, + "extensions/slack/src/monitor/events/channels.test.ts": { + "durationMs": 504 + }, + "extensions/whatsapp/src/session.test.ts": { + "durationMs": 479 + }, + "extensions/whatsapp/src/auto-reply.web-auto-reply.last-route.test.ts": { + "durationMs": 434 + }, + "extensions/telegram/src/proxy.test.ts": { + "durationMs": 402 + }, + "extensions/discord/src/components.test.ts": { + "durationMs": 400 + }, + "extensions/telegram/src/audit.test.ts": { + "durationMs": 395 + }, + "src/browser/pw-tools-core.last-file-chooser-arm-wins.test.ts": { + "durationMs": 383 + }, + "extensions/telegram/src/polling-session.test.ts": { + "durationMs": 379 + }, + "extensions/signal/src/monitor.tool-result.pairs-uuid-only-senders-uuid-allowlist-entry.test.ts": { + "durationMs": 379 + }, + "extensions/telegram/src/bot-native-command-menu.test.ts": { + "durationMs": 359 + }, + "extensions/whatsapp/src/login.test.ts": { + "durationMs": 350 + }, + "extensions/whatsapp/src/channel.outbound.test.ts": { + "durationMs": 335 + }, + "src/browser/chrome.test.ts": { + "durationMs": 324 + }, + "extensions/whatsapp/src/outbound-adapter.poll.test.ts": { + "durationMs": 284 + }, + "src/browser/server.agent-contract-form-layout-act-commands.test.ts": { + "durationMs": 282 + }, + "extensions/discord/src/send.webhook-activity.test.ts": { + "durationMs": 246 + }, + "src/browser/routes/basic.existing-session.test.ts": { + "durationMs": 221 + }, + "extensions/imessage/src/monitor/deliver.test.ts": { + "durationMs": 220 + }, + "extensions/telegram/src/network-config.test.ts": { + "durationMs": 216 + }, + "extensions/discord/src/voice-message.test.ts": { + "durationMs": 207 + }, + "src/browser/server.post-tabs-open-profile-unknown-returns-404.test.ts": { + "durationMs": 159 + }, + "extensions/discord/src/monitor/thread-session-close.test.ts": { + "durationMs": 130 + }, + "extensions/signal/src/monitor.tool-result.sends-tool-summaries-responseprefix.test.ts": { + "durationMs": 129 + }, + "src/browser/pw-tools-core.interactions.set-input-files.test.ts": { + "durationMs": 127 + }, + "src/browser/server.evaluate-disabled-does-not-block-storage.test.ts": { + "durationMs": 122 + }, + "src/browser/pw-tools-core.interactions.evaluate.abort.test.ts": { + "durationMs": 122 + }, + "extensions/whatsapp/src/auto-reply.broadcast-groups.combined.test.ts": { + "durationMs": 120 + }, + "src/browser/cdp-proxy-bypass.test.ts": { + "durationMs": 119 + }, + "src/browser/server-context.tab-selection-state.test.ts": { + "durationMs": 115 + }, + "src/browser/screenshot.test.ts": { + "durationMs": 108 + }, + "extensions/telegram/src/bot-native-commands.skills-allowlist.test.ts": { + "durationMs": 103 + }, + "src/browser/client-fetch.loopback-auth.test.ts": { + "durationMs": 101 + }, + "src/browser/bridge-server.auth.test.ts": { + "durationMs": 94 + }, + "extensions/whatsapp/src/inbound/media.node.test.ts": { + "durationMs": 90 + }, + "src/browser/control-auth.auto-token.test.ts": { + "durationMs": 85 + }, + "extensions/telegram/src/send.test.ts": { + "durationMs": 82 + }, + "extensions/telegram/src/draft-stream.test.ts": { + "durationMs": 81 + }, + "extensions/slack/src/sent-thread-cache.test.ts": { + "durationMs": 78 + }, + "extensions/slack/src/monitor/media.test.ts": { + "durationMs": 74 + }, + "extensions/discord/src/monitor.test.ts": { + "durationMs": 71 + }, + "extensions/telegram/src/sticker-cache.test.ts": { + "durationMs": 71 + }, + "extensions/slack/src/monitor/message-handler/prepare.test.ts": { + "durationMs": 68 + }, + "src/browser/server-context.existing-session.test.ts": { + "durationMs": 67 + }, + "src/browser/server.auth-fail-closed.test.ts": { + "durationMs": 63 + }, + "extensions/telegram/src/target-writeback.test.ts": { + "durationMs": 62 + }, + "extensions/signal/src/send-reactions.test.ts": { + "durationMs": 62 + }, + "src/browser/pw-session.connections.test.ts": { + "durationMs": 55 + }, + "extensions/slack/src/monitor/monitor.test.ts": { + "durationMs": 42 + }, + "extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts": { + "durationMs": 42 + }, + "extensions/discord/src/monitor/monitor.test.ts": { + "durationMs": 40 + }, + "extensions/discord/src/monitor/provider.lifecycle.test.ts": { + "durationMs": 39 + }, + "src/browser/cdp.test.ts": { + "durationMs": 39 + }, + "extensions/telegram/src/bot/delivery.test.ts": { + "durationMs": 33 + }, + "src/browser/profiles-service.test.ts": { + "durationMs": 32 + }, + "extensions/telegram/src/format.wrap-md.test.ts": { + "durationMs": 28 + }, + "extensions/telegram/src/thread-bindings.test.ts": { + "durationMs": 25 + }, + "extensions/slack/src/monitor/message-handler/prepare.thread-session-key.test.ts": { + "durationMs": 24 + }, + "extensions/slack/src/send.blocks.test.ts": { + "durationMs": 21 + }, + "extensions/telegram/src/format.test.ts": { + "durationMs": 20 + }, + "src/browser/server.auth-token-gates-http.test.ts": { + "durationMs": 20 + }, + "extensions/telegram/src/bot-message-context.implicit-mention.test.ts": { + "durationMs": 20 + }, + "extensions/telegram/src/action-runtime.test.ts": { + "durationMs": 19 + }, + "extensions/telegram/src/bot-message-context.topic-agentid.test.ts": { + "durationMs": 19 + }, + "src/browser/routes/agent.existing-session.test.ts": { + "durationMs": 19 + }, + "extensions/signal/src/format.chunking.test.ts": { + "durationMs": 18 + }, + "extensions/whatsapp/src/resolve-outbound-target.test.ts": { + "durationMs": 18 + }, + "extensions/telegram/src/bot-native-commands.group-auth.test.ts": { + "durationMs": 17 + }, + "extensions/discord/src/monitor/native-command.commands-allowfrom.test.ts": { + "durationMs": 17 + }, + "extensions/discord/src/monitor/model-picker.test.ts": { + "durationMs": 16 + }, + "src/browser/navigation-guard.test.ts": { + "durationMs": 16 + }, + "extensions/slack/src/action-runtime.test.ts": { + "durationMs": 15 + }, + "extensions/telegram/src/lane-delivery.test.ts": { + "durationMs": 14 + }, + "src/browser/server-context.reset.test.ts": { + "durationMs": 14 + }, + "extensions/discord/src/monitor/model-picker-preferences.test.ts": { + "durationMs": 14 + }, + "extensions/discord/src/actions/runtime.test.ts": { + "durationMs": 14 + }, + "extensions/discord/src/monitor/provider.proxy.test.ts": { + "durationMs": 13 + }, + "extensions/telegram/src/bot-message.test.ts": { + "durationMs": 13 + }, + "extensions/telegram/src/bot.fetch-abort.test.ts": { + "durationMs": 13 + }, + "extensions/discord/src/monitor/monitor.agent-components.test.ts": { + "durationMs": 13 + }, + "extensions/signal/src/format.test.ts": { + "durationMs": 13 + }, + "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/slack/src/format.test.ts": { + "durationMs": 12 + }, + "extensions/slack/src/channel.test.ts": { + "durationMs": 12 + }, + "src/browser/pw-tools-core.interactions.batch.test.ts": { + "durationMs": 12 + }, + "extensions/discord/src/monitor/message-handler.preflight.acp-bindings.test.ts": { + "durationMs": 11 + }, + "extensions/telegram/src/channel.test.ts": { + "durationMs": 11 + }, + "extensions/imessage/src/send.test.ts": { + "durationMs": 11 + }, + "src/browser/pw-tools-core.snapshot.navigate-guard.test.ts": { + "durationMs": 11 + }, + "extensions/telegram/src/bot-message-context.sender-prefix.test.ts": { + "durationMs": 11 + }, + "extensions/whatsapp/src/active-listener.test.ts": { + "durationMs": 11 + }, + "src/browser/paths.test.ts": { + "durationMs": 11 + }, + "src/browser/server-context.hot-reload-profiles.test.ts": { + "durationMs": 11 + }, + "src/browser/proxy-files.test.ts": { + "durationMs": 11 + }, + "src/browser/client.test.ts": { + "durationMs": 10 + }, + "extensions/signal/src/format.links.test.ts": { + "durationMs": 10 + }, + "extensions/telegram/src/token.test.ts": { + "durationMs": 10 + }, + "extensions/discord/src/resolve-users.test.ts": { + "durationMs": 10 + }, + "extensions/imessage/src/channel.outbound.test.ts": { + "durationMs": 10 + }, + "extensions/discord/src/monitor/monitor.threading-utils.test.ts": { + "durationMs": 10 + }, + "extensions/imessage/src/monitor/inbound-processing.test.ts": { + "durationMs": 10 + }, + "extensions/discord/src/resolve-channels.test.ts": { + "durationMs": 9 + }, + "src/browser/pw-session.get-page-for-targetid.extension-fallback.test.ts": { + "durationMs": 9 + }, + "extensions/discord/src/api.test.ts": { + "durationMs": 9 + }, + "extensions/discord/src/monitor/inbound-job.test.ts": { + "durationMs": 9 + }, + "extensions/discord/src/monitor/message-handler.inbound-context.test.ts": { + "durationMs": 9 + }, + "src/browser/server-lifecycle.test.ts": { + "durationMs": 9 + }, + "extensions/telegram/src/accounts.test.ts": { + "durationMs": 9 + }, + "extensions/imessage/src/monitor.gating.test.ts": { + "durationMs": 9 + }, + "extensions/signal/src/format.visual.test.ts": { + "durationMs": 9 + }, + "src/browser/config.test.ts": { + "durationMs": 8 + }, + "extensions/telegram/src/network-errors.test.ts": { + "durationMs": 8 + }, + "src/browser/server-context.loopback-direct-ws.test.ts": { + "durationMs": 8 + }, + "extensions/telegram/src/update-offset-store.test.ts": { + "durationMs": 8 + }, + "extensions/slack/src/client.test.ts": { + "durationMs": 8 + }, + "extensions/discord/src/targets.test.ts": { + "durationMs": 8 + }, + "src/browser/routes/dispatcher.abort.test.ts": { + "durationMs": 8 + }, + "extensions/signal/src/client.test.ts": { + "durationMs": 8 + }, + "extensions/discord/src/send.permissions.authz.test.ts": { + "durationMs": 8 + }, + "extensions/whatsapp/src/login.coverage.test.ts": { + "durationMs": 8 + }, + "src/browser/server-context.ensure-browser-available.waits-for-cdp-ready.test.ts": { + "durationMs": 8 + }, + "extensions/slack/src/probe.test.ts": { + "durationMs": 8 + }, + "src/browser/chrome-mcp.test.ts": { + "durationMs": 7 + }, + "src/browser/browser-utils.test.ts": { + "durationMs": 7 + }, + "src/browser/profiles.test.ts": { + "durationMs": 7 + }, + "extensions/signal/src/monitor.tool-result.autostart.test.ts": { + "durationMs": 7 + }, + "extensions/discord/src/actions/runtime.presence.test.ts": { + "durationMs": 7 + }, + "extensions/discord/src/audit.test.ts": { + "durationMs": 7 + }, + "extensions/imessage/src/targets.test.ts": { + "durationMs": 7 + }, + "extensions/discord/src/monitor/threading.auto-thread.test.ts": { + "durationMs": 7 + }, + "extensions/telegram/src/model-buttons.test.ts": { + "durationMs": 7 + }, + "extensions/telegram/src/targets.test.ts": { + "durationMs": 7 + }, + "extensions/slack/src/actions.blocks.test.ts": { + "durationMs": 7 + }, + "extensions/whatsapp/src/logout.test.ts": { + "durationMs": 7 + }, + "extensions/discord/src/monitor/thread-bindings.shared-state.test.ts": { + "durationMs": 6 + }, + "extensions/whatsapp/src/action-runtime.test.ts": { + "durationMs": 6 + }, + "extensions/telegram/src/setup-surface.test.ts": { + "durationMs": 6 + }, + "extensions/imessage/src/monitor/self-chat-cache.test.ts": { + "durationMs": 6 + }, + "extensions/signal/src/monitor/access-policy.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/subagent-hooks.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/actions/runtime.moderation.authz.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/monitor.gateway.test.ts": { + "durationMs": 6 + }, + "src/browser/pw-session.create-page.navigation-guard.test.ts": { + "durationMs": 6 + }, + "extensions/whatsapp/src/login-qr.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/monitor/dm-command-decision.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/monitor/auto-presence.test.ts": { + "durationMs": 6 + }, + "extensions/telegram/src/bot/helpers.test.ts": { + "durationMs": 6 + }, + "extensions/telegram/src/exec-approvals-handler.test.ts": { + "durationMs": 6 + }, + "src/browser/pw-session.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/directory-live.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/gateway-logging.test.ts": { + "durationMs": 6 + }, + "extensions/discord/src/monitor/native-command-context.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/threading.parent-info.test.ts": { + "durationMs": 5 + }, + "extensions/whatsapp/src/normalize-target.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/group-policy.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/send.typing.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/startup-status.test.ts": { + "durationMs": 5 + }, + "extensions/telegram/src/status-reaction-variants.test.ts": { + "durationMs": 5 + }, + "extensions/telegram/src/reaction-level.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/token.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/http/registry.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/draft-stream.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/targets.test.ts": { + "durationMs": 5 + }, + "extensions/imessage/src/monitor/loop-rate-limiter.test.ts": { + "durationMs": 5 + }, + "extensions/signal/src/setup-allow-from.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/message-handler.bot-self-filter.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/provider.allowlist.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/provider.rest-proxy.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/route-resolution.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/chunk.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/draft-stream.test.ts": { + "durationMs": 5 + }, + "extensions/imessage/src/monitor/reflection-guard.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/channel-actions.test.ts": { + "durationMs": 5 + }, + "src/browser/pw-role-snapshot.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/monitor/provider.reconnect.test.ts": { + "durationMs": 5 + }, + "src/browser/chrome.default-browser.test.ts": { + "durationMs": 5 + }, + "extensions/slack/src/actions.read.test.ts": { + "durationMs": 5 + }, + "extensions/discord/src/monitor/dm-command-auth.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/monitor/gateway-error-guard.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/monitor/presence.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/threading-tool-context.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/stream-mode.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/account-inspect.test.ts": { + "durationMs": 4 + }, + "src/browser/session-tab-registry.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/group-migration.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/channel-migration.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/outbound-adapter.interactive-order.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/mentions.test.ts": { + "durationMs": 4 + }, + "src/browser/control-auth.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/status-issues.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/monitor/provider.auth-errors.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/status-issues.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/setup-core.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/monitor/allow-list.test.ts": { + "durationMs": 4 + }, + "extensions/signal/src/identity.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/voice.test.ts": { + "durationMs": 4 + }, + "extensions/imessage/src/monitor/monitor-provider.echo-cache.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/inline-buttons.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/modal-metadata.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/resolve-users.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/resolve-allowlist-common.test.ts": { + "durationMs": 4 + }, + "src/browser/routes/agent.snapshot.plan.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/monitor/thread-bindings.persona.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/shared-interactive.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/threading.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/monitor/events/message-subtype-handlers.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/button-types.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/api-fetch.test.ts": { + "durationMs": 4 + }, + "extensions/signal/src/probe.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/accounts.whatsapp-auth.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/setup-account-state.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/channel-actions.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/auto-reply/monitor/group-members.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/reconnect.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/channel.directory.test.ts": { + "durationMs": 4 + }, + "extensions/signal/src/channel.outbound.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/probe.intents.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/group-policy.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/monitor/inbound-context.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/monitor.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/exec-approvals.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/sequential-key.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/voice/command.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/outbound-adapter.sendpayload.test.ts": { + "durationMs": 4 + }, + "extensions/imessage/src/monitor/sanitize-outbound.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/status-issues.test.ts": { + "durationMs": 4 + }, + "extensions/discord/src/client.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/message-action-dispatch.test.ts": { + "durationMs": 4 + }, + "extensions/signal/src/channel.test.ts": { + "durationMs": 4 + }, + "extensions/imessage/src/monitor.shutdown.unhandled-rejection.test.ts": { + "durationMs": 4 + }, + "extensions/whatsapp/src/channel.test.ts": { + "durationMs": 4 + }, + "extensions/telegram/src/allowed-updates.test.ts": { + "durationMs": 4 + }, + "extensions/slack/src/monitor/message-handler.debounce-key.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/bot-message-dispatch.sticker-media.test.ts": { + "durationMs": 3 + }, + "src/browser/cdp-timeouts.test.ts": { + "durationMs": 3 + }, + "extensions/signal/src/monitor.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/draft-chunking.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/normalize.test.ts": { + "durationMs": 3 + }, + "src/browser/url-pattern.test.ts": { + "durationMs": 3 + }, + "extensions/imessage/src/group-policy.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/blocks-fallback.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/allow-from.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/message-actions.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/monitor/commands.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/shared-interactive.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/group-policy.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/resolve-allowlist-common.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/group-access.base-access.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/monitor/message-handler/dispatch.streaming.test.ts": { + "durationMs": 3 + }, + "extensions/imessage/src/probe.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/group-policy.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/normalize.test.ts": { + "durationMs": 3 + }, + "extensions/imessage/src/setup-allow-from.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/caption.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/approval-buttons.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/forum-service-message.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/bot-access.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/monitor/threading.starter.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/group-access.policy-access.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/account-inspect.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/accounts.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/monitor/provider.interop.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/monitor/context.test.ts": { + "durationMs": 3 + }, + "extensions/whatsapp/src/accounts.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/accounts.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/pluralkit.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/fetch.env-proxy-runtime.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/conversation-route.base-session-key.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/probe.parse-token.test.ts": { + "durationMs": 3 + }, + "src/browser/chrome-mcp.snapshot.test.ts": { + "durationMs": 3 + }, + "src/browser/routes/agent.storage.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/blocks-input.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/draft-chunking.test.ts": { + "durationMs": 3 + }, + "src/browser/chrome.launch-args.test.ts": { + "durationMs": 3 + }, + "src/browser/routes/agent.shared.test.ts": { + "durationMs": 3 + }, + "extensions/discord/src/session-key-normalization.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/resolve-channels.test.ts": { + "durationMs": 3 + }, + "extensions/slack/src/interactive-replies.test.ts": { + "durationMs": 3 + }, + "src/browser/pw-session.page-cdp.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/bot.helpers.test.ts": { + "durationMs": 3 + }, + "extensions/telegram/src/reasoning-lane-coordinator.test.ts": { + "durationMs": 3 + } + } +} diff --git a/test/scripts/test-runner-manifest.test.ts b/test/scripts/test-runner-manifest.test.ts index 0fac87c25e1..67e2b58e3f5 100644 --- a/test/scripts/test-runner-manifest.test.ts +++ b/test/scripts/test-runner-manifest.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { dedupeFilesPreserveOrder, packFilesByDuration, + packFilesByDurationWithBaseLoads, selectMemoryHeavyFiles, selectTimedHeavyFiles, selectUnitHeavyFileGroups, @@ -133,4 +134,21 @@ describe("packFilesByDuration", () => { ["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; + + 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"]]); + }); }); diff --git a/test/test-runner-manifest.test.ts b/test/test-runner-manifest.test.ts index 72c32e2cf77..ebad441811f 100644 --- a/test/test-runner-manifest.test.ts +++ b/test/test-runner-manifest.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "vitest"; -import { loadTestRunnerBehavior } from "../scripts/test-runner-manifest.mjs"; +import { + loadChannelTimingManifest, + loadTestRunnerBehavior, +} from "../scripts/test-runner-manifest.mjs"; describe("loadTestRunnerBehavior", () => { it("loads channel isolated entries from the behavior manifest", () => { @@ -16,4 +19,11 @@ describe("loadTestRunnerBehavior", () => { expect(behavior.channels.isolatedPrefixes).toContain("extensions/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); + }); });