// // HistoricalGameScraperTests.swift // SportsTimeTests // // TDD specification tests for HistoricalGameScraper types. // import Testing import Foundation @testable import SportsTime // MARK: - ScrapedGame Tests @Suite("ScrapedGame") struct ScrapedGameTests { // MARK: - Test Data private func makeGame( homeTeam: String = "Yankees", awayTeam: String = "Red Sox", homeScore: Int? = 5, awayScore: Int? = 3, stadiumName: String = "Yankee Stadium", sport: Sport = .mlb ) -> ScrapedGame { ScrapedGame( date: TestClock.now, homeTeam: homeTeam, awayTeam: awayTeam, homeScore: homeScore, awayScore: awayScore, stadiumName: stadiumName, sport: sport ) } // MARK: - Specification Tests: formattedScore /// - Expected Behavior: Format is "awayTeam awayScore - homeTeam homeScore" @Test("formattedScore: formats as 'away score - home score'") func formattedScore_format() { let game = makeGame(homeTeam: "Yankees", awayTeam: "Red Sox", homeScore: 5, awayScore: 3) #expect(game.formattedScore == "Red Sox 3 - Yankees 5") } /// - Expected Behavior: nil when homeScore is nil @Test("formattedScore: nil when homeScore nil") func formattedScore_nilHomeScore() { let game = makeGame(homeScore: nil, awayScore: 3) #expect(game.formattedScore == nil) } /// - Expected Behavior: nil when awayScore is nil @Test("formattedScore: nil when awayScore nil") func formattedScore_nilAwayScore() { let game = makeGame(homeScore: 5, awayScore: nil) #expect(game.formattedScore == nil) } /// - Expected Behavior: nil when both scores are nil @Test("formattedScore: nil when both scores nil") func formattedScore_bothNil() { let game = makeGame(homeScore: nil, awayScore: nil) #expect(game.formattedScore == nil) } // MARK: - Specification Tests: Properties @Test("ScrapedGame: stores date") func scrapedGame_date() { let date = TestClock.now let game = ScrapedGame( date: date, homeTeam: "Home", awayTeam: "Away", homeScore: 1, awayScore: 0, stadiumName: "Stadium", sport: .mlb ) #expect(game.date == date) } @Test("ScrapedGame: stores homeTeam") func scrapedGame_homeTeam() { let game = makeGame(homeTeam: "Home Team") #expect(game.homeTeam == "Home Team") } @Test("ScrapedGame: stores awayTeam") func scrapedGame_awayTeam() { let game = makeGame(awayTeam: "Away Team") #expect(game.awayTeam == "Away Team") } @Test("ScrapedGame: stores stadiumName") func scrapedGame_stadiumName() { let game = makeGame(stadiumName: "Test Stadium") #expect(game.stadiumName == "Test Stadium") } @Test("ScrapedGame: stores sport") func scrapedGame_sport() { let game = makeGame(sport: .nba) #expect(game.sport == .nba) } // MARK: - Edge Cases @Test("formattedScore: handles zero scores") func formattedScore_zeroScores() { let game = makeGame(homeScore: 0, awayScore: 0) #expect(game.formattedScore == "Red Sox 0 - Yankees 0") } @Test("formattedScore: handles high scores") func formattedScore_highScores() { let game = makeGame(homeScore: 123, awayScore: 456) #expect(game.formattedScore == "Red Sox 456 - Yankees 123") } // MARK: - Invariant Tests /// - Invariant: formattedScore != nil implies both scores are present @Test("Invariant: formattedScore implies both scores present") func invariant_formattedScoreImpliesBothScores() { let withScore = makeGame(homeScore: 5, awayScore: 3) if withScore.formattedScore != nil { #expect(withScore.homeScore != nil) #expect(withScore.awayScore != nil) } } /// - Invariant: formattedScore == nil implies at least one score is nil @Test("Invariant: nil formattedScore implies missing score") func invariant_nilFormattedScoreImpliesMissingScore() { let testCases: [(homeScore: Int?, awayScore: Int?)] = [ (nil, 3), (5, nil), (nil, nil) ] for (home, away) in testCases { let game = makeGame(homeScore: home, awayScore: away) if game.formattedScore == nil { #expect(home == nil || away == nil) } } } }