import type { Paradigm } from '@openfeature/core'; import type { Provider, ProviderStatus } from '../src'; import { OpenFeature, OpenFeatureAPI } from '../src'; import { OpenFeatureClient } from '../src/client/internal/open-feature-client'; const mockProvider = (config?: { initialStatus?: ProviderStatus; runsOn?: Paradigm }) => { return { metadata: { name: 'mock-events-success', }, runsOn: config?.runsOn || 'server', initialize: jest.fn(() => { return Promise.resolve('started'); }), onClose: jest.fn(() => { return Promise.resolve('closed'); }), } as unknown as Provider; }; describe('OpenFeature', () => { afterEach(async () => { await OpenFeature.clearProviders(); jest.clearAllMocks(); }); describe('Requirement 1.1.1', () => { it('OpenFeatureAPI should be a singleton', () => { expect(OpenFeature === OpenFeatureAPI.getInstance()).toBeTruthy(); }); }); describe('Requirement 1.1.2', () => { it('should equal previously set provider', () => { const fakeProvider = { metadata: { name: 'test' } } as unknown as Provider; OpenFeature.setProvider(fakeProvider); expect(OpenFeature.providerMetadata === fakeProvider.metadata).toBeTruthy(); }); describe('Requirement 1.1.2.1', () => { it('should throw because the provider is not intended for the server', () => { const provider = mockProvider({ runsOn: 'client' }); expect(() => OpenFeature.setProvider(provider)).toThrow( "Provider 'mock-events-success' is intended for use on the client.", ); }); it('should succeed because the provider is intended for the server', () => { const provider = mockProvider({ runsOn: 'server' }); expect(() => OpenFeature.setProvider(provider)).not.toThrow(); }); }); describe('Requirement 1.1.2.2', () => { it('MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values', () => { const provider = mockProvider(); OpenFeature.setProvider(provider); expect(OpenFeature.providerMetadata.name).toBe('mock-events-success'); expect(provider.initialize).toHaveBeenCalled(); }); }); describe('Requirement 1.1.2.3', () => { it("MUST invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.", () => { const fakeProvider = { metadata: { name: 'test' } } as unknown as Provider; const provider = mockProvider(); OpenFeature.setProvider(provider); OpenFeature.setProvider(fakeProvider); expect(provider.onClose).toHaveBeenCalled(); }); }); }); describe('Requirement 1.1.3', () => { it('should set the default provider if no domain is provided', () => { const provider = mockProvider(); OpenFeature.setProvider(provider); const registeredProvider = OpenFeature.getProvider(); expect(registeredProvider).toEqual(provider); }); it('should not change providers associated with a domain when setting a new default provider', () => { const domain = 'my-domain'; const fakeProvider = { metadata: { name: 'test' } } as unknown as Provider; const provider = mockProvider(); OpenFeature.setProvider(provider); OpenFeature.setProvider(domain, fakeProvider); const defaultProvider = OpenFeature.getProvider(); const domainSpecificProvider = OpenFeature.getProvider(domain); expect(defaultProvider).toEqual(provider); expect(domainSpecificProvider).toEqual(fakeProvider); }); it('should bind a new provider to existing clients in a matching domain', () => { const domain = 'my-domain'; const fakeProvider = { metadata: { name: 'test' } } as unknown as Provider; const provider = mockProvider(); OpenFeature.setProvider(provider); const domainSpecificClient = OpenFeature.getClient(domain); OpenFeature.setProvider(domain, fakeProvider); expect(domainSpecificClient.metadata.providerMetadata.name).toEqual(fakeProvider.metadata.name); }); it('should close a provider if it is replaced and no other client uses it', async () => { const provider1 = { ...mockProvider(), onClose: jest.fn() }; const provider2 = { ...mockProvider(), onClose: jest.fn() }; OpenFeature.setProvider('domain1', provider1); expect(provider1.onClose).not.toHaveBeenCalled(); OpenFeature.setProvider('domain1', provider2); expect(provider1.onClose).toHaveBeenCalledTimes(1); }); it('should not close provider if it is used by another client', async () => { const provider1 = { ...mockProvider(), onClose: jest.fn() }; await OpenFeature.setProviderAndWait('domain1', provider1); await OpenFeature.setProviderAndWait('domain2', provider1); await OpenFeature.setProviderAndWait('domain1', { ...provider1 }); expect(provider1.onClose).not.toHaveBeenCalled(); await OpenFeature.setProviderAndWait('domain2', { ...provider1 }); expect(provider1.onClose).toHaveBeenCalledTimes(1); }); it('should return the default provider metadata when passing an unregistered domain', async () => { const mockProvider = { metadata: { name: 'test' } } as unknown as Provider; OpenFeature.setProvider(mockProvider); const metadata = OpenFeature.getProviderMetadata('unused'); expect(metadata.name === mockProvider.metadata.name).toBeTruthy(); }); it('should return the domain specific provider metadata when passing a registered domain', async () => { const mockProvider = { metadata: { name: 'mock' } } as unknown as Provider; const mockDomainProvider = { metadata: { name: 'named-mock' } } as unknown as Provider; OpenFeature.setProvider(mockProvider); OpenFeature.setProvider('mocked', mockDomainProvider); const metadata = OpenFeature.getProviderMetadata('mocked'); expect(metadata.name === mockDomainProvider.metadata.name).toBeTruthy(); }); }); describe('Requirement 1.1.4', () => { it('should allow addition of hooks', () => { expect(OpenFeature.addHooks).toBeDefined(); }); }); describe('Requirement 1.1.5', () => { it('should implement a provider metadata accessor and mutator', () => { expect(OpenFeature.providerMetadata).toBeDefined(); }); }); describe('Requirement 1.1.6', () => { it('should implement a client factory', () => { expect(OpenFeature.getClient).toBeDefined(); expect(OpenFeature.getClient()).toBeInstanceOf(OpenFeatureClient); const domain = 'my-domain'; const domainSpecificClient = OpenFeature.getClient(domain); // check that using a named configuration also works as expected. expect(domainSpecificClient).toBeInstanceOf(OpenFeatureClient); // Alias for domain, left for backwards compatibility expect(domainSpecificClient.metadata.name).toEqual(domain); expect(domainSpecificClient.metadata.domain).toEqual(domain); }); it('should return a client with the default provider if no provider has been bound to the domain', () => { const domainSpecificClient = OpenFeature.getClient('unbound'); expect(domainSpecificClient.metadata.providerMetadata.name).toEqual(OpenFeature.providerMetadata.name); }); it('should return a client with the provider bound to the domain', () => { const domain = 'my-domain'; const fakeProvider = { metadata: { name: 'test' } } as unknown as Provider; OpenFeature.setProvider(domain, fakeProvider); const namedClient = OpenFeature.getClient(domain); expect(namedClient.metadata.providerMetadata.name).toEqual(fakeProvider.metadata.name); }); it('should be chainable', () => { const client = OpenFeature.addHooks().clearHooks().setLogger(console).getClient(); expect(client).toBeDefined(); }); }); describe('Requirement 1.6.1', () => { it('MUST define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider', async () => { const provider = mockProvider(); OpenFeature.setProvider(provider); expect(OpenFeature.providerMetadata.name).toBe('mock-events-success'); await OpenFeature.close(); expect(provider.onClose).toHaveBeenCalled(); }); it('runs the shutdown function on all providers for all clients', async () => { const provider = mockProvider(); OpenFeature.setProvider(provider); OpenFeature.setProvider('domain1', { ...provider }); OpenFeature.setProvider('domain2', { ...provider }); expect(OpenFeature.providerMetadata.name).toBe(provider.metadata.name); await OpenFeature.close(); expect(provider.onClose).toHaveBeenCalledTimes(3); }); it('runs the shutdown function on all providers for all clients even if some fail', async () => { const failingClose = jest.fn(() => { throw Error(); }); const provider1 = { ...mockProvider(), onClose: failingClose }; const provider2 = { ...mockProvider(), onClose: failingClose }; const provider3 = mockProvider(); OpenFeature.setProvider(provider1); OpenFeature.setProvider('domain1', provider2); OpenFeature.setProvider('domain2', provider3); expect(OpenFeature.providerMetadata.name).toBe(provider1.metadata.name); await OpenFeature.close(); expect(provider3.onClose).toHaveBeenCalled(); }); }); });