Skip to content

Commit

Permalink
Merge pull request #181 from checkr/zz/fix-segments-evaluation
Browse files Browse the repository at this point in the history
Fix segments evaluation
  • Loading branch information
zhouzhuojie authored Oct 23, 2018
2 parents 5d33474 + b07eaa7 commit 7e37e2e
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
17 changes: 11 additions & 6 deletions pkg/handler/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,15 @@ var evalFlag = func(evalContext models.EvalContext) *models.EvalResult {
var sID *int64

for _, segment := range f.Segments {
variantID, log := evalSegment(f.ID, evalContext, segment)
sID = util.Int64Ptr(int64(segment.ID))
variantID, log, evalNextSegment := evalSegment(f.ID, evalContext, segment)
if evalContext.EnableDebug {
logs = append(logs, log)
}
if variantID != nil {
sID = util.Int64Ptr(int64(segment.ID))
vID = util.Int64Ptr(int64(*variantID))
}
if !evalNextSegment {
break
}
}
Expand Down Expand Up @@ -202,6 +204,7 @@ var evalSegment = func(
) (
vID *uint, // returns VariantID
log *models.SegmentDebugLog,
evalNextSegment bool,
) {
if len(segment.Constraints) != 0 {
m, ok := evalContext.EntityContext.(map[string]interface{})
Expand All @@ -210,7 +213,7 @@ var evalSegment = func(
Msg: fmt.Sprintf("constraints are present in the segment_id %v, but got invalid entity_context: %s.", segment.ID, spew.Sdump(evalContext.EntityContext)),
SegmentID: int64(segment.ID),
}
return nil, log
return nil, log, false
}

expr := segment.SegmentEvaluation.ConditionsExpr
Expand All @@ -220,14 +223,14 @@ var evalSegment = func(
Msg: err.Error(),
SegmentID: int64(segment.ID),
}
return nil, log
return nil, log, true
}
if !match {
log = &models.SegmentDebugLog{
Msg: debugConstraintMsg(expr, m),
SegmentID: int64(segment.ID),
}
return nil, log
return nil, log, true
}
}

Expand All @@ -242,7 +245,9 @@ var evalSegment = func(
SegmentID: int64(segment.ID),
}

return vID, log
// at this point, all constraints are matched, so we shouldn't go to next segment
// thus setting evalNextSegment = false
return vID, log, false
}

func debugConstraintMsg(expr conditions.Expr, m map[string]interface{}) string {
Expand Down
35 changes: 30 additions & 5 deletions pkg/handler/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ import (
func TestEvalSegment(t *testing.T) {
t.Run("test empty evalContext", func(t *testing.T) {
s := entity.GenFixtureSegment()
vID, log := evalSegment(100, models.EvalContext{}, s)
vID, log, evalNextSegment := evalSegment(100, models.EvalContext{}, s)

assert.Nil(t, vID)
assert.NotEmpty(t, log)
assert.False(t, evalNextSegment)
})

t.Run("test happy code path", func(t *testing.T) {
s := entity.GenFixtureSegment()
s.RolloutPercent = uint(100)
vID, log := evalSegment(100, models.EvalContext{
vID, log, evalNextSegment := evalSegment(100, models.EvalContext{
EnableDebug: true,
EntityContext: map[string]interface{}{"dl_state": "CA"},
EntityID: "entityID1",
Expand All @@ -35,12 +36,13 @@ func TestEvalSegment(t *testing.T) {

assert.NotNil(t, vID)
assert.NotEmpty(t, log)
assert.False(t, evalNextSegment)
})

t.Run("test constraint evaluation error", func(t *testing.T) {
s := entity.GenFixtureSegment()
s.RolloutPercent = uint(100)
vID, log := evalSegment(100, models.EvalContext{
vID, log, evalNextSegment := evalSegment(100, models.EvalContext{
EnableDebug: true,
EntityContext: map[string]interface{}{},
EntityID: "entityID1",
Expand All @@ -50,12 +52,13 @@ func TestEvalSegment(t *testing.T) {

assert.Nil(t, vID)
assert.NotEmpty(t, log)
assert.True(t, evalNextSegment)
})

t.Run("test constraint not match", func(t *testing.T) {
s := entity.GenFixtureSegment()
s.RolloutPercent = uint(100)
vID, log := evalSegment(100, models.EvalContext{
vID, log, evalNextSegment := evalSegment(100, models.EvalContext{
EnableDebug: true,
EntityContext: map[string]interface{}{"dl_state": "NY"},
EntityID: "entityID1",
Expand All @@ -65,12 +68,13 @@ func TestEvalSegment(t *testing.T) {

assert.Nil(t, vID)
assert.NotEmpty(t, log)
assert.True(t, evalNextSegment)
})

t.Run("test evalContext wrong format", func(t *testing.T) {
s := entity.GenFixtureSegment()
s.RolloutPercent = uint(100)
vID, log := evalSegment(100, models.EvalContext{
vID, log, evalNextSegment := evalSegment(100, models.EvalContext{
EnableDebug: true,
EntityContext: nil,
EntityID: "entityID1",
Expand All @@ -80,6 +84,7 @@ func TestEvalSegment(t *testing.T) {

assert.Nil(t, vID)
assert.NotEmpty(t, log)
assert.False(t, evalNextSegment)
})
}

Expand Down Expand Up @@ -171,6 +176,26 @@ func TestEvalFlag(t *testing.T) {
assert.NotNil(t, result.VariantID)
})

t.Run("test multiple segments with the first segment 0% rollout", func(t *testing.T) {
f := entity.GenFixtureFlag()
f.Segments = append(f.Segments, entity.GenFixtureSegment())
f.Segments[0].Constraints = []entity.Constraint{}
f.Segments[0].RolloutPercent = uint(0)

f.PrepareEvaluation()
cache := &EvalCache{mapCache: map[string]*entity.Flag{"100": &f}}
defer gostub.StubFunc(&GetEvalCache, cache).Reset()
result := evalFlag(models.EvalContext{
EnableDebug: true,
EntityContext: map[string]interface{}{"dl_state": "CA", "state": "CA", "rate": 2000},
EntityID: "entityID1",
EntityType: "entityType1",
FlagID: int64(100),
})
assert.NotNil(t, result)
assert.Nil(t, result.VariantID)
})

t.Run("test no match path with multiple constraints", func(t *testing.T) {
f := entity.GenFixtureFlag()
f.Segments[0].Constraints = []entity.Constraint{
Expand Down

0 comments on commit 7e37e2e

Please sign in to comment.