refactor: use more ramdba helpers
[e-mobility-charging-stations-simulator.git] / tests / utils / Utils.test.ts
CommitLineData
8f801a93 1import { version } from 'node:process'
66a7748d 2import { describe, it } from 'node:test'
6c43b441 3
66a7748d
JB
4import { hoursToMilliseconds, hoursToSeconds } from 'date-fns'
5import { expect } from 'expect'
8f801a93 6import { satisfies } from 'semver'
a2111e8d 7
66a7748d 8import { Constants } from '../../src/utils/Constants.js'
9bf0ef23 9import {
40615072 10 clone,
9bf0ef23
JB
11 convertToBoolean,
12 convertToDate,
13 convertToFloat,
14 convertToInt,
1553f6df 15 extractTimeSeriesValues,
be4c6702 16 formatDurationMilliSeconds,
8053c842 17 formatDurationSeconds,
9bf0ef23
JB
18 generateUUID,
19 getRandomFloat,
20 getRandomInteger,
21 hasOwnProp,
80c58041 22 isArraySorted,
bcf95df1 23 isAsyncFunction,
9bf0ef23
JB
24 isNotEmptyArray,
25 isNotEmptyString,
9bf0ef23 26 isObject,
5dc7c990 27 isValidDate,
9bf0ef23
JB
28 roundTo,
29 secureRandom,
30 sleep,
66a7748d
JB
31 validateUUID
32} from '../../src/utils/Utils.js'
a2111e8d 33
6c43b441
JB
34await describe('Utils test suite', async () => {
35 await it('Verify generateUUID()/validateUUID()', () => {
66a7748d
JB
36 const uuid = generateUUID()
37 expect(uuid).toBeDefined()
38 expect(uuid.length).toEqual(36)
39 expect(validateUUID(uuid)).toBe(true)
40 expect(validateUUID('abcdef00-0000-4000-0000-000000000000')).toBe(true)
41 expect(validateUUID('')).toBe(false)
878e026c 42 // Shall invalidate Nil UUID
66a7748d
JB
43 expect(validateUUID('00000000-0000-0000-0000-000000000000')).toBe(false)
44 expect(validateUUID('987FBC9-4BED-3078-CF07A-9141BA07C9F3')).toBe(false)
45 })
8bd02502 46
6c43b441 47 await it('Verify sleep()', async () => {
66a7748d
JB
48 const start = performance.now()
49 await sleep(1000)
50 const stop = performance.now()
51 expect(stop - start).toBeGreaterThanOrEqual(1000)
52 })
45999aab 53
6c43b441 54 await it('Verify formatDurationMilliSeconds()', () => {
66a7748d
JB
55 expect(formatDurationMilliSeconds(0)).toBe('0 seconds')
56 expect(formatDurationMilliSeconds(900)).toBe('0 seconds')
57 expect(formatDurationMilliSeconds(1000)).toBe('1 second')
58 expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours')
59 })
be4c6702 60
6c43b441 61 await it('Verify formatDurationSeconds()', () => {
66a7748d
JB
62 expect(formatDurationSeconds(0)).toBe('0 seconds')
63 expect(formatDurationSeconds(0.9)).toBe('0 seconds')
64 expect(formatDurationSeconds(1)).toBe('1 second')
65 expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours')
66 })
8053c842 67
40615072 68 await it('Verify isValidDate()', () => {
5dc7c990
JB
69 expect(isValidDate(undefined)).toBe(false)
70 expect(isValidDate(-1)).toBe(true)
71 expect(isValidDate(0)).toBe(true)
72 expect(isValidDate(1)).toBe(true)
73 expect(isValidDate(-0.5)).toBe(true)
74 expect(isValidDate(0.5)).toBe(true)
75 expect(isValidDate(new Date())).toBe(true)
66a7748d 76 })
ac8178a4 77
6c43b441 78 await it('Verify convertToDate()', () => {
66a7748d 79 expect(convertToDate(undefined)).toBe(undefined)
79fd697f 80 expect(convertToDate(null)).toBe(undefined)
66a7748d
JB
81 expect(() => convertToDate('')).toThrow(new Error("Cannot convert to date: ''"))
82 expect(() => convertToDate('00:70:61')).toThrow(new Error("Cannot convert to date: '00:70:61'"))
83 expect(convertToDate(0)).toStrictEqual(new Date('1970-01-01T00:00:00.000Z'))
84 expect(convertToDate(-1)).toStrictEqual(new Date('1969-12-31T23:59:59.999Z'))
85 const dateStr = '2020-01-01T00:00:00.000Z'
86 let date = convertToDate(dateStr)
87 expect(date).toBeInstanceOf(Date)
88 expect(date).toStrictEqual(new Date(dateStr))
89 date = convertToDate(new Date(dateStr))
90 expect(date).toBeInstanceOf(Date)
91 expect(date).toStrictEqual(new Date(dateStr))
92 })
df645d8f 93
6c43b441 94 await it('Verify convertToInt()', () => {
66a7748d
JB
95 expect(convertToInt(undefined)).toBe(0)
96 expect(convertToInt(null)).toBe(0)
97 expect(convertToInt(0)).toBe(0)
98 const randomInteger = getRandomInteger()
99 expect(convertToInt(randomInteger)).toEqual(randomInteger)
100 expect(convertToInt('-1')).toBe(-1)
101 expect(convertToInt('1')).toBe(1)
102 expect(convertToInt('1.1')).toBe(1)
103 expect(convertToInt('1.9')).toBe(1)
104 expect(convertToInt('1.999')).toBe(1)
105 expect(convertToInt(-1)).toBe(-1)
106 expect(convertToInt(1)).toBe(1)
107 expect(convertToInt(1.1)).toBe(1)
108 expect(convertToInt(1.9)).toBe(1)
109 expect(convertToInt(1.999)).toBe(1)
878e026c 110 expect(() => {
66a7748d
JB
111 convertToInt('NaN')
112 }).toThrow("Cannot convert to integer: 'NaN'")
113 })
df645d8f 114
6c43b441 115 await it('Verify convertToFloat()', () => {
66a7748d
JB
116 expect(convertToFloat(undefined)).toBe(0)
117 expect(convertToFloat(null)).toBe(0)
118 expect(convertToFloat(0)).toBe(0)
119 const randomFloat = getRandomFloat()
120 expect(convertToFloat(randomFloat)).toEqual(randomFloat)
121 expect(convertToFloat('-1')).toBe(-1)
122 expect(convertToFloat('1')).toBe(1)
123 expect(convertToFloat('1.1')).toBe(1.1)
124 expect(convertToFloat('1.9')).toBe(1.9)
125 expect(convertToFloat('1.999')).toBe(1.999)
126 expect(convertToFloat(-1)).toBe(-1)
127 expect(convertToFloat(1)).toBe(1)
128 expect(convertToFloat(1.1)).toBe(1.1)
129 expect(convertToFloat(1.9)).toBe(1.9)
130 expect(convertToFloat(1.999)).toBe(1.999)
878e026c 131 expect(() => {
66a7748d
JB
132 convertToFloat('NaN')
133 }).toThrow("Cannot convert to float: 'NaN'")
134 })
df645d8f 135
6c43b441 136 await it('Verify convertToBoolean()', () => {
66a7748d
JB
137 expect(convertToBoolean(undefined)).toBe(false)
138 expect(convertToBoolean(null)).toBe(false)
139 expect(convertToBoolean('true')).toBe(true)
140 expect(convertToBoolean('false')).toBe(false)
141 expect(convertToBoolean('TRUE')).toBe(true)
142 expect(convertToBoolean('FALSE')).toBe(false)
143 expect(convertToBoolean('1')).toBe(true)
144 expect(convertToBoolean('0')).toBe(false)
145 expect(convertToBoolean(1)).toBe(true)
146 expect(convertToBoolean(0)).toBe(false)
147 expect(convertToBoolean(true)).toBe(true)
148 expect(convertToBoolean(false)).toBe(false)
149 expect(convertToBoolean('')).toBe(false)
150 expect(convertToBoolean('NoNBoolean')).toBe(false)
151 })
df645d8f 152
6c43b441 153 await it('Verify secureRandom()', () => {
66a7748d
JB
154 const random = secureRandom()
155 expect(typeof random === 'number').toBe(true)
156 expect(random).toBeGreaterThanOrEqual(0)
157 expect(random).toBeLessThan(1)
158 })
a2111e8d 159
6c43b441 160 await it('Verify getRandomInteger()', () => {
66a7748d
JB
161 let randomInteger = getRandomInteger()
162 expect(Number.isSafeInteger(randomInteger)).toBe(true)
163 expect(randomInteger).toBeGreaterThanOrEqual(0)
164 expect(randomInteger).toBeLessThanOrEqual(Constants.MAX_RANDOM_INTEGER)
165 expect(randomInteger).not.toEqual(getRandomInteger())
166 randomInteger = getRandomInteger(0, -Constants.MAX_RANDOM_INTEGER)
167 expect(randomInteger).toBeGreaterThanOrEqual(-Constants.MAX_RANDOM_INTEGER)
168 expect(randomInteger).toBeLessThanOrEqual(0)
68f56fdf 169 expect(() => getRandomInteger(0, 1)).toThrow(
66a7748d
JB
170 'The value of "max" is out of range. It must be greater than the value of "min" (1). Received 1'
171 )
68f56fdf 172 expect(() => getRandomInteger(-1)).toThrow(
66a7748d
JB
173 'The value of "max" is out of range. It must be greater than the value of "min" (0). Received 0'
174 )
68f56fdf 175 expect(() => getRandomInteger(Constants.MAX_RANDOM_INTEGER + 1)).toThrow(
878e026c
JB
176 `The value of "max" is out of range. It must be <= ${
177 Constants.MAX_RANDOM_INTEGER + 1
66a7748d
JB
178 }. Received 281_474_976_710_656`
179 )
180 randomInteger = getRandomInteger(2, 1)
181 expect(randomInteger).toBeGreaterThanOrEqual(1)
182 expect(randomInteger).toBeLessThanOrEqual(2)
183 const maximum = 2.2
184 const minimum = 1.1
185 randomInteger = getRandomInteger(maximum, minimum)
186 expect(randomInteger).toBeLessThanOrEqual(Math.floor(maximum))
187 expect(randomInteger).toBeGreaterThanOrEqual(Math.ceil(minimum))
188 })
a2111e8d 189
6c43b441 190 await it('Verify roundTo()', () => {
66a7748d
JB
191 expect(roundTo(0, 2)).toBe(0)
192 expect(roundTo(0.5, 0)).toBe(1)
193 expect(roundTo(0.5, 2)).toBe(0.5)
194 expect(roundTo(-0.5, 0)).toBe(-1)
195 expect(roundTo(-0.5, 2)).toBe(-0.5)
196 expect(roundTo(1.005, 0)).toBe(1)
197 expect(roundTo(1.005, 2)).toBe(1.01)
198 expect(roundTo(2.175, 2)).toBe(2.18)
199 expect(roundTo(5.015, 2)).toBe(5.02)
200 expect(roundTo(-1.005, 2)).toBe(-1.01)
201 expect(roundTo(-2.175, 2)).toBe(-2.18)
202 expect(roundTo(-5.015, 2)).toBe(-5.02)
203 })
316d1564 204
6c43b441 205 await it('Verify getRandomFloat()', () => {
66a7748d
JB
206 let randomFloat = getRandomFloat()
207 expect(typeof randomFloat === 'number').toBe(true)
208 expect(randomFloat).toBeGreaterThanOrEqual(0)
209 expect(randomFloat).toBeLessThanOrEqual(Number.MAX_VALUE)
210 expect(randomFloat).not.toEqual(getRandomFloat())
211 expect(() => getRandomFloat(0, 1)).toThrow(new RangeError('Invalid interval'))
68f56fdf 212 expect(() => getRandomFloat(Number.MAX_VALUE, -Number.MAX_VALUE)).toThrow(
66a7748d
JB
213 new RangeError('Invalid interval')
214 )
215 randomFloat = getRandomFloat(0, -Number.MAX_VALUE)
216 expect(randomFloat).toBeGreaterThanOrEqual(-Number.MAX_VALUE)
217 expect(randomFloat).toBeLessThanOrEqual(0)
218 })
a2111e8d 219
6c43b441 220 await it('Verify extractTimeSeriesValues()', () => {
66a7748d
JB
221 expect(extractTimeSeriesValues([])).toEqual([])
222 expect(extractTimeSeriesValues([{ timestamp: Date.now(), value: 1.1 }])).toEqual([1.1])
1553f6df
JB
223 expect(
224 extractTimeSeriesValues([
225 { timestamp: Date.now(), value: 1.1 },
66a7748d
JB
226 { timestamp: Date.now(), value: 2.2 }
227 ])
228 ).toEqual([1.1, 2.2])
229 })
1553f6df 230
6c43b441 231 await it('Verify isObject()', () => {
66a7748d
JB
232 expect(isObject('test')).toBe(false)
233 expect(isObject(undefined)).toBe(false)
234 expect(isObject(null)).toBe(false)
235 expect(isObject(0)).toBe(false)
236 expect(isObject([])).toBe(false)
237 expect(isObject([0, 1])).toBe(false)
238 expect(isObject(['0', '1'])).toBe(false)
239 expect(isObject({})).toBe(true)
240 expect(isObject({ 1: 1 })).toBe(true)
241 expect(isObject({ 1: '1' })).toBe(true)
242 expect(isObject(new Map())).toBe(true)
243 expect(isObject(new Set())).toBe(true)
244 expect(isObject(new WeakMap())).toBe(true)
245 expect(isObject(new WeakSet())).toBe(true)
246 })
a2111e8d 247
bcf95df1
JB
248 await it('Verify isAsyncFunction()', () => {
249 expect(isAsyncFunction(null)).toBe(false)
250 expect(isAsyncFunction(undefined)).toBe(false)
251 expect(isAsyncFunction(true)).toBe(false)
252 expect(isAsyncFunction(false)).toBe(false)
253 expect(isAsyncFunction(0)).toBe(false)
254 expect(isAsyncFunction('')).toBe(false)
255 expect(isAsyncFunction([])).toBe(false)
256 expect(isAsyncFunction(new Date())).toBe(false)
257 // eslint-disable-next-line prefer-regex-literals
258 expect(isAsyncFunction(new RegExp('[a-z]', 'i'))).toBe(false)
259 expect(isAsyncFunction(new Error())).toBe(false)
260 expect(isAsyncFunction(new Map())).toBe(false)
261 expect(isAsyncFunction(new Set())).toBe(false)
262 expect(isAsyncFunction(new WeakMap())).toBe(false)
263 expect(isAsyncFunction(new WeakSet())).toBe(false)
264 expect(isAsyncFunction(new Int8Array())).toBe(false)
265 expect(isAsyncFunction(new Uint8Array())).toBe(false)
266 expect(isAsyncFunction(new Uint8ClampedArray())).toBe(false)
267 expect(isAsyncFunction(new Int16Array())).toBe(false)
268 expect(isAsyncFunction(new Uint16Array())).toBe(false)
269 expect(isAsyncFunction(new Int32Array())).toBe(false)
270 expect(isAsyncFunction(new Uint32Array())).toBe(false)
271 expect(isAsyncFunction(new Float32Array())).toBe(false)
272 expect(isAsyncFunction(new Float64Array())).toBe(false)
273 expect(isAsyncFunction(new BigInt64Array())).toBe(false)
274 expect(isAsyncFunction(new BigUint64Array())).toBe(false)
275 // eslint-disable-next-line @typescript-eslint/no-empty-function
276 expect(isAsyncFunction(new Promise(() => {}))).toBe(false)
277 expect(isAsyncFunction(new WeakRef({}))).toBe(false)
278 // eslint-disable-next-line @typescript-eslint/no-empty-function
279 expect(isAsyncFunction(new FinalizationRegistry(() => {}))).toBe(false)
280 expect(isAsyncFunction(new ArrayBuffer(16))).toBe(false)
281 expect(isAsyncFunction(new SharedArrayBuffer(16))).toBe(false)
282 expect(isAsyncFunction(new DataView(new ArrayBuffer(16)))).toBe(false)
283 expect(isAsyncFunction({})).toBe(false)
284 expect(isAsyncFunction({ a: 1 })).toBe(false)
285 // eslint-disable-next-line @typescript-eslint/no-empty-function
286 expect(isAsyncFunction(() => {})).toBe(false)
287 // eslint-disable-next-line @typescript-eslint/no-empty-function
288 expect(isAsyncFunction(function () {})).toBe(false)
289 // eslint-disable-next-line @typescript-eslint/no-empty-function
290 expect(isAsyncFunction(function named () {})).toBe(false)
291 // eslint-disable-next-line @typescript-eslint/no-empty-function
292 expect(isAsyncFunction(async () => {})).toBe(true)
293 // eslint-disable-next-line @typescript-eslint/no-empty-function
294 expect(isAsyncFunction(async function () {})).toBe(true)
295 // eslint-disable-next-line @typescript-eslint/no-empty-function
296 expect(isAsyncFunction(async function named () {})).toBe(true)
297 class TestClass {
298 // eslint-disable-next-line @typescript-eslint/no-empty-function
299 public testSync (): void {}
300 // eslint-disable-next-line @typescript-eslint/no-empty-function
301 public async testAsync (): Promise<void> {}
302 // eslint-disable-next-line @typescript-eslint/no-empty-function
303 public testArrowSync = (): void => {}
304 // eslint-disable-next-line @typescript-eslint/no-empty-function
305 public testArrowAsync = async (): Promise<void> => {}
306 // eslint-disable-next-line @typescript-eslint/no-empty-function
307 public static testStaticSync (): void {}
308 // eslint-disable-next-line @typescript-eslint/no-empty-function
309 public static async testStaticAsync (): Promise<void> {}
310 }
311 const testClass = new TestClass()
312 // eslint-disable-next-line @typescript-eslint/unbound-method
313 expect(isAsyncFunction(testClass.testSync)).toBe(false)
314 // eslint-disable-next-line @typescript-eslint/unbound-method
315 expect(isAsyncFunction(testClass.testAsync)).toBe(true)
316 expect(isAsyncFunction(testClass.testArrowSync)).toBe(false)
317 expect(isAsyncFunction(testClass.testArrowAsync)).toBe(true)
318 // eslint-disable-next-line @typescript-eslint/unbound-method
319 expect(isAsyncFunction(TestClass.testStaticSync)).toBe(false)
320 // eslint-disable-next-line @typescript-eslint/unbound-method
321 expect(isAsyncFunction(TestClass.testStaticAsync)).toBe(true)
322 })
323
40615072 324 await it('Verify clone()', () => {
66a7748d 325 const obj = { 1: 1 }
40615072
JB
326 expect(clone(obj)).toStrictEqual(obj)
327 expect(clone(obj) === obj).toBe(false)
66a7748d 328 const nestedObj = { 1: obj, 2: obj }
40615072
JB
329 expect(clone(nestedObj)).toStrictEqual(nestedObj)
330 expect(clone(nestedObj) === nestedObj).toBe(false)
66a7748d 331 const array = [1, 2]
40615072
JB
332 expect(clone(array)).toStrictEqual(array)
333 expect(clone(array) === array).toBe(false)
66a7748d 334 const objArray = [obj, obj]
40615072
JB
335 expect(clone(objArray)).toStrictEqual(objArray)
336 expect(clone(objArray) === objArray).toBe(false)
66a7748d 337 const date = new Date()
40615072
JB
338 expect(clone(date)).toStrictEqual(date)
339 expect(clone(date) === date).toBe(false)
8f801a93
JB
340 if (satisfies(version, '>=21.0.0')) {
341 const url = new URL('https://domain.tld')
342 expect(() => clone(url)).toThrowError(new Error('Cannot clone object of unsupported type.'))
343 }
66a7748d 344 const map = new Map([['1', '2']])
3fad0dec 345 expect(clone(map)).toStrictEqual(map)
cfab8dc9 346 expect(clone(map) === map).toBe(false)
66a7748d 347 const set = new Set(['1'])
3fad0dec 348 expect(clone(set)).toStrictEqual(set)
cfab8dc9 349 expect(clone(set) === set).toBe(false)
66a7748d 350 const weakMap = new WeakMap([[{ 1: 1 }, { 2: 2 }]])
8f801a93 351 expect(() => clone(weakMap)).toThrowError(new Error('#<WeakMap> could not be cloned.'))
66a7748d 352 const weakSet = new WeakSet([{ 1: 1 }, { 2: 2 }])
8f801a93 353 expect(() => clone(weakSet)).toThrowError(new Error('#<WeakSet> could not be cloned.'))
66a7748d 354 })
dd5a1d6c 355
6c43b441 356 await it('Verify hasOwnProp()', () => {
66a7748d
JB
357 expect(hasOwnProp('test', '')).toBe(false)
358 expect(hasOwnProp(undefined, '')).toBe(false)
359 expect(hasOwnProp(null, '')).toBe(false)
360 expect(hasOwnProp([], '')).toBe(false)
361 expect(hasOwnProp({}, '')).toBe(false)
362 expect(hasOwnProp({ 1: 1 }, 1)).toBe(true)
363 expect(hasOwnProp({ 1: 1 }, '1')).toBe(true)
364 expect(hasOwnProp({ 1: 1 }, 2)).toBe(false)
365 expect(hasOwnProp({ 1: 1 }, '2')).toBe(false)
366 expect(hasOwnProp({ 1: '1' }, '1')).toBe(true)
367 expect(hasOwnProp({ 1: '1' }, 1)).toBe(true)
368 expect(hasOwnProp({ 1: '1' }, '2')).toBe(false)
369 expect(hasOwnProp({ 1: '1' }, 2)).toBe(false)
370 })
af29b598 371
6c43b441 372 await it('Verify isNotEmptyString()', () => {
66a7748d
JB
373 expect(isNotEmptyString('')).toBe(false)
374 expect(isNotEmptyString(' ')).toBe(false)
375 expect(isNotEmptyString(' ')).toBe(false)
376 expect(isNotEmptyString('test')).toBe(true)
377 expect(isNotEmptyString(' test')).toBe(true)
378 expect(isNotEmptyString('test ')).toBe(true)
379 expect(isNotEmptyString(undefined)).toBe(false)
380 expect(isNotEmptyString(null)).toBe(false)
381 expect(isNotEmptyString(0)).toBe(false)
382 expect(isNotEmptyString({})).toBe(false)
383 expect(isNotEmptyString([])).toBe(false)
384 expect(isNotEmptyString(new Map())).toBe(false)
385 expect(isNotEmptyString(new Set())).toBe(false)
386 expect(isNotEmptyString(new WeakMap())).toBe(false)
387 expect(isNotEmptyString(new WeakSet())).toBe(false)
388 })
429f8c9d 389
6c43b441 390 await it('Verify isNotEmptyArray()', () => {
66a7748d
JB
391 expect(isNotEmptyArray([])).toBe(false)
392 expect(isNotEmptyArray([1, 2])).toBe(true)
393 expect(isNotEmptyArray(['1', '2'])).toBe(true)
394 expect(isNotEmptyArray(undefined)).toBe(false)
395 expect(isNotEmptyArray(null)).toBe(false)
396 expect(isNotEmptyArray('')).toBe(false)
397 expect(isNotEmptyArray('test')).toBe(false)
398 expect(isNotEmptyArray(0)).toBe(false)
399 expect(isNotEmptyArray({})).toBe(false)
400 expect(isNotEmptyArray(new Map())).toBe(false)
401 expect(isNotEmptyArray(new Set())).toBe(false)
402 expect(isNotEmptyArray(new WeakMap())).toBe(false)
403 expect(isNotEmptyArray(new WeakSet())).toBe(false)
404 })
878e026c 405
6c43b441 406 await it('Verify isArraySorted()', () => {
80c58041
JB
407 expect(
408 isArraySorted([], (a, b) => {
66a7748d
JB
409 return a - b
410 })
411 ).toBe(true)
80c58041
JB
412 expect(
413 isArraySorted([1], (a, b) => {
66a7748d
JB
414 return a - b
415 })
416 ).toBe(true)
417 expect(isArraySorted<number>([1, 2, 3, 4, 5], (a, b) => a - b)).toBe(true)
418 expect(isArraySorted<number>([1, 2, 3, 5, 4], (a, b) => a - b)).toBe(false)
419 expect(isArraySorted<number>([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false)
420 })
66a7748d 421})