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