Files
Sportstime/SportsTimeTests/Services/HistoricalGameScraperTests.swift
2026-02-18 13:00:15 -06:00

152 lines
4.5 KiB
Swift

//
// 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)
}
}
}
}