feat: use SMA and SMM for worker tasks usage
[poolifier.git] / tests / worker / abstract-worker.test.js
1 const { expect } = require('expect')
2 const sinon = require('sinon')
3 const { ClusterWorker, KillBehaviors, ThreadWorker } = require('../../lib')
4 const { EMPTY_FUNCTION } = require('../../lib/utils')
5
6 describe('Abstract worker test suite', () => {
7 class StubWorkerWithMainWorker extends ThreadWorker {
8 constructor (fn, opts) {
9 super(fn, opts)
10 this.mainWorker = undefined
11 }
12 }
13
14 afterEach(() => {
15 sinon.restore()
16 })
17
18 it('Verify worker options default values', () => {
19 const worker = new ThreadWorker(() => {})
20 expect(worker.opts.maxInactiveTime).toStrictEqual(60000)
21 expect(worker.opts.killBehavior).toBe(KillBehaviors.SOFT)
22 expect(worker.opts.killHandler).toStrictEqual(EMPTY_FUNCTION)
23 expect(worker.opts.async).toBe(undefined)
24 })
25
26 it('Verify that worker options are set at worker creation', () => {
27 const killHandler = () => {
28 console.info('Worker received kill message')
29 }
30 const worker = new ClusterWorker(() => {}, {
31 maxInactiveTime: 6000,
32 killBehavior: KillBehaviors.HARD,
33 killHandler,
34 async: true
35 })
36 expect(worker.opts.maxInactiveTime).toStrictEqual(6000)
37 expect(worker.opts.killBehavior).toBe(KillBehaviors.HARD)
38 expect(worker.opts.killHandler).toStrictEqual(killHandler)
39 expect(worker.opts.async).toBe(undefined)
40 })
41
42 it('Verify that taskFunctions parameter is mandatory', () => {
43 expect(() => new ClusterWorker()).toThrowError(
44 'taskFunctions parameter is mandatory'
45 )
46 })
47
48 it('Verify that taskFunctions parameter is a function or a plain object', () => {
49 expect(() => new ClusterWorker(0)).toThrowError(
50 new TypeError(
51 'taskFunctions parameter is not a function or a plain object'
52 )
53 )
54 expect(() => new ClusterWorker('')).toThrowError(
55 new TypeError(
56 'taskFunctions parameter is not a function or a plain object'
57 )
58 )
59 expect(() => new ClusterWorker(true)).toThrowError(
60 new TypeError(
61 'taskFunctions parameter is not a function or a plain object'
62 )
63 )
64 expect(() => new ClusterWorker([])).toThrowError(
65 new TypeError(
66 'taskFunctions parameter is not a function or a plain object'
67 )
68 )
69 expect(() => new ClusterWorker(new Map())).toThrowError(
70 new TypeError(
71 'taskFunctions parameter is not a function or a plain object'
72 )
73 )
74 expect(() => new ClusterWorker(new Set())).toThrowError(
75 new TypeError(
76 'taskFunctions parameter is not a function or a plain object'
77 )
78 )
79 expect(() => new ClusterWorker(new WeakMap())).toThrowError(
80 new TypeError(
81 'taskFunctions parameter is not a function or a plain object'
82 )
83 )
84 expect(() => new ClusterWorker(new WeakSet())).toThrowError(
85 new TypeError(
86 'taskFunctions parameter is not a function or a plain object'
87 )
88 )
89 })
90
91 it('Verify that taskFunctions parameter is not an empty object', () => {
92 expect(() => new ClusterWorker({})).toThrowError(
93 new Error('taskFunctions parameter object is empty')
94 )
95 })
96
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
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
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 })
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 )
132 })
133
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
139 worker.getMainWorker = sinon.stub().returns({
140 id: 1,
141 send: sinon.stub().returns()
142 })
143 worker.handleKillMessage()
144 expect(worker.getMainWorker().send.calledOnce).toBe(true)
145 expect(worker.opts.killHandler.calledOnce).toBe(true)
146 })
147
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 })
157
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)
165 })
166
167 it('Verify that getMainWorker() throw error if main worker is not set', () => {
168 expect(() =>
169 new StubWorkerWithMainWorker(() => {}).getMainWorker()
170 ).toThrowError('Main worker not set')
171 })
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 }
232 const worker = new ClusterWorker({ fn1, fn2 })
233 worker.getMainWorker = sinon.stub().returns({
234 id: 1,
235 send: sinon.stub().returns()
236 })
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)
259 expect(worker.getMainWorker().send.calledOnce).toBe(true)
260 })
261
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
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 })
302 })