js-sdk/packages/server/test/open-feature.spec.ts

234 lines
9.4 KiB
TypeScript

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();
});
});
});