1 package flagstate
2
3 import (
4 "testing"
5
6 "github.com/launchdarkly/go-sdk-common/v3/ldreason"
7 "github.com/launchdarkly/go-sdk-common/v3/ldtime"
8 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
9
10 "github.com/stretchr/testify/assert"
11 )
12
13 func TestAllFlags(t *testing.T) {
14 t.Run("IsValid", func(t *testing.T) {
15 assert.False(t, AllFlags{}.IsValid())
16 assert.True(t, AllFlags{valid: true}.IsValid())
17 })
18
19 t.Run("GetFlag", func(t *testing.T) {
20 f := FlagState{}
21 a := AllFlags{
22 flags: map[string]FlagState{"known-flag": f},
23 }
24
25 f1, ok := a.GetFlag("known-flag")
26 assert.True(t, ok)
27 assert.Equal(t, f, f1)
28
29 f2, ok := a.GetFlag("unknown-flag")
30 assert.False(t, ok)
31 assert.Equal(t, FlagState{}, f2)
32 })
33
34 t.Run("GetValue", func(t *testing.T) {
35 f := FlagState{Value: ldvalue.String("hi")}
36 a := AllFlags{
37 flags: map[string]FlagState{"known-flag": f},
38 }
39
40 assert.Equal(t, f.Value, a.GetValue("known-flag"))
41 assert.Equal(t, ldvalue.Null(), a.GetValue("unknown-flag"))
42 })
43
44 t.Run("ToValuesMap", func(t *testing.T) {
45 a0 := AllFlags{}
46 assert.Len(t, a0.ToValuesMap(), 0)
47 assert.NotNil(t, a0.ToValuesMap())
48
49 a1 := AllFlags{
50 flags: map[string]FlagState{
51 "flag1": {Value: ldvalue.String("value1")},
52 "flag2": {Value: ldvalue.String("value2")},
53 },
54 }
55 assert.Equal(t, map[string]ldvalue.Value{
56 "flag1": ldvalue.String("value1"),
57 "flag2": ldvalue.String("value2"),
58 }, a1.ToValuesMap())
59 })
60 }
61
62 func TestAllFlagsJSON(t *testing.T) {
63 t.Run("invalid state", func(t *testing.T) {
64 bytes, err := AllFlags{}.MarshalJSON()
65 assert.NoError(t, err)
66 assert.JSONEq(t, `{"$valid":false,"$flagsState":{}}`, string(bytes))
67 })
68
69 t.Run("minimal flag", func(t *testing.T) {
70 a := AllFlags{
71 valid: true,
72 flags: map[string]FlagState{
73 "flag1": {
74 Value: ldvalue.String("value1"),
75 Version: 1000,
76 },
77 },
78 }
79 bytes, err := a.MarshalJSON()
80 assert.NoError(t, err)
81 assert.JSONEq(t,
82 `{
83 "$valid":true,
84 "flag1": "value1",
85 "$flagsState":{
86 "flag1": {"version":1000}
87 }
88 }`, string(bytes))
89 })
90
91 t.Run("flag with all properties except trackReason", func(t *testing.T) {
92 a := AllFlags{
93 valid: true,
94 flags: map[string]FlagState{
95 "flag1": {
96 Value: ldvalue.String("value1"),
97 Variation: ldvalue.NewOptionalInt(1),
98 Version: 1000,
99 Reason: ldreason.NewEvalReasonFallthrough(),
100 TrackEvents: true,
101 DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
102 },
103 },
104 }
105 bytes, err := a.MarshalJSON()
106 assert.NoError(t, err)
107 assert.JSONEq(t,
108 `{
109 "$valid":true,
110 "flag1": "value1",
111 "$flagsState":{
112 "flag1": {"variation":1,"version":1000,"reason":{"kind":"FALLTHROUGH"},"trackEvents":true,"debugEventsUntilDate":100000}
113 }
114 }`, string(bytes))
115 })
116
117 t.Run("flag with trackReason", func(t *testing.T) {
118 a := AllFlags{
119 valid: true,
120 flags: map[string]FlagState{
121 "flag1": {
122 Value: ldvalue.String("value1"),
123 Variation: ldvalue.NewOptionalInt(1),
124 Version: 1000,
125 Reason: ldreason.NewEvalReasonFallthrough(),
126 TrackEvents: true,
127 TrackReason: true,
128 },
129 },
130 }
131 bytes, err := a.MarshalJSON()
132 assert.NoError(t, err)
133 assert.JSONEq(t,
134 `{
135 "$valid":true,
136 "flag1": "value1",
137 "$flagsState":{
138 "flag1": {"variation":1,"version":1000,"reason":{"kind":"FALLTHROUGH"},"trackEvents":true,"trackReason":true}
139 }
140 }`, string(bytes))
141 })
142
143 t.Run("omitting details", func(t *testing.T) {
144 a := AllFlags{
145 valid: true,
146 flags: map[string]FlagState{
147 "flag1": {
148 Value: ldvalue.String("value1"),
149 Variation: ldvalue.NewOptionalInt(1),
150 Version: 1000,
151 Reason: ldreason.NewEvalReasonFallthrough(),
152 OmitDetails: true,
153 },
154 },
155 }
156 bytes, err := a.MarshalJSON()
157 assert.NoError(t, err)
158 assert.JSONEq(t,
159 `{
160 "$valid":true,
161 "flag1": "value1",
162 "$flagsState":{
163 "flag1": {"variation":1}
164 }
165 }`, string(bytes))
166 })
167 }
168
169 func TestAllFlagsBuilder(t *testing.T) {
170 t.Run("result is always valid", func(t *testing.T) {
171 assert.True(t, NewAllFlagsBuilder().Build().IsValid())
172 })
173
174 t.Run("add flags without reasons", func(t *testing.T) {
175 b := NewAllFlagsBuilder()
176
177 flag1 := FlagState{
178 Value: ldvalue.String("value1"),
179 Variation: ldvalue.NewOptionalInt(1),
180 Version: 1000,
181 Reason: ldreason.NewEvalReasonFallthrough(),
182 }
183 flag2 := FlagState{
184 Value: ldvalue.String("value2"),
185 Version: 2000,
186 Reason: ldreason.NewEvalReasonError(ldreason.EvalErrorException),
187 TrackEvents: true,
188 DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
189 }
190 b.AddFlag("flag1", flag1)
191 b.AddFlag("flag2", flag2)
192
193 flag1WithoutReason, flag2WithoutReason := flag1, flag2
194 flag1WithoutReason.Reason = ldreason.EvaluationReason{}
195 flag2WithoutReason.Reason = ldreason.EvaluationReason{}
196
197 a := b.Build()
198 assert.Equal(t, map[string]FlagState{
199 "flag1": flag1WithoutReason,
200 "flag2": flag2WithoutReason,
201 }, a.flags)
202 })
203
204 t.Run("add flags with reasons", func(t *testing.T) {
205 b := NewAllFlagsBuilder(OptionWithReasons())
206
207 flag1 := FlagState{
208 Value: ldvalue.String("value1"),
209 Variation: ldvalue.NewOptionalInt(1),
210 Version: 1000,
211 Reason: ldreason.NewEvalReasonFallthrough(),
212 }
213 flag2 := FlagState{
214 Value: ldvalue.String("value2"),
215 Version: 2000,
216 Reason: ldreason.NewEvalReasonError(ldreason.EvalErrorException),
217 TrackEvents: true,
218 TrackReason: true,
219 DebugEventsUntilDate: ldtime.UnixMillisecondTime(100000),
220 }
221 b.AddFlag("flag1", flag1)
222 b.AddFlag("flag2", flag2)
223
224 a := b.Build()
225 assert.Equal(t, map[string]FlagState{
226 "flag1": flag1,
227 "flag2": flag2,
228 }, a.flags)
229 })
230
231 t.Run("add flags with details only if tracked", func(t *testing.T) {
232 b := NewAllFlagsBuilder(OptionWithReasons(), OptionDetailsOnlyForTrackedFlags())
233
234
235 flag1 := FlagState{
236 Value: ldvalue.String("value1"),
237 Variation: ldvalue.NewOptionalInt(1),
238 Version: 1000,
239 Reason: ldreason.NewEvalReasonFallthrough(),
240 }
241
242
243
244 flag2 := FlagState{
245 Value: ldvalue.String("value2"),
246 Variation: ldvalue.NewOptionalInt(2),
247 Version: 2000,
248 Reason: ldreason.NewEvalReasonFallthrough(),
249 DebugEventsUntilDate: ldtime.UnixMillisecondTime(1),
250 }
251
252
253 flag3 := FlagState{
254 Value: ldvalue.String("value3"),
255 Variation: ldvalue.NewOptionalInt(3),
256 Version: 3000,
257 Reason: ldreason.NewEvalReasonRuleMatch(3, "rule3"),
258 TrackEvents: true,
259 }
260
261
262 flag4 := FlagState{
263 Value: ldvalue.String("value4"),
264 Variation: ldvalue.NewOptionalInt(4),
265 Version: 4000,
266 Reason: ldreason.NewEvalReasonRuleMatch(4, "rule4"),
267 DebugEventsUntilDate: ldtime.UnixMillisNow() + 10000,
268 }
269
270
271 flag5 := FlagState{
272 Value: ldvalue.String("value5"),
273 Variation: ldvalue.NewOptionalInt(5),
274 Version: 5000,
275 Reason: ldreason.NewEvalReasonRuleMatch(5, "rule5"),
276 TrackReason: true,
277 }
278
279 b.AddFlag("flag1", flag1)
280 b.AddFlag("flag2", flag2)
281 b.AddFlag("flag3", flag3)
282 b.AddFlag("flag4", flag4)
283 b.AddFlag("flag5", flag5)
284
285 flag1WithoutDetails, flag2WithoutDetails := flag1, flag2
286 flag1WithoutDetails.OmitDetails = true
287 flag2WithoutDetails.OmitDetails = true
288
289 a := b.Build()
290 assert.Equal(t, map[string]FlagState{
291 "flag1": flag1WithoutDetails,
292 "flag2": flag2WithoutDetails,
293 "flag3": flag3,
294 "flag4": flag4,
295 "flag5": flag5,
296 }, a.flags)
297 })
298 }
299
300 func TestAllFlagsOptions(t *testing.T) {
301 assert.Equal(t, "ClientSideOnly", OptionClientSideOnly().String())
302 assert.Equal(t, "WithReasons", OptionWithReasons().String())
303 assert.Equal(t, "DetailsOnlyForTrackedFlags", OptionDetailsOnlyForTrackedFlags().String())
304 }
305
View as plain text