4
4
import static cloud .eppo .helpers .AssignmentTestCase .runTestCase ;
5
5
import static cloud .eppo .helpers .BanditTestCase .parseBanditTestCaseFile ;
6
6
import static cloud .eppo .helpers .BanditTestCase .runBanditTestCase ;
7
+ import static cloud .eppo .helpers .TestUtils .mockHttpError ;
7
8
import static org .junit .jupiter .api .Assertions .*;
8
9
import static org .junit .jupiter .api .Assertions .assertThrows ;
9
10
import static org .mockito .Mockito .*;
23
24
import com .github .tomakehurst .wiremock .junit5 .WireMockExtension ;
24
25
import java .io .File ;
25
26
import java .lang .reflect .Field ;
27
+ import java .util .concurrent .CompletableFuture ;
26
28
import java .util .stream .Stream ;
27
29
import org .apache .commons .io .FileUtils ;
28
30
import org .junit .jupiter .api .AfterAll ;
@@ -238,6 +240,71 @@ public void testPolling() {
238
240
verify (httpClientSpy , times (2 )).get (anyString ());
239
241
}
240
242
243
+ @ Test
244
+ public void testGracefulInitializationFailure () {
245
+ // Set up bad HTTP response
246
+ mockHttpError ();
247
+
248
+ // Initialize and no exception should be thrown.
249
+ assertDoesNotThrow (() -> initFailingGracefulClient (true ));
250
+ }
251
+
252
+ @ Test
253
+ public void testClientMakesDefaultAssignmentsAfterFailingToInitialize () {
254
+ // Set up bad HTTP response
255
+ mockHttpError ();
256
+
257
+ // Initialize and no exception should be thrown.
258
+ try {
259
+ EppoClient eppoClient = initFailingGracefulClient (true );
260
+ assertEquals ("default" , eppoClient .getStringAssignment ("experiment1" , "subject1" , "default" ));
261
+ } catch (Exception e ) {
262
+ fail ("Unexpected exception: " + e );
263
+ }
264
+ }
265
+
266
+ @ Test
267
+ public void testClientMakesDefaultAssignmentsAfterFailingToInitializeNonGracefulMode () {
268
+ // Set up bad HTTP response
269
+ mockHttpError ();
270
+
271
+ // Initialize and the exception should be thrown.
272
+ try {
273
+ initFailingGracefulClient (false );
274
+ } catch (RuntimeException e ) {
275
+ // Expected
276
+ assertEquals ("Intentional Error" , e .getMessage ());
277
+ } finally {
278
+ assertEquals (
279
+ "default" ,
280
+ EppoClient .getInstance ().getStringAssignment ("experiment1" , "subject1" , "default" ));
281
+ }
282
+ }
283
+
284
+ @ Test
285
+ public void testNonGracefulInitializationFailure () {
286
+ // Set up bad HTTP response
287
+ mockHttpError ();
288
+
289
+ // Initialize and assert exception thrown
290
+ assertThrows (Exception .class , () -> initFailingGracefulClient (false ));
291
+ }
292
+
293
+ public static void mockHttpError () {
294
+ // Create a mock instance of EppoHttpClient
295
+ EppoHttpClient mockHttpClient = mock (EppoHttpClient .class );
296
+
297
+ // Mock sync get
298
+ when (mockHttpClient .get (anyString ())).thenThrow (new RuntimeException ("Intentional Error" ));
299
+
300
+ // Mock async get
301
+ CompletableFuture <byte []> mockAsyncResponse = new CompletableFuture <>();
302
+ when (mockHttpClient .getAsync (anyString ())).thenReturn (mockAsyncResponse );
303
+ mockAsyncResponse .completeExceptionally (new RuntimeException ("Intentional Error" ));
304
+
305
+ setBaseClientHttpClientOverrideField (mockHttpClient );
306
+ }
307
+
241
308
@ SuppressWarnings ("SameParameterValue" )
242
309
private void sleepUninterruptedly (long sleepMs ) {
243
310
try {
@@ -261,6 +328,20 @@ private EppoClient initClient(String apiKey) {
261
328
.buildAndInit ();
262
329
}
263
330
331
+ private EppoClient initFailingGracefulClient (boolean isGracefulMode ) {
332
+ mockAssignmentLogger = mock (AssignmentLogger .class );
333
+ mockBanditLogger = mock (BanditLogger .class );
334
+
335
+ return new EppoClient .Builder ()
336
+ .apiKey (DUMMY_FLAG_API_KEY )
337
+ .host ("blag" )
338
+ .assignmentLogger (mockAssignmentLogger )
339
+ .banditLogger (mockBanditLogger )
340
+ .isGracefulMode (isGracefulMode )
341
+ .forceReinitialize (true ) // Useful for tests
342
+ .buildAndInit ();
343
+ }
344
+
264
345
private void uninitClient () {
265
346
try {
266
347
Field httpClientOverrideField = EppoClient .class .getDeclaredField ("instance" );
@@ -281,4 +362,16 @@ private void initBuggyClient() {
281
362
throw new RuntimeException (e );
282
363
}
283
364
}
365
+
366
+ public static void setBaseClientHttpClientOverrideField (EppoHttpClient httpClient ) {
367
+ // Uses reflection to set a static override field used for tests (e.g., httpClientOverride)
368
+ try {
369
+ Field httpClientOverrideField = BaseEppoClient .class .getDeclaredField ("httpClientOverride" );
370
+ httpClientOverrideField .setAccessible (true );
371
+ httpClientOverrideField .set (null , httpClient );
372
+ httpClientOverrideField .setAccessible (false );
373
+ } catch (NoSuchFieldException | IllegalAccessException e ) {
374
+ throw new RuntimeException (e );
375
+ }
376
+ }
284
377
}
0 commit comments