17
17
package com .optimizely .ab .android .sdk ;
18
18
19
19
import android .content .Context ;
20
- import android .content .Intent ;
21
- import android .content .pm .PackageInfo ;
22
- import android .content .pm .PackageManager ;
23
- import android .os .Build ;
24
20
25
21
import androidx .test .ext .junit .runners .AndroidJUnit4 ;
26
- import androidx .test .filters .SdkSuppress ;
27
22
import androidx .test .platform .app .InstrumentationRegistry ;
28
23
29
- import com .optimizely .ab .Optimizely ;
30
- import com .optimizely .ab .android .datafile_handler .DatafileHandler ;
31
- import com .optimizely .ab .android .datafile_handler .DatafileLoadedListener ;
32
- import com .optimizely .ab .android .datafile_handler .DefaultDatafileHandler ;
33
- import com .optimizely .ab .android .event_handler .DefaultEventHandler ;
34
- import com .optimizely .ab .android .shared .DatafileConfig ;
35
- import com .optimizely .ab .android .user_profile .DefaultUserProfileService ;
36
- import com .optimizely .ab .bucketing .UserProfileService ;
37
- import com .optimizely .ab .config .DatafileProjectConfig ;
38
- import com .optimizely .ab .config .ProjectConfig ;
39
- import com .optimizely .ab .config .Variation ;
40
- import com .optimizely .ab .config .parser .ConfigParseException ;
41
- import com .optimizely .ab .event .EventHandler ;
42
- import com .optimizely .ab .event .EventProcessor ;
43
- import com .optimizely .ab .event .internal .UserEvent ;
44
- import com .optimizely .ab .notification .NotificationCenter ;
45
- import com .optimizely .ab .notification .UpdateConfigNotification ;
24
+ import com .google .gson .JsonArray ;
25
+ import com .google .gson .JsonObject ;
26
+ import com .google .gson .JsonParser ;
27
+ import com .optimizely .ab .OptimizelyUserContext ;
28
+ import com .optimizely .ab .android .odp .DefaultODPApiManager ;
29
+ import com .optimizely .ab .odp .ODPApiManager ;
46
30
import com .optimizely .ab .odp .ODPEventManager ;
47
31
import com .optimizely .ab .odp .ODPManager ;
32
+ import com .optimizely .ab .odp .ODPSegmentManager ;
48
33
49
34
import org .junit .Before ;
50
35
import org .junit .Test ;
51
36
import org .junit .runner .RunWith ;
52
37
import org .mockito .ArgumentCaptor ;
53
- import org .mockito .invocation .InvocationOnMock ;
54
- import org .mockito .stubbing .Answer ;
55
- import org .slf4j .Logger ;
56
-
57
- import java .util .Collections ;
58
- import java .util .Set ;
59
- import java .util .concurrent .CountDownLatch ;
60
- import java .util .concurrent .ExecutorService ;
61
- import java .util .concurrent .Executors ;
62
- import java .util .concurrent .TimeUnit ;
63
38
64
39
import static junit .framework .Assert .assertEquals ;
65
40
import static junit .framework .Assert .assertFalse ;
70
45
import static org .mockito .Matchers .any ;
71
46
import static org .mockito .Matchers .anyString ;
72
47
import static org .mockito .Matchers .eq ;
73
- import static org .mockito .Mockito .doAnswer ;
74
48
import static org .mockito .Mockito .mock ;
75
49
import static org .mockito .Mockito .spy ;
76
50
import static org .mockito .Mockito .times ;
77
51
import static org .mockito .Mockito .verify ;
78
52
import static org .mockito .Mockito .when ;
79
53
54
+ import java .util .Arrays ;
55
+ import java .util .HashSet ;
56
+ import java .util .Set ;
57
+
80
58
/**
81
59
* Tests for Optimizely ODP Integration
82
60
*/
83
61
@ RunWith (AndroidJUnit4 .class )
84
62
public class ODPIntegrationTest {
85
63
86
64
private OptimizelyManager optimizelyManager ;
65
+ private OptimizelyClient optimizelyClient ;
87
66
private ODPManager odpManager ;
88
- private DefaultDatafileHandler datafileHandler ;
89
- private NotificationCenter notificationCenter ;
67
+ private ODPEventManager odpEventManager ;
68
+ private ODPSegmentManager odpSegmentManager ;
69
+ private ODPApiManager odpApiManager ;
90
70
private Context context = InstrumentationRegistry .getInstrumentation ().getTargetContext ();
91
71
private String testSdkKey = "12345" ;
72
+ private String testUser = "test-user" ;
73
+ private String testVuid = "vuid_123" ; // must start with "vuid_" to be parsed properly in java-sdk core
92
74
93
- private String emptyV4Core =
75
+ private String odpDatafile = "{" +
94
76
"\" version\" : \" 4\" ," +
95
77
"\" rollouts\" : []," +
96
78
"\" anonymizeIP\" : true," +
@@ -103,89 +85,126 @@ public class ODPIntegrationTest {
103
85
"\" attributes\" : []," +
104
86
"\" accountId\" : \" 10367498574\" ," +
105
87
"\" events\" : []," +
106
- "\" revision\" : \" 100\" ," ;
107
-
108
- String integration1 = "\" integrations\" :[{\" key\" :\" odp\" ,\" host\" :\" h-1\" ,\" publicKey\" :\" p-1\" }]" ;
109
- String integration2 = "\" integrations\" :[{\" key\" :\" odp\" ,\" host\" :\" h-2\" ,\" publicKey\" :\" p-2\" }]" ;
110
- String odpDatafile1 = "{" + emptyV4Core + integration1 + "}" ;
111
- String odpDatafile2 = "{" + emptyV4Core + integration2 + "}" ;
88
+ "\" revision\" : \" 100\" ," +
89
+ "\" typedAudiences\" :[{\" id\" : \" 12\" ,\" conditions\" : [\" or\" ,{\" value\" : \" segment-1\" ,\" type\" : \" third_party_dimension\" ,\" name\" : \" odp.audiences\" ,\" match\" : \" qualified\" }],\" name\" : \" audience-1\" }]," +
90
+ "\" integrations\" :[{\" key\" :\" odp\" ,\" host\" :\" h-1\" ,\" publicKey\" :\" p-1\" }]" +
91
+ "}" ;
112
92
113
93
@ Before
114
94
public void setup () throws Exception {
115
- odpManager = mock (ODPManager .class );
116
- when (odpManager .getEventManager ()).thenReturn (mock (ODPEventManager .class ));
117
-
118
- datafileHandler = new DefaultDatafileHandler ();
119
- notificationCenter = new NotificationCenter ();
120
-
121
- optimizelyManager = new OptimizelyManager (
122
- null ,
123
- testSdkKey ,
124
- null ,
125
- mock (Logger .class ),
126
- 3600L ,
127
- datafileHandler ,
128
- null ,
129
- 3600L ,
130
- mock (DefaultEventHandler .class ),
131
- mock (EventProcessor .class ),
132
- null ,
133
- notificationCenter ,
134
- null ,
135
- odpManager ,
136
- null );
95
+ odpApiManager = mock (DefaultODPApiManager .class );
96
+ when (odpApiManager .sendEvents (anyString (), anyString (), anyString ())).thenReturn (200 ); // return success, otherwise retried 3 times.
97
+
98
+ odpEventManager = new ODPEventManager (odpApiManager );
99
+ odpSegmentManager = new ODPSegmentManager (odpApiManager );
100
+
101
+ optimizelyManager = OptimizelyManager .builder ()
102
+ .withSDKKey (testSdkKey )
103
+ .withVuid (testVuid )
104
+ .withODPEventManager (odpEventManager )
105
+ .withODPSegmentManager (odpSegmentManager )
106
+ .build (context );
107
+
108
+ optimizelyManager .initialize (context , odpDatafile );
109
+ optimizelyClient = optimizelyManager .getOptimizely ();
137
110
}
138
111
139
112
@ Test
140
- public void initializeSynchronous_updateODPConfig () {
141
- // NOTE: odpConfig is updated when Optimizely.java (java-sdk core) is initialized.
142
- // Same for async-initialization, so need to repeat the same test (hard to test for async-init).
113
+ public void identifyOdpEventSentWhenUserContextCreated () throws InterruptedException {
114
+ optimizelyClient .createUserContext (testUser );
143
115
144
- optimizelyManager .initialize (context , odpDatafile1 );
145
- verify (odpManager , times (1 )).updateSettings (
146
- eq ("h-1" ),
147
- eq ("p-1" ),
148
- eq (Collections .emptySet ()));
116
+ Thread .sleep (2000 ); // wait for batch timeout (1sec)
117
+
118
+ ArgumentCaptor <String > captor = ArgumentCaptor .forClass (String .class );
119
+ verify (odpApiManager , times (1 )).sendEvents (eq ("p-1" ), eq ("h-1/v3/events" ), captor .capture ());
120
+ String eventStr = captor .getValue ();
121
+
122
+ // 2 events (client_initialized, identified) will be batched in a single sendEvents() call.
123
+ JsonArray jsonArray = JsonParser .parseString (eventStr ).getAsJsonArray ();
124
+ assertEquals (jsonArray .size (), 2 );
125
+
126
+ // "client_initialized" event (vuid only)
127
+ JsonObject firstEvt = jsonArray .get (0 ).getAsJsonObject ();
128
+ JsonObject firstIdentifiers = firstEvt .get ("identifiers" ).getAsJsonObject ();
129
+ JsonObject firstData = firstEvt .get ("data" ).getAsJsonObject ();
130
+
131
+ // "identified" event (vuid + fs_user_id)
132
+ JsonObject secondEvt = jsonArray .get (1 ).getAsJsonObject ();
133
+ JsonObject secondIdentifiers = secondEvt .get ("identifiers" ).getAsJsonObject ();
149
134
150
- // validate no other calls
135
+ assertEquals (firstEvt .get ("action" ).getAsString (), "client_initialized" );
136
+ assertEquals (firstIdentifiers .size (), 1 );
137
+ assertEquals (firstIdentifiers .get ("vuid" ).getAsString (), testVuid );
151
138
152
- verify (odpManager , times (1 )).updateSettings (
153
- anyString (),
154
- anyString (),
155
- any (Set .class ));
139
+ assertEquals (secondEvt .get ("action" ).getAsString (), "identified" );
140
+ assertEquals (secondIdentifiers .size (), 2 );
141
+ assertEquals (secondIdentifiers .get ("vuid" ).getAsString (), testVuid );
142
+ assertEquals (secondIdentifiers .get ("fs_user_id" ).getAsString (), testUser );
143
+
144
+ // validate that ODP event data includes correct values.
145
+ assertEquals (firstData .size (), 8 ); // {idempotence_id, os, os_version, data_source_type, data_source_version, device_type, model, data_source}
146
+ assertEquals (firstData .get ("data_source" ).getAsString (), "android-sdk" );
156
147
}
157
148
158
149
@ Test
159
- public void updateODPConfigWhenDatafileUpdatedByBackgroundPolling () throws InterruptedException {
160
- // NOTE: same logic for async-initialization, so no need to repeat for async
150
+ public void identifyOdpEventSentWhenVuidUserContextCreated () throws InterruptedException {
151
+ optimizelyClient . createUserContext (); // empty userId. vuid will be used.
161
152
162
- boolean updateConfigOnBackgroundDatafile = true ;
163
- optimizelyManager .initialize (context , odpDatafile1 , true , updateConfigOnBackgroundDatafile );
153
+ Thread .sleep (2000 ); // wait for batch timeout (1sec)
164
154
165
- // datafile will be saved when a new datafile is downloaded by background polling
166
- datafileHandler . saveDatafile ( context , new DatafileConfig ( null , testSdkKey , null ), odpDatafile2 );
167
- Thread . sleep ( 1000 ); // need a delay for file-observer (update notification)
155
+ ArgumentCaptor < String > captor = ArgumentCaptor . forClass ( String . class );
156
+ verify ( odpApiManager , times ( 1 )). sendEvents ( eq ( "p-1" ), eq ( "h-1/v3/events" ), captor . capture () );
157
+ String eventStr = captor . getValue ();
168
158
169
- // odpConfig updated on initialization
159
+ // 2 events (client_initialized, identified) will be batched in a single sendEvents() call.
160
+ JsonArray jsonArray = JsonParser .parseString (eventStr ).getAsJsonArray ();
161
+ assertEquals (jsonArray .size (), 2 );
170
162
171
- verify (odpManager , times (1 )).updateSettings (
172
- eq ("h-1" ),
173
- eq ("p-1" ),
174
- eq (Collections .emptySet ()));
163
+ // "client_initialized" event (vuid only)
164
+ JsonObject firstEvt = jsonArray .get (0 ).getAsJsonObject ();
165
+ JsonObject firstIdentifiers = firstEvt .get ("identifiers" ).getAsJsonObject ();
175
166
176
- // odpConfig updated on background polling
167
+ // "identified" event (vuid only)
168
+ JsonObject secondEvt = jsonArray .get (1 ).getAsJsonObject ();
169
+ JsonObject secondIdentifiers = secondEvt .get ("identifiers" ).getAsJsonObject ();
177
170
178
- verify (odpManager , times (1 )).updateSettings (
179
- eq ("h-2" ),
180
- eq ("p-2" ),
181
- eq (Collections .emptySet ()));
171
+ assertEquals (firstEvt .get ("action" ).getAsString (), "client_initialized" );
172
+ assertEquals (firstIdentifiers .size (), 1 );
173
+ assertEquals (firstIdentifiers .get ("vuid" ).getAsString (), testVuid );
182
174
183
- // no other calls
175
+ assertEquals (secondEvt .get ("action" ).getAsString (), "identified" );
176
+ assertEquals (secondIdentifiers .size (), 1 );
177
+ assertEquals (secondIdentifiers .get ("vuid" ).getAsString (), testVuid );
178
+ }
184
179
185
- verify (odpManager , times (2 )).updateSettings (
186
- anyString (),
187
- anyString (),
188
- any (Set .class ));
180
+ @ Test
181
+ public void fetchQualifiedSegmentsWithUserContext () throws InterruptedException {
182
+ OptimizelyUserContext user = optimizelyClient .createUserContext (testUser );
183
+
184
+ Boolean status = user .fetchQualifiedSegments ();
185
+
186
+ verify (odpApiManager , times (1 )).fetchQualifiedSegments (
187
+ eq ("p-1" ),
188
+ eq ("h-1/v3/graphql" ),
189
+ eq ("fs_user_id" ),
190
+ eq (testUser ),
191
+ eq (new HashSet <>(Arrays .asList ("segment-1" )))
192
+ );
193
+ }
194
+
195
+ @ Test
196
+ public void fetchQualifiedSegmentsWithVuidUserContext () throws InterruptedException {
197
+ OptimizelyUserContext user = optimizelyClient .createUserContext (); // empty userId. vuid will be used.
198
+
199
+ Boolean status = user .fetchQualifiedSegments ();
200
+
201
+ verify (odpApiManager , times (1 )).fetchQualifiedSegments (
202
+ eq ("p-1" ),
203
+ eq ("h-1/v3/graphql" ),
204
+ eq ("vuid" ),
205
+ eq (testVuid ),
206
+ eq (new HashSet <>(Arrays .asList ("segment-1" )))
207
+ );
189
208
}
190
209
191
210
}
0 commit comments