From 0861e498121cce71a6bde5d5ca106287d02cb142 Mon Sep 17 00:00:00 2001 From: RealAnna <89971034+RealAnna@users.noreply.github.com> Date: Fri, 5 May 2023 19:21:49 +0200 Subject: [PATCH] test: add events tests for web-sdk (#410) - added tests based on [spec PR](https://github.com/open-feature/spec/pull/171) Signed-off-by: RealAnna <89971034+RealAnna@users.noreply.github.com> --- packages/client/test/client.spec.ts | 3 +- packages/client/test/events.spec.ts | 158 ++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 packages/client/test/events.spec.ts diff --git a/packages/client/test/client.spec.ts b/packages/client/test/client.spec.ts index 37cd13d46..dfdc31968 100644 --- a/packages/client/test/client.spec.ts +++ b/packages/client/test/client.spec.ts @@ -426,4 +426,5 @@ describe('Evaluation details structure', () => { }); }); -// context tests are not needed? + + diff --git a/packages/client/test/events.spec.ts b/packages/client/test/events.spec.ts new file mode 100644 index 000000000..92b048016 --- /dev/null +++ b/packages/client/test/events.spec.ts @@ -0,0 +1,158 @@ +import { + Provider, + ProviderEvents, + OpenFeatureEventEmitter, + OpenFeatureAPI, +} from '../src'; + +const ERROR_REASON = 'error'; +const ERROR_CODE = 'MOCKED_ERROR'; + +const MOCK_PROVIDER = { + metadata: { + name: 'mock-events', + }, + initialize: jest.fn(() => { + return Promise.resolve(undefined); + }), + events: new OpenFeatureEventEmitter(), +} as unknown as Provider; + +const ERROR_MOCK_PROVIDER = { + metadata: { + name: 'mock-events-failure', + }, + initialize: jest.fn(() => { + return Promise.reject({ + reason: ERROR_REASON, + errorCode: ERROR_CODE, + }); + }), + events: new OpenFeatureEventEmitter(), +} as unknown as Provider; + + + +describe('Events', () => { + // set timeouts short for this suite. + jest.setTimeout(1000); + let API: OpenFeatureAPI = new (OpenFeatureAPI as any)(); + + afterEach(() => { + jest.clearAllMocks(); + API = new (OpenFeatureAPI as any)(); //TODO this hacky thing should be removed with provider mapping: https://github.com/open-feature/js-sdk/issues/421 + }); + + describe('Requirement 5.1.1', () => { + it('The provider defines a mechanism for signalling the occurrence of an event`PROVIDER_READY`', (done) => { + + MOCK_PROVIDER.events?.addListener(ProviderEvents.Ready, () => { + try { + expect(API.providerMetadata.name).toBe(MOCK_PROVIDER.metadata.name); + expect(MOCK_PROVIDER.initialize).toHaveBeenCalled(); + done(); + } catch (err) { + done(err); + } + }); + API.setProvider(MOCK_PROVIDER); + }); + + it('It defines a mechanism for signalling `PROVIDER_ERROR`', (done) => { + //make sure an error event is fired when initialize promise reject + ERROR_MOCK_PROVIDER.events?.addListener(ProviderEvents.Error, () => { + try { + expect(API.providerMetadata.name).toBe(ERROR_MOCK_PROVIDER.metadata.name); + expect(ERROR_MOCK_PROVIDER.initialize).toHaveBeenCalled(); + done(); + } catch (err) { + done(err); + } + }); + API.setProvider(ERROR_MOCK_PROVIDER); + }); + + }); + + describe('Requirement 5.2.1, 5.2.3, 5.2.4, 5.2.5 ', () => { + + it('If the provider `initialize` function terminates normally, `PROVIDER_READY` handlers MUST run', (done) => { + const client = API.getClient(); + client.addHandler(ProviderEvents.Ready, () => { + done(); + }); + API.setProvider(MOCK_PROVIDER); + }); + + it('If the provider `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers MUST run.', (done) => { + const client = API.getClient(); + client.addHandler(ProviderEvents.Error, () => { + done(); + }); + API.setProvider(ERROR_MOCK_PROVIDER); + }); + + it('It defines a mechanism for signalling `PROVIDER_CONFIGURATION_CHANGED`', (done) => { + const client = API.getClient(); + client.addHandler(ProviderEvents.ConfigurationChanged, () => { + done(); + }); + API.setProvider(MOCK_PROVIDER); + // emit a change event from the mock provider + MOCK_PROVIDER.events?.emit(ProviderEvents.ConfigurationChanged); + }); + + it('`PROVIDER_READY` handlers added after the provider is already in a ready state MUST run immediately.', (done) => { + const client = API.getClient(); + + MOCK_PROVIDER.events?.addListener(ProviderEvents.Ready, () => { + try { + expect(MOCK_PROVIDER.initialize).toHaveBeenCalled(); + jest.setTimeout(10); + let count = 0; + client.addHandler(ProviderEvents.Ready, () => { + if (count == 0) { + count++; + done(); + } + }); + } catch (err) { + done(err); + } + }); + + API.setProvider(MOCK_PROVIDER); + }); + }); + + describe('Requirement 5.2.6 ', () => { + it('Event handlers MUST persist across `provider` changes.', (done) => { + const client = API.getClient(); + + const myProvider: Provider = { + metadata: { + name: 'first', + }, + initialize: jest.fn(() => { + return Promise.resolve(undefined); + }), + events: new OpenFeatureEventEmitter(), + } as unknown as Provider; + + API.setProvider(myProvider); + let counter = 0; + client.addHandler(ProviderEvents.Ready, () => { + if (API.providerMetadata.name == myProvider.metadata.name) { + API.setProvider(MOCK_PROVIDER); + counter++; + } else { + expect(counter).toBeGreaterThan(0); + expect(API.providerMetadata.name).toBe(MOCK_PROVIDER.metadata.name); + if (counter == 1) { + done(); + } + } + }); + }); + }); +});