refactor: cleanup worker error handling code
[poolifier.git] / tests / pools / worker-node.test.mjs
CommitLineData
9974369e
JB
1import { MessageChannel, Worker as ThreadWorker } from 'node:worker_threads'
2import { Worker as ClusterWorker } from 'node:cluster'
a074ffee
JB
3import { expect } from 'expect'
4import { WorkerNode } from '../../lib/pools/worker-node.js'
5import { WorkerTypes } from '../../lib/index.js'
6import { CircularArray } from '../../lib/circular-array.js'
7import { Deque } from '../../lib/deque.js'
8import { DEFAULT_TASK_NAME } from '../../lib/utils.js'
26fb3c18
JB
9
10describe('Worker node test suite', () => {
c3719753
JB
11 const threadWorkerNode = new WorkerNode(
12 WorkerTypes.thread,
13 './tests/worker-files/thread/testWorker.mjs',
14 { tasksQueueBackPressureSize: 12 }
15 )
16 const clusterWorkerNode = new WorkerNode(
17 WorkerTypes.cluster,
18 './tests/worker-files/cluster/testWorker.js',
19 { tasksQueueBackPressureSize: 12 }
20 )
26fb3c18
JB
21
22 it('Worker node instantiation', () => {
948faff7 23 expect(() => new WorkerNode()).toThrow(
c3719753 24 new TypeError('Cannot construct a worker node without a worker type')
26fb3c18 25 )
c3719753
JB
26 expect(
27 () =>
28 new WorkerNode(
29 'invalidWorkerType',
30 './tests/worker-files/thread/testWorker.mjs',
31 { tasksQueueBackPressureSize: 12 }
32 )
33 ).toThrow(
34 new TypeError(
35 "Cannot construct a worker node with an invalid worker type 'invalidWorkerType'"
36 )
37 )
38 expect(
39 () =>
40 new WorkerNode(
41 WorkerTypes.thread,
42 './tests/worker-files/thread/testWorker.mjs'
43 )
44 ).toThrow(
26fb3c18 45 new TypeError(
c3719753 46 'Cannot construct a worker node without worker node options'
26fb3c18
JB
47 )
48 )
49 expect(
c3719753
JB
50 () =>
51 new WorkerNode(
52 WorkerTypes.thread,
53 './tests/worker-files/thread/testWorker.mjs',
54 ''
55 )
948faff7 56 ).toThrow(
26fb3c18 57 new TypeError(
c3719753 58 'Cannot construct a worker node with invalid options: must be a plain object'
26fb3c18
JB
59 )
60 )
c3719753
JB
61 expect(
62 () =>
63 new WorkerNode(
64 WorkerTypes.thread,
65 './tests/worker-files/thread/testWorker.mjs',
66 {}
67 )
68 ).toThrow(
5b49e864 69 new TypeError(
c3719753 70 'Cannot construct a worker node without a tasks queue back pressure size option'
5b49e864
JB
71 )
72 )
c3719753
JB
73 expect(
74 () =>
75 new WorkerNode(
76 WorkerTypes.thread,
77 './tests/worker-files/thread/testWorker.mjs',
78 { tasksQueueBackPressureSize: 'invalidTasksQueueBackPressureSize' }
79 )
80 ).toThrow(
81 new TypeError(
82 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
83 )
84 )
85 expect(
86 () =>
87 new WorkerNode(
88 WorkerTypes.thread,
89 './tests/worker-files/thread/testWorker.mjs',
90 { tasksQueueBackPressureSize: 0.2 }
91 )
92 ).toThrow(
93 new TypeError(
94 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
95 )
96 )
97 expect(
98 () =>
99 new WorkerNode(
100 WorkerTypes.thread,
101 './tests/worker-files/thread/testWorker.mjs',
102 { tasksQueueBackPressureSize: 0 }
103 )
104 ).toThrow(
5b49e864 105 new RangeError(
c3719753 106 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
5b49e864
JB
107 )
108 )
c3719753
JB
109 expect(
110 () =>
111 new WorkerNode(
112 WorkerTypes.thread,
113 './tests/worker-files/thread/testWorker.mjs',
114 { tasksQueueBackPressureSize: -1 }
115 )
116 ).toThrow(
5b49e864 117 new RangeError(
c3719753 118 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
5b49e864
JB
119 )
120 )
75de9f41 121 expect(threadWorkerNode).toBeInstanceOf(WorkerNode)
9974369e 122 expect(threadWorkerNode.worker).toBeInstanceOf(ThreadWorker)
75de9f41 123 expect(threadWorkerNode.info).toStrictEqual({
c3719753 124 id: threadWorkerNode.worker.threadId,
26fb3c18
JB
125 type: WorkerTypes.thread,
126 dynamic: false,
127 ready: false
128 })
75de9f41
JB
129 expect(threadWorkerNode.usage).toStrictEqual({
130 tasks: {
131 executed: 0,
132 executing: 0,
133 queued: 0,
134 maxQueued: 0,
463226a4 135 sequentiallyStolen: 0,
75de9f41
JB
136 stolen: 0,
137 failed: 0
138 },
139 runTime: {
4ba4c7f9 140 history: new CircularArray()
75de9f41
JB
141 },
142 waitTime: {
4ba4c7f9 143 history: new CircularArray()
75de9f41
JB
144 },
145 elu: {
146 idle: {
4ba4c7f9 147 history: new CircularArray()
75de9f41
JB
148 },
149 active: {
4ba4c7f9 150 history: new CircularArray()
75de9f41
JB
151 }
152 }
153 })
154 expect(threadWorkerNode.messageChannel).toBeInstanceOf(MessageChannel)
155 expect(threadWorkerNode.tasksQueueBackPressureSize).toBe(12)
156 expect(threadWorkerNode.tasksQueue).toBeInstanceOf(Deque)
157 expect(threadWorkerNode.tasksQueue.size).toBe(0)
68f1f531
JB
158 expect(threadWorkerNode.tasksQueueSize()).toBe(
159 threadWorkerNode.tasksQueue.size
160 )
161 expect(threadWorkerNode.onBackPressureStarted).toBe(false)
75de9f41
JB
162 expect(threadWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
163
164 expect(clusterWorkerNode).toBeInstanceOf(WorkerNode)
9974369e 165 expect(clusterWorkerNode.worker).toBeInstanceOf(ClusterWorker)
75de9f41 166 expect(clusterWorkerNode.info).toStrictEqual({
c3719753 167 id: clusterWorkerNode.worker.id,
75de9f41
JB
168 type: WorkerTypes.cluster,
169 dynamic: false,
170 ready: false
171 })
172 expect(clusterWorkerNode.usage).toStrictEqual({
26fb3c18
JB
173 tasks: {
174 executed: 0,
175 executing: 0,
176 queued: 0,
177 maxQueued: 0,
463226a4 178 sequentiallyStolen: 0,
26fb3c18
JB
179 stolen: 0,
180 failed: 0
181 },
182 runTime: {
4ba4c7f9 183 history: new CircularArray()
26fb3c18
JB
184 },
185 waitTime: {
4ba4c7f9 186 history: new CircularArray()
26fb3c18
JB
187 },
188 elu: {
189 idle: {
4ba4c7f9 190 history: new CircularArray()
26fb3c18
JB
191 },
192 active: {
4ba4c7f9 193 history: new CircularArray()
26fb3c18
JB
194 }
195 }
196 })
75de9f41
JB
197 expect(clusterWorkerNode.messageChannel).toBeUndefined()
198 expect(clusterWorkerNode.tasksQueueBackPressureSize).toBe(12)
199 expect(clusterWorkerNode.tasksQueue).toBeInstanceOf(Deque)
200 expect(clusterWorkerNode.tasksQueue.size).toBe(0)
68f1f531
JB
201 expect(clusterWorkerNode.tasksQueueSize()).toBe(
202 clusterWorkerNode.tasksQueue.size
203 )
204 expect(clusterWorkerNode.onBackPressureStarted).toBe(false)
75de9f41 205 expect(clusterWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
26fb3c18
JB
206 })
207
208 it('Worker node getTaskFunctionWorkerUsage()', () => {
209 expect(() =>
75de9f41 210 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
948faff7 211 ).toThrow(
26fb3c18
JB
212 new TypeError(
213 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list is not yet defined"
214 )
215 )
66979634 216 threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1']
26fb3c18 217 expect(() =>
75de9f41 218 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
948faff7 219 ).toThrow(
26fb3c18
JB
220 new TypeError(
221 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list has less than 3 elements"
222 )
223 )
66979634 224 threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1', 'fn2']
6cd5248f 225 expect(
75de9f41 226 threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
6cd5248f 227 ).toStrictEqual({
26fb3c18
JB
228 tasks: {
229 executed: 0,
230 executing: 0,
231 queued: 0,
463226a4 232 sequentiallyStolen: 0,
5ad42e34 233 stolen: 0,
26fb3c18
JB
234 failed: 0
235 },
236 runTime: {
4ba4c7f9 237 history: new CircularArray()
26fb3c18
JB
238 },
239 waitTime: {
4ba4c7f9 240 history: new CircularArray()
26fb3c18
JB
241 },
242 elu: {
243 idle: {
4ba4c7f9 244 history: new CircularArray()
26fb3c18
JB
245 },
246 active: {
4ba4c7f9 247 history: new CircularArray()
26fb3c18
JB
248 }
249 }
250 })
75de9f41 251 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn1')).toStrictEqual({
26fb3c18
JB
252 tasks: {
253 executed: 0,
254 executing: 0,
255 queued: 0,
463226a4 256 sequentiallyStolen: 0,
5ad42e34 257 stolen: 0,
26fb3c18
JB
258 failed: 0
259 },
260 runTime: {
4ba4c7f9 261 history: new CircularArray()
26fb3c18
JB
262 },
263 waitTime: {
4ba4c7f9 264 history: new CircularArray()
26fb3c18
JB
265 },
266 elu: {
267 idle: {
4ba4c7f9 268 history: new CircularArray()
26fb3c18
JB
269 },
270 active: {
4ba4c7f9 271 history: new CircularArray()
26fb3c18
JB
272 }
273 }
274 })
75de9f41 275 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn2')).toStrictEqual({
26fb3c18
JB
276 tasks: {
277 executed: 0,
278 executing: 0,
279 queued: 0,
463226a4 280 sequentiallyStolen: 0,
5ad42e34 281 stolen: 0,
26fb3c18
JB
282 failed: 0
283 },
284 runTime: {
4ba4c7f9 285 history: new CircularArray()
26fb3c18
JB
286 },
287 waitTime: {
4ba4c7f9 288 history: new CircularArray()
26fb3c18
JB
289 },
290 elu: {
291 idle: {
4ba4c7f9 292 history: new CircularArray()
26fb3c18
JB
293 },
294 active: {
4ba4c7f9 295 history: new CircularArray()
26fb3c18
JB
296 }
297 }
298 })
75de9f41 299 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
26fb3c18 300 })
adee6053
JB
301
302 it('Worker node deleteTaskFunctionWorkerUsage()', () => {
303 expect(threadWorkerNode.info.taskFunctionNames).toStrictEqual([
304 DEFAULT_TASK_NAME,
305 'fn1',
306 'fn2'
307 ])
308 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
309 expect(
310 threadWorkerNode.deleteTaskFunctionWorkerUsage('invalidTaskFunction')
311 ).toBe(false)
312 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
313 expect(threadWorkerNode.deleteTaskFunctionWorkerUsage('fn1')).toBe(true)
314 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(1)
315 })
26fb3c18 316})