diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 7b3e3c6..54bf53f 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -522,6 +522,7 @@ 7A54BFD52D43D7E100176D6D /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( + "Extensions/AudioRecordDto+Presets.swift", Extensions/TestError.swift, "Extensions/VehicleDto+Presets.swift", "Extensions/VehicleEventDto+Presets.swift", diff --git a/AutoCatCoreTests/Storage/StorageServiceTests+AudioRecords.swift b/AutoCatCoreTests/Storage/StorageServiceTests+AudioRecords.swift new file mode 100644 index 0000000..9fac9f6 --- /dev/null +++ b/AutoCatCoreTests/Storage/StorageServiceTests+AudioRecords.swift @@ -0,0 +1,65 @@ +// +// StorageServiceTests+AudioRecords.swift +// AutoCatCoreTests +// +// Created by Selim Mustafaev on 05.04.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import Testing +import AutoCatCore + +extension StorageServiceTests { + + @Test("Audio records save/load") + func audioRecordsSaveLoad() async throws { + + try await storageService.add(record: .default) + let records = try await storageService.loadRecords() + + #expect(records.count == 1) + #expect(records.first?.path == AudioRecordDto.testPath) + } + + @Test("Audio records save/delete") + func audioRecordsSaveDelete() async throws { + + try await storageService.add(record: .default) + try await storageService.deleteRecord(id: AudioRecordDto.default.id) + let records = try await storageService.loadRecords() + + #expect(records.isEmpty) + } + + @Test("Audio records delete wrong record") + func audioRecordsDeleteWrongRecord() async throws { + + await #expect(throws: StorageError.recordNotFound) { + try await storageService.deleteRecord(id: AudioRecordDto.default.id) + } + } + + @Test("Audio record update") + func audioRecordUpdate() async throws { + + try await storageService.add(record: .default) + let updatedRecord = try await storageService.updateRecord( + id: AudioRecordDto.default.id, + number: "123" + ) + + #expect(updatedRecord.path == AudioRecordDto.testPath) + #expect(updatedRecord.number == "123") + } + + @Test("Audio records update wrong record") + func audioRecordUpdateWrongRecord() async throws { + + await #expect(throws: StorageError.recordNotFound) { + _ = try await storageService.updateRecord( + id: AudioRecordDto.default.id, + number: "123" + ) + } + } +} diff --git a/AutoCatCoreTests/VehicleRecordServiceTests.swift b/AutoCatCoreTests/VehicleRecordServiceTests.swift index 203dbb7..7bba7b6 100644 --- a/AutoCatCoreTests/VehicleRecordServiceTests.swift +++ b/AutoCatCoreTests/VehicleRecordServiceTests.swift @@ -11,13 +11,22 @@ import Mockable @testable import AutoCatCore import Foundation +extension VehicleRecordService { + + func setUrl(_ url: URL) async { + + self.url = url + } +} + struct VehicleRecordServiceTests { let locationServiceMock: MockLocationServiceProtocol let audioRecordServiceMock = MockAudioRecordServiceProtocol() let settingsServiceMock = MockSettingsServiceProtocol() - let vehicleRecordService: VehicleRecordService + var vehicleRecordService: VehicleRecordService + init() async { self.locationServiceMock = await .init() @@ -68,12 +77,123 @@ struct VehicleRecordServiceTests { .startRecording(to: .any) .called(.once) - verify(locationServiceMock) + await verify(locationServiceMock) .getRecentLocation() - .called(.once) + .calledEventually(1, before: .seconds(1)) await #expect(vehicleRecordService.url != nil) await #expect(vehicleRecordService.location != nil) await #expect(vehicleRecordService.locationTask != nil) } + + @Test("Start recording (location error)") + func startRecordingLocationError() async throws { + + given(audioRecordServiceMock) + .startRecording(to: .any) + .willReturn() + + given(locationServiceMock) + .getRecentLocation() + .willThrow(TestError.generic) + + try await vehicleRecordService.startRecording() + + verify(audioRecordServiceMock) + .startRecording(to: .any) + .called(.once) + + await verify(locationServiceMock) + .getRecentLocation() + .calledEventually(1, before: .seconds(1)) + + await #expect(vehicleRecordService.url != nil) + await #expect(vehicleRecordService.location == nil) + await #expect(vehicleRecordService.locationTask != nil) + } + + @Test("Stop recording") + func stopRecording() async throws { + + await vehicleRecordService.setUrl(URL(string: "http://localhost")!) + + given(audioRecordServiceMock) + .stopRecording() + .willReturn() + + given(audioRecordServiceMock) + .recognizeText(from: .any) + .willReturn("123") + + given(audioRecordServiceMock) + .getDuration(from: .any) + .willReturn(123) + + let record = try await vehicleRecordService.stopRecording() + + verify(audioRecordServiceMock) + .stopRecording() + .called(.once) + + verify(audioRecordServiceMock) + .recognizeText(from: .any) + .called(.once) + + verify(audioRecordServiceMock) + .getDuration(from: .any) + .called(.once) + + #expect(await vehicleRecordService.url == nil) + #expect(await vehicleRecordService.locationTask == nil) + #expect(record.duration == 123) + #expect(record.rawText == "123") + } + + @Test("Stop recording (no URL)") + func stopRecordingNoUrl() async throws { + + given(audioRecordServiceMock) + .cancelRecording() + .willReturn() + + await #expect(throws: VehicleRecordError.emptyUrl) { + _ = try await vehicleRecordService.stopRecording() + } + + verify(audioRecordServiceMock) + .cancelRecording() + .called(.once) + } + + @Test("Cencel recording") + func cancelRecording() async throws { + + await vehicleRecordService.setUrl(URL(string: "http://localhost")!) + + given(audioRecordServiceMock) + .cancelRecording() + .willReturn() + + given(audioRecordServiceMock) + .startRecording(to: .any) + .willReturn() + + given(locationServiceMock) + .getRecentLocation() + .willReturn(.valid) + + _ = try await vehicleRecordService.startRecording() + await vehicleRecordService.cancelRecording() + + verify(audioRecordServiceMock) + .startRecording(to: .any) + .called(.once) + + verify(audioRecordServiceMock) + .cancelRecording() + .called(.once) + + #expect(await vehicleRecordService.url == nil) + #expect(await vehicleRecordService.locationTask == nil) + } } diff --git a/AutoCatTests/Extensions/AudioRecordDto+Presets.swift b/AutoCatTests/Extensions/AudioRecordDto+Presets.swift new file mode 100644 index 0000000..89eefe6 --- /dev/null +++ b/AutoCatTests/Extensions/AudioRecordDto+Presets.swift @@ -0,0 +1,24 @@ +// +// AudioRecordDto+Presets.swift +// AutoCatTests +// +// Created by Selim Mustafaev on 05.04.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import AutoCatCore + +extension AudioRecordDto { + + static let testPath = "testPath" + + public static var `default`: Self { + .init( + path: testPath, + number: nil, + raw: "", + duration: 0, + event: nil + ) + } +} diff --git a/AutoCatTests/SearchTests.swift b/AutoCatTests/SearchTests.swift index 6ea6811..7ed245b 100644 --- a/AutoCatTests/SearchTests.swift +++ b/AutoCatTests/SearchTests.swift @@ -70,9 +70,9 @@ struct SearchTests { viewModel.searchText = "test1" viewModel.searchText = "test2" - try await Task.sleep(for: .milliseconds(600)) + try await Task.sleep(for: .milliseconds(700)) viewModel.searchText = "test3" - try await Task.sleep(for: .milliseconds(600)) + try await Task.sleep(for: .milliseconds(700)) verify(apiServiceMock) .getVehicles(with: .any, pageToken: .any, pageSize: .any)