@@ -916,6 +916,139 @@ public async Task OneInitializerNoSynchronizerIsWellBehaved()
916916 dataSource . Dispose ( ) ;
917917 }
918918
919+ [ Fact ]
920+ public async Task OneInitializerOneSynchronizerIsWellBehaved ( )
921+ {
922+ // Create a capturing sink to observe all updates
923+ var capturingSink = new CapturingDataSourceUpdatesWithHeaders ( ) ;
924+
925+ // Create dummy data for initializer and synchronizer
926+ var initializerDummyData = new FullDataSet < ItemDescriptor > ( new Dictionary < DataKind , KeyedItems < ItemDescriptor > > ( ) ) ;
927+ var synchronizerDummyData = new FullDataSet < ItemDescriptor > ( new Dictionary < DataKind , KeyedItems < ItemDescriptor > > ( ) ) ;
928+
929+ // Track whether the initializer factory was invoked
930+ bool initializerFactoryInvoked = false ;
931+ IDataSourceUpdatesV2 initializerUpdateSink = null ;
932+
933+ // Create initializer factory: emits Initializing, calls init with dummy data, then reports Valid
934+ SourceFactory initializerFactory = ( updatesSink ) =>
935+ {
936+ initializerFactoryInvoked = true ;
937+ initializerUpdateSink = updatesSink ;
938+ var source = new MockDataSourceWithInit (
939+ async ( ) =>
940+ {
941+ // Emit Initializing
942+ updatesSink . UpdateStatus ( DataSourceState . Initializing , null ) ;
943+ await Task . Delay ( 10 ) ;
944+
945+ // Report Valid
946+ updatesSink . UpdateStatus ( DataSourceState . Valid , null ) ;
947+ await Task . Delay ( 10 ) ;
948+
949+ // Call Apply with dummy data (with selector to trigger switch to synchronizer)
950+ updatesSink . Apply ( new ChangeSet < ItemDescriptor > (
951+ ChangeSetType . Full ,
952+ Selector . Make ( 1 , "dummy-state" ) ,
953+ initializerDummyData . Data ,
954+ null
955+ ) ) ;
956+ await Task . Delay ( 10 ) ;
957+ }
958+ ) ;
959+ return source ;
960+ } ;
961+
962+ // Track whether the synchronizer factory was invoked
963+ bool synchronizerFactoryInvoked = false ;
964+ IDataSourceUpdatesV2 synchronizerUpdateSink = null ;
965+
966+ // Create synchronizer factory: emits Initializing, calls init with dummy data, then reports Valid
967+ SourceFactory synchronizerFactory = ( updatesSink ) =>
968+ {
969+ synchronizerFactoryInvoked = true ;
970+ synchronizerUpdateSink = updatesSink ;
971+ var source = new MockDataSourceWithInit (
972+ async ( ) =>
973+ {
974+ // Emit Initializing
975+ updatesSink . UpdateStatus ( DataSourceState . Initializing , null ) ;
976+ await Task . Delay ( 10 ) ;
977+
978+ // Report Valid
979+ updatesSink . UpdateStatus ( DataSourceState . Valid , null ) ;
980+ await Task . Delay ( 10 ) ;
981+
982+ // Call Apply with dummy data
983+ updatesSink . Apply ( new ChangeSet < ItemDescriptor > (
984+ ChangeSetType . Full ,
985+ Selector . Make ( 2 , "dummy-state" ) ,
986+ synchronizerDummyData . Data ,
987+ null
988+ ) ) ;
989+ await Task . Delay ( 10 ) ;
990+ }
991+ ) ;
992+ return source ;
993+ } ;
994+
995+ // Create FDv2DataSource with one initializer, one synchronizer, and empty fdv1Synchronizers
996+ var initializers = new List < SourceFactory > { initializerFactory } ;
997+ var synchronizers = new List < SourceFactory > { synchronizerFactory } ;
998+ var fdv1Synchronizers = new List < SourceFactory > ( ) ;
999+
1000+ var dataSource = FDv2DataSource . CreateFDv2DataSource (
1001+ capturingSink ,
1002+ initializers ,
1003+ synchronizers ,
1004+ fdv1Synchronizers
1005+ ) ;
1006+
1007+ // Start the data source
1008+ var startTask = dataSource . Start ( ) ;
1009+
1010+ // Wait for all expected status updates to be recorded
1011+ // Expected sequence: Initializing (initializer), Valid (initializer), Interrupted (switching to synchronizer), Valid (synchronizer)
1012+ var statusUpdates = capturingSink . WaitForStatusUpdates ( 4 , TimeSpan . FromSeconds ( 5 ) ) ;
1013+
1014+ // Verify that Start() completed successfully
1015+ var startResult = await startTask ;
1016+ Assert . True ( startResult ) ;
1017+
1018+ // Verify status updates by position
1019+ // Position 0: Initializing (from initializer)
1020+ Assert . True ( statusUpdates . Count > 0 , "Expected at least 1 status update" ) ;
1021+ Assert . Equal ( DataSourceState . Initializing , statusUpdates [ 0 ] . State ) ;
1022+
1023+ // Position 1: Valid (from initializer)
1024+ Assert . True ( statusUpdates . Count > 1 , "Expected at least 2 status updates" ) ;
1025+ Assert . Equal ( DataSourceState . Valid , statusUpdates [ 1 ] . State ) ;
1026+
1027+ // Position 2: Interrupted (switching to synchronizer)
1028+ Assert . True ( statusUpdates . Count > 2 , "Expected at least 3 status updates" ) ;
1029+ Assert . Equal ( DataSourceState . Interrupted , statusUpdates [ 2 ] . State ) ;
1030+
1031+ // Position 3: Valid (from synchronizer)
1032+ Assert . True ( statusUpdates . Count > 3 , "Expected at least 4 status updates" ) ;
1033+ Assert . Equal ( DataSourceState . Valid , statusUpdates [ 3 ] . State ) ;
1034+
1035+ // Verify that both factories were invoked
1036+ Assert . True ( initializerFactoryInvoked , "Initializer factory should have been invoked" ) ;
1037+ Assert . True ( synchronizerFactoryInvoked , "Synchronizer factory should have been invoked" ) ;
1038+
1039+ // Verify that Apply was called twice: once for initializer, once for synchronizer
1040+ var firstChangeSet = capturingSink . Applies . ExpectValue ( TimeSpan . FromSeconds ( 1 ) ) ;
1041+ Assert . Equal ( ChangeSetType . Full , firstChangeSet . Type ) ;
1042+
1043+ var secondChangeSet = capturingSink . Applies . ExpectValue ( TimeSpan . FromSeconds ( 1 ) ) ;
1044+ Assert . Equal ( ChangeSetType . Full , secondChangeSet . Type ) ;
1045+
1046+ // Verify that there are no more Apply calls
1047+ capturingSink . Applies . ExpectNoValue ( TimeSpan . FromMilliseconds ( 100 ) ) ;
1048+
1049+ dataSource . Dispose ( ) ;
1050+ }
1051+
9191052 [ Fact ]
9201053 public async Task NoInitializersAndNoSynchronizersIsWellBehaved ( )
9211054 {
0 commit comments