|
15 | 15 | ***************************************************************************/
|
16 | 16 | package com.optimizely.ab.bucketing;
|
17 | 17 |
|
18 |
| -import ch.qos.logback.classic.Level; |
| 18 | +import java.util.AbstractMap; |
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.Arrays; |
| 21 | +import java.util.Collections; |
| 22 | +import java.util.HashMap; |
| 23 | +import java.util.List; |
| 24 | +import java.util.Map; |
| 25 | + |
| 26 | +import static org.hamcrest.CoreMatchers.is; |
| 27 | +import static org.hamcrest.MatcherAssert.assertThat; |
| 28 | +import static org.junit.Assert.assertFalse; |
| 29 | +import static org.junit.Assert.assertNotEquals; |
| 30 | +import static org.junit.Assert.assertNotNull; |
| 31 | +import static org.junit.Assert.assertNull; |
| 32 | +import static org.junit.Assert.assertTrue; |
| 33 | +import org.junit.Before; |
| 34 | +import org.junit.Rule; |
| 35 | +import org.junit.Test; |
| 36 | +import static org.mockito.Matchers.any; |
| 37 | +import static org.mockito.Matchers.anyMapOf; |
| 38 | +import static org.mockito.Matchers.anyObject; |
| 39 | +import static org.mockito.Matchers.anyString; |
| 40 | +import static org.mockito.Matchers.eq; |
| 41 | +import org.mockito.Mock; |
| 42 | +import static org.mockito.Mockito.atMost; |
| 43 | +import static org.mockito.Mockito.doReturn; |
| 44 | +import static org.mockito.Mockito.doThrow; |
| 45 | +import static org.mockito.Mockito.mock; |
| 46 | +import static org.mockito.Mockito.never; |
| 47 | +import static org.mockito.Mockito.spy; |
| 48 | +import static org.mockito.Mockito.times; |
| 49 | +import static org.mockito.Mockito.verify; |
| 50 | +import static org.mockito.Mockito.when; |
| 51 | +import org.mockito.junit.MockitoJUnit; |
| 52 | +import org.mockito.junit.MockitoRule; |
| 53 | + |
| 54 | +import com.fasterxml.jackson.annotation.JsonFormat; |
19 | 55 | import com.optimizely.ab.Optimizely;
|
20 | 56 | import com.optimizely.ab.OptimizelyDecisionContext;
|
21 | 57 | import com.optimizely.ab.OptimizelyForcedDecision;
|
22 | 58 | import com.optimizely.ab.OptimizelyUserContext;
|
23 |
| -import com.optimizely.ab.config.*; |
| 59 | +import com.optimizely.ab.config.DatafileProjectConfigTestUtils; |
| 60 | +import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.noAudienceProjectConfigV3; |
| 61 | +import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV3; |
| 62 | +import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validProjectConfigV4; |
| 63 | +import com.optimizely.ab.config.Experiment; |
| 64 | +import com.optimizely.ab.config.FeatureFlag; |
| 65 | +import com.optimizely.ab.config.ProjectConfig; |
| 66 | +import com.optimizely.ab.config.Rollout; |
| 67 | +import com.optimizely.ab.config.TrafficAllocation; |
| 68 | +import com.optimizely.ab.config.ValidProjectConfigV4; |
| 69 | +import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_HOUSE_KEY; |
| 70 | +import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_NATIONALITY_KEY; |
| 71 | +import static com.optimizely.ab.config.ValidProjectConfigV4.AUDIENCE_ENGLISH_CITIZENS_VALUE; |
| 72 | +import static com.optimizely.ab.config.ValidProjectConfigV4.AUDIENCE_GRYFFINDOR_VALUE; |
| 73 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_BOOLEAN_FEATURE; |
| 74 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_MULTI_VARIATE_FEATURE; |
| 75 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_MULTI_VARIATE_FUTURE_FEATURE; |
| 76 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_MUTEX_GROUP_FEATURE; |
| 77 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_SINGLE_VARIABLE_BOOLEAN; |
| 78 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_SINGLE_VARIABLE_INTEGER; |
| 79 | +import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_MULTI_VARIATE_FEATURE_KEY; |
| 80 | +import static com.optimizely.ab.config.ValidProjectConfigV4.HOLDOUT_BASIC_HOLDOUT; |
| 81 | +import static com.optimizely.ab.config.ValidProjectConfigV4.HOLDOUT_EXCLUDED_FLAGS_HOLDOUT; |
| 82 | +import static com.optimizely.ab.config.ValidProjectConfigV4.HOLDOUT_INCLUDED_FLAGS_HOLDOUT; |
| 83 | +import static com.optimizely.ab.config.ValidProjectConfigV4.ROLLOUT_2; |
| 84 | +import static com.optimizely.ab.config.ValidProjectConfigV4.ROLLOUT_3_EVERYONE_ELSE_RULE; |
| 85 | +import static com.optimizely.ab.config.ValidProjectConfigV4.ROLLOUT_3_EVERYONE_ELSE_RULE_ENABLED_VARIATION; |
| 86 | +import static com.optimizely.ab.config.ValidProjectConfigV4.VARIATION_HOLDOUT_VARIATION_OFF; |
| 87 | +import static com.optimizely.ab.config.ValidProjectConfigV4.generateValidProjectConfigV4_holdout; |
| 88 | +import com.optimizely.ab.config.Variation; |
24 | 89 | import com.optimizely.ab.error.ErrorHandler;
|
25 | 90 | import com.optimizely.ab.internal.ControlAttribute;
|
26 | 91 | import com.optimizely.ab.internal.LogbackVerifier;
|
27 | 92 | import com.optimizely.ab.optimizelydecision.DecisionReasons;
|
28 | 93 | import com.optimizely.ab.optimizelydecision.DecisionResponse;
|
29 |
| -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; |
30 |
| -import org.junit.Before; |
31 |
| -import org.junit.Rule; |
32 |
| -import org.junit.Test; |
33 |
| -import org.mockito.Mock; |
34 |
| -import org.mockito.junit.MockitoJUnit; |
35 |
| -import org.mockito.junit.MockitoRule; |
36 |
| - |
37 |
| -import java.util.*; |
38 | 94 |
|
39 |
| -import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.*; |
40 |
| -import static com.optimizely.ab.config.ValidProjectConfigV4.*; |
| 95 | +import ch.qos.logback.classic.Level; |
| 96 | +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; |
41 | 97 | import static junit.framework.TestCase.assertEquals;
|
42 |
| -import static org.hamcrest.CoreMatchers.is; |
43 |
| -import static org.hamcrest.MatcherAssert.assertThat; |
44 |
| -import static org.junit.Assert.*; |
45 |
| -import static org.mockito.Matchers.*; |
46 |
| -import static org.mockito.Mockito.*; |
47 | 98 |
|
48 | 99 | public class DecisionServiceTest {
|
49 | 100 |
|
@@ -1243,14 +1294,65 @@ public void getVariationForFeatureReturnHoldoutDecisionForGlobalHoldout() {
|
1243 | 1294 | optimizely.createUserContext("user123", attributes),
|
1244 | 1295 | holdoutProjectConfig
|
1245 | 1296 | ).getResult();
|
1246 |
| - |
| 1297 | + |
| 1298 | + assertEquals(HOLDOUT_BASIC_HOLDOUT, featureDecision.experiment); |
1247 | 1299 | assertEquals(VARIATION_HOLDOUT_VARIATION_OFF, featureDecision.variation);
|
1248 | 1300 | assertEquals(FeatureDecision.DecisionSource.HOLDOUT, featureDecision.decisionSource);
|
1249 | 1301 |
|
1250 | 1302 | logbackVerifier.expectMessage(Level.INFO, "User (user123) is in variation (ho_off_key) of holdout (basic_holdout).");
|
1251 | 1303 | }
|
1252 |
| - |
1253 | 1304 |
|
| 1305 | + @Test |
| 1306 | + public void includedFlagsHoldoutOnlyAppliestoSpecificFlags() { |
| 1307 | + ProjectConfig holdoutProjectConfig = generateValidProjectConfigV4_holdout(); |
1254 | 1308 |
|
| 1309 | + Bucketer mockBucketer = new Bucketer(); |
1255 | 1310 |
|
| 1311 | + DecisionService decisionService = new DecisionService(mockBucketer, mockErrorHandler, null); |
| 1312 | + |
| 1313 | + Map<String, Object> attributes = new HashMap<>(); |
| 1314 | + attributes.put("$opt_bucketing_id", "ppid120000"); |
| 1315 | + FeatureDecision featureDecision = decisionService.getVariationForFeature( |
| 1316 | + FEATURE_FLAG_BOOLEAN_FEATURE, |
| 1317 | + optimizely.createUserContext("user123", attributes), |
| 1318 | + holdoutProjectConfig |
| 1319 | + ).getResult(); |
| 1320 | + |
| 1321 | + assertEquals(HOLDOUT_INCLUDED_FLAGS_HOLDOUT, featureDecision.experiment); |
| 1322 | + assertEquals(VARIATION_HOLDOUT_VARIATION_OFF, featureDecision.variation); |
| 1323 | + assertEquals(FeatureDecision.DecisionSource.HOLDOUT, featureDecision.decisionSource); |
| 1324 | + |
| 1325 | + logbackVerifier.expectMessage(Level.INFO, "User (user123) is in variation (ho_off_key) of holdout (holdout_included_flags)."); |
| 1326 | + } |
| 1327 | + |
| 1328 | + @Test |
| 1329 | + public void excludedFlagsHoldoutAppliesToAllExceptSpecified() { |
| 1330 | + ProjectConfig holdoutProjectConfig = generateValidProjectConfigV4_holdout(); |
| 1331 | + |
| 1332 | + Bucketer mockBucketer = new Bucketer(); |
| 1333 | + |
| 1334 | + DecisionService decisionService = new DecisionService(mockBucketer, mockErrorHandler, null); |
| 1335 | + |
| 1336 | + Map<String, Object> attributes = new HashMap<>(); |
| 1337 | + attributes.put("$opt_bucketing_id", "ppid300002"); |
| 1338 | + FeatureDecision excludedDecision = decisionService.getVariationForFeature( |
| 1339 | + FEATURE_FLAG_SINGLE_VARIABLE_BOOLEAN, // excluded from ho (holdout_excluded_flags) |
| 1340 | + optimizely.createUserContext("user123", attributes), |
| 1341 | + holdoutProjectConfig |
| 1342 | + ).getResult(); |
| 1343 | + |
| 1344 | + assertNotEquals(FeatureDecision.DecisionSource.HOLDOUT, excludedDecision.decisionSource); |
| 1345 | + |
| 1346 | + FeatureDecision featureDecision = decisionService.getVariationForFeature( |
| 1347 | + FEATURE_FLAG_SINGLE_VARIABLE_INTEGER, // excluded from ho (holdout_excluded_flags) |
| 1348 | + optimizely.createUserContext("user123", attributes), |
| 1349 | + holdoutProjectConfig |
| 1350 | + ).getResult(); |
| 1351 | + |
| 1352 | + assertEquals(HOLDOUT_EXCLUDED_FLAGS_HOLDOUT, featureDecision.experiment); |
| 1353 | + assertEquals(VARIATION_HOLDOUT_VARIATION_OFF, featureDecision.variation); |
| 1354 | + assertEquals(FeatureDecision.DecisionSource.HOLDOUT, featureDecision.decisionSource); |
| 1355 | + |
| 1356 | + logbackVerifier.expectMessage(Level.INFO, "User (user123) is in variation (ho_off_key) of holdout (holdout_excluded_flags)."); |
| 1357 | + } |
1256 | 1358 | }
|
0 commit comments