RadrootsAppleLocationServicesTests.swift (7517B)
1 import Foundation 2 import Testing 3 4 @testable import RadrootsKit 5 6 #if canImport(CoreLocation) 7 import CoreLocation 8 #endif 9 10 @Test func appleLocationServicesReportsCurrentAvailability() async { 11 let service = RadrootsAppleLocationServices( 12 adapters: RadrootsAppleLocationServicesAdapters( 13 locationServicesEnabled: { true }, 14 authorizationStatus: { .authorizedWhenInUse }, 15 requestWhenInUseAuthorization: { _ in .authorizedWhenInUse }, 16 requestCurrentLocation: { _ in 17 throw RadrootsLocationServicesError.permanentFailure 18 } 19 ) 20 ) 21 22 let availability = await service.currentAvailability() 23 24 #expect(availability.locationServicesEnabled) 25 #expect(availability.authorization == .authorizedWhenInUse) 26 #expect(availability.canRequestCurrentLocation) 27 } 28 29 @Test func appleLocationServicesShortCircuitsAuthorizationRequests() async throws { 30 let authorized = RadrootsAppleLocationServices( 31 adapters: RadrootsAppleLocationServicesAdapters( 32 locationServicesEnabled: { true }, 33 authorizationStatus: { .authorizedWhenInUse }, 34 requestWhenInUseAuthorization: { _ in 35 throw RadrootsLocationServicesError.permanentFailure 36 }, 37 requestCurrentLocation: { _ in 38 throw RadrootsLocationServicesError.permanentFailure 39 } 40 ) 41 ) 42 43 #expect(try await authorized.requestWhenInUseAuthorization() == .authorizedWhenInUse) 44 45 let denied = RadrootsAppleLocationServices( 46 adapters: RadrootsAppleLocationServicesAdapters( 47 locationServicesEnabled: { true }, 48 authorizationStatus: { .denied }, 49 requestWhenInUseAuthorization: { _ in 50 throw RadrootsLocationServicesError.permanentFailure 51 }, 52 requestCurrentLocation: { _ in 53 throw RadrootsLocationServicesError.permanentFailure 54 } 55 ) 56 ) 57 58 await #expect(throws: RadrootsLocationServicesError.permissionDenied) { 59 _ = try await denied.requestWhenInUseAuthorization() 60 } 61 } 62 63 @Test func appleLocationServicesRequestsAuthorizationWhenUndetermined() async throws { 64 let service = RadrootsAppleLocationServices( 65 adapters: RadrootsAppleLocationServicesAdapters( 66 locationServicesEnabled: { true }, 67 authorizationStatus: { .notDetermined }, 68 requestWhenInUseAuthorization: { timeoutSeconds in 69 #expect(timeoutSeconds == 12) 70 return .authorizedWhenInUse 71 }, 72 requestCurrentLocation: { _ in 73 throw RadrootsLocationServicesError.permanentFailure 74 } 75 ) 76 ) 77 78 #expect(try await service.requestWhenInUseAuthorization() == .authorizedWhenInUse) 79 } 80 81 @Test func appleLocationServicesReturnsCurrentLocationForAuthorizedState() async throws { 82 let capturedAt = Date(timeIntervalSince1970: 100) 83 let reading = try RadrootsLocationReading( 84 coordinate: RadrootsLocationCoordinate(latitude: 49.2827, longitude: -123.1207), 85 horizontalAccuracyMeters: 4, 86 capturedAt: capturedAt 87 ) 88 let service = RadrootsAppleLocationServices( 89 adapters: RadrootsAppleLocationServicesAdapters( 90 now: { Date(timeIntervalSince1970: 102) }, 91 locationServicesEnabled: { true }, 92 authorizationStatus: { .authorizedWhenInUse }, 93 requestWhenInUseAuthorization: { _ in .authorizedWhenInUse }, 94 requestCurrentLocation: { request in 95 #expect(request.timeoutSeconds == 3) 96 return reading 97 } 98 ) 99 ) 100 101 let result = try await service.currentLocation( 102 RadrootsCurrentLocationRequest( 103 timeoutSeconds: 3, 104 maximumCachedReadingAgeSeconds: 5 105 )) 106 107 #expect(result.reading == reading) 108 #expect(result.authorization == .authorizedWhenInUse) 109 } 110 111 @Test func appleLocationServicesRejectsCurrentLocationForUnauthorizedState() async { 112 let service = RadrootsAppleLocationServices( 113 adapters: RadrootsAppleLocationServicesAdapters( 114 locationServicesEnabled: { true }, 115 authorizationStatus: { .notDetermined }, 116 requestWhenInUseAuthorization: { _ in .authorizedWhenInUse }, 117 requestCurrentLocation: { _ in 118 throw RadrootsLocationServicesError.permanentFailure 119 } 120 ) 121 ) 122 123 await #expect(throws: RadrootsLocationServicesError.permissionDenied) { 124 _ = try await service.currentLocation(RadrootsCurrentLocationRequest(timeoutSeconds: 1)) 125 } 126 } 127 128 @Test func appleLocationServicesRejectsStaleCurrentLocationReadings() async throws { 129 let reading = try RadrootsLocationReading( 130 coordinate: RadrootsLocationCoordinate(latitude: 49.2827, longitude: -123.1207), 131 horizontalAccuracyMeters: 4, 132 capturedAt: Date(timeIntervalSince1970: 100) 133 ) 134 let service = RadrootsAppleLocationServices( 135 adapters: RadrootsAppleLocationServicesAdapters( 136 now: { Date(timeIntervalSince1970: 200) }, 137 locationServicesEnabled: { true }, 138 authorizationStatus: { .authorizedWhenInUse }, 139 requestWhenInUseAuthorization: { _ in .authorizedWhenInUse }, 140 requestCurrentLocation: { _ in reading } 141 ) 142 ) 143 144 await #expect(throws: RadrootsLocationServicesError.transientFailure) { 145 _ = try await service.currentLocation( 146 RadrootsCurrentLocationRequest( 147 timeoutSeconds: 1, 148 maximumCachedReadingAgeSeconds: 5 149 )) 150 } 151 } 152 153 @Test func appleLocationServicesPropagatesTypedLocationFailures() async { 154 let service = RadrootsAppleLocationServices( 155 adapters: RadrootsAppleLocationServicesAdapters( 156 locationServicesEnabled: { true }, 157 authorizationStatus: { .authorizedWhenInUse }, 158 requestWhenInUseAuthorization: { _ in .authorizedWhenInUse }, 159 requestCurrentLocation: { _ in 160 throw RadrootsLocationServicesError.timeout 161 } 162 ) 163 ) 164 165 await #expect(throws: RadrootsLocationServicesError.timeout) { 166 _ = try await service.currentLocation(RadrootsCurrentLocationRequest(timeoutSeconds: 1)) 167 } 168 } 169 170 #if canImport(CoreLocation) 171 @Test func appleLocationServicesMapsCoreLocationAuthorization() { 172 #expect( 173 RadrootsAppleLocationServicesAdapters.authorization( 174 for: CLAuthorizationStatus.notDetermined, 175 locationServicesEnabled: true 176 ) == .notDetermined) 177 #expect( 178 RadrootsAppleLocationServicesAdapters.authorization( 179 for: CLAuthorizationStatus.denied, 180 locationServicesEnabled: true 181 ) == .denied) 182 #expect( 183 RadrootsAppleLocationServicesAdapters.authorization( 184 for: CLAuthorizationStatus.authorizedAlways, 185 locationServicesEnabled: true 186 ) == .authorizedAlways) 187 #expect( 188 RadrootsAppleLocationServicesAdapters.authorization( 189 for: CLAuthorizationStatus.authorizedAlways, 190 locationServicesEnabled: false 191 ) == .unavailable) 192 #if os(iOS) 193 #expect( 194 RadrootsAppleLocationServicesAdapters.authorization( 195 for: CLAuthorizationStatus.authorizedWhenInUse, 196 locationServicesEnabled: true 197 ) == .authorizedWhenInUse) 198 #endif 199 } 200 #endif