Complete rewrite of unit test suite using TDD methodology: Planning Engine Tests: - GameDAGRouterTests: Beam search, anchor games, transitions - ItineraryBuilderTests: Stop connection, validators, EV enrichment - RouteFiltersTests: Region, time window, scoring filters - ScenarioA/B/C/D PlannerTests: All planning scenarios - TravelEstimatorTests: Distance, duration, travel days - TripPlanningEngineTests: Orchestration, caching, preferences Domain Model Tests: - AchievementDefinitionsTests, AnySportTests, DivisionTests - GameTests, ProgressTests, RegionTests, StadiumTests - TeamTests, TravelSegmentTests, TripTests, TripPollTests - TripPreferencesTests, TripStopTests, SportTests Service Tests: - FreeScoreAPITests, RouteDescriptionGeneratorTests - SuggestedTripsGeneratorTests Export Tests: - ShareableContentTests (card types, themes, dimensions) Bug fixes discovered through TDD: - ShareCardDimensions: mapSnapshotSize exceeded available width (960x480) - ScenarioBPlanner: Added anchor game validation filter All tests include: - Specification tests (expected behavior) - Invariant tests (properties that must always hold) - Edge case tests (boundary conditions) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
103 lines
3.5 KiB
Swift
103 lines
3.5 KiB
Swift
//
|
|
// MockServices.swift
|
|
// SportsTimeTests
|
|
//
|
|
// Mock implementations of services for testing. These mocks allow tests
|
|
// to control service behavior and verify interactions.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreLocation
|
|
@testable import SportsTime
|
|
|
|
// MARK: - Mock Data Provider
|
|
|
|
/// Mock data provider for testing components that depend on game/stadium/team data.
|
|
@MainActor
|
|
final class MockDataProvider {
|
|
var games: [Game] = []
|
|
var stadiums: [String: Stadium] = [:]
|
|
var teams: [String: Team] = [:]
|
|
|
|
var shouldFail = false
|
|
var failureError: Error = NSError(domain: "MockError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Mock failure"])
|
|
|
|
func configure(games: [Game], stadiums: [Stadium], teams: [Team]) {
|
|
self.games = games
|
|
self.stadiums = Dictionary(uniqueKeysWithValues: stadiums.map { ($0.id, $0) })
|
|
self.teams = Dictionary(uniqueKeysWithValues: teams.map { ($0.id, $0) })
|
|
}
|
|
|
|
func stadium(for id: String) -> Stadium? {
|
|
stadiums[id]
|
|
}
|
|
|
|
func team(for id: String) -> Team? {
|
|
teams[id]
|
|
}
|
|
|
|
func filterGames(sports: Set<Sport>, startDate: Date, endDate: Date) throws -> [Game] {
|
|
if shouldFail { throw failureError }
|
|
return games.filter { game in
|
|
sports.contains(game.sport) &&
|
|
game.dateTime >= startDate &&
|
|
game.dateTime <= endDate
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Mock Location Service
|
|
|
|
/// Mock location service for testing distance and travel time calculations.
|
|
@MainActor
|
|
final class MockLocationService {
|
|
var stubbedDistances: [String: Double] = [:] // "from_to" -> meters
|
|
var stubbedTravelTimes: [String: TimeInterval] = [:] // "from_to" -> seconds
|
|
var defaultDistanceMeters: Double = 100_000 // ~62 miles
|
|
var defaultTravelTimeSeconds: TimeInterval = 3600 // 1 hour
|
|
|
|
var shouldFail = false
|
|
var failureError: Error = NSError(domain: "LocationError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Location unavailable"])
|
|
|
|
var calculateDistanceCalls: [(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D)] = []
|
|
var calculateTravelTimeCalls: [(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D)] = []
|
|
|
|
func stubDistance(from: String, to: String, meters: Double) {
|
|
stubbedDistances["\(from)_\(to)"] = meters
|
|
}
|
|
|
|
func stubTravelTime(from: String, to: String, seconds: TimeInterval) {
|
|
stubbedTravelTimes["\(from)_\(to)"] = seconds
|
|
}
|
|
|
|
func calculateDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) async throws -> Double {
|
|
calculateDistanceCalls.append((from: from, to: to))
|
|
if shouldFail { throw failureError }
|
|
return defaultDistanceMeters
|
|
}
|
|
|
|
func calculateTravelTime(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) async throws -> TimeInterval {
|
|
calculateTravelTimeCalls.append((from: from, to: to))
|
|
if shouldFail { throw failureError }
|
|
return defaultTravelTimeSeconds
|
|
}
|
|
}
|
|
|
|
// MARK: - Mock Route Service
|
|
|
|
/// Mock route service for testing route optimization.
|
|
@MainActor
|
|
final class MockRouteService {
|
|
var shouldFail = false
|
|
var failureError: Error = NSError(domain: "RouteError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Route unavailable"])
|
|
|
|
var optimizeRouteCalls: [[CLLocationCoordinate2D]] = []
|
|
var stubbedRoute: [Int]? // Indices in order
|
|
|
|
func optimizeRoute(waypoints: [CLLocationCoordinate2D]) async throws -> [Int] {
|
|
optimizeRouteCalls.append(waypoints)
|
|
if shouldFail { throw failureError }
|
|
return stubbedRoute ?? Array(0..<waypoints.count)
|
|
}
|
|
}
|