@furystack/utils 8.2.0 → 8.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [8.2.1] - 2026-03-06
4
+
5
+ ### 🧪 Tests
6
+
7
+ - Refactored all `Semaphore` tests to use `using()` / `usingAsync()` wrappers for proper disposal, ensuring cleanup even if assertions fail
8
+ - Added `eslint-disable` comment in `EventHub` test for intentional post-disposal behavior verification
9
+
3
10
  ## [8.2.0] - 2026-03-03
4
11
 
5
12
  ### ✨ Features
@@ -66,6 +66,8 @@ describe('EventHub', () => {
66
66
  const hub = new EventHub();
67
67
  const listener = vi.fn((_val) => { });
68
68
  hub.addListener('test', listener);
69
+ // We want to test the dispose behavior, so we need to disable the rule
70
+ // eslint-disable-next-line furystack/prefer-using-wrapper
69
71
  hub[Symbol.dispose]();
70
72
  hub.emit('test', 'test');
71
73
  expect(listener).not.toBeCalled();
@@ -1 +1 @@
1
- {"version":3,"file":"event-hub.spec.js","sourceRoot":"","sources":["../src/event-hub.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-hub.spec.js","sourceRoot":"","sources":["../src/event-hub.spec.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"semaphore.spec.d.ts","sourceRoot":"","sources":["../src/semaphore.spec.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,cAAc,yCA6czB,CAAA"}
1
+ {"version":3,"file":"semaphore.spec.d.ts","sourceRoot":"","sources":["../src/semaphore.spec.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,yCA4czB,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import { sleepAsync } from './sleep-async.js';
3
3
  import { using } from './using.js';
4
+ import { usingAsync } from './using-async.js';
4
5
  import { Semaphore, SemaphoreDisposedError } from './semaphore.js';
5
6
  export const semaphoreTests = describe('Semaphore', () => {
6
7
  it('should be constructed with a given concurrency limit', () => {
@@ -14,284 +15,284 @@ export const semaphoreTests = describe('Semaphore', () => {
14
15
  });
15
16
  });
16
17
  it('should execute a single task and return its result', async () => {
17
- const s = new Semaphore(2);
18
- const result = await s.execute(async () => 42);
19
- expect(result).toBe(42);
20
- expect(s.completedCount.getValue()).toBe(1);
21
- expect(s.runningCount.getValue()).toBe(0);
22
- s[Symbol.dispose]();
18
+ await usingAsync(new Semaphore(2), async (s) => {
19
+ const result = await s.execute(async () => 42);
20
+ expect(result).toBe(42);
21
+ expect(s.completedCount.getValue()).toBe(1);
22
+ expect(s.runningCount.getValue()).toBe(0);
23
+ });
23
24
  });
24
25
  it('should execute up to N tasks concurrently and queue the rest', async () => {
25
- const s = new Semaphore(2);
26
- const running = [];
27
- const resolvers = [];
28
- const createTask = (name) => s.execute(async () => {
29
- running.push(name);
30
- await new Promise((resolve) => resolvers.push(resolve));
31
- return name;
26
+ await usingAsync(new Semaphore(2), async (s) => {
27
+ const running = [];
28
+ const resolvers = [];
29
+ const createTask = (name) => s.execute(async () => {
30
+ running.push(name);
31
+ await new Promise((resolve) => resolvers.push(resolve));
32
+ return name;
33
+ });
34
+ const p1 = createTask('a');
35
+ const p2 = createTask('b');
36
+ const p3 = createTask('c');
37
+ await sleepAsync(10);
38
+ expect(running).toEqual(['a', 'b']);
39
+ expect(s.runningCount.getValue()).toBe(2);
40
+ expect(s.pendingCount.getValue()).toBe(1);
41
+ resolvers[0]();
42
+ await p1;
43
+ await sleepAsync(10);
44
+ expect(running).toEqual(['a', 'b', 'c']);
45
+ expect(s.runningCount.getValue()).toBe(2);
46
+ expect(s.pendingCount.getValue()).toBe(0);
47
+ resolvers[1]();
48
+ resolvers[2]();
49
+ await Promise.all([p2, p3]);
50
+ expect(s.completedCount.getValue()).toBe(3);
51
+ expect(s.runningCount.getValue()).toBe(0);
32
52
  });
33
- const p1 = createTask('a');
34
- const p2 = createTask('b');
35
- const p3 = createTask('c');
36
- await sleepAsync(10);
37
- expect(running).toEqual(['a', 'b']);
38
- expect(s.runningCount.getValue()).toBe(2);
39
- expect(s.pendingCount.getValue()).toBe(1);
40
- resolvers[0]();
41
- await p1;
42
- await sleepAsync(10);
43
- expect(running).toEqual(['a', 'b', 'c']);
44
- expect(s.runningCount.getValue()).toBe(2);
45
- expect(s.pendingCount.getValue()).toBe(0);
46
- resolvers[1]();
47
- resolvers[2]();
48
- await Promise.all([p2, p3]);
49
- expect(s.completedCount.getValue()).toBe(3);
50
- expect(s.runningCount.getValue()).toBe(0);
51
- s[Symbol.dispose]();
52
53
  });
53
54
  it('should propagate task rejection to the caller and continue processing', async () => {
54
- const s = new Semaphore(1);
55
- const taskError = new Error('task failed');
56
- const p1 = s.execute(async () => {
57
- throw taskError;
55
+ await usingAsync(new Semaphore(1), async (s) => {
56
+ const taskError = new Error('task failed');
57
+ const p1 = s.execute(async () => {
58
+ throw taskError;
59
+ });
60
+ const p2 = s.execute(async () => 'ok');
61
+ await expect(p1).rejects.toThrow('task failed');
62
+ const result = await p2;
63
+ expect(result).toBe('ok');
64
+ expect(s.failedCount.getValue()).toBe(1);
65
+ expect(s.completedCount.getValue()).toBe(1);
58
66
  });
59
- const p2 = s.execute(async () => 'ok');
60
- await expect(p1).rejects.toThrow('task failed');
61
- const result = await p2;
62
- expect(result).toBe('ok');
63
- expect(s.failedCount.getValue()).toBe(1);
64
- expect(s.completedCount.getValue()).toBe(1);
65
- s[Symbol.dispose]();
66
67
  });
67
68
  describe('ObservableValue counters', () => {
68
69
  it('should update pendingCount and runningCount on transitions', async () => {
69
- const s = new Semaphore(1);
70
- const pendingChanges = [];
71
- const runningChanges = [];
72
- s.pendingCount.subscribe((v) => {
73
- pendingChanges.push(v);
74
- });
75
- s.runningCount.subscribe((v) => {
76
- runningChanges.push(v);
77
- });
78
- let resolve;
79
- const p1 = s.execute(async () => {
80
- await new Promise((r) => (resolve = r));
70
+ await usingAsync(new Semaphore(1), async (s) => {
71
+ const pendingChanges = [];
72
+ const runningChanges = [];
73
+ s.pendingCount.subscribe((v) => {
74
+ pendingChanges.push(v);
75
+ });
76
+ s.runningCount.subscribe((v) => {
77
+ runningChanges.push(v);
78
+ });
79
+ let resolve;
80
+ const p1 = s.execute(async () => {
81
+ await new Promise((r) => (resolve = r));
82
+ });
83
+ const p2 = s.execute(async () => 'done');
84
+ await sleepAsync(10);
85
+ expect(pendingChanges).toContain(1);
86
+ expect(runningChanges).toContain(1);
87
+ resolve();
88
+ await p1;
89
+ await sleepAsync(10);
90
+ await p2;
91
+ expect(s.pendingCount.getValue()).toBe(0);
92
+ expect(s.runningCount.getValue()).toBe(0);
93
+ expect(s.completedCount.getValue()).toBe(2);
81
94
  });
82
- const p2 = s.execute(async () => 'done');
83
- await sleepAsync(10);
84
- expect(pendingChanges).toContain(1);
85
- expect(runningChanges).toContain(1);
86
- resolve();
87
- await p1;
88
- await sleepAsync(10);
89
- await p2;
90
- expect(s.pendingCount.getValue()).toBe(0);
91
- expect(s.runningCount.getValue()).toBe(0);
92
- expect(s.completedCount.getValue()).toBe(2);
93
- s[Symbol.dispose]();
94
95
  });
95
96
  it('should update completedCount and failedCount correctly', async () => {
96
- const s = new Semaphore(2);
97
- await s.execute(async () => 'ok');
98
- await s
99
- .execute(async () => {
100
- throw new Error('fail');
101
- })
102
- .catch(() => { });
103
- expect(s.completedCount.getValue()).toBe(1);
104
- expect(s.failedCount.getValue()).toBe(1);
105
- s[Symbol.dispose]();
97
+ await usingAsync(new Semaphore(2), async (s) => {
98
+ await s.execute(async () => 'ok');
99
+ await s
100
+ .execute(async () => {
101
+ throw new Error('fail');
102
+ })
103
+ .catch(() => { });
104
+ expect(s.completedCount.getValue()).toBe(1);
105
+ expect(s.failedCount.getValue()).toBe(1);
106
+ });
106
107
  });
107
108
  });
108
109
  describe('AbortSignal support', () => {
109
110
  it('should abort a pending task when the caller signal aborts', async () => {
110
- const s = new Semaphore(1);
111
- let resolve;
112
- const p1 = s.execute(async () => {
113
- await new Promise((r) => (resolve = r));
111
+ await usingAsync(new Semaphore(1), async (s) => {
112
+ let resolve;
113
+ const p1 = s.execute(async () => {
114
+ await new Promise((r) => (resolve = r));
115
+ });
116
+ const controller = new AbortController();
117
+ const p2 = s.execute(async () => 'should not run', { signal: controller.signal });
118
+ await sleepAsync(10);
119
+ expect(s.pendingCount.getValue()).toBe(1);
120
+ controller.abort(new Error('cancelled'));
121
+ await expect(p2).rejects.toThrow('cancelled');
122
+ expect(s.pendingCount.getValue()).toBe(0);
123
+ resolve();
124
+ await p1;
114
125
  });
115
- const controller = new AbortController();
116
- const p2 = s.execute(async () => 'should not run', { signal: controller.signal });
117
- await sleepAsync(10);
118
- expect(s.pendingCount.getValue()).toBe(1);
119
- controller.abort(new Error('cancelled'));
120
- await expect(p2).rejects.toThrow('cancelled');
121
- expect(s.pendingCount.getValue()).toBe(0);
122
- resolve();
123
- await p1;
124
- s[Symbol.dispose]();
125
126
  });
126
127
  it('should reject immediately if the caller signal is already aborted', async () => {
127
- const s = new Semaphore(1);
128
- const controller = new AbortController();
129
- controller.abort(new Error('pre-aborted'));
130
- await expect(s.execute(async () => 'should not run', { signal: controller.signal })).rejects.toThrow('pre-aborted');
131
- expect(s.pendingCount.getValue()).toBe(0);
132
- expect(s.runningCount.getValue()).toBe(0);
133
- s[Symbol.dispose]();
128
+ await usingAsync(new Semaphore(1), async (s) => {
129
+ const controller = new AbortController();
130
+ controller.abort(new Error('pre-aborted'));
131
+ await expect(s.execute(async () => 'should not run', { signal: controller.signal })).rejects.toThrow('pre-aborted');
132
+ expect(s.pendingCount.getValue()).toBe(0);
133
+ expect(s.runningCount.getValue()).toBe(0);
134
+ });
134
135
  });
135
136
  it('should clean up the caller signal listener when the task completes normally', async () => {
136
- const s = new Semaphore(1);
137
- const controller = new AbortController();
138
- const removeSpy = vi.spyOn(controller.signal, 'removeEventListener');
139
- await s.execute(async () => 'done', { signal: controller.signal });
140
- expect(removeSpy).toHaveBeenCalledWith('abort', expect.any(Function));
141
- removeSpy.mockRestore();
142
- s[Symbol.dispose]();
137
+ await usingAsync(new Semaphore(1), async (s) => {
138
+ const controller = new AbortController();
139
+ const removeSpy = vi.spyOn(controller.signal, 'removeEventListener');
140
+ await s.execute(async () => 'done', { signal: controller.signal });
141
+ expect(removeSpy).toHaveBeenCalledWith('abort', expect.any(Function));
142
+ removeSpy.mockRestore();
143
+ });
143
144
  });
144
145
  it('should abort the signal passed to a running task when the caller signal aborts', async () => {
145
- const s = new Semaphore(1);
146
- const signalAborted = vi.fn();
147
- const controller = new AbortController();
148
- const p = s.execute(async ({ signal }) => {
149
- signal.addEventListener('abort', signalAborted);
150
- await new Promise((resolve) => {
151
- signal.addEventListener('abort', () => resolve());
152
- });
153
- throw signal.reason;
154
- }, { signal: controller.signal });
155
- await sleepAsync(10);
156
- expect(s.runningCount.getValue()).toBe(1);
157
- controller.abort(new Error('stop'));
158
- await expect(p).rejects.toThrow('stop');
159
- expect(signalAborted).toBeCalledTimes(1);
160
- s[Symbol.dispose]();
146
+ await usingAsync(new Semaphore(1), async (s) => {
147
+ const signalAborted = vi.fn();
148
+ const controller = new AbortController();
149
+ const p = s.execute(async ({ signal }) => {
150
+ signal.addEventListener('abort', signalAborted);
151
+ await new Promise((resolve) => {
152
+ signal.addEventListener('abort', () => resolve());
153
+ });
154
+ throw signal.reason;
155
+ }, { signal: controller.signal });
156
+ await sleepAsync(10);
157
+ expect(s.runningCount.getValue()).toBe(1);
158
+ controller.abort(new Error('stop'));
159
+ await expect(p).rejects.toThrow('stop');
160
+ expect(signalAborted).toBeCalledTimes(1);
161
+ });
161
162
  });
162
163
  });
163
164
  describe('EventHub events', () => {
164
165
  it('should emit taskStarted when a task begins running', async () => {
165
- const s = new Semaphore(1);
166
- const listener = vi.fn();
167
- s.subscribe('taskStarted', listener);
168
- await s.execute(async () => 'done');
169
- expect(listener).toBeCalledTimes(1);
170
- s[Symbol.dispose]();
166
+ await usingAsync(new Semaphore(1), async (s) => {
167
+ const listener = vi.fn();
168
+ s.subscribe('taskStarted', listener);
169
+ await s.execute(async () => 'done');
170
+ expect(listener).toBeCalledTimes(1);
171
+ });
171
172
  });
172
173
  it('should emit taskCompleted when a task resolves', async () => {
173
- const s = new Semaphore(1);
174
- const listener = vi.fn();
175
- s.subscribe('taskCompleted', listener);
176
- await s.execute(async () => 'done');
177
- expect(listener).toBeCalledTimes(1);
178
- s[Symbol.dispose]();
174
+ await usingAsync(new Semaphore(1), async (s) => {
175
+ const listener = vi.fn();
176
+ s.subscribe('taskCompleted', listener);
177
+ await s.execute(async () => 'done');
178
+ expect(listener).toBeCalledTimes(1);
179
+ });
179
180
  });
180
181
  it('should emit taskFailed with the error when a task rejects', async () => {
181
- const s = new Semaphore(1);
182
- const listener = vi.fn();
183
- s.subscribe('taskFailed', listener);
184
- const taskError = new Error('boom');
185
- await s
186
- .execute(async () => {
187
- throw taskError;
188
- })
189
- .catch(() => { });
190
- expect(listener).toBeCalledTimes(1);
191
- expect(listener).toBeCalledWith({ error: taskError });
192
- s[Symbol.dispose]();
182
+ await usingAsync(new Semaphore(1), async (s) => {
183
+ const listener = vi.fn();
184
+ s.subscribe('taskFailed', listener);
185
+ const taskError = new Error('boom');
186
+ await s
187
+ .execute(async () => {
188
+ throw taskError;
189
+ })
190
+ .catch(() => { });
191
+ expect(listener).toBeCalledTimes(1);
192
+ expect(listener).toBeCalledWith({ error: taskError });
193
+ });
193
194
  });
194
195
  it('should emit events in correct order for queued tasks', async () => {
195
- const s = new Semaphore(1);
196
- const events = [];
197
- s.subscribe('taskStarted', () => {
198
- events.push('started');
199
- });
200
- s.subscribe('taskCompleted', () => {
201
- events.push('completed');
196
+ await usingAsync(new Semaphore(1), async (s) => {
197
+ const events = [];
198
+ s.subscribe('taskStarted', () => {
199
+ events.push('started');
200
+ });
201
+ s.subscribe('taskCompleted', () => {
202
+ events.push('completed');
203
+ });
204
+ await Promise.all([s.execute(async () => 'a'), s.execute(async () => 'b')]);
205
+ await sleepAsync(10);
206
+ expect(events).toEqual(['started', 'completed', 'started', 'completed']);
202
207
  });
203
- await Promise.all([s.execute(async () => 'a'), s.execute(async () => 'b')]);
204
- await sleepAsync(10);
205
- expect(events).toEqual(['started', 'completed', 'started', 'completed']);
206
- s[Symbol.dispose]();
207
208
  });
208
209
  });
209
210
  describe('setMaxConcurrent', () => {
210
211
  it('should return the updated value from getMaxConcurrent', () => {
211
- const s = new Semaphore(2);
212
- s.setMaxConcurrent(5);
213
- expect(s.getMaxConcurrent()).toBe(5);
214
- s[Symbol.dispose]();
212
+ using(new Semaphore(2), (s) => {
213
+ s.setMaxConcurrent(5);
214
+ expect(s.getMaxConcurrent()).toBe(5);
215
+ });
215
216
  });
216
217
  it('should throw when given a non-positive integer', () => {
217
- const s = new Semaphore(2);
218
- expect(() => s.setMaxConcurrent(0)).toThrow('maxConcurrent must be a positive integer');
219
- expect(() => s.setMaxConcurrent(-1)).toThrow('maxConcurrent must be a positive integer');
220
- expect(() => s.setMaxConcurrent(1.5)).toThrow('maxConcurrent must be a positive integer');
221
- s[Symbol.dispose]();
218
+ using(new Semaphore(2), (s) => {
219
+ expect(() => s.setMaxConcurrent(0)).toThrow('maxConcurrent must be a positive integer');
220
+ expect(() => s.setMaxConcurrent(-1)).toThrow('maxConcurrent must be a positive integer');
221
+ expect(() => s.setMaxConcurrent(1.5)).toThrow('maxConcurrent must be a positive integer');
222
+ });
222
223
  });
223
224
  it('should immediately start queued tasks when increased', async () => {
224
- const s = new Semaphore(1);
225
- const running = [];
226
- const resolvers = [];
227
- const createTask = (name) => s.execute(async () => {
228
- running.push(name);
229
- await new Promise((resolve) => resolvers.push(resolve));
230
- return name;
225
+ await usingAsync(new Semaphore(1), async (s) => {
226
+ const running = [];
227
+ const resolvers = [];
228
+ const createTask = (name) => s.execute(async () => {
229
+ running.push(name);
230
+ await new Promise((resolve) => resolvers.push(resolve));
231
+ return name;
232
+ });
233
+ const p1 = createTask('a');
234
+ const p2 = createTask('b');
235
+ const p3 = createTask('c');
236
+ await sleepAsync(10);
237
+ expect(running).toEqual(['a']);
238
+ expect(s.runningCount.getValue()).toBe(1);
239
+ expect(s.pendingCount.getValue()).toBe(2);
240
+ s.setMaxConcurrent(3);
241
+ await sleepAsync(10);
242
+ expect(running).toEqual(['a', 'b', 'c']);
243
+ expect(s.runningCount.getValue()).toBe(3);
244
+ expect(s.pendingCount.getValue()).toBe(0);
245
+ resolvers.forEach((r) => r());
246
+ await Promise.all([p1, p2, p3]);
231
247
  });
232
- const p1 = createTask('a');
233
- const p2 = createTask('b');
234
- const p3 = createTask('c');
235
- await sleepAsync(10);
236
- expect(running).toEqual(['a']);
237
- expect(s.runningCount.getValue()).toBe(1);
238
- expect(s.pendingCount.getValue()).toBe(2);
239
- s.setMaxConcurrent(3);
240
- await sleepAsync(10);
241
- expect(running).toEqual(['a', 'b', 'c']);
242
- expect(s.runningCount.getValue()).toBe(3);
243
- expect(s.pendingCount.getValue()).toBe(0);
244
- resolvers.forEach((r) => r());
245
- await Promise.all([p1, p2, p3]);
246
- s[Symbol.dispose]();
247
248
  });
248
249
  it('should not abort running tasks when decreased', async () => {
249
- const s = new Semaphore(3);
250
- const resolvers = [];
251
- const createTask = () => s.execute(async () => {
252
- await new Promise((resolve) => resolvers.push(resolve));
250
+ await usingAsync(new Semaphore(3), async (s) => {
251
+ const resolvers = [];
252
+ const createTask = () => s.execute(async () => {
253
+ await new Promise((resolve) => resolvers.push(resolve));
254
+ });
255
+ const p1 = createTask();
256
+ const p2 = createTask();
257
+ const p3 = createTask();
258
+ await sleepAsync(10);
259
+ expect(s.runningCount.getValue()).toBe(3);
260
+ s.setMaxConcurrent(1);
261
+ expect(s.runningCount.getValue()).toBe(3);
262
+ resolvers.forEach((r) => r());
263
+ await Promise.all([p1, p2, p3]);
264
+ expect(s.completedCount.getValue()).toBe(3);
253
265
  });
254
- const p1 = createTask();
255
- const p2 = createTask();
256
- const p3 = createTask();
257
- await sleepAsync(10);
258
- expect(s.runningCount.getValue()).toBe(3);
259
- s.setMaxConcurrent(1);
260
- expect(s.runningCount.getValue()).toBe(3);
261
- resolvers.forEach((r) => r());
262
- await Promise.all([p1, p2, p3]);
263
- expect(s.completedCount.getValue()).toBe(3);
264
- s[Symbol.dispose]();
265
266
  });
266
267
  it('should not start new tasks until running count drops below new lower limit', async () => {
267
- const s = new Semaphore(2);
268
- const running = [];
269
- const resolvers = [];
270
- const createTask = (name) => s.execute(async () => {
271
- running.push(name);
272
- await new Promise((resolve) => resolvers.push(resolve));
273
- return name;
268
+ await usingAsync(new Semaphore(2), async (s) => {
269
+ const running = [];
270
+ const resolvers = [];
271
+ const createTask = (name) => s.execute(async () => {
272
+ running.push(name);
273
+ await new Promise((resolve) => resolvers.push(resolve));
274
+ return name;
275
+ });
276
+ const p1 = createTask('a');
277
+ const p2 = createTask('b');
278
+ const p3 = createTask('c');
279
+ await sleepAsync(10);
280
+ expect(running).toEqual(['a', 'b']);
281
+ expect(s.pendingCount.getValue()).toBe(1);
282
+ s.setMaxConcurrent(1);
283
+ resolvers[0]();
284
+ await p1;
285
+ await sleepAsync(10);
286
+ expect(running).toEqual(['a', 'b']);
287
+ expect(s.pendingCount.getValue()).toBe(1);
288
+ resolvers[1]();
289
+ await p2;
290
+ await sleepAsync(10);
291
+ expect(running).toEqual(['a', 'b', 'c']);
292
+ expect(s.pendingCount.getValue()).toBe(0);
293
+ resolvers[2]();
294
+ await p3;
274
295
  });
275
- const p1 = createTask('a');
276
- const p2 = createTask('b');
277
- const p3 = createTask('c');
278
- await sleepAsync(10);
279
- expect(running).toEqual(['a', 'b']);
280
- expect(s.pendingCount.getValue()).toBe(1);
281
- s.setMaxConcurrent(1);
282
- resolvers[0]();
283
- await p1;
284
- await sleepAsync(10);
285
- expect(running).toEqual(['a', 'b']);
286
- expect(s.pendingCount.getValue()).toBe(1);
287
- resolvers[1]();
288
- await p2;
289
- await sleepAsync(10);
290
- expect(running).toEqual(['a', 'b', 'c']);
291
- expect(s.pendingCount.getValue()).toBe(0);
292
- resolvers[2]();
293
- await p3;
294
- s[Symbol.dispose]();
295
296
  });
296
297
  });
297
298
  describe('Disposal', () => {
@@ -348,7 +349,7 @@ export const semaphoreTests = describe('Semaphore', () => {
348
349
  const listener = vi.fn();
349
350
  s.subscribe('taskStarted', listener);
350
351
  s[Symbol.dispose]();
351
- s.emit('taskStarted', undefined);
352
+ expect(() => s.emit('taskStarted', undefined)).not.toThrow();
352
353
  expect(listener).not.toBeCalled();
353
354
  });
354
355
  });