v1.1.0: pooled runtime, 959 tests, production hardening (0 squash)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import { Semaphore } from './semaphore.js'
|
||||
|
||||
describe('Semaphore', () => {
|
||||
it('allows up to maxConcurrency parallel executions', async () => {
|
||||
const semaphore = new Semaphore(2)
|
||||
let concurrent = 0
|
||||
let maxConcurrent = 0
|
||||
|
||||
const tasks = Array.from({ length: 4 }, () =>
|
||||
semaphore.run(async () => {
|
||||
concurrent++
|
||||
if (concurrent > maxConcurrent) {
|
||||
maxConcurrent = concurrent
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
concurrent--
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(tasks)
|
||||
assert.strictEqual(maxConcurrent, 2)
|
||||
})
|
||||
|
||||
it('queues when at maxConcurrency', async () => {
|
||||
const semaphore = new Semaphore(1)
|
||||
const order: number[] = []
|
||||
|
||||
const tasks = Array.from({ length: 3 }, (_, i) =>
|
||||
semaphore.run(async () => {
|
||||
order.push(i)
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(tasks)
|
||||
assert.deepStrictEqual(order, [0, 1, 2])
|
||||
})
|
||||
|
||||
it('executes queued tasks FIFO', async () => {
|
||||
const semaphore = new Semaphore(1)
|
||||
const completionOrder: number[] = []
|
||||
|
||||
const tasks = Array.from({ length: 5 }, (_, i) =>
|
||||
semaphore.run(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 5))
|
||||
completionOrder.push(i)
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(tasks)
|
||||
assert.deepStrictEqual(completionOrder, [0, 1, 2, 3, 4])
|
||||
})
|
||||
|
||||
it('propagates errors without leaking slots', async () => {
|
||||
const semaphore = new Semaphore(1)
|
||||
let subsequentRan = false
|
||||
|
||||
const errorTask = semaphore.run(async () => {
|
||||
throw new Error('intentional error')
|
||||
})
|
||||
|
||||
const normalTask = semaphore.run(async () => {
|
||||
subsequentRan = true
|
||||
})
|
||||
|
||||
await assert.rejects(errorTask, /intentional error/)
|
||||
await normalTask
|
||||
assert.strictEqual(subsequentRan, true)
|
||||
})
|
||||
|
||||
it('handles many tasks', async () => {
|
||||
const semaphore = new Semaphore(4)
|
||||
const results: number[] = []
|
||||
|
||||
const tasks = Array.from({ length: 100 }, (_, i) =>
|
||||
semaphore.run(async () => {
|
||||
results.push(i)
|
||||
await new Promise(resolve => setTimeout(resolve, 1))
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(tasks)
|
||||
assert.strictEqual(results.length, 100)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user