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