import Testing @testable import Kordant import AppTrackingTransparency // MARK: - ATTService Tests struct ATTServiceTests { /// Creates an ATTService instance with an isolated UserDefaults suite for testing. @MainActor private func makeService() -> ATTService { let defaults = UserDefaults(suiteName: UUID().uuidString)! return ATTService(defaults: defaults) } // MARK: - Initial State @Test("ATTService starts with notDetermined status and no permission requested") @MainActor func initialState() { let service = makeService() #expect(service.trackingStatus == .notDetermined) #expect(service.hasRequestedPermission == false) #expect(service.hasShownExplanation == false) #expect(service.analyticsMode == .anonymous) #expect(service.shouldShowATTPrompt() == true) #expect(service.shouldShowExplanation() == true) } @Test("ATTService restores persisted permission-requested state") @MainActor func persistedRequestState() { let defaults = UserDefaults(suiteName: UUID().uuidString)! defaults.set(true, forKey: "kordant.att.requested") let service = ATTService(defaults: defaults) #expect(service.hasRequestedPermission == true) #expect(service.shouldShowATTPrompt() == false) } @Test("ATTService restores persisted explanation-shown state") @MainActor func persistedExplanationState() { let defaults = UserDefaults(suiteName: UUID().uuidString)! defaults.set(true, forKey: "kordant.att.explanationShown") let service = ATTService(defaults: defaults) #expect(service.hasShownExplanation == true) #expect(service.shouldShowExplanation() == false) } // MARK: - Authorization Status Checks @Test("ATTService isTrackingAuthorized returns true only for .authorized") @MainActor func trackingAuthorized() { let service = makeService() #expect(service.isTrackingAuthorized() == false) // Simulate authorized state service.trackingStatus = .authorized #expect(service.isTrackingAuthorized() == true) service.trackingStatus = .denied #expect(service.isTrackingAuthorized() == false) } @Test("ATTService isTrackingDenied returns true only for .denied") @MainActor func trackingDenied() { let service = makeService() service.trackingStatus = .denied #expect(service.isTrackingDenied() == true) service.trackingStatus = .authorized #expect(service.isTrackingDenied() == false) } @Test("ATTService isTrackingRestricted returns true only for .restricted") @MainActor func trackingRestricted() { let service = makeService() service.trackingStatus = .restricted #expect(service.isTrackingRestricted() == true) service.trackingStatus = .authorized #expect(service.isTrackingRestricted() == false) } // MARK: - Analytics Mode @Test("ATTService analytics mode is anonymous for all non-authorized states") @MainActor func analyticsModeForDenied() { let service = makeService() service.trackingStatus = .denied service.refreshStatus() #expect(service.analyticsMode == .anonymous) #expect(service.analyticsMode.usesIDFA == false) } @Test("ATTService analytics mode is anonymous for restricted state") @MainActor func analyticsModeForRestricted() { let service = makeService() service.trackingStatus = .restricted service.refreshStatus() #expect(service.analyticsMode == .anonymous) #expect(service.analyticsMode.usesIDFA == false) } @Test("ATTService analytics mode is full for authorized state") @MainActor func analyticsModeForAuthorized() { let service = makeService() service.trackingStatus = .authorized service.refreshStatus() #expect(service.analyticsMode == .full) #expect(service.analyticsMode.usesIDFA == true) } @Test("ATTService analytics mode is anonymous for notDetermined") @MainActor func analyticsModeForNotDetermined() { let service = makeService() #expect(service.analyticsMode == .anonymous) #expect(service.analyticsMode.usesIDFA == false) } // MARK: - Prompt Logic @Test("ATTService shouldShowATTPrompt returns false after permission requested") @MainActor func promptNotShownAfterRequest() { let service = makeService() #expect(service.shouldShowATTPrompt() == true) service.hasRequestedPermission = true #expect(service.shouldShowATTPrompt() == false) } @Test("ATTService shouldShowATTPrompt returns false if already authorized") @MainActor func promptNotShownIfAuthorized() { let service = makeService() service.trackingStatus = .authorized #expect(service.shouldShowATTPrompt() == false) } @Test("ATTService shouldShowATTPrompt returns false if denied previously") @MainActor func promptNotShownIfDenied() { let service = makeService() service.hasRequestedPermission = true service.trackingStatus = .denied #expect(service.shouldShowATTPrompt() == false) } @Test("ATTService shouldShowExplanation returns true only before explanation shown") @MainActor func explanationLogic() { let service = makeService() #expect(service.shouldShowExplanation() == true) service.markExplanationShown() #expect(service.shouldShowExplanation() == false) #expect(service.hasShownExplanation == true) } // MARK: - State Management @Test("ATTService markExplanationShown persists state") @MainActor func markExplanationPersists() { let defaults = UserDefaults(suiteName: UUID().uuidString)! let service = ATTService(defaults: defaults) service.markExplanationShown() // Create a new instance and verify persistence let service2 = ATTService(defaults: defaults) #expect(service2.hasShownExplanation == true) } @Test("ATTService refreshStatus updates tracking status") @MainActor func refreshStatus() { let service = makeService() #expect(service.trackingStatus == .notDetermined) // Simulate status change (in a real scenario the system updates this) service.trackingStatus = .denied #expect(service.analyticsMode == .denied) // indirect: analyticsMode should become anonymous } @Test("ATTService resetPermissionState clears request flag") @MainActor func resetPermissionState() { let defaults = UserDefaults(suiteName: UUID().uuidString)! let service = ATTService(defaults: defaults) service.hasRequestedPermission = true defaults.set(true, forKey: "kordant.att.requested") service.resetPermissionState() #expect(service.hasRequestedPermission == false) } } // MARK: - AnalyticsMode Tests struct AnalyticsModeTests { @Test("AnalyticsMode usesIDFA is true only for full mode") func usesIDFA() { #expect(AnalyticsMode.anonymous.usesIDFA == false) #expect(AnalyticsMode.full.usesIDFA == true) } @Test("AnalyticsMode raw values are correct") func rawValues() { #expect(AnalyticsMode.anonymous.rawValue == "anonymous") #expect(AnalyticsMode.full.rawValue == "full") } }