Files
PlantGuide/PlantGuideTests/Mocks/MockCareScheduleRepository.swift
Trey t f41c77876a Add batch actions for multi-task completion (Phase 7)
Implement batch task completion feature allowing users to select and
complete multiple care tasks at once. Adds edit mode to Today View with
selection checkmarks, floating BatchActionBar, and confirmation dialog
for completing more than 3 tasks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 15:19:21 -06:00

202 lines
5.8 KiB
Swift

//
// 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
var batchCompleteCallCount = 0
// MARK: - Error Configuration
var shouldThrowOnSave = false
var shouldThrowOnFetch = false
var shouldThrowOnFetchAll = false
var shouldThrowOnFetchAllTasks = false
var shouldThrowOnUpdateTask = false
var shouldThrowOnDelete = false
var shouldThrowOnBatchComplete = 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?
var lastBatchCompletedTaskIDs: Set<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)
}
func batchCompleteTasks(_ taskIDs: Set<UUID>) async throws {
batchCompleteCallCount += 1
lastBatchCompletedTaskIDs = taskIDs
if shouldThrowOnBatchComplete {
throw errorToThrow
}
let now = Date()
for (plantID, var schedule) in schedules {
var modified = false
for (index, task) in schedule.tasks.enumerated() {
if taskIDs.contains(task.id) && !task.isCompleted {
schedule.tasks[index] = CareTask(
id: task.id,
plantID: task.plantID,
type: task.type,
scheduledDate: task.scheduledDate,
completedDate: now,
notes: task.notes
)
modified = true
}
}
if modified {
schedules[plantID] = schedule
}
}
}
// 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
batchCompleteCallCount = 0
shouldThrowOnSave = false
shouldThrowOnFetch = false
shouldThrowOnFetchAll = false
shouldThrowOnFetchAllTasks = false
shouldThrowOnUpdateTask = false
shouldThrowOnDelete = false
shouldThrowOnBatchComplete = false
lastSavedSchedule = nil
lastFetchedPlantID = nil
lastUpdatedTask = nil
lastDeletedPlantID = nil
lastBatchCompletedTaskIDs = 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 }
}
}