1 import { MessageChannel, Worker as ThreadWorker } from 'node:worker_threads'
2 import { Worker as ClusterWorker } from 'node:cluster'
3 import { expect } from 'expect'
4 import { WorkerNode } from '../../lib/pools/worker-node.cjs'
5 import { WorkerTypes } from '../../lib/index.cjs'
6 import { CircularArray } from '../../lib/circular-array.cjs'
7 import { Deque } from '../../lib/deque.cjs'
8 import { DEFAULT_TASK_NAME } from '../../lib/utils.cjs'
10 describe('Worker node test suite', () => {
11 const threadWorkerNode = new WorkerNode(
13 './tests/worker-files/thread/testWorker.mjs',
14 { tasksQueueBackPressureSize: 12 }
16 const clusterWorkerNode = new WorkerNode(
18 './tests/worker-files/cluster/testWorker.cjs',
19 { tasksQueueBackPressureSize: 12 }
22 it('Worker node instantiation', () => {
23 expect(() => new WorkerNode()).toThrow(
24 new TypeError('Cannot construct a worker node without a worker type')
30 './tests/worker-files/thread/testWorker.mjs',
31 { tasksQueueBackPressureSize: 12 }
35 "Cannot construct a worker node with an invalid worker type 'invalidWorkerType'"
42 './tests/worker-files/thread/testWorker.mjs'
46 'Cannot construct a worker node without worker node options'
53 './tests/worker-files/thread/testWorker.mjs',
58 'Cannot construct a worker node with invalid options: must be a plain object'
65 './tests/worker-files/thread/testWorker.mjs',
70 'Cannot construct a worker node without a tasks queue back pressure size option'
77 './tests/worker-files/thread/testWorker.mjs',
78 { tasksQueueBackPressureSize: 'invalidTasksQueueBackPressureSize' }
82 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
89 './tests/worker-files/thread/testWorker.mjs',
90 { tasksQueueBackPressureSize: 0.2 }
94 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer'
101 './tests/worker-files/thread/testWorker.mjs',
102 { tasksQueueBackPressureSize: 0 }
106 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
113 './tests/worker-files/thread/testWorker.mjs',
114 { tasksQueueBackPressureSize: -1 }
118 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer'
121 expect(threadWorkerNode).toBeInstanceOf(WorkerNode)
122 expect(threadWorkerNode.worker).toBeInstanceOf(ThreadWorker)
123 expect(threadWorkerNode.info).toStrictEqual({
124 id: threadWorkerNode.worker.threadId,
125 type: WorkerTypes.thread,
130 expect(threadWorkerNode.usage).toStrictEqual({
136 sequentiallyStolen: 0,
141 history: new CircularArray()
144 history: new CircularArray()
148 history: new CircularArray()
151 history: new CircularArray()
155 expect(threadWorkerNode.messageChannel).toBeInstanceOf(MessageChannel)
156 expect(threadWorkerNode.tasksQueueBackPressureSize).toBe(12)
157 expect(threadWorkerNode.tasksQueue).toBeInstanceOf(Deque)
158 expect(threadWorkerNode.tasksQueue.size).toBe(0)
159 expect(threadWorkerNode.tasksQueueSize()).toBe(
160 threadWorkerNode.tasksQueue.size
162 expect(threadWorkerNode.onBackPressureStarted).toBe(false)
163 expect(threadWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
165 expect(clusterWorkerNode).toBeInstanceOf(WorkerNode)
166 expect(clusterWorkerNode.worker).toBeInstanceOf(ClusterWorker)
167 expect(clusterWorkerNode.info).toStrictEqual({
168 id: clusterWorkerNode.worker.id,
169 type: WorkerTypes.cluster,
174 expect(clusterWorkerNode.usage).toStrictEqual({
180 sequentiallyStolen: 0,
185 history: new CircularArray()
188 history: new CircularArray()
192 history: new CircularArray()
195 history: new CircularArray()
199 expect(clusterWorkerNode.messageChannel).toBeUndefined()
200 expect(clusterWorkerNode.tasksQueueBackPressureSize).toBe(12)
201 expect(clusterWorkerNode.tasksQueue).toBeInstanceOf(Deque)
202 expect(clusterWorkerNode.tasksQueue.size).toBe(0)
203 expect(clusterWorkerNode.tasksQueueSize()).toBe(
204 clusterWorkerNode.tasksQueue.size
206 expect(clusterWorkerNode.onBackPressureStarted).toBe(false)
207 expect(clusterWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map)
210 it('Worker node getTaskFunctionWorkerUsage()', () => {
212 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
215 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list is not yet defined"
218 threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1']
220 threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
223 "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list has less than 3 elements"
226 threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1', 'fn2']
228 threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
234 sequentiallyStolen: 0,
239 history: new CircularArray()
242 history: new CircularArray()
246 history: new CircularArray()
249 history: new CircularArray()
253 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn1')).toStrictEqual({
258 sequentiallyStolen: 0,
263 history: new CircularArray()
266 history: new CircularArray()
270 history: new CircularArray()
273 history: new CircularArray()
277 expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn2')).toStrictEqual({
282 sequentiallyStolen: 0,
287 history: new CircularArray()
290 history: new CircularArray()
294 history: new CircularArray()
297 history: new CircularArray()
301 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
304 it('Worker node deleteTaskFunctionWorkerUsage()', () => {
305 expect(threadWorkerNode.info.taskFunctionNames).toStrictEqual([
310 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
312 threadWorkerNode.deleteTaskFunctionWorkerUsage('invalidTaskFunction')
314 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
315 expect(threadWorkerNode.deleteTaskFunctionWorkerUsage('fn1')).toBe(true)
316 expect(threadWorkerNode.taskFunctionsUsage.size).toBe(1)