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

@@ -32,6 +32,23 @@ import CoreLocation
/// Scenario B: Selected games planning
/// Input: selected_games, date_range (or trip_duration), optional must_stop
/// Output: Itinerary options connecting all selected games with possible bonus games
///
/// - Expected Behavior:
/// - No selected games returns .failure with .noValidRoutes
/// - No valid date ranges returns .failure with .missingDateRange
/// - GameFirst mode uses sliding windows with gameFirstTripDuration
/// - Explicit dateRange (non-gameFirst) uses that range directly
/// - All anchor games MUST appear in every valid route
/// - Uses arrivalBeforeGameStart validator for travel segments
/// - No routes satisfy constraints .failure with .constraintsUnsatisfiable
/// - Success returns sorted itineraries based on leisureLevel
///
/// - Invariants:
/// - Every returned route contains ALL selected (anchor) games
/// - Anchor games cannot be dropped for geographic convenience
/// - Sliding window always spans from first to last selected game
/// - Date ranges always contain all selected game dates
///
final class ScenarioBPlanner: ScenarioPlanner {
// MARK: - ScenarioPlanner Protocol
@@ -142,6 +159,14 @@ final class ScenarioBPlanner: ScenarioPlanner {
// Deduplicate
validRoutes = deduplicateRoutes(validRoutes)
// Filter routes to only include those containing ALL anchor games
// This is critical: regional search uses filtered anchors, so we must
// verify each route satisfies the original anchor constraint
validRoutes = validRoutes.filter { route in
let routeGameIds = Set(route.map { $0.id })
return anchorGameIds.isSubset(of: routeGameIds)
}
// Build itineraries for each valid route
for routeGames in validRoutes {
let stops = buildStops(from: routeGames, stadiums: request.stadiums)