Systematic audit of 1,191 tests found tests written to pass rather than verify correctness. Key fixes: Infrastructure: - TestClock: fixed timezone from .current to America/New_York (deterministic) - TestFixtures: added 1.3x road routing factor to match production - ItineraryTestHelpers: real per-city coordinates instead of hardcoded (40,-80) Planning tests: - Added missing Scenario E factory dispatch tests - Tightened 12 loose assertions (>= 1 → == 8.0, > 0 → range checks) - Fixed 4 no-op tests that accepted both success and failure - Fixed wrong repeat-city invariant (was checking same-day, not different-day) - Fixed tautological assertion in missing-stadium edge case Services/Domain/Export tests: - Replaced 4 placeholder tests (#expect(true)) with real assertions - Fixed tautological assertions in POISearchServiceTests - Fixed Chicago coordinate in RegionMapSelectorTests (-89 → -87.6553) - Added sort order verification to ItineraryRowFlatteningTests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
211 lines
6.4 KiB
Swift
211 lines
6.4 KiB
Swift
//
|
|
// GameTests.swift
|
|
// SportsTimeTests
|
|
//
|
|
// TDD specification tests for Game model.
|
|
//
|
|
|
|
import Testing
|
|
import Foundation
|
|
@testable import SportsTime
|
|
|
|
@Suite("Game")
|
|
@MainActor
|
|
struct GameTests {
|
|
|
|
// MARK: - Test Data
|
|
|
|
private func makeGame(dateTime: Date) -> Game {
|
|
Game(
|
|
id: "game1",
|
|
homeTeamId: "team1",
|
|
awayTeamId: "team2",
|
|
stadiumId: "stadium1",
|
|
dateTime: dateTime,
|
|
sport: .mlb,
|
|
season: "2026",
|
|
isPlayoff: false
|
|
)
|
|
}
|
|
|
|
// MARK: - Specification Tests: gameDate
|
|
|
|
@Test("gameDate returns start of day for dateTime")
|
|
func gameDate_returnsStartOfDay() {
|
|
// Use TestFixtures.date which creates dates at 7:05 PM EST — safely same
|
|
// calendar day in any US timezone when interpreted by Calendar.current.
|
|
let dateTime = TestFixtures.date(year: 2026, month: 6, day: 15, hour: 19, minute: 5)
|
|
|
|
let game = makeGame(dateTime: dateTime)
|
|
|
|
// Production gameDate uses Calendar.current, so assert with the same calendar
|
|
let systemCalendar = Calendar.current
|
|
let expectedStart = systemCalendar.startOfDay(for: dateTime)
|
|
#expect(game.gameDate == expectedStart)
|
|
|
|
// Verify it's at midnight in the system calendar
|
|
let components = systemCalendar.dateComponents([.hour, .minute, .second], from: game.gameDate)
|
|
#expect(components.hour == 0)
|
|
#expect(components.minute == 0)
|
|
#expect(components.second == 0)
|
|
}
|
|
|
|
@Test("gameDate is same for games on same calendar day")
|
|
func gameDate_sameDay() {
|
|
let calendar = TestClock.calendar
|
|
|
|
// Morning game
|
|
let morningTime = calendar.date(from: DateComponents(
|
|
year: 2026, month: 6, day: 15,
|
|
hour: 10, minute: 0
|
|
))!
|
|
|
|
// Evening game
|
|
let eveningTime = calendar.date(from: DateComponents(
|
|
year: 2026, month: 6, day: 15,
|
|
hour: 20, minute: 0
|
|
))!
|
|
|
|
let morningGame = makeGame(dateTime: morningTime)
|
|
let eveningGame = makeGame(dateTime: eveningTime)
|
|
|
|
#expect(morningGame.gameDate == eveningGame.gameDate)
|
|
}
|
|
|
|
@Test("gameDate differs for games on different calendar days")
|
|
func gameDate_differentDays() {
|
|
let calendar = TestClock.calendar
|
|
|
|
let day1 = calendar.date(from: DateComponents(
|
|
year: 2026, month: 6, day: 15, hour: 19
|
|
))!
|
|
let day2 = calendar.date(from: DateComponents(
|
|
year: 2026, month: 6, day: 16, hour: 19
|
|
))!
|
|
|
|
let game1 = makeGame(dateTime: day1)
|
|
let game2 = makeGame(dateTime: day2)
|
|
|
|
#expect(game1.gameDate != game2.gameDate)
|
|
}
|
|
|
|
// MARK: - Specification Tests: startTime Alias
|
|
|
|
@Test("startTime is alias for dateTime")
|
|
func startTime_isAliasForDateTime() {
|
|
let dateTime = TestClock.now
|
|
let game = makeGame(dateTime: dateTime)
|
|
|
|
#expect(game.startTime == game.dateTime)
|
|
}
|
|
|
|
// MARK: - Specification Tests: Equality
|
|
|
|
@Test("equality based on id only")
|
|
func equality_basedOnId() {
|
|
let dateTime = TestClock.now
|
|
|
|
let game1 = Game(
|
|
id: "game1",
|
|
homeTeamId: "team1",
|
|
awayTeamId: "team2",
|
|
stadiumId: "stadium1",
|
|
dateTime: dateTime,
|
|
sport: .mlb,
|
|
season: "2026",
|
|
isPlayoff: false
|
|
)
|
|
|
|
// Same id, different fields
|
|
let game2 = Game(
|
|
id: "game1",
|
|
homeTeamId: "different-team",
|
|
awayTeamId: "different-team2",
|
|
stadiumId: "different-stadium",
|
|
dateTime: dateTime.addingTimeInterval(3600),
|
|
sport: .nba,
|
|
season: "2027",
|
|
isPlayoff: true
|
|
)
|
|
|
|
#expect(game1 == game2, "Games with same id should be equal")
|
|
}
|
|
|
|
@Test("inequality when ids differ")
|
|
func inequality_differentIds() {
|
|
let dateTime = TestClock.now
|
|
|
|
let game1 = Game(
|
|
id: "game1",
|
|
homeTeamId: "team1",
|
|
awayTeamId: "team2",
|
|
stadiumId: "stadium1",
|
|
dateTime: dateTime,
|
|
sport: .mlb,
|
|
season: "2026",
|
|
isPlayoff: false
|
|
)
|
|
|
|
let game2 = Game(
|
|
id: "game2",
|
|
homeTeamId: "team1",
|
|
awayTeamId: "team2",
|
|
stadiumId: "stadium1",
|
|
dateTime: dateTime,
|
|
sport: .mlb,
|
|
season: "2026",
|
|
isPlayoff: false
|
|
)
|
|
|
|
#expect(game1 != game2, "Games with different ids should not be equal")
|
|
}
|
|
|
|
// MARK: - Invariant Tests
|
|
|
|
@Test("Invariant: gameDate is always at midnight")
|
|
func invariant_gameDateAtMidnight() {
|
|
// Production gameDate uses Calendar.current, so create dates and assert
|
|
// with Calendar.current to avoid cross-timezone mismatches.
|
|
// Use TestFixtures.date (7pm EST default) to ensure same calendar day in any US tz.
|
|
let times = [8, 12, 15, 19, 22].map { hour in
|
|
TestFixtures.date(year: 2026, month: 6, day: 15, hour: hour)
|
|
}
|
|
|
|
let systemCalendar = Calendar.current
|
|
for time in times {
|
|
let game = makeGame(dateTime: time)
|
|
let components = systemCalendar.dateComponents([.hour, .minute, .second], from: game.gameDate)
|
|
#expect(components.hour == 0, "gameDate hour should be 0")
|
|
#expect(components.minute == 0, "gameDate minute should be 0")
|
|
#expect(components.second == 0, "gameDate second should be 0")
|
|
}
|
|
}
|
|
|
|
@Test("Invariant: startTime equals dateTime")
|
|
func invariant_startTimeEqualsDateTime() {
|
|
for _ in 0..<10 {
|
|
let dateTime = TestClock.now.addingTimeInterval(Double.random(in: -86400...86400))
|
|
let game = makeGame(dateTime: dateTime)
|
|
#expect(game.startTime == game.dateTime)
|
|
}
|
|
}
|
|
|
|
// MARK: - Property Tests
|
|
|
|
@Test("Property: gameDate is in same calendar day as dateTime")
|
|
func property_gameDateSameCalendarDay() {
|
|
let calendar = TestClock.calendar
|
|
|
|
let dateTime = calendar.date(from: DateComponents(
|
|
year: 2026, month: 7, day: 4, hour: 19, minute: 5
|
|
))!
|
|
|
|
let game = makeGame(dateTime: dateTime)
|
|
|
|
let dateTimeDay = calendar.component(.day, from: dateTime)
|
|
let gameDateDay = calendar.component(.day, from: game.gameDate)
|
|
|
|
#expect(dateTimeDay == gameDateDay)
|
|
}
|
|
}
|