Skip to content

Commit 457811b

Browse files
aepfliCopilot
andcommitted
feat(evaluator): port new test scenarios from PRs #341-345
- semver edge cases: v-prefix, partial version, build metadata (@semver-edge-cases) - operator error null-return fallback (@operator-errors): semver invalid version/operator, fractional null bucket key - nested $ref resolution via is_privileged (@evaluator-ref-edge-cases) - nested fractional expressions: if/var as bucket variant names (@fractional-nested) - fractional hash edge cases (@fractional-v2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
1 parent 1a274c8 commit 457811b

4 files changed

Lines changed: 217 additions & 0 deletions

File tree

evaluator/flags/testkit-flags.json

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,92 @@
374374
null
375375
]
376376
}
377+
},
378+
"fractional-hash-edge-flag": {
379+
"state": "ENABLED",
380+
"variants": { "lower": "lower", "upper": "upper" },
381+
"defaultVariant": "lower",
382+
"targeting": {
383+
"fractional": [
384+
{ "var": "targetingKey" },
385+
[ "lower", 50 ],
386+
[ "upper", 50 ]
387+
]
388+
}
389+
},
390+
"semver-v-prefix-flag": {
391+
"state": "ENABLED",
392+
"variants": { "match": "match", "no-match": "no-match" },
393+
"defaultVariant": "no-match",
394+
"targeting": { "if": [{"sem_ver": [{"var": "version"}, "=", "v1.0.0"]}, "match", "no-match"] }
395+
},
396+
"semver-partial-version-flag": {
397+
"state": "ENABLED",
398+
"variants": { "match": "match", "no-match": "no-match" },
399+
"defaultVariant": "no-match",
400+
"targeting": { "if": [{"sem_ver": [{"var": "version"}, "^", "1"]}, "match", "no-match"] }
401+
},
402+
"semver-build-metadata-flag": {
403+
"state": "ENABLED",
404+
"variants": { "match": "match", "no-match": "no-match" },
405+
"defaultVariant": "no-match",
406+
"targeting": { "if": [{"sem_ver": [{"var": "version"}, "=", "1.0.0+build"]}, "match", "no-match"] }
407+
},
408+
"semver-invalid-version-flag": {
409+
"state": "ENABLED",
410+
"variants": { "match": "match", "no-match": "no-match", "fallback": "fallback" },
411+
"defaultVariant": "fallback",
412+
"targeting": { "sem_ver": [{"var": "version"}, "=", "1.0.0"] }
413+
},
414+
"semver-invalid-operator-flag": {
415+
"state": "ENABLED",
416+
"variants": { "match": "match", "no-match": "no-match", "fallback": "fallback" },
417+
"defaultVariant": "fallback",
418+
"targeting": { "sem_ver": [{"var": "version"}, "===", "1.0.0"] }
419+
},
420+
"fractional-null-bucket-key-flag": {
421+
"state": "ENABLED",
422+
"variants": { "one": "one", "two": "two", "fallback": "fallback" },
423+
"defaultVariant": "fallback",
424+
"targeting": {
425+
"fractional": [
426+
{"var": "missing_key"},
427+
["one", 50],
428+
["two", 50]
429+
]
430+
}
431+
},
432+
"nested-ref-targeted-flag": {
433+
"state": "ENABLED",
434+
"variants": { "privileged": "privileged", "standard": "standard", "none": "none" },
435+
"defaultVariant": "none",
436+
"targeting": {
437+
"if": [{"$ref": "is_privileged"}, "privileged", "standard"]
438+
}
439+
},
440+
"fractional-nested-if-flag": {
441+
"state": "ENABLED",
442+
"variants": { "premium": "premium", "standard": "standard", "fallback": "fallback" },
443+
"defaultVariant": "fallback",
444+
"targeting": {
445+
"fractional": [
446+
{ "var": "targetingKey" },
447+
[{"if": [{"==": [{"var": "tier"}, "premium"]}, "premium", "standard"]}, 50],
448+
[ "standard", 50 ]
449+
]
450+
}
451+
},
452+
"fractional-nested-var-flag": {
453+
"state": "ENABLED",
454+
"variants": { "red": "red", "green": "green", "blue": "blue", "fallback": "fallback" },
455+
"defaultVariant": "fallback",
456+
"targeting": {
457+
"fractional": [
458+
{ "var": "targetingKey" },
459+
[ { "var": "color" }, 50 ],
460+
[ "blue", 50 ]
461+
]
462+
}
377463
}
378464
},
379465
"$evaluators": {
@@ -382,6 +468,12 @@
382468
"ballmer@macrosoft.com",
383469
{"var": ["email"]}
384470
]
471+
},
472+
"is_privileged": {
473+
"or": [
474+
{"$ref": "is_ballmer"},
475+
{"==": ["admin", {"var": ["role"]}]}
476+
]
385477
}
386478
}
387479
}

evaluator/gherkin/evaluator-refs.feature

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,19 @@ Feature: Evaluator evaluator refs
1313
| key | value |
1414
| some-email-targeted-flag | hi |
1515
| some-other-email-targeted-flag | yes |
16+
17+
# Nested $ref resolution — is_privileged references is_ballmer.
18+
# Use -t "not @evaluator-ref-edge-cases" to exclude during transition.
19+
@evaluator-ref-edge-cases
20+
Scenario Outline: Nested evaluator ref resolution
21+
Given an evaluator
22+
And a String-flag with key "nested-ref-targeted-flag" and a fallback value "fallback"
23+
And a context containing a key "<context_key>", with type "String" and with value "<context_value>"
24+
When the flag was evaluated with details
25+
Then the resolved details value should be "<value>"
26+
27+
Examples:
28+
| context_key | context_value | value |
29+
| email | ballmer@macrosoft.com | privileged |
30+
| role | admin | privileged |
31+
| email | other@example.com | standard |

