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

@@ -23,6 +23,26 @@
import Foundation
import CoreLocation
/// DAG-based route finder for multi-game trips.
///
/// Finds time-respecting paths through a graph of games with driving feasibility
/// and multi-dimensional diversity selection.
///
/// - Expected Behavior:
/// - Empty games empty result
/// - Single game [[game]] if no anchors or game is anchor
/// - Two games [[sorted]] if transition feasible and anchors satisfied
/// - All routes are chronologically ordered
/// - All routes respect driving constraints
/// - Anchor games MUST appear in all returned routes
/// - allowRepeatCities=false no city appears twice in same route
/// - Returns diverse set spanning games, cities, miles, and duration
///
/// - Invariants:
/// - All returned routes have games in chronological order
/// - All games in a route have feasible transitions between consecutive games
/// - Maximum 75 routes returned (maxOptions)
/// - Routes with anchor games include ALL anchor games
enum GameDAGRouter {
// MARK: - Configuration
@@ -104,6 +124,16 @@ enum GameDAGRouter {
///
/// - Returns: Array of diverse route options
///
/// - Expected Behavior:
/// - Empty games empty result
/// - Single game with no anchors [[game]]
/// - Single game that is the anchor [[game]]
/// - Single game not matching anchor []
/// - Two feasible games [[game1, game2]] (chronological order)
/// - Two infeasible games (no anchors) [[game1], [game2]] (separate routes)
/// - Two infeasible games (with anchors) [] (can't satisfy)
/// - anchorGameIds must be subset of any returned route
/// - allowRepeatCities=false filters routes with duplicate cities
static func findRoutes(
games: [Game],
stadiums: [String: Stadium],