Commit | Line | Data |
---|---|---|
a074ffee JB |
1 | import { expect } from 'expect' |
2 | import { restore, stub } from 'sinon' | |
d35e5717 JB |
3 | import { ClusterWorker, KillBehaviors, ThreadWorker } from '../../lib/index.cjs' |
4 | import { DEFAULT_TASK_NAME, EMPTY_FUNCTION } from '../../lib/utils.cjs' | |
7fc5cce6 | 5 | |
e1ffb94f | 6 | describe('Abstract worker test suite', () => { |
1f68cede | 7 | class StubWorkerWithMainWorker extends ThreadWorker { |
e1ffb94f JB |
8 | constructor (fn, opts) { |
9 | super(fn, opts) | |
41072404 | 10 | delete this.mainWorker |
e1ffb94f | 11 | } |
7fc5cce6 | 12 | } |
c510fea7 | 13 | |
dc021bcc | 14 | afterEach(() => { |
a074ffee | 15 | restore() |
dc021bcc JB |
16 | }) |
17 | ||
e088a00c | 18 | it('Verify worker options default values', () => { |
8620fb25 | 19 | const worker = new ThreadWorker(() => {}) |
cca3bb1a JB |
20 | expect(worker.opts).toStrictEqual({ |
21 | killBehavior: KillBehaviors.SOFT, | |
22 | maxInactiveTime: 60000, | |
23 | killHandler: EMPTY_FUNCTION | |
24 | }) | |
8620fb25 JB |
25 | }) |
26 | ||
c20084b6 | 27 | it('Verify that worker options are checked at worker creation', () => { |
948faff7 | 28 | expect(() => new ClusterWorker(() => {}, '')).toThrow( |
c20084b6 JB |
29 | new TypeError('opts worker options parameter is not a plain object') |
30 | ) | |
948faff7 JB |
31 | expect(() => new ClusterWorker(() => {}, { killBehavior: '' })).toThrow( |
32 | new TypeError("killBehavior option '' is not valid") | |
33 | ) | |
34 | expect(() => new ClusterWorker(() => {}, { killBehavior: 0 })).toThrow( | |
c20084b6 JB |
35 | new TypeError("killBehavior option '0' is not valid") |
36 | ) | |
948faff7 JB |
37 | expect(() => new ThreadWorker(() => {}, { maxInactiveTime: '' })).toThrow( |
38 | new TypeError('maxInactiveTime option is not an integer') | |
39 | ) | |
40 | expect(() => new ThreadWorker(() => {}, { maxInactiveTime: 0.5 })).toThrow( | |
41 | new TypeError('maxInactiveTime option is not an integer') | |
42 | ) | |
43 | expect(() => new ThreadWorker(() => {}, { maxInactiveTime: 0 })).toThrow( | |
c20084b6 JB |
44 | new TypeError( |
45 | 'maxInactiveTime option is not a positive integer greater or equal than 5' | |
46 | ) | |
47 | ) | |
948faff7 | 48 | expect(() => new ThreadWorker(() => {}, { maxInactiveTime: 4 })).toThrow( |
c20084b6 JB |
49 | new TypeError( |
50 | 'maxInactiveTime option is not a positive integer greater or equal than 5' | |
51 | ) | |
52 | ) | |
948faff7 | 53 | expect(() => new ThreadWorker(() => {}, { killHandler: '' })).toThrow( |
c20084b6 JB |
54 | new TypeError('killHandler option is not a function') |
55 | ) | |
948faff7 | 56 | expect(() => new ThreadWorker(() => {}, { killHandler: 0 })).toThrow( |
c20084b6 JB |
57 | new TypeError('killHandler option is not a function') |
58 | ) | |
c20084b6 JB |
59 | }) |
60 | ||
8620fb25 | 61 | it('Verify that worker options are set at worker creation', () => { |
df9aaf20 JB |
62 | const killHandler = () => { |
63 | console.info('Worker received kill message') | |
64 | } | |
8620fb25 | 65 | const worker = new ClusterWorker(() => {}, { |
df9aaf20 | 66 | killBehavior: KillBehaviors.HARD, |
cca3bb1a | 67 | maxInactiveTime: 6000, |
c20084b6 | 68 | killHandler |
8620fb25 | 69 | }) |
cca3bb1a JB |
70 | expect(worker.opts).toStrictEqual({ |
71 | killBehavior: KillBehaviors.HARD, | |
72 | maxInactiveTime: 6000, | |
73 | killHandler | |
74 | }) | |
8620fb25 JB |
75 | }) |
76 | ||
a86b6df1 | 77 | it('Verify that taskFunctions parameter is mandatory', () => { |
948faff7 | 78 | expect(() => new ClusterWorker()).toThrow( |
c20084b6 | 79 | new Error('taskFunctions parameter is mandatory') |
a86b6df1 | 80 | ) |
d4aeae5a JB |
81 | }) |
82 | ||
f34fdabe | 83 | it('Verify that taskFunctions parameter is a function or a plain object', () => { |
948faff7 | 84 | expect(() => new ClusterWorker(0)).toThrow( |
f34fdabe JB |
85 | new TypeError( |
86 | 'taskFunctions parameter is not a function or a plain object' | |
87 | ) | |
d4aeae5a | 88 | ) |
948faff7 | 89 | expect(() => new ClusterWorker('')).toThrow( |
f34fdabe JB |
90 | new TypeError( |
91 | 'taskFunctions parameter is not a function or a plain object' | |
92 | ) | |
a86b6df1 | 93 | ) |
948faff7 | 94 | expect(() => new ClusterWorker(true)).toThrow( |
f34fdabe JB |
95 | new TypeError( |
96 | 'taskFunctions parameter is not a function or a plain object' | |
97 | ) | |
d4aeae5a | 98 | ) |
948faff7 | 99 | expect(() => new ClusterWorker([])).toThrow( |
f34fdabe JB |
100 | new TypeError( |
101 | 'taskFunctions parameter is not a function or a plain object' | |
102 | ) | |
a86b6df1 | 103 | ) |
948faff7 | 104 | expect(() => new ClusterWorker(new Map())).toThrow( |
f34fdabe JB |
105 | new TypeError( |
106 | 'taskFunctions parameter is not a function or a plain object' | |
107 | ) | |
a86b6df1 | 108 | ) |
948faff7 | 109 | expect(() => new ClusterWorker(new Set())).toThrow( |
f34fdabe JB |
110 | new TypeError( |
111 | 'taskFunctions parameter is not a function or a plain object' | |
112 | ) | |
a86b6df1 | 113 | ) |
948faff7 | 114 | expect(() => new ClusterWorker(new WeakMap())).toThrow( |
f34fdabe JB |
115 | new TypeError( |
116 | 'taskFunctions parameter is not a function or a plain object' | |
117 | ) | |
d4aeae5a | 118 | ) |
948faff7 | 119 | expect(() => new ClusterWorker(new WeakSet())).toThrow( |
f34fdabe JB |
120 | new TypeError( |
121 | 'taskFunctions parameter is not a function or a plain object' | |
122 | ) | |
a86b6df1 | 123 | ) |
f34fdabe JB |
124 | }) |
125 | ||
126 | it('Verify that taskFunctions parameter is not an empty object', () => { | |
948faff7 | 127 | expect(() => new ClusterWorker({})).toThrow( |
0d80593b | 128 | new Error('taskFunctions parameter object is empty') |
630f0acf | 129 | ) |
a86b6df1 JB |
130 | }) |
131 | ||
2a69b8c5 JB |
132 | it('Verify that taskFunctions parameter with unique function is taken', () => { |
133 | const worker = new ThreadWorker(() => {}) | |
6cd5248f | 134 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
135 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
136 | expect(worker.taskFunctions.size).toBe(2) | |
6cd5248f | 137 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
138 | worker.taskFunctions.get('fn1') |
139 | ) | |
140 | }) | |
141 | ||
6934964f | 142 | it('Verify that taskFunctions parameter with multiple task functions is checked', () => { |
f34fdabe JB |
143 | const fn1 = () => { |
144 | return 1 | |
145 | } | |
146 | const fn2 = '' | |
948faff7 | 147 | expect(() => new ThreadWorker({ '': fn1 })).toThrow( |
6934964f JB |
148 | new TypeError('A taskFunctions parameter object key is an empty string') |
149 | ) | |
948faff7 | 150 | expect(() => new ThreadWorker({ fn1, fn2 })).toThrow( |
f34fdabe JB |
151 | new TypeError('A taskFunctions parameter object value is not a function') |
152 | ) | |
153 | }) | |
154 | ||
a86b6df1 JB |
155 | it('Verify that taskFunctions parameter with multiple task functions is taken', () => { |
156 | const fn1 = () => { | |
157 | return 1 | |
158 | } | |
159 | const fn2 = () => { | |
160 | return 2 | |
161 | } | |
162 | const worker = new ClusterWorker({ fn1, fn2 }) | |
6cd5248f | 163 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
164 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
165 | expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) | |
166 | expect(worker.taskFunctions.size).toBe(3) | |
6cd5248f | 167 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
168 | worker.taskFunctions.get('fn1') |
169 | ) | |
d4aeae5a JB |
170 | }) |
171 | ||
07588f30 | 172 | it('Verify that async kill handler is called when worker is killed', () => { |
a074ffee | 173 | const killHandlerStub = stub().returns() |
07588f30 | 174 | const worker = new ClusterWorker(() => {}, { |
1cc6e9ef | 175 | killHandler: async () => await Promise.resolve(killHandlerStub()) |
07588f30 JB |
176 | }) |
177 | worker.isMain = false | |
178 | worker.handleKillMessage() | |
179 | expect(killHandlerStub.calledOnce).toBe(true) | |
180 | }) | |
df9aaf20 | 181 | |
318d4156 | 182 | it('Verify that getMainWorker() throw error if main worker is not set', () => { |
7fc5cce6 | 183 | expect(() => |
1f68cede | 184 | new StubWorkerWithMainWorker(() => {}).getMainWorker() |
948faff7 | 185 | ).toThrow('Main worker not set') |
7fc5cce6 | 186 | }) |
2a69b8c5 | 187 | |
9eae3c69 | 188 | it('Verify that hasTaskFunction() is working', () => { |
2a69b8c5 JB |
189 | const fn1 = () => { |
190 | return 1 | |
191 | } | |
192 | const fn2 = () => { | |
193 | return 2 | |
194 | } | |
195 | const worker = new ClusterWorker({ fn1, fn2 }) | |
66979634 JB |
196 | expect(worker.hasTaskFunction(0)).toStrictEqual({ |
197 | status: false, | |
198 | error: new TypeError('name parameter is not a string') | |
199 | }) | |
200 | expect(worker.hasTaskFunction('')).toStrictEqual({ | |
201 | status: false, | |
202 | error: new TypeError('name parameter is an empty string') | |
203 | }) | |
204 | expect(worker.hasTaskFunction(DEFAULT_TASK_NAME)).toStrictEqual({ | |
205 | status: true | |
206 | }) | |
207 | expect(worker.hasTaskFunction('fn1')).toStrictEqual({ status: true }) | |
208 | expect(worker.hasTaskFunction('fn2')).toStrictEqual({ status: true }) | |
209 | expect(worker.hasTaskFunction('fn3')).toStrictEqual({ status: false }) | |
2a69b8c5 JB |
210 | }) |
211 | ||
9eae3c69 | 212 | it('Verify that addTaskFunction() is working', () => { |
2a69b8c5 JB |
213 | const fn1 = () => { |
214 | return 1 | |
215 | } | |
216 | const fn2 = () => { | |
217 | return 2 | |
218 | } | |
219 | const fn1Replacement = () => { | |
220 | return 3 | |
221 | } | |
222 | const worker = new ThreadWorker(fn1) | |
66979634 JB |
223 | expect(worker.addTaskFunction(0, fn1)).toStrictEqual({ |
224 | status: false, | |
225 | error: new TypeError('name parameter is not a string') | |
226 | }) | |
227 | expect(worker.addTaskFunction('', fn1)).toStrictEqual({ | |
228 | status: false, | |
229 | error: new TypeError('name parameter is an empty string') | |
230 | }) | |
231 | expect(worker.addTaskFunction('fn3', '')).toStrictEqual({ | |
232 | status: false, | |
233 | error: new TypeError('fn parameter is not a function') | |
234 | }) | |
6cd5248f | 235 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
236 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
237 | expect(worker.taskFunctions.size).toBe(2) | |
6cd5248f | 238 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
239 | worker.taskFunctions.get('fn1') |
240 | ) | |
66979634 JB |
241 | expect(worker.addTaskFunction(DEFAULT_TASK_NAME, fn2)).toStrictEqual({ |
242 | status: false, | |
243 | error: new Error( | |
244 | 'Cannot add a task function with the default reserved name' | |
245 | ) | |
246 | }) | |
2a69b8c5 | 247 | worker.addTaskFunction('fn2', fn2) |
6cd5248f | 248 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
249 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
250 | expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) | |
251 | expect(worker.taskFunctions.size).toBe(3) | |
6cd5248f | 252 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
253 | worker.taskFunctions.get('fn1') |
254 | ) | |
255 | worker.addTaskFunction('fn1', fn1Replacement) | |
6cd5248f | 256 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
257 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
258 | expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) | |
259 | expect(worker.taskFunctions.size).toBe(3) | |
6cd5248f | 260 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
261 | worker.taskFunctions.get('fn1') |
262 | ) | |
263 | }) | |
264 | ||
9eae3c69 | 265 | it('Verify that listTaskFunctionNames() is working', () => { |
c50b93fb JB |
266 | const fn1 = () => { |
267 | return 1 | |
268 | } | |
269 | const fn2 = () => { | |
270 | return 2 | |
271 | } | |
272 | const worker = new ClusterWorker({ fn1, fn2 }) | |
66979634 | 273 | expect(worker.listTaskFunctionNames()).toStrictEqual([ |
6cd5248f JB |
274 | DEFAULT_TASK_NAME, |
275 | 'fn1', | |
276 | 'fn2' | |
277 | ]) | |
c50b93fb JB |
278 | }) |
279 | ||
9eae3c69 | 280 | it('Verify that setDefaultTaskFunction() is working', () => { |
2a69b8c5 JB |
281 | const fn1 = () => { |
282 | return 1 | |
283 | } | |
284 | const fn2 = () => { | |
285 | return 2 | |
286 | } | |
287 | const worker = new ThreadWorker({ fn1, fn2 }) | |
66979634 JB |
288 | expect(worker.setDefaultTaskFunction(0, fn1)).toStrictEqual({ |
289 | status: false, | |
290 | error: new TypeError('name parameter is not a string') | |
291 | }) | |
292 | expect(worker.setDefaultTaskFunction('', fn1)).toStrictEqual({ | |
293 | status: false, | |
294 | error: new TypeError('name parameter is an empty string') | |
295 | }) | |
6cd5248f | 296 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function) |
2a69b8c5 JB |
297 | expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function) |
298 | expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function) | |
299 | expect(worker.taskFunctions.size).toBe(3) | |
6cd5248f | 300 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
301 | worker.taskFunctions.get('fn1') |
302 | ) | |
66979634 JB |
303 | expect(worker.setDefaultTaskFunction(DEFAULT_TASK_NAME)).toStrictEqual({ |
304 | status: false, | |
305 | error: new Error( | |
2a69b8c5 JB |
306 | 'Cannot set the default task function reserved name as the default task function' |
307 | ) | |
66979634 JB |
308 | }) |
309 | expect(worker.setDefaultTaskFunction('fn3')).toStrictEqual({ | |
310 | status: false, | |
311 | error: new Error( | |
6934964f JB |
312 | 'Cannot set the default task function to a non-existing task function' |
313 | ) | |
66979634 | 314 | }) |
2a69b8c5 | 315 | worker.setDefaultTaskFunction('fn1') |
6cd5248f | 316 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
317 | worker.taskFunctions.get('fn1') |
318 | ) | |
319 | worker.setDefaultTaskFunction('fn2') | |
6cd5248f | 320 | expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual( |
2a69b8c5 JB |
321 | worker.taskFunctions.get('fn2') |
322 | ) | |
323 | }) | |
c510fea7 | 324 | }) |