Skip to content

Commit

Permalink
fix: Better handle waiting for initialization for failure cases. (#314)
Browse files Browse the repository at this point in the history
Affects #312
  • Loading branch information
kinyoklion authored Nov 14, 2023
1 parent f45910f commit 16515df
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
42 changes: 42 additions & 0 deletions packages/shared/sdk-server/__tests__/LDClientImpl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ describe('LDClientImpl', () => {
expect(callbacks.onError).not.toBeCalled();
});

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

setTimeout(async () => {
const initializedClient = await client.waitForInitialization();
expect(initializedClient).toEqual(client);
done();
}, 10);
});

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

const initializedClient = await client.waitForInitialization();
expect(initializedClient).toEqual(client);
});

it('fires ready event in offline mode', async () => {
client = createClient({ offline: true });
const initializedClient = await client.waitForInitialization();
Expand All @@ -74,6 +93,29 @@ describe('LDClientImpl', () => {
expect(callbacks.onError).toBeCalled();
});

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

setTimeout(async () => {
await expect(client.waitForInitialization()).rejects.toThrow('failed');

expect(client.initialized()).toBeFalsy();
expect(callbacks.onReady).not.toBeCalled();
expect(callbacks.onFailed).toBeCalled();
expect(callbacks.onError).toBeCalled();
done();
}, 10);
});

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

await expect(client.waitForInitialization()).rejects.toThrow('failed');
await expect(client.waitForInitialization()).rejects.toThrow('failed');
});

it('isOffline returns true in offline mode', () => {
client = createClient({ offline: true });
expect(client.isOffline()).toEqual(true);
Expand Down
26 changes: 25 additions & 1 deletion packages/shared/sdk-server/src/LDClientImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export default class LDClientImpl implements LDClient {

private initReject?: (err: Error) => void;

private rejectionReason: Error | undefined;

private initializedPromise?: Promise<LDClient>;

private logger?: LDLogger;
Expand Down Expand Up @@ -231,9 +233,30 @@ export default class LDClientImpl implements LDClient {
}

waitForInitialization(): Promise<LDClient> {
// An initialization promise is only created if someone is going to use that promise.
// If we always created an initialization promise, and there was no call waitForInitialization
// by the time the promise was rejected, then that would result in an unhandled promise
// rejection.

// Initialization promise was created by a previous call to waitForInitialization.
if (this.initializedPromise) {
return this.initializedPromise;
}

// Initialization completed before waitForInitialization was called, so we have completed
// and there was no promise. So we make a resolved promise and return it.
if (this.initState === InitState.Initialized) {
return Promise.resolve(this);
this.initializedPromise = Promise.resolve(this);
return this.initializedPromise;
}

// Initialization failed before waitForInitialization was called, so we have completed
// and there was no promise. So we make a rejected promise and return it.
if (this.initState === InitState.Failed) {
this.initializedPromise = Promise.reject(this.rejectionReason);
return this.initializedPromise;
}

if (!this.initializedPromise) {
this.initializedPromise = new Promise((resolve, reject) => {
this.initResolve = resolve;
Expand Down Expand Up @@ -703,6 +726,7 @@ export default class LDClientImpl implements LDClient {

if (!this.initialized()) {
this.initState = InitState.Failed;
this.rejectionReason = error;
this.initReject?.(error);
}
}
Expand Down

0 comments on commit 16515df

Please sign in to comment.