// // ScenarioAPlannerTests.swift // SportsTimeTests // // Phase 4: ScenarioAPlanner Tests // Scenario A: User provides dates, planner finds games. // import Testing import CoreLocation @testable import SportsTime @Suite("ScenarioAPlanner Tests") struct ScenarioAPlannerTests { // MARK: - Test Fixtures private let calendar = Calendar.current private let planner = ScenarioAPlanner() /// Creates a date with specific year/month/day/hour private func makeDate(year: Int = 2026, month: Int = 6, day: Int, hour: Int = 19) -> Date { var components = DateComponents() components.year = year components.month = month components.day = day components.hour = hour components.minute = 0 return calendar.date(from: components)! } /// Creates a stadium at a known location private func makeStadium( id: String = "stadium_test_\(UUID().uuidString)", city: String, lat: Double, lon: Double, sport: Sport = .mlb ) -> Stadium { Stadium( id: id, name: "\(city) Stadium", city: city, state: "ST", latitude: lat, longitude: lon, capacity: 40000, sport: sport ) } /// Creates a game at a stadium private func makeGame( id: String = "game_test_\(UUID().uuidString)", stadiumId: String, homeTeamId: String = "team_test_\(UUID().uuidString)", awayTeamId: String = "team_test_\(UUID().uuidString)", dateTime: Date, sport: Sport = .mlb ) -> Game { Game( id: id, homeTeamId: homeTeamId, awayTeamId: awayTeamId, stadiumId: stadiumId, dateTime: dateTime, sport: sport, season: "2026" ) } /// Creates a PlanningRequest with the given parameters private func makePlanningRequest( startDate: Date, endDate: Date, games: [Game], stadiums: [String: Stadium], teams: [String: Team] = [:], allowRepeatCities: Bool = true, numberOfDrivers: Int = 1, maxDrivingHoursPerDriver: Double = 8.0, mustStopLocations: [LocationInput] = [] ) -> PlanningRequest { let preferences = TripPreferences( planningMode: .dateRange, sports: [.mlb], startDate: startDate, endDate: endDate, leisureLevel: .moderate, mustStopLocations: mustStopLocations, numberOfDrivers: numberOfDrivers, maxDrivingHoursPerDriver: maxDrivingHoursPerDriver, allowRepeatCities: allowRepeatCities ) return PlanningRequest( preferences: preferences, availableGames: games, teams: teams, stadiums: stadiums ) } // MARK: - 4A: Valid Inputs @Test("4.1 - Valid date range returns games in range") func test_planByDates_ValidDateRange_ReturnsGamesInRange() { // Setup: 3 games across nearby cities over 5 days let chicagoId = "stadium_chicago_\(UUID().uuidString)" let milwaukeeId = "stadium_milwaukee_\(UUID().uuidString)" let detroitId = "stadium_detroit_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let milwaukee = makeStadium(id: milwaukeeId, city: "Milwaukee", lat: 43.0389, lon: -87.9065) let detroit = makeStadium(id: detroitId, city: "Detroit", lat: 42.3314, lon: -83.0458) let stadiums = [chicagoId: chicago, milwaukeeId: milwaukee, detroitId: detroit] let game1 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) let game2 = makeGame(stadiumId: milwaukeeId, dateTime: makeDate(day: 7, hour: 19)) let game3 = makeGame(stadiumId: detroitId, dateTime: makeDate(day: 9, hour: 19)) let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [game1, game2, game3], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify #expect(result.isSuccess, "Should succeed with valid date range and games") #expect(!result.options.isEmpty, "Should return at least one itinerary option") // All returned games should be within date range for option in result.options { #expect(option.stops.allSatisfy { !$0.games.isEmpty || $0.city.isEmpty == false }, "Each option should have valid stops") } } @Test("4.2 - Single day range returns games on that day") func test_planByDates_SingleDayRange_ReturnsGamesOnThatDay() { // Setup: Multiple games on a single day at the same stadium let stadiumId = "stadium_test_\(UUID().uuidString)" let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [stadiumId: stadium] // Doubleheader on June 5 let game1 = makeGame(stadiumId: stadiumId, dateTime: makeDate(day: 5, hour: 13)) let game2 = makeGame(stadiumId: stadiumId, dateTime: makeDate(day: 5, hour: 19)) // Game outside the range let gameOutside = makeGame(stadiumId: stadiumId, dateTime: makeDate(day: 6, hour: 19)) let request = makePlanningRequest( startDate: makeDate(day: 5, hour: 0), endDate: makeDate(day: 5, hour: 23), games: [game1, game2, gameOutside], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify #expect(result.isSuccess, "Should succeed for single day range") #expect(!result.options.isEmpty, "Should return at least one option") // Games in options should only be from June 5 if let firstOption = result.options.first { let gameIds = Set(firstOption.stops.flatMap { $0.games }) #expect(gameIds.contains(game1.id) || gameIds.contains(game2.id), "Should include games from the single day") #expect(!gameIds.contains(gameOutside.id), "Should not include games outside the date range") } } @Test("4.3 - Multi-week range returns multiple games") func test_planByDates_MultiWeekRange_ReturnsMultipleGames() { // Setup: Games spread across 3 weeks in nearby cities let chicagoId = "stadium_chicago_\(UUID().uuidString)" let milwaukeeId = "stadium_milwaukee_\(UUID().uuidString)" let detroitId = "stadium_detroit_\(UUID().uuidString)" let clevelandId = "stadium_cleveland_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let milwaukee = makeStadium(id: milwaukeeId, city: "Milwaukee", lat: 43.0389, lon: -87.9065) let detroit = makeStadium(id: detroitId, city: "Detroit", lat: 42.3314, lon: -83.0458) let cleveland = makeStadium(id: clevelandId, city: "Cleveland", lat: 41.4993, lon: -81.6944) let stadiums = [ chicagoId: chicago, milwaukeeId: milwaukee, detroitId: detroit, clevelandId: cleveland ] // Games across 3 weeks let game1 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 1, hour: 19)) let game2 = makeGame(stadiumId: milwaukeeId, dateTime: makeDate(day: 5, hour: 19)) let game3 = makeGame(stadiumId: detroitId, dateTime: makeDate(day: 10, hour: 19)) let game4 = makeGame(stadiumId: clevelandId, dateTime: makeDate(day: 15, hour: 19)) let game5 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 20, hour: 19)) let request = makePlanningRequest( startDate: makeDate(day: 1, hour: 0), endDate: makeDate(day: 21, hour: 23), games: [game1, game2, game3, game4, game5], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify #expect(result.isSuccess, "Should succeed for multi-week range") #expect(!result.options.isEmpty, "Should return itinerary options") // Should have options with multiple games let optionWithMultipleGames = result.options.first { $0.totalGames >= 2 } #expect(optionWithMultipleGames != nil, "Should have options covering multiple games") } // MARK: - 4B: Edge Cases @Test("4.4 - No games in range returns failure") func test_planByDates_NoGamesInRange_ThrowsError() { // Setup: Games outside the requested date range let stadiumId = "stadium_test_\(UUID().uuidString)" let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [stadiumId: stadium] // Games in July, but request is for June let gameOutside1 = makeGame(stadiumId: stadiumId, dateTime: makeDate(month: 7, day: 10, hour: 19)) let gameOutside2 = makeGame(stadiumId: stadiumId, dateTime: makeDate(month: 7, day: 15, hour: 19)) let request = makePlanningRequest( startDate: makeDate(month: 6, day: 1, hour: 0), endDate: makeDate(month: 6, day: 30, hour: 23), games: [gameOutside1, gameOutside2], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify: Should fail with noGamesInRange #expect(!result.isSuccess, "Should fail when no games in range") #expect(result.failure?.reason == .noGamesInRange, "Should return noGamesInRange failure reason") } @Test("4.5 - End date before start date returns failure") func test_planByDates_EndDateBeforeStartDate_ThrowsError() { // Setup: Invalid date range where end < start let stadiumId = "stadium_test_\(UUID().uuidString)" let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [stadiumId: stadium] let game = makeGame(stadiumId: stadiumId, dateTime: makeDate(day: 5, hour: 19)) // End date before start date let request = makePlanningRequest( startDate: makeDate(day: 15, hour: 0), // June 15 endDate: makeDate(day: 5, hour: 23), // June 5 (before start) games: [game], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify: Should fail with missingDateRange (invalid range) #expect(!result.isSuccess, "Should fail when end date is before start date") #expect(result.failure?.reason == .missingDateRange, "Should return missingDateRange for invalid date range") } @Test("4.6 - Single game in range returns single game route") func test_planByDates_SingleGameInRange_ReturnsSingleGameRoute() { // Setup: Only one game in the date range let stadiumId = "stadium_test_\(UUID().uuidString)" let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [stadiumId: stadium] let game = makeGame(stadiumId: stadiumId, dateTime: makeDate(day: 10, hour: 19)) let request = makePlanningRequest( startDate: makeDate(day: 5, hour: 0), endDate: makeDate(day: 15, hour: 23), games: [game], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify #expect(result.isSuccess, "Should succeed with single game") #expect(!result.options.isEmpty, "Should return at least one option") if let firstOption = result.options.first { #expect(firstOption.totalGames == 1, "Should have exactly 1 game") #expect(firstOption.stops.count == 1, "Should have exactly 1 stop") #expect(firstOption.stops.first?.games.contains(game.id) == true, "Stop should contain the single game") } } @Test("4.7 - Max games in range handles gracefully", .timeLimit(.minutes(5))) func test_planByDates_MaxGamesInRange_HandlesGracefully() { // Setup: Generate 10K games using fixture generator let config = FixtureGenerator.Configuration( seed: 42, gameCount: 10000, stadiumCount: 30, teamCount: 60, dateRange: makeDate(day: 1, hour: 0)...makeDate(month: 9, day: 30, hour: 23), geographicSpread: .nationwide ) let data = FixtureGenerator.generate(with: config) let request = makePlanningRequest( startDate: makeDate(day: 1, hour: 0), endDate: makeDate(month: 9, day: 30, hour: 23), games: data.games, stadiums: data.stadiumsById ) // Execute with timing let startTime = Date() let result = planner.plan(request: request) let elapsed = Date().timeIntervalSince(startTime) // Verify: Should complete without crash/hang #expect(elapsed < TestConstants.performanceTimeout, "Should complete within performance timeout") // Should produce some result (success or failure is acceptable) // The key is that it doesn't crash or hang if result.isSuccess { #expect(!result.options.isEmpty, "If success, should have options") } // Failure is also acceptable for extreme scale (e.g., no valid routes) } // MARK: - 4C: Integration with DAG @Test("4.8 - Uses DAG router for routing") func test_planByDates_UsesDAGRouterForRouting() { // Setup: Games that require DAG routing logic // Create games in multiple cities with feasible transitions let chicagoId = "stadium_chicago_\(UUID().uuidString)" let milwaukeeId = "stadium_milwaukee_\(UUID().uuidString)" let detroitId = "stadium_detroit_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let milwaukee = makeStadium(id: milwaukeeId, city: "Milwaukee", lat: 43.0389, lon: -87.9065) let detroit = makeStadium(id: detroitId, city: "Detroit", lat: 42.3314, lon: -83.0458) let stadiums = [chicagoId: chicago, milwaukeeId: milwaukee, detroitId: detroit] // Games that can form a sensible route: Chicago → Milwaukee → Detroit let game1 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) let game2 = makeGame(stadiumId: milwaukeeId, dateTime: makeDate(day: 7, hour: 19)) let game3 = makeGame(stadiumId: detroitId, dateTime: makeDate(day: 9, hour: 19)) let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [game1, game2, game3], stadiums: stadiums ) // Execute let result = planner.plan(request: request) // Verify: DAG router should produce routes #expect(result.isSuccess, "Should succeed with routable games") #expect(!result.options.isEmpty, "Should produce routes") // Verify routes are in chronological order (DAG property) for option in result.options { // Stops should be in order that respects game times var previousGameDate: Date? for stop in option.stops { if let firstGameId = stop.games.first, let game = [game1, game2, game3].first(where: { $0.id == firstGameId }) { if let prev = previousGameDate { #expect(game.startTime >= prev, "Games should be in chronological order (DAG property)") } previousGameDate = game.startTime } } } } @Test("4.9 - Respects driver constraints") func test_planByDates_RespectsDriverConstraints() { // Setup: Games that would require excessive daily driving if constraints are loose let nycId = "stadium_nyc_\(UUID().uuidString)" let chicagoId = "stadium_chicago_\(UUID().uuidString)" // NYC to Chicago is ~790 miles (~13 hours of driving) let nyc = makeStadium(id: nycId, city: "New York", lat: 40.7128, lon: -73.9352) let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [nycId: nyc, chicagoId: chicago] // Games on consecutive days - can't drive 790 miles in 8 hours (single driver) let game1 = makeGame(stadiumId: nycId, dateTime: makeDate(day: 5, hour: 14)) let game2 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 6, hour: 19)) // Test with strict constraints (1 driver, 8 hours max) let strictRequest = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 7, hour: 23), games: [game1, game2], stadiums: stadiums, numberOfDrivers: 1, maxDrivingHoursPerDriver: 8.0 ) let strictResult = planner.plan(request: strictRequest) // With strict constraints, should NOT have a route with both games on consecutive days if strictResult.isSuccess { let hasConsecutiveDayRoute = strictResult.options.contains { option in option.totalGames == 2 && option.stops.count == 2 } // If there's a 2-game route, verify it has adequate travel time if hasConsecutiveDayRoute, let twoGameOption = strictResult.options.first(where: { $0.totalGames == 2 }) { // With only 1 day between games, ~13 hours of driving is too much for 8hr/day limit // The route should either not exist or have adequate travel days let totalHours = twoGameOption.totalDrivingHours let daysAvailable = 1.0 // Only 1 day between games let hoursPerDay = totalHours / daysAvailable // This assertion is soft - the router may reject this route entirely #expect(hoursPerDay <= 8.0 || !hasConsecutiveDayRoute, "Route should respect daily driving limits") } } // Test with relaxed constraints (2 drivers = 16 hours max per day) let relaxedRequest = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 7, hour: 23), games: [game1, game2], stadiums: stadiums, numberOfDrivers: 2, maxDrivingHoursPerDriver: 8.0 ) let relaxedResult = planner.plan(request: relaxedRequest) // With 2 drivers (16 hours/day), the trip becomes more feasible // Note: 790 miles at 60mph is ~13 hours, which fits in 16 hours if relaxedResult.isSuccess { // Should have more routing options with relaxed constraints #expect(relaxedResult.options.count >= 1, "Should have options with relaxed driver constraints") } } // MARK: - 4D: Must-Stop Filtering (Issues #4 & #8) @Test("4.10 - Must-stop filters to games in that city") func test_planByDates_MustStop_FiltersToGamesInCity() { // Setup: Games in Chicago, Milwaukee, Detroit // Must-stop = Chicago → should only return Chicago games let chicagoId = "stadium_chicago_\(UUID().uuidString)" let milwaukeeId = "stadium_milwaukee_\(UUID().uuidString)" let detroitId = "stadium_detroit_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let milwaukee = makeStadium(id: milwaukeeId, city: "Milwaukee", lat: 43.0389, lon: -87.9065) let detroit = makeStadium(id: detroitId, city: "Detroit", lat: 42.3314, lon: -83.0458) let stadiums = [chicagoId: chicago, milwaukeeId: milwaukee, detroitId: detroit] let chicagoGame = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) let milwaukeeGame = makeGame(stadiumId: milwaukeeId, dateTime: makeDate(day: 7, hour: 19)) let detroitGame = makeGame(stadiumId: detroitId, dateTime: makeDate(day: 9, hour: 19)) let mustStop = LocationInput(name: "Chicago") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [chicagoGame, milwaukeeGame, detroitGame], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: Should succeed and ONLY include Chicago games #expect(result.isSuccess, "Should succeed with must-stop in Chicago") #expect(!result.options.isEmpty, "Should return at least one option") // All games in result should be in Chicago only for option in result.options { let allGameIds = Set(option.stops.flatMap { $0.games }) #expect(allGameIds.contains(chicagoGame.id), "Should include Chicago game") #expect(!allGameIds.contains(milwaukeeGame.id), "Should NOT include Milwaukee game") #expect(!allGameIds.contains(detroitGame.id), "Should NOT include Detroit game") } } @Test("4.11 - Must-stop with no matching games returns failure") func test_planByDates_MustStop_NoMatchingGames_ReturnsFailure() { // Setup: Games only in Milwaukee and Detroit // Must-stop = Chicago → no games there, should fail let milwaukeeId = "stadium_milwaukee_\(UUID().uuidString)" let detroitId = "stadium_detroit_\(UUID().uuidString)" let milwaukee = makeStadium(id: milwaukeeId, city: "Milwaukee", lat: 43.0389, lon: -87.9065) let detroit = makeStadium(id: detroitId, city: "Detroit", lat: 42.3314, lon: -83.0458) let stadiums = [milwaukeeId: milwaukee, detroitId: detroit] let milwaukeeGame = makeGame(stadiumId: milwaukeeId, dateTime: makeDate(day: 7, hour: 19)) let detroitGame = makeGame(stadiumId: detroitId, dateTime: makeDate(day: 9, hour: 19)) let mustStop = LocationInput(name: "Chicago") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [milwaukeeGame, detroitGame], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: Should fail with noGamesInRange (no home games in Chicago) #expect(!result.isSuccess, "Should fail when no games in must-stop city") #expect(result.failure?.reason == .noGamesInRange, "Should return noGamesInRange failure") } @Test("4.12 - Must-stop only returns HOME games (Issue #8)") func test_planByDates_MustStop_OnlyReturnsHomeGames() { // Setup: Cubs home game in Chicago + Cubs away game in Milwaukee (playing at Milwaukee) // Must-stop = Chicago → should ONLY return the Chicago home game // This tests Issue #8: "Must stop needs to be home team" let chicagoStadiumId = "stadium_chicago_\(UUID().uuidString)" let milwaukeeStadiumId = "stadium_milwaukee_\(UUID().uuidString)" let cubsTeamId = "team_cubs_\(UUID().uuidString)" let brewersTeamId = "team_brewers_\(UUID().uuidString)" let wrigleyField = makeStadium(id: chicagoStadiumId, city: "Chicago", lat: 41.9484, lon: -87.6553) let millerPark = makeStadium(id: milwaukeeStadiumId, city: "Milwaukee", lat: 43.0280, lon: -87.9712) let stadiums = [chicagoStadiumId: wrigleyField, milwaukeeStadiumId: millerPark] // Cubs HOME game at Wrigley (Chicago) let cubsHomeGame = makeGame( stadiumId: chicagoStadiumId, homeTeamId: cubsTeamId, awayTeamId: brewersTeamId, dateTime: makeDate(day: 5, hour: 19) ) // Cubs AWAY game at Miller Park (Milwaukee) - Cubs are playing but NOT at home let cubsAwayGame = makeGame( stadiumId: milwaukeeStadiumId, homeTeamId: brewersTeamId, awayTeamId: cubsTeamId, dateTime: makeDate(day: 7, hour: 19) ) let mustStop = LocationInput(name: "Chicago") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [cubsHomeGame, cubsAwayGame], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: Should succeed with ONLY the Chicago home game #expect(result.isSuccess, "Should succeed with Chicago home game") #expect(!result.options.isEmpty, "Should return at least one option") // The away game in Milwaukee should NOT be included even though Cubs are playing for option in result.options { let allGameIds = Set(option.stops.flatMap { $0.games }) #expect(allGameIds.contains(cubsHomeGame.id), "Should include Cubs HOME game in Chicago") #expect(!allGameIds.contains(cubsAwayGame.id), "Should NOT include Cubs AWAY game in Milwaukee") } } @Test("4.13 - Must-stop with partial city name match works") func test_planByDates_MustStop_PartialCityMatch_Works() { // Setup: User types "Chicago" but stadium city is "Chicago, IL" // Should still match via contains let chicagoId = "stadium_chicago_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [chicagoId: chicago] let game = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) // User might type partial name let mustStop = LocationInput(name: "Chicago, IL") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [game], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: Should still find Chicago games with partial match #expect(result.isSuccess, "Should succeed with partial city name match") #expect(!result.options.isEmpty, "Should return options") } @Test("4.14 - Must-stop case insensitive") func test_planByDates_MustStop_CaseInsensitive() { // Setup: Must-stop = "CHICAGO" (uppercase) should match "Chicago" let chicagoId = "stadium_chicago_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [chicagoId: chicago] let game = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) let mustStop = LocationInput(name: "CHICAGO") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [game], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: Case insensitive match #expect(result.isSuccess, "Should succeed with case-insensitive match") } @Test("4.15 - Multiple games in must-stop city all included") func test_planByDates_MustStop_MultipleGamesInCity_AllIncluded() { // Setup: Multiple games in Chicago on different days let chicagoId = "stadium_chicago_\(UUID().uuidString)" let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298) let stadiums = [chicagoId: chicago] let game1 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 5, hour: 19)) let game2 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 7, hour: 13)) let game3 = makeGame(stadiumId: chicagoId, dateTime: makeDate(day: 9, hour: 19)) let mustStop = LocationInput(name: "Chicago") let request = makePlanningRequest( startDate: makeDate(day: 4, hour: 0), endDate: makeDate(day: 10, hour: 23), games: [game1, game2, game3], stadiums: stadiums, mustStopLocations: [mustStop] ) // Execute let result = planner.plan(request: request) // Verify: All Chicago games should be included #expect(result.isSuccess, "Should succeed with multiple games in must-stop city") if let option = result.options.first { let allGameIds = Set(option.stops.flatMap { $0.games }) #expect(allGameIds.contains(game1.id), "Should include first Chicago game") #expect(allGameIds.contains(game2.id), "Should include second Chicago game") #expect(allGameIds.contains(game3.id), "Should include third Chicago game") } } }