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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user