25
25
import com .github .tomakehurst .wiremock .junit5 .WireMockExtension ;
26
26
import java .io .File ;
27
27
import java .lang .reflect .Field ;
28
+ import java .util .ArrayList ;
29
+ import java .util .List ;
28
30
import java .util .concurrent .CompletableFuture ;
31
+ import java .util .concurrent .ExecutionException ;
29
32
import java .util .stream .Stream ;
30
33
import org .apache .commons .io .FileUtils ;
31
34
import org .junit .jupiter .api .AfterAll ;
@@ -51,6 +54,8 @@ public class EppoClientTest {
51
54
private AssignmentLogger mockAssignmentLogger ;
52
55
private BanditLogger mockBanditLogger ;
53
56
57
+ private static final byte [] EMPTY_CONFIG = "{\" flags\" :{}}" .getBytes ();
58
+
54
59
@ BeforeAll
55
60
public static void initMockServer () {
56
61
mockServer = new WireMockServer (TEST_PORT );
@@ -271,6 +276,47 @@ public void testGetConfiguration() {
271
276
assertEquals (VariationType .NUMERIC , configuration .getFlagType ("numeric_flag" ));
272
277
}
273
278
279
+ @ Test
280
+ public void testConfigurationChangeListener () throws ExecutionException , InterruptedException {
281
+ List <Configuration > received = new ArrayList <>();
282
+
283
+ // Set up a changing response from the "server"
284
+ EppoHttpClient mockHttpClient = mock (EppoHttpClient .class );
285
+
286
+ // Mock sync get to return empty
287
+ when (mockHttpClient .get (anyString ())).thenReturn (EMPTY_CONFIG );
288
+
289
+ // Mock async get to return empty
290
+ when (mockHttpClient .get (anyString ())).thenReturn (EMPTY_CONFIG );
291
+
292
+ setBaseClientHttpClientOverrideField (mockHttpClient );
293
+
294
+ EppoClient .Builder clientBuilder =
295
+ EppoClient .builder (DUMMY_FLAG_API_KEY )
296
+ .forceReinitialize (true )
297
+ .onConfigurationChange (received ::add )
298
+ .isGracefulMode (false );
299
+
300
+ // Initialize and no exception should be thrown.
301
+ EppoClient eppoClient = clientBuilder .buildAndInit ();
302
+
303
+ verify (mockHttpClient , times (1 )).get (anyString ());
304
+ assertEquals (1 , received .size ());
305
+
306
+ // Now, return the boolean flag config so that the config has changed.
307
+ when (mockHttpClient .get (anyString ())).thenReturn (BOOL_FLAG_CONFIG );
308
+
309
+ // Trigger a reload of the client
310
+ eppoClient .loadConfiguration ();
311
+
312
+ assertEquals (2 , received .size ());
313
+
314
+ // Reload the client again; the config hasn't changed, but Java doesn't check eTag (yet)
315
+ eppoClient .loadConfiguration ();
316
+
317
+ assertEquals (3 , received .size ());
318
+ }
319
+
274
320
public static void mockHttpError () {
275
321
// Create a mock instance of EppoHttpClient
276
322
EppoHttpClient mockHttpClient = mock (EppoHttpClient .class );
@@ -353,4 +399,40 @@ public static void setBaseClientHttpClientOverrideField(EppoHttpClient httpClien
353
399
throw new RuntimeException (e );
354
400
}
355
401
}
402
+
403
+ private static final byte [] BOOL_FLAG_CONFIG =
404
+ ("{\n "
405
+ + " \" createdAt\" : \" 2024-04-17T19:40:53.716Z\" ,\n "
406
+ + " \" format\" : \" SERVER\" ,\n "
407
+ + " \" environment\" : {\n "
408
+ + " \" name\" : \" Test\" \n "
409
+ + " },\n "
410
+ + " \" flags\" : {\n "
411
+ + " \" 9a2025738dde19ff44cd30b9d2967000\" : {\n "
412
+ + " \" key\" : \" 9a2025738dde19ff44cd30b9d2967000\" ,\n "
413
+ + " \" enabled\" : true,\n "
414
+ + " \" variationType\" : \" BOOLEAN\" ,\n "
415
+ + " \" variations\" : {\n "
416
+ + " \" b24=\" : {\n "
417
+ + " \" key\" : \" b24=\" ,\n "
418
+ + " \" value\" : \" dHJ1ZQ==\" \n "
419
+ + " }\n "
420
+ + " },\n "
421
+ + " \" allocations\" : [\n "
422
+ + " {\n "
423
+ + " \" key\" : \" b24=\" ,\n "
424
+ + " \" doLog\" : true,\n "
425
+ + " \" splits\" : [\n "
426
+ + " {\n "
427
+ + " \" variationKey\" : \" b24=\" ,\n "
428
+ + " \" shards\" : []\n "
429
+ + " }\n "
430
+ + " ]\n "
431
+ + " }\n "
432
+ + " ],\n "
433
+ + " \" totalShards\" : 10000\n "
434
+ + " }\n "
435
+ + " }\n "
436
+ + "}" )
437
+ .getBytes ();
356
438
}
0 commit comments