monitor/core/modules/Metrics/statsUtils.test.ts
2025-04-16 22:30:27 +07:00

199 lines
7.5 KiB
TypeScript

//@ts-nocheck
import { test, expect, suite, it } from 'vitest';
import {
MultipleCounter,
QuantileArray,
TimeCounter,
estimateArrayJsonSize,
isWithinMargin,
} from './statsUtils';
suite('MultipleCounter', () => {
it('should instantiate empty correctly', () => {
const counter = new MultipleCounter();
expect(counter).toBeInstanceOf(MultipleCounter);
expect(counter.toArray()).toEqual([]);
expect(counter.toJSON()).toEqual({});
});
it('should instantiate locked if specified', () => {
const lockedCounter = new MultipleCounter(undefined, true);
expect(() => lockedCounter.count('a')).toThrowError('is locked');
expect(() => lockedCounter.clear()).toThrowError('is locked');
});
it('should handle instantiation data error', () => {
expect(() => new MultipleCounter({ a: 'b' as any })).toThrowError('only integer');
});
const counterWithData = new MultipleCounter({ a: 1, b: 2 });
it('should instantiate with object correctly', () => {
expect(counterWithData.toArray()).toEqual([['a', 1], ['b', 2]]);
expect(counterWithData.toJSON()).toEqual({ a: 1, b: 2 });
});
it('should count and clear', () => {
counterWithData.count('a');
expect(counterWithData.toJSON()).toEqual({ a: 2, b: 2 });
counterWithData.count('b');
counterWithData.count('c', 5);
expect(counterWithData.toJSON()).toEqual({ a: 2, b: 3, c: 5 });
counterWithData.clear();
expect(counterWithData.toJSON()).toEqual({});
});
it('should sort the data', () => {
const counter = new MultipleCounter({ a: 3, z: 1, c: 2 });
expect(counter.toSortedKeyObject()).toEqual({ a: 3, c: 2, z: 1 });
expect(counter.toSortedKeyObject(true)).toEqual({ z: 1, c: 2, a: 3 });
expect(counter.toSortedValuesObject()).toEqual({ a: 3, c: 2, z: 1 });
expect(counter.toSortedValuesObject(true)).toEqual({ z: 1, c: 2, a: 3 });
expect(counter.toSortedKeysArray()).toEqual([['a', 3], ['c', 2], ['z', 1]]);
expect(counter.toSortedKeysArray(true)).toEqual([['z', 1], ['c', 2], ['a', 3]]);
expect(counter.toSortedValuesArray()).toEqual([['z', 1], ['c', 2], ['a', 3]]);
expect(counter.toSortedValuesArray(true)).toEqual([['a', 3], ['c', 2], ['z', 1]]);
});
suite('should handle merging counters', () => {
it('with another counter', () => {
const ogCounter = new MultipleCounter({ a: 1, b: 2 });
const newCounter = new MultipleCounter({ b: 3, c: 4 });
ogCounter.merge(newCounter);
expect(ogCounter.toJSON()).toEqual({ a: 1, b: 5, c: 4 });
});
it('with an array', () => {
const ogCounter = new MultipleCounter({ a: 1, b: 2 });
ogCounter.merge([['b', 3], ['c', 4]]);
expect(ogCounter.toJSON()).toEqual({ a: 1, b: 5, c: 4 });
});
it('with an object', () => {
const ogCounter = new MultipleCounter({ a: 1, b: 2 });
ogCounter.merge({ b: 3, c: 4 });
expect(ogCounter.toJSON()).toEqual({ a: 1, b: 5, c: 4 });
});
it('with invalid data', () => {
const ogCounter = new MultipleCounter();
expect(() => ogCounter.merge('a' as any)).toThrowError('Invalid data type for merge');
});
});
});
suite('QuantileArray', () => {
const array = new QuantileArray(4, 2);
test('min data', () => {
array.count(0);
expect(array.result()).toEqual({ enoughData: false });
});
test('zeros only', () => {
array.count(0);
array.count(0);
array.count(0);
expect(array.result()).toEqual({
enoughData: true,
count: 4,
p5: 0,
p25: 0,
p50: 0,
p75: 0,
p95: 0,
});
});
const repeatedExpectedResult = {
enoughData: true,
count: 4,
p5: 0,
p25: 0,
p50: 0.5,
p75: 1,
p95: 1,
}
test('calc quantile', () => {
array.count(1);
array.count(1);
expect(array.result()).toEqual(repeatedExpectedResult);
});
test('summary', () => {
expect(array.resultSummary('ms')).toEqual({
...repeatedExpectedResult,
summary: 'p5:0ms/p25:0ms/p50:1ms/p75:1ms/p95:1ms (x4)',
});
expect(array.resultSummary()).toEqual({
...repeatedExpectedResult,
summary: 'p5:0/p25:0/p50:1/p75:1/p95:1 (x4)',
});
});
test('clear', () => {
array.clear();
expect(array.result()).toEqual({ enoughData: false });
expect(array.resultSummary()).toEqual({
enoughData: false,
summary: 'not enough data available',
});
});
});
suite('TimeCounter', async () => {
const counter = new TimeCounter();
await new Promise((resolve) => setTimeout(resolve, 150));
const duration = counter.stop();
// Check if the duration is a valid object
test('duration is valid', () => {
expect(duration.seconds).toBeTypeOf('number');
expect(duration.milliseconds).toBeTypeOf('number');
expect(duration.nanoseconds).toBeTypeOf('number');
expect(counter.toJSON()).toEqual(duration);
});
// Check if the duration is within the expected range
test('duration within range', () => {
const isCloseTo50ms = (x: number) => (x > 150 && x < 175);
expect(duration.seconds * 1000).toSatisfy(isCloseTo50ms);
expect(duration.milliseconds).toSatisfy(isCloseTo50ms);
expect(duration.nanoseconds / 1_000_000).toSatisfy(isCloseTo50ms);
});
});
suite('estimateArrayJsonSize', () => {
test('obeys minimas', () => {
const result = estimateArrayJsonSize([], 1);
expect(result).toEqual({ enoughData: false });
const result2 = estimateArrayJsonSize([1], 2);
expect(result2).toEqual({ enoughData: false });
});
test('calculates size correctly', () => {
const array = Array.from({ length: 1000 }, (_, i) => ({ id: i, value: `value${i}` }));
const realFullSize = JSON.stringify(array).length;
const realElementSize = realFullSize / array.length;
const result = estimateArrayJsonSize(array, 100);
expect(result.enoughData).toBe(true);
expect(result.bytesTotal).toSatisfy((x: number) => isWithinMargin(x, realFullSize, 0.1));
expect(result.bytesPerElement).toSatisfy((x: number) => isWithinMargin(x, realElementSize, 0.1));
});
test('handles small arrays', () => {
const array = [{ id: 1, value: 'value1' }];
const result = estimateArrayJsonSize(array, 0);
expect(result.enoughData).toBe(true);
expect(result.bytesTotal).toBeGreaterThan(0);
expect(result.bytesTotal).toBeLessThan(100);
expect(result.bytesPerElement).toBeGreaterThan(0);
expect(result.bytesTotal).toBeLessThan(100);
});
test('handles large arrays', () => {
const array = Array.from({ length: 20000 }, (_, i) => ({ id: i, value: `value${i}` }));
const realFullSize = JSON.stringify(array).length;
const realElementSize = realFullSize / array.length;
const result = estimateArrayJsonSize(array, 100);
expect(result.enoughData).toBe(true);
expect(result.bytesTotal).toSatisfy((x: number) => isWithinMargin(x, realFullSize, 0.1));
expect(result.bytesPerElement).toSatisfy((x: number) => isWithinMargin(x, realElementSize, 0.1));
});
});