evaluator/gherkin/fractional.feature

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,59 @@ Feature: Evaluator fractional operator
5656
| queen | ace-of-spades |
5757
| ten | ace-of-hearts |
5858
| nine | ace-of-diamonds |
59+
60+
# Hash edge-case vectors — keys chosen by brute-force search so their
61+
# MurmurHash3-x86-32 (seed=0) falls at the six critical boundary values.
62+
@fractional-v2
63+
Scenario Outline: Fractional operator hash edge cases
64+
Given a String-flag with key "fractional-hash-edge-flag" and a fallback value "fallback"
65+
And a context containing a targeting key with value "<key>"
66+
When the flag was evaluated with details
67+
Then the resolved details value should be "<value>"
68+
69+
Examples:
70+
| key | value |
71+
| ejOoVL | lower |
72+
| bY9fO- | lower |
73+
| SI7p- | lower |
74+
| 6LvT0 | upper |
75+
| ceQdGm | upper |
76+
77+
# Nested JSON Logic expressions as bucket variant names.
78+
# Use -t "not @fractional-nested" to exclude during transition.
79+
@fractional-nested
80+
Scenario Outline: Fractional operator with nested if expression as variant name
81+
Given a String-flag with key "fractional-nested-if-flag" and a fallback value "fallback"
82+
And a context containing a targeting key with value "<targetingKey>"
83+
And a context containing a key "tier", with type "String" and with value "<tier>"
84+
When the flag was evaluated with details
85+
Then the resolved details value should be "<value>"
86+
87+
Examples:
88+
| targetingKey | tier | value |
89+
| jon@company.com | premium | premium |
90+
| jon@company.com | basic | standard |
91+
| user1 | premium | standard |
92+
| user1 | basic | standard |
93+
94+
@fractional-nested
95+
Scenario Outline: Fractional operator with nested var expression as variant name
96+
Given a String-flag with key "fractional-nested-var-flag" and a fallback value "fallback"
97+
And a context containing a targeting key with value "<targetingKey>"
98+
And a context containing a key "color", with type "String" and with value "<color>"
99+
When the flag was evaluated with details
100+
Then the resolved details value should be "<value>"
101+
102+
Examples:
103+
| targetingKey | color | value |
104+
| jon@company.com | red | red |
105+
| jon@company.com | green | green |
106+
| user1 | red | blue |
107+
| jon@company.com | yellow | fallback |
108+
| jon@company.com | | fallback |
109+
110+
@operator-errors
111+
Scenario: Fractional operator with missing bucket key falls back to default variant
112+
Given a String-flag with key "fractional-null-bucket-key-flag" and a fallback value "wrong"
113+
When the flag was evaluated with details
114+
Then the resolved details value should be "fallback"

evaluator/gherkin/semver.feature

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,56 @@ Feature: Evaluator semantic version operator
3131
| 3.0.1 | minor |
3232
| 3.1.0 | major |
3333
| 4.0.0 | none |
34+
35+
# Edge cases: v-prefix, partial versions, build metadata.
36+
# Use -t "not @semver-edge-cases" to exclude during transition.
37+
@semver-edge-cases
38+
Scenario Outline: sem_ver v-prefix handling
39+
Given a String-flag with key "semver-v-prefix-flag" and a fallback value "fallback"
40+
And a context containing a key "version", with type "String" and with value "<version>"
41+
When the flag was evaluated with details
42+
Then the resolved details value should be "<value>"
43+
44+
Examples:
45+
| version | value |
46+
| 1.0.0 | match |
47+
| v1.0.0 | match |
48+
| 2.0.0 | no-match |
49+
50+
@semver-edge-cases
51+
Scenario Outline: sem_ver partial version handling
52+
Given a String-flag with key "semver-partial-version-flag" and a fallback value "fallback"
53+
And a context containing a key "version", with type "String" and with value "<version>"
54+
When the flag was evaluated with details
55+
Then the resolved details value should be "<value>"
56+
57+
Examples:
58+
| version | value |
59+
| 1.5.0 | match |
60+
| 1.0.0 | match |
61+
| 2.0.0 | no-match |
62+
63+
@semver-edge-cases
64+
Scenario Outline: sem_ver build metadata ignored
65+
Given a String-flag with key "semver-build-metadata-flag" and a fallback value "fallback"
66+
And a context containing a key "version", with type "String" and with value "<version>"
67+
When the flag was evaluated with details
68+
Then the resolved details value should be "<value>"
69+
70+
Examples:
71+
| version | value |
72+
| 1.0.0 | match |
73+
| 1.0.0+other | match |
74+
| 2.0.0 | no-match |
75+
76+
@operator-errors
77+
Scenario Outline: sem_ver operator errors return null and fall back to default variant
78+
Given a String-flag with key "<key>" and a fallback value "wrong"
79+
And a context containing a key "version", with type "String" and with value "<context_value>"
80+
When the flag was evaluated with details
81+
Then the resolved details value should be "<value>"
82+
83+
Examples:
84+
| key | context_value | value |
85+
| semver-invalid-version-flag | not-a-version | fallback |
86+
| semver-invalid-operator-flag | 1.0.0 | fallback |

0 commit comments

Comments
 (0)