refactor(tests): TDD rewrite of all unit tests with spec documentation
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>
This commit is contained in:
102
SportsTimeTests/Helpers/MockServices.swift
Normal file
102
SportsTimeTests/Helpers/MockServices.swift
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user