refactor(tests): TDD rewrite of all unit tests with spec documentation
Complete rewrite of unit test suite using TDD methodology: Planning Engine Tests: - GameDAGRouterTests: Beam search, anchor games, transitions - ItineraryBuilderTests: Stop connection, validators, EV enrichment - RouteFiltersTests: Region, time window, scoring filters - ScenarioA/B/C/D PlannerTests: All planning scenarios - TravelEstimatorTests: Distance, duration, travel days - TripPlanningEngineTests: Orchestration, caching, preferences Domain Model Tests: - AchievementDefinitionsTests, AnySportTests, DivisionTests - GameTests, ProgressTests, RegionTests, StadiumTests - TeamTests, TravelSegmentTests, TripTests, TripPollTests - TripPreferencesTests, TripStopTests, SportTests Service Tests: - FreeScoreAPITests, RouteDescriptionGeneratorTests - SuggestedTripsGeneratorTests Export Tests: - ShareableContentTests (card types, themes, dimensions) Bug fixes discovered through TDD: - ShareCardDimensions: mapSnapshotSize exceeded available width (960x480) - ScenarioBPlanner: Added anchor game validation filter All tests include: - Specification tests (expected behavior) - Invariant tests (properties that must always hold) - Edge case tests (boundary conditions) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,52 +2,202 @@
|
||||
// SportTests.swift
|
||||
// SportsTimeTests
|
||||
//
|
||||
// TDD specification tests for Sport enum.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
import Foundation
|
||||
@testable import SportsTime
|
||||
|
||||
@Suite("Sport AnySport Conformance")
|
||||
struct SportAnySportTests {
|
||||
@Suite("Sport")
|
||||
struct SportTests {
|
||||
|
||||
@Test("Sport conforms to AnySport protocol")
|
||||
func sportConformsToAnySport() {
|
||||
let sport: any AnySport = Sport.mlb
|
||||
#expect(sport.sportId == "MLB")
|
||||
#expect(sport.displayName == "Major League Baseball")
|
||||
#expect(sport.iconName == "baseball.fill")
|
||||
// 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("Sport.id equals Sport.sportId")
|
||||
func sportIdEqualsSportId() {
|
||||
for sport in Sport.allCases {
|
||||
#expect(sport.id == sport.sportId)
|
||||
@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 = Calendar.current
|
||||
|
||||
// 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)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Sport isInSeason works correctly")
|
||||
func sportIsInSeason() {
|
||||
let mlb = Sport.mlb
|
||||
// MARK: - Specification Tests: isInSeason (Wrap-Around)
|
||||
|
||||
// April is in MLB season (March-October)
|
||||
let april = Calendar.current.date(from: DateComponents(year: 2026, month: 4, day: 15))!
|
||||
#expect(mlb.isInSeason(for: april))
|
||||
@Test("NBA: isInSeason returns true for months 10-12 and 1-6 (wrap-around)")
|
||||
func nba_isInSeason_wrapAround() {
|
||||
let calendar = Calendar.current
|
||||
|
||||
// January is not in MLB season
|
||||
let january = Calendar.current.date(from: DateComponents(year: 2026, month: 1, day: 15))!
|
||||
#expect(!mlb.isInSeason(for: january))
|
||||
// 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("Sport with wrap-around season works correctly")
|
||||
func sportWrapAroundSeason() {
|
||||
let nba = Sport.nba
|
||||
@Test("NFL: isInSeason returns true for months 9-12 and 1-2 (wrap-around)")
|
||||
func nfl_isInSeason_wrapAround() {
|
||||
let calendar = Calendar.current
|
||||
|
||||
// December is in NBA season (October-June wraps)
|
||||
let december = Calendar.current.date(from: DateComponents(year: 2026, month: 12, day: 15))!
|
||||
#expect(nba.isInSeason(for: december))
|
||||
// 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)")
|
||||
}
|
||||
|
||||
// July is not in NBA season
|
||||
let july = Calendar.current.date(from: DateComponents(year: 2026, month: 7, day: 15))!
|
||||
#expect(!nba.isInSeason(for: july))
|
||||
// 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() {
|
||||
let calendar = Calendar.current
|
||||
|
||||
// MLB: First day of March (in season)
|
||||
let marchFirst = calendar.date(from: DateComponents(year: 2026, month: 3, day: 1))!
|
||||
#expect(Sport.mlb.isInSeason(for: marchFirst))
|
||||
|
||||
// MLB: Last day of October (in season)
|
||||
let octLast = calendar.date(from: DateComponents(year: 2026, month: 10, day: 31))!
|
||||
#expect(Sport.mlb.isInSeason(for: octLast))
|
||||
|
||||
// MLB: First day of November (out of season)
|
||||
let novFirst = calendar.date(from: DateComponents(year: 2026, month: 11, day: 1))!
|
||||
#expect(!Sport.mlb.isInSeason(for: novFirst))
|
||||
|
||||
// MLB: Last day of February (out of season)
|
||||
let febLast = calendar.date(from: DateComponents(year: 2026, month: 2, day: 28))!
|
||||
#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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user