58 lines
1.4 KiB
TypeScript
58 lines
1.4 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { Semaphore } from '../src/utils/semaphore.js'
|
|
|
|
describe('Semaphore', () => {
|
|
it('throws on max < 1', () => {
|
|
expect(() => new Semaphore(0)).toThrow()
|
|
})
|
|
|
|
it('allows up to max concurrent holders', async () => {
|
|
const sem = new Semaphore(2)
|
|
let running = 0
|
|
let peak = 0
|
|
|
|
const work = async () => {
|
|
await sem.acquire()
|
|
running++
|
|
peak = Math.max(peak, running)
|
|
await new Promise((r) => setTimeout(r, 30))
|
|
running--
|
|
sem.release()
|
|
}
|
|
|
|
await Promise.all([work(), work(), work(), work()])
|
|
expect(peak).toBeLessThanOrEqual(2)
|
|
})
|
|
|
|
it('run() auto-releases on success', async () => {
|
|
const sem = new Semaphore(1)
|
|
const result = await sem.run(async () => 42)
|
|
expect(result).toBe(42)
|
|
expect(sem.active).toBe(0)
|
|
})
|
|
|
|
it('run() auto-releases on error', async () => {
|
|
const sem = new Semaphore(1)
|
|
await expect(sem.run(async () => { throw new Error('oops') })).rejects.toThrow('oops')
|
|
expect(sem.active).toBe(0)
|
|
})
|
|
|
|
it('tracks active and pending counts', async () => {
|
|
const sem = new Semaphore(1)
|
|
await sem.acquire()
|
|
expect(sem.active).toBe(1)
|
|
|
|
// This will queue
|
|
const p = sem.acquire()
|
|
expect(sem.pending).toBe(1)
|
|
|
|
sem.release()
|
|
await p
|
|
expect(sem.active).toBe(1)
|
|
expect(sem.pending).toBe(0)
|
|
|
|
sem.release()
|
|
expect(sem.active).toBe(0)
|
|
})
|
|
})
|