// // SearchTests.swift // AutoCatTests // // Created by Selim Mustafaev on 22.02.2025. // Copyright © 2025 Selim Mustafaev. All rights reserved. // import Testing import Mockable import AutoCatCore @testable import AutoCat @MainActor struct SearchTests { let apiServiceMock = MockApiServiceProtocol() let vehicleServiceMock = MockVehicleServiceProtocol() var viewModel: SearchViewModel init() { viewModel = SearchViewModel( apiService: apiServiceMock, vehicleService: vehicleServiceMock ) } @Test("Initial load") func initialLoad() async { given(apiServiceMock) .getVehicles(with: .any, pageToken: .value(nil), pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) await viewModel.onAppear() verify(apiServiceMock) .getVehicles(with: .any, pageToken: .value(nil), pageSize: .any) .called(.once) #expect(viewModel.vehicles == [.normal]) #expect(viewModel.vehicleSections.count == 1) #expect(viewModel.searchText == "") #expect(viewModel.hud == nil) } @Test("Initial load error") func initialLoadError() async { given(apiServiceMock) .getVehicles(with: .any, pageToken: .value(nil), pageSize: .any) .willThrow(TestError.generic) await viewModel.onAppear() #expect(viewModel.vehicles.isEmpty) #expect(viewModel.hud == .error(TestError.generic)) } @Test("Search") func search() async throws { given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) await viewModel.onAppear() viewModel.searchText = "test1" viewModel.searchText = "test2" try await Task.sleep(for: .milliseconds(700)) viewModel.searchText = "test3" try await Task.sleep(for: .milliseconds(700)) verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.exactly(3)) #expect(viewModel.vehicles == [.normal]) #expect(viewModel.vehicleSections.count == 1) #expect(viewModel.filter.searchString == "test3") #expect(viewModel.hud == nil) } @Test("Search error") func searchError() async throws { var filterWithString = Filter() filterWithString.searchString = "test" given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willThrow(TestError.generic) await viewModel.onAppear() viewModel.searchText = "test" try await Task.sleep(for: .milliseconds(600)) #expect(viewModel.vehicles.isEmpty) #expect(viewModel.hud == .error(TestError.generic)) } @Test("Pagination") func pagination() async { given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [])) await viewModel.onAppear() await viewModel.loadMoreData() await viewModel.loadMoreData() verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.exactly(3)) #expect(viewModel.vehicles.count == 2) #expect(viewModel.hasMoreData == false) #expect(viewModel.hud == nil) } @Test("Pagination error") func paginationError() async { given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willThrow(TestError.generic) await viewModel.onAppear() await viewModel.loadMoreData() await viewModel.loadMoreData() verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.exactly(3)) #expect(viewModel.vehicles.count == 2) #expect(viewModel.hasMoreData == false) #expect(viewModel.hud == .error(TestError.generic)) } @Test("Update vehicle") func updateVehicle() async { let updatedVehicle: VehicleDto = .normal .addEvent(.valid) given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) given(vehicleServiceMock) .updateSearch(number: .value(VehicleDto.normal.number)) .willReturn((updatedVehicle, [])) await viewModel.onAppear() await viewModel.updateVehicle(.normal) verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.once) verify(vehicleServiceMock) .updateSearch(number: .any) .called(.once) #expect(viewModel.vehicles.first?.events.isEmpty == false) #expect(viewModel.hud == nil) } @Test("Update vehicle error") func updateVehicleError() async { let updatedVehicle: VehicleDto = .normal .addEvent(.valid) given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) given(vehicleServiceMock) .updateSearch(number: .value(VehicleDto.normal.number)) .willReturn((updatedVehicle, [TestError.generic])) await viewModel.onAppear() await viewModel.updateVehicle(.normal) verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.once) verify(vehicleServiceMock) .updateSearch(number: .any) .called(.once) #expect(viewModel.vehicles.first?.events.isEmpty == true) if case .message(_, _) = viewModel.hud {} else { Issue.record("hud must be in .message state") } } @Test("Update vehicle error 2") func updateVehicleError2() async { given(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .willReturn(PagedResponse(items: [VehicleDto.normal])) given(vehicleServiceMock) .updateSearch(number: .value(VehicleDto.normal.number)) .willThrow(TestError.generic) await viewModel.onAppear() await viewModel.updateVehicle(.normal) verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any) .called(.once) verify(vehicleServiceMock) .updateSearch(number: .any) .called(.once) #expect(viewModel.vehicles.first?.events.isEmpty == true) #expect(viewModel.hud == .error(TestError.generic)) } }