Skip to content

Commit

Permalink
allow subject attributes to be string wrapper numerics; attempt to sa…
Browse files Browse the repository at this point in the history
…fely coerce (#35)

* allow subject attributes to be string wrapper numerics; attempt to safely coerce

* add comment
  • Loading branch information
leoromanovsky committed Mar 1, 2024
1 parent 5f87650 commit bf8c56b
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 5 deletions.
8 changes: 4 additions & 4 deletions eppoclient/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ func evaluateCondition(subjectAttributes dictionary, condition condition) bool {
case "NOT_ONE_OF":
return isNotOneOf(subjectValue, convertToStringArray(condition.Value))
default:
// Attempt to evaluate as numeric condition if both values are numeric.
subjectValueNumeric, isNumericSubject := subjectValue.(float64) // Assuming float64 for general numeric comparison; adjust as needed.
conditionValueNumeric, isNumericCondition := condition.Value.(float64) // Same assumption as above.
if isNumericSubject && isNumericCondition {
// Attempt to coerce both values to float64 and compare them.
subjectValueNumeric, isNumericSubjectErr := ToFloat64(subjectValue)
conditionValueNumeric, isNumericConditionErr := ToFloat64(condition.Value)
if isNumericSubjectErr == nil && isNumericConditionErr == nil {
return evaluateNumericCondition(subjectValueNumeric, conditionValueNumeric, condition)
}

Expand Down
15 changes: 14 additions & 1 deletion eppoclient/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,24 @@ func Test_findMatchingRule_whenNoRulesMatch(t *testing.T) {
}

func Test_findMatchingRule_Success(t *testing.T) {
// both numeric and string wrapped numeric attributes must match.

// Test with a numeric value
subjectAttributes := make(dictionary)
subjectAttributes["age"] = 99.0

result, _ := findMatchingRule(subjectAttributes, []rule{numericRule})
result, err := findMatchingRule(subjectAttributes, []rule{numericRule})

assert.NoError(t, err)
assert.Equal(t, numericRule, result)

// Test with a string value
subjectAttributes = make(dictionary)
subjectAttributes["age"] = "99.0"

result, err = findMatchingRule(subjectAttributes, []rule{numericRule})

assert.NoError(t, err)
assert.Equal(t, numericRule, result)
}

Expand Down
24 changes: 24 additions & 0 deletions eppoclient/utils.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package eppoclient

import (
"errors"
"fmt"
"strconv"
)

type dictionary map[string]interface{}

type testData struct {
Expand All @@ -26,3 +32,21 @@ type testDataShardRange struct {
Start int `json:"start"`
End int `json:"end"`
}

// ToFloat64 attempts to convert an interface{} value to a float64.
// It supports inputs of type float64 or string (which can be parsed as float64).
// Returns a float64 and nil error on success, or 0 and an error on failure.
func ToFloat64(val interface{}) (float64, error) {
switch v := val.(type) {
case float64:
return v, nil
case string:
floatVal, err := strconv.ParseFloat(v, 64)
if err != nil {
return 0, fmt.Errorf("cannot convert string '%s' to float64: %w", v, err)
}
return floatVal, nil
default:
return 0, errors.New("value is neither a float64 nor a convertible string")
}
}
34 changes: 34 additions & 0 deletions eppoclient/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package eppoclient

import (
"testing"
)

func TestToFloat64(t *testing.T) {
tests := []struct {
name string
input interface{}
expected float64
expectErr bool
}{
{"Float64Input", 123.456, 123.456, false},
{"StringInputValid", "789.012", 789.012, false},
{"StringInputInvalid", "abc", 0, true},
{"SemVerInputInvalid", "1.2.3", 0, true},
{"BoolInput", true, 0, true},
{"IntInput", 123, 0, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := ToFloat64(tt.input)
if (err != nil) != tt.expectErr {
t.Errorf("ToFloat64(%v) error = %v, expectErr %v", tt.input, err, tt.expectErr)
return
}
if !tt.expectErr && result != tt.expected {
t.Errorf("ToFloat64(%v) = %v, want %v", tt.input, result, tt.expected)
}
})
}
}

0 comments on commit bf8c56b

Please sign in to comment.