From ecbfeedda5be133ef1e1e3264bbac0d53331d886 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Mon, 16 Mar 2026 11:58:33 +0100 Subject: [PATCH 1/2] test(fractional): add nested fractional gherkin scenarios Add two new flag definitions and @fractional-nested tagged scenario outlines to validate nested JSON Logic expressions as bucket variant names in the fractional operator. New flags: - fractional-nested-if-flag: bucket variant name is an if expression that resolves to 'premium' or 'standard' based on context 'tier' - fractional-nested-var-flag: bucket variant name is a var expression that reads 'color' from evaluation context New scenarios (tagged @fractional @fractional-nested): - 'Fractional operator with nested if expression as variant name' - 'Fractional operator with nested var expression as variant name' The @fractional-nested tag allows providers to exclude these scenarios during the transition period before their implementation supports nested expressions: -t 'not @fractional-nested' All rows verified against the Rust flagd-evaluator test suite. Signed-off-by: Simon Schrottner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- flags/custom-ops.json | 42 +++++++++++++++++++++++++++++++++++++++ gherkin/targeting.feature | 34 +++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/flags/custom-ops.json b/flags/custom-ops.json index 8f56bd3..31e8f6b 100644 --- a/flags/custom-ops.json +++ b/flags/custom-ops.json @@ -99,6 +99,48 @@ ] } }, + "fractional-nested-if-flag": { + "state": "ENABLED", + "variants": { + "premium": "premium", + "standard": "standard", + "fallback": "fallback" + }, + "defaultVariant": "fallback", + "targeting": { + "fractional": [ + { "var": "targetingKey" }, + [ + { + "if": [ + { "==": [{ "var": "tier" }, "premium"] }, + "premium", + "standard" + ] + }, + 50 + ], + [ "standard", 50 ] + ] + } + }, + "fractional-nested-var-flag": { + "state": "ENABLED", + "variants": { + "red": "red", + "green": "green", + "blue": "blue", + "fallback": "fallback" + }, + "defaultVariant": "fallback", + "targeting": { + "fractional": [ + { "var": "targetingKey" }, + [ { "var": "color" }, 50 ], + [ "blue", 50 ] + ] + } + }, "starts-ends-flag": { "state": "ENABLED", "variants": { diff --git a/gherkin/targeting.feature b/gherkin/targeting.feature index eac8927..00eccb3 100644 --- a/gherkin/targeting.feature +++ b/gherkin/targeting.feature @@ -135,6 +135,40 @@ Feature: Targeting rules | 6LvT0 | upper | | ceQdGm | upper | + # Nested JSON Logic expressions as bucket variant names. + # Requires providers to support the @fractional-nested feature. + # Use -t "not @fractional-nested" to exclude during transition. + @fractional @fractional-nested + Scenario Outline: Fractional operator with nested if expression as variant name + # fractional-nested-if-flag: seed=targetingKey, bucket0=[if(tier=="premium","premium","standard"),50], bucket1=["standard",50] + # jon@company.com bv(100)=36 → bucket0; user1 bv(100)=76 → bucket1 + Given a String-flag with key "fractional-nested-if-flag" and a default value "fallback" + And a context containing a targeting key with value "" + And a context containing a key "tier", with type "String" and with value "" + When the flag was evaluated with details + Then the resolved details value should be "" + Examples: + | targetingKey | tier | value | + | jon@company.com | premium | premium | + | jon@company.com | basic | standard | + | user1 | premium | standard | + | user1 | basic | standard | + + @fractional @fractional-nested + Scenario Outline: Fractional operator with nested var expression as variant name + # fractional-nested-var-flag: seed=targetingKey, bucket0=[var("color"),50], bucket1=["blue",50] + # jon@company.com bv(100)=36 → bucket0 (resolves var "color"); user1 bv(100)=76 → bucket1 ("blue") + Given a String-flag with key "fractional-nested-var-flag" and a default value "fallback" + And a context containing a targeting key with value "" + And a context containing a key "color", with type "String" and with value "" + When the flag was evaluated with details + Then the resolved details value should be "" + Examples: + | targetingKey | color | value | + | jon@company.com | red | red | + | jon@company.com | green | green | + | user1 | red | blue | + @string Scenario Outline: Substring operators Given a String-flag with key "starts-ends-flag" and a default value "fallback" From fdcf310f2f9bcf6e755cc9d0292c994b3f910b17 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Mon, 16 Mar 2026 12:06:36 +0100 Subject: [PATCH 2/2] test(fractional): add invalid/missing variant fallback cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review feedback: add two rows to the nested-var scenario that exercise fallback behavior when the resolved variant name is not present in the flag's variants map. | jon@company.com | yellow | fallback | (variant not in map → error → caller default) | jon@company.com | | fallback | (empty string → not in map → error → caller default) Signed-off-by: Simon Schrottner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- gherkin/targeting.feature | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gherkin/targeting.feature b/gherkin/targeting.feature index 00eccb3..964761d 100644 --- a/gherkin/targeting.feature +++ b/gherkin/targeting.feature @@ -164,10 +164,12 @@ Feature: Targeting rules When the flag was evaluated with details Then the resolved details value should be "" Examples: - | targetingKey | color | value | - | jon@company.com | red | red | - | jon@company.com | green | green | - | user1 | red | blue | + | targetingKey | color | value | + | jon@company.com | red | red | + | jon@company.com | green | green | + | user1 | red | blue | + | jon@company.com | yellow | fallback | + | jon@company.com | | fallback | @string Scenario Outline: Substring operators