refactor: change domain model IDs from UUID to String canonical IDs
This refactor fixes the achievement system by using stable canonical string IDs (e.g., "stadium_mlb_fenway_park") instead of random UUIDs. This ensures stadium mappings for achievements are consistent across app launches and CloudKit sync operations. Changes: - Stadium, Team, Game: id property changed from UUID to String - Trip, TripStop, TripPreferences: updated to use String IDs for games/stadiums - CKModels: removed UUID parsing, use canonical IDs directly - AchievementEngine: now matches against canonical stadium IDs - All test files updated to use String IDs instead of UUID() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
/// Creates a stadium at a known location
|
||||
private func makeStadium(
|
||||
id: UUID = UUID(),
|
||||
id: String = "stadium_test_\(UUID().uuidString)",
|
||||
city: String,
|
||||
lat: Double,
|
||||
lon: Double,
|
||||
@@ -51,9 +51,9 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
/// Creates a team
|
||||
private func makeTeam(
|
||||
id: UUID = UUID(),
|
||||
id: String = "team_test_\(UUID().uuidString)",
|
||||
name: String,
|
||||
stadiumId: UUID,
|
||||
stadiumId: String,
|
||||
sport: Sport = .mlb
|
||||
) -> Team {
|
||||
Team(
|
||||
@@ -71,10 +71,10 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
/// Creates a game at a stadium
|
||||
private func makeGame(
|
||||
id: UUID = UUID(),
|
||||
stadiumId: UUID,
|
||||
homeTeamId: UUID,
|
||||
awayTeamId: UUID,
|
||||
id: String = "game_test_\(UUID().uuidString)",
|
||||
stadiumId: String,
|
||||
homeTeamId: String,
|
||||
awayTeamId: String,
|
||||
dateTime: Date,
|
||||
sport: Sport = .mlb
|
||||
) -> Game {
|
||||
@@ -93,10 +93,10 @@ struct ScenarioDPlannerTests {
|
||||
private func makePlanningRequest(
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
followTeamId: UUID?,
|
||||
followTeamId: String?,
|
||||
allGames: [Game],
|
||||
stadiums: [UUID: Stadium],
|
||||
teams: [UUID: Team] = [:],
|
||||
stadiums: [String: Stadium],
|
||||
teams: [String: Team] = [:],
|
||||
selectedRegions: Set<Region> = [],
|
||||
allowRepeatCities: Bool = true,
|
||||
useHomeLocation: Bool = false,
|
||||
@@ -132,12 +132,12 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.1.1 - Single team with home games returns trip with those games")
|
||||
func test_followTeam_HomeGames_ReturnsTrip() {
|
||||
// Setup: Team with 2 home games
|
||||
let stadiumId = UUID()
|
||||
let stadiumId = "stadium_test_\(UUID().uuidString)"
|
||||
let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let stadiums = [stadiumId: stadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
let team = makeTeam(id: teamId, name: "Chicago Cubs", stadiumId: stadiumId)
|
||||
|
||||
let game1 = makeGame(
|
||||
@@ -179,8 +179,8 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.1.2 - Team with away games includes those games")
|
||||
func test_followTeam_AwayGames_IncludesAwayGames() {
|
||||
// Setup: Team with one home game and one away game (2 cities for simpler route)
|
||||
let homeStadiumId = UUID()
|
||||
let awayStadiumId = UUID()
|
||||
let homeStadiumId = "stadium_home_\(UUID().uuidString)"
|
||||
let awayStadiumId = "stadium_away_\(UUID().uuidString)"
|
||||
|
||||
let homeStadium = makeStadium(id: homeStadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let awayStadium = makeStadium(id: awayStadiumId, city: "Milwaukee", lat: 43.0389, lon: -87.9065)
|
||||
@@ -190,8 +190,8 @@ struct ScenarioDPlannerTests {
|
||||
awayStadiumId: awayStadium
|
||||
]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
|
||||
// Home game
|
||||
let homeGame = makeGame(
|
||||
@@ -237,8 +237,8 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.1.3 - Team games filtered by selected regions")
|
||||
func test_followTeam_RegionFilter_FiltersGames() {
|
||||
// Setup: Team with games in multiple regions
|
||||
let eastStadiumId = UUID()
|
||||
let centralStadiumId = UUID()
|
||||
let eastStadiumId = "stadium_east_\(UUID().uuidString)"
|
||||
let centralStadiumId = "stadium_central_\(UUID().uuidString)"
|
||||
|
||||
// East region (> -85 longitude)
|
||||
let eastStadium = makeStadium(id: eastStadiumId, city: "New York", lat: 40.7128, lon: -73.9352)
|
||||
@@ -247,8 +247,8 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
let stadiums = [eastStadiumId: eastStadium, centralStadiumId: centralStadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
|
||||
let eastGame = makeGame(
|
||||
stadiumId: eastStadiumId,
|
||||
@@ -292,14 +292,14 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.2.1 - No team selected returns missingTeamSelection failure")
|
||||
func test_followTeam_NoTeamSelected_ReturnsMissingTeamSelection() {
|
||||
// Setup: No team ID
|
||||
let stadiumId = UUID()
|
||||
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,
|
||||
homeTeamId: UUID(),
|
||||
awayTeamId: UUID(),
|
||||
homeTeamId: "team_test_\(UUID().uuidString)",
|
||||
awayTeamId: "team_opponent_\(UUID().uuidString)",
|
||||
dateTime: makeDate(day: 5, hour: 19)
|
||||
)
|
||||
|
||||
@@ -323,17 +323,17 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.2.2 - Team with no games in date range returns noGamesInRange failure")
|
||||
func test_followTeam_NoGamesInRange_ReturnsNoGamesFailure() {
|
||||
// Setup: Team's games are outside date range
|
||||
let stadiumId = UUID()
|
||||
let stadiumId = "stadium_test_\(UUID().uuidString)"
|
||||
let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let stadiums = [stadiumId: stadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
|
||||
// Game is in July, but we search June
|
||||
let game = makeGame(
|
||||
stadiumId: stadiumId,
|
||||
homeTeamId: teamId,
|
||||
awayTeamId: UUID(),
|
||||
awayTeamId: "team_opponent_\(UUID().uuidString)",
|
||||
dateTime: makeDate(month: 7, day: 15, hour: 19)
|
||||
)
|
||||
|
||||
@@ -357,13 +357,13 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.2.3 - Team not involved in any games returns noGamesInRange failure")
|
||||
func test_followTeam_TeamNotInGames_ReturnsNoGamesFailure() {
|
||||
// Setup: Games exist but team isn't playing
|
||||
let stadiumId = UUID()
|
||||
let stadiumId = "stadium_test_\(UUID().uuidString)"
|
||||
let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let stadiums = [stadiumId: stadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let otherTeam1 = UUID()
|
||||
let otherTeam2 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let otherTeam1 = "team_other1_\(UUID().uuidString)"
|
||||
let otherTeam2 = "team_other2_\(UUID().uuidString)"
|
||||
|
||||
// Game between other teams
|
||||
let game = makeGame(
|
||||
@@ -393,12 +393,12 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.2.4 - Repeat city filter removes duplicate city visits")
|
||||
func test_followTeam_RepeatCityFilter_RemovesDuplicates() {
|
||||
// Setup: Team has multiple games at same stadium
|
||||
let stadiumId = UUID()
|
||||
let stadiumId = "stadium_test_\(UUID().uuidString)"
|
||||
let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let stadiums = [stadiumId: stadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
|
||||
let game1 = makeGame(
|
||||
stadiumId: stadiumId,
|
||||
@@ -445,16 +445,16 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.2.5 - Missing date range returns missingDateRange failure")
|
||||
func test_followTeam_MissingDateRange_ReturnsMissingDateRangeFailure() {
|
||||
// Setup: Invalid date range (end before start)
|
||||
let stadiumId = UUID()
|
||||
let stadiumId = "stadium_test_\(UUID().uuidString)"
|
||||
let stadium = makeStadium(id: stadiumId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let stadiums = [stadiumId: stadium]
|
||||
|
||||
let teamId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
|
||||
let game = makeGame(
|
||||
stadiumId: stadiumId,
|
||||
homeTeamId: teamId,
|
||||
awayTeamId: UUID(),
|
||||
awayTeamId: "team_opponent_\(UUID().uuidString)",
|
||||
dateTime: makeDate(day: 5, hour: 19)
|
||||
)
|
||||
|
||||
@@ -481,16 +481,16 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.3.1 - Route connects team games chronologically")
|
||||
func test_followTeam_RouteIsChronological() {
|
||||
// Setup: Team with games in 2 nearby cities chronologically
|
||||
let chicagoId = UUID()
|
||||
let milwaukeeId = UUID()
|
||||
let chicagoId = "stadium_chicago_\(UUID().uuidString)"
|
||||
let milwaukeeId = "stadium_milwaukee_\(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 stadiums = [chicagoId: chicago, milwaukeeId: milwaukee]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
|
||||
// Games in chronological order: Chicago → Milwaukee
|
||||
let game1 = makeGame(
|
||||
@@ -534,16 +534,16 @@ struct ScenarioDPlannerTests {
|
||||
@Test("D.3.2 - Travel segments connect stops correctly")
|
||||
func test_followTeam_TravelSegmentsConnectStops() {
|
||||
// Setup: Team with 2 games in different cities
|
||||
let nycId = UUID()
|
||||
let bostonId = UUID()
|
||||
let nycId = "stadium_nyc_\(UUID().uuidString)"
|
||||
let bostonId = "stadium_boston_\(UUID().uuidString)"
|
||||
|
||||
let nyc = makeStadium(id: nycId, city: "New York", lat: 40.7128, lon: -73.9352)
|
||||
let boston = makeStadium(id: bostonId, city: "Boston", lat: 42.3601, lon: -71.0589)
|
||||
|
||||
let stadiums = [nycId: nyc, bostonId: boston]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponentId = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponentId = "team_opponent_\(UUID().uuidString)"
|
||||
|
||||
let game1 = makeGame(
|
||||
stadiumId: nycId,
|
||||
@@ -597,9 +597,9 @@ struct ScenarioDPlannerTests {
|
||||
// Setup: Simulates Houston → Chicago → Anaheim (Astros July 20-29 scenario)
|
||||
// Houston to Chicago: ~1000 miles, Chicago to Anaheim: ~2000 miles
|
||||
// With 4+ days between each leg, both should be feasible
|
||||
let houstonId = UUID()
|
||||
let chicagoId = UUID()
|
||||
let anaheimId = UUID()
|
||||
let houstonId = "stadium_houston_\(UUID().uuidString)"
|
||||
let chicagoId = "stadium_chicago_\(UUID().uuidString)"
|
||||
let anaheimId = "stadium_anaheim_\(UUID().uuidString)"
|
||||
|
||||
let houston = makeStadium(id: houstonId, city: "Houston", lat: 29.7604, lon: -95.3698)
|
||||
let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
@@ -607,10 +607,10 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
let stadiums = [houstonId: houston, chicagoId: chicago, anaheimId: anaheim]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let opponent3 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
let opponent3 = "team_opponent3_\(UUID().uuidString)"
|
||||
|
||||
// Houston home games: July 20-22
|
||||
let houstonGame = makeGame(
|
||||
@@ -674,9 +674,9 @@ struct ScenarioDPlannerTests {
|
||||
func test_followTeam_ThreeCityRoute_InsufficientTime_ExcludesUnreachableCities() {
|
||||
// Setup: Same cities but games too close together
|
||||
// Chicago to Anaheim needs ~37 hours driving, but only 1 day between games
|
||||
let houstonId = UUID()
|
||||
let chicagoId = UUID()
|
||||
let anaheimId = UUID()
|
||||
let houstonId = "stadium_houston_\(UUID().uuidString)"
|
||||
let chicagoId = "stadium_chicago_\(UUID().uuidString)"
|
||||
let anaheimId = "stadium_anaheim_\(UUID().uuidString)"
|
||||
|
||||
let houston = makeStadium(id: houstonId, city: "Houston", lat: 29.7604, lon: -95.3698)
|
||||
let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
@@ -684,10 +684,10 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
let stadiums = [houstonId: houston, chicagoId: chicago, anaheimId: anaheim]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let opponent3 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
let opponent3 = "team_opponent3_\(UUID().uuidString)"
|
||||
|
||||
// Houston: July 20
|
||||
let houstonGame = makeGame(
|
||||
@@ -744,17 +744,17 @@ struct ScenarioDPlannerTests {
|
||||
func test_followTeam_PicksOptimalGamePerCity_ForRouteFeasibility() {
|
||||
// Setup: Team has 3 games in each city (series)
|
||||
// With allowRepeatCities=false, router should pick games that make the route work
|
||||
let chicagoId = UUID()
|
||||
let anaheimId = UUID()
|
||||
let chicagoId = "stadium_chicago_\(UUID().uuidString)"
|
||||
let anaheimId = "stadium_anaheim_\(UUID().uuidString)"
|
||||
|
||||
let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let anaheim = makeStadium(id: anaheimId, city: "Anaheim", lat: 33.8003, lon: -117.8827)
|
||||
|
||||
let stadiums = [chicagoId: chicago, anaheimId: anaheim]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
|
||||
// Chicago series: July 24, 25, 26
|
||||
let chicagoGame1 = makeGame(
|
||||
@@ -826,8 +826,8 @@ struct ScenarioDPlannerTests {
|
||||
func test_followTeam_FiveDaySegment_AtLimit_Succeeds() {
|
||||
// Setup: ~38 hours of driving with exactly 5 days between games
|
||||
// 5 days × 8 hours = 40 hours max, which should pass
|
||||
let seattleId = UUID()
|
||||
let miamiId = UUID()
|
||||
let seattleId = "stadium_seattle_\(UUID().uuidString)"
|
||||
let miamiId = "stadium_denver_\(UUID().uuidString)"
|
||||
|
||||
// Seattle to Miami: ~3,300 miles straight line × 1.3 = ~4,300 miles
|
||||
// At 60 mph = ~72 hours - this is too far even for 5 days
|
||||
@@ -837,9 +837,9 @@ struct ScenarioDPlannerTests {
|
||||
|
||||
let stadiums = [seattleId: seattle, miamiId: denver]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
|
||||
let seattleGame = makeGame(
|
||||
stadiumId: seattleId,
|
||||
@@ -889,17 +889,17 @@ struct ScenarioDPlannerTests {
|
||||
// Setup: Distance that would take > 40 hours to drive
|
||||
// Seattle to Miami: ~3,300 miles straight line × 1.3 = ~4,300 miles
|
||||
// At 60 mph = ~72 hours - exceeds 40 hour (5 day) limit
|
||||
let seattleId = UUID()
|
||||
let miamiId = UUID()
|
||||
let seattleId = "stadium_seattle_\(UUID().uuidString)"
|
||||
let miamiId = "stadium_miami_\(UUID().uuidString)"
|
||||
|
||||
let seattle = makeStadium(id: seattleId, city: "Seattle", lat: 47.6062, lon: -122.3321)
|
||||
let miami = makeStadium(id: miamiId, city: "Miami", lat: 25.7617, lon: -80.1918)
|
||||
|
||||
let stadiums = [seattleId: seattle, miamiId: miami]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
|
||||
let seattleGame = makeGame(
|
||||
stadiumId: seattleId,
|
||||
@@ -950,17 +950,17 @@ struct ScenarioDPlannerTests {
|
||||
// Setup: Same Chicago→Anaheim route but with 2 drivers
|
||||
// With 2 drivers × 8 hours = 16 hours/day
|
||||
// Chicago to Anaheim in 3 days = 48 hours available (vs 24 hours with 1 driver)
|
||||
let chicagoId = UUID()
|
||||
let anaheimId = UUID()
|
||||
let chicagoId = "stadium_chicago_\(UUID().uuidString)"
|
||||
let anaheimId = "stadium_anaheim_\(UUID().uuidString)"
|
||||
|
||||
let chicago = makeStadium(id: chicagoId, city: "Chicago", lat: 41.8781, lon: -87.6298)
|
||||
let anaheim = makeStadium(id: anaheimId, city: "Anaheim", lat: 33.8003, lon: -117.8827)
|
||||
|
||||
let stadiums = [chicagoId: chicago, anaheimId: anaheim]
|
||||
|
||||
let teamId = UUID()
|
||||
let opponent1 = UUID()
|
||||
let opponent2 = UUID()
|
||||
let teamId = "team_test_\(UUID().uuidString)"
|
||||
let opponent1 = "team_opponent1_\(UUID().uuidString)"
|
||||
let opponent2 = "team_opponent2_\(UUID().uuidString)"
|
||||
|
||||
let chicagoGame = makeGame(
|
||||
stadiumId: chicagoId,
|
||||
|
||||
Reference in New Issue
Block a user