diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ff63d7db458..59fb1e58b33 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -127,13 +127,10 @@ jobs: sandbox: - 'sandbox:none' - 'sandbox:docker' - # The suite is ~16min of wall clock on one runner, dominated by a long - # tail of sdk-typescript files. vitest assigns files to shards by path - # hash, so those spread out instead of clustering in one shard. + # Keep three-way test concurrency inside one runner so each sandbox + # shares setup work instead of occupying three pool runners. shard: - - '1/3' - - '2/3' - - '3/3' + - '1/1' node-version: - '22.x' steps: @@ -362,10 +359,10 @@ jobs: # test:integration:sandbox:docker: that script would rebuild the image # the step above just built. if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then - npx cross-env QWEN_E2E_RENDERER=ink QWEN_SANDBOX=docker vitest run --root ./integration-tests --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --shard='${{ matrix.shard }}' 9>&- + npx cross-env QWEN_E2E_RENDERER=ink QWEN_SANDBOX=docker vitest run --root ./integration-tests --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --poolOptions.forks.maxForks=3 --shard='${{ matrix.shard }}' 9>&- else run_shard() { - QWEN_E2E_RENDERER=ink npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --shard='${{ matrix.shard }}' + QWEN_E2E_RENDERER=ink npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --poolOptions.forks.maxForks=3 --shard='${{ matrix.shard }}' } # One bounded retry: pool runners' sandbox:none shards die under # shared-host pressure with every test green and no vitest FAIL diff --git a/scripts/tests/e2e-workflow.test.js b/scripts/tests/e2e-workflow.test.js index 12d1131d561..d6a6fe19964 100644 --- a/scripts/tests/e2e-workflow.test.js +++ b/scripts/tests/e2e-workflow.test.js @@ -34,6 +34,18 @@ describe('e2e workflow', () => { expect(group).toContain('github.head_ref || github.ref_name'); }); + it('runs three Vitest forks on one Linux runner per sandbox', () => { + const linuxJob = yml.jobs['e2e-test-linux']; + const runStep = linuxJob.steps.find( + (step) => step.name === 'Run E2E tests', + ); + + expect(linuxJob.strategy.matrix.shard).toEqual(['1/1']); + expect(runStep.run.match(/--poolOptions\.forks\.maxForks=3/g)).toHaveLength( + 2, + ); + }); + describe('sandbox image preparation', () => { const steps = yml.jobs['e2e-test-linux'].steps; const setupStep = steps.find((step) => step.name === 'Set up Docker'); @@ -193,7 +205,7 @@ describe('e2e workflow', () => { // shard and exclude coverage lives only in this argument list. The // excludes are shared verbatim with the docker leg above. expect(runStep.run).toContain( - "npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --shard='${{ matrix.shard }}'", + "npm run test:integration:sandbox:none -- --exclude '**/interactive/cron-interactive.test.ts' --exclude '**/channel-plugin.test.ts' --exclude '**/chat-transcript-document.test.ts' --poolOptions.forks.maxForks=3 --shard='${{ matrix.shard }}'", ); });