Skip to content

Commit 18a6287

Browse files
committed
implements tests
1 parent d746e86 commit 18a6287

File tree

3 files changed

+374
-46
lines changed

3 files changed

+374
-46
lines changed

src/Queue.spec.ts

Lines changed: 336 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,357 @@
11
import { describe, expect, test } from '@jest/globals'
22
import { Queue } from './Queue'
3+
import { QueueConfig } from './QueueConfig'
4+
import { WorkerFn } from './WorkerFn'
35

46
describe('Queue', () => {
5-
const nToS = (n: number) => n.toString()
6-
const nToS2 = (n: number) => (n + 1).toString()
7-
const queues = [new Queue({ worker: nToS, workPolicy: 'async-cycle-one' })]
8-
afterAll(() => {
9-
queues.map((queue) => queue.clear())
7+
let queue: Queue<any, any>
8+
9+
const shouldWork = async (cfg: QueueConfig<any, any>) => {
10+
const worker: WorkerFn<number, string> = async (item) =>
11+
`Processed: ${item}`
12+
queue = new Queue<number, string>({ worker, ...cfg })
13+
14+
await queue.push(1)
15+
const result = await queue.get()
16+
expect(result).toBe('Processed: 1')
17+
18+
queue.clear()
19+
}
20+
21+
test('should push and get items with default worker', async () => {
22+
const worker: WorkerFn<number, string> = async (item) => {
23+
return `Processed: ${item}`
24+
}
25+
queue = new Queue<number, string>({ worker })
26+
await queue.push(1)
27+
const result = await queue.get()
28+
expect(result).toBe('Processed: 1')
29+
})
30+
31+
test('should push and get items with custom worker during push', async () => {
32+
const defaultWorker: WorkerFn<number, string> = async (item) => {
33+
return `Default: ${item}`
34+
}
35+
const customWorker: WorkerFn<number, string> = async (item) => {
36+
return `Custom: ${item}`
37+
}
38+
queue = new Queue<number, string>({ worker: defaultWorker })
39+
await queue.push(1, customWorker)
40+
const result = await queue.get()
41+
expect(result).toBe('Custom: 1')
42+
})
43+
44+
test('should block push when queue size limit reached', async () => {
45+
// TODO
46+
})
47+
48+
test('should block push when buffer size limit reached', async () => {
49+
// TODO
50+
})
51+
52+
test('should push successfully after getting an item when limit reached', async () => {
53+
// TODO
54+
})
55+
56+
test('should work with after-add policy', async () => {
57+
await shouldWork({ workPolicy: 'after-add' })
58+
})
59+
60+
test('should work with async-cycle-one policy with default interval', async () => {
61+
await shouldWork({ workPolicy: 'async-cycle-one' })
62+
})
63+
64+
test('should work with async-cycle-many policy with default interval', async () => {
65+
await shouldWork({ workPolicy: 'async-cycle-many', groupSize: 2 })
66+
})
67+
68+
test('should work with async-cycle-one policy with custom interval', async () => {
69+
await shouldWork({ workPolicy: 'async-cycle-one', interval: 100 })
70+
await shouldWork({ workPolicy: 'async-cycle-one', interval: 200 })
71+
})
72+
73+
test('should work with async-cycle-many policy with custom interval', async () => {
74+
await shouldWork({
75+
workPolicy: 'async-cycle-many',
76+
groupSize: 2,
77+
interval: 100,
78+
})
79+
await shouldWork({
80+
workPolicy: 'async-cycle-many',
81+
groupSize: 2,
82+
interval: 200,
83+
})
1084
})
1185

12-
const isClear = (queue: Queue<any, any>) =>
13-
queue.length === 0 && queue.working === 0 && queue.buff === 0
86+
test('should work with async-cycle-many policy with different group sizes', async () => {
87+
await shouldWork({ workPolicy: 'async-cycle-many', groupSize: 5 })
88+
await shouldWork({ workPolicy: 'async-cycle-many', groupSize: 10 })
89+
})
90+
91+
test('should clear intervals and remove after-push behavior on clear method call', async () => {
92+
const worker: WorkerFn<number, string> = async (item) =>
93+
`Processed: ${item}`
94+
queue = new Queue<number, string>({
95+
worker,
96+
workPolicy: 'async-cycle-many',
97+
groupSize: 2,
98+
})
99+
100+
await queue.push(1)
101+
queue.clear()
102+
103+
expect(queue.clearead).toBe(true)
104+
})
14105

15-
test('one push, gets valid', async () => {
16-
await queues[0].push(1)
17-
expect(await queues[0].get()).toBe('1')
18-
expect(isClear(queues[0])).toBe(true)
106+
test.skip('should reflect correct state in length, size, buff, and working properties', async () => {
107+
// todo
19108
})
20109

21-
test('multiplie push, gets valid', async () => {
22-
await queues[0].push(1)
23-
await queues[0].push(2)
24-
await queues[0].push(3)
25-
expect(await queues[0].get()).toBe('1')
26-
expect(await queues[0].get()).toBe('2')
27-
expect(await queues[0].get()).toBe('3')
110+
test('should throw error when no worker is provided during initialization or push', async () => {
111+
queue = new Queue<number, string>()
28112

29-
expect(isClear(queues[0])).toBe(true)
113+
await expect(queue.push(1)).rejects.toThrow('Worker is not provided')
114+
await expect(queue.push(2, undefined)).rejects.toThrow(
115+
'Worker is not provided'
116+
)
30117
})
31118

32-
describe('custom workers', () => {
33-
test('default worker', async () => {
34-
const q = new Queue({ worker: nToS })
35-
await q.push(1)
36-
expect(await q.get()).toBe('1')
119+
test('should handle array responses from the worker function', async () => {
120+
const worker: WorkerFn<number, string[]> = async (item) => [
121+
`Processed: ${item}`,
122+
`Extra: ${item * 2}`,
123+
]
124+
queue = new Queue<number, string[]>({ worker, workPolicy: 'after-add' })
125+
126+
await queue.push(1)
127+
128+
const result1 = await queue.get()
129+
const result2 = await queue.get()
130+
131+
expect(result1).toBe('Processed: 1')
132+
expect(result2).toBe('Extra: 2')
133+
})
134+
135+
test('should handle empty or undefined tasks', async () => {
136+
const worker: WorkerFn<number | undefined, string> = async (item) =>
137+
item ? `Processed: ${item}` : 'Undefined'
138+
queue = new Queue<number | undefined, string>({
139+
worker,
140+
workPolicy: 'after-add',
37141
})
38142

39-
test('custom worker', async () => {
40-
const q = new Queue<number, string>()
41-
await q.push(1, nToS)
42-
expect(await q.get()).toBe('1')
143+
await queue.push(undefined)
144+
145+
const result = await queue.get()
146+
expect(result).toBe('Undefined')
147+
})
148+
149+
test('should handle concurrency and race conditions for simultaneous push and processing', async () => {
150+
const worker: WorkerFn<number, string> = async (item) => {
151+
await new Promise((resolve) => setTimeout(resolve, 10))
152+
return `Processed: ${item}`
153+
}
154+
queue = new Queue<number, string>({
155+
worker,
156+
workPolicy: 'async-cycle-one',
157+
interval: 5,
43158
})
44159

45-
test('custom worker in priority', async () => {
46-
const q = new Queue({ worker: nToS })
47-
await q.push(1, nToS2)
48-
expect(await q.get()).toBe('2')
160+
await Promise.all([queue.push(1), queue.push(2), queue.push(3)])
161+
162+
const results = await Promise.all([
163+
queue.get(),
164+
queue.get(),
165+
queue.get(),
166+
])
167+
168+
expect(results.sort()).toEqual([
169+
'Processed: 1',
170+
'Processed: 2',
171+
'Processed: 3',
172+
])
173+
})
174+
175+
test('should handle multiple pushes before processing starts', async () => {
176+
const worker: WorkerFn<number, string> = async (item) =>
177+
`Processed: ${item}`
178+
queue = new Queue<number, string>({
179+
worker,
180+
workPolicy: 'async-cycle-one',
181+
interval: 100,
49182
})
50183

51-
test('throwing error when worker not provided', async () => {
52-
const q = new Queue<number, string>({ workPolicy: 'after-add' })
53-
expect(async () => await q.push(1, undefined)).rejects.toThrow(
54-
'Worker is not provided'
55-
)
184+
await queue.push(1)
185+
await queue.push(2)
186+
187+
const results = await Promise.all([queue.get(), queue.get()])
188+
189+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
190+
})
191+
192+
test('should handle simultaneous push and get operation', async () => {
193+
const worker: WorkerFn<number, string> = async (item) =>
194+
`Processed: ${item}`
195+
queue = new Queue<number, string>({
196+
worker,
197+
workPolicy: 'async-cycle-one',
198+
interval: 5,
56199
})
200+
201+
const pushPromise = queue.push(1)
202+
const getResult = queue.get()
203+
204+
await pushPromise
205+
const result = await getResult
206+
207+
expect(result).toBe('Processed: 1')
208+
})
209+
210+
test('should process tasks concurrently when using async-cycle-one policy', async () => {
211+
const worker: WorkerFn<number, string> = async (item) => {
212+
await new Promise((resolve) => setTimeout(resolve, 10))
213+
return `Processed: ${item}`
214+
}
215+
queue = new Queue<number, string>({
216+
worker,
217+
workPolicy: 'async-cycle-one',
218+
interval: 5,
219+
})
220+
221+
await queue.push(1)
222+
await queue.push(2)
223+
224+
const startTime = Date.now()
225+
const results = await Promise.all([queue.get(), queue.get()])
226+
const endTime = Date.now()
227+
228+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
229+
expect(endTime - startTime).toBeGreaterThanOrEqual(20)
230+
})
231+
232+
test('should process tasks concurrently when using async-cycle-many policy', async () => {
233+
const worker: WorkerFn<number, string> = async (item) => {
234+
await new Promise((resolve) => setTimeout(resolve, 10))
235+
return `Processed: ${item}`
236+
}
237+
queue = new Queue<number, string>({
238+
worker,
239+
workPolicy: 'async-cycle-many',
240+
groupSize: 2,
241+
interval: 5,
242+
})
243+
244+
await queue.push(1)
245+
await queue.push(2)
246+
247+
const startTime = Date.now()
248+
const results = await Promise.all([queue.get(), queue.get()])
249+
const endTime = Date.now()
250+
251+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
252+
expect(endTime - startTime).toBeLessThanOrEqual(30)
253+
})
254+
255+
test('should handle mixed worker functions for different tasks', async () => {
256+
const worker1: WorkerFn<number, string> = async (item) =>
257+
`Processed (1): ${item}`
258+
const worker2: WorkerFn<number, string> = async (item) =>
259+
`Processed (2): ${item}`
260+
queue = new Queue<number, string>({
261+
worker: worker1,
262+
workPolicy: 'after-add',
263+
})
264+
265+
await queue.push(1)
266+
await queue.push(2, worker2)
267+
268+
const results = await Promise.all([queue.get(), queue.get()])
269+
270+
expect(results.sort()).toEqual(['Processed (1): 1', 'Processed (2): 2'])
271+
})
272+
273+
test('should process tasks with different processing times', async () => {
274+
const worker: WorkerFn<number, string> = async (item) => {
275+
await new Promise((resolve) => setTimeout(resolve, item * 10))
276+
return `Processed: ${item}`
277+
}
278+
queue = new Queue<number, string>({ worker, workPolicy: 'after-add' })
279+
280+
await queue.push(1)
281+
await queue.push(2)
282+
283+
const results = await Promise.all([queue.get(), queue.get()])
284+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
285+
})
286+
287+
test('should handle tasks that fail during processing', async () => {
288+
const worker: WorkerFn<number, string> = async (item) => {
289+
if (item === 2) {
290+
throw new Error('Task failed')
291+
}
292+
return `Processed: ${item}`
293+
}
294+
queue = new Queue<number, string>({ worker, workPolicy: 'after-add' })
295+
296+
await queue.push(1)
297+
await queue.push(2)
298+
299+
const result1 = await queue.get()
300+
expect(result1).toBe('Processed: 1')
301+
302+
await expect(queue.get()).rejects.toThrow('Task failed')
303+
})
304+
305+
test('should work correctly when no config is provided during initialization', async () => {
306+
const worker: WorkerFn<number, string> = async (item) =>
307+
`Processed: ${item}`
308+
queue = new Queue<number, string>()
309+
310+
await queue.push(1, worker)
311+
await queue.push(2, worker)
312+
313+
const results = await Promise.all([queue.get(), queue.get()])
314+
315+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
316+
})
317+
318+
test('should handle tasks that return promises', async () => {
319+
const worker = (item: number) => {
320+
return new Promise<string>((resolve) => {
321+
setTimeout(() => resolve(`Processed: ${item}`), 10)
322+
})
323+
}
324+
const queue = new Queue<number, string>({
325+
worker,
326+
workPolicy: 'after-add',
327+
})
328+
329+
await queue.push(1)
330+
await queue.push(2)
331+
332+
const results = await Promise.all([queue.get(), queue.get()])
333+
334+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
335+
})
336+
337+
test('should handle tasks that return non-promise values', async () => {
338+
const worker: WorkerFn<number, string> = (item) => {
339+
return `Processed: ${item}`
340+
}
341+
const queue = new Queue<number, string>({
342+
worker,
343+
workPolicy: 'after-add',
344+
})
345+
346+
await queue.push(1)
347+
await queue.push(2)
348+
349+
const results = await Promise.all([queue.get(), queue.get()])
350+
351+
expect(results.sort()).toEqual(['Processed: 1', 'Processed: 2'])
352+
})
353+
354+
afterEach(() => {
355+
queue && queue.clear()
57356
})
58357
})

0 commit comments

Comments
 (0)