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>
152 lines
4.5 KiB
Swift
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: Date(),
|
|
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 = Date()
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|