- Implement camera capture and plant identification workflow - Add Core Data persistence for plants, care schedules, and cached API data - Create collection view with grid/list layouts and filtering - Build plant detail views with care information display - Integrate Trefle botanical API for plant care data - Add local image storage for captured plant photos - Implement dependency injection container for testability - Include accessibility support throughout the app Bug fixes in this commit: - Fix Trefle API decoding by removing duplicate CodingKeys - Fix LocalCachedImage to load from correct PlantImages directory - Set dateAdded when saving plants for proper collection sorting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
167 lines
4.6 KiB
Swift
167 lines
4.6 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
|
|
|
|
// 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 }
|
|
}
|
|
}
|