Harden planning test suite with realistic fixtures and output sanity checks

Adds messy/realistic data factories to TestFixtures, new PlannerOutputSanityTests,
and updates all scenario planner tests with improved coverage and assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-04 13:38:41 -05:00
parent 188076717b
commit 9b622f8bbb
13 changed files with 2174 additions and 446 deletions

View File

@@ -467,3 +467,108 @@ extension TestFixtures {
static let centralCities = ["Chicago", "Houston", "Dallas", "Minneapolis", "Detroit"]
static let westCoastCities = ["Los Angeles", "San Francisco", "Seattle", "Phoenix"]
}
// MARK: - Messy / Realistic Data Factories
extension TestFixtures {
/// Creates games that are all in the past relative to a reference date.
static func pastGames(
count: Int,
sport: Sport = .mlb,
cities: [String] = ["New York", "Boston", "Chicago"],
referenceDate: Date = TestClock.now
) -> [Game] {
(0..<count).map { i in
let city = cities[i % cities.count]
let daysAgo = 30 + (i * 2) // 30-60 days in the past
let gameDate = TestClock.calendar.date(byAdding: .day, value: -daysAgo, to: referenceDate)!
return game(sport: sport, city: city, dateTime: gameDate)
}
}
/// Creates a mix of past and future games, returning them categorized.
static func mixedPastFutureGames(
pastCount: Int = 5,
futureCount: Int = 5,
sport: Sport = .mlb,
cities: [String] = ["New York", "Boston", "Chicago", "Philadelphia"],
referenceDate: Date = TestClock.now
) -> (past: [Game], future: [Game], all: [Game]) {
let past = (0..<pastCount).map { i in
let city = cities[i % cities.count]
let gameDate = TestClock.calendar.date(byAdding: .day, value: -(i + 1) * 5, to: referenceDate)!
return game(id: "past_\(i)", sport: sport, city: city, dateTime: gameDate)
}
let future = (0..<futureCount).map { i in
let city = cities[i % cities.count]
let gameDate = TestClock.calendar.date(byAdding: .day, value: (i + 1) * 2, to: referenceDate)!
return game(id: "future_\(i)", sport: sport, city: city, dateTime: gameDate)
}
return (past, future, past + future)
}
/// Creates games from sports the user didn't select (for testing sport filtering).
static func gamesWithWrongSport(
selectedSport: Sport = .mlb,
wrongSport: Sport = .nba,
correctCount: Int = 3,
wrongCount: Int = 3,
cities: [String] = ["New York", "Boston", "Chicago"]
) -> (correct: [Game], wrong: [Game], all: [Game]) {
let start = TestClock.addingDays(1)
let correct = (0..<correctCount).map { i in
let city = cities[i % cities.count]
let dt = TestClock.calendar.date(byAdding: .day, value: i, to: start)!
return game(id: "correct_\(i)", sport: selectedSport, city: city, dateTime: dt)
}
let wrong = (0..<wrongCount).map { i in
let city = cities[i % cities.count]
let dt = TestClock.calendar.date(byAdding: .day, value: i, to: start)!
return game(id: "wrong_\(i)", sport: wrongSport, city: city, dateTime: dt)
}
return (correct, wrong, correct + wrong)
}
/// Creates games referencing stadium IDs that don't exist in any stadium map.
static func gamesWithMissingStadium(
count: Int = 3,
sport: Sport = .mlb
) -> [Game] {
let start = TestClock.addingDays(1)
return (0..<count).map { i in
let dt = TestClock.calendar.date(byAdding: .day, value: i, to: start)!
return game(
id: "orphan_\(i)",
sport: sport,
city: "Atlantis",
dateTime: dt,
stadiumId: "stadium_nonexistent_\(i)"
)
}
}
/// Creates two different games sharing the same ID (simulates rescheduled games).
static func duplicateIdGames(sport: Sport = .mlb) -> [Game] {
let dt1 = TestClock.addingDays(2)
let dt2 = TestClock.addingDays(3)
return [
game(id: "dup_game_001", sport: sport, city: "New York", dateTime: dt1),
game(id: "dup_game_001", sport: sport, city: "Boston", dateTime: dt2),
]
}
/// Creates games spread over many days for long-trip duration testing.
static func longTripGames(
days: Int = 30,
sport: Sport = .mlb,
cities: [String] = ["New York", "Boston", "Chicago", "Philadelphia", "Atlanta"]
) -> [Game] {
let start = TestClock.addingDays(1)
return (0..<days).map { i in
let city = cities[i % cities.count]
let dt = TestClock.calendar.date(byAdding: .day, value: i, to: start)!
return game(id: "long_\(i)", sport: sport, city: city, dateTime: dt)
}
}
}