import assert from 'node:assert/strict'; import test from 'node:test'; import { filterGeneratedWorktreeStatus, parseRunnerArgs, runSuitesWithConcurrency, runWithConcurrency, } from './runner.mjs'; test('runner accepts a bounded suite concurrency and rejects unsafe values', () => { assert.equal( parseRunnerArgs(['node', 'runner', '--suite-concurrency', '3']).suiteConcurrency, 3, ); assert.throws( () => parseRunnerArgs(['node', 'runner', '--suite-concurrency', '0']), /between 1 and 16/, ); assert.throws( () => parseRunnerArgs(['node', 'runner', '--suite-concurrency', '17']), /between 1 and 16/, ); }); test('runWithConcurrency preserves result order and never exceeds its worker budget', async () => { let active = 0; let peak = 0; const results = await runWithConcurrency([40, 5, 20, 1], 2, async (delay, index) => { active += 1; peak = Math.max(peak, active); await new Promise((resolve) => setTimeout(resolve, delay)); active -= 1; return `result-${index}`; }); assert.deepEqual(results, ['result-0', 'result-1', 'result-2', 'result-3']); assert.equal(peak, 2); }); test('exclusive suites finish before parallel suites start', async () => { const events = []; const results = await runSuitesWithConcurrency( [ { id: 'mutating', exclusive: true }, { id: 'parallel-a' }, { id: 'parallel-b' }, ], 2, async (suite) => { events.push(`start:${suite.id}`); await new Promise((resolve) => setTimeout(resolve, 5)); events.push(`end:${suite.id}`); return suite.id; }, ); assert.deepEqual(results, ['mutating', 'parallel-a', 'parallel-b']); assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-a')); assert.ok(events.indexOf('end:mutating') < events.indexOf('start:parallel-b')); }); test('worktree cleanliness ignores release-gate and runtime build output', () => { const status = [ '?? .release-gate/ea07f6f/report.json', ' M .runtime/portal/server.mjs', ' D .runtime/portal/public/plaza-covers/cover.jpg', ' M release-gate/runner.mjs', ].join('\n'); assert.equal( filterGeneratedWorktreeStatus(status), ' M release-gate/runner.mjs', ); });