Skip to content

Commit

Permalink
added checks to ensure that returned variant matches requested flag t…
Browse files Browse the repository at this point in the history
…ype and additional tests

Signed-off-by: jarebudev <23311805+jarebudev@users.noreply.github.com>
  • Loading branch information
jarebudev committed Dec 22, 2024
1 parent 2cee133 commit a95aa2b
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 36 deletions.
70 changes: 41 additions & 29 deletions libs/providers/unleash-web/src/lib/unleash-web-provider.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UnleashWebProvider } from './unleash-web-provider';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
import { OpenFeature, ProviderEvents, TypeMismatchError } from '@openfeature/web-sdk';
import testdata from './testdata.json';
import TestLogger from './test-logger';

Expand Down Expand Up @@ -178,83 +178,95 @@ describe('UnleashWebProvider evaluations', () => {
});

describe('method resolveBooleanEvaluation', () => {
it('should return false for missing toggle', async () => {
const evaluation = await provider.resolveBooleanEvaluation('nonExistent');
it('should return false for missing toggle', () => {
const evaluation = provider.resolveBooleanEvaluation('nonExistent');
expect(evaluation).toHaveProperty(valueProperty, false);
});

it('should return true if enabled toggle exists', async () => {
const evaluation = await provider.resolveBooleanEvaluation('simpleToggle');
it('should return true if enabled toggle exists', () => {
const evaluation = provider.resolveBooleanEvaluation('simpleToggle');
expect(evaluation).toHaveProperty(valueProperty, true);
});

it('should return false if a disabled toggle exists', async () => {
const evaluation = await provider.resolveBooleanEvaluation('disabledToggle');
it('should return false if a disabled toggle exists', () => {
const evaluation = provider.resolveBooleanEvaluation('disabledToggle');
expect(evaluation).toHaveProperty(valueProperty, false);
});
});

describe('method resolveStringEvaluation', () => {
it('should return default value for missing value', async () => {
const evaluation = await provider.resolveStringEvaluation('nonExistent', 'defaultValue');
it('should return default value for missing value', () => {
const evaluation = provider.resolveStringEvaluation('nonExistent', 'defaultValue');
expect(evaluation).toHaveProperty(valueProperty, 'defaultValue');
});

it('should return right value if variant toggle exists and is enabled', async () => {
const evaluation = await provider.resolveStringEvaluation('variantToggleString', 'variant1');
it('should return right value if variant toggle exists and is enabled', () => {
const evaluation = provider.resolveStringEvaluation('variantToggleString', 'variant1');
expect(evaluation).toHaveProperty(valueProperty, 'some-text');
});

it('should return default value if a toggle is disabled', async () => {
const evaluation = await provider.resolveStringEvaluation('disabledVariant', 'defaultValue');
it('should return default value if a toggle is disabled', () => {
const evaluation = provider.resolveStringEvaluation('disabledVariant', 'defaultValue');
expect(evaluation).toHaveProperty(valueProperty, 'defaultValue');
});

it('should throw TypeMismatchError if requested variant type is not a string', () => {
expect(() => provider.resolveStringEvaluation('variantToggleJson', 'default string')).toThrow(TypeMismatchError);
});
});

describe('method resolveNumberEvaluation', () => {
it('should return default value for missing value', async () => {
const evaluation = await provider.resolveNumberEvaluation('nonExistent', 5);
it('should return default value for missing value', () => {
const evaluation = provider.resolveNumberEvaluation('nonExistent', 5);
expect(evaluation).toHaveProperty(valueProperty, 5);
});

it('should return integer value if variant toggle exists and is enabled', async () => {
const evaluation = await provider.resolveNumberEvaluation('variantToggleInteger', 0);
it('should return integer value if variant toggle exists and is enabled', () => {
const evaluation = provider.resolveNumberEvaluation('variantToggleInteger', 0);
expect(evaluation).toHaveProperty(valueProperty, 3);
});

it('should return double value if variant toggle exists and is enabled', async () => {
const evaluation = await provider.resolveNumberEvaluation('variantToggleDouble', 0);
it('should return double value if variant toggle exists and is enabled', () => {
const evaluation = provider.resolveNumberEvaluation('variantToggleDouble', 0);
expect(evaluation).toHaveProperty(valueProperty, 1.2);
});

it('should return default value if a toggle is disabled', async () => {
const evaluation = await provider.resolveNumberEvaluation('disabledVariant', 0);
it('should return default value if a toggle is disabled', () => {
const evaluation = provider.resolveNumberEvaluation('disabledVariant', 0);
expect(evaluation).toHaveProperty(valueProperty, 0);
});

Check failure on line 238 in libs/providers/unleash-web/src/lib/unleash-web-provider.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-build (18.x)

Delete `··`

Check failure on line 238 in libs/providers/unleash-web/src/lib/unleash-web-provider.spec.ts

View workflow job for this annotation

GitHub Actions / lint-test-build (20.x)

Delete `··`
it('should throw TypeMismatchError if requested variant type is not a number', () => {
expect(() => provider.resolveNumberEvaluation('variantToggleCsv', 0)).toThrow(TypeMismatchError);
});
});

describe('method resolveObjectEvaluation', () => {
it('should return default value for missing value', async () => {
it('should return default value for missing value', () => {
const defaultValue = '{"notFound" : true}';
const evaluation = await provider.resolveObjectEvaluation('nonExistent', JSON.parse(defaultValue));
const evaluation = provider.resolveObjectEvaluation('nonExistent', JSON.parse(defaultValue));
expect(evaluation).toHaveProperty(valueProperty, JSON.parse(defaultValue));
});

it('should return json value if variant toggle exists and is enabled', async () => {
it('should return json value if variant toggle exists and is enabled', () => {
const expectedVariant = '{hello: world}';
const evaluation = await provider.resolveObjectEvaluation('variantToggleJson', JSON.parse('{"default": false}'));
const evaluation = provider.resolveObjectEvaluation('variantToggleJson', JSON.parse('{"default": false}'));
expect(evaluation).toHaveProperty(valueProperty, expectedVariant);
});

it('should return csv value if variant toggle exists and is enabled', async () => {
const evaluation = await provider.resolveObjectEvaluation('variantToggleCsv', 'a,b,c,d');
it('should return csv value if variant toggle exists and is enabled', () => {
const evaluation = provider.resolveObjectEvaluation('variantToggleCsv', 'a,b,c,d');
expect(evaluation).toHaveProperty(valueProperty, '1,2,3,4');
});

it('should return default value if a toggle is disabled', async () => {
it('should return default value if a toggle is disabled', () => {
const defaultValue = '{foo: bar}';
const evaluation = await provider.resolveObjectEvaluation('disabledVariant', defaultValue);
const evaluation = provider.resolveObjectEvaluation('disabledVariant', defaultValue);
expect(evaluation).toHaveProperty(valueProperty, defaultValue);
});

it('should throw TypeMismatchError if requested variant type is not json or csv', () => {
expect(() => provider.resolveObjectEvaluation('variantToggleInteger', 'a,b,c,d')).toThrow(TypeMismatchError);
});
});
});
35 changes: 28 additions & 7 deletions libs/providers/unleash-web/src/lib/unleash-web-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProviderEvents,
ResolutionDetails,
ProviderFatalError,
TypeMismatchError,
} from '@openfeature/web-sdk';
import { UnleashClient } from 'unleash-proxy-client';
import { UnleashConfig } from './unleash-web-provider-config';
Expand Down Expand Up @@ -122,32 +123,52 @@ export class UnleashWebProvider implements Provider {
}

resolveStringEvaluation(flagKey: string, defaultValue: string): ResolutionDetails<string> {
return this.evaluate(flagKey, defaultValue);
return this.evaluate(flagKey, defaultValue, 'string');
}

resolveNumberEvaluation(flagKey: string, defaultValue: number): ResolutionDetails<number> {
const resolutionDetails = this.evaluate(flagKey, defaultValue);
resolutionDetails.value = Number(resolutionDetails.value);
return resolutionDetails;
return this.evaluate(flagKey, defaultValue, 'number');
}

resolveObjectEvaluation<U extends JsonValue>(flagKey: string, defaultValue: U): ResolutionDetails<U> {
return this.evaluate(flagKey, defaultValue);
return this.evaluate(flagKey, defaultValue, 'object');
}

private evaluate<T>(flagKey: string, defaultValue: T): ResolutionDetails<T> {
private throwTypeMismatchError(variant: string, variantType: string, flagType: string) {
throw new TypeMismatchError(`Type of requested variant ${variant} is of type ${variantType} but requested flag type of ${flagType}`);

Check failure on line 138 in libs/providers/unleash-web/src/lib/unleash-web-provider.ts

View workflow job for this annotation

GitHub Actions / lint-test-build (18.x)

Replace ``Type·of·requested·variant·${variant}·is·of·type·${variantType}·but·requested·flag·type·of·${flagType}`` with `⏎······`Type·of·requested·variant·${variant}·is·of·type·${variantType}·but·requested·flag·type·of·${flagType}`,⏎····`

Check failure on line 138 in libs/providers/unleash-web/src/lib/unleash-web-provider.ts

View workflow job for this annotation

GitHub Actions / lint-test-build (20.x)

Replace ``Type·of·requested·variant·${variant}·is·of·type·${variantType}·but·requested·flag·type·of·${flagType}`` with `⏎······`Type·of·requested·variant·${variant}·is·of·type·${variantType}·but·requested·flag·type·of·${flagType}`,⏎····`
}

private evaluate<T>(flagKey: string, defaultValue: T, flagType: string): ResolutionDetails<T> {
const evaluatedVariant = this._client?.getVariant(flagKey);
let value;
let variant;
if (typeof evaluatedVariant === 'undefined') {
throw new FlagNotFoundError();
}

if (evaluatedVariant.name === 'disabled') {
if (evaluatedVariant.name === 'disabled' || typeof evaluatedVariant.payload === 'undefined') {
value = defaultValue;
} else {
variant = evaluatedVariant.name;
value = evaluatedVariant.payload?.value;

const variantType = evaluatedVariant.payload?.type;

if (flagType === 'string' && flagType !== variantType) {
this.throwTypeMismatchError(variant, variantType, flagType);
}
if (flagType === 'number') {
const numberValue = parseFloat(value);
if (flagType !== variantType || isNaN(numberValue)) {
this.throwTypeMismatchError(variant, variantType, flagType);
}
value = numberValue;
}
if (flagType === 'object') {
if (variantType !== 'json' && variantType !== 'csv') {
this.throwTypeMismatchError(variant, variantType, flagType);
}
}
}
return {
variant: variant,
Expand Down

0 comments on commit a95aa2b

Please sign in to comment.