Skip to content

Commit 117eb91

Browse files
author
Bilal Al
committed
added tests
1 parent 4d8dec8 commit 117eb91

File tree

3 files changed

+354
-5
lines changed

3 files changed

+354
-5
lines changed

client/src/test/java/io/split/client/impressions/ImpressionsManagerImplTest.java

Lines changed: 178 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
import pluggable.CustomStorageWrapper;
2929

3030
import java.net.URISyntaxException;
31-
import java.util.AbstractMap;
32-
import java.util.ArrayList;
33-
import java.util.HashMap;
34-
import java.util.List;
35-
import java.util.Optional;
31+
import java.util.*;
3632
import java.util.stream.Collectors;
3733
import java.util.stream.Stream;
3834

@@ -850,4 +846,181 @@ public void testCounterConsumerModeNoneMode() {
850846
manager.start();
851847
Assert.assertNotNull(manager.getCounter());
852848
}
849+
850+
@Test
851+
public void testImpressionToggleStandaloneOptimizedMode() {
852+
SplitClientConfig config = SplitClientConfig.builder()
853+
.impressionsQueueSize(10)
854+
.endpoint("nowhere.com", "nowhere.com")
855+
.impressionsMode(ImpressionsManager.Mode.OPTIMIZED)
856+
.build();
857+
ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
858+
859+
ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class);
860+
ImpressionCounter impressionCounter = new ImpressionCounter();
861+
ImpressionObserver impressionObserver = new ImpressionObserver(200);
862+
TelemetryStorageProducer telemetryStorageProducer = new InMemoryTelemetryStorage();
863+
TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class);
864+
UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null);
865+
uniqueKeysTracker.start();
866+
867+
ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionOptimized(false, impressionObserver, impressionCounter, telemetryStorageProducer);
868+
ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter);
869+
870+
ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null);
871+
treatmentLog.start();
872+
873+
// These 4 unique test name will cause 4 entries but we are caping at the first 3.
874+
KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L);
875+
KeyImpression ki2 = keyImpression("test1", "mati", "on", 2L, 1L);
876+
KeyImpression ki3 = keyImpression("test1", "pato", "on", 3L, 1L);
877+
KeyImpression ki4 = keyImpression("test1", "bilal", "on", 4L, 1L);
878+
879+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki1.keyName, null, ki1.feature, ki1.treatment, ki1.time, null, 1L, null), true)).collect(Collectors.toList()));
880+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki2.keyName, null, ki2.feature, ki2.treatment, ki2.time, null, 1L, null), false)).collect(Collectors.toList()));
881+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null), true)).collect(Collectors.toList()));
882+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki4.keyName, null, ki4.feature, ki4.treatment, ki4.time, null, 1L, null), false)).collect(Collectors.toList()));
883+
treatmentLog.sendImpressions();
884+
885+
verify(senderMock).postImpressionsBulk(impressionsCaptor.capture());
886+
887+
List<TestImpressions> captured = impressionsCaptor.getValue();
888+
Assert.assertEquals(2, captured.get(0).keyImpressions.size());
889+
for (TestImpressions testImpressions : captured) {
890+
for (KeyImpression keyImpression : testImpressions.keyImpressions) {
891+
Assert.assertEquals(null, keyImpression.previousTime);
892+
}
893+
}
894+
// Only the first 2 impressions make it to the server
895+
Assert.assertTrue(captured.get(0).keyImpressions.contains(keyImpression("test1", "adil", "on", 1L, 1L)));
896+
Assert.assertTrue(captured.get(0).keyImpressions.contains(keyImpression("test1", "pato", "on", 3L, 1L)));
897+
898+
HashMap<String, HashSet<String>> trackedKeys = ((UniqueKeysTrackerImp) uniqueKeysTracker).popAll();
899+
HashSet<String> keys = new HashSet<>();
900+
keys.add("mati");
901+
keys.add("bilal");
902+
Assert.assertEquals(1, trackedKeys.size());
903+
Assert.assertEquals(keys, trackedKeys.get("test1"));
904+
905+
treatmentLog.sendImpressionCounters();
906+
verify(senderMock).postCounters(impressionCountCaptor.capture());
907+
HashMap<ImpressionCounter.Key, Integer> capturedCounts = impressionCountCaptor.getValue();
908+
Assert.assertEquals(1, capturedCounts.size());
909+
Assert.assertTrue(capturedCounts.entrySet().contains(new AbstractMap.SimpleEntry<>(new ImpressionCounter.Key("test1", 0), 2)));
910+
911+
// Assert that the sender is never called if the counters are empty.
912+
Mockito.reset(senderMock);
913+
treatmentLog.sendImpressionCounters();
914+
verify(senderMock, times(0)).postCounters(Mockito.any());
915+
}
916+
917+
@Test
918+
public void testImpressionToggleStandaloneModeDebugMode() {
919+
SplitClientConfig config = SplitClientConfig.builder()
920+
.impressionsQueueSize(10)
921+
.endpoint("nowhere.com", "nowhere.com")
922+
.impressionsMode(ImpressionsManager.Mode.DEBUG)
923+
.build();
924+
ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
925+
926+
ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class);
927+
ImpressionCounter impressionCounter = Mockito.mock(ImpressionCounter.class);
928+
ImpressionObserver impressionObserver = new ImpressionObserver(200);
929+
ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionDebug(false, impressionObserver);
930+
TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class);
931+
UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null);
932+
uniqueKeysTracker.start();
933+
ProcessImpressionNone processImpressionNone = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter);
934+
935+
ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null);
936+
treatmentLog.start();
937+
938+
// These 4 unique test name will cause 4 entries but we are caping at the first 3.
939+
KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L);
940+
KeyImpression ki2 = keyImpression("test1", "mati", "on", 2L, 1L);
941+
KeyImpression ki3 = keyImpression("test1", "pato", "on", 3L, 1L);
942+
KeyImpression ki4 = keyImpression("test1", "bilal", "on", 4L, 1L);
943+
944+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki1.keyName, null, ki1.feature, ki1.treatment, ki1.time, null, 1L, null), true)).collect(Collectors.toList()));
945+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki2.keyName, null, ki2.feature, ki2.treatment, ki2.time, null, 1L, null), false)).collect(Collectors.toList()));
946+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null), true)).collect(Collectors.toList()));
947+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki4.keyName, null, ki4.feature, ki4.treatment, ki4.time, null, 1L, null), false)).collect(Collectors.toList()));
948+
treatmentLog.sendImpressions();
949+
950+
HashMap<String, HashSet<String>> trackedKeys = ((UniqueKeysTrackerImp) uniqueKeysTracker).popAll();
951+
HashSet<String> keys = new HashSet<>();
952+
keys.add("mati");
953+
keys.add("bilal");
954+
Assert.assertEquals(1, trackedKeys.size());
955+
Assert.assertEquals(keys, trackedKeys.get("test1"));
956+
957+
verify(senderMock).postImpressionsBulk(impressionsCaptor.capture());
958+
959+
List<TestImpressions> captured = impressionsCaptor.getValue();
960+
Assert.assertEquals(2, captured.get(0).keyImpressions.size());
961+
for (TestImpressions testImpressions : captured) {
962+
KeyImpression keyImpression1 = testImpressions.keyImpressions.get(0);
963+
KeyImpression keyImpression3 = testImpressions.keyImpressions.get(1);
964+
Assert.assertEquals(null, keyImpression1.previousTime);
965+
Assert.assertEquals(null, keyImpression3.previousTime);
966+
}
967+
// Only the first 2 impressions make it to the server
968+
Assert.assertTrue(captured.get(0).keyImpressions.contains(keyImpression("test1", "adil", "on", 1L, 1L)));
969+
Assert.assertTrue(captured.get(0).keyImpressions.contains(keyImpression("test1", "pato", "on", 3L, 1L)));
970+
}
971+
972+
@Test
973+
public void testImpressionToggleStandaloneModeNoneMode() {
974+
SplitClientConfig config = SplitClientConfig.builder()
975+
.impressionsQueueSize(10)
976+
.endpoint("nowhere.com", "nowhere.com")
977+
.impressionsMode(ImpressionsManager.Mode.NONE)
978+
.build();
979+
ImpressionsStorage storage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
980+
981+
ImpressionsSender senderMock = Mockito.mock(ImpressionsSender.class);
982+
TelemetrySynchronizer telemetrySynchronizer = Mockito.mock(TelemetryInMemorySubmitter.class);
983+
ImpressionCounter impressionCounter = new ImpressionCounter();
984+
UniqueKeysTracker uniqueKeysTracker = new UniqueKeysTrackerImp(telemetrySynchronizer, 1000, 1000, null);
985+
uniqueKeysTracker.start();
986+
987+
ProcessImpressionStrategy processImpressionStrategy = new ProcessImpressionNone(false, uniqueKeysTracker, impressionCounter);
988+
ProcessImpressionNone processImpressionNone = (ProcessImpressionNone) processImpressionStrategy;
989+
990+
ImpressionsManagerImpl treatmentLog = ImpressionsManagerImpl.instanceForTest(config, senderMock, TELEMETRY_STORAGE, storage, storage, processImpressionNone, processImpressionStrategy, impressionCounter, null);
991+
treatmentLog.start();
992+
993+
// These 4 unique test name will cause 4 entries but we are caping at the first 3.
994+
KeyImpression ki1 = keyImpression("test1", "adil", "on", 1L, 1L);
995+
KeyImpression ki2 = keyImpression("test1", "mati", "on", 2L, 1L);
996+
KeyImpression ki3 = keyImpression("test1", "pato", "on", 3L, 1L);
997+
KeyImpression ki4 = keyImpression("test1", "bilal", "on", 4L, 1L);
998+
999+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki1.keyName, null, ki1.feature, ki1.treatment, ki1.time, null, 1L, null), true)).collect(Collectors.toList()));
1000+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki2.keyName, null, ki2.feature, ki2.treatment, ki2.time, null, 1L, null), false)).collect(Collectors.toList()));
1001+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki3.keyName, null, ki3.feature, ki3.treatment, ki3.time, null, 1L, null), true)).collect(Collectors.toList()));
1002+
treatmentLog.track(Stream.of(new DecoratedImpression(new Impression(ki4.keyName, null, ki4.feature, ki4.treatment, ki4.time, null, 1L, null), false)).collect(Collectors.toList()));
1003+
treatmentLog.close();
1004+
HashMap<String, HashSet<String>> trackedKeys = ((UniqueKeysTrackerImp) uniqueKeysTracker).popAll();
1005+
uniqueKeysTracker.stop();
1006+
1007+
HashSet<String> keys = new HashSet<>();
1008+
keys.add("adil");
1009+
keys.add("mati");
1010+
keys.add("pato");
1011+
keys.add("bilal");
1012+
Assert.assertEquals(1, trackedKeys.size());
1013+
Assert.assertEquals(keys, trackedKeys.get("test1"));
1014+
1015+
//treatmentLog.sendImpressionCounters();
1016+
verify(senderMock).postCounters(impressionCountCaptor.capture());
1017+
HashMap<ImpressionCounter.Key, Integer> capturedCounts = impressionCountCaptor.getValue();
1018+
Assert.assertEquals(1, capturedCounts.size());
1019+
Assert.assertTrue(capturedCounts.entrySet().contains(new AbstractMap.SimpleEntry<>(new ImpressionCounter.Key("test1", 0), 4)));
1020+
1021+
// Assert that the sender is never called if the counters are empty.
1022+
Mockito.reset(senderMock);
1023+
treatmentLog.sendImpressionCounters();
1024+
verify(senderMock, times(0)).postCounters(Mockito.any());
1025+
}
8531026
}

