diff --git a/src/test/llm.test.js b/src/test/llm.test.js index efcf0bf..cba53ed 100644 --- a/src/test/llm.test.js +++ b/src/test/llm.test.js @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import { mkdtemp, writeFile, chmod, rm, readFile } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; -import { extractBalancedJSON, extractJSONText, extractMeaningfulError } from '../llm.js'; +import { extractBalancedJSON, extractJSONText, extractMeaningfulError, mapWithConcurrency } from '../llm.js'; const ENV_KEYS = [ 'AI_ASSISTANT_CLI', 'MODEL', 'OPENCODE_MODEL', 'PATH', 'AI_ASSISTANT_TIMEOUT_MS', 'AI_ASSISTANT_MAX_BUFFER', @@ -273,3 +273,42 @@ describe('extractMeaningfulError', () => { assert.equal(extractMeaningfulError(null), ''); }); }); + +describe('mapWithConcurrency', () => { + it('回傳與輸入同索引對應的結果(保序)', async () => { + const out = await mapWithConcurrency([1, 2, 3, 4], 2, async (n) => n * 10); + assert.deepEqual(out, [10, 20, 30, 40]); + }); + + it('遵守併發上限(同時執行數不超過 limit)', async () => { + let active = 0, peak = 0; + const wait = () => new Promise(r => setTimeout(r, 5)); + await mapWithConcurrency([1, 2, 3, 4, 5, 6], 2, async () => { + active += 1; peak = Math.max(peak, active); + await wait(); + active -= 1; + }); + assert.ok(peak <= 2, `peak=${peak} 應 <= 2`); + }); + + it('limit 大於項目數時仍全部執行', async () => { + const out = await mapWithConcurrency(['a', 'b'], 10, async (s) => s.toUpperCase()); + assert.deepEqual(out, ['A', 'B']); + }); + + it('limit<=0 表示不限制(全部同時並行)', async () => { + let active = 0, peak = 0; + const wait = () => new Promise(r => setTimeout(r, 5)); + await mapWithConcurrency([1, 2, 3, 4, 5], 0, async () => { + active += 1; peak = Math.max(peak, active); + await wait(); + active -= 1; + }); + assert.equal(peak, 5, `peak=${peak} 應等於項目數(不限制)`); + }); + + it('空輸入回傳空陣列', async () => { + assert.deepEqual(await mapWithConcurrency([], 3, async () => 1), []); + assert.deepEqual(await mapWithConcurrency(null, 3, async () => 1), []); + }); +});