perf: avoid branching on pool type
[poolifier.git] / tests / utils.test.mjs
1 import { Worker } from 'node:worker_threads'
2 import cluster from 'node:cluster'
3 import os from 'node:os'
4 import { randomInt } from 'node:crypto'
5 import { expect } from 'expect'
6 import {
7 DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
8 DEFAULT_TASK_NAME,
9 EMPTY_FUNCTION,
10 availableParallelism,
11 average,
12 buildInternalWorkerChoiceStrategyOptions,
13 exponentialDelay,
14 getWorkerId,
15 getWorkerType,
16 isAsyncFunction,
17 isKillBehavior,
18 isPlainObject,
19 max,
20 median,
21 min,
22 // once,
23 round,
24 secureRandom,
25 sleep
26 } from '../lib/utils.js'
27 import { KillBehaviors, WorkerTypes } from '../lib/index.js'
28
29 describe('Utils test suite', () => {
30 it('Verify DEFAULT_TASK_NAME value', () => {
31 expect(DEFAULT_TASK_NAME).toBe('default')
32 })
33
34 it('Verify EMPTY_FUNCTION value', () => {
35 expect(EMPTY_FUNCTION).toStrictEqual(expect.any(Function))
36 })
37
38 it('Verify DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS values', () => {
39 expect(DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS).toStrictEqual({
40 aggregate: false,
41 average: false,
42 median: false
43 })
44 })
45
46 it('Verify availableParallelism() behavior', () => {
47 const parallelism = availableParallelism()
48 expect(typeof parallelism === 'number').toBe(true)
49 expect(Number.isSafeInteger(parallelism)).toBe(true)
50 let expectedParallelism = 1
51 try {
52 expectedParallelism = os.availableParallelism()
53 } catch {
54 expectedParallelism = os.cpus().length
55 }
56 expect(parallelism).toBe(expectedParallelism)
57 })
58
59 it('Verify getWorkerType() behavior', () => {
60 expect(
61 getWorkerType(new Worker('./tests/worker-files/thread/testWorker.mjs'))
62 ).toBe(WorkerTypes.thread)
63 expect(getWorkerType(cluster.fork())).toBe(WorkerTypes.cluster)
64 })
65
66 it('Verify getWorkerId() behavior', () => {
67 const threadWorker = new Worker(
68 './tests/worker-files/thread/testWorker.mjs'
69 )
70 const clusterWorker = cluster.fork()
71 expect(getWorkerId(threadWorker)).toBe(threadWorker.threadId)
72 expect(getWorkerId(clusterWorker)).toBe(clusterWorker.id)
73 })
74
75 it('Verify sleep() behavior', async () => {
76 const start = performance.now()
77 const sleepMs = 1000
78 await sleep(sleepMs)
79 const elapsed = performance.now() - start
80 expect(elapsed).toBeGreaterThanOrEqual(sleepMs - 1)
81 })
82
83 it('Verify exponentialDelay() behavior', () => {
84 const delay = exponentialDelay(randomInt(1000))
85 expect(typeof delay === 'number').toBe(true)
86 expect(delay).toBeGreaterThanOrEqual(Number.MIN_VALUE)
87 expect(delay).toBeLessThanOrEqual(Number.MAX_VALUE)
88 })
89
90 it('Verify average() computation', () => {
91 expect(average([])).toBe(0)
92 expect(average([0.08])).toBe(0.08)
93 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(
94 3.1642857142857146
95 )
96 expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(
97 2.8533333333333335
98 )
99 })
100
101 it('Verify median() computation', () => {
102 expect(median([])).toBe(0)
103 expect(median([0.08])).toBe(0.08)
104 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05)
105 expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535)
106 })
107
108 it('Verify round() behavior', () => {
109 expect(round(0)).toBe(0)
110 expect(round(0.5, 0)).toBe(1)
111 expect(round(0.5)).toBe(0.5)
112 expect(round(-0.5, 0)).toBe(-1)
113 expect(round(-0.5)).toBe(-0.5)
114 expect(round(1.005)).toBe(1.01)
115 expect(round(2.175)).toBe(2.18)
116 expect(round(5.015)).toBe(5.02)
117 expect(round(-1.005)).toBe(-1.01)
118 expect(round(-2.175)).toBe(-2.18)
119 expect(round(-5.015)).toBe(-5.02)
120 })
121
122 it('Verify isPlainObject() behavior', () => {
123 expect(isPlainObject(null)).toBe(false)
124 expect(isPlainObject(undefined)).toBe(false)
125 expect(isPlainObject(true)).toBe(false)
126 expect(isPlainObject(false)).toBe(false)
127 expect(isPlainObject(0)).toBe(false)
128 expect(isPlainObject('')).toBe(false)
129 expect(isPlainObject([])).toBe(false)
130 expect(isPlainObject(() => {})).toBe(false)
131 expect(isPlainObject(new Date())).toBe(false)
132 expect(isPlainObject(new RegExp())).toBe(false)
133 expect(isPlainObject(new Error())).toBe(false)
134 expect(isPlainObject(new Map())).toBe(false)
135 expect(isPlainObject(new Set())).toBe(false)
136 expect(isPlainObject(new WeakMap())).toBe(false)
137 expect(isPlainObject(new WeakSet())).toBe(false)
138 expect(isPlainObject(new Int8Array())).toBe(false)
139 expect(isPlainObject(new Uint8Array())).toBe(false)
140 expect(isPlainObject(new Uint8ClampedArray())).toBe(false)
141 expect(isPlainObject(new Int16Array())).toBe(false)
142 expect(isPlainObject(new Uint16Array())).toBe(false)
143 expect(isPlainObject(new Int32Array())).toBe(false)
144 expect(isPlainObject(new Uint32Array())).toBe(false)
145 expect(isPlainObject(new Float32Array())).toBe(false)
146 expect(isPlainObject(new Float64Array())).toBe(false)
147 expect(isPlainObject(new BigInt64Array())).toBe(false)
148 expect(isPlainObject(new BigUint64Array())).toBe(false)
149 expect(isPlainObject(new Promise(() => {}))).toBe(false)
150 expect(isPlainObject(new WeakRef({}))).toBe(false)
151 expect(isPlainObject(new FinalizationRegistry(() => {}))).toBe(false)
152 expect(isPlainObject(new ArrayBuffer())).toBe(false)
153 expect(isPlainObject(new SharedArrayBuffer())).toBe(false)
154 expect(isPlainObject(new DataView(new ArrayBuffer()))).toBe(false)
155 expect(isPlainObject({})).toBe(true)
156 expect(isPlainObject({ a: 1 })).toBe(true)
157 })
158
159 it('Verify isKillBehavior() behavior', () => {
160 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.SOFT)).toBe(true)
161 expect(isKillBehavior(KillBehaviors.SOFT, KillBehaviors.HARD)).toBe(false)
162 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.HARD)).toBe(true)
163 expect(isKillBehavior(KillBehaviors.HARD, KillBehaviors.SOFT)).toBe(false)
164 expect(isKillBehavior(KillBehaviors.SOFT)).toBe(false)
165 expect(isKillBehavior(KillBehaviors.HARD)).toBe(false)
166 expect(isKillBehavior(KillBehaviors.HARD, null)).toBe(false)
167 expect(isKillBehavior(KillBehaviors.HARD, undefined)).toBe(false)
168 expect(isKillBehavior(KillBehaviors.SOFT, 'unknown')).toBe(false)
169 })
170
171 it('Verify isAsyncFunction() behavior', () => {
172 expect(isAsyncFunction(null)).toBe(false)
173 expect(isAsyncFunction(undefined)).toBe(false)
174 expect(isAsyncFunction(true)).toBe(false)
175 expect(isAsyncFunction(false)).toBe(false)
176 expect(isAsyncFunction(0)).toBe(false)
177 expect(isAsyncFunction('')).toBe(false)
178 expect(isAsyncFunction([])).toBe(false)
179 expect(isAsyncFunction(new Date())).toBe(false)
180 expect(isAsyncFunction(new RegExp())).toBe(false)
181 expect(isAsyncFunction(new Error())).toBe(false)
182 expect(isAsyncFunction(new Map())).toBe(false)
183 expect(isAsyncFunction(new Set())).toBe(false)
184 expect(isAsyncFunction(new WeakMap())).toBe(false)
185 expect(isAsyncFunction(new WeakSet())).toBe(false)
186 expect(isAsyncFunction(new Int8Array())).toBe(false)
187 expect(isAsyncFunction(new Uint8Array())).toBe(false)
188 expect(isAsyncFunction(new Uint8ClampedArray())).toBe(false)
189 expect(isAsyncFunction(new Int16Array())).toBe(false)
190 expect(isAsyncFunction(new Uint16Array())).toBe(false)
191 expect(isAsyncFunction(new Int32Array())).toBe(false)
192 expect(isAsyncFunction(new Uint32Array())).toBe(false)
193 expect(isAsyncFunction(new Float32Array())).toBe(false)
194 expect(isAsyncFunction(new Float64Array())).toBe(false)
195 expect(isAsyncFunction(new BigInt64Array())).toBe(false)
196 expect(isAsyncFunction(new BigUint64Array())).toBe(false)
197 expect(isAsyncFunction(new Promise(() => {}))).toBe(false)
198 expect(isAsyncFunction(new WeakRef({}))).toBe(false)
199 expect(isAsyncFunction(new FinalizationRegistry(() => {}))).toBe(false)
200 expect(isAsyncFunction(new ArrayBuffer())).toBe(false)
201 expect(isAsyncFunction(new SharedArrayBuffer())).toBe(false)
202 expect(isAsyncFunction(new DataView(new ArrayBuffer()))).toBe(false)
203 expect(isAsyncFunction({})).toBe(false)
204 expect(isAsyncFunction({ a: 1 })).toBe(false)
205 expect(isAsyncFunction(() => {})).toBe(false)
206 expect(isAsyncFunction(function () {})).toBe(false)
207 expect(isAsyncFunction(function named () {})).toBe(false)
208 expect(isAsyncFunction(async () => {})).toBe(true)
209 expect(isAsyncFunction(async function () {})).toBe(true)
210 expect(isAsyncFunction(async function named () {})).toBe(true)
211 })
212
213 it('Verify secureRandom() behavior', () => {
214 const randomNumber = secureRandom()
215 expect(typeof randomNumber === 'number').toBe(true)
216 expect(randomNumber).toBeGreaterThanOrEqual(0)
217 expect(randomNumber).toBeLessThan(1)
218 })
219
220 it('Verify min() behavior', () => {
221 expect(min()).toBe(Infinity)
222 expect(min(1, 2)).toBe(1)
223 expect(min(2, 1)).toBe(1)
224 expect(min(1, 1)).toBe(1)
225 })
226
227 it('Verify max() behavior', () => {
228 expect(max()).toBe(-Infinity)
229 expect(max(1, 2)).toBe(2)
230 expect(max(2, 1)).toBe(2)
231 expect(max(1, 1)).toBe(1)
232 })
233
234 it('Verify buildInternalWorkerChoiceStrategyOptions() behavior', () => {
235 const poolMaxSize = 10
236 const internalWorkerChoiceStrategyOptions =
237 buildInternalWorkerChoiceStrategyOptions(poolMaxSize)
238 expect(internalWorkerChoiceStrategyOptions).toStrictEqual({
239 retries:
240 poolMaxSize +
241 Object.keys(internalWorkerChoiceStrategyOptions.weights).length,
242 runTime: { median: false },
243 waitTime: { median: false },
244 elu: { median: false },
245 weights: expect.objectContaining({
246 0: expect.any(Number),
247 [poolMaxSize - 1]: expect.any(Number)
248 })
249 })
250 })
251
252 // it('Verify once()', () => {
253 // let called = 0
254 // const fn = () => ++called
255 // const onceFn = once(fn, this)
256 // const result1 = onceFn()
257 // expect(called).toBe(1)
258 // expect(result1).toBe(1)
259 // const result2 = onceFn()
260 // expect(called).toBe(1)
261 // expect(result2).toBe(1)
262 // const result3 = onceFn()
263 // expect(called).toBe(1)
264 // expect(result3).toBe(1)
265 // })
266 })