From 11d3aca5200e32ae369c966b15e6d8b7d4ad78ae Mon Sep 17 00:00:00 2001 From: Harvmaster Date: Thu, 6 Aug 2026 10:09:34 +0000 Subject: [PATCH] Add tests. Use exactOptionalPropertyTypes in tsconfig --- test/exponential-backoff.test.ts | 37 +++++++++++++++++++++++++++++++- tsconfig.json | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/test/exponential-backoff.test.ts b/test/exponential-backoff.test.ts index 556ef11..34c8663 100644 --- a/test/exponential-backoff.test.ts +++ b/test/exponential-backoff.test.ts @@ -1,6 +1,6 @@ import { expect, test, vi } from 'vitest'; import { ExponentialBackoff } from '../source/exponential-backoff.ts'; -import { ExponentialBackoffMaxRetriesHitError, ExponentialBackoffStoppedRetriesError } from '../source/errors.ts'; +import { ExponentialBackoffMaxRetriesHitError, ExponentialBackoffNumberNotFiniteError, ExponentialBackoffStoppedRetriesError } from '../source/errors.ts'; /** * A valid options object that satisfies {@link ExponentialBackoff.validateOptions}. @@ -582,6 +582,39 @@ const testExponentialBackoffValidateOptionsRejectsNaN = (): void => { } }; +/** Tests that calculateDelay will not result in NaN from extremely large growth rates and attempts */ +const testExponentialBackoffCalculateDelayDoesNotResultInNaN = (): void => { + // Large number, 1 trillion. + // Theory being that 1 trillion to the power of 1 trillion should be a very large number and cause either an unsafe value or a NaN. + const largeNumber = 1_000_000_000_000; + + // Test the calculateDelay function + const result = ExponentialBackoff.calculateDelay({ + baseDelay: 10000, + growthRate: largeNumber, + jitter: 0, + maxDelay: 10_000, + maxAttempts: largeNumber, + }, largeNumber); + + // Test to ensure it was bounded to the max delay + expect(result).toBe(10_000); +}; + +/** Tests that passing undefined into the constructor does not cause an error during spread */ +const testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread = async (): Promise => { + const options = { + baseDelay: undefined, + growthRate: undefined, + jitter: undefined, + maxDelay: undefined, + maxAttempts: undefined, + }; + + // We expect an error during validation as undefined is not a finite number, not an issue with the spread operator + // @ts-expect-error - Passing undefined is allowed if the exactOptionalPropertyTypes option is set to false in TS Compiler options. + expect(() => new ExponentialBackoff(options)).toThrow(ExponentialBackoffNumberNotFiniteError); +}; const runTests = async (): Promise => { test('ExponentialBackoff.run: delegates to a new instance using default options', testExponentialBackoffRunUsesDefaultOptions); test('ExponentialBackoff.run: retries and succeeds with partial options', testExponentialBackoffRunWithPartialOptions); @@ -608,6 +641,8 @@ const runTests = async (): Promise => { test('ExponentialBackoff.validateOptions: rejects Infinity', testExponentialBackoffValidateOptionsRejectsNonFiniteValues); test('ExponentialBackoff.validateOptions: rejects non-integer values', testExponentialBackoffValidateOptionsRejectsNonIntegerValues); test('ExponentialBackoff.validateOptions: rejects NaN', testExponentialBackoffValidateOptionsRejectsNaN); + test('ExponentialBackoff: calculateDelay does not result in NaN from extremely large growth rates and attempts', testExponentialBackoffCalculateDelayDoesNotResultInNaN); + test('ExponentialBackoff: constructor does not cause an error during spread', testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread); }; await runTests(); diff --git a/tsconfig.json b/tsconfig.json index 106b15f..e6b7c1a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "allowImportingTsExtensions": true, + "exactOptionalPropertyTypes": true, "noEmit": true, "declaration": true, "declarationMap": true