// // DynamicSportTests.swift // SportsTimeTests // // TDD specification tests for DynamicSport model. // import Testing import Foundation import SwiftUI @testable import SportsTime @Suite("DynamicSport") struct DynamicSportTests { // MARK: - Test Data private func makeDynamicSport( id: String = "xfl", abbreviation: String = "XFL", displayName: String = "XFL Football", iconName: String = "football.fill", colorHex: String = "#FF0000", seasonStart: Int = 2, seasonEnd: Int = 5 ) -> DynamicSport { DynamicSport( id: id, abbreviation: abbreviation, displayName: displayName, iconName: iconName, colorHex: colorHex, seasonStartMonth: seasonStart, seasonEndMonth: seasonEnd ) } private var calendar: Calendar { TestClock.calendar } private func date(month: Int) -> Date { calendar.date(from: DateComponents(year: 2026, month: month, day: 15))! } // MARK: - Specification Tests: AnySport Conformance @Test("sportId: returns id") func sportId_returnsId() { let sport = makeDynamicSport(id: "test_sport") #expect(sport.sportId == "test_sport") } @Test("displayName: returns displayName property") func displayName_returnsProperty() { let sport = makeDynamicSport(displayName: "Test Sport Name") #expect(sport.displayName == "Test Sport Name") } @Test("iconName: returns iconName property") func iconName_returnsProperty() { let sport = makeDynamicSport(iconName: "custom.icon") #expect(sport.iconName == "custom.icon") } @Test("seasonMonths: returns (seasonStartMonth, seasonEndMonth)") func seasonMonths_returnsTuple() { let sport = makeDynamicSport(seasonStart: 3, seasonEnd: 10) let (start, end) = sport.seasonMonths #expect(start == 3) #expect(end == 10) } @Test("color: converts colorHex to Color") func color_convertsHex() { let sport = makeDynamicSport(colorHex: "#FF0000") // We can't directly compare Colors, but we can verify it doesn't crash // and returns some color value let _ = sport.color } // MARK: - Specification Tests: isInSeason (via AnySport) @Test("isInSeason: uses default AnySport implementation for normal season") func isInSeason_normalSeason() { // Season: February to May (normal range) let sport = makeDynamicSport(seasonStart: 2, seasonEnd: 5) #expect(sport.isInSeason(for: date(month: 2)) == true) #expect(sport.isInSeason(for: date(month: 3)) == true) #expect(sport.isInSeason(for: date(month: 5)) == true) #expect(sport.isInSeason(for: date(month: 1)) == false) #expect(sport.isInSeason(for: date(month: 6)) == false) } @Test("isInSeason: uses default AnySport implementation for wrap-around season") func isInSeason_wrapAroundSeason() { // Season: October to April (wraps around) let sport = makeDynamicSport(seasonStart: 10, seasonEnd: 4) #expect(sport.isInSeason(for: date(month: 10)) == true) #expect(sport.isInSeason(for: date(month: 12)) == true) #expect(sport.isInSeason(for: date(month: 1)) == true) #expect(sport.isInSeason(for: date(month: 4)) == true) #expect(sport.isInSeason(for: date(month: 6)) == false) #expect(sport.isInSeason(for: date(month: 8)) == false) } // MARK: - Specification Tests: Identifiable Conformance @Test("id: returns id property") func id_returnsIdProperty() { let sport = makeDynamicSport(id: "unique_id") #expect(sport.id == "unique_id") } // MARK: - Specification Tests: Hashable Conformance @Test("hashable: same values are equal") func hashable_equality() { let sport1 = makeDynamicSport(id: "test") let sport2 = makeDynamicSport(id: "test") #expect(sport1 == sport2) } @Test("hashable: different ids are not equal") func hashable_inequality() { let sport1 = makeDynamicSport(id: "test1") let sport2 = makeDynamicSport(id: "test2") #expect(sport1 != sport2) } // MARK: - Specification Tests: Codable Conformance @Test("codable: encodes and decodes correctly") func codable_roundTrip() throws { let sport = makeDynamicSport( id: "test_sport", abbreviation: "TST", displayName: "Test Sport", iconName: "star.fill", colorHex: "#00FF00", seasonStart: 4, seasonEnd: 9 ) let encoded = try JSONEncoder().encode(sport) let decoded = try JSONDecoder().decode(DynamicSport.self, from: encoded) #expect(decoded.id == sport.id) #expect(decoded.abbreviation == sport.abbreviation) #expect(decoded.displayName == sport.displayName) #expect(decoded.iconName == sport.iconName) #expect(decoded.colorHex == sport.colorHex) #expect(decoded.seasonStartMonth == sport.seasonStartMonth) #expect(decoded.seasonEndMonth == sport.seasonEndMonth) } // MARK: - Invariant Tests @Test("Invariant: sportId == id") func invariant_sportIdEqualsId() { let sport = makeDynamicSport(id: "any_id") #expect(sport.sportId == sport.id) } @Test("Invariant: seasonMonths matches individual properties") func invariant_seasonMonthsMatchesProperties() { let sport = makeDynamicSport(seasonStart: 5, seasonEnd: 11) let (start, end) = sport.seasonMonths #expect(start == sport.seasonStartMonth) #expect(end == sport.seasonEndMonth) } }