listless

A simple list app for Apple platforms
Log | Files | Refs | README | LICENSE

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 }