Skip to content

Commit 8dcc0c3

Browse files
author
Dave Johnston
authored
[FFM-5387]: Fix Golang Group Rule Evaluate and Add Tests (#101)
Run evaluator tests against the ff-test-grid test-cases Improve test coverage Fix the group rule evaluator. Currently rules are being AND'd but they should be OR'd
1 parent 875e3de commit 8dcc0c3

File tree

4 files changed

+58
-24
lines changed

4 files changed

+58
-24
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ test:
2828
go test -race -v --cover ./...
2929

3030
report:
31-
go test ./... -covermode=atomic -coverpkg=./... -coverprofile=c.out
32-
gocov convert ./c.out | gocov-html > ~/go-sdk-test-report.html
31+
go test -race -v -covermode=atomic -coverprofile=cover.out ./... | tee /dev/stderr | go-junit-report -set-exit-code > junit.xml
32+
gocov convert ./cover.out | gocov-html > coverage-report.html
3333

3434

3535
build-test-wrapper:

evaluation/evaluator.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,18 @@ func (e Evaluator) evaluateRules(servingRules []rest.ServingRule, target *Target
179179
return ""
180180
}
181181

182+
// evaluateGroupRules evaluates the groups rules. Note Group rule are represented by a rest.Clause, instead
183+
// of a rest.Rule. Unlike feature clauses which are AND'd, in a case of a group these must be OR'd.
184+
func (e Evaluator) evaluateGroupRules(rules []rest.Clause, target *Target) (bool, rest.Clause) {
185+
for _, r := range rules {
186+
rule := r
187+
if e.evaluateClause(&rule, target) {
188+
return true, r
189+
}
190+
}
191+
return false, rest.Clause{}
192+
}
193+
182194
func (e Evaluator) evaluateVariationMap(variationsMap []rest.VariationMap, target *Target) string {
183195
if variationsMap == nil || target == nil {
184196
return ""
@@ -252,11 +264,14 @@ func (e Evaluator) isTargetIncludedOrExcludedInSegment(segmentList []string, tar
252264
// Should Target be included via segment rules
253265
rules := segment.Rules
254266
// if rules is nil pointer or points to the empty slice
255-
if (rules != nil && len(*rules) > 0) && e.evaluateClauses(*rules, target) {
256-
e.logger.Debugf(
257-
"Target %s included in segment %s via rules", target.Name, segment.Name)
258-
return true
267+
if rules != nil && len(*rules) > 0 {
268+
if included, clause := e.evaluateGroupRules(*rules, target); included {
269+
e.logger.Debugf(
270+
"Target [%s] included in group [%s] via rule %+v", target.Name, segment.Name, clause)
271+
return true
272+
}
259273
}
274+
260275
}
261276
return false
262277
}

tests/evaluator_test.go

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import (
44
"encoding/json"
55
"fmt"
66
"io/ioutil"
7+
"os"
78
"path/filepath"
89
"reflect"
910
"testing"
1011

12+
"github.com/stretchr/testify/assert"
13+
1114
"github.com/harness/ff-golang-server-sdk/logger"
1215

1316
"github.com/harness/ff-golang-server-sdk/evaluation"
@@ -34,25 +37,32 @@ type testFile struct {
3437
}
3538

3639
func loadFiles() []testFile {
37-
files, err := ioutil.ReadDir(source)
40+
41+
slice := []testFile{}
42+
err := filepath.Walk(source,
43+
func(path string, info os.FileInfo, err error) error {
44+
if err != nil {
45+
return err
46+
}
47+
if info.IsDir() || filepath.Ext(info.Name()) != ".json" {
48+
return nil
49+
}
50+
if f, err := loadFile(path); err == nil {
51+
slice = append(slice, f)
52+
} else {
53+
log.Errorf("unable to load %s because %v", info.Name(), err)
54+
}
55+
return nil
56+
})
3857
if err != nil {
3958
log.Error(err)
4059
}
4160

42-
slice := make([]testFile, 0, len(files))
43-
for _, file := range files {
44-
if file.IsDir() || filepath.Ext(file.Name()) != ".json" {
45-
continue
46-
}
47-
if f, err := loadFile(file.Name()); err == nil {
48-
slice = append(slice, f)
49-
}
50-
}
5161
return slice
5262
}
5363

5464
func loadFile(filename string) (testFile, error) {
55-
fp := filepath.Clean(filepath.Join(source, filename))
65+
fp := filepath.Clean(filename)
5666
content, err := ioutil.ReadFile(fp)
5767
if err != nil {
5868
log.Error(err)
@@ -91,7 +101,7 @@ func TestEvaluator(t *testing.T) {
91101
}
92102

93103
for _, testCase := range fixture.Tests {
94-
testName := fmt.Sprintf("test fixture %s with flag %s", fixture.Filename, testCase.Flag)
104+
testName := fixture.Filename
95105
if testCase.Target != nil {
96106
testName = fmt.Sprintf("%s and target %s", testName, *testCase.Target)
97107
}
@@ -114,16 +124,25 @@ func TestEvaluator(t *testing.T) {
114124
got = evaluator.BoolVariation(testCase.Flag, target, false)
115125
case rest.FeatureConfigKindString:
116126
got = evaluator.StringVariation(testCase.Flag, target, "blue")
117-
case rest.FeatureConfigKindInt:
118-
got = evaluator.IntVariation(testCase.Flag, target, 100)
119-
case "number":
127+
case rest.FeatureConfigKindInt, "number":
120128
got = evaluator.NumberVariation(testCase.Flag, target, 50.00)
121129
case rest.FeatureConfigKindJson:
122130
got = evaluator.JSONVariation(testCase.Flag, target, map[string]interface{}{})
131+
str, _ := json.Marshal(&got)
132+
got = string(str)
133+
123134
}
124-
if !reflect.DeepEqual(got, testCase.Expected) {
125-
t.Errorf("eval engine got = %v, want %v", got, testCase.Expected)
135+
136+
if flag.Kind == rest.FeatureConfigKindJson {
137+
expected := fmt.Sprintf("%s", testCase.Expected)
138+
jsonStr := fmt.Sprintf("%s", got)
139+
assert.JSONEq(t, expected, jsonStr, "eval engine got = [%v], want [%v]", jsonStr, expected)
140+
} else {
141+
if !reflect.DeepEqual(got, testCase.Expected) {
142+
t.Errorf("eval engine got = [%v], want [%v]", got, testCase.Expected)
143+
}
126144
}
145+
127146
})
128147
}
129148
}

tests/ff-test-cases

Submodule ff-test-cases updated 79 files

0 commit comments

Comments
 (0)