import { tap, expect } from '@push.rocks/tapbundle'; import * as lik from '../ts/index.js'; let testAsyncExecutionStack: lik.AsyncExecutionStack; tap.test('should create a valid instance of AsyncExectionStack', async () => { testAsyncExecutionStack = new lik.AsyncExecutionStack(); expect(testAsyncExecutionStack).toBeInstanceOf(lik.AsyncExecutionStack); }); tap.test('should run in parallel', async (toolsArg) => { await testAsyncExecutionStack.getExclusiveExecutionSlot(async () => { await toolsArg.delayFor(2000); console.log('should run first'); }, 2500); testAsyncExecutionStack.getNonExclusiveExecutionSlot(async () => { await toolsArg.delayFor(2000); console.log('should run third'); }, 2500); testAsyncExecutionStack.getNonExclusiveExecutionSlot(async () => { await toolsArg.delayFor(1000); console.log('should run second'); }, 2500); await testAsyncExecutionStack.getExclusiveExecutionSlot(async () => { console.log('should run fourth'); }, 0); }); // Test default non-exclusive unlimited concurrency (no cap means all run together) tap.test('default non-exclusive unlimited concurrency', async (tools) => { const stack = new lik.AsyncExecutionStack(); // default maxConcurrency should be unlimited (Infinity) expect(Number.isFinite(stack.getNonExclusiveMaxConcurrency())).toBe(false); const activeCounts: number[] = []; const tasks: Promise[] = []; for (let i = 0; i < 4; i++) { tasks.push( stack.getNonExclusiveExecutionSlot(async () => { activeCounts.push(stack.getActiveNonExclusiveCount()); await tools.delayFor(20); }) ); } await Promise.all(tasks); const maxActive = Math.max(...activeCounts); expect(maxActive).toBe(4); }); // Test respecting a non-exclusive concurrency limit tap.test('non-exclusive respects maxConcurrency', async (tools) => { const stack = new lik.AsyncExecutionStack(); stack.setNonExclusiveMaxConcurrency(2); const activeCounts: number[] = []; const tasks: Promise[] = []; for (let i = 0; i < 5; i++) { tasks.push( stack.getNonExclusiveExecutionSlot(async () => { activeCounts.push(stack.getActiveNonExclusiveCount()); await tools.delayFor(50); }) ); } await Promise.all(tasks); // never more than 2 at once const maxActive = Math.max(...activeCounts); expect(maxActive).toBeLessThanOrEqual(2); }); // Test concurrency stats (active vs pending) for non-exclusive tasks tap.test('non-exclusive concurrency stats reflect active and pending', async (tools) => { const stack = new lik.AsyncExecutionStack(); stack.setNonExclusiveMaxConcurrency(2); // initially, no tasks expect(stack.getActiveNonExclusiveCount()).toBe(0); expect(stack.getPendingNonExclusiveCount()).toBe(0); // enqueue three tasks const p1 = stack.getNonExclusiveExecutionSlot(async () => { await tools.delayFor(30); }); const p2 = stack.getNonExclusiveExecutionSlot(async () => { await tools.delayFor(30); }); const p3 = stack.getNonExclusiveExecutionSlot(async () => { await tools.delayFor(30); }); // give time for scheduling await tools.delayFor(10); // two should be running, one pending expect(stack.getActiveNonExclusiveCount()).toBe(2); expect(stack.getPendingNonExclusiveCount()).toBe(1); await Promise.all([p1, p2, p3]); // after completion, counts reset expect(stack.getActiveNonExclusiveCount()).toBe(0); expect(stack.getPendingNonExclusiveCount()).toBe(0); }); export default tap.start();