Skip to content

Commit a95aa2b

Browse files
committed
added checks to ensure that returned variant matches requested flag type and additional tests
Signed-off-by: jarebudev <[email protected]>
1 parent 2cee133 commit a95aa2b

File tree

2 files changed

+69
-36
lines changed

2 files changed

+69
-36
lines changed

libs/providers/unleash-web/src/lib/unleash-web-provider.spec.ts

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UnleashWebProvider } from './unleash-web-provider';
22
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
3-
import { OpenFeature, ProviderEvents } from '@openfeature/web-sdk';
3+
import { OpenFeature, ProviderEvents, TypeMismatchError } from '@openfeature/web-sdk';
44
import testdata from './testdata.json';
55
import TestLogger from './test-logger';
66

@@ -178,83 +178,95 @@ describe('UnleashWebProvider evaluations', () => {
178178
});
179179

180180
describe('method resolveBooleanEvaluation', () => {
181-
it('should return false for missing toggle', async () => {
182-
const evaluation = await provider.resolveBooleanEvaluation('nonExistent');
181+
it('should return false for missing toggle', () => {
182+
const evaluation = provider.resolveBooleanEvaluation('nonExistent');
183183
expect(evaluation).toHaveProperty(valueProperty, false);
184184
});
185185

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

191-
it('should return false if a disabled toggle exists', async () => {
192-
const evaluation = await provider.resolveBooleanEvaluation('disabledToggle');
191+
it('should return false if a disabled toggle exists', () => {
192+
const evaluation = provider.resolveBooleanEvaluation('disabledToggle');
193193
expect(evaluation).toHaveProperty(valueProperty, false);
194194
});
195195
});
196196

197197
describe('method resolveStringEvaluation', () => {
198-
it('should return default value for missing value', async () => {
199-
const evaluation = await provider.resolveStringEvaluation('nonExistent', 'defaultValue');
198+
it('should return default value for missing value', () => {
199+
const evaluation = provider.resolveStringEvaluation('nonExistent', 'defaultValue');
200200
expect(evaluation).toHaveProperty(valueProperty, 'defaultValue');
201201
});
202202

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

208-
it('should return default value if a toggle is disabled', async () => {
209-
const evaluation = await provider.resolveStringEvaluation('disabledVariant', 'defaultValue');
208+
it('should return default value if a toggle is disabled', () => {
209+
const evaluation = provider.resolveStringEvaluation('disabledVariant', 'defaultValue');
210210
expect(evaluation).toHaveProperty(valueProperty, 'defaultValue');
211211
});
212+
213+
it('should throw TypeMismatchError if requested variant type is not a string', () => {
214+
expect(() => provider.resolveStringEvaluation('variantToggleJson', 'default string')).toThrow(TypeMismatchError);
215+
});
212216
});
213217

214218
describe('method resolveNumberEvaluation', () => {
215-
it('should return default value for missing value', async () => {
216-
const evaluation = await provider.resolveNumberEvaluation('nonExistent', 5);
219+
it('should return default value for missing value', () => {
220+
const evaluation = provider.resolveNumberEvaluation('nonExistent', 5);
217221
expect(evaluation).toHaveProperty(valueProperty, 5);
218222
});
219223

220-
it('should return integer value if variant toggle exists and is enabled', async () => {
221-
const evaluation = await provider.resolveNumberEvaluation('variantToggleInteger', 0);
224+
it('should return integer value if variant toggle exists and is enabled', () => {
225+
const evaluation = provider.resolveNumberEvaluation('variantToggleInteger', 0);
222226
expect(evaluation).toHaveProperty(valueProperty, 3);
223227
});
224228

225-
it('should return double value if variant toggle exists and is enabled', async () => {
226-
const evaluation = await provider.resolveNumberEvaluation('variantToggleDouble', 0);
229+
it('should return double value if variant toggle exists and is enabled', () => {
230+
const evaluation = provider.resolveNumberEvaluation('variantToggleDouble', 0);
227231
expect(evaluation).toHaveProperty(valueProperty, 1.2);
228232
});
229233

230-
it('should return default value if a toggle is disabled', async () => {
231-
const evaluation = await provider.resolveNumberEvaluation('disabledVariant', 0);
234+
it('should return default value if a toggle is disabled', () => {
235+
const evaluation = provider.resolveNumberEvaluation('disabledVariant', 0);
232236
expect(evaluation).toHaveProperty(valueProperty, 0);
233237
});
238+
239+
it('should throw TypeMismatchError if requested variant type is not a number', () => {
240+
expect(() => provider.resolveNumberEvaluation('variantToggleCsv', 0)).toThrow(TypeMismatchError);
241+
});
234242
});
235243

236244
describe('method resolveObjectEvaluation', () => {
237-
it('should return default value for missing value', async () => {
245+
it('should return default value for missing value', () => {
238246
const defaultValue = '{"notFound" : true}';
239-
const evaluation = await provider.resolveObjectEvaluation('nonExistent', JSON.parse(defaultValue));
247+
const evaluation = provider.resolveObjectEvaluation('nonExistent', JSON.parse(defaultValue));
240248
expect(evaluation).toHaveProperty(valueProperty, JSON.parse(defaultValue));
241249
});
242250

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

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

254-
it('should return default value if a toggle is disabled', async () => {
262+
it('should return default value if a toggle is disabled', () => {
255263
const defaultValue = '{foo: bar}';
256-
const evaluation = await provider.resolveObjectEvaluation('disabledVariant', defaultValue);
264+
const evaluation = provider.resolveObjectEvaluation('disabledVariant', defaultValue);
257265
expect(evaluation).toHaveProperty(valueProperty, defaultValue);
258266
});
267+
268+
it('should throw TypeMismatchError if requested variant type is not json or csv', () => {
269+
expect(() => provider.resolveObjectEvaluation('variantToggleInteger', 'a,b,c,d')).toThrow(TypeMismatchError);
270+
});
259271
});
260272
});

libs/providers/unleash-web/src/lib/unleash-web-provider.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ProviderEvents,
99
ResolutionDetails,
1010
ProviderFatalError,
11+
TypeMismatchError,
1112
} from '@openfeature/web-sdk';
1213
import { UnleashClient } from 'unleash-proxy-client';
1314
import { UnleashConfig } from './unleash-web-provider-config';
@@ -122,32 +123,52 @@ export class UnleashWebProvider implements Provider {
122123
}
123124

124125
resolveStringEvaluation(flagKey: string, defaultValue: string): ResolutionDetails<string> {
125-
return this.evaluate(flagKey, defaultValue);
126+
return this.evaluate(flagKey, defaultValue, 'string');
126127
}
127128

128129
resolveNumberEvaluation(flagKey: string, defaultValue: number): ResolutionDetails<number> {
129-
const resolutionDetails = this.evaluate(flagKey, defaultValue);
130-
resolutionDetails.value = Number(resolutionDetails.value);
131-
return resolutionDetails;
130+
return this.evaluate(flagKey, defaultValue, 'number');
132131
}
133132

134133
resolveObjectEvaluation<U extends JsonValue>(flagKey: string, defaultValue: U): ResolutionDetails<U> {
135-
return this.evaluate(flagKey, defaultValue);
134+
return this.evaluate(flagKey, defaultValue, 'object');
136135
}
137136

138-
private evaluate<T>(flagKey: string, defaultValue: T): ResolutionDetails<T> {
137+
private throwTypeMismatchError(variant: string, variantType: string, flagType: string) {
138+
throw new TypeMismatchError(`Type of requested variant ${variant} is of type ${variantType} but requested flag type of ${flagType}`);
139+
}
140+
141+
private evaluate<T>(flagKey: string, defaultValue: T, flagType: string): ResolutionDetails<T> {
139142
const evaluatedVariant = this._client?.getVariant(flagKey);
140143
let value;
141144
let variant;
142145
if (typeof evaluatedVariant === 'undefined') {
143146
throw new FlagNotFoundError();
144147
}
145148

146-
if (evaluatedVariant.name === 'disabled') {
149+
if (evaluatedVariant.name === 'disabled' || typeof evaluatedVariant.payload === 'undefined') {
147150
value = defaultValue;
148151
} else {
149152
variant = evaluatedVariant.name;
150153
value = evaluatedVariant.payload?.value;
154+
155+
const variantType = evaluatedVariant.payload?.type;
156+
157+
if (flagType === 'string' && flagType !== variantType) {
158+
this.throwTypeMismatchError(variant, variantType, flagType);
159+
}
160+
if (flagType === 'number') {
161+
const numberValue = parseFloat(value);
162+
if (flagType !== variantType || isNaN(numberValue)) {
163+
this.throwTypeMismatchError(variant, variantType, flagType);
164+
}
165+
value = numberValue;
166+
}
167+
if (flagType === 'object') {
168+
if (variantType !== 'json' && variantType !== 'csv') {
169+
this.throwTypeMismatchError(variant, variantType, flagType);
170+
}
171+
}
151172
}
152173
return {
153174
variant: variant,

0 commit comments

Comments
 (0)