Massive v2 rewrite
This commit is contained in:
parent
1025f3b523
commit
5f1328f626
77 changed files with 28105 additions and 3542 deletions
270
.tests/time.test.js
Normal file
270
.tests/time.test.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import { parseDuration, formatDuration, isValidDurationString } from '../dist/utils/time.js';
|
||||
|
||||
describe('Time utilities', () => {
|
||||
describe('parseDuration', () => {
|
||||
describe('numeric inputs', () => {
|
||||
test('should parse positive numbers as milliseconds', () => {
|
||||
expect(parseDuration(1000)).toBe(1000);
|
||||
expect(parseDuration(5000)).toBe(5000);
|
||||
expect(parseDuration(0)).toBe(0);
|
||||
});
|
||||
|
||||
test('should throw error for negative numbers', () => {
|
||||
expect(() => parseDuration(-1000)).toThrow('Duration cannot be negative');
|
||||
expect(() => parseDuration(-1)).toThrow('Duration cannot be negative');
|
||||
});
|
||||
|
||||
test('should throw error for numbers exceeding MAX_SAFE_INTEGER', () => {
|
||||
expect(() => parseDuration(Number.MAX_SAFE_INTEGER + 1)).toThrow('Duration too large');
|
||||
});
|
||||
});
|
||||
|
||||
describe('string inputs', () => {
|
||||
describe('valid duration strings', () => {
|
||||
test('should parse seconds correctly', () => {
|
||||
expect(parseDuration('5s')).toBe(5000);
|
||||
expect(parseDuration('30s')).toBe(30000);
|
||||
expect(parseDuration('1s')).toBe(1000);
|
||||
});
|
||||
|
||||
test('should parse minutes correctly', () => {
|
||||
expect(parseDuration('5m')).toBe(300000);
|
||||
expect(parseDuration('1m')).toBe(60000);
|
||||
expect(parseDuration('10m')).toBe(600000);
|
||||
});
|
||||
|
||||
test('should parse hours correctly', () => {
|
||||
expect(parseDuration('1h')).toBe(3600000);
|
||||
expect(parseDuration('2h')).toBe(7200000);
|
||||
});
|
||||
|
||||
test('should parse days correctly', () => {
|
||||
expect(parseDuration('1d')).toBe(86400000);
|
||||
expect(parseDuration('2d')).toBe(172800000);
|
||||
});
|
||||
|
||||
test('should parse decimal values', () => {
|
||||
expect(parseDuration('1.5s')).toBe(1500);
|
||||
expect(parseDuration('2.5m')).toBe(150000);
|
||||
});
|
||||
|
||||
test('should be case-insensitive for units', () => {
|
||||
expect(parseDuration('5S')).toBe(5000);
|
||||
expect(parseDuration('5M')).toBe(300000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('numeric strings', () => {
|
||||
test('should parse numeric strings as milliseconds', () => {
|
||||
expect(parseDuration('1000')).toBe(1000);
|
||||
expect(parseDuration('5000')).toBe(5000);
|
||||
});
|
||||
|
||||
test('should throw error for negative numeric strings', () => {
|
||||
expect(() => parseDuration('-1000')).toThrow('Duration cannot be negative');
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid inputs', () => {
|
||||
test('should throw error for empty string', () => {
|
||||
expect(() => parseDuration('')).toThrow('Duration cannot be empty');
|
||||
expect(() => parseDuration(' ')).toThrow('Duration cannot be empty');
|
||||
});
|
||||
|
||||
test('should throw error for invalid format', () => {
|
||||
expect(() => parseDuration('abc')).toThrow('Invalid duration format');
|
||||
expect(() => parseDuration('5x')).toThrow('Invalid duration format');
|
||||
expect(() => parseDuration('s5')).toThrow('Invalid duration format');
|
||||
});
|
||||
|
||||
test('should throw error for negative duration values', () => {
|
||||
expect(() => parseDuration('-5s')).toThrow('Invalid duration format');
|
||||
expect(() => parseDuration('-10m')).toThrow('Invalid duration format');
|
||||
});
|
||||
|
||||
describe('edge case validation', () => {
|
||||
test('should handle duration strings that exceed MAX_SAFE_INTEGER when multiplied', () => {
|
||||
// Test a very large number that when multiplied by day multiplier exceeds MAX_SAFE_INTEGER
|
||||
const largeValue = Math.floor(Number.MAX_SAFE_INTEGER / (24 * 60 * 60 * 1000)) + 1;
|
||||
expect(() => parseDuration(`${largeValue}d`)).toThrow('Duration too large');
|
||||
});
|
||||
|
||||
test('should handle duration strings with whitespace around units', () => {
|
||||
expect(parseDuration('5 s')).toBe(5000);
|
||||
expect(parseDuration('10 m')).toBe(600000);
|
||||
expect(parseDuration('2 h')).toBe(7200000);
|
||||
expect(parseDuration('1 d')).toBe(86400000);
|
||||
});
|
||||
|
||||
test('should throw error for duration strings with negative values in unit format', () => {
|
||||
expect(() => parseDuration('-1.5s')).toThrow('Invalid duration format');
|
||||
expect(() => parseDuration('-10m')).toThrow('Invalid duration format');
|
||||
expect(() => parseDuration('-2.5h')).toThrow('Invalid duration format');
|
||||
});
|
||||
|
||||
test('should handle various decimal formats', () => {
|
||||
expect(parseDuration('0.5s')).toBe(500);
|
||||
expect(parseDuration('0.1m')).toBe(6000);
|
||||
expect(parseDuration('1.0h')).toBe(3600000);
|
||||
expect(parseDuration('0.25d')).toBe(21600000);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid types', () => {
|
||||
test('should throw error for non-string/number inputs', () => {
|
||||
expect(() => parseDuration(null)).toThrow('Duration must be a string or number');
|
||||
expect(() => parseDuration(undefined)).toThrow('Duration must be a string or number');
|
||||
expect(() => parseDuration({})).toThrow('Duration must be a string or number');
|
||||
expect(() => parseDuration([])).toThrow('Duration must be a string or number');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatDuration', () => {
|
||||
test('should format milliseconds to ms for values < 1000', () => {
|
||||
expect(formatDuration(0)).toBe('0ms');
|
||||
expect(formatDuration(999)).toBe('999ms');
|
||||
expect(formatDuration(500)).toBe('500ms');
|
||||
});
|
||||
|
||||
test('should format to seconds', () => {
|
||||
expect(formatDuration(1000)).toBe('1s');
|
||||
expect(formatDuration(5000)).toBe('5s');
|
||||
expect(formatDuration(59000)).toBe('59s');
|
||||
});
|
||||
|
||||
test('should format to minutes', () => {
|
||||
expect(formatDuration(60000)).toBe('1m');
|
||||
expect(formatDuration(300000)).toBe('5m');
|
||||
expect(formatDuration(3540000)).toBe('59m');
|
||||
});
|
||||
|
||||
test('should format to hours', () => {
|
||||
expect(formatDuration(3600000)).toBe('1h');
|
||||
expect(formatDuration(7200000)).toBe('2h');
|
||||
expect(formatDuration(86340000)).toBe('23h');
|
||||
});
|
||||
|
||||
test('should format to days', () => {
|
||||
expect(formatDuration(86400000)).toBe('1d');
|
||||
expect(formatDuration(172800000)).toBe('2d');
|
||||
expect(formatDuration(604800000)).toBe('7d');
|
||||
});
|
||||
|
||||
test('should throw error for negative values', () => {
|
||||
expect(() => formatDuration(-1000)).toThrow('Duration cannot be negative');
|
||||
});
|
||||
|
||||
test('should format edge case durations correctly', () => {
|
||||
expect(formatDuration(1)).toBe('1ms');
|
||||
expect(formatDuration(999)).toBe('999ms');
|
||||
expect(formatDuration(1001)).toBe('1s');
|
||||
expect(formatDuration(59999)).toBe('59s');
|
||||
expect(formatDuration(60001)).toBe('1m');
|
||||
expect(formatDuration(3599999)).toBe('59m');
|
||||
expect(formatDuration(3600001)).toBe('1h');
|
||||
expect(formatDuration(86399999)).toBe('23h');
|
||||
expect(formatDuration(86400001)).toBe('1d');
|
||||
});
|
||||
|
||||
test('should format very large durations to days', () => {
|
||||
expect(formatDuration(Number.MAX_SAFE_INTEGER)).toBe(`${Math.floor(Number.MAX_SAFE_INTEGER / (24 * 60 * 60 * 1000))}d`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidDurationString', () => {
|
||||
test('should return true for valid duration formats', () => {
|
||||
expect(isValidDurationString('5s')).toBe(true);
|
||||
expect(isValidDurationString('10m')).toBe(true);
|
||||
expect(isValidDurationString('2h')).toBe(true);
|
||||
expect(isValidDurationString('1d')).toBe(true);
|
||||
expect(isValidDurationString('1000')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for invalid formats', () => {
|
||||
expect(isValidDurationString('abc')).toBe(false);
|
||||
expect(isValidDurationString('5x')).toBe(false);
|
||||
expect(isValidDurationString('')).toBe(false);
|
||||
expect(isValidDurationString('-5s')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false for various invalid input types', () => {
|
||||
expect(isValidDurationString(null)).toBe(false);
|
||||
expect(isValidDurationString(undefined)).toBe(false);
|
||||
expect(isValidDurationString({})).toBe(false);
|
||||
expect(isValidDurationString([])).toBe(false);
|
||||
expect(isValidDurationString(true)).toBe(false);
|
||||
expect(isValidDurationString(false)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false for durations that would overflow', () => {
|
||||
const largeValue = Math.floor(Number.MAX_SAFE_INTEGER / (24 * 60 * 60 * 1000)) + 1;
|
||||
expect(isValidDurationString(`${largeValue}d`)).toBe(false);
|
||||
// Test overflow in unit-based durations (not pure numeric strings which have different validation)
|
||||
expect(isValidDurationString(`${Number.MAX_SAFE_INTEGER}d`)).toBe(false);
|
||||
});
|
||||
|
||||
test('should return false for edge case invalid formats', () => {
|
||||
expect(isValidDurationString('5.5.5s')).toBe(false);
|
||||
expect(isValidDurationString('5ss')).toBe(false);
|
||||
expect(isValidDurationString('s')).toBe(false);
|
||||
expect(isValidDurationString('5')).toBe(true); // Valid numeric string
|
||||
expect(isValidDurationString('5.m')).toBe(false);
|
||||
expect(isValidDurationString('.5m')).toBe(false);
|
||||
});
|
||||
|
||||
test('should handle decimal validation correctly', () => {
|
||||
expect(isValidDurationString('1.5s')).toBe(true);
|
||||
expect(isValidDurationString('0.5m')).toBe(true);
|
||||
expect(isValidDurationString('2.0h')).toBe(true);
|
||||
expect(isValidDurationString('1.25d')).toBe(true);
|
||||
});
|
||||
|
||||
test('should validate case insensitive units', () => {
|
||||
expect(isValidDurationString('5S')).toBe(true);
|
||||
expect(isValidDurationString('5M')).toBe(true);
|
||||
expect(isValidDurationString('5H')).toBe(true);
|
||||
expect(isValidDurationString('5D')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('integration tests', () => {
|
||||
test('parseDuration and formatDuration should work together', () => {
|
||||
const testValues = ['5s', '10m', '2h', '1d'];
|
||||
|
||||
testValues.forEach(value => {
|
||||
const parsed = parseDuration(value);
|
||||
const formatted = formatDuration(parsed);
|
||||
expect(formatted).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle round-trip conversion with decimal values', () => {
|
||||
// Note: formatDuration floors values, so we test known good round-trips
|
||||
const testCases = [
|
||||
{ input: '1.5s', parsed: 1500, formatted: '1s' },
|
||||
{ input: '2.5m', parsed: 150000, formatted: '2m' },
|
||||
{ input: '1.5h', parsed: 5400000, formatted: '1h' }
|
||||
];
|
||||
|
||||
testCases.forEach(({ input, parsed, formatted }) => {
|
||||
expect(parseDuration(input)).toBe(parsed);
|
||||
expect(formatDuration(parsed)).toBe(formatted);
|
||||
});
|
||||
});
|
||||
|
||||
test('should validate and format edge boundary values', () => {
|
||||
// Test values at unit boundaries
|
||||
expect(formatDuration(999)).toBe('999ms');
|
||||
expect(formatDuration(1000)).toBe('1s');
|
||||
expect(formatDuration(59999)).toBe('59s');
|
||||
expect(formatDuration(60000)).toBe('1m');
|
||||
expect(formatDuration(3599999)).toBe('59m');
|
||||
expect(formatDuration(3600000)).toBe('1h');
|
||||
expect(formatDuration(86399999)).toBe('23h');
|
||||
expect(formatDuration(86400000)).toBe('1d');
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue