@@ -884,6 +884,53 @@ extension POSCatalogSyncCoordinatorTests {
884884 #expect( mockIncrementalSyncService. startIncrementalSyncCallCount == 1 )
885885 }
886886
887+ @Test func performFullSyncIfApplicable_with_zero_maxAge_ignores_temporal_eligibility( ) async throws {
888+ // Given - user past 30-day grace period with no recent open (normally ineligible)
889+ let fortyDaysAgo = Date ( ) . addingTimeInterval ( - 40 * 24 * 60 * 60 )
890+ mockSiteSettings. mockFirstPOSCatalogSyncDate = fortyDaysAgo
891+ // No lastPOSOpenedDate set (never opened recently)
892+
893+ let coordinator = POSCatalogSyncCoordinator (
894+ fullSyncService: mockSyncService,
895+ incrementalSyncService: mockIncrementalSyncService,
896+ grdbManager: grdbManager,
897+ catalogEligibilityChecker: MockPOSLocalCatalogEligibilityService ( ) ,
898+ siteSettings: mockSiteSettings
899+ )
900+ try createSiteInDatabase ( siteID: sampleSiteID, lastFullSyncDate: fortyDaysAgo)
901+ mockSyncService. startFullSyncResult = . success( POSCatalog ( products: [ ] , variations: [ ] , syncDate: . now) )
902+
903+ // When - maxAge is zero (forced sync, e.g. pull to refresh)
904+ try await coordinator. performFullSyncIfApplicable ( for: sampleSiteID, maxAge: . zero)
905+
906+ // Then - sync should proceed despite temporal ineligibility
907+ #expect( mockSyncService. startFullSyncCallCount == 1 )
908+ }
909+
910+ @Test func performIncrementalSyncIfApplicable_with_zero_maxAge_ignores_temporal_eligibility( ) async throws {
911+ // Given - user past 30-day grace period with no recent open (normally ineligible)
912+ let fortyDaysAgo = Date ( ) . addingTimeInterval ( - 40 * 24 * 60 * 60 )
913+ mockSiteSettings. mockFirstPOSCatalogSyncDate = fortyDaysAgo
914+ // No lastPOSOpenedDate set (never opened recently)
915+
916+ let coordinator = POSCatalogSyncCoordinator (
917+ fullSyncService: mockSyncService,
918+ incrementalSyncService: mockIncrementalSyncService,
919+ grdbManager: grdbManager,
920+ catalogEligibilityChecker: MockPOSLocalCatalogEligibilityService ( ) ,
921+ siteSettings: mockSiteSettings
922+ )
923+ // Set up database with full sync date (required for incremental sync)
924+ try createSiteInDatabase ( siteID: sampleSiteID, lastFullSyncDate: fortyDaysAgo)
925+ mockIncrementalSyncService. startIncrementalSyncResult = . success( POSCatalog ( products: [ ] , variations: [ ] , syncDate: . now) )
926+
927+ // When - maxAge is zero (forced sync, e.g. after a purchase)
928+ try await coordinator. performIncrementalSyncIfApplicable ( for: sampleSiteID, maxAge: . zero)
929+
930+ // Then - sync should proceed despite temporal ineligibility
931+ #expect( mockIncrementalSyncService. startIncrementalSyncCallCount == 1 )
932+ }
933+
887934 // MARK: - isSyncStale Tests
888935
889936 @Test func isSyncStale_returns_true_when_no_full_sync_performed( ) async throws {
@@ -1394,8 +1441,9 @@ extension POSCatalogSyncCoordinatorTests {
13941441 connectivityObserver: nil
13951442 )
13961443
1397- // When - Try to perform sync
1398- try ? await sut. performFullSyncIfApplicable ( for: sampleSiteID, maxAge: . zero)
1444+ // When - Try to perform sync with non-zero maxAge (temporal criteria are checked)
1445+ // Note: maxAge of .zero would bypass temporal eligibility checks
1446+ try ? await sut. performFullSyncIfApplicable ( for: sampleSiteID, maxAge: sampleMaxAge)
13991447
14001448 // Then - Should track pos_not_opened_30_days
14011449 let syncSkipped = mockAnalytics. trackedEvents. first { $0. eventName == " local_catalog_sync_skipped " }
@@ -1428,8 +1476,9 @@ extension POSCatalogSyncCoordinatorTests {
14281476 connectivityObserver: nil
14291477 )
14301478
1431- // When - Try to perform incremental sync
1432- try await sut. performIncrementalSyncIfApplicable ( for: sampleSiteID, maxAge: . zero)
1479+ // When - Try to perform incremental sync with non-zero maxAge (temporal criteria are checked)
1480+ // Note: maxAge of .zero would bypass temporal eligibility checks
1481+ try await sut. performIncrementalSyncIfApplicable ( for: sampleSiteID, maxAge: sampleMaxAge)
14331482
14341483 // Then - Should track pos_not_opened_30_days
14351484 let syncSkipped = mockAnalytics. trackedEvents. first { $0. eventName == " local_catalog_sync_skipped " }
0 commit comments