1515package com .optimizely .ab .android .sdk .cmab ;
1616
1717import com .optimizely .ab .android .shared .Client ;
18+ import com .optimizely .ab .cmab .client .CmabFetchException ;
19+ import com .optimizely .ab .cmab .client .CmabInvalidResponseException ;
1820import org .junit .Before ;
1921import org .junit .Test ;
2022import org .junit .runner .RunWith ;
@@ -88,15 +90,20 @@ public void testConstructorWithContextAndHelper() {
8890 }
8991
9092 @ Test
91- public void testFetchDecisionSuccess () throws Exception {
93+ public void testFetchDecisionSuccess () {
9294 HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
9395 ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
9496
95- String mockResponseJson = "{\" variation_id\" :\" variation_1\" ,\" status\" :\" success\" }" ;
96- when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
97- when (mockUrlConnection .getResponseCode ()).thenReturn (200 );
98- when (mockClient .readStream (mockUrlConnection )).thenReturn (mockResponseJson );
99- when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
97+ try {
98+ String mockResponseJson = "{\" variation_id\" :\" variation_1\" ,\" status\" :\" success\" }" ;
99+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
100+ when (mockUrlConnection .getResponseCode ()).thenReturn (200 );
101+ when (mockClient .readStream (mockUrlConnection )).thenReturn (mockResponseJson );
102+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
103+ } catch (IOException e ) {
104+ // Never happens with mocked connection
105+ }
106+
100107 when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
101108 Client .Request <String > request = invocation .getArgument (0 );
102109 return request .execute ();
@@ -120,13 +127,18 @@ public void testFetchDecisionSuccess() throws Exception {
120127
121128 verify (mockUrlConnection ).setConnectTimeout (10 *1000 );
122129 verify (mockUrlConnection ).setReadTimeout (60 *1000 );
123- verify (mockUrlConnection ).setRequestMethod ("POST" );
130+ try {
131+ verify (mockUrlConnection ).setRequestMethod ("POST" );
132+ } catch (Exception e ) {
133+ // Never happens - verify() doesn't actually invoke the method
134+ }
124135 verify (mockUrlConnection ).setRequestProperty ("content-type" , "application/json" );
125136 verify (mockUrlConnection ).setDoOutput (true );
126137 }
127138
128- @ Test
129- public void testFetchDecisionConnectionFailure () throws Exception {
139+ @ Test (expected = CmabFetchException .class )
140+ public void testFetchDecisionConnectionFailure () {
141+ // When openConnection returns null, should throw CmabFetchException
130142 when (mockClient .openConnection (any (URL .class ))).thenReturn (null );
131143 when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
132144 Client .Request <String > request = invocation .getArgument (0 );
@@ -135,20 +147,164 @@ public void testFetchDecisionConnectionFailure() throws Exception {
135147
136148 mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
137149
138- String result = mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
139- assertNull (result );
150+ // Should throw CmabFetchException when connection fails to open
151+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
152+ }
153+
154+ @ Test (expected = CmabFetchException .class )
155+ public void testFetchDecisionThrowsExceptionOn500Error () {
156+ HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
157+ ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
158+
159+ try {
160+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
161+ when (mockUrlConnection .getResponseCode ()).thenReturn (500 );
162+ when (mockUrlConnection .getResponseMessage ()).thenReturn ("Internal Server Error" );
163+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
164+ } catch (IOException e ) {
165+ // Never happens with mocked connection
166+ }
167+
168+ when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
169+ Client .Request <String > request = invocation .getArgument (0 );
170+ return request .execute ();
171+ });
172+
173+ doReturn ("{\" user_id\" :\" test-user-456\" }" )
174+ .when (mockCmabClientHelper )
175+ .buildRequestJson (any (), any (), any (), any ());
176+
177+ mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
178+
179+ // Should throw CmabFetchException for 500 error
180+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
181+ }
182+
183+ @ Test (expected = CmabFetchException .class )
184+ public void testFetchDecisionThrowsExceptionOn400Error () {
185+ HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
186+ ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
187+
188+ try {
189+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
190+ when (mockUrlConnection .getResponseCode ()).thenReturn (400 );
191+ when (mockUrlConnection .getResponseMessage ()).thenReturn ("Bad Request" );
192+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
193+ } catch (IOException e ) {
194+ // Never happens with mocked connection
195+ }
196+
197+ when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
198+ Client .Request <String > request = invocation .getArgument (0 );
199+ return request .execute ();
200+ });
201+
202+ doReturn ("{\" user_id\" :\" test-user-456\" }" )
203+ .when (mockCmabClientHelper )
204+ .buildRequestJson (any (), any (), any (), any ());
205+
206+ mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
207+
208+ // Should throw CmabFetchException for 400 error
209+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
210+ }
211+
212+ @ Test (expected = CmabFetchException .class )
213+ public void testFetchDecisionThrowsExceptionOnNetworkError () {
214+ HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
215+ ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
216+
217+ try {
218+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
219+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
220+ doThrow (new IOException ("Network error" )).when (mockUrlConnection ).getResponseCode ();
221+ } catch (IOException e ) {
222+ // Never happens with mocked connection
223+ }
224+
225+ when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
226+ Client .Request <String > request = invocation .getArgument (0 );
227+ return request .execute ();
228+ });
229+
230+ doReturn ("{\" user_id\" :\" test-user-456\" }" )
231+ .when (mockCmabClientHelper )
232+ .buildRequestJson (any (), any (), any (), any ());
233+
234+ mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
235+
236+ // Should throw CmabFetchException when network IOException occurs
237+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
238+ }
239+
240+ @ Test (expected = CmabInvalidResponseException .class )
241+ public void testFetchDecisionThrowsExceptionOnInvalidJson () {
242+ HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
243+ ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
244+
245+ try {
246+ String invalidResponseJson = "{\" invalid\" :\" response\" }" ;
247+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
248+ when (mockUrlConnection .getResponseCode ()).thenReturn (200 );
249+ when (mockClient .readStream (mockUrlConnection )).thenReturn (invalidResponseJson );
250+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
251+ } catch (IOException e ) {
252+ // Never happens with mocked connection
253+ }
254+
255+ when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
256+ Client .Request <String > request = invocation .getArgument (0 );
257+ return request .execute ();
258+ });
259+
260+ doReturn ("{\" user_id\" :\" test-user-456\" }" )
261+ .when (mockCmabClientHelper )
262+ .buildRequestJson (any (), any (), any (), any ());
263+ doReturn (false ) // Invalid response
264+ .when (mockCmabClientHelper )
265+ .validateResponse (any ());
266+
267+ mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
268+
269+ // Should throw CmabInvalidResponseException when response validation fails
270+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
140271 }
141272
142273 @ Test
143- public void testRetryOnFailureWithRetryBackoff () throws Exception {
144- when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenReturn (null );
274+ public void testRetryConfigurationPassedToClient () {
275+ HttpURLConnection mockUrlConnection = mock (HttpURLConnection .class );
276+ ByteArrayOutputStream mockOutputStream = mock (ByteArrayOutputStream .class );
277+
278+ try {
279+ String mockResponseJson = "{\" variation_id\" :\" variation_1\" ,\" status\" :\" success\" }" ;
280+ when (mockClient .openConnection (any (URL .class ))).thenReturn (mockUrlConnection );
281+ when (mockUrlConnection .getResponseCode ()).thenReturn (200 );
282+ when (mockClient .readStream (mockUrlConnection )).thenReturn (mockResponseJson );
283+ when (mockUrlConnection .getOutputStream ()).thenReturn (mockOutputStream );
284+ } catch (IOException e ) {
285+ // Never happens with mocked connection
286+ }
287+
288+ when (mockClient .execute (any (Client .Request .class ), anyInt (), anyInt ())).thenAnswer (invocation -> {
289+ Client .Request <String > request = invocation .getArgument (0 );
290+ return request .execute ();
291+ });
292+
293+ doReturn ("{\" user_id\" :\" test-user-456\" }" )
294+ .when (mockCmabClientHelper )
295+ .buildRequestJson (any (), any (), any (), any ());
296+ doReturn (true )
297+ .when (mockCmabClientHelper )
298+ .validateResponse (any ());
299+ doReturn ("variation_1" )
300+ .when (mockCmabClientHelper )
301+ .parseVariationId (any ());
145302
146303 mockCmabClient = new DefaultCmabClient (mockClient , mockCmabClientHelper );
147304
148- String result = mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
149- assertNull (result );
305+ mockCmabClient .fetchDecision (testRuleId , testUserId , testAttributes , testCmabUuid );
150306
151- // Verify the retry configuration matches our constants
307+ // Verify the retry configuration is passed to client.execute()
152308 verify (mockClient ).execute (any (Client .Request .class ), eq (DefaultCmabClient .REQUEST_BACKOFF_TIMEOUT ), eq (DefaultCmabClient .REQUEST_RETRIES_POWER ));
153309 assertEquals ("REQUEST_BACKOFF_TIMEOUT should be 1" , 1 , DefaultCmabClient .REQUEST_BACKOFF_TIMEOUT );
154310 assertEquals ("REQUEST_RETRIES_POWER should be 1" , 1 , DefaultCmabClient .REQUEST_RETRIES_POWER );
0 commit comments