199 lines
7.5 KiB
TypeScript
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));
|
|
});
|
|
});
|