Skip to content

Commit 86ba602

Browse files
committed
chore(flag-evaluation): bump version to 0.1.5 and improve operator evaluations
- Updated version in package.json to 0.1.5. - Refined evaluation logic for "SET" and "NOT_SET" operators to use strict equality checks. - Added tests to ensure correct behavior for missing context fields with "SET" and "NOT_SET" operators, including handling empty string values.
1 parent e1e77eb commit 86ba602

3 files changed

Lines changed: 131 additions & 5 deletions

File tree

packages/flag-evaluation/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bucketco/flag-evaluation",
3-
"version": "0.1.4",
3+
"version": "0.1.5",
44
"license": "MIT",
55
"repository": {
66
"type": "git",

packages/flag-evaluation/src/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,9 @@ export function evaluate(
325325
: fieldValueDate < daysAgo.getTime();
326326
}
327327
case "SET":
328-
return fieldValue != "";
328+
return fieldValue !== "";
329329
case "NOT_SET":
330-
return fieldValue == "";
330+
return fieldValue === "";
331331
case "IS":
332332
return fieldValue === value;
333333
case "IS_NOT":
@@ -357,13 +357,17 @@ function evaluateRecursively(
357357
case "constant":
358358
return filter.value;
359359
case "context":
360-
if (!(filter.field in context)) {
360+
if (
361+
!(filter.field in context) &&
362+
filter.operator !== "SET" &&
363+
filter.operator !== "NOT_SET"
364+
) {
361365
missingContextFieldsSet.add(filter.field);
362366
return false;
363367
}
364368

365369
return evaluate(
366-
context[filter.field],
370+
context[filter.field] ?? "",
367371
filter.operator,
368372
filter.values || [],
369373
filter.valueSet,

packages/flag-evaluation/test/index.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,126 @@ describe("evaluate feature targeting integration ", () => {
311311
});
312312
});
313313

314+
it("should not report missing context fields for `SET` operator when field doesn't exist", () => {
315+
const res = evaluateFeatureRules({
316+
featureKey: "test_feature",
317+
rules: [
318+
{
319+
value: true,
320+
filter: {
321+
type: "context",
322+
field: "user.name",
323+
operator: "SET",
324+
values: [],
325+
},
326+
},
327+
],
328+
context: {},
329+
});
330+
331+
expect(res).toEqual({
332+
featureKey: "test_feature",
333+
value: undefined,
334+
context: {},
335+
ruleEvaluationResults: [false],
336+
reason: "no matched rules",
337+
missingContextFields: [],
338+
});
339+
});
340+
341+
it("should not report missing context fields for `NOT_SET` operator when field doesn't exist", () => {
342+
const res = evaluateFeatureRules({
343+
featureKey: "test_feature",
344+
rules: [
345+
{
346+
value: true,
347+
filter: {
348+
type: "context",
349+
field: "user.name",
350+
operator: "NOT_SET",
351+
values: [],
352+
},
353+
},
354+
],
355+
context: {},
356+
});
357+
358+
expect(res).toEqual({
359+
featureKey: "test_feature",
360+
value: true,
361+
context: {},
362+
ruleEvaluationResults: [true],
363+
reason: "rule #0 matched",
364+
missingContextFields: [],
365+
});
366+
});
367+
368+
it("should handle `SET` operator with empty string field value", () => {
369+
const res = evaluateFeatureRules({
370+
featureKey: "test_feature",
371+
rules: [
372+
{
373+
value: true,
374+
filter: {
375+
type: "context",
376+
field: "user.name",
377+
operator: "SET",
378+
values: [],
379+
},
380+
},
381+
],
382+
context: {
383+
user: {
384+
name: "",
385+
},
386+
},
387+
});
388+
389+
expect(res).toEqual({
390+
featureKey: "test_feature",
391+
value: undefined,
392+
context: {
393+
"user.name": "",
394+
},
395+
ruleEvaluationResults: [false],
396+
reason: "no matched rules",
397+
missingContextFields: [],
398+
});
399+
});
400+
401+
it("should handle `NOT_SET` operator with empty string field value", () => {
402+
const res = evaluateFeatureRules({
403+
featureKey: "test_feature",
404+
rules: [
405+
{
406+
value: true,
407+
filter: {
408+
type: "context",
409+
field: "user.name",
410+
operator: "NOT_SET",
411+
values: [],
412+
},
413+
},
414+
],
415+
context: {
416+
user: {
417+
name: "",
418+
},
419+
},
420+
});
421+
422+
expect(res).toEqual({
423+
featureKey: "test_feature",
424+
value: true,
425+
context: {
426+
"user.name": "",
427+
},
428+
ruleEvaluationResults: [true],
429+
reason: "rule #0 matched",
430+
missingContextFields: [],
431+
});
432+
});
433+
314434
it.each([
315435
{
316436
context: { "company.id": "company1" },
@@ -376,6 +496,8 @@ describe("operator evaluation", () => {
376496

377497
["value", "SET", "", true],
378498
["", "SET", "", false],
499+
["value", "NOT_SET", "", false],
500+
["", "NOT_SET", "", true],
379501

380502
// non numeric values should return false
381503
["value", "GT", "value", false],

0 commit comments

Comments
 (0)