1
+ /**
2
+ *
3
+ * Copyright 2016-2019, 2021, Optimizely and contributors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ package com .optimizely .ab .config ;
18
+
19
+ import java .util .Arrays ;
20
+ import java .util .Collections ;
21
+ import java .util .List ;
22
+
23
+ import static org .junit .Assert .assertEquals ;
24
+ import static org .junit .Assert .assertFalse ;
25
+ import static org .junit .Assert .assertNull ;
26
+ import static org .junit .Assert .assertSame ;
27
+ import static org .junit .Assert .assertTrue ;
28
+ import static org .junit .Assert .fail ;
29
+ import org .junit .Before ;
30
+ import org .junit .Test ;
31
+
32
+ public class HoldoutConfigTest {
33
+
34
+ private Holdout globalHoldout ;
35
+ private Holdout includedHoldout ;
36
+ private Holdout excludedHoldout ;
37
+ private Holdout mixedHoldout ;
38
+
39
+ @ Before
40
+ public void setUp () {
41
+ // Global holdout (no included/excluded flags)
42
+ globalHoldout = new Holdout ("global1" , "global_holdout" );
43
+
44
+ // Holdout with included flags
45
+ includedHoldout = new Holdout ("included1" , "included_holdout" , null ,
46
+ Collections .emptyList (), null , Collections .emptyList (),
47
+ Collections .emptyList (), Arrays .asList ("flag1" , "flag2" ), null , "" );
48
+
49
+ // Global holdout with excluded flags
50
+ excludedHoldout = new Holdout ("excluded1" , "excluded_holdout" , null ,
51
+ Collections .emptyList (), null , Collections .emptyList (),
52
+ Collections .emptyList (), null , Arrays .asList ("flag3" ), "" );
53
+
54
+ // Another global holdout for testing
55
+ mixedHoldout = new Holdout ("mixed1" , "mixed_holdout" );
56
+ }
57
+
58
+ @ Test
59
+ public void testEmptyConstructor () {
60
+ HoldoutConfig config = new HoldoutConfig ();
61
+
62
+ assertTrue (config .getAllHoldouts ().isEmpty ());
63
+ assertTrue (config .getHoldoutForFlag ("any_flag" ).isEmpty ());
64
+ assertNull (config .getHoldout ("any_id" ));
65
+ }
66
+
67
+ @ Test
68
+ public void testConstructorWithEmptyList () {
69
+ HoldoutConfig config = new HoldoutConfig (Collections .emptyList ());
70
+
71
+ assertTrue (config .getAllHoldouts ().isEmpty ());
72
+ assertTrue (config .getHoldoutForFlag ("any_flag" ).isEmpty ());
73
+ assertNull (config .getHoldout ("any_id" ));
74
+ }
75
+
76
+ @ Test
77
+ public void testConstructorWithGlobalHoldouts () {
78
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , mixedHoldout );
79
+ HoldoutConfig config = new HoldoutConfig (holdouts );
80
+
81
+ assertEquals (2 , config .getAllHoldouts ().size ());
82
+ assertTrue (config .getAllHoldouts ().contains (globalHoldout ));
83
+ }
84
+
85
+ @ Test
86
+ public void testGetHoldout () {
87
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , includedHoldout );
88
+ HoldoutConfig config = new HoldoutConfig (holdouts );
89
+
90
+ assertEquals (globalHoldout , config .getHoldout ("global1" ));
91
+ assertEquals (includedHoldout , config .getHoldout ("included1" ));
92
+ assertNull (config .getHoldout ("nonexistent" ));
93
+ }
94
+
95
+ @ Test
96
+ public void testGetHoldoutForFlagWithGlobalHoldouts () {
97
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , mixedHoldout );
98
+ HoldoutConfig config = new HoldoutConfig (holdouts );
99
+
100
+ List <Holdout > flagHoldouts = config .getHoldoutForFlag ("any_flag" );
101
+ assertEquals (2 , flagHoldouts .size ());
102
+ assertTrue (flagHoldouts .contains (globalHoldout ));
103
+ assertTrue (flagHoldouts .contains (mixedHoldout ));
104
+ }
105
+
106
+ @ Test
107
+ public void testGetHoldoutForFlagWithIncludedHoldouts () {
108
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , includedHoldout );
109
+ HoldoutConfig config = new HoldoutConfig (holdouts );
110
+
111
+ // Flag included in holdout
112
+ List <Holdout > flag1Holdouts = config .getHoldoutForFlag ("flag1" );
113
+ assertEquals (2 , flag1Holdouts .size ());
114
+ assertTrue (flag1Holdouts .contains (globalHoldout )); // Global first
115
+ assertTrue (flag1Holdouts .contains (includedHoldout )); // Included second
116
+
117
+ List <Holdout > flag2Holdouts = config .getHoldoutForFlag ("flag2" );
118
+ assertEquals (2 , flag2Holdouts .size ());
119
+ assertTrue (flag2Holdouts .contains (globalHoldout ));
120
+ assertTrue (flag2Holdouts .contains (includedHoldout ));
121
+
122
+ // Flag not included in holdout
123
+ List <Holdout > flag3Holdouts = config .getHoldoutForFlag ("flag3" );
124
+ assertEquals (1 , flag3Holdouts .size ());
125
+ assertTrue (flag3Holdouts .contains (globalHoldout )); // Only global
126
+ }
127
+
128
+ @ Test
129
+ public void testGetHoldoutForFlagWithExcludedHoldouts () {
130
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , excludedHoldout );
131
+ HoldoutConfig config = new HoldoutConfig (holdouts );
132
+
133
+ // Flag excluded from holdout
134
+ List <Holdout > flag3Holdouts = config .getHoldoutForFlag ("flag3" );
135
+ assertEquals (1 , flag3Holdouts .size ());
136
+ assertTrue (flag3Holdouts .contains (globalHoldout )); // excludedHoldout should be filtered out
137
+
138
+ // Flag not excluded
139
+ List <Holdout > flag1Holdouts = config .getHoldoutForFlag ("flag1" );
140
+ assertEquals (2 , flag1Holdouts .size ());
141
+ assertTrue (flag1Holdouts .contains (globalHoldout ));
142
+ assertTrue (flag1Holdouts .contains (excludedHoldout ));
143
+ }
144
+
145
+ @ Test
146
+ public void testGetHoldoutForFlagWithMixedHoldouts () {
147
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , includedHoldout , excludedHoldout );
148
+ HoldoutConfig config = new HoldoutConfig (holdouts );
149
+
150
+ // flag1 is included in includedHoldout
151
+ List <Holdout > flag1Holdouts = config .getHoldoutForFlag ("flag1" );
152
+ assertEquals (3 , flag1Holdouts .size ());
153
+ assertTrue (flag1Holdouts .contains (globalHoldout ));
154
+ assertTrue (flag1Holdouts .contains (excludedHoldout ));
155
+ assertTrue (flag1Holdouts .contains (includedHoldout ));
156
+
157
+ // flag3 is excluded from excludedHoldout
158
+ List <Holdout > flag3Holdouts = config .getHoldoutForFlag ("flag3" );
159
+ assertEquals (1 , flag3Holdouts .size ());
160
+ assertTrue (flag3Holdouts .contains (globalHoldout )); // Only global, excludedHoldout filtered out
161
+
162
+ // flag4 has no specific inclusion/exclusion
163
+ List <Holdout > flag4Holdouts = config .getHoldoutForFlag ("flag4" );
164
+ assertEquals (2 , flag4Holdouts .size ());
165
+ assertTrue (flag4Holdouts .contains (globalHoldout ));
166
+ assertTrue (flag4Holdouts .contains (excludedHoldout ));
167
+ }
168
+
169
+ @ Test
170
+ public void testCachingBehavior () {
171
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , includedHoldout );
172
+ HoldoutConfig config = new HoldoutConfig (holdouts );
173
+
174
+ // First call
175
+ List <Holdout > firstCall = config .getHoldoutForFlag ("flag1" );
176
+ // Second call should return cached result (same object reference)
177
+ List <Holdout > secondCall = config .getHoldoutForFlag ("flag1" );
178
+
179
+ assertSame (firstCall , secondCall );
180
+ assertEquals (2 , firstCall .size ());
181
+ }
182
+
183
+ @ Test
184
+ public void testGetAllHoldoutsIsUnmodifiable () {
185
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , includedHoldout );
186
+ HoldoutConfig config = new HoldoutConfig (holdouts );
187
+
188
+ List <Holdout > allHoldouts = config .getAllHoldouts ();
189
+
190
+ try {
191
+ allHoldouts .add (mixedHoldout );
192
+ fail ("Should throw UnsupportedOperationException" );
193
+ } catch (UnsupportedOperationException e ) {
194
+ // Expected
195
+ }
196
+ }
197
+
198
+ @ Test
199
+ public void testEmptyFlagHoldouts () {
200
+ HoldoutConfig config = new HoldoutConfig ();
201
+
202
+ List <Holdout > flagHoldouts = config .getHoldoutForFlag ("any_flag" );
203
+ assertTrue (flagHoldouts .isEmpty ());
204
+
205
+ // Should return same empty list for subsequent calls (caching)
206
+ List <Holdout > secondCall = config .getHoldoutForFlag ("any_flag" );
207
+ assertSame (flagHoldouts , secondCall );
208
+ }
209
+
210
+ @ Test
211
+ public void testHoldoutWithBothIncludedAndExcluded () {
212
+ // Create a holdout with both included and excluded flags (included takes precedence)
213
+ Holdout bothHoldout = new Holdout ("both1" , "both_holdout" , null ,
214
+ Collections .emptyList (), null , Collections .emptyList (),
215
+ Collections .emptyList (), Arrays .asList ("flag1" ), Arrays .asList ("flag2" ), "" );
216
+
217
+ List <Holdout > holdouts = Arrays .asList (globalHoldout , bothHoldout );
218
+ HoldoutConfig config = new HoldoutConfig (holdouts );
219
+
220
+ // flag1 should include bothHoldout (included takes precedence)
221
+ List <Holdout > flag1Holdouts = config .getHoldoutForFlag ("flag1" );
222
+ assertEquals (2 , flag1Holdouts .size ());
223
+ assertTrue (flag1Holdouts .contains (globalHoldout ));
224
+ assertTrue (flag1Holdouts .contains (bothHoldout ));
225
+
226
+ // flag2 should not include bothHoldout (not in included list)
227
+ List <Holdout > flag2Holdouts = config .getHoldoutForFlag ("flag2" );
228
+ assertEquals (1 , flag2Holdouts .size ());
229
+ assertTrue (flag2Holdouts .contains (globalHoldout ));
230
+ assertFalse (flag2Holdouts .contains (bothHoldout ));
231
+ }
232
+
233
+ }
0 commit comments