client/src/test/java/io/split/engine/experiments/SplitParserTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import java.util.stream.Collectors;
4949
import java.util.stream.Stream;
5050

51+
import static org.junit.Assert.assertFalse;
5152
import static org.junit.Assert.assertTrue;
5253

5354
/**
@@ -639,6 +640,26 @@ public void InListSemverMatcher() throws IOException {
639640
assertTrue(false);
640641
}
641642

643+
@Test
644+
public void ImpressionToggleParseTest() throws IOException {
645+
SplitParser parser = new SplitParser();
646+
String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/splits_imp_toggle.json")), StandardCharsets.UTF_8);
647+
SplitChange change = Json.fromJson(splits, SplitChange.class);
648+
for (Split split : change.splits) {
649+
// should not cause exception
650+
ParsedSplit parsedSplit = parser.parse(split);
651+
if (split.name.equals("without_impression_toggle")) {
652+
assertTrue(split.trackImpression);
653+
}
654+
if (split.name.equals("impression_toggle_on")) {
655+
assertTrue(split.trackImpression);
656+
}
657+
if (split.name.equals("impression_toggle_off")) {
658+
assertFalse(split.trackImpression);
659+
}
660+
}
661+
}
662+
642663
public void setMatcherTest(Condition c, io.split.engine.matchers.Matcher m) {
643664

644665
SegmentChangeFetcher segmentChangeFetcher = Mockito.mock(SegmentChangeFetcher.class);
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
{
2+
"splits": [
3+
{
4+
"trafficTypeName": "user",
5+
"name": "without_impression_toggle",
6+
"trafficAllocation": 24,
7+
"trafficAllocationSeed": -172559061,
8+
"seed": -906334215,
9+
"status": "ACTIVE",
10+
"killed": true,
11+
"defaultTreatment": "off",
12+
"changeNumber": 1585948717645,
13+
"algo": 2,
14+
"configurations": {},
15+
"conditions": [
16+
{
17+
"conditionType": "ROLLOUT",
18+
"matcherGroup": {
19+
"combiner": "AND",
20+
"matchers": [
21+
{
22+
"keySelector": {
23+
"trafficType": "user",
24+
"attribute": null
25+
},
26+
"matcherType": "ALL_KEYS",
27+
"negate": false,
28+
"userDefinedSegmentMatcherData": null,
29+
"whitelistMatcherData": null,
30+
"unaryNumericMatcherData": null,
31+
"betweenMatcherData": null,
32+
"booleanMatcherData": null,
33+
"dependencyMatcherData": null,
34+
"stringMatcherData": null
35+
}
36+
]
37+
},
38+
"partitions": [
39+
{
40+
"treatment": "on",
41+
"size": 100
42+
},
43+
{
44+
"treatment": "off",
45+
"size": 0
46+
}
47+
],
48+
"label": "default rule"
49+
}
50+
]
51+
},
52+
{
53+
"trafficTypeName": "user",
54+
"name": "impression_toggle_on",
55+
"trafficAllocation": 24,
56+
"trafficAllocationSeed": -172559061,
57+
"seed": -906334215,
58+
"status": "ACTIVE",
59+
"killed": true,
60+
"defaultTreatment": "off",
61+
"changeNumber": 1585948717645,
62+
"algo": 2,
63+
"configurations": {},
64+
"conditions": [
65+
{
66+
"conditionType": "ROLLOUT",
67+
"matcherGroup": {
68+
"combiner": "AND",
69+
"matchers": [
70+
{
71+
"keySelector": {
72+
"trafficType": "user",
73+
"attribute": null
74+
},
75+
"matcherType": "ALL_KEYS",
76+
"negate": false,
77+
"userDefinedSegmentMatcherData": null,
78+
"whitelistMatcherData": null,
79+
"unaryNumericMatcherData": null,
80+
"betweenMatcherData": null,
81+
"booleanMatcherData": null,
82+
"dependencyMatcherData": null,
83+
"stringMatcherData": null
84+
}
85+
]
86+
},
87+
"partitions": [
88+
{
89+
"treatment": "on",
90+
"size": 100
91+
},
92+
{
93+
"treatment": "off",
94+
"size": 0
95+
}
96+
],
97+
"label": "default rule"
98+
}
99+
],
100+
"trackImpression": true
101+
},
102+
{
103+
"trafficTypeName": "user",
104+
"name": "impression_toggle_off",
105+
"trafficAllocation": 24,
106+
"trafficAllocationSeed": -172559061,
107+
"seed": -906334215,
108+
"status": "ACTIVE",
109+
"killed": true,
110+
"defaultTreatment": "off",
111+
"changeNumber": 1585948717645,
112+
"algo": 2,
113+
"configurations": {},
114+
"conditions": [
115+
{
116+
"conditionType": "ROLLOUT",
117+
"matcherGroup": {
118+
"combiner": "AND",
119+
"matchers": [
120+
{
121+
"keySelector": {
122+
"trafficType": "user",
123+
"attribute": null
124+
},
125+
"matcherType": "ALL_KEYS",
126+
"negate": false,
127+
"userDefinedSegmentMatcherData": null,
128+
"whitelistMatcherData": null,
129+
"unaryNumericMatcherData": null,
130+
"betweenMatcherData": null,
131+
"booleanMatcherData": null,
132+
"dependencyMatcherData": null,
133+
"stringMatcherData": null
134+
}
135+
]
136+
},
137+
"partitions": [
138+
{
139+
"treatment": "on",
140+
"size": 100
141+
},
142+
{
143+
"treatment": "off",
144+
"size": 0
145+
}
146+
],
147+
"label": "default rule"
148+
}
149+
],
150+
"trackImpression": false
151+
}
152+
],
153+
"since": -1,
154+
"till": 1585948850109
155+
}

0 commit comments

Comments
 (0)