CloudKitErrorClassifierTests.swift (4310B)
1 import CloudKit 2 import Foundation 3 import Testing 4 5 #if os(macOS) 6 @testable import Listless 7 #else 8 @testable import Listless_iOS 9 #endif 10 11 @Suite("CloudKitErrorClassifier") 12 struct CloudKitErrorClassifierTests { 13 14 // MARK: - Transient Errors 15 16 @Test( 17 "Network and server errors are transient", 18 arguments: [ 19 CKError.Code.networkUnavailable, 20 CKError.Code.networkFailure, 21 CKError.Code.serviceUnavailable, 22 CKError.Code.requestRateLimited, 23 CKError.Code.zoneBusy, 24 CKError.Code.serverResponseLost, 25 CKError.Code.operationCancelled, 26 ] 27 ) 28 func transientErrors(code: CKError.Code) { 29 let error = CKError(code) 30 let issue = CloudKitErrorClassifier.classify(error) 31 32 guard case .transient(let message) = issue else { 33 Issue.record("Expected .transient, got \(issue)") 34 return 35 } 36 #expect(message.contains("retry")) 37 } 38 39 // MARK: - Deferred Errors 40 41 @Test( 42 "First-launch errors are deferred", 43 arguments: [ 44 CKError.Code.accountTemporarilyUnavailable, 45 CKError.Code.zoneNotFound, 46 CKError.Code.userDeletedZone, 47 ] 48 ) 49 func deferredErrors(code: CKError.Code) { 50 let error = CKError(code) 51 let issue = CloudKitErrorClassifier.classify(error) 52 53 guard case .deferred(let message) = issue else { 54 Issue.record("Expected .deferred, got \(issue)") 55 return 56 } 57 #expect(message.contains("retry")) 58 } 59 60 // MARK: - Transient Actionable Errors 61 62 @Test("Not authenticated is transient with sign-in message") 63 func notAuthenticated() { 64 let error = CKError(.notAuthenticated) 65 let issue = CloudKitErrorClassifier.classify(error) 66 67 guard case .transient(let message) = issue else { 68 Issue.record("Expected .transient, got \(issue)") 69 return 70 } 71 #expect(message.contains("Sign in")) 72 } 73 74 @Test("Quota exceeded is transient with storage message") 75 func quotaExceeded() { 76 let error = CKError(.quotaExceeded) 77 let issue = CloudKitErrorClassifier.classify(error) 78 79 guard case .transient(let message) = issue else { 80 Issue.record("Expected .transient, got \(issue)") 81 return 82 } 83 #expect(message.contains("storage full")) 84 } 85 86 @Test( 87 "Permission errors are transient with unavailable message", 88 arguments: [ 89 CKError.Code.permissionFailure, 90 CKError.Code.badContainer, 91 CKError.Code.missingEntitlement, 92 ] 93 ) 94 func permissionErrors(code: CKError.Code) { 95 let error = CKError(code) 96 let issue = CloudKitErrorClassifier.classify(error) 97 98 guard case .transient(let message) = issue else { 99 Issue.record("Expected .transient, got \(issue)") 100 return 101 } 102 #expect(message.contains("unavailable")) 103 } 104 105 // MARK: - Default / Unknown CKError 106 107 @Test("Unknown CKError code is deferred") 108 func unknownCKError() { 109 let error = CKError(.internalError) 110 let issue = CloudKitErrorClassifier.classify(error) 111 112 guard case .deferred(let message) = issue else { 113 Issue.record("Expected .deferred, got \(issue)") 114 return 115 } 116 #expect(message.contains("retry")) 117 } 118 119 // MARK: - Non-CloudKit Errors 120 121 @Test("Core Data error is transient") 122 func coreDataError() { 123 let error = NSError(domain: NSCocoaErrorDomain, code: 1570, userInfo: nil) 124 let issue = CloudKitErrorClassifier.classify(error) 125 126 guard case .transient(let message) = issue else { 127 Issue.record("Expected .transient, got \(issue)") 128 return 129 } 130 #expect(message.contains("retry")) 131 } 132 133 @Test("Unknown domain error is transient") 134 func unknownDomainError() { 135 let error = NSError(domain: "com.example.unknown", code: 42, userInfo: nil) 136 let issue = CloudKitErrorClassifier.classify(error) 137 138 guard case .transient(let message) = issue else { 139 Issue.record("Expected .transient, got \(issue)") 140 return 141 } 142 #expect(message.contains("retry")) 143 } 144 }