// // MockCareScheduleRepository.swift // PlantGuideTests // // Mock implementation of CareScheduleRepositoryProtocol for unit testing. // Provides configurable behavior and call tracking for verification. // import Foundation @testable import PlantGuide // MARK: - MockCareScheduleRepository /// Mock implementation of CareScheduleRepositoryProtocol for testing final class MockCareScheduleRepository: CareScheduleRepositoryProtocol, @unchecked Sendable { // MARK: - Storage var schedules: [UUID: PlantCareSchedule] = [:] // MARK: - Call Tracking var saveCallCount = 0 var fetchForPlantCallCount = 0 var fetchAllCallCount = 0 var fetchAllTasksCallCount = 0 var updateTaskCallCount = 0 var deleteCallCount = 0 // MARK: - Error Configuration var shouldThrowOnSave = false var shouldThrowOnFetch = false var shouldThrowOnFetchAll = false var shouldThrowOnFetchAllTasks = false var shouldThrowOnUpdateTask = false var shouldThrowOnDelete = false var errorToThrow: Error = NSError( domain: "MockError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Mock care schedule repository error"] ) // MARK: - Captured Values var lastSavedSchedule: PlantCareSchedule? var lastFetchedPlantID: UUID? var lastUpdatedTask: CareTask? var lastDeletedPlantID: UUID? // MARK: - CareScheduleRepositoryProtocol func save(_ schedule: PlantCareSchedule) async throws { saveCallCount += 1 lastSavedSchedule = schedule if shouldThrowOnSave { throw errorToThrow } schedules[schedule.plantID] = schedule } func fetch(for plantID: UUID) async throws -> PlantCareSchedule? { fetchForPlantCallCount += 1 lastFetchedPlantID = plantID if shouldThrowOnFetch { throw errorToThrow } return schedules[plantID] } func fetchAll() async throws -> [PlantCareSchedule] { fetchAllCallCount += 1 if shouldThrowOnFetchAll { throw errorToThrow } return Array(schedules.values) } func fetchAllTasks() async throws -> [CareTask] { fetchAllTasksCallCount += 1 if shouldThrowOnFetchAllTasks { throw errorToThrow } return schedules.values.flatMap { $0.tasks } } func updateTask(_ task: CareTask) async throws { updateTaskCallCount += 1 lastUpdatedTask = task if shouldThrowOnUpdateTask { throw errorToThrow } // Find and update the task in the appropriate schedule for (plantID, var schedule) in schedules { if let index = schedule.tasks.firstIndex(where: { $0.id == task.id }) { schedule.tasks[index] = task schedules[plantID] = schedule break } } } func delete(for plantID: UUID) async throws { deleteCallCount += 1 lastDeletedPlantID = plantID if shouldThrowOnDelete { throw errorToThrow } schedules.removeValue(forKey: plantID) } // MARK: - Helper Methods /// Resets all state for clean test setup func reset() { schedules = [:] saveCallCount = 0 fetchForPlantCallCount = 0 fetchAllCallCount = 0 fetchAllTasksCallCount = 0 updateTaskCallCount = 0 deleteCallCount = 0 shouldThrowOnSave = false shouldThrowOnFetch = false shouldThrowOnFetchAll = false shouldThrowOnFetchAllTasks = false shouldThrowOnUpdateTask = false shouldThrowOnDelete = false lastSavedSchedule = nil lastFetchedPlantID = nil lastUpdatedTask = nil lastDeletedPlantID = nil } /// Adds a schedule directly to storage (bypasses save method) func addSchedule(_ schedule: PlantCareSchedule) { schedules[schedule.plantID] = schedule } /// Adds multiple schedules directly to storage func addSchedules(_ schedulesToAdd: [PlantCareSchedule]) { for schedule in schedulesToAdd { schedules[schedule.plantID] = schedule } } /// Gets all tasks for a specific plant func getTasks(for plantID: UUID) -> [CareTask] { schedules[plantID]?.tasks ?? [] } /// Gets overdue tasks across all schedules func getOverdueTasks() -> [CareTask] { schedules.values.flatMap { $0.overdueTasks } } /// Gets pending tasks across all schedules func getPendingTasks() -> [CareTask] { schedules.values.flatMap { $0.pendingTasks } } }