diff --git a/evaluator/flags/testkit-flags.json b/evaluator/flags/testkit-flags.json index cd55c18..e2e70cd 100644 --- a/evaluator/flags/testkit-flags.json +++ b/evaluator/flags/testkit-flags.json @@ -699,6 +699,30 @@ } }, "defaultVariant": "template" + }, + "large-integer-flag": { + "state": "ENABLED", + "variants": { + "one": 1, + "max-int32": 2147483647 + }, + "defaultVariant": "max-int32" + }, + "huge-integer-flag": { + "state": "ENABLED", + "variants": { + "one": 1, + "max-safe": 9007199254740991 + }, + "defaultVariant": "max-safe" + }, + "integral-float-flag": { + "state": "ENABLED", + "variants": { + "tenth": 0.1, + "ten": 10.0 + }, + "defaultVariant": "ten" } }, "$evaluators": { diff --git a/evaluator/gherkin/precision.feature b/evaluator/gherkin/precision.feature new file mode 100644 index 0000000..dce6ce5 --- /dev/null +++ b/evaluator/gherkin/precision.feature @@ -0,0 +1,38 @@ +@precision +Feature: Evaluator numeric precision + + # Validates that a numeric flag value survives evaluation unrounded and unnarrowed. + # The evaluator has no accessor types of its own, so what is under test here is narrower than + # in the provider suite: only that the value written in the flag definition is the value that + # comes back out. + # Flags are configured in evaluator/flags/testkit-flags.json. + + Background: + Given an evaluator + + Scenario Outline: Resolve numeric values without loss of precision + Given a -flag with key "" and a fallback value "" + When the flag was evaluated with details + Then the resolved details value should be "" + And the reason should be "STATIC" + + Examples: Integer evaluations + # 2147483647 is 2^31 - 1, outside what a 32-bit float represents exactly, so a round trip + # through one returns 2147483648. + | key | type | default | resolved_value | + | large-integer-flag | Integer | 1 | 2147483647 | + + Examples: Float evaluations + # A float whose value is integral must stay a float rather than arriving as 10. + | key | type | default | resolved_value | + | integral-float-flag | Float | 0.1 | 10.0 | + + @large-integers + Scenario: Resolve an integer beyond 32 bits without loss of precision + # 9007199254740991 is 2^53 - 1, the largest integer a double represents exactly. Tagged + # separately because a language whose integer type is 32 bits cannot ask for it at all -- + # excluding @large-integers is the honest answer there, not failing it. + Given a Integer-flag with key "huge-integer-flag" and a fallback value "1" + When the flag was evaluated with details + Then the resolved details value should be "9007199254740991" + And the reason should be "STATIC" diff --git a/flags/precision-flags.json b/flags/precision-flags.json new file mode 100644 index 0000000..899c4ae --- /dev/null +++ b/flags/precision-flags.json @@ -0,0 +1,28 @@ +{ + "flags": { + "large-integer-flag": { + "state": "ENABLED", + "variants": { + "one": 1, + "max-int32": 2147483647 + }, + "defaultVariant": "max-int32" + }, + "huge-integer-flag": { + "state": "ENABLED", + "variants": { + "one": 1, + "max-safe": 9007199254740991 + }, + "defaultVariant": "max-safe" + }, + "integral-float-flag": { + "state": "ENABLED", + "variants": { + "tenth": 0.1, + "ten": 10.0 + }, + "defaultVariant": "ten" + } + } +} diff --git a/gherkin/evaluation.feature b/gherkin/evaluation.feature index a6516c7..04cac47 100644 --- a/gherkin/evaluation.feature +++ b/gherkin/evaluation.feature @@ -34,6 +34,35 @@ Feature: flagd evaluations | integer-zero-flag | Integer | 1 | 0 | | float-zero-flag | Float | 0.1 | 0.0 | + @precision + Scenario Outline: Resolves numeric values without loss of precision + # 2147483647 is 2^31 - 1. It is outside the range a 32-bit float represents exactly, so a + # provider or transport that routes integers through a float and back returns 2147483648. + # 10.0 is the mirror case: a float whose value happens to be integral, which must stay a + # float rather than arriving as the integer 10. + Given a -flag with key "" and a default value "" + When the flag was evaluated with details + Then the resolved details value should be "" + And the variant should be "" + And the reason should be "STATIC" + + Examples: + | key | type | default | resolved_value | variant | + | large-integer-flag | Integer | 1 | 2147483647 | max-int32 | + | integral-float-flag | Float | 0.1 | 10.0 | ten | + + @precision @large-integers + Scenario: Resolves an integer beyond 32 bits without loss of precision + # 9007199254740991 is 2^53 - 1, the largest integer a double represents exactly. Separate + # from the scenario above, and separately tagged, because a language whose integer type is + # 32 bits cannot ask for it at all -- excluding @large-integers is the honest answer there, + # not failing it. + Given a Integer-flag with key "huge-integer-flag" and a default value "1" + When the flag was evaluated with details + Then the resolved details value should be "9007199254740991" + And the variant should be "max-safe" + And the reason should be "STATIC" + @targeting Scenario Outline: Resolves zero value with targeting Given a -flag with key "" and a default value ""