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