Fix test infrastructure for Rooms feature and improve testability

- Update Plant test fixtures to use roomID instead of deprecated location
- Add URLDataFetcher protocol to ImageCache for dependency injection
- Update ImageCacheTests to use protocol-based mock instead of URLSession subclass
- Add missing cancelReminders(for:plantID:) method to MockNotificationService
- Add Equatable conformance to ImageCacheError for test assertions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-23 14:55:50 -06:00
parent 7786a40ae0
commit 08ced7dbbb
5 changed files with 89 additions and 43 deletions

View File

@@ -9,16 +9,16 @@
import XCTest
@testable import PlantGuide
// MARK: - MockURLSession
// MARK: - MockURLDataFetcher
/// Mock URL session for testing image downloads
final class MockURLSessionForCache: URLSession, @unchecked Sendable {
/// Mock URL data fetcher for testing image downloads
final class MockURLDataFetcher: URLDataFetcher, @unchecked Sendable {
var dataToReturn: Data?
var errorToThrow: Error?
var downloadCallCount = 0
var lastRequestedURL: URL?
override func data(from url: URL) async throws -> (Data, URLResponse) {
func data(from url: URL) async throws -> (Data, URLResponse) {
downloadCallCount += 1
lastRequestedURL = url
@@ -44,7 +44,7 @@ final class ImageCacheTests: XCTestCase {
// MARK: - Properties
private var sut: ImageCache!
private var mockSession: MockURLSessionForCache!
private var mockFetcher: MockURLDataFetcher!
private var testDirectory: URL!
private var fileManager: FileManager!
@@ -54,7 +54,7 @@ final class ImageCacheTests: XCTestCase {
try await super.setUp()
fileManager = FileManager.default
mockSession = MockURLSessionForCache()
mockFetcher = MockURLDataFetcher()
// Create a unique test directory for each test
let tempDir = fileManager.temporaryDirectory
@@ -63,7 +63,7 @@ final class ImageCacheTests: XCTestCase {
sut = ImageCache(
fileManager: fileManager,
urlSession: mockSession
urlDataFetcher: mockFetcher
)
}
@@ -74,7 +74,7 @@ final class ImageCacheTests: XCTestCase {
}
sut = nil
mockSession = nil
mockFetcher = nil
testDirectory = nil
fileManager = nil
@@ -103,21 +103,21 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When
try await sut.cacheImage(from: url, for: plantID)
// Then
XCTAssertEqual(mockSession.downloadCallCount, 1)
XCTAssertEqual(mockSession.lastRequestedURL, url)
XCTAssertEqual(mockFetcher.downloadCallCount, 1)
XCTAssertEqual(mockFetcher.lastRequestedURL, url)
}
func testCacheImage_WhenDownloadFails_ThrowsDownloadFailed() async {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.errorToThrow = URLError(.notConnectedToInternet)
mockFetcher.errorToThrow = URLError(.notConnectedToInternet)
// When/Then
do {
@@ -139,7 +139,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createInvalidImageData()
mockFetcher.dataToReturn = createInvalidImageData()
// When/Then
do {
@@ -156,14 +156,14 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When - Cache twice
try await sut.cacheImage(from: url, for: plantID)
try await sut.cacheImage(from: url, for: plantID)
// Then - Should only download once
XCTAssertEqual(mockSession.downloadCallCount, 1)
XCTAssertEqual(mockFetcher.downloadCallCount, 1)
}
// MARK: - getCachedImage() Tests
@@ -184,7 +184,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache the image first
try await sut.cacheImage(from: url, for: plantID)
@@ -201,7 +201,7 @@ final class ImageCacheTests: XCTestCase {
let plantID1 = UUID()
let plantID2 = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache for plant 1
try await sut.cacheImage(from: url, for: plantID1)
@@ -218,7 +218,7 @@ final class ImageCacheTests: XCTestCase {
let plantID = UUID()
let url1 = URL(string: "https://example.com/plant1.jpg")!
let url2 = URL(string: "https://example.com/plant2.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache url1
try await sut.cacheImage(from: url1, for: plantID)
@@ -235,7 +235,7 @@ final class ImageCacheTests: XCTestCase {
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
let urlHash = url.absoluteString.sha256Hash
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache the image
try await sut.cacheImage(from: url, for: plantID)
@@ -255,7 +255,7 @@ final class ImageCacheTests: XCTestCase {
let plantID2 = UUID()
let url1 = URL(string: "https://example.com/plant1.jpg")!
let url2 = URL(string: "https://example.com/plant2.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache images for both plants
try await sut.cacheImage(from: url1, for: plantID1)
@@ -267,9 +267,9 @@ final class ImageCacheTests: XCTestCase {
// Then - Plant 1's image should be gone, plant 2's should still exist
// Note: Due to memory cache clearing behavior, we need to re-cache
// This test verifies the disk cache behavior
mockSession.downloadCallCount = 0
mockFetcher.downloadCallCount = 0
try await sut.cacheImage(from: url1, for: plantID1)
XCTAssertEqual(mockSession.downloadCallCount, 1) // Had to redownload
XCTAssertEqual(mockFetcher.downloadCallCount, 1) // Had to redownload
}
func testClearCache_ForPlant_RemovesMultipleImages() async throws {
@@ -277,7 +277,7 @@ final class ImageCacheTests: XCTestCase {
let plantID = UUID()
let url1 = URL(string: "https://example.com/plant1.jpg")!
let url2 = URL(string: "https://example.com/plant2.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache multiple images for the same plant
try await sut.cacheImage(from: url1, for: plantID)
@@ -301,7 +301,7 @@ final class ImageCacheTests: XCTestCase {
let plantID2 = UUID()
let url1 = URL(string: "https://example.com/plant1.jpg")!
let url2 = URL(string: "https://example.com/plant2.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache images for multiple plants
try await sut.cacheImage(from: url1, for: plantID1)
@@ -333,7 +333,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache an image
try await sut.cacheImage(from: url, for: plantID)
@@ -349,7 +349,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache an image
try await sut.cacheImage(from: url, for: plantID)
@@ -370,7 +370,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// Cache the image
try await sut.cacheImage(from: url, for: plantID)
@@ -435,7 +435,7 @@ final class ImageCacheTests: XCTestCase {
let plantID = UUID()
let longPath = String(repeating: "path/", count: 50) + "image.jpg"
let url = URL(string: "https://example.com/\(longPath)")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When/Then - Should not throw
try await sut.cacheImage(from: url, for: plantID)
@@ -445,7 +445,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let url = URL(string: "https://example.com/plant%20image%231.jpg")!
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When/Then - Should not throw
try await sut.cacheImage(from: url, for: plantID)
@@ -455,7 +455,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let urls = (0..<5).map { URL(string: "https://example.com/plant\($0).jpg")! }
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When - Cache all images
for url in urls {
@@ -473,7 +473,7 @@ final class ImageCacheTests: XCTestCase {
// Given
let plantID = UUID()
let urls = (0..<10).map { URL(string: "https://example.com/plant\($0).jpg")! }
mockSession.dataToReturn = createTestImageData()
mockFetcher.dataToReturn = createTestImageData()
// When - Cache concurrently
await withTaskGroup(of: Void.self) { group in