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