1717package com .optimizely .ab .android .sdk ;
1818
1919import 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 ;
2420
2521import androidx .test .ext .junit .runners .AndroidJUnit4 ;
26- import androidx .test .filters .SdkSuppress ;
2722import androidx .test .platform .app .InstrumentationRegistry ;
2823
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 ;
4630import com .optimizely .ab .odp .ODPEventManager ;
4731import com .optimizely .ab .odp .ODPManager ;
32+ import com .optimizely .ab .odp .ODPSegmentManager ;
4833
4934import org .junit .Before ;
5035import org .junit .Test ;
5136import org .junit .runner .RunWith ;
5237import 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 ;
6338
6439import static junit .framework .Assert .assertEquals ;
6540import static junit .framework .Assert .assertFalse ;
7045import static org .mockito .Matchers .any ;
7146import static org .mockito .Matchers .anyString ;
7247import static org .mockito .Matchers .eq ;
73- import static org .mockito .Mockito .doAnswer ;
7448import static org .mockito .Mockito .mock ;
7549import static org .mockito .Mockito .spy ;
7650import static org .mockito .Mockito .times ;
7751import static org .mockito .Mockito .verify ;
7852import static org .mockito .Mockito .when ;
7953
54+ import java .util .Arrays ;
55+ import java .util .HashSet ;
56+ import java .util .Set ;
57+
8058/**
8159 * Tests for Optimizely ODP Integration
8260 */
8361@ RunWith (AndroidJUnit4 .class )
8462public class ODPIntegrationTest {
8563
8664 private OptimizelyManager optimizelyManager ;
65+ private OptimizelyClient optimizelyClient ;
8766 private ODPManager odpManager ;
88- private DefaultDatafileHandler datafileHandler ;
89- private NotificationCenter notificationCenter ;
67+ private ODPEventManager odpEventManager ;
68+ private ODPSegmentManager odpSegmentManager ;
69+ private ODPApiManager odpApiManager ;
9070 private Context context = InstrumentationRegistry .getInstrumentation ().getTargetContext ();
9171 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
9274
93- private String emptyV4Core =
75+ private String odpDatafile = "{" +
9476 "\" version\" : \" 4\" ," +
9577 "\" rollouts\" : []," +
9678 "\" anonymizeIP\" : true," +
@@ -103,89 +85,126 @@ public class ODPIntegrationTest {
10385 "\" attributes\" : []," +
10486 "\" accountId\" : \" 10367498574\" ," +
10587 "\" 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+ "}" ;
11292
11393 @ Before
11494 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 ();
137110 }
138111
139112 @ 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 );
143115
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 ();
149134
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 );
151138
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" );
156147 }
157148
158149 @ 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.
161152
162- boolean updateConfigOnBackgroundDatafile = true ;
163- optimizelyManager .initialize (context , odpDatafile1 , true , updateConfigOnBackgroundDatafile );
153+ Thread .sleep (2000 ); // wait for batch timeout (1sec)
164154
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 ();
168158
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 );
170162
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 ();
175166
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 ();
177170
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 );
182174
183- // no other calls
175+ assertEquals (secondEvt .get ("action" ).getAsString (), "identified" );
176+ assertEquals (secondIdentifiers .size (), 1 );
177+ assertEquals (secondIdentifiers .get ("vuid" ).getAsString (), testVuid );
178+ }
184179
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+ );
189208 }
190209
191210}
0 commit comments