Fix team-first future window selection

This commit is contained in:
Trey t
2026-04-03 15:31:52 -05:00
parent 0fa3db5401
commit 188076717b
3 changed files with 169 additions and 11 deletions

View File

@@ -17,7 +17,7 @@ struct ScenarioEPlannerTests {
// MARK: - Test Data
private let planner = ScenarioEPlanner()
private let planner = ScenarioEPlanner(currentDate: TestClock.now)
// East Coast coordinates
private let nycCoord = CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855)
@@ -1177,6 +1177,157 @@ struct ScenarioEPlannerTests {
#expect(result.isSuccess || result.failure?.reason != .noGamesInRange)
}
// MARK: - Past Date Filtering Tests
@Test("teamFirst: past-only games return noValidRoutes")
func teamFirst_pastOnlyGames_returnsNoResults() {
// Simulate: currentDate is June 1, but all games are in March (past)
let calendar = TestClock.calendar
let currentDate = TestFixtures.date(year: 2026, month: 6, day: 1, hour: 12)
let pastDate = TestFixtures.date(year: 2026, month: 3, day: 10, hour: 19)
let nycStadium = makeStadium(id: "nyc", city: "New York", coordinate: nycCoord)
let bostonStadium = makeStadium(id: "boston", city: "Boston", coordinate: bostonCoord)
let pastGame1 = makeGame(id: "past-yankees", homeTeamId: "yankees", awayTeamId: "opp", stadiumId: "nyc", dateTime: pastDate)
let pastGame2 = makeGame(id: "past-redsox", homeTeamId: "redsox", awayTeamId: "opp", stadiumId: "boston",
dateTime: calendar.date(byAdding: .day, value: 2, to: pastDate)!)
let prefs = TripPreferences(
planningMode: .teamFirst,
sports: [.mlb],
selectedTeamIds: ["yankees", "redsox"]
)
let request = PlanningRequest(
preferences: prefs,
availableGames: [pastGame1, pastGame2],
teams: ["yankees": makeTeam(id: "yankees", name: "Yankees"),
"redsox": makeTeam(id: "redsox", name: "Red Sox")],
stadiums: ["nyc": nycStadium, "boston": bostonStadium]
)
let planner = ScenarioEPlanner(currentDate: currentDate)
let result = planner.plan(request: request)
// All games are in the past no valid windows should exist
guard case .failure(let failure) = result else {
Issue.record("Expected failure when all games are in the past")
return
}
#expect(failure.reason == .noValidRoutes, "Should fail with noValidRoutes when all windows are in the past")
}
@Test("teamFirst: mix of past and future games only returns future windows")
func teamFirst_mixPastFuture_onlyReturnsFutureWindows() {
let calendar = TestClock.calendar
// Current date: March 15, 2026
let currentDate = TestFixtures.date(year: 2026, month: 3, day: 15, hour: 12)
let nycStadium = makeStadium(id: "nyc", city: "New York", coordinate: nycCoord)
let bostonStadium = makeStadium(id: "boston", city: "Boston", coordinate: bostonCoord)
// Past games (early March)
let pastGame1 = makeGame(id: "past-yankees", homeTeamId: "yankees", awayTeamId: "opp", stadiumId: "nyc",
dateTime: TestFixtures.date(year: 2026, month: 3, day: 2, hour: 19))
let pastGame2 = makeGame(id: "past-redsox", homeTeamId: "redsox", awayTeamId: "opp", stadiumId: "boston",
dateTime: TestFixtures.date(year: 2026, month: 3, day: 3, hour: 19))
// Future games (late March / April)
let futureGame1 = makeGame(id: "future-yankees", homeTeamId: "yankees", awayTeamId: "opp", stadiumId: "nyc",
dateTime: TestFixtures.date(year: 2026, month: 3, day: 20, hour: 19))
let futureGame2 = makeGame(id: "future-redsox", homeTeamId: "redsox", awayTeamId: "opp", stadiumId: "boston",
dateTime: TestFixtures.date(year: 2026, month: 3, day: 22, hour: 19))
let prefs = TripPreferences(
planningMode: .teamFirst,
sports: [.mlb],
selectedTeamIds: ["yankees", "redsox"]
)
let request = PlanningRequest(
preferences: prefs,
availableGames: [pastGame1, pastGame2, futureGame1, futureGame2],
teams: ["yankees": makeTeam(id: "yankees", name: "Yankees"),
"redsox": makeTeam(id: "redsox", name: "Red Sox")],
stadiums: ["nyc": nycStadium, "boston": bostonStadium]
)
let planner = ScenarioEPlanner(currentDate: currentDate)
let result = planner.plan(request: request)
if case .success(let options) = result {
// All returned stops should be on or after currentDate
for option in options {
for stop in option.stops {
#expect(stop.arrivalDate >= calendar.startOfDay(for: currentDate),
"All stops should be in the future, got \(stop.arrivalDate)")
}
}
}
// Failure is acceptable if routing constraints prevent a valid route
}
@Test("teamFirst: evaluates all sampled windows across full season")
func teamFirst_evaluatesAllSampledWindows_fullSeasonCoverage() {
let calendar = TestClock.calendar
let currentDate = TestFixtures.date(year: 2026, month: 4, day: 1, hour: 12)
let nycStadium = makeStadium(id: "nyc", city: "New York", coordinate: nycCoord)
let bostonStadium = makeStadium(id: "boston", city: "Boston", coordinate: bostonCoord)
// Create games spread across April-September (6 months)
// Each team gets a home game every ~10 days
var allGames: [Game] = []
for monthOffset in 0..<6 {
let month = 4 + monthOffset
for dayOffset in stride(from: 1, through: 25, by: 10) {
let gameDate = TestFixtures.date(year: 2026, month: month, day: dayOffset, hour: 19)
allGames.append(makeGame(
id: "yankees-\(month)-\(dayOffset)",
homeTeamId: "yankees", awayTeamId: "opp", stadiumId: "nyc",
dateTime: gameDate
))
let gameDate2 = calendar.date(byAdding: .day, value: 1, to: gameDate)!
allGames.append(makeGame(
id: "redsox-\(month)-\(dayOffset)",
homeTeamId: "redsox", awayTeamId: "opp", stadiumId: "boston",
dateTime: gameDate2
))
}
}
let prefs = TripPreferences(
planningMode: .teamFirst,
sports: [.mlb],
selectedTeamIds: ["yankees", "redsox"]
)
let request = PlanningRequest(
preferences: prefs,
availableGames: allGames,
teams: ["yankees": makeTeam(id: "yankees", name: "Yankees"),
"redsox": makeTeam(id: "redsox", name: "Red Sox")],
stadiums: ["nyc": nycStadium, "boston": bostonStadium]
)
let planner = ScenarioEPlanner(currentDate: currentDate)
let result = planner.plan(request: request)
guard case .success(let options) = result else {
Issue.record("Expected success with games spread across full season")
return
}
#expect(!options.isEmpty, "Should find options across the season")
// Verify results span multiple months (not clustered in first month)
let months = Set(options.flatMap { option in
option.stops.map { calendar.component(.month, from: $0.arrivalDate) }
})
#expect(months.count >= 2, "Results should span at least 2 months, got months: \(months.sorted())")
}
// MARK: - Helper Methods
private func makeStadium(

View File

@@ -17,7 +17,7 @@ struct TeamFirstIntegrationTests {
// MARK: - Test Data
private let planner = ScenarioEPlanner()
private let planner = ScenarioEPlanner(currentDate: TestClock.now)
// MLB stadiums with realistic coordinates
private let yankeeStadiumCoord = CLLocationCoordinate2D(latitude: 40.8296, longitude: -73.9262)