1 package ldtestdata
2
3 import (
4 "encoding/json"
5 "testing"
6 "time"
7
8 "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
9 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
10 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldbuilders"
11 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
12 "github.com/launchdarkly/go-server-sdk/v6/subsystems"
13 "github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoreimpl"
14
15 m "github.com/launchdarkly/go-test-helpers/v3/matchers"
16 )
17
18 const (
19 trueVar = 0
20 falseVar = 1
21 )
22
23 func verifyFlag(t *testing.T, configureFlag func(*FlagBuilder), expectedFlag *ldbuilders.FlagBuilder) {
24 t.Helper()
25 expectedJSON, _ := json.Marshal(expectedFlag.Build())
26 testDataSourceTest(t, func(p testDataSourceTestParams) {
27 t.Helper()
28 p.withDataSource(t, func(subsystems.DataSource) {
29 t.Helper()
30 f := p.td.Flag("flagkey")
31 configureFlag(f)
32 p.td.Update(f)
33 up := p.updates.DataStore.WaitForUpsert(t, ldstoreimpl.Features(), "flagkey", 1, time.Millisecond)
34 upJSON := ldstoreimpl.Features().Serialize(up.Item)
35 m.In(t).Assert(string(upJSON), m.JSONStrEqual(string(expectedJSON)))
36 })
37 })
38 }
39
40 func basicBool() *ldbuilders.FlagBuilder {
41 return ldbuilders.NewFlagBuilder("flagkey").Version(1).Variations(ldvalue.Bool(true), ldvalue.Bool(false)).
42 OffVariation(1)
43 }
44
45 func TestFlagConfig(t *testing.T) {
46 basicString := func() *ldbuilders.FlagBuilder {
47 return ldbuilders.NewFlagBuilder("flagkey").Version(1).On(true).Variations(threeStringValues...)
48 }
49
50 t.Run("simple boolean flag", func(t *testing.T) {
51 verifyFlag(t, func(f *FlagBuilder) {}, basicBool().On(true).FallthroughVariation(trueVar))
52 verifyFlag(t, func(f *FlagBuilder) { f.BooleanFlag() }, basicBool().On(true).FallthroughVariation(trueVar))
53 verifyFlag(t, func(f *FlagBuilder) { f.On(true) }, basicBool().On(true).FallthroughVariation(trueVar))
54 verifyFlag(t, func(f *FlagBuilder) { f.On(false) }, basicBool().On(false).FallthroughVariation(trueVar))
55 verifyFlag(t, func(f *FlagBuilder) { f.VariationForAll(false) }, basicBool().On(true).FallthroughVariation(falseVar))
56 verifyFlag(t, func(f *FlagBuilder) { f.VariationForAll(true) }, basicBool().On(true).FallthroughVariation(trueVar))
57
58 verifyFlag(t, func(f *FlagBuilder) {
59 f.FallthroughVariation(true).OffVariation(false)
60 }, basicBool().On(true).FallthroughVariation(trueVar).OffVariation(falseVar))
61
62 verifyFlag(t, func(f *FlagBuilder) {
63 f.FallthroughVariation(false).OffVariation(true)
64 }, basicBool().On(true).FallthroughVariation(falseVar).OffVariation(trueVar))
65 })
66
67 t.Run("using boolean config methods forces flag to be boolean", func(t *testing.T) {
68 verifyFlag(t, func(f *FlagBuilder) {
69 f.Variations(ldvalue.Int(1), ldvalue.Int(2))
70 f.BooleanFlag()
71 }, basicBool().On(true).FallthroughVariation(trueVar))
72
73 verifyFlag(t, func(f *FlagBuilder) {
74 f.Variations(ldvalue.Bool(true), ldvalue.Int(2))
75 f.BooleanFlag()
76 }, basicBool().On(true).FallthroughVariation(trueVar))
77
78 verifyFlag(t, func(f *FlagBuilder) {
79 f.ValueForAll(ldvalue.String("x"))
80 f.BooleanFlag()
81 }, basicBool().On(true).FallthroughVariation(trueVar))
82 })
83
84 t.Run("flag with string variations", func(t *testing.T) {
85 verifyFlag(t, func(f *FlagBuilder) {
86 f.Variations(threeStringValues...).OffVariationIndex(0).FallthroughVariationIndex(2)
87 }, basicString().OffVariation(0).FallthroughVariation(2))
88
89 verifyFlag(t, func(f *FlagBuilder) {
90 f.Variations(threeStringValues...).VariationForAllIndex(1)
91 }, basicString().OffVariation(1).FallthroughVariation(1))
92 })
93 }
94
95 func TestFlagTargets(t *testing.T) {
96 basicString := func() *ldbuilders.FlagBuilder {
97 return ldbuilders.NewFlagBuilder("flagkey").Version(1).On(true).Variations(threeStringValues...)
98 }
99
100 t.Run("user targets", func(t *testing.T) {
101 verifyFlag(t,
102 func(f *FlagBuilder) {
103 f.VariationForUser("a", true).VariationForUser("b", true)
104 },
105 basicBool().On(true).FallthroughVariation(trueVar).
106 AddTarget(0, "a", "b").AddContextTarget(ldcontext.DefaultKind, 0))
107
108 verifyFlag(t,
109 func(f *FlagBuilder) {
110 f.VariationForUser("a", true).VariationForUser("a", true)
111 },
112 basicBool().On(true).FallthroughVariation(trueVar).
113 AddTarget(0, "a").AddContextTarget(ldcontext.DefaultKind, 0))
114
115 verifyFlag(t,
116 func(f *FlagBuilder) {
117 f.VariationForUser("a", false).VariationForUser("b", true).VariationForUser("c", false)
118 },
119 basicBool().On(true).FallthroughVariation(trueVar).
120 AddTarget(0, "b").AddContextTarget(ldcontext.DefaultKind, 0).
121 AddTarget(1, "a", "c").AddContextTarget(ldcontext.DefaultKind, 1))
122
123 verifyFlag(t,
124 func(f *FlagBuilder) {
125 f.VariationForUser("a", true).VariationForUser("b", true).VariationForUser("a", false)
126 },
127 basicBool().On(true).FallthroughVariation(trueVar).
128 AddTarget(0, "b").AddContextTarget(ldcontext.DefaultKind, 0).
129 AddTarget(1, "a").AddContextTarget(ldcontext.DefaultKind, 1))
130
131 verifyFlag(t,
132 func(f *FlagBuilder) {
133 f.Variations(threeStringValues...).OffVariationIndex(0).FallthroughVariationIndex(2).
134 VariationIndexForUser("a", 2).VariationIndexForUser("b", 2)
135 },
136 basicString().On(true).OffVariation(0).FallthroughVariation(2).
137 AddTarget(2, "a", "b").AddContextTarget(ldcontext.DefaultKind, 2))
138
139 verifyFlag(t,
140 func(f *FlagBuilder) {
141 f.Variations(threeStringValues...).OffVariationIndex(0).FallthroughVariationIndex(2).
142 VariationIndexForUser("a", 2).VariationIndexForUser("b", 1).VariationIndexForUser("c", 2)
143 },
144 basicString().On(true).OffVariation(0).FallthroughVariation(2).
145 AddTarget(1, "b").AddContextTarget(ldcontext.DefaultKind, 1).
146 AddTarget(2, "a", "c").AddContextTarget(ldcontext.DefaultKind, 2))
147 })
148
149 t.Run("context targets", func(t *testing.T) {
150 verifyFlag(t,
151 func(f *FlagBuilder) {
152 f.VariationForKey("org", "a", true).VariationForKey("org", "b", true)
153 },
154 basicBool().On(true).FallthroughVariation(trueVar).
155 AddContextTarget("org", 0, "a", "b"))
156
157 verifyFlag(t,
158 func(f *FlagBuilder) {
159 f.VariationForKey("org", "a", true).VariationForKey("other", "a", true)
160 },
161 basicBool().On(true).FallthroughVariation(trueVar).
162 AddContextTarget("org", 0, "a").
163 AddContextTarget("other", 0, "a"))
164
165 verifyFlag(t,
166 func(f *FlagBuilder) {
167 f.VariationForKey("org", "a", true).VariationForKey("org", "a", true)
168 },
169 basicBool().On(true).FallthroughVariation(trueVar).
170 AddContextTarget("org", 0, "a"))
171
172 verifyFlag(t,
173 func(f *FlagBuilder) {
174 f.Variations(threeStringValues...).OffVariationIndex(0).FallthroughVariationIndex(2).
175 VariationIndexForKey("org", "a", 2).VariationIndexForKey("org", "b", 2)
176 },
177 basicString().On(true).OffVariation(0).FallthroughVariation(2).
178 AddContextTarget("org", 2, "a", "b"))
179
180 verifyFlag(t,
181 func(f *FlagBuilder) {
182 f.VariationForKey("", "a", true).VariationForKey("", "b", true)
183 },
184 basicBool().On(true).FallthroughVariation(trueVar).
185 AddTarget(0, "a", "b").AddContextTarget("user", 0))
186 })
187 }
188
189 func TestRuleConfig(t *testing.T) {
190 t.Run("simple match returning variation 0/true", func(t *testing.T) {
191 matchReturnsVariation0 := basicBool().On(true).FallthroughVariation(0).AddRule(
192 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
193 ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Lucy")),
194 ),
195 )
196
197 verifyFlag(t, func(f *FlagBuilder) {
198 f.IfMatch("name", ldvalue.String("Lucy")).ThenReturn(true)
199 }, matchReturnsVariation0)
200
201 verifyFlag(t, func(f *FlagBuilder) {
202 f.IfMatch("name", ldvalue.String("Lucy")).ThenReturnIndex(0)
203 }, matchReturnsVariation0)
204 })
205
206 t.Run("simple match returning variation 1/false", func(t *testing.T) {
207 matchReturnsVariation1 := basicBool().On(true).FallthroughVariation(0).AddRule(
208 ldbuilders.NewRuleBuilder().ID("rule0").Variation(falseVar).Clauses(
209 ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Lucy")),
210 ),
211 )
212
213 verifyFlag(t, func(f *FlagBuilder) {
214 f.IfMatch("name", ldvalue.String("Lucy")).ThenReturn(false)
215 }, matchReturnsVariation1)
216
217 verifyFlag(t, func(f *FlagBuilder) {
218 f.IfMatch("name", ldvalue.String("Lucy")).ThenReturnIndex(1)
219 }, matchReturnsVariation1)
220 })
221
222 t.Run("negated match", func(t *testing.T) {
223 verifyFlag(t, func(f *FlagBuilder) {
224 f.IfNotMatch("name", ldvalue.String("Lucy")).ThenReturn(true)
225 }, basicBool().On(true).FallthroughVariation(0).AddRule(
226 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
227 ldbuilders.Negate(ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Lucy"))),
228 ),
229 ))
230 })
231
232 t.Run("multiple clauses", func(t *testing.T) {
233 verifyFlag(t, func(f *FlagBuilder) {
234 f.IfMatch("name", ldvalue.String("Lucy")).
235 AndMatch("country", ldvalue.String("gb")).
236 ThenReturn(true)
237 }, basicBool().On(true).FallthroughVariation(0).AddRule(
238 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
239 ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Lucy")),
240 ldbuilders.ClauseWithKind("user", "country", ldmodel.OperatorIn, ldvalue.String("gb")),
241 ),
242 ))
243 })
244
245 t.Run("multiple rules", func(t *testing.T) {
246 verifyFlag(t, func(f *FlagBuilder) {
247 f.IfMatch("name", ldvalue.String("Lucy")).ThenReturn(true).
248 IfMatch("name", ldvalue.String("Mina")).ThenReturn(true)
249 }, basicBool().On(true).FallthroughVariation(0).AddRule(
250 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
251 ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Lucy")),
252 ),
253 ).AddRule(
254 ldbuilders.NewRuleBuilder().ID("rule1").Variation(trueVar).Clauses(
255 ldbuilders.ClauseWithKind("user", "name", ldmodel.OperatorIn, ldvalue.String("Mina")),
256 ),
257 ))
258 })
259
260 t.Run("simple match with context kind", func(t *testing.T) {
261 matchReturnsVariation0 := basicBool().On(true).FallthroughVariation(0).AddRule(
262 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
263 ldbuilders.ClauseWithKind("org", "name", ldmodel.OperatorIn, ldvalue.String("Catco")),
264 ),
265 )
266
267 verifyFlag(t, func(f *FlagBuilder) {
268 f.IfMatchContext("org", "name", ldvalue.String("Catco")).ThenReturn(true)
269 }, matchReturnsVariation0)
270
271 verifyFlag(t, func(f *FlagBuilder) {
272 f.IfMatchContext("org", "name", ldvalue.String("Catco")).ThenReturnIndex(0)
273 }, matchReturnsVariation0)
274 })
275
276 t.Run("negated match with context kind", func(t *testing.T) {
277 verifyFlag(t, func(f *FlagBuilder) {
278 f.IfNotMatchContext("org", "name", ldvalue.String("Catco")).ThenReturn(true)
279 }, basicBool().On(true).FallthroughVariation(0).AddRule(
280 ldbuilders.NewRuleBuilder().ID("rule0").Variation(trueVar).Clauses(
281 ldbuilders.Negate(ldbuilders.ClauseWithKind("org", "name", ldmodel.OperatorIn, ldvalue.String("Catco"))),
282 ),
283 ))
284 })
285 }
286
View as plain text