AutoCat/AutoCatCoreTests/LocationServiceTests.swift

225 lines
7.0 KiB
Swift

//
// LocationServiceTests.swift
// AutoCatCoreTests
//
// Created by Selim Mustafaev on 01.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Testing
import CoreLocation
import Mockable
import Intents
@testable import AutoCatCore
@MainActor
struct LocationServiceTests {
let latitude: CLLocationDegrees = 10
let longitude: CLLocationDegrees = 10
let address = "Test Address"
let location: CLLocation
let geocoderMock = MockGeocoderProtocol()
let locationManagerMock = MockSwiftLocationProtocol()
let settingsServiceMock = MockSettingsServiceProtocol()
let locationService: LocationService
init() {
self.location = CLLocation(latitude: latitude, longitude: longitude)
self.locationService = LocationService(geocoder: geocoderMock,
locationManager: locationManagerMock,
settingsService: settingsServiceMock)
given(settingsServiceMock)
.user.willReturn(User())
}
@Test
func getValidAddress() async throws {
let placemark = CLPlacemark(location: location, name: address, postalAddress: nil)
given(geocoderMock)
.reverseGeocodeLocation(.any)
.willReturn([placemark])
let result = try await locationService.getAddressForLocation(latitude: latitude,
longitude: longitude)
verify(geocoderMock)
.reverseGeocodeLocation(.any)
.called(.once)
#expect(result == address)
}
@Test
func getNilAddress() async throws {
let placemark = CLPlacemark(location: location, name: nil, postalAddress: nil)
given(geocoderMock)
.reverseGeocodeLocation(.any)
.willReturn([placemark])
await #expect(throws: LocationError.reverseGeocode) {
_ = try await locationService.getAddressForLocation(latitude: latitude,
longitude: longitude)
}
verify(geocoderMock)
.reverseGeocodeLocation(.any)
.called(.once)
}
@Test
func addressNotFound() async throws {
given(geocoderMock)
.reverseGeocodeLocation(.any)
.willReturn([])
await #expect(throws: LocationError.reverseGeocode) {
_ = try await locationService.getAddressForLocation(latitude: latitude,
longitude: longitude)
}
verify(geocoderMock)
.reverseGeocodeLocation(.any)
.called(.once)
}
@Test("Get location: denied")
func getLocationDenied() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.denied)
await #expect(throws: CLError(.denied)) {
_ = try await locationService.requestCurrentLocation()
}
}
@Test("Get location: not determined -> denied")
func getLocationNotDeterminedDenied() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.notDetermined)
given(locationManagerMock)
.requestPermission(.value(.always))
.willReturn(.denied)
when(locationManagerMock)
.requestPermission(.value(.always))
.perform {
given(locationManagerMock)
.authorizationStatus
.willReturn(.denied)
}
await #expect(throws: CLError(.denied)) {
_ = try await locationService.requestCurrentLocation()
}
}
@Test("Get location: not determined -> allow")
func getLocationNotDeterminedAllow() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.notDetermined)
given(locationManagerMock)
.requestPermission(.value(.always))
.willReturn(.authorizedWhenInUse)
when(locationManagerMock)
.requestPermission(.value(.always))
.perform {
given(locationManagerMock)
.authorizationStatus
.willReturn(.authorizedWhenInUse)
}
given(locationManagerMock)
.requestLocation(accuracy: .any, timeout: .any)
.willReturn(.didUpdateLocations([location]))
let event = try await locationService.requestCurrentLocation()
#expect(event.latitude == latitude)
#expect(event.longitude == longitude)
}
@Test("Get location: normal")
func getLocationNormal() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.authorizedWhenInUse)
given(locationManagerMock)
.requestLocation(accuracy: .any, timeout: .any)
.willReturn(.didUpdateLocations([location]))
let event = try await locationService.requestCurrentLocation()
#expect(event.latitude == latitude)
#expect(event.longitude == longitude)
}
@Test("Get location: no location")
func getLocationNone() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.authorizedWhenInUse)
given(locationManagerMock)
.requestLocation(accuracy: .any, timeout: .any)
.willReturn(.didUpdateLocations([]))
await #expect(throws: LocationError.generic) {
_ = try await locationService.requestCurrentLocation()
}
}
@Test("Get location: parallel requests")
func getLocationParallel() async throws {
given(locationManagerMock)
.authorizationStatus
.willReturn(.authorizedWhenInUse)
given(locationManagerMock)
.requestLocation(accuracy: .any, timeout: .any)
.willReturn(.didUpdateLocations([location]))
async let task1 = locationService.requestCurrentLocation()
async let task2 = locationService.requestCurrentLocation()
try await Task.sleep(nanoseconds: 500_000_000)
async let task3 = locationService.requestCurrentLocation()
let (event1, event2, event3) = try await (task1, task2, task3)
verify(locationManagerMock)
.requestLocation(accuracy: .any, timeout: .any)
.called(.exactly(2))
#expect(event1.latitude == latitude)
#expect(event1.longitude == longitude)
#expect(event2.latitude == latitude)
#expect(event2.longitude == longitude)
#expect(event3.latitude == latitude)
#expect(event3.longitude == longitude)
}
}