← All repositories

listless

A simple list app for Apple platforms

git clone https://code.inqk.net/listless.git


Tests/Unit/CloudKitErrorClassifierTests.swift (4310B)

1import CloudKit
2import Foundation
3import Testing
4
5#if os(macOS)
6@testable import Listless
7#else
8@testable import Listless_iOS
9#endif
10
11@Suite("CloudKitErrorClassifier")
12struct 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}