From cd1b64196bd8dab6a673489c84e21eba4aca2a84 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Fri, 20 Oct 2023 12:07:42 +0200 Subject: [PATCH] chore(shared): Improve test coverage --- .../src/__tests__/callWithRetry.test.ts | 23 ++++++++++++++ packages/shared/src/__tests__/keys.test.ts | 30 +++++++++++++++++++ packages/shared/src/callWithRetry.ts | 2 +- .../__tests__/createDeferredPromise.test.ts | 22 ++++++++++++++ .../src/utils/__tests__/instance.test.ts | 20 +++++++++++++ .../runWithExponentialBackOff.test.ts | 21 +++++++++++++ packages/shared/src/utils/instance.ts | 3 ++ 7 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 packages/shared/src/__tests__/callWithRetry.test.ts create mode 100644 packages/shared/src/utils/__tests__/createDeferredPromise.test.ts create mode 100644 packages/shared/src/utils/__tests__/instance.test.ts create mode 100644 packages/shared/src/utils/__tests__/runWithExponentialBackOff.test.ts diff --git a/packages/shared/src/__tests__/callWithRetry.test.ts b/packages/shared/src/__tests__/callWithRetry.test.ts new file mode 100644 index 0000000000..bb276d769c --- /dev/null +++ b/packages/shared/src/__tests__/callWithRetry.test.ts @@ -0,0 +1,23 @@ +import { callWithRetry } from '../callWithRetry'; + +describe('callWithRetry', () => { + test('should return the result of the function if it succeeds', async () => { + const fn = jest.fn().mockResolvedValue('result'); + const result = await callWithRetry(fn); + expect(result).toBe('result'); + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('should retry the function if it fails', async () => { + const fn = jest.fn().mockRejectedValueOnce(new Error('error')).mockResolvedValueOnce('result'); + const result = await callWithRetry(fn, 1, 2); + expect(result).toBe('result'); + expect(fn).toHaveBeenCalledTimes(2); + }); + + test('should throw an error if the function fails too many times', async () => { + const fn = jest.fn().mockRejectedValue(new Error('error')); + await expect(callWithRetry(fn, 1, 2)).rejects.toThrow('error'); + expect(fn).toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/shared/src/__tests__/keys.test.ts b/packages/shared/src/__tests__/keys.test.ts index 5867c95e97..5c916a2db2 100644 --- a/packages/shared/src/__tests__/keys.test.ts +++ b/packages/shared/src/__tests__/keys.test.ts @@ -1,7 +1,9 @@ import { buildPublishableKey, createDevOrStagingUrlCache, + isDevelopmentFromApiKey, isLegacyFrontendApiKey, + isProductionFromApiKey, isPublishableKey, parsePublishableKey, } from '../keys'; @@ -90,3 +92,31 @@ describe('isDevOrStagingUrl(url)', () => { expect(isDevOrStagingUrl(a)).toBe(expected); }); }); + +describe('isDevelopmentFromApiKey(key)', () => { + const cases: Array<[string, boolean]> = [ + ['sk_live_Y2xlcmsuY2xlcmsuZGV2JA==', false], + ['sk_test_Y2xlcmsuY2xlcmsuZGV2JA==', true], + ['live_Y2xlcmsuY2xlcmsuZGV2JA==', false], + ['test_Y2xlcmsuY2xlcmsuZGV2JA==', true], + ]; + + test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => { + const result = isDevelopmentFromApiKey(publishableKeyStr); + expect(result).toEqual(expected); + }); +}); + +describe('isProductionFromApiKey(key)', () => { + const cases: Array<[string, boolean]> = [ + ['sk_live_Y2xlcmsuY2xlcmsuZGV2JA==', true], + ['sk_test_Y2xlcmsuY2xlcmsuZGV2JA==', false], + ['live_Y2xlcmsuY2xlcmsuZGV2JA==', true], + ['test_Y2xlcmsuY2xlcmsuZGV2JA==', false], + ]; + + test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => { + const result = isProductionFromApiKey(publishableKeyStr); + expect(result).toEqual(expected); + }); +}); diff --git a/packages/shared/src/callWithRetry.ts b/packages/shared/src/callWithRetry.ts index 84050f5659..e2723a3ba5 100644 --- a/packages/shared/src/callWithRetry.ts +++ b/packages/shared/src/callWithRetry.ts @@ -23,6 +23,6 @@ export async function callWithRetry( } await wait(2 ** attempt * 100); - return callWithRetry(fn, attempt + 1); + return callWithRetry(fn, attempt + 1, maxAttempts); } } diff --git a/packages/shared/src/utils/__tests__/createDeferredPromise.test.ts b/packages/shared/src/utils/__tests__/createDeferredPromise.test.ts new file mode 100644 index 0000000000..9cd04a20e9 --- /dev/null +++ b/packages/shared/src/utils/__tests__/createDeferredPromise.test.ts @@ -0,0 +1,22 @@ +import { createDeferredPromise } from '../createDeferredPromise'; + +describe('createDeferredPromise', () => { + test('resolves with correct value', async () => { + const { promise, resolve } = createDeferredPromise(); + const expectedValue = 'hello world'; + resolve(expectedValue); + const result = await promise; + expect(result).toBe(expectedValue); + }); + + test('rejects with correct error', async () => { + const { promise, reject } = createDeferredPromise(); + const expectedError = new Error('something went wrong'); + reject(expectedError); + try { + await promise; + } catch (error) { + expect(error).toBe(expectedError); + } + }); +}); diff --git a/packages/shared/src/utils/__tests__/instance.test.ts b/packages/shared/src/utils/__tests__/instance.test.ts new file mode 100644 index 0000000000..c877a19506 --- /dev/null +++ b/packages/shared/src/utils/__tests__/instance.test.ts @@ -0,0 +1,20 @@ +import { isStaging } from '../instance'; + +describe('isStaging', () => { + it.each([ + ['clerk', false], + ['clerk.com', false], + ['whatever.com', false], + ['clerk.abcef', false], + ['clerk.abcef.12345', false], + ['clerk.abcef.12345.lcl', false], + ['clerk.abcef.12345.lcl.dev', false], + ['clerk.abcef.12345.stg.dev', false], + ['clerk.abcef.12345.lclstage.dev', true], + ['clerk.abcef.12345.stgstage.dev', true], + ['clerk.abcef.12345.clerkstage.dev', true], + ['clerk.abcef.12345.accountsstage.dev', true], + ])('validates the frontendApi format', (str, expected) => { + expect(isStaging(str)).toBe(expected); + }); +}); diff --git a/packages/shared/src/utils/__tests__/runWithExponentialBackOff.test.ts b/packages/shared/src/utils/__tests__/runWithExponentialBackOff.test.ts new file mode 100644 index 0000000000..c80ec1c023 --- /dev/null +++ b/packages/shared/src/utils/__tests__/runWithExponentialBackOff.test.ts @@ -0,0 +1,21 @@ +import { runWithExponentialBackOff } from '../runWithExponentialBackOff'; + +describe('runWithExponentialBackOff', () => { + test('resolves with the result of the callback', async () => { + const result = await runWithExponentialBackOff(() => Promise.resolve('success')); + expect(result).toBe('success'); + }); + + test('retries the callback until it succeeds', async () => { + let attempts = 0; + const result = await runWithExponentialBackOff(() => { + attempts++; + if (attempts < 3) { + throw new Error('failed'); + } + return Promise.resolve('success'); + }); + expect(result).toBe('success'); + expect(attempts).toBe(3); + }); +}); diff --git a/packages/shared/src/utils/instance.ts b/packages/shared/src/utils/instance.ts index 5441634f48..39035fd267 100644 --- a/packages/shared/src/utils/instance.ts +++ b/packages/shared/src/utils/instance.ts @@ -1,3 +1,6 @@ +/** + * Check if the frontendApi ends with a staging domain + */ export function isStaging(frontendApi: string): boolean { return ( frontendApi.endsWith('.lclstage.dev') ||