...

Source file src/github.com/launchdarkly/go-server-sdk-evaluation/v2/evaluator_rule_test.go

Documentation: github.com/launchdarkly/go-server-sdk-evaluation/v2

     1  package evaluation
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/launchdarkly/go-server-sdk-evaluation/v2/ldbuilders"
     7  	"github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
     8  
     9  	"github.com/launchdarkly/go-sdk-common/v3/ldattr"
    10  	"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
    11  	"github.com/launchdarkly/go-sdk-common/v3/ldlog"
    12  	"github.com/launchdarkly/go-sdk-common/v3/ldlogtest"
    13  	"github.com/launchdarkly/go-sdk-common/v3/ldreason"
    14  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
    15  	m "github.com/launchdarkly/go-test-helpers/v3/matchers"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestMalformedFlagErrorForBadRuleProperties(t *testing.T) {
    21  	basicContext := ldcontext.New("userkey")
    22  
    23  	type testCaseParams struct {
    24  		name    string
    25  		context ldcontext.Context
    26  		flag    ldmodel.FeatureFlag
    27  		message string
    28  	}
    29  
    30  	for _, p := range []testCaseParams{
    31  		{
    32  			name:    "variation index too high",
    33  			context: basicContext,
    34  			flag:    makeFlagToMatchContext(basicContext, ldbuilders.Variation(999)),
    35  			message: "nonexistent variation index 999",
    36  		},
    37  		{
    38  			name:    "negative variation index",
    39  			context: basicContext,
    40  			flag:    makeFlagToMatchContext(basicContext, ldbuilders.Variation(-1)),
    41  			message: "nonexistent variation index -1",
    42  		},
    43  		{
    44  			name:    "no variation or rollout",
    45  			context: basicContext,
    46  			flag:    makeFlagToMatchContext(basicContext, ldbuilders.Rollout()),
    47  			message: "rollout or experiment with no variations",
    48  		},
    49  	} {
    50  		t.Run(p.name, func(t *testing.T) {
    51  			t.Run("returns error", func(t *testing.T) {
    52  				result := basicEvaluator().Evaluate(&p.flag, p.context, FailOnAnyPrereqEvent(t))
    53  				m.In(t).Assert(result, ResultDetailError(ldreason.EvalErrorMalformedFlag))
    54  			})
    55  
    56  			t.Run("logs error", func(t *testing.T) {
    57  				logCapture := ldlogtest.NewMockLog()
    58  				e := NewEvaluatorWithOptions(basicDataProvider(),
    59  					EvaluatorOptionErrorLogger(logCapture.Loggers.ForLevel(ldlog.Error)))
    60  				_ = e.Evaluate(&p.flag, p.context, FailOnAnyPrereqEvent(t))
    61  
    62  				errorLines := logCapture.GetOutput(ldlog.Error)
    63  				if assert.Len(t, errorLines, 1) {
    64  					assert.Regexp(t, p.message, errorLines[0])
    65  				}
    66  			})
    67  		})
    68  	}
    69  }
    70  
    71  func TestMalformedFlagErrorForBadClauseProperties(t *testing.T) {
    72  	basicContext := ldcontext.New("userkey")
    73  
    74  	type testCaseParams struct {
    75  		name    string
    76  		context ldcontext.Context
    77  		clause  ldmodel.Clause
    78  		message string
    79  	}
    80  
    81  	for _, p := range []testCaseParams{
    82  		{
    83  			name:    "undefined attribute",
    84  			context: basicContext,
    85  			clause:  ldbuilders.ClauseRef(ldattr.Ref{}, ldmodel.OperatorIn, ldvalue.String("a")),
    86  			message: "rule clause did not specify an attribute",
    87  		},
    88  		{
    89  			name:    "invalid attribute reference",
    90  			context: basicContext,
    91  			clause:  ldbuilders.ClauseRef(ldattr.NewRef("///"), ldmodel.OperatorIn, ldvalue.String("a")),
    92  			message: "invalid attribute reference",
    93  		},
    94  	} {
    95  		t.Run(p.name, func(t *testing.T) {
    96  			goodClause := makeClauseToMatchContext(p.context)
    97  			flag := ldbuilders.NewFlagBuilder("feature").
    98  				On(true).
    99  				AddRule(ldbuilders.NewRuleBuilder().ID("bad").Variation(1).Clauses(p.clause)).
   100  				AddRule(ldbuilders.NewRuleBuilder().ID("good").Variation(1).Clauses(goodClause)).
   101  				Variations(ldvalue.Bool(false), ldvalue.Bool(true)).
   102  				Build()
   103  
   104  			t.Run("returns error", func(t *testing.T) {
   105  				result := basicEvaluator().Evaluate(&flag, p.context, FailOnAnyPrereqEvent(t))
   106  				m.In(t).Assert(result, ResultDetailError(ldreason.EvalErrorMalformedFlag))
   107  			})
   108  
   109  			t.Run("logs error", func(t *testing.T) {
   110  				logCapture := ldlogtest.NewMockLog()
   111  				e := NewEvaluatorWithOptions(basicDataProvider(),
   112  					EvaluatorOptionErrorLogger(logCapture.Loggers.ForLevel(ldlog.Error)))
   113  				_ = e.Evaluate(&flag, p.context, FailOnAnyPrereqEvent(t))
   114  
   115  				errorLines := logCapture.GetOutput(ldlog.Error)
   116  				if assert.Len(t, errorLines, 1) {
   117  					assert.Regexp(t, p.message, errorLines[0])
   118  				}
   119  			})
   120  		})
   121  	}
   122  }
   123  
   124  func TestClauseWithUnknownOperatorDoesNotStopSubsequentRuleFromMatching(t *testing.T) {
   125  	context := ldcontext.New("key")
   126  	badClause := ldbuilders.Clause(ldattr.NameAttr, "doesSomethingUnsupported", ldvalue.String("Bob"))
   127  	goodClause := makeClauseToMatchContext(context)
   128  	f := ldbuilders.NewFlagBuilder("feature").
   129  		On(true).
   130  		AddRule(ldbuilders.NewRuleBuilder().ID("bad").Variation(1).Clauses(badClause)).
   131  		AddRule(ldbuilders.NewRuleBuilder().ID("good").Variation(1).Clauses(goodClause)).
   132  		Variations(ldvalue.Bool(false), ldvalue.Bool(true)).
   133  		Build()
   134  
   135  	result := basicEvaluator().Evaluate(&f, context, nil)
   136  	m.In(t).Assert(result, ResultDetailProps(1, ldvalue.Bool(true), ldreason.NewEvalReasonRuleMatch(1, "good")))
   137  }
   138  

View as plain text