diff --git a/core/pkg/evaluator/fractional.go b/core/pkg/evaluator/fractional.go index 267151797..0163e0929 100644 --- a/core/pkg/evaluator/fractional.go +++ b/core/pkg/evaluator/fractional.go @@ -48,6 +48,10 @@ func (fe *Fractional) Evaluate(values, data any) any { return nil } + if feDistributions == nil { + return nil + } + hashValue := uint32(murmur3.StringSum32(valueToDistribute)) return distributeValue(hashValue, feDistributions) } @@ -78,11 +82,18 @@ if len(valuesArray) < 1 { valuesArray = valuesArray[1:] } + if dataMap[targetingKeyKey] == nil { + return "", nil, nil + } targetingKey, ok := dataMap[targetingKeyKey].(string) if !ok { return "", nil, fmt.Errorf("flag %q: bucketing value not supplied and no targetingKey in context", flagKey) } + if targetingKey == "" { + return "", nil, nil + } + bucketBy = fmt.Sprintf("%s%s", properties.FlagKey, targetingKey) } diff --git a/core/pkg/evaluator/fractional_test.go b/core/pkg/evaluator/fractional_test.go index cd92df66f..6fcfa7ffb 100644 --- a/core/pkg/evaluator/fractional_test.go +++ b/core/pkg/evaluator/fractional_test.go @@ -442,6 +442,67 @@ func TestFractionalEvaluation(t *testing.T) { expectedValue: blueHex, expectedReason: model.TargetingMatchReason, }, + "null targetingKey returns default variant": { + flags: []model.Flag{{ + Key: "headerColor", + State: "ENABLED", + DefaultVariant: redVariant, + Variants: colorVariants, + Targeting: []byte(`{ + "fractional": [ + ["blue", 50], + ["green", 50] + ] + }`), + }}, + flagKey: "headerColor", + context: map[string]any{ + "targetingKey": nil, + }, + expectedVariant: redVariant, + expectedValue: redHex, + expectedReason: model.DefaultReason, + }, + "missing targetingKey returns default variant": { + flags: []model.Flag{{ + Key: "headerColor", + State: "ENABLED", + DefaultVariant: redVariant, + Variants: colorVariants, + Targeting: []byte(`{ + "fractional": [ + ["blue", 50], + ["green", 50] + ] + }`), + }}, + flagKey: "headerColor", + context: map[string]any{}, + expectedVariant: redVariant, + expectedValue: redHex, + expectedReason: model.DefaultReason, + }, + "empty targetingKey returns default variant": { + flags: []model.Flag{{ + Key: "headerColor", + State: "ENABLED", + DefaultVariant: redVariant, + Variants: colorVariants, + Targeting: []byte(`{ + "fractional": [ + ["blue", 50], + ["green", 50] + ] + }`), + }}, + flagKey: "headerColor", + context: map[string]any{ + "targetingKey": "", + }, + expectedVariant: redVariant, + expectedValue: redHex, + expectedReason: model.DefaultReason, + }, "single-entry always returns the sole variant": { flags: []model.Flag{{ Key: "headerColor",