11package com .launchdarkly .client ;
22
3- import com .google .gson .JsonElement ;
4- import com .google .gson .JsonPrimitive ;
5-
6- import org .junit .Assert ;
73import org .junit .Before ;
84import org .junit .Test ;
95
106import java .util .Arrays ;
117
8+ import static com .launchdarkly .client .TestUtil .booleanFlagWithClauses ;
9+ import static com .launchdarkly .client .TestUtil .fallthroughVariation ;
10+ import static com .launchdarkly .client .TestUtil .jbool ;
11+ import static com .launchdarkly .client .TestUtil .jint ;
12+ import static com .launchdarkly .client .TestUtil .js ;
1213import static com .launchdarkly .client .VersionedDataKind .FEATURES ;
1314import static com .launchdarkly .client .VersionedDataKind .SEGMENTS ;
14- import static java .util .Collections .singletonList ;
15+ import static org .junit .Assert .assertEquals ;
16+ import static org .junit .Assert .assertNull ;
1517
1618public class FeatureFlagTest {
1719
20+ private static LDUser BASE_USER = new LDUser .Builder ("x" ).build ();
21+
1822 private FeatureStore featureStore ;
1923
2024 @ Before
@@ -23,51 +27,222 @@ public void before() {
2327 }
2428
2529 @ Test
26- public void testPrereqDoesNotExist () throws EvaluationException {
27- String keyA = "keyA" ;
28- String keyB = "keyB" ;
29- FeatureFlag f1 = newFlagWithPrereq (keyA , keyB );
30-
31- featureStore .upsert (FEATURES , f1 );
32- LDUser user = new LDUser .Builder ("userKey" ).build ();
33- FeatureFlag .EvalResult actual = f1 .evaluate (user , featureStore );
34-
35- Assert .assertNull (actual .getValue ());
36- Assert .assertNotNull (actual .getPrerequisiteEvents ());
37- Assert .assertEquals (0 , actual .getPrerequisiteEvents ().size ());
30+ public void flagReturnsOffVariationIfFlagIsOff () throws Exception {
31+ FeatureFlag f = new FeatureFlagBuilder ("feature" )
32+ .on (false )
33+ .offVariation (1 )
34+ .fallthrough (fallthroughVariation (0 ))
35+ .variations (js ("fall" ), js ("off" ), js ("on" ))
36+ .build ();
37+ FeatureFlag .EvalResult result = f .evaluate (BASE_USER , featureStore );
38+
39+ assertEquals (js ("off" ), result .getValue ());
40+ assertEquals (0 , result .getPrerequisiteEvents ().size ());
3841 }
3942
4043 @ Test
41- public void testPrereqCollectsEventsForPrereqs () throws EvaluationException {
42- String keyA = "keyA" ;
43- String keyB = "keyB" ;
44- String keyC = "keyC" ;
45- FeatureFlag flagA = newFlagWithPrereq (keyA , keyB );
46- FeatureFlag flagB = newFlagWithPrereq (keyB , keyC );
47- FeatureFlag flagC = newFlagOff (keyC );
48-
49- featureStore .upsert (FEATURES , flagA );
50- featureStore .upsert (FEATURES , flagB );
51- featureStore .upsert (FEATURES , flagC );
52-
53- LDUser user = new LDUser .Builder ("userKey" ).build ();
44+ public void flagReturnsNullIfFlagIsOffAndOffVariationIsUnspecified () throws Exception {
45+ FeatureFlag f = new FeatureFlagBuilder ("feature" )
46+ .on (false )
47+ .fallthrough (fallthroughVariation (0 ))
48+ .variations (js ("fall" ), js ("off" ), js ("on" ))
49+ .build ();
50+ FeatureFlag .EvalResult result = f .evaluate (BASE_USER , featureStore );
51+
52+ assertNull (result .getValue ());
53+ assertEquals (0 , result .getPrerequisiteEvents ().size ());
54+ }
55+
56+ @ Test
57+ public void flagReturnsOffVariationIfPrerequisiteIsNotFound () throws Exception {
58+ FeatureFlag f0 = new FeatureFlagBuilder ("feature0" )
59+ .on (true )
60+ .prerequisites (Arrays .asList (new Prerequisite ("feature1" , 1 )))
61+ .fallthrough (fallthroughVariation (0 ))
62+ .offVariation (1 )
63+ .variations (js ("fall" ), js ("off" ), js ("on" ))
64+ .build ();
65+ FeatureFlag .EvalResult result = f0 .evaluate (BASE_USER , featureStore );
66+
67+ assertEquals (js ("off" ), result .getValue ());
68+ assertEquals (0 , result .getPrerequisiteEvents ().size ());
69+ }
70+
71+ @ Test
72+ public void flagReturnsOffVariationAndEventIfPrerequisiteIsNotMet () throws Exception {
73+ FeatureFlag f0 = new FeatureFlagBuilder ("feature0" )
74+ .on (true )
75+ .prerequisites (Arrays .asList (new Prerequisite ("feature1" , 1 )))
76+ .fallthrough (fallthroughVariation (0 ))
77+ .offVariation (1 )
78+ .variations (js ("fall" ), js ("off" ), js ("on" ))
79+ .version (1 )
80+ .build ();
81+ FeatureFlag f1 = new FeatureFlagBuilder ("feature1" )
82+ .on (true )
83+ .fallthrough (fallthroughVariation (0 ))
84+ .variations (js ("nogo" ), js ("go" ))
85+ .version (2 )
86+ .build ();
87+ featureStore .upsert (FEATURES , f1 );
88+ FeatureFlag .EvalResult result = f0 .evaluate (BASE_USER , featureStore );
89+
90+ assertEquals (js ("off" ), result .getValue ());
91+
92+ assertEquals (1 , result .getPrerequisiteEvents ().size ());
93+ FeatureRequestEvent event = result .getPrerequisiteEvents ().get (0 );
94+ assertEquals (f1 .getKey (), event .key );
95+ assertEquals ("feature" , event .kind );
96+ assertEquals (js ("nogo" ), event .value );
97+ assertEquals (f1 .getVersion (), event .version .intValue ());
98+ assertEquals (f0 .getKey (), event .prereqOf );
99+ }
54100
55- FeatureFlag .EvalResult flagAResult = flagA .evaluate (user , featureStore );
56- Assert .assertNotNull (flagAResult );
57- Assert .assertNull (flagAResult .getValue ());
58- Assert .assertEquals (2 , flagAResult .getPrerequisiteEvents ().size ());
101+ @ Test
102+ public void flagReturnsFallthroughVariationAndEventIfPrerequisiteIsMetAndThereAreNoRules () throws Exception {
103+ FeatureFlag f0 = new FeatureFlagBuilder ("feature0" )
104+ .on (true )
105+ .prerequisites (Arrays .asList (new Prerequisite ("feature1" , 1 )))
106+ .fallthrough (fallthroughVariation (0 ))
107+ .offVariation (1 )
108+ .variations (js ("fall" ), js ("off" ), js ("on" ))
109+ .version (1 )
110+ .build ();
111+ FeatureFlag f1 = new FeatureFlagBuilder ("feature1" )
112+ .on (true )
113+ .fallthrough (fallthroughVariation (1 ))
114+ .variations (js ("nogo" ), js ("go" ))
115+ .version (2 )
116+ .build ();
117+ featureStore .upsert (FEATURES , f1 );
118+ FeatureFlag .EvalResult result = f0 .evaluate (BASE_USER , featureStore );
119+
120+ assertEquals (js ("fall" ), result .getValue ());
121+ assertEquals (1 , result .getPrerequisiteEvents ().size ());
122+
123+ FeatureRequestEvent event = result .getPrerequisiteEvents ().get (0 );
124+ assertEquals (f1 .getKey (), event .key );
125+ assertEquals ("feature" , event .kind );
126+ assertEquals (js ("go" ), event .value );
127+ assertEquals (f1 .getVersion (), event .version .intValue ());
128+ assertEquals (f0 .getKey (), event .prereqOf );
129+ }
59130
60- FeatureFlag .EvalResult flagBResult = flagB .evaluate (user , featureStore );
61- Assert .assertNotNull (flagBResult );
62- Assert .assertNull (flagBResult .getValue ());
63- Assert .assertEquals (1 , flagBResult .getPrerequisiteEvents ().size ());
131+ @ Test
132+ public void multipleLevelsOfPrerequisitesProduceMultipleEvents () throws Exception {
133+ FeatureFlag f0 = new FeatureFlagBuilder ("feature0" )
134+ .on (true )
135+ .prerequisites (Arrays .asList (new Prerequisite ("feature1" , 1 )))
136+ .fallthrough (fallthroughVariation (0 ))
137+ .offVariation (1 )
138+ .variations (js ("fall" ), js ("off" ), js ("on" ))
139+ .version (1 )
140+ .build ();
141+ FeatureFlag f1 = new FeatureFlagBuilder ("feature1" )
142+ .on (true )
143+ .prerequisites (Arrays .asList (new Prerequisite ("feature2" , 1 )))
144+ .fallthrough (fallthroughVariation (1 ))
145+ .variations (js ("nogo" ), js ("go" ))
146+ .version (2 )
147+ .build ();
148+ FeatureFlag f2 = new FeatureFlagBuilder ("feature2" )
149+ .on (true )
150+ .fallthrough (fallthroughVariation (1 ))
151+ .variations (js ("nogo" ), js ("go" ))
152+ .version (3 )
153+ .build ();
154+ featureStore .upsert (FEATURES , f1 );
155+ featureStore .upsert (FEATURES , f2 );
156+ FeatureFlag .EvalResult result = f0 .evaluate (BASE_USER , featureStore );
157+
158+ assertEquals (js ("fall" ), result .getValue ());
159+ assertEquals (2 , result .getPrerequisiteEvents ().size ());
160+
161+ FeatureRequestEvent event0 = result .getPrerequisiteEvents ().get (0 );
162+ assertEquals (f2 .getKey (), event0 .key );
163+ assertEquals ("feature" , event0 .kind );
164+ assertEquals (js ("go" ), event0 .value );
165+ assertEquals (f2 .getVersion (), event0 .version .intValue ());
166+ assertEquals (f1 .getKey (), event0 .prereqOf );
64167
65- FeatureFlag .EvalResult flagCResult = flagC .evaluate (user , featureStore );
66- Assert .assertNotNull (flagCResult );
67- Assert .assertEquals (null , flagCResult .getValue ());
68- Assert .assertEquals (0 , flagCResult .getPrerequisiteEvents ().size ());
168+ FeatureRequestEvent event1 = result .getPrerequisiteEvents ().get (1 );
169+ assertEquals (f1 .getKey (), event1 .key );
170+ assertEquals ("feature" , event1 .kind );
171+ assertEquals (js ("go" ), event1 .value );
172+ assertEquals (f1 .getVersion (), event1 .version .intValue ());
173+ assertEquals (f0 .getKey (), event1 .prereqOf );
69174 }
70-
175+
176+ @ Test
177+ public void flagMatchesUserFromTargets () throws Exception {
178+ FeatureFlag f = new FeatureFlagBuilder ("feature" )
179+ .on (true )
180+ .targets (Arrays .asList (new Target (Arrays .asList ("whoever" , "userkey" ), 2 )))
181+ .fallthrough (fallthroughVariation (0 ))
182+ .offVariation (1 )
183+ .variations (js ("fall" ), js ("off" ), js ("on" ))
184+ .build ();
185+ LDUser user = new LDUser .Builder ("userkey" ).build ();
186+ FeatureFlag .EvalResult result = f .evaluate (user , featureStore );
187+
188+ assertEquals (js ("on" ), result .getValue ());
189+ assertEquals (0 , result .getPrerequisiteEvents ().size ());
190+ }
191+
192+ @ Test
193+ public void flagMatchesUserFromRules () throws Exception {
194+ Clause clause = new Clause ("key" , Operator .in , Arrays .asList (js ("userkey" )), false );
195+ Rule rule = new Rule (Arrays .asList (clause ), 2 , null );
196+ FeatureFlag f = new FeatureFlagBuilder ("feature" )
197+ .on (true )
198+ .rules (Arrays .asList (rule ))
199+ .fallthrough (fallthroughVariation (0 ))
200+ .offVariation (1 )
201+ .variations (js ("fall" ), js ("off" ), js ("on" ))
202+ .build ();
203+ LDUser user = new LDUser .Builder ("userkey" ).build ();
204+ FeatureFlag .EvalResult result = f .evaluate (user , featureStore );
205+
206+ assertEquals (js ("on" ), result .getValue ());
207+ assertEquals (0 , result .getPrerequisiteEvents ().size ());
208+ }
209+
210+ @ Test
211+ public void clauseCanMatchBuiltInAttribute () throws Exception {
212+ Clause clause = new Clause ("name" , Operator .in , Arrays .asList (js ("Bob" )), false );
213+ FeatureFlag f = booleanFlagWithClauses (clause );
214+ LDUser user = new LDUser .Builder ("key" ).name ("Bob" ).build ();
215+
216+ assertEquals (jbool (true ), f .evaluate (user , featureStore ).getValue ());
217+ }
218+
219+ @ Test
220+ public void clauseCanMatchCustomAttribute () throws Exception {
221+ Clause clause = new Clause ("legs" , Operator .in , Arrays .asList (jint (4 )), false );
222+ FeatureFlag f = booleanFlagWithClauses (clause );
223+ LDUser user = new LDUser .Builder ("key" ).custom ("legs" , 4 ).build ();
224+
225+ assertEquals (jbool (true ), f .evaluate (user , featureStore ).getValue ());
226+ }
227+
228+ @ Test
229+ public void clauseReturnsFalseForMissingAttribute () throws Exception {
230+ Clause clause = new Clause ("legs" , Operator .in , Arrays .asList (jint (4 )), false );
231+ FeatureFlag f = booleanFlagWithClauses (clause );
232+ LDUser user = new LDUser .Builder ("key" ).name ("Bob" ).build ();
233+
234+ assertEquals (jbool (false ), f .evaluate (user , featureStore ).getValue ());
235+ }
236+
237+ @ Test
238+ public void clauseCanBeNegated () throws Exception {
239+ Clause clause = new Clause ("name" , Operator .in , Arrays .asList (js ("Bob" )), true );
240+ FeatureFlag f = booleanFlagWithClauses (clause );
241+ LDUser user = new LDUser .Builder ("key" ).name ("Bob" ).build ();
242+
243+ assertEquals (jbool (false ), f .evaluate (user , featureStore ).getValue ());
244+ }
245+
71246 @ Test
72247 public void testSegmentMatchClauseRetrievesSegmentFromStore () throws Exception {
73248 Segment segment = new Segment .Builder ("segkey" )
@@ -80,7 +255,7 @@ public void testSegmentMatchClauseRetrievesSegmentFromStore() throws Exception {
80255 LDUser user = new LDUser .Builder ("foo" ).build ();
81256
82257 FeatureFlag .EvalResult result = flag .evaluate (user , featureStore );
83- Assert . assertEquals (new JsonPrimitive (true ), result .getValue ());
258+ assertEquals (jbool (true ), result .getValue ());
84259 }
85260
86261 @ Test
@@ -89,34 +264,11 @@ public void testSegmentMatchClauseFallsThroughIfSegmentNotFound() throws Excepti
89264 LDUser user = new LDUser .Builder ("foo" ).build ();
90265
91266 FeatureFlag .EvalResult result = flag .evaluate (user , featureStore );
92- Assert .assertEquals (new JsonPrimitive (false ), result .getValue ());
93- }
94-
95- private FeatureFlag newFlagWithPrereq (String featureKey , String prereqKey ) {
96- return new FeatureFlagBuilder (featureKey )
97- .prerequisites (singletonList (new Prerequisite (prereqKey , 0 )))
98- .variations (Arrays .<JsonElement >asList (new JsonPrimitive (0 ), new JsonPrimitive (1 )))
99- .fallthrough (new VariationOrRollout (0 , null ))
100- .on (true )
101- .build ();
267+ assertEquals (jbool (false ), result .getValue ());
102268 }
103-
104- private FeatureFlag newFlagOff (String featureKey ) {
105- return new FeatureFlagBuilder (featureKey )
106- .variations (Arrays .<JsonElement >asList (new JsonPrimitive (0 ), new JsonPrimitive (1 )))
107- .fallthrough (new VariationOrRollout (0 , null ))
108- .on (false )
109- .build ();
110- }
111-
269+
112270 private FeatureFlag segmentMatchBooleanFlag (String segmentKey ) {
113- Clause clause = new Clause ("" , Operator .segmentMatch , Arrays .asList (new JsonPrimitive (segmentKey )), false );
114- Rule rule = new Rule (Arrays .asList (clause ), 1 , null );
115- return new FeatureFlagBuilder ("key" )
116- .variations (Arrays .<JsonElement >asList (new JsonPrimitive (false ), new JsonPrimitive (true )))
117- .fallthrough (new VariationOrRollout (0 , null ))
118- .on (true )
119- .rules (Arrays .asList (rule ))
120- .build ();
271+ Clause clause = new Clause ("" , Operator .segmentMatch , Arrays .asList (js (segmentKey )), false );
272+ return booleanFlagWithClauses (clause );
121273 }
122274}
0 commit comments