/* eslint-env jest */ import { runQueue } from '../run-queue'; /** A promise and the hand that settles it. */ const deferred = (): { promise: Promise; resolve: (value: string) => void; reject: (reason: unknown) => void; } => { let resolve: (value: string) => void = () => undefined; let reject: (reason: unknown) => void = () => undefined; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; const flush = (): Promise => new Promise(resolve => setTimeout(resolve)); describe('working through a queue', () => { it('runs two at a time, in order, and reports each', async () => { const pending = new Map>(); const started: number[] = []; const done: number[] = []; const run = runQueue([1, 2, 3, 4], { concurrency: 2, run: item => { const call = deferred(); pending.set(item, call); return call.promise; }, onStart: item => started.push(item), onDone: item => done.push(item), }); await flush(); expect(started).toEqual([1, 2]); pending.get(2)?.resolve('b'); await flush(); expect(started).toEqual([1, 2, 3]); pending.get(1)?.resolve('a'); pending.get(3)?.resolve('c'); await flush(); pending.get(4)?.resolve('d'); const outcome = await run; expect(done).toEqual([2, 1, 3, 4]); expect(outcome).toEqual({ done: [2, 1, 3, 4], failed: [], skipped: [], stopped: false, }); }); it('starts nothing more after an error that ends the run', async () => { const errors: number[] = []; const outcome = await runQueue([1, 2, 3, 4, 5], { concurrency: 1, run: async item => { if (item === 2) { throw Object.assign(new Error('Not enough credits'), { data: { status: 402 }, }); } if (item === 1) { throw new Error('Nothing usable'); } return item; }, onError: item => errors.push(item), stopsOn: error => (error as { data?: { status?: number } }).data?.status === 402, }); expect(errors).toEqual([1, 2]); expect(outcome.failed).toEqual([1, 2]); expect(outcome.skipped).toEqual([3, 4, 5]); expect(outcome.stopped).toBe(true); }); it('stops starting once cancelled', async () => { let cancelled = false; const outcome = await runQueue([1, 2, 3], { concurrency: 1, run: async item => { cancelled = true; return item; }, cancelled: () => cancelled, }); expect(outcome.done).toEqual([1]); expect(outcome.skipped).toEqual([2, 3]); expect(outcome.stopped).toBe(false); }); it('copes with nothing to do', async () => { const run = jest.fn(); expect(await runQueue([], { concurrency: 2, run })).toEqual({ done: [], failed: [], skipped: [], stopped: false, }); expect(run).not.toHaveBeenCalled(); }); });