@@ -785,7 +785,8 @@ public async Task RefreshProviderAsync_WithExistingDataAndSyncWindow_MergesCorre
785785 var userId = Guid . NewGuid ( ) ;
786786 var provider = "withings" ;
787787 var lastSyncTime = DateTime . UtcNow . AddDays ( - 1 ) ;
788- var syncStartDate = lastSyncTime . AddDays ( - 90 ) ; // 90 days before last sync
788+ var syncStartDate = lastSyncTime . AddDays ( - 90 ) ; // Fetch starts 90 days before last sync
789+ var cutoffDate = syncStartDate . AddDays ( 2 ) ; // Splice at 88 days before last sync (2-day buffer)
789790
790791 // Existing data from previous sync (has both old and recent measurements)
791792 var existingSourceData = new List < SourceData >
@@ -796,23 +797,23 @@ public async Task RefreshProviderAsync_WithExistingDataAndSyncWindow_MergesCorre
796797 LastUpdate = lastSyncTime ,
797798 Measurements = new List < RawMeasurement >
798799 {
799- // Old measurements (before sync window )
800- CreateTestRawMeasurement ( syncStartDate . AddDays ( - 10 ) . ToString ( "yyyy-MM-dd" ) , 65.0m ) ,
801- CreateTestRawMeasurement ( syncStartDate . AddDays ( - 5 ) . ToString ( "yyyy-MM-dd" ) , 66.0m ) ,
802- // Recent measurements (within sync window )
803- CreateTestRawMeasurement ( syncStartDate . AddDays ( 5 ) . ToString ( "yyyy-MM-dd" ) , 70.0m ) ,
804- CreateTestRawMeasurement ( syncStartDate . AddDays ( 10 ) . ToString ( "yyyy-MM-dd" ) , 71.0m ) ,
805- CreateTestRawMeasurement ( syncStartDate . AddDays ( 15 ) . ToString ( "yyyy-MM-dd" ) , 72.0m )
800+ // Old measurements (before cutoff at day -88, should be preserved )
801+ CreateTestRawMeasurement ( syncStartDate . AddDays ( - 10 ) . ToString ( "yyyy-MM-dd" ) , 65.0m ) , // day -100
802+ CreateTestRawMeasurement ( syncStartDate . AddDays ( - 5 ) . ToString ( "yyyy-MM-dd" ) , 66.0m ) , // day -95
803+ // Recent measurements (after cutoff, should be replaced by provider data )
804+ CreateTestRawMeasurement ( syncStartDate . AddDays ( 5 ) . ToString ( "yyyy-MM-dd" ) , 70.0m ) , // day -85
805+ CreateTestRawMeasurement ( syncStartDate . AddDays ( 10 ) . ToString ( "yyyy-MM-dd" ) , 71.0m ) , // day -80
806+ CreateTestRawMeasurement ( syncStartDate . AddDays ( 15 ) . ToString ( "yyyy-MM-dd" ) , 72.0m ) // day -75
806807 }
807808 }
808809 } ;
809810
810- // New measurements from provider (only has data within sync window )
811+ // New measurements from provider (fetched from day -90, truncated to day -88 onwards )
811812 var newMeasurements = new List < RawMeasurement >
812813 {
813- CreateTestRawMeasurement ( syncStartDate . AddDays ( 5 ) . ToString ( "yyyy-MM-dd" ) , 70.5m ) , // Updated weight
814- CreateTestRawMeasurement ( syncStartDate . AddDays ( 12 ) . ToString ( "yyyy-MM-dd" ) , 71.5m ) , // New measurement
815- // Note: measurements at day 10 and 15 are missing (user deleted them)
814+ CreateTestRawMeasurement ( syncStartDate . AddDays ( 5 ) . ToString ( "yyyy-MM-dd" ) , 70.5m ) , // day -85, updated weight
815+ CreateTestRawMeasurement ( syncStartDate . AddDays ( 12 ) . ToString ( "yyyy-MM-dd" ) , 71.5m ) , // day -78, new measurement
816+ // Note: measurements at days -80 and -75 are missing (user deleted them upstream )
816817 } ;
817818
818819 var providerService = new Mock < IProviderService > ( ) ;
@@ -939,6 +940,79 @@ public async Task RefreshProviderAsync_WithFullSync_ReplacesAllData()
939940 It . Is < List < SourceData > > ( sd => VerifyFullSyncReplacement ( sd , provider , newMeasurements ) ) ) , Times . Once ) ;
940941 }
941942
943+ [ Fact ]
944+ public async Task GetMeasurementsForUserAsync_WithForceFullSyncFlag_ClearsDataAndPerformsFullSync ( )
945+ {
946+ // Arrange
947+ var userId = Guid . NewGuid ( ) ;
948+ var provider = "fitbit" ;
949+
950+ // Existing data exists
951+ var existingSourceData = new List < SourceData >
952+ {
953+ new SourceData
954+ {
955+ Source = provider ,
956+ LastUpdate = DateTime . UtcNow . AddDays ( - 30 ) ,
957+ Measurements = new List < RawMeasurement >
958+ {
959+ CreateTestRawMeasurement ( "2024-01-01" , 70.0m ) ,
960+ CreateTestRawMeasurement ( "2024-01-02" , 71.0m )
961+ }
962+ }
963+ } ;
964+
965+ // New measurements from full sync
966+ var newMeasurements = new List < RawMeasurement >
967+ {
968+ CreateTestRawMeasurement ( "2024-01-01" , 70.5m ) , // Updated
969+ CreateTestRawMeasurement ( "2024-01-02" , 71.5m ) , // Updated
970+ CreateTestRawMeasurement ( "2024-01-03" , 72.0m ) // New
971+ } ;
972+
973+ var providerService = new Mock < IProviderService > ( ) ;
974+ // Verify that startDate is null (full sync) after clearing data
975+ providerService . Setup ( x => x . SyncMeasurementsAsync ( userId , true , null ) )
976+ . ReturnsAsync ( new ProviderSyncResult
977+ {
978+ Provider = provider ,
979+ Success = true ,
980+ Measurements = newMeasurements
981+ } ) ;
982+
983+ _providerIntegrationServiceMock . Setup ( x => x . GetProviderService ( provider ) )
984+ . Returns ( providerService . Object ) ;
985+
986+ // Mock force_full_sync flag to return true
987+ _sourceDataServiceMock . Setup ( x => x . GetForceFullSyncAsync ( userId , provider ) )
988+ . ReturnsAsync ( true ) ;
989+
990+ // Mock GetLastSyncTimeAsync to return null (simulating cleared data after ClearProviderDataAsync is called)
991+ _sourceDataServiceMock . Setup ( x => x . GetLastSyncTimeAsync ( userId , provider ) )
992+ . ReturnsAsync ( ( DateTime ? ) null ) ;
993+
994+ _sourceDataServiceMock . Setup ( x => x . GetSourceDataAsync ( userId , new List < string > { provider } ) )
995+ . ReturnsAsync ( existingSourceData ) ;
996+
997+ // Mock ClearSourceDataAsync to simulate data being cleared
998+ _sourceDataServiceMock . Setup ( x => x . ClearSourceDataAsync ( userId , provider ) )
999+ . Returns ( Task . CompletedTask ) ;
1000+
1001+ // Act
1002+ var result = await _sut . GetMeasurementsForUserAsync ( userId , new List < string > { provider } , true ) ;
1003+
1004+ // Assert
1005+ // Verify ClearProviderDataAsync was called (this is called on MeasurementSyncService, not the mock)
1006+ // We verify this indirectly by checking that a full sync was performed (startDate = null)
1007+ providerService . Verify ( x => x . SyncMeasurementsAsync ( userId , true , null ) , Times . Once ,
1008+ "Should perform full sync (startDate = null) after force_full_sync flag is detected" ) ;
1009+
1010+ // Verify data was updated
1011+ _sourceDataServiceMock . Verify ( x => x . UpdateSourceDataAsync (
1012+ userId ,
1013+ It . Is < List < SourceData > > ( sd => sd . Count == 1 && sd [ 0 ] . Source == provider ) ) , Times . Once ) ;
1014+ }
1015+
9421016 #endregion
9431017
9441018 #region Private Helper Methods
@@ -981,14 +1055,17 @@ private static bool VerifyMergedData(List<SourceData> sd, string provider, DateT
9811055 return false ;
9821056
9831057 var measurements = sd [ 0 ] . Measurements ! ; // We already checked it's not null
984- return measurements . Count == 4 && // 2 old (preserved) + 2 new (from provider)
985- // Old measurements preserved
1058+ // Cutoff is at syncStartDate + 2 days (88 days before last sync)
1059+ // Old measurements (before cutoff): preserved
1060+ // New measurements (after cutoff): from provider
1061+ return measurements . Count == 4 && // 2 old (preserved before day -88) + 2 new (from provider after day -88)
1062+ // Old measurements preserved (days -100, -95 are before cutoff at day -88)
9861063 measurements . Any ( m => m . Date == syncStartDate . AddDays ( - 10 ) . ToString ( "yyyy-MM-dd" ) && m . Weight == 65.0m ) &&
9871064 measurements . Any ( m => m . Date == syncStartDate . AddDays ( - 5 ) . ToString ( "yyyy-MM-dd" ) && m . Weight == 66.0m ) &&
988- // New measurements from provider
1065+ // New measurements from provider (days -85, -78 are after cutoff at day -88)
9891066 measurements . Any ( m => m . Date == syncStartDate . AddDays ( 5 ) . ToString ( "yyyy-MM-dd" ) && m . Weight == 70.5m ) &&
9901067 measurements . Any ( m => m . Date == syncStartDate . AddDays ( 12 ) . ToString ( "yyyy-MM-dd" ) && m . Weight == 71.5m ) &&
991- // Deleted measurements are gone
1068+ // Deleted measurements are gone (days -80, -75 were in old data but not in provider data)
9921069 ! measurements . Any ( m => m . Date == syncStartDate . AddDays ( 10 ) . ToString ( "yyyy-MM-dd" ) ) &&
9931070 ! measurements . Any ( m => m . Date == syncStartDate . AddDays ( 15 ) . ToString ( "yyyy-MM-dd" ) ) ;
9941071 }
0 commit comments