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:
Trey t
2026-01-16 14:07:41 -06:00
parent 035dd6f5de
commit 8162b4a029
102 changed files with 13409 additions and 9883 deletions

View File

@@ -9,6 +9,21 @@
import Foundation
import CoreLocation
/// Travel estimation utilities for calculating distances and driving times.
///
/// Uses Haversine formula for coordinate-based distance with a road routing factor,
/// or fallback distances when coordinates are unavailable.
///
/// - Constants:
/// - averageSpeedMph: 60 mph
/// - roadRoutingFactor: 1.3 (accounts for roads vs straight-line distance)
/// - fallbackDistanceMiles: 300 miles (when coordinates unavailable)
///
/// - Invariants:
/// - All distance calculations are symmetric: distance(A,B) == distance(B,A)
/// - Road distance >= straight-line distance (roadRoutingFactor >= 1.0)
/// - Travel duration is always distance / averageSpeedMph
/// - Segments exceeding 5x maxDailyDrivingHours return nil (unreachable)
enum TravelEstimator {
// MARK: - Constants
@@ -19,8 +34,21 @@ enum TravelEstimator {
// MARK: - Travel Estimation
/// Estimates a travel segment between two stops.
/// Returns nil if trip exceeds maximum allowed driving hours (2 days worth).
/// Estimates a travel segment between two ItineraryStops.
///
/// - Parameters:
/// - from: Origin stop
/// - to: Destination stop
/// - constraints: Driving constraints (drivers, hours per day)
/// - Returns: TravelSegment or nil if unreachable
///
/// - Expected Behavior:
/// - With valid coordinates calculates distance using Haversine * roadRoutingFactor
/// - Missing coordinates uses fallback distance (300 miles)
/// - Same city (no coords) 0 distance, 0 duration
/// - Driving hours > 5x maxDailyDrivingHours returns nil
/// - Duration = distance / 60 mph
/// - Result distance in meters, duration in seconds
static func estimate(
from: ItineraryStop,
to: ItineraryStop,
@@ -47,7 +75,19 @@ enum TravelEstimator {
}
/// Estimates a travel segment between two LocationInputs.
/// Returns nil if coordinates are missing or if trip exceeds maximum allowed driving hours (2 days worth).
///
/// - Parameters:
/// - from: Origin location
/// - to: Destination location
/// - constraints: Driving constraints
/// - Returns: TravelSegment or nil if unreachable/invalid
///
/// - Expected Behavior:
/// - Missing from.coordinate returns nil
/// - Missing to.coordinate returns nil
/// - Valid coordinates calculates distance using Haversine * roadRoutingFactor
/// - Driving hours > 5x maxDailyDrivingHours returns nil
/// - Duration = distance / 60 mph
static func estimate(
from: LocationInput,
to: LocationInput,
@@ -81,8 +121,18 @@ enum TravelEstimator {
// MARK: - Distance Calculations
/// Calculates distance in miles between two stops.
/// Uses Haversine formula if coordinates available, fallback otherwise.
/// Calculates road distance in miles between two ItineraryStops.
///
/// - Parameters:
/// - from: Origin stop
/// - to: Destination stop
/// - Returns: Distance in miles
///
/// - Expected Behavior:
/// - Both have coordinates Haversine distance * 1.3
/// - Either missing coordinates fallback distance
/// - Same city (no coords) 0 miles
/// - Different cities (no coords) 300 miles
static func calculateDistanceMiles(
from: ItineraryStop,
to: ItineraryStop
@@ -94,7 +144,18 @@ enum TravelEstimator {
return estimateFallbackDistance(from: from, to: to)
}
/// Calculates distance in miles between two coordinates using Haversine.
/// Calculates straight-line distance in miles using Haversine formula.
///
/// - Parameters:
/// - from: Origin coordinate
/// - to: Destination coordinate
/// - Returns: Straight-line distance in miles
///
/// - Expected Behavior:
/// - Same point 0 miles
/// - NYC to Boston ~190 miles (validates formula accuracy)
/// - Symmetric: distance(A,B) == distance(B,A)
/// - Uses Earth radius of 3958.8 miles
static func haversineDistanceMiles(
from: CLLocationCoordinate2D,
to: CLLocationCoordinate2D
@@ -114,7 +175,18 @@ enum TravelEstimator {
return earthRadiusMiles * c
}
/// Calculates distance in meters between two coordinates using Haversine.
/// Calculates straight-line distance in meters using Haversine formula.
///
/// - Parameters:
/// - from: Origin coordinate
/// - to: Destination coordinate
/// - Returns: Straight-line distance in meters
///
/// - Expected Behavior:
/// - Same point 0 meters
/// - Symmetric: distance(A,B) == distance(B,A)
/// - Uses Earth radius of 6,371,000 meters
/// - haversineDistanceMeters / 1609.34 haversineDistanceMiles
static func haversineDistanceMeters(
from: CLLocationCoordinate2D,
to: CLLocationCoordinate2D
@@ -135,6 +207,15 @@ enum TravelEstimator {
}
/// Fallback distance when coordinates aren't available.
///
/// - Parameters:
/// - from: Origin stop
/// - to: Destination stop
/// - Returns: Estimated distance in miles
///
/// - Expected Behavior:
/// - Same city 0 miles
/// - Different cities 300 miles (fallback constant)
static func estimateFallbackDistance(
from: ItineraryStop,
to: ItineraryStop
@@ -147,7 +228,20 @@ enum TravelEstimator {
// MARK: - Travel Days
/// Calculates which calendar days travel spans.
/// Calculates which calendar days a driving segment spans.
///
/// - Parameters:
/// - departure: Departure date/time
/// - drivingHours: Total driving hours
/// - Returns: Array of calendar days (start of day) that travel spans
///
/// - Expected Behavior:
/// - 0 hours [departure day]
/// - 1-8 hours [departure day] (1 day)
/// - 8.01-16 hours [departure day, next day] (2 days)
/// - 16.01-24 hours [departure day, +1, +2] (3 days)
/// - All dates are normalized to start of day (midnight)
/// - Assumes 8 driving hours per day max
static func calculateTravelDays(
departure: Date,
drivingHours: Double