@@ -331,9 +331,9 @@ impl Client {
331331 pub fn close ( & self ) {
332332 self . event_processor . close ( ) ;
333333
334- // If the system is in offline mode, no receiver will be listening to this broadcast
335- // channel, so sending on it would always result in an error.
336- if !self . offline {
334+ // If the system is in offline mode or daemon mode , no receiver will be listening to this
335+ // broadcast channel, so sending on it would always result in an error.
336+ if !self . offline && ! self . daemon_mode {
337337 if let Err ( e) = self . shutdown_broadcast . send ( ( ) ) {
338338 error ! ( "Failed to shutdown client appropriately: {}" , e) ;
339339 }
@@ -850,7 +850,8 @@ mod tests {
850850 use eval:: { ContextBuilder , MultiContextBuilder } ;
851851 use futures:: FutureExt ;
852852 use hyper:: client:: HttpConnector ;
853- use launchdarkly_server_sdk_evaluation:: Reason ;
853+ use launchdarkly_server_sdk_evaluation:: { Flag , Reason , Segment } ;
854+ use maplit:: hashmap;
854855 use std:: collections:: HashMap ;
855856 use tokio:: time:: Instant ;
856857
@@ -859,13 +860,19 @@ mod tests {
859860 use crate :: events:: create_event_sender;
860861 use crate :: events:: event:: { OutputEvent , VariationKey } ;
861862 use crate :: events:: processor_builders:: EventProcessorBuilder ;
863+ use crate :: stores:: persistent_store:: tests:: InMemoryPersistentDataStore ;
862864 use crate :: stores:: store_types:: { PatchTarget , StorageItem } ;
863865 use crate :: test_common:: {
864866 self , basic_flag, basic_flag_with_prereq, basic_flag_with_prereqs_and_visibility,
865867 basic_flag_with_visibility, basic_int_flag, basic_migration_flag, basic_off_flag,
866868 } ;
867- use crate :: { ConfigBuilder , MigratorBuilder , Operation , Origin } ;
869+ use crate :: {
870+ AllData , ConfigBuilder , MigratorBuilder , NullEventProcessorBuilder , Operation , Origin ,
871+ PersistentDataStore , PersistentDataStoreBuilder , PersistentDataStoreFactory ,
872+ SerializedItem ,
873+ } ;
868874 use test_case:: test_case;
875+
869876
870877 use super :: * ;
871878
@@ -1415,28 +1422,109 @@ mod tests {
14151422 assert_eq ! ( event_rx. iter( ) . count( ) , 0 ) ;
14161423 }
14171424
1425+ struct InMemoryPersistentDataStoreFactory {
1426+ data : AllData < Flag , Segment > ,
1427+ initialized : bool ,
1428+ }
1429+
1430+ impl PersistentDataStoreFactory for InMemoryPersistentDataStoreFactory {
1431+ fn create_persistent_data_store (
1432+ & self ,
1433+ ) -> Result < Box < ( dyn PersistentDataStore + ' static ) > , std:: io:: Error > {
1434+ let serialized_data =
1435+ AllData :: < SerializedItem , SerializedItem > :: try_from ( self . data . clone ( ) ) ?;
1436+ Ok ( Box :: new ( InMemoryPersistentDataStore {
1437+ data : serialized_data,
1438+ initialized : self . initialized ,
1439+ } ) )
1440+ }
1441+ }
1442+
14181443 #[ test]
14191444 fn variation_detail_handles_daemon_mode ( ) {
1420- let ( client, event_rx) = make_mocked_client_with_delay ( 1000 , false , true ) ;
1445+ testing_logger:: setup ( ) ;
1446+ let factory = InMemoryPersistentDataStoreFactory {
1447+ data : AllData {
1448+ flags : hashmap ! [ "flag" . into( ) => basic_flag( "flag" ) ] ,
1449+ segments : HashMap :: new ( ) ,
1450+ } ,
1451+ initialized : true ,
1452+ } ;
1453+ let builder = PersistentDataStoreBuilder :: new ( Arc :: new ( factory) ) ;
1454+
1455+ let config = ConfigBuilder :: new ( "sdk-key" )
1456+ . daemon_mode ( true )
1457+ . data_store ( & builder)
1458+ . event_processor ( & NullEventProcessorBuilder :: new ( ) )
1459+ . build ( )
1460+ . expect ( "config should build" ) ;
1461+
1462+ let client = Client :: build ( config) . expect ( "Should be built." ) ;
1463+
14211464 client. start_with_default_executor ( ) ;
14221465
14231466 let context = ContextBuilder :: new ( "bob" )
14241467 . build ( )
14251468 . expect ( "Failed to create context" ) ;
14261469
1427- let detail = client. variation_detail ( & context, "myFlag " , FlagValue :: Bool ( false ) ) ;
1470+ let detail = client. variation_detail ( & context, "flag " , FlagValue :: Bool ( false ) ) ;
14281471
1429- assert ! ( ! detail. value. unwrap( ) . as_bool( ) . unwrap( ) ) ;
1472+ assert ! ( detail. value. unwrap( ) . as_bool( ) . unwrap( ) ) ;
14301473 assert ! ( matches!(
14311474 detail. reason,
1432- Reason :: Error {
1433- error : eval :: Error :: FlagNotFound
1475+ Reason :: Fallthrough {
1476+ in_experiment : false
14341477 }
14351478 ) ) ;
14361479 client. flush ( ) ;
14371480 client. close ( ) ;
14381481
1439- assert_eq ! ( event_rx. iter( ) . count( ) , 2 ) ;
1482+ testing_logger:: validate ( |captured_logs| {
1483+ assert_eq ! ( captured_logs. len( ) , 1 ) ;
1484+ assert_eq ! (
1485+ captured_logs[ 0 ] . body,
1486+ "Started LaunchDarkly Client in daemon mode"
1487+ ) ;
1488+ } ) ;
1489+ }
1490+
1491+ #[ test]
1492+ fn daemon_mode_is_quiet_if_store_is_not_initialized ( ) {
1493+ testing_logger:: setup ( ) ;
1494+
1495+ let factory = InMemoryPersistentDataStoreFactory {
1496+ data : AllData {
1497+ flags : HashMap :: new ( ) ,
1498+ segments : HashMap :: new ( ) ,
1499+ } ,
1500+ initialized : false ,
1501+ } ;
1502+ let builder = PersistentDataStoreBuilder :: new ( Arc :: new ( factory) ) ;
1503+
1504+ let config = ConfigBuilder :: new ( "sdk-key" )
1505+ . daemon_mode ( true )
1506+ . data_store ( & builder)
1507+ . event_processor ( & NullEventProcessorBuilder :: new ( ) )
1508+ . build ( )
1509+ . expect ( "config should build" ) ;
1510+
1511+ let client = Client :: build ( config) . expect ( "Should be built." ) ;
1512+
1513+ client. start_with_default_executor ( ) ;
1514+
1515+ let context = ContextBuilder :: new ( "bob" )
1516+ . build ( )
1517+ . expect ( "Failed to create context" ) ;
1518+
1519+ client. variation_detail ( & context, "flag" , FlagValue :: Bool ( false ) ) ;
1520+
1521+ testing_logger:: validate ( |captured_logs| {
1522+ assert_eq ! ( captured_logs. len( ) , 1 ) ;
1523+ assert_eq ! (
1524+ captured_logs[ 0 ] . body,
1525+ "Started LaunchDarkly Client in daemon mode"
1526+ ) ;
1527+ } ) ;
14401528 }
14411529
14421530 #[ test]
0 commit comments