Stabilize unit and UI tests for SportsTime

This commit is contained in:
treyt
2026-02-18 13:00:15 -06:00
parent 1488be7c1f
commit 20ac1a7e59
49 changed files with 432 additions and 325 deletions

View File

@@ -0,0 +1,48 @@
//
// TestClock.swift
// SportsTimeTests
//
// Centralized time utilities for deterministic tests.
//
import Foundation
enum TestClock {
static let timeZone = TimeZone.current
static let locale = Locale(identifier: "en_US_POSIX")
static let calendar: Calendar = {
var calendar = Calendar.current
calendar.timeZone = timeZone
calendar.locale = locale
return calendar
}()
static let baseDate: Date = {
let components = DateComponents(
calendar: calendar,
timeZone: timeZone,
year: 2026,
month: 1,
day: 15,
hour: 12,
minute: 0,
second: 0
)
return calendar.date(from: components) ?? Date(timeIntervalSince1970: 0)
}()
static var now: Date { baseDate }
static func startOfDay(for date: Date = baseDate) -> Date {
calendar.startOfDay(for: date)
}
static func addingDays(_ days: Int, to date: Date = baseDate) -> Date {
calendar.date(byAdding: .day, value: days, to: date) ?? date
}
static func addingHours(_ hours: Int, to date: Date = baseDate) -> Date {
calendar.date(byAdding: .hour, value: hours, to: date) ?? date
}
}

View File

@@ -86,13 +86,15 @@ enum TestFixtures {
season: String = "2026",
isPlayoff: Bool = false
) -> Game {
let actualDateTime = dateTime ?? Calendar.current.date(byAdding: .day, value: 1, to: Date())!
let actualDateTime = dateTime ?? TestClock.calendar.date(byAdding: .day, value: 1, to: TestClock.now)!
let homeId = homeTeamId ?? "team_\(sport.rawValue.lowercased())_\(city.lowercased().replacingOccurrences(of: " ", with: "_"))"
let awayId = awayTeamId ?? "team_\(sport.rawValue.lowercased())_visitor"
let stadId = stadiumId ?? "stadium_\(sport.rawValue.lowercased())_\(city.lowercased().replacingOccurrences(of: " ", with: "_"))"
let formatter = DateFormatter()
formatter.dateFormat = "MMdd"
formatter.timeZone = TestClock.timeZone
formatter.locale = TestClock.locale
let dateStr = formatter.string(from: actualDateTime)
let actualId = id ?? "game_\(sport.rawValue.lowercased())_\(season)_\(awayId.split(separator: "_").last ?? "vis")_\(homeId.split(separator: "_").last ?? "home")_\(dateStr)"
@@ -119,12 +121,12 @@ enum TestFixtures {
count: Int,
sport: Sport = .mlb,
cities: [String] = ["New York", "Boston", "Chicago", "Los Angeles"],
startDate: Date = Date(),
startDate: Date = TestClock.now,
daySpread: Int = 1
) -> [Game] {
(0..<count).map { i in
let city = cities[i % cities.count]
let gameDate = Calendar.current.date(byAdding: .day, value: i * daySpread, to: startDate)!
let gameDate = TestClock.calendar.date(byAdding: .day, value: i * daySpread, to: startDate)!
return game(sport: sport, city: city, dateTime: gameDate)
}
}
@@ -132,12 +134,12 @@ enum TestFixtures {
/// Creates games for same-day conflict testing.
static func sameDayGames(
cities: [String],
date: Date = Date(),
date: Date = TestClock.now,
sport: Sport = .mlb
) -> [Game] {
cities.enumerated().map { index, city in
// Stagger times by 3 hours
let time = Calendar.current.date(byAdding: .hour, value: 13 + (index * 3), to: Calendar.current.startOfDay(for: date))!
let time = TestClock.calendar.date(byAdding: .hour, value: 13 + (index * 3), to: TestClock.calendar.startOfDay(for: date))!
return game(sport: sport, city: city, dateTime: time)
}
}
@@ -241,8 +243,8 @@ enum TestFixtures {
) -> TripStop {
let coordinate = coordinates[city]
let actualState = state ?? states[city] ?? "NY"
let arrival = arrivalDate ?? Date()
let departure = departureDate ?? Calendar.current.date(byAdding: .day, value: 1, to: arrival)!
let arrival = arrivalDate ?? TestClock.now
let departure = departureDate ?? TestClock.calendar.date(byAdding: .day, value: 1, to: arrival)!
return TripStop(
stopNumber: stopNumber,
@@ -259,14 +261,14 @@ enum TestFixtures {
/// Creates a sequence of trip stops for a multi-city trip.
static func tripStops(
cities: [String],
startDate: Date = Date(),
startDate: Date = TestClock.now,
daysPerStop: Int = 1
) -> [TripStop] {
var stops: [TripStop] = []
var currentDate = startDate
for (index, city) in cities.enumerated() {
let departure = Calendar.current.date(byAdding: .day, value: daysPerStop, to: currentDate)!
let departure = TestClock.calendar.date(byAdding: .day, value: daysPerStop, to: currentDate)!
stops.append(tripStop(
stopNumber: index + 1,
city: city,
@@ -317,8 +319,8 @@ enum TestFixtures {
needsEVCharging: Bool = false,
maxDrivingHoursPerDriver: Double? = nil
) -> TripPreferences {
let start = startDate ?? Date()
let end = endDate ?? Calendar.current.date(byAdding: .day, value: 7, to: start)!
let start = startDate ?? TestClock.now
let end = endDate ?? TestClock.calendar.date(byAdding: .day, value: 7, to: start)!
return TripPreferences(
planningMode: mode,
@@ -416,12 +418,12 @@ enum TestFixtures {
components.hour = hour
components.minute = minute
components.timeZone = TimeZone(identifier: "America/New_York")
return Calendar.current.date(from: components)!
return TestClock.calendar.date(from: components)!
}
/// Creates dates for a range of days.
static func dateRange(start: Date = Date(), days: Int) -> (start: Date, end: Date) {
let end = Calendar.current.date(byAdding: .day, value: days, to: start)!
static func dateRange(start: Date = TestClock.now, days: Int) -> (start: Date, end: Date) {
let end = TestClock.calendar.date(byAdding: .day, value: days, to: start)!
return (start, end)
}