23
23
import com .github .tomakehurst .wiremock .junit5 .WireMockExtension ;
24
24
import java .io .File ;
25
25
import java .lang .reflect .Field ;
26
+ import java .util .concurrent .CompletableFuture ;
26
27
import java .util .stream .Stream ;
27
28
import org .apache .commons .io .FileUtils ;
28
29
import org .junit .jupiter .api .AfterAll ;
@@ -238,6 +239,39 @@ public void testPolling() {
238
239
verify (httpClientSpy , times (2 )).get (anyString ());
239
240
}
240
241
242
+ // NOTE: Graceful mode during init is intrinsically true since the call is non-blocking and
243
+ // exceptions are caught without rethrowing in `FetchConfigurationsTask`
244
+
245
+ @ Test
246
+ public void testClientMakesDefaultAssignmentsAfterFailingToInitialize () {
247
+ // Set up bad HTTP response
248
+ mockHttpError ();
249
+
250
+ // Initialize and no exception should be thrown.
251
+ try {
252
+ EppoClient eppoClient = initFailingGracefulClient (true );
253
+ Thread .sleep (25 ); // Sleep to allow the async config fetch call to happen (and fail)
254
+ assertEquals ("default" , eppoClient .getStringAssignment ("experiment1" , "subject1" , "default" ));
255
+ } catch (Exception e ) {
256
+ fail ("Unexpected exception: " + e );
257
+ }
258
+ }
259
+
260
+ public static void mockHttpError () {
261
+ // Create a mock instance of EppoHttpClient
262
+ EppoHttpClient mockHttpClient = mock (EppoHttpClient .class );
263
+
264
+ // Mock sync get
265
+ when (mockHttpClient .get (anyString ())).thenThrow (new RuntimeException ("Intentional Error" ));
266
+
267
+ // Mock async get
268
+ CompletableFuture <byte []> mockAsyncResponse = new CompletableFuture <>();
269
+ when (mockHttpClient .getAsync (anyString ())).thenReturn (mockAsyncResponse );
270
+ mockAsyncResponse .completeExceptionally (new RuntimeException ("Intentional Error" ));
271
+
272
+ setBaseClientHttpClientOverrideField (mockHttpClient );
273
+ }
274
+
241
275
@ SuppressWarnings ("SameParameterValue" )
242
276
private void sleepUninterruptedly (long sleepMs ) {
243
277
try {
@@ -261,6 +295,20 @@ private EppoClient initClient(String apiKey) {
261
295
.buildAndInit ();
262
296
}
263
297
298
+ private EppoClient initFailingGracefulClient (boolean isGracefulMode ) {
299
+ mockAssignmentLogger = mock (AssignmentLogger .class );
300
+ mockBanditLogger = mock (BanditLogger .class );
301
+
302
+ return new EppoClient .Builder ()
303
+ .apiKey (DUMMY_FLAG_API_KEY )
304
+ .host ("blag" )
305
+ .assignmentLogger (mockAssignmentLogger )
306
+ .banditLogger (mockBanditLogger )
307
+ .isGracefulMode (isGracefulMode )
308
+ .forceReinitialize (true ) // Useful for tests
309
+ .buildAndInit ();
310
+ }
311
+
264
312
private void uninitClient () {
265
313
try {
266
314
Field httpClientOverrideField = EppoClient .class .getDeclaredField ("instance" );
@@ -281,4 +329,16 @@ private void initBuggyClient() {
281
329
throw new RuntimeException (e );
282
330
}
283
331
}
332
+
333
+ public static void setBaseClientHttpClientOverrideField (EppoHttpClient httpClient ) {
334
+ // Uses reflection to set a static override field used for tests (e.g., httpClientOverride)
335
+ try {
336
+ Field httpClientOverrideField = BaseEppoClient .class .getDeclaredField ("httpClientOverride" );
337
+ httpClientOverrideField .setAccessible (true );
338
+ httpClientOverrideField .set (null , httpClient );
339
+ httpClientOverrideField .setAccessible (false );
340
+ } catch (NoSuchFieldException | IllegalAccessException e ) {
341
+ throw new RuntimeException (e );
342
+ }
343
+ }
284
344
}
0 commit comments