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>
206 lines
7.0 KiB
Swift
206 lines
7.0 KiB
Swift
//
|
|
// SportTests.swift
|
|
// SportsTimeTests
|
|
//
|
|
// TDD specification tests for Sport enum.
|
|
//
|
|
|
|
import Testing
|
|
import Foundation
|
|
@testable import SportsTime
|
|
|
|
@Suite("Sport")
|
|
struct SportTests {
|
|
|
|
// MARK: - Specification Tests: Season Months
|
|
|
|
@Test("MLB season: March (3) to October (10)")
|
|
func mlb_seasonMonths() {
|
|
let (start, end) = Sport.mlb.seasonMonths
|
|
#expect(start == 3)
|
|
#expect(end == 10)
|
|
}
|
|
|
|
@Test("NBA season: October (10) to June (6) - wraps around year")
|
|
func nba_seasonMonths() {
|
|
let (start, end) = Sport.nba.seasonMonths
|
|
#expect(start == 10)
|
|
#expect(end == 6)
|
|
}
|
|
|
|
@Test("NHL season: October (10) to June (6) - wraps around year")
|
|
func nhl_seasonMonths() {
|
|
let (start, end) = Sport.nhl.seasonMonths
|
|
#expect(start == 10)
|
|
#expect(end == 6)
|
|
}
|
|
|
|
@Test("NFL season: September (9) to February (2) - wraps around year")
|
|
func nfl_seasonMonths() {
|
|
let (start, end) = Sport.nfl.seasonMonths
|
|
#expect(start == 9)
|
|
#expect(end == 2)
|
|
}
|
|
|
|
@Test("MLS season: February (2) to December (12)")
|
|
func mls_seasonMonths() {
|
|
let (start, end) = Sport.mls.seasonMonths
|
|
#expect(start == 2)
|
|
#expect(end == 12)
|
|
}
|
|
|
|
@Test("WNBA season: May (5) to October (10)")
|
|
func wnba_seasonMonths() {
|
|
let (start, end) = Sport.wnba.seasonMonths
|
|
#expect(start == 5)
|
|
#expect(end == 10)
|
|
}
|
|
|
|
@Test("NWSL season: March (3) to November (11)")
|
|
func nwsl_seasonMonths() {
|
|
let (start, end) = Sport.nwsl.seasonMonths
|
|
#expect(start == 3)
|
|
#expect(end == 11)
|
|
}
|
|
|
|
// MARK: - Specification Tests: isInSeason (Normal Range)
|
|
|
|
@Test("MLB: isInSeason returns true for months 3-10")
|
|
func mlb_isInSeason_normalRange() {
|
|
let calendar = TestClock.calendar
|
|
|
|
// In season: March through October
|
|
for month in 3...10 {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(Sport.mlb.isInSeason(for: date), "MLB should be in season in month \(month)")
|
|
}
|
|
|
|
// Out of season: January, February, November, December
|
|
for month in [1, 2, 11, 12] {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(!Sport.mlb.isInSeason(for: date), "MLB should NOT be in season in month \(month)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Specification Tests: isInSeason (Wrap-Around)
|
|
|
|
@Test("NBA: isInSeason returns true for months 10-12 and 1-6 (wrap-around)")
|
|
func nba_isInSeason_wrapAround() {
|
|
let calendar = TestClock.calendar
|
|
|
|
// In season: October through June (wraps)
|
|
let inSeasonMonths = [10, 11, 12, 1, 2, 3, 4, 5, 6]
|
|
for month in inSeasonMonths {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(Sport.nba.isInSeason(for: date), "NBA should be in season in month \(month)")
|
|
}
|
|
|
|
// Out of season: July, August, September
|
|
for month in [7, 8, 9] {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(!Sport.nba.isInSeason(for: date), "NBA should NOT be in season in month \(month)")
|
|
}
|
|
}
|
|
|
|
@Test("NFL: isInSeason returns true for months 9-12 and 1-2 (wrap-around)")
|
|
func nfl_isInSeason_wrapAround() {
|
|
let calendar = TestClock.calendar
|
|
|
|
// In season: September through February (wraps)
|
|
let inSeasonMonths = [9, 10, 11, 12, 1, 2]
|
|
for month in inSeasonMonths {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(Sport.nfl.isInSeason(for: date), "NFL should be in season in month \(month)")
|
|
}
|
|
|
|
// Out of season: March through August
|
|
for month in 3...8 {
|
|
let date = calendar.date(from: DateComponents(year: 2026, month: month, day: 15))!
|
|
#expect(!Sport.nfl.isInSeason(for: date), "NFL should NOT be in season in month \(month)")
|
|
}
|
|
}
|
|
|
|
// MARK: - Specification Tests: Boundary Values
|
|
|
|
@Test("isInSeason boundary: first and last day of season month")
|
|
func isInSeason_boundaryDays() {
|
|
// Production isInSeason uses Calendar.current to extract month.
|
|
// Use noon (hour: 12) so the date stays in the correct calendar day
|
|
// regardless of system timezone across the US.
|
|
|
|
// MLB: First day of March (in season)
|
|
let marchFirst = TestFixtures.date(year: 2026, month: 3, day: 1, hour: 12)
|
|
#expect(Sport.mlb.isInSeason(for: marchFirst))
|
|
|
|
// MLB: Last day of October (in season)
|
|
let octLast = TestFixtures.date(year: 2026, month: 10, day: 31, hour: 12)
|
|
#expect(Sport.mlb.isInSeason(for: octLast))
|
|
|
|
// MLB: First day of November (out of season)
|
|
let novFirst = TestFixtures.date(year: 2026, month: 11, day: 1, hour: 12)
|
|
#expect(!Sport.mlb.isInSeason(for: novFirst))
|
|
|
|
// MLB: Last day of February (out of season)
|
|
let febLast = TestFixtures.date(year: 2026, month: 2, day: 28, hour: 12)
|
|
#expect(!Sport.mlb.isInSeason(for: febLast))
|
|
}
|
|
|
|
// MARK: - Specification Tests: Supported Sports
|
|
|
|
@Test("supported returns all 7 sports")
|
|
func supported_returnsAllSports() {
|
|
let supported = Sport.supported
|
|
#expect(supported.count == 7)
|
|
#expect(supported.contains(.mlb))
|
|
#expect(supported.contains(.nba))
|
|
#expect(supported.contains(.nhl))
|
|
#expect(supported.contains(.nfl))
|
|
#expect(supported.contains(.mls))
|
|
#expect(supported.contains(.wnba))
|
|
#expect(supported.contains(.nwsl))
|
|
}
|
|
|
|
@Test("CaseIterable matches supported")
|
|
func caseIterable_matchesSupported() {
|
|
let allCases = Set(Sport.allCases)
|
|
let supported = Set(Sport.supported)
|
|
#expect(allCases == supported)
|
|
}
|
|
|
|
// MARK: - Invariant Tests
|
|
|
|
@Test("Invariant: seasonMonths values are always 1-12")
|
|
func invariant_seasonMonthsValidRange() {
|
|
for sport in Sport.allCases {
|
|
let (start, end) = sport.seasonMonths
|
|
#expect(start >= 1 && start <= 12, "\(sport) start month must be 1-12")
|
|
#expect(end >= 1 && end <= 12, "\(sport) end month must be 1-12")
|
|
}
|
|
}
|
|
|
|
@Test("Invariant: each sport has unique displayName")
|
|
func invariant_uniqueDisplayNames() {
|
|
var displayNames: Set<String> = []
|
|
for sport in Sport.allCases {
|
|
#expect(!displayNames.contains(sport.displayName), "Duplicate displayName: \(sport.displayName)")
|
|
displayNames.insert(sport.displayName)
|
|
}
|
|
}
|
|
|
|
// MARK: - Property Tests
|
|
|
|
@Test("Property: id equals rawValue")
|
|
func property_idEqualsRawValue() {
|
|
for sport in Sport.allCases {
|
|
#expect(sport.id == sport.rawValue)
|
|
}
|
|
}
|
|
|
|
@Test("Property: sportId equals rawValue (AnySport conformance)")
|
|
func property_sportIdEqualsRawValue() {
|
|
for sport in Sport.allCases {
|
|
#expect(sport.sportId == sport.rawValue)
|
|
}
|
|
}
|
|
}
|