Skip to content

Commit

Permalink
Fix client and server test.
Browse files Browse the repository at this point in the history
  • Loading branch information
kinyoklion committed Oct 1, 2024
1 parent c9b0a1d commit 1b21500
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 78 deletions.
2 changes: 1 addition & 1 deletion packages/shared/sdk-client/__tests__/setupCrypto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hasher } from '../src/api';
import { Hasher } from '@launchdarkly/js-sdk-common';

export const setupCrypto = () => {
let counter = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('given an LDClient with test data', () => {
logger = new TestLogger();
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-all-flags-test-data',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('given an offline client', () => {
logger = new TestLogger();
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-all-flags-offline',
createBasicPlatform(),
{
offline: true,
Expand Down
28 changes: 9 additions & 19 deletions packages/shared/sdk-server/__tests__/LDClient.evaluation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,6 @@ import VersionedDataKinds from '../src/store/VersionedDataKinds';
import { createBasicPlatform } from './createBasicPlatform';
import TestLogger, { LogLevel } from './Logger';
import makeCallbacks from './makeCallbacks';
import { MockStreamingProcessor, setupMockStreamingProcessor } from './streamingProcessor';

jest.mock('@launchdarkly/js-sdk-common', () => {
const actual = jest.requireActual('@launchdarkly/js-sdk-common');
return {
...actual,
...{
internal: {
...actual.internal,
StreamingProcessor: MockStreamingProcessor,
},
},
};
});

const defaultUser = { key: 'user' };

Expand All @@ -32,7 +18,7 @@ describe('given an LDClient with test data', () => {
beforeEach(async () => {
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-evaluation-test-data',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down Expand Up @@ -278,7 +264,7 @@ describe('given an offline client', () => {
logger = new TestLogger();
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-evaluation-offline',
createBasicPlatform(),
{
offline: true,
Expand Down Expand Up @@ -342,7 +328,7 @@ describe('given a client and store that are uninitialized', () => {
});

client = new LDClientImpl(
'sdk-key',
'sdk-key-evaluation-uninitialized-store',
createBasicPlatform(),
{
updateProcessor: new InertUpdateProcessor(),
Expand Down Expand Up @@ -389,14 +375,18 @@ describe('given a client that is un-initialized and store that is initialized',
},
segments: {},
});
setupMockStreamingProcessor(true);

client = new LDClientImpl(
'sdk-key',
'sdk-key-initialized-store',
createBasicPlatform(),
{
sendEvents: false,
featureStore: store,
updateProcessor: () => ({
start: jest.fn(),
stop: jest.fn(),
close: jest.fn(),
}),
},
makeCallbacks(true),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('given a client with mock event processor', () => {

td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-events',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/sdk-server/__tests__/LDClient.hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('given an LDClient with test data', () => {
testHook = new TestHook();
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-hooks-test-data',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down Expand Up @@ -350,7 +350,7 @@ it('can add a hook after initialization', async () => {
const logger = new TestLogger();
const td = new TestData();
const client = new LDClientImpl(
'sdk-key',
'sdk-key-hook-after-init',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('given an LDClient with test data', () => {
td = new TestData();
[errors, callbacks] = makeCallbacks();
client = new LDClientImpl(
'sdk-key',
'sdk-key-migration',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('given test data with big segments', () => {
};

client = new LDClientImpl(
'sdk-key',
'sdk-key-big-segments-test-data',
{ ...createBasicPlatform(), crypto },
{
updateProcessor: td.getFactory(),
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('given test data with big segments', () => {
};

client = new LDClientImpl(
'sdk-key',
'sdk-key-big-segments-with-user',
{ ...createBasicPlatform(), crypto },
{
updateProcessor: td.getFactory(),
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('given test data with big segments', () => {
};

client = new LDClientImpl(
'sdk-key',
'sdk-key-big-segments-store-error',
{ ...createBasicPlatform(), crypto },
{
updateProcessor: td.getFactory(),
Expand All @@ -180,7 +180,7 @@ describe('given test data with big segments', () => {
describe('given a client without big segment support.', () => {
beforeEach(async () => {
client = new LDClientImpl(
'sdk-key',
'sdk-key-big-segments-no-store',
{ ...createBasicPlatform(), crypto },
{
updateProcessor: td.getFactory(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('given an LDClient with test data', () => {
queue = new AsyncQueue();
td = new TestData();
client = new LDClientImpl(
'sdk-key',
'sdk-key-listeners',
createBasicPlatform(),
{
updateProcessor: td.getFactory(),
Expand Down
113 changes: 70 additions & 43 deletions packages/shared/sdk-server/__tests__/LDClientImpl.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
import { LDClientImpl, LDOptions } from '../src';
import { LDClientContext, LDStreamingError } from '@launchdarkly/js-sdk-common';

import { LDOptions } from '../src/api/options/LDOptions';
import { LDFeatureStore } from '../src/api/subsystems/LDFeatureStore';
import LDClientImpl from '../src/LDClientImpl';
import { createBasicPlatform } from './createBasicPlatform';
import TestLogger, { LogLevel } from './Logger';
import { MockStreamingProcessor, setupMockStreamingProcessor } from './streamingProcessor';

jest.mock('@launchdarkly/js-sdk-common', () => {
const actual = jest.requireActual('@launchdarkly/js-sdk-common');
return {
...actual,
...{
internal: {
...actual.internal,
StreamingProcessor: MockStreamingProcessor,
},
},
};
});

function getUpdateProcessorFactory(shouldError: boolean = false, initTimeoutMs: number = 0) {
let initTimeoutHandle: any;
let patchTimeoutHandle: any;
let deleteTimeoutHandle: any;

return (
_clientContext: LDClientContext,
featureStore: LDFeatureStore,
initSuccessHandler: VoidFunction,
errorHandler?: (e: Error) => void,
) => ({
start: jest.fn(async () => {
if (shouldError) {
initTimeoutHandle = setTimeout(() => {
const unauthorized = new Error('test-error') as LDStreamingError;
// @ts-ignore
unauthorized.code = 401;
errorHandler?.(unauthorized);
}, 0);
} else {
// execute put which will resolve the identify promise
initTimeoutHandle = setTimeout(() => {
featureStore.init({}, () => {});
initSuccessHandler();
}, initTimeoutMs);
}
}),
close: jest.fn(() => {
clearTimeout(initTimeoutHandle);
clearTimeout(patchTimeoutHandle);
clearTimeout(deleteTimeoutHandle);
}),
eventSource: {},
});
}

describe('LDClientImpl', () => {
let client: LDClientImpl;
Expand All @@ -26,19 +52,15 @@ describe('LDClientImpl', () => {
hasEventListeners: jest.fn().mockName('hasEventListeners'),
};
const createClient = (options: LDOptions = {}) =>
new LDClientImpl('sdk-key', createBasicPlatform(), options, callbacks);

beforeEach(() => {
setupMockStreamingProcessor();
});
new LDClientImpl('sdk-key-ldclientimpl.test', createBasicPlatform(), options, callbacks);

afterEach(() => {
client.close();
jest.resetAllMocks();
});

it('fires ready event in online mode', async () => {
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory() });
const initializedClient = await client.waitForInitialization({ timeout: 10 });

expect(initializedClient).toEqual(client);
Expand All @@ -49,8 +71,7 @@ describe('LDClientImpl', () => {
});

it('wait for initialization completes even if initialization completes before it is called', (done) => {
setupMockStreamingProcessor();
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory() });

setTimeout(async () => {
const initializedClient = await client.waitForInitialization({ timeout: 10 });
Expand All @@ -60,7 +81,7 @@ describe('LDClientImpl', () => {
});

it('waiting for initialization the second time produces the same result', async () => {
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory() });
await client.waitForInitialization({ timeout: 10 });

const initializedClient = await client.waitForInitialization({ timeout: 10 });
Expand All @@ -79,9 +100,7 @@ describe('LDClientImpl', () => {
});

it('initialization fails: failed event fires and initialization promise rejects', async () => {
setupMockStreamingProcessor(true);
client = createClient();

client = createClient({ updateProcessor: getUpdateProcessorFactory(true) });
await expect(client.waitForInitialization({ timeout: 10 })).rejects.toThrow('failed');

expect(client.initialized()).toBeFalsy();
Expand All @@ -91,8 +110,7 @@ describe('LDClientImpl', () => {
});

it('initialization promise is rejected even if the failure happens before wait is called', (done) => {
setupMockStreamingProcessor(true);
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory(true) });

setTimeout(async () => {
await expect(client.waitForInitialization({ timeout: 10 })).rejects.toThrow('failed');
Expand All @@ -106,8 +124,7 @@ describe('LDClientImpl', () => {
});

it('waiting a second time results in the same rejection', async () => {
setupMockStreamingProcessor(true);
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory(true) });

await expect(client.waitForInitialization({ timeout: 10 })).rejects.toThrow('failed');
await expect(client.waitForInitialization({ timeout: 10 })).rejects.toThrow('failed');
Expand All @@ -124,31 +141,34 @@ describe('LDClientImpl', () => {
});

it('resolves immediately if the client is already ready', async () => {
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory() });
await client.waitForInitialization({ timeout: 10 });
await client.waitForInitialization({ timeout: 10 });
});

it('creates only one Promise when waiting for initialization - when not using a timeout', async () => {
client = createClient();
client = createClient({ updateProcessor: getUpdateProcessorFactory() });
const p1 = client.waitForInitialization();
const p2 = client.waitForInitialization();

expect(p2).toBe(p1);
});

it('rejects the returned promise when initialization does not complete within the timeout', async () => {
setupMockStreamingProcessor(undefined, undefined, undefined, undefined, undefined, 10000);
client = createClient();
client = createClient({
updateProcessor: getUpdateProcessorFactory(false, 10000),
});
await expect(async () => client.waitForInitialization({ timeout: 1 })).rejects.toThrow(
'waitForInitialization timed out after 1 seconds.',
);
});

it('logs an error when the initialization does not complete within the timeout', async () => {
setupMockStreamingProcessor(undefined, undefined, undefined, undefined, undefined, 10000);
const logger = new TestLogger();
client = createClient({ logger });
client = createClient({
logger,
updateProcessor: getUpdateProcessorFactory(false, 10000),
});
try {
await client.waitForInitialization({ timeout: 1 });
} catch {
Expand All @@ -163,14 +183,18 @@ describe('LDClientImpl', () => {
});

it('does not reject the returned promise when initialization completes within the timeout', async () => {
setupMockStreamingProcessor(undefined, undefined, undefined, undefined, undefined, 1000);
client = createClient();
await expect(async () => client.waitForInitialization({ timeout: 5 })).not.toThrow();
client = createClient({
updateProcessor: getUpdateProcessorFactory(false, 100),
});
await expect(client.waitForInitialization({ timeout: 3 })).resolves.not.toThrow();
});

it('logs when no timeout is set', async () => {
const logger = new TestLogger();
client = createClient({ logger });
client = createClient({
logger,
updateProcessor: getUpdateProcessorFactory(),
});
await client.waitForInitialization();
logger.expectMessages([
{
Expand All @@ -183,7 +207,10 @@ describe('LDClientImpl', () => {

it('logs when the timeout is too high', async () => {
const logger = new TestLogger();
client = createClient({ logger });
client = createClient({
logger,
updateProcessor: getUpdateProcessorFactory(),
});
await client.waitForInitialization({ timeout: Number.MAX_SAFE_INTEGER });

logger.expectMessages([
Expand All @@ -199,7 +226,7 @@ describe('LDClientImpl', () => {
'does not log when timeout is under high timeout threshold',
async (timeout) => {
const logger = new TestLogger();
client = createClient({ logger });
client = createClient({ logger, updateProcessor: getUpdateProcessorFactory() });
await client.waitForInitialization({ timeout });
expect(logger.getCount(LogLevel.Warn)).toBe(0);
},
Expand Down
Loading

0 comments on commit 1b21500

Please sign in to comment.