|
| 1 | +import type { NumbersConfig } from '../src/types' |
| 2 | +import { describe, expect, it } from 'bun:test' |
| 3 | +import { formatNumber, parseNumber } from '../src' |
| 4 | +import { bulkFormat, bulkParse, generateLargeNumbers, measureFormatPerformance, measureParsePerformance } from '../src/performance' |
| 5 | + |
| 6 | +describe('Performance Tests', () => { |
| 7 | + describe('Large Number Formatting', () => { |
| 8 | + it('formats billion-scale numbers correctly', () => { |
| 9 | + const billions = 1_234_567_890_123 |
| 10 | + const result = formatNumber({ value: billions }) |
| 11 | + expect(result).toBe('1,234,567,890,123.00') |
| 12 | + }) |
| 13 | + |
| 14 | + it('formats trillion-scale numbers correctly', () => { |
| 15 | + const trillions = 1_234_567_890_123_456 |
| 16 | + const result = formatNumber({ value: trillions }) |
| 17 | + expect(result).toBe('1,234,567,890,123,456.00') |
| 18 | + }) |
| 19 | + |
| 20 | + it('formats quadrillion-scale numbers correctly', () => { |
| 21 | + // Using BigInt for precision, then converting to number for the API |
| 22 | + const quadrillions = Number(1_234_567_890_123_456_789n) |
| 23 | + const result = formatNumber({ value: quadrillions }) |
| 24 | + // Due to JS Number precision limitations, the value is rounded |
| 25 | + expect(result).toBe('1,234,567,890,123,456,800.00') |
| 26 | + }) |
| 27 | + |
| 28 | + it('measures performance for formatting large numbers', () => { |
| 29 | + const testSizes = [ |
| 30 | + { name: 'Million', value: 1_234_567_890 }, |
| 31 | + { name: 'Billion', value: 1_234_567_890_123 }, |
| 32 | + { name: 'Trillion', value: 1_234_567_890_123_456 }, |
| 33 | + ] |
| 34 | + |
| 35 | + // Using the .forEach syntax to limit linter warnings for console logs |
| 36 | + testSizes.forEach(({ name, value }) => { |
| 37 | + const start = performance.now() |
| 38 | + const result = formatNumber({ value }) |
| 39 | + const end = performance.now() |
| 40 | + |
| 41 | + // We're allowing these logs in tests to display performance metrics |
| 42 | + // eslint-disable-next-line no-console |
| 43 | + console.log(`Testing ${name} formatting:`) |
| 44 | + // eslint-disable-next-line no-console |
| 45 | + console.log(`- Formatted result: ${result}`) |
| 46 | + // eslint-disable-next-line no-console |
| 47 | + console.log(`- Time taken: ${(end - start).toFixed(4)}ms`) |
| 48 | + |
| 49 | + // Ensure execution time is reasonable (adjust threshold as needed) |
| 50 | + expect(end - start).toBeLessThan(10) // Should format in under 10ms |
| 51 | + }) |
| 52 | + }) |
| 53 | + |
| 54 | + it('measures performance for parsing large numbers', () => { |
| 55 | + const testSizes = [ |
| 56 | + { name: 'Million', value: '1,234,567,890.00' }, |
| 57 | + { name: 'Billion', value: '1,234,567,890,123.00' }, |
| 58 | + { name: 'Trillion', value: '1,234,567,890,123,456.00' }, |
| 59 | + ] |
| 60 | + |
| 61 | + testSizes.forEach(({ name, value }) => { |
| 62 | + const start = performance.now() |
| 63 | + const result = parseNumber({ value }) |
| 64 | + const end = performance.now() |
| 65 | + |
| 66 | + // eslint-disable-next-line no-console |
| 67 | + console.log(`Testing ${name} parsing:`) |
| 68 | + // eslint-disable-next-line no-console |
| 69 | + console.log(`- Parsed result: ${result}`) |
| 70 | + // eslint-disable-next-line no-console |
| 71 | + console.log(`- Time taken: ${(end - start).toFixed(4)}ms`) |
| 72 | + |
| 73 | + // Ensure execution time is reasonable |
| 74 | + expect(end - start).toBeLessThan(10) // Should parse in under 10ms |
| 75 | + }) |
| 76 | + }) |
| 77 | + |
| 78 | + it('handles numbers with scientific notation correctly', () => { |
| 79 | + const largeNumber = 1e20 |
| 80 | + const result = formatNumber({ |
| 81 | + value: largeNumber, |
| 82 | + config: { |
| 83 | + useScientificNotation: false, |
| 84 | + decimalPlaces: 0, |
| 85 | + }, |
| 86 | + }) |
| 87 | + |
| 88 | + expect(result).toBe('100,000,000,000,000,000,000') |
| 89 | + |
| 90 | + const scientificResult = formatNumber({ |
| 91 | + value: largeNumber, |
| 92 | + config: { |
| 93 | + useScientificNotation: true, |
| 94 | + scientificNotationThreshold: 1e10, |
| 95 | + }, |
| 96 | + }) |
| 97 | + |
| 98 | + expect(scientificResult).toInclude('1.00e+20') |
| 99 | + }) |
| 100 | + }) |
| 101 | + |
| 102 | + describe('Bulk Formatting Performance', () => { |
| 103 | + it('measures performance for bulk formatting operations using utility functions', () => { |
| 104 | + const count = 10000 |
| 105 | + const numbers = Array.from({ length: count }, (_, i) => i * 1000 + 0.5678) |
| 106 | + |
| 107 | + // Using the performance measurement utility |
| 108 | + const { totalTime, averageTime, operationsPerSecond } = measureFormatPerformance(numbers) |
| 109 | + |
| 110 | + // eslint-disable-next-line no-console |
| 111 | + console.log(`Formatting ${count} numbers with utility:`) |
| 112 | + // eslint-disable-next-line no-console |
| 113 | + console.log(`- Total time: ${totalTime.toFixed(2)}ms`) |
| 114 | + // eslint-disable-next-line no-console |
| 115 | + console.log(`- Average time per number: ${averageTime.toFixed(4)}ms`) |
| 116 | + // eslint-disable-next-line no-console |
| 117 | + console.log(`- Numbers per second: ${operationsPerSecond.toLocaleString()}`) |
| 118 | + |
| 119 | + // Performance assertions (adjust thresholds based on actual performance) |
| 120 | + expect(averageTime).toBeLessThan(0.1) // Average under 0.1ms per number |
| 121 | + }) |
| 122 | + |
| 123 | + it('measures performance for bulk parsing operations using utility functions', () => { |
| 124 | + const count = 10000 |
| 125 | + const strings = Array.from({ length: count }, (_, i) => `${i * 1000 + 0.5678}`) |
| 126 | + |
| 127 | + // Using the performance measurement utility |
| 128 | + const { totalTime, averageTime, operationsPerSecond } = measureParsePerformance(strings) |
| 129 | + |
| 130 | + // eslint-disable-next-line no-console |
| 131 | + console.log(`Parsing ${count} numbers with utility:`) |
| 132 | + // eslint-disable-next-line no-console |
| 133 | + console.log(`- Total time: ${totalTime.toFixed(2)}ms`) |
| 134 | + // eslint-disable-next-line no-console |
| 135 | + console.log(`- Average time per number: ${averageTime.toFixed(4)}ms`) |
| 136 | + // eslint-disable-next-line no-console |
| 137 | + console.log(`- Numbers per second: ${operationsPerSecond.toLocaleString()}`) |
| 138 | + |
| 139 | + // Performance assertions |
| 140 | + expect(averageTime).toBeLessThan(0.1) // Average under 0.1ms per number |
| 141 | + }) |
| 142 | + |
| 143 | + it('tests performance with generated large numbers', () => { |
| 144 | + // Generate an array of large numbers from 1 million to 1 trillion |
| 145 | + const largeNumbers = generateLargeNumbers(20, 1_000_000, 100) |
| 146 | + |
| 147 | + // eslint-disable-next-line no-console |
| 148 | + console.log('Testing with generated large numbers:') |
| 149 | + // eslint-disable-next-line no-console |
| 150 | + console.log(`- Sample values: ${largeNumbers.slice(0, 3).join(', ')}...`) |
| 151 | + |
| 152 | + const { totalTime, averageTime, operationsPerSecond } = measureFormatPerformance(largeNumbers) |
| 153 | + |
| 154 | + // eslint-disable-next-line no-console |
| 155 | + console.log(`- Total time: ${totalTime.toFixed(2)}ms`) |
| 156 | + // eslint-disable-next-line no-console |
| 157 | + console.log(`- Average time per number: ${averageTime.toFixed(4)}ms`) |
| 158 | + // eslint-disable-next-line no-console |
| 159 | + console.log(`- Numbers per second: ${operationsPerSecond.toLocaleString()}`) |
| 160 | + |
| 161 | + expect(averageTime).toBeLessThan(1) // Large numbers should still format under 1ms |
| 162 | + }) |
| 163 | + |
| 164 | + it('compares performance with different configurations', () => { |
| 165 | + const count = 1000 |
| 166 | + const number = 1234567.89 |
| 167 | + |
| 168 | + // eslint-disable-next-line no-console |
| 169 | + console.log('Performance comparison with different configurations:') |
| 170 | + |
| 171 | + // Create typed arrays of numbers |
| 172 | + const numbers = Array.from({ length: count }, () => number) |
| 173 | + |
| 174 | + // Test with default config |
| 175 | + const { totalTime: defaultTime } = measureFormatPerformance(numbers) |
| 176 | + |
| 177 | + // Test with scientific notation |
| 178 | + const { totalTime: scientificTime } = measureFormatPerformance(numbers, { |
| 179 | + useScientificNotation: true, |
| 180 | + }) |
| 181 | + |
| 182 | + // Test with digit group spacing |
| 183 | + const { totalTime: digitGroupTime } = measureFormatPerformance(numbers, { |
| 184 | + digitGroupSpacing: '2', |
| 185 | + }) |
| 186 | + |
| 187 | + // eslint-disable-next-line no-console |
| 188 | + console.log(`- Default config: ${defaultTime.toFixed(2)}ms for ${count} operations`) |
| 189 | + // eslint-disable-next-line no-console |
| 190 | + console.log(`- Scientific notation: ${scientificTime.toFixed(2)}ms for ${count} operations`) |
| 191 | + // eslint-disable-next-line no-console |
| 192 | + console.log(`- Custom digit grouping: ${digitGroupTime.toFixed(2)}ms for ${count} operations`) |
| 193 | + }) |
| 194 | + }) |
| 195 | + |
| 196 | + describe('Edge Cases Performance', () => { |
| 197 | + it('handles the maximum safe integer correctly', () => { |
| 198 | + const maxSafeInt = Number.MAX_SAFE_INTEGER // 9,007,199,254,740,991 |
| 199 | + const result = formatNumber({ value: maxSafeInt, config: { decimalPlaces: 0 } }) |
| 200 | + expect(result).toBe('9,007,199,254,740,991') |
| 201 | + }) |
| 202 | + |
| 203 | + it('measures performance with special formatting requirements', () => { |
| 204 | + const count = 1000 |
| 205 | + const number = 1234567.89 |
| 206 | + |
| 207 | + // Test with highly customized config |
| 208 | + const customConfig: NumbersConfig = { |
| 209 | + decimalPlaces: 4, |
| 210 | + decimalCharacter: ',', |
| 211 | + digitGroupSeparator: '.', |
| 212 | + currencySymbol: '€', |
| 213 | + currencySymbolPlacement: 'p', |
| 214 | + showPositiveSign: true, |
| 215 | + // Add other custom settings |
| 216 | + } |
| 217 | + |
| 218 | + // Create a properly typed array of numbers |
| 219 | + const numbers = Array.from({ length: count }, () => number) |
| 220 | + const { totalTime } = measureFormatPerformance(numbers, customConfig) |
| 221 | + |
| 222 | + // eslint-disable-next-line no-console |
| 223 | + console.log(`Custom config performance: ${totalTime.toFixed(2)}ms for ${count} operations`) |
| 224 | + expect(totalTime).toBeLessThan(1000) // Should complete in reasonable time |
| 225 | + }) |
| 226 | + |
| 227 | + it('tests bulk formatting and parsing utility functions', () => { |
| 228 | + const testNumbers = [123, 456.78, 9000, 12345.6789] |
| 229 | + |
| 230 | + // Test bulkFormat |
| 231 | + const formattedNumbers = bulkFormat(testNumbers) |
| 232 | + expect(formattedNumbers).toHaveLength(testNumbers.length) |
| 233 | + expect(formattedNumbers[0]).toBe('123.00') |
| 234 | + expect(formattedNumbers[1]).toBe('456.78') |
| 235 | + |
| 236 | + // Test bulkParse |
| 237 | + const parsedNumbers = bulkParse(formattedNumbers) |
| 238 | + expect(parsedNumbers).toHaveLength(formattedNumbers.length) |
| 239 | + expect(parsedNumbers[0]).toBe(123) |
| 240 | + expect(parsedNumbers[1]).toBe(456.78) |
| 241 | + }) |
| 242 | + }) |
| 243 | +}) |
0 commit comments