docs: generate documentation
[poolifier.git] / tests / pools / worker-node.test.mjs
... / ...
CommitLineData
1import { Worker as ClusterWorker } from 'node:cluster'
2import { MessageChannel, Worker as ThreadWorker } from 'node:worker_threads'
3
4import { expect } from 'expect'
5
6import { CircularArray } from '../../lib/circular-array.cjs'
7import { WorkerTypes } from '../../lib/index.cjs'
8import { WorkerNode } from '../../lib/pools/worker-node.cjs'
9import { PriorityQueue } from '../../lib/priority-queue.cjs'
10import { DEFAULT_TASK_NAME } from '../../lib/utils.cjs'
11
12describe('Worker node test suite', () => {
13 const threadWorkerNode = new WorkerNode(
14 WorkerTypes.thread,
15 './tests/worker-files/thread/testWorker.mjs',
16 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 6 }
17 )
18 const clusterWorkerNode = new WorkerNode(
19 WorkerTypes.cluster,
20 './tests/worker-files/cluster/testWorker.cjs',
21 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 6 }
22 )
23
24 it('Worker node instantiation', () => {
25 expect(() => new WorkerNode()).toThrow(
26 new TypeError('Cannot construct a worker node without a worker type')
27 )
28 expect(
29 () =>
30 new WorkerNode(
31 'invalidWorkerType',
32 './tests/worker-files/thread/testWorker.mjs',
33 { tasksQueueBackPressureSize: 12 }
34 )
35 ).toThrow(
36 new TypeError(
37 "Cannot construct a worker node with an invalid worker type 'invalidWorkerType'"
38 )
39 )
40 expect(
41 () =>
42 new WorkerNode(
43 WorkerTypes.thread,
44 './tests/worker-files/thread/testWorker.mjs'
45 )
46 ).toThrow(
47 new TypeError(
48 'Cannot construct a worker node without worker node options'
49 )
50 )
51 expect(
52 () =>
53 new WorkerNode(
54 WorkerTypes.thread,
55 './tests/worker-files/thread/testWorker.mjs',
56 ''
57 )
58 ).toThrow(
59 new TypeError(
60 'Cannot construct a worker node with invalid options: must be a plain object'
61 )
62 )
63 expect(
64 () =>
65 new WorkerNode(
66 WorkerTypes.thread,
67 './tests/worker-files/thread/testWorker.mjs',
68 {}
69 )
70 ).toThrow(
71 new TypeError(
72 'Cannot construct a worker node without a tasks queue back pressure size option'
73 )
74 )
75 expect(
76 () =>
77 new WorkerNode(
78 WorkerTypes.thread,
79 './tests/worker-files/thread/testWorker.mjs',
80 { tasksQueueBackPressureSize: 'invalidTasksQueueBackPressureSize' }
81 )
82 ).toThrow(
83 new TypeError(
84 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
85 )
86 )
87 expect(
88 () =>
89 new WorkerNode(
90 WorkerTypes.thread,
91 './tests/worker-files/thread/testWorker.mjs',
92 { tasksQueueBackPressureSize: 0.2 }
93 )
94 ).toThrow(
95 new TypeError(
96 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
97 )
98 )
99 expect(
100 () =>
101 new WorkerNode(
102 WorkerTypes.thread,
103 './tests/worker-files/thread/testWorker.mjs',
104 { tasksQueueBackPressureSize: 0 }
105 )
106 ).toThrow(
107 new RangeError(
108 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
109 )
110 )
111 expect(
112 () =>
113 new WorkerNode(
114 WorkerTypes.thread,
115 './tests/worker-files/thread/testWorker.mjs',
116 { tasksQueueBackPressureSize: -1 }
117 )
118 ).toThrow(
119 new RangeError(
120 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
121 )
122 )
123 expect(
124 () =>
125 new WorkerNode(
126 WorkerTypes.thread,
127 './tests/worker-files/thread/testWorker.mjs',
128 {
129 tasksQueueBackPressureSize: 12
130 }
131 )
132 ).toThrow(
133 new TypeError(
134 'Cannot construct a worker node without a tasks queue bucket size option'
135 )
136 )
137 expect(
138 () =>
139 new WorkerNode(
140 WorkerTypes.thread,
141 './tests/worker-files/thread/testWorker.mjs',
142 {
143 tasksQueueBackPressureSize: 12,
144 tasksQueueBucketSize: 'invalidTasksQueueBucketSize'
145 }
146 )
147 ).toThrow(
148 new TypeError(
149 'Cannot construct a worker node with a tasks queue bucket size option that is not an integer'
150 )
151 )
152 expect(
153 () =>
154 new WorkerNode(
155 WorkerTypes.thread,
156 './tests/worker-files/thread/testWorker.mjs',
157 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 0.2 }
158 )
159 ).toThrow(
160 new TypeError(
161 'Cannot construct a worker node with a tasks queue bucket size option that is not an integer'
162 )
163 )
164 expect(
165 () =>
166 new WorkerNode(
167 WorkerTypes.thread,
168 './tests/worker-files/thread/testWorker.mjs',
169 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: 0 }
170 )
171 ).toThrow(
172 new RangeError(
173 'Cannot construct a worker node with a tasks queue bucket size option that is not a positive integer'
174 )
175 )
176 expect(
177 () =>
178 new WorkerNode(
179 WorkerTypes.thread,
180 './tests/worker-files/thread/testWorker.mjs',
181 { tasksQueueBackPressureSize: 12, tasksQueueBucketSize: -1 }
182 )
183 ).toThrow(
184 new RangeError(
185 'Cannot construct a worker node with a tasks queue bucket size option that is not a positive integer'
186 )
187 )
188 expect(threadWorkerNode).toBeInstanceOf(WorkerNode)
189 expect(threadWorkerNode.worker).toBeInstanceOf(ThreadWorker)
190 expect(threadWorkerNode.info).toStrictEqual({
191 id: threadWorkerNode.worker.threadId,
192 type: WorkerTypes.thread,
193 dynamic: false,
194 ready: false,
195 stealing: false
196 })
197 expect(threadWorkerNode.usage).toStrictEqual({
198 tasks: {
199 executed: 0,
200 executing: 0,
201 queued: 0,
202 maxQueued: 0,
203 sequentiallyStolen: 0,
204 stolen: 0,
205 failed: 0
206 },
207 runTime: {
208 history: new CircularArray()
209 },
210 waitTime: {
211 history: new CircularArray()
212 },
213 elu: {
214 idle: {
215 history: new CircularArray()
216 },
217 active: {
218 history: new CircularArray()
219 }
220 }
221 })
222 expect(threadWorkerNode.messageChannel).toBeInstanceOf(MessageChannel)
223 expect(threadWorkerNode.tasksQueueBackPressureSize).toBe(12)
224 expect(threadWorkerNode.tasksQueue).toBeInstanceOf(PriorityQueue)
225 expect(threadWorkerNode.tasksQueue.size).toBe(0)
226 expect(threadWorkerNode.tasksQueue.k).toBe(6)
227 expect(threadWorkerNode.tasksQueueSize()).toBe(
228 threadWorkerNode.tasksQueue.size
229 )
230 expect(threadWorkerNode.onBackPressureStarted).toBe(false)
231 expect(threadWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
232
233 expect(clusterWorkerNode).toBeInstanceOf(WorkerNode)
234 expect(clusterWorkerNode.worker).toBeInstanceOf(ClusterWorker)
235 expect(clusterWorkerNode.info).toStrictEqual({
236 id: clusterWorkerNode.worker.id,
237 type: WorkerTypes.cluster,
238 dynamic: false,
239 ready: false,
240 stealing: false
241 })
242 expect(clusterWorkerNode.usage).toStrictEqual({
243 tasks: {
244 executed: 0,
245 executing: 0,
246 queued: 0,
247 maxQueued: 0,
248 sequentiallyStolen: 0,
249 stolen: 0,
250 failed: 0
251 },
252 runTime: {
253 history: new CircularArray()
254 },
255 waitTime: {
256 history: new CircularArray()
257 },
258 elu: {
259 idle: {
260 history: new CircularArray()
261 },
262 active: {
263 history: new CircularArray()
264 }
265 }
266 })
267 expect(clusterWorkerNode.messageChannel).toBeUndefined()
268 expect(clusterWorkerNode.tasksQueueBackPressureSize).toBe(12)
269 expect(clusterWorkerNode.tasksQueue).toBeInstanceOf(PriorityQueue)
270 expect(clusterWorkerNode.tasksQueue.size).toBe(0)
271 expect(clusterWorkerNode.tasksQueue.k).toBe(6)
272 expect(clusterWorkerNode.tasksQueueSize()).toBe(
273 clusterWorkerNode.tasksQueue.size
274 )
275 expect(clusterWorkerNode.onBackPressureStarted).toBe(false)
276 expect(clusterWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
277 })
278
279 it('Worker node getTaskFunctionWorkerUsage()', () => {
280 expect(() =>
281 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
282 ).toThrow(
283 new TypeError(
284 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list is not yet defined"
285 )
286 )
287 threadWorkerNode.info.taskFunctionsProperties = [
288 { name: DEFAULT_TASK_NAME },
289 { name: 'fn1' }
290 ]
291 expect(() =>
292 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
293 ).toThrow(
294 new TypeError(
295 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list has less than 3 elements"
296 )
297 )
298 threadWorkerNode.info.taskFunctionsProperties = [
299 { name: DEFAULT_TASK_NAME },
300 { name: 'fn1' },
301 { name: 'fn2' }
302 ]
303 expect(
304 threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
305 ).toStrictEqual({
306 tasks: {
307 executed: 0,
308 executing: 0,
309 queued: 0,
310 sequentiallyStolen: 0,
311 stolen: 0,
312 failed: 0
313 },
314 runTime: {
315 history: new CircularArray()
316 },
317 waitTime: {
318 history: new CircularArray()
319 },
320 elu: {
321 idle: {
322 history: new CircularArray()
323 },
324 active: {
325 history: new CircularArray()
326 }
327 }
328 })
329 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn1')).toStrictEqual({
330 tasks: {
331 executed: 0,
332 executing: 0,
333 queued: 0,
334 sequentiallyStolen: 0,
335 stolen: 0,
336 failed: 0
337 },
338 runTime: {
339 history: new CircularArray()
340 },
341 waitTime: {
342 history: new CircularArray()
343 },
344 elu: {
345 idle: {
346 history: new CircularArray()
347 },
348 active: {
349 history: new CircularArray()
350 }
351 }
352 })
353 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn2')).toStrictEqual({
354 tasks: {
355 executed: 0,
356 executing: 0,
357 queued: 0,
358 sequentiallyStolen: 0,
359 stolen: 0,
360 failed: 0
361 },
362 runTime: {
363 history: new CircularArray()
364 },
365 waitTime: {
366 history: new CircularArray()
367 },
368 elu: {
369 idle: {
370 history: new CircularArray()
371 },
372 active: {
373 history: new CircularArray()
374 }
375 }
376 })
377 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
378 })
379
380 it('Worker node deleteTaskFunctionWorkerUsage()', () => {
381 expect(threadWorkerNode.info.taskFunctionsProperties).toStrictEqual([
382 { name: DEFAULT_TASK_NAME },
383 { name: 'fn1' },
384 { name: 'fn2' }
385 ])
386 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
387 expect(
388 threadWorkerNode.deleteTaskFunctionWorkerUsage('invalidTaskFunction')
389 ).toBe(false)
390 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
391 expect(threadWorkerNode.deleteTaskFunctionWorkerUsage('fn1')).toBe(true)
392 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(1)
393 })
394})