test(09-03): add travel corridor game inclusion tests
Added 5 TDD tests for Feature 1 (travel corridor game inclusion): - Direct route with games along path includes all corridor games - Game slightly off corridor within tolerance included - Game far from corridor excluded - Multiple games mixed filters correctly (excludes south/beyond-end games) - No games along corridor returns empty route or failure Tests validate that Scenario C planning correctly: - Includes games within ~50-100 miles of direct travel corridor - Excludes games in wrong direction (south when traveling north) - Excludes games beyond the destination city Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2020,4 +2020,257 @@ struct ScenarioCPlannerTests {
|
||||
Issue.record("Expected success")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Feature 1: Travel Corridor Game Inclusion Tests
|
||||
|
||||
@Test("Direct route with games along path includes all corridor games")
|
||||
func corridor_DirectRouteWithGamesAlongPath_IncludesAllGames() {
|
||||
let planner = ScenarioCPlanner()
|
||||
|
||||
// Create stadiums: LA → San Jose → SF (direct north on I-5/101)
|
||||
let la = makeStadium(name: "Dodger Stadium", city: "Los Angeles", state: "CA",
|
||||
latitude: 34.0739, longitude: -118.2400)
|
||||
let sj = makeStadium(name: "SAP Center", city: "San Jose", state: "CA",
|
||||
latitude: 37.3326, longitude: -121.9010) // Midpoint between LA and SF
|
||||
let sf = sfStadium
|
||||
|
||||
// Games along the path
|
||||
let laGame = makeGame(stadiumId: la.id, date: date("2026-06-05 19:00"))
|
||||
let sjGame = makeGame(stadiumId: sj.id, date: date("2026-06-06 19:00"))
|
||||
let sfGame = makeGame(stadiumId: sf.id, date: date("2026-06-07 19:00"))
|
||||
|
||||
let startLoc = LocationInput(
|
||||
name: "Los Angeles",
|
||||
coordinate: CLLocationCoordinate2D(latitude: la.latitude, longitude: la.longitude)
|
||||
)
|
||||
let endLoc = LocationInput(
|
||||
name: "San Francisco",
|
||||
coordinate: CLLocationCoordinate2D(latitude: sf.latitude, longitude: sf.longitude)
|
||||
)
|
||||
|
||||
let request = makeRequest(
|
||||
games: [laGame, sjGame, sfGame],
|
||||
stadiums: [la.id: la, sj.id: sj, sf.id: sf],
|
||||
startLocation: startLoc,
|
||||
endLocation: endLoc,
|
||||
startDate: date("2026-06-01 00:00"),
|
||||
endDate: date("2026-06-30 23:59")
|
||||
)
|
||||
|
||||
let result = planner.plan(request: request)
|
||||
|
||||
if case .success(let options) = result {
|
||||
#expect(!options.isEmpty, "Should return at least one route")
|
||||
|
||||
// Best option should include all 3 games (LA → SJ → SF)
|
||||
let topOption = options.first!
|
||||
let gameIds = topOption.stops.flatMap { $0.games }
|
||||
|
||||
#expect(gameIds.contains(laGame.id), "Should include LA game")
|
||||
#expect(gameIds.contains(sjGame.id), "Should include San Jose game (midpoint)")
|
||||
#expect(gameIds.contains(sfGame.id), "Should include SF game")
|
||||
} else {
|
||||
Issue.record("Expected success with games along direct corridor")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Game slightly off corridor within tolerance included")
|
||||
func corridor_GameSlightlyOffCorridor_IncludedWithinTolerance() {
|
||||
let planner = ScenarioCPlanner()
|
||||
|
||||
// LA → SF is north on I-5
|
||||
// Sacramento is ~20 miles east of I-5, should be within corridor tolerance
|
||||
let la = laStadium
|
||||
let sacramento = makeStadium(name: "Golden 1 Center", city: "Sacramento", state: "CA",
|
||||
latitude: 38.5802, longitude: -121.4996) // Slightly east of direct path
|
||||
let sf = sfStadium
|
||||
|
||||
let laGame = makeGame(stadiumId: la.id, date: date("2026-06-05 19:00"))
|
||||
let sacGame = makeGame(stadiumId: sacramento.id, date: date("2026-06-06 19:00"))
|
||||
let sfGame = makeGame(stadiumId: sf.id, date: date("2026-06-07 19:00"))
|
||||
|
||||
let startLoc = LocationInput(
|
||||
name: "Los Angeles",
|
||||
coordinate: CLLocationCoordinate2D(latitude: la.latitude, longitude: la.longitude)
|
||||
)
|
||||
let endLoc = LocationInput(
|
||||
name: "San Francisco",
|
||||
coordinate: CLLocationCoordinate2D(latitude: sf.latitude, longitude: sf.longitude)
|
||||
)
|
||||
|
||||
let request = makeRequest(
|
||||
games: [laGame, sacGame, sfGame],
|
||||
stadiums: [la.id: la, sacramento.id: sacramento, sf.id: sf],
|
||||
startLocation: startLoc,
|
||||
endLocation: endLoc,
|
||||
startDate: date("2026-06-01 00:00"),
|
||||
endDate: date("2026-06-30 23:59")
|
||||
)
|
||||
|
||||
let result = planner.plan(request: request)
|
||||
|
||||
if case .success(let options) = result {
|
||||
#expect(!options.isEmpty)
|
||||
|
||||
// Sacramento should be included (within corridor tolerance)
|
||||
let allGameIds = options.flatMap { $0.stops.flatMap { $0.games } }
|
||||
#expect(allGameIds.contains(sacGame.id), "Sacramento should be included (within ~50mi corridor tolerance)")
|
||||
} else {
|
||||
Issue.record("Expected success with slightly off-corridor game")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Game far from corridor excluded")
|
||||
func corridor_GameFarFromCorridor_Excluded() {
|
||||
let planner = ScenarioCPlanner()
|
||||
|
||||
// LA → SF is north-bound
|
||||
// Phoenix is ~300 miles east, far from corridor
|
||||
let la = laStadium
|
||||
let phoenix = makeStadium(name: "Chase Field", city: "Phoenix", state: "AZ",
|
||||
latitude: 33.4452, longitude: -112.0667) // Far east of LA→SF path
|
||||
let sf = sfStadium
|
||||
|
||||
let laGame = makeGame(stadiumId: la.id, date: date("2026-06-05 19:00"))
|
||||
let phoenixGame = makeGame(stadiumId: phoenix.id, date: date("2026-06-06 19:00"))
|
||||
let sfGame = makeGame(stadiumId: sf.id, date: date("2026-06-07 19:00"))
|
||||
|
||||
let startLoc = LocationInput(
|
||||
name: "Los Angeles",
|
||||
coordinate: CLLocationCoordinate2D(latitude: la.latitude, longitude: la.longitude)
|
||||
)
|
||||
let endLoc = LocationInput(
|
||||
name: "San Francisco",
|
||||
coordinate: CLLocationCoordinate2D(latitude: sf.latitude, longitude: sf.longitude)
|
||||
)
|
||||
|
||||
let request = makeRequest(
|
||||
games: [laGame, phoenixGame, sfGame],
|
||||
stadiums: [la.id: la, phoenix.id: phoenix, sf.id: sf],
|
||||
startLocation: startLoc,
|
||||
endLocation: endLoc,
|
||||
startDate: date("2026-06-01 00:00"),
|
||||
endDate: date("2026-06-30 23:59")
|
||||
)
|
||||
|
||||
let result = planner.plan(request: request)
|
||||
|
||||
if case .success(let options) = result {
|
||||
#expect(!options.isEmpty)
|
||||
|
||||
// Phoenix should be excluded (too far from LA→SF corridor)
|
||||
let allGameIds = options.flatMap { $0.stops.flatMap { $0.games } }
|
||||
#expect(!allGameIds.contains(phoenixGame.id), "Phoenix should be excluded (300mi east, far from corridor)")
|
||||
} else {
|
||||
Issue.record("Expected success but with Phoenix excluded")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Multiple games some on corridor some off filters correctly")
|
||||
func corridor_MultipleGamesMixed_FiltersCorrectly() {
|
||||
let planner = ScenarioCPlanner()
|
||||
|
||||
// LA → Portland route
|
||||
let la = laStadium
|
||||
let sd = sdStadium // South of LA - wrong direction
|
||||
let sf = sfStadium // On path north
|
||||
let seattle = makeStadium(name: "T-Mobile Park", city: "Seattle", state: "WA",
|
||||
latitude: 47.5914, longitude: -122.3325) // Beyond Portland
|
||||
let portland = makeStadium(name: "Providence Park", city: "Portland", state: "OR",
|
||||
latitude: 45.5212, longitude: -122.6917)
|
||||
|
||||
let laGame = makeGame(stadiumId: la.id, date: date("2026-06-05 19:00"))
|
||||
let sdGame = makeGame(stadiumId: sd.id, date: date("2026-06-06 19:00")) // Should exclude (south)
|
||||
let sfGame = makeGame(stadiumId: sf.id, date: date("2026-06-07 19:00")) // Should include (on path)
|
||||
let seattleGame = makeGame(stadiumId: seattle.id, date: date("2026-06-08 19:00")) // Should exclude (beyond end)
|
||||
|
||||
let startLoc = LocationInput(
|
||||
name: "Los Angeles",
|
||||
coordinate: CLLocationCoordinate2D(latitude: la.latitude, longitude: la.longitude)
|
||||
)
|
||||
let endLoc = LocationInput(
|
||||
name: "Portland",
|
||||
coordinate: CLLocationCoordinate2D(latitude: portland.latitude, longitude: portland.longitude)
|
||||
)
|
||||
|
||||
let request = makeRequest(
|
||||
games: [laGame, sdGame, sfGame, seattleGame],
|
||||
stadiums: [la.id: la, sd.id: sd, sf.id: sf, seattle.id: seattle, portland.id: portland],
|
||||
startLocation: startLoc,
|
||||
endLocation: endLoc,
|
||||
startDate: date("2026-06-01 00:00"),
|
||||
endDate: date("2026-06-30 23:59")
|
||||
)
|
||||
|
||||
let result = planner.plan(request: request)
|
||||
|
||||
if case .success(let options) = result {
|
||||
#expect(!options.isEmpty)
|
||||
|
||||
let allGameIds = options.flatMap { $0.stops.flatMap { $0.games } }
|
||||
|
||||
// Should include games on corridor
|
||||
#expect(allGameIds.contains(laGame.id), "LA should be included (start)")
|
||||
#expect(allGameIds.contains(sfGame.id), "SF should be included (on path north)")
|
||||
|
||||
// Should exclude off-corridor games
|
||||
#expect(!allGameIds.contains(sdGame.id), "San Diego should be excluded (south, wrong direction)")
|
||||
#expect(!allGameIds.contains(seattleGame.id), "Seattle should be excluded (beyond end point)")
|
||||
} else {
|
||||
Issue.record("Expected success with selective corridor filtering")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("No games along corridor returns empty route or failure")
|
||||
func corridor_NoGamesAlongCorridor_ReturnsEmptyOrFailure() {
|
||||
let planner = ScenarioCPlanner()
|
||||
|
||||
// LA → Seattle route (I-5 corridor)
|
||||
let la = laStadium
|
||||
let seattle = makeStadium(name: "T-Mobile Park", city: "Seattle", state: "WA",
|
||||
latitude: 47.5914, longitude: -122.3325)
|
||||
|
||||
// Games far from corridor
|
||||
let phoenix = makeStadium(name: "Chase Field", city: "Phoenix", state: "AZ",
|
||||
latitude: 33.4452, longitude: -112.0667) // East
|
||||
let denver = makeStadium(name: "Coors Field", city: "Denver", state: "CO",
|
||||
latitude: 39.7559, longitude: -104.9942) // Far east
|
||||
|
||||
let phoenixGame = makeGame(stadiumId: phoenix.id, date: date("2026-06-05 19:00"))
|
||||
let denverGame = makeGame(stadiumId: denver.id, date: date("2026-06-06 19:00"))
|
||||
|
||||
let startLoc = LocationInput(
|
||||
name: "Los Angeles",
|
||||
coordinate: CLLocationCoordinate2D(latitude: la.latitude, longitude: la.longitude)
|
||||
)
|
||||
let endLoc = LocationInput(
|
||||
name: "Seattle",
|
||||
coordinate: CLLocationCoordinate2D(latitude: seattle.latitude, longitude: seattle.longitude)
|
||||
)
|
||||
|
||||
let request = makeRequest(
|
||||
games: [phoenixGame, denverGame],
|
||||
stadiums: [la.id: la, seattle.id: seattle, phoenix.id: phoenix, denver.id: denver],
|
||||
startLocation: startLoc,
|
||||
endLocation: endLoc,
|
||||
startDate: date("2026-06-01 00:00"),
|
||||
endDate: date("2026-06-30 23:59")
|
||||
)
|
||||
|
||||
let result = planner.plan(request: request)
|
||||
|
||||
// Should either fail (.noGamesInRange or .noValidRoutes) OR return route with no games
|
||||
switch result {
|
||||
case .failure(let failure):
|
||||
// Expected: no games in corridor
|
||||
#expect(failure.reason == .noValidRoutes || failure.reason == .noGamesInRange,
|
||||
"Should fail with noValidRoutes or noGamesInRange")
|
||||
case .success(let options):
|
||||
// If success, route should have only start/end waypoints, no games
|
||||
if let topOption = options.first {
|
||||
let gameCount = topOption.stops.flatMap { $0.games }.count
|
||||
#expect(gameCount == 0, "Route should have no games if all games are off-corridor")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user