Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: onContextChanged not running for named providers #491

Merged
merged 5 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/client/src/open-feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider> implements Ma
async setContext(context: EvaluationContext): Promise<void> {
const oldContext = this._context;
this._context = context;
await this._defaultProvider?.onContextChange?.(oldContext, context);

const allProviders = [this._defaultProvider, ...this._clientProviders.values()];
await Promise.all(allProviders.map((provider) => provider.onContextChange?.(oldContext, context)));
}

getContext(): EvaluationContext {
Expand Down
70 changes: 70 additions & 0 deletions packages/client/test/evaluation-context.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { EvaluationContext, JsonValue, OpenFeature, Provider, ProviderMetadata, ResolutionDetails } from '../src';

class MockProvider implements Provider {
readonly metadata: ProviderMetadata;

constructor(options?: { name?: string }) {
this.metadata = { name: options?.name ?? 'mock-provider' };
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
onContextChange(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
return Promise.resolve();
}

resolveBooleanEvaluation(): ResolutionDetails<boolean> {
throw new Error('Not implemented');
}

resolveNumberEvaluation(): ResolutionDetails<number> {
throw new Error('Not implemented');
}

resolveObjectEvaluation<T extends JsonValue>(): ResolutionDetails<T> {
throw new Error('Not implemented');
}

resolveStringEvaluation(): ResolutionDetails<string> {
throw new Error('Not implemented');
}
}

describe('Evaluation Context', () => {
describe('Requirement 3.2.2', () => {
it('the API MUST have a method for setting the global evaluation context', () => {
const context: EvaluationContext = { property1: false };
OpenFeature.setContext(context);
expect(OpenFeature.getContext()).toEqual(context);
});
});

describe('Requirement 3.2.4', () => {
describe('when the global evaluation context is set, the on context changed handler MUST run', () => {
it('on all registered providers', async () => {
// Set initial context
const context: EvaluationContext = { property1: false };
await OpenFeature.setContext(context);

// Set some providers
const defaultProvider = new MockProvider();
const provider1 = new MockProvider();
const provider2 = new MockProvider();

OpenFeature.setProvider(defaultProvider);
OpenFeature.setProvider('client1', provider1);
OpenFeature.setProvider('client2', provider2);

// Spy on context changed handlers of all providers
const contextChangedSpys = [defaultProvider, provider1, provider2].map((provider) =>
jest.spyOn(provider, 'onContextChange')
);

// Change context
const newContext: EvaluationContext = { property1: true, property2: 'prop2' };
await OpenFeature.setContext(newContext);

contextChangedSpys.forEach((spy) => expect(spy).toHaveBeenCalledWith(context, newContext));
});
});
});
});