From 49a9a4022bfef6fe08c89f3446bf3ad7311e93f6 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:34:26 -0800 Subject: [PATCH] fix: Fix issue where flush callback could be called twice. --- .../__tests__/LDClientImpl.events.test.ts | 118 ++++++++++++++++++ .../shared/sdk-server/src/LDClientImpl.ts | 4 +- 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 packages/shared/sdk-server/__tests__/LDClientImpl.events.test.ts diff --git a/packages/shared/sdk-server/__tests__/LDClientImpl.events.test.ts b/packages/shared/sdk-server/__tests__/LDClientImpl.events.test.ts new file mode 100644 index 0000000000..0646df80ff --- /dev/null +++ b/packages/shared/sdk-server/__tests__/LDClientImpl.events.test.ts @@ -0,0 +1,118 @@ +import { LDClientImpl } from '../src'; +import { createBasicPlatform } from './createBasicPlatform'; +import TestLogger from './Logger'; +import makeCallbacks from './makeCallbacks'; + +it('flushes events successfully and executes the callback', async () => { + const platform = createBasicPlatform(); + platform.requests.fetch.mockImplementation(() => + Promise.resolve({ status: 200, headers: new Headers() }), + ); + + const client = new LDClientImpl( + 'sdk-key-events', + platform, + { + logger: new TestLogger(), + stream: false, + }, + makeCallbacks(false), + ); + + client.identify({ key: 'user' }); + client.variation('dev-test-flag', { key: 'user' }, false); + + const flushCallback = jest.fn(); + + await client.flush(flushCallback); + + expect(platform.requests.fetch).toHaveBeenCalledWith( + 'https://events.launchdarkly.com/bulk', + expect.objectContaining({ + method: 'POST', + body: expect.any(String), + }), + ); + expect(flushCallback).toHaveBeenCalledWith(null, true); + expect(flushCallback).toHaveBeenCalledTimes(1); +}); + +it('flushes events successfully', async () => { + const platform = createBasicPlatform(); + platform.requests.fetch.mockImplementation(() => + Promise.resolve({ status: 200, headers: new Headers() }), + ); + + const client = new LDClientImpl( + 'sdk-key-events', + platform, + { + logger: new TestLogger(), + stream: false, + }, + makeCallbacks(false), + ); + + client.identify({ key: 'user' }); + client.variation('dev-test-flag', { key: 'user' }, false); + + await client.flush(); + + expect(platform.requests.fetch).toHaveBeenCalledWith( + 'https://events.launchdarkly.com/bulk', + expect.objectContaining({ + method: 'POST', + body: expect.any(String), + }), + ); +}); + +it('calls error callback once when flush fails with http status code', async () => { + const platform = createBasicPlatform(); + platform.requests.fetch.mockImplementation(() => + Promise.resolve({ status: 401, headers: new Headers() }), + ); + + const flushCallback = jest.fn(); + const client = new LDClientImpl( + 'sdk-key-events', + platform, + { + logger: new TestLogger(), + stream: false, + }, + makeCallbacks(false), + ); + + client.identify({ key: 'user' }); + client.variation('dev-test-flag', { key: 'user' }, false); + + await client.flush(flushCallback); + + expect(flushCallback).toHaveBeenCalledWith(expect.any(Error), false); + expect(flushCallback).toHaveBeenCalledTimes(1); +}); + +it('calls error callback once when flush fails with exception', async () => { + const platform = createBasicPlatform(); + platform.requests.fetch.mockImplementation(() => Promise.reject(new Error('test error'))); + + const flushCallback = jest.fn(); + const client = new LDClientImpl( + 'sdk-key-events', + platform, + { + logger: new TestLogger(), + stream: false, + }, + makeCallbacks(false), + ); + + client.identify({ key: 'user' }); + client.variation('dev-test-flag', { key: 'user' }, false); + + await client.flush(flushCallback); + + expect(flushCallback).toHaveBeenCalledWith(expect.any(Error), false); + expect(flushCallback).toHaveBeenCalledTimes(1); +}); diff --git a/packages/shared/sdk-server/src/LDClientImpl.ts b/packages/shared/sdk-server/src/LDClientImpl.ts index 44a355158e..ecc332c2f2 100644 --- a/packages/shared/sdk-server/src/LDClientImpl.ts +++ b/packages/shared/sdk-server/src/LDClientImpl.ts @@ -773,9 +773,9 @@ export default class LDClientImpl implements LDClient { try { await this._eventProcessor.flush(); } catch (err) { - callback?.(err as Error, false); + return callback?.(err as Error, false); } - callback?.(null, true); + return callback?.(null, true); } addHook(hook: Hook): void {