import { setTimeout } from 'node:timers/promises'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { workers } from 'moroutine'; import { slowTask, waitForAbort, slowStream } from './fixtures/async-dispose.ts'; describe('async dispose', () => { it('waits for in-flight tasks to complete', async () => { const run = workers(1); const promise = run(slowTask(100)); await run[Symbol.asyncDispose](); const result = await promise; assert.equal(result, 'done'); }); it('fires signal on async dispose', async () => { const run = workers(1); const promise = run(waitForAbort(run.signal)); await run[Symbol.asyncDispose](); const result = await promise; assert.equal(result, 'aborted'); }); it('fires signal on sync dispose', async () => { const run = workers(1); assert.equal(run.signal.aborted, false); run[Symbol.dispose](); assert.equal(run.signal.aborted, true); }); it('rejects new tasks after async dispose starts', async () => { const run = workers(1); await run[Symbol.asyncDispose](); await assert.rejects(() => run(slowTask(10)), { message: /disposed/ }); }); it('force-terminates after shutdownTimeout', async () => { const run = workers(1, { shutdownTimeout: 50 }); run(slowTask(10_000)); // will not finish in time const start = performance.now(); await run[Symbol.asyncDispose](); const elapsed = performance.now() - start; assert.ok(elapsed < 500, `Expected fast teardown, took ${elapsed}ms`); }); it('waits for streaming task to finish', async () => { const run = workers(1); const results: number[] = []; const iterating = (async () => { for await (const n of run(slowStream(5))) { results.push(n); } })(); // Let a couple items flow before disposing await setTimeout(60); await run[Symbol.asyncDispose](); await iterating; assert.equal(results.length, 5); }); });