Files
Sportstime/SportsTimeTests/Domain/TripStopTests.swift
2026-02-18 13:00:15 -06:00

214 lines
7.1 KiB
Swift

//
// TripStopTests.swift
// SportsTimeTests
//
// TDD specification tests for TripStop model.
//
import Testing
import Foundation
@testable import SportsTime
@Suite("TripStop")
struct TripStopTests {
// MARK: - Test Data
private func makeStop(
arrivalDate: Date,
departureDate: Date,
games: [String] = []
) -> TripStop {
TripStop(
stopNumber: 1,
city: "New York",
state: "NY",
arrivalDate: arrivalDate,
departureDate: departureDate,
games: games
)
}
// MARK: - Specification Tests: stayDuration
@Test("stayDuration: same day arrival and departure returns 1")
func stayDuration_sameDay() {
let calendar = TestClock.calendar
let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)
#expect(stop.stayDuration == 1)
}
@Test("stayDuration: 2-day stay returns 2")
func stayDuration_twoDays() {
let calendar = TestClock.calendar
let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 16))!
let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)
#expect(stop.stayDuration == 1, "1 day between 15th and 16th")
}
@Test("stayDuration: week-long stay")
func stayDuration_weekLong() {
let calendar = TestClock.calendar
let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 22))!
let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)
#expect(stop.stayDuration == 7, "7 days between 15th and 22nd")
}
@Test("stayDuration: minimum is 1 even if dates are reversed")
func stayDuration_minimumIsOne() {
let calendar = TestClock.calendar
let arrivalDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 20))!
let departureDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)
#expect(stop.stayDuration >= 1, "stayDuration should never be less than 1")
}
// MARK: - Specification Tests: hasGames
@Test("hasGames: true when games array is non-empty")
func hasGames_true() {
let now = TestClock.now
let stop = makeStop(arrivalDate: now, departureDate: now, games: ["game1", "game2"])
#expect(stop.hasGames == true)
}
@Test("hasGames: false when games array is empty")
func hasGames_false() {
let now = TestClock.now
let stop = makeStop(arrivalDate: now, departureDate: now, games: [])
#expect(stop.hasGames == false)
}
// MARK: - Specification Tests: formattedDateRange
@Test("formattedDateRange: single date for 1-day stay")
func formattedDateRange_singleDay() {
let calendar = TestClock.calendar
let date = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let stop = makeStop(arrivalDate: date, departureDate: date)
// Should show just "Jun 15"
#expect(stop.formattedDateRange == "Jun 15")
}
@Test("formattedDateRange: range for multi-day stay")
func formattedDateRange_multiDay() {
let calendar = TestClock.calendar
let arrival = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))!
let departure = calendar.date(from: DateComponents(year: 2026, month: 6, day: 18))!
let stop = makeStop(arrivalDate: arrival, departureDate: departure)
// Should show "Jun 15 - Jun 18"
#expect(stop.formattedDateRange == "Jun 15 - Jun 18")
}
// MARK: - Specification Tests: locationDescription
@Test("locationDescription: combines city and state")
func locationDescription_format() {
let stop = TripStop(
stopNumber: 1,
city: "Boston",
state: "MA",
arrivalDate: TestClock.now,
departureDate: TestClock.now
)
#expect(stop.locationDescription == "Boston, MA")
}
// MARK: - Invariant Tests
@Test("Invariant: stayDuration >= 1")
func invariant_stayDurationAtLeastOne() {
let calendar = TestClock.calendar
// Test various date combinations
let testCases: [(arrival: DateComponents, departure: DateComponents)] = [
(DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 15)),
(DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 16)),
(DateComponents(year: 2026, month: 6, day: 15), DateComponents(year: 2026, month: 6, day: 22)),
(DateComponents(year: 2026, month: 12, day: 31), DateComponents(year: 2027, month: 1, day: 2)),
]
for (arrival, departure) in testCases {
let arrivalDate = calendar.date(from: arrival)!
let departureDate = calendar.date(from: departure)!
let stop = makeStop(arrivalDate: arrivalDate, departureDate: departureDate)
#expect(stop.stayDuration >= 1, "stayDuration should be at least 1")
}
}
@Test("Invariant: hasGames equals !games.isEmpty")
func invariant_hasGamesConsistent() {
let now = TestClock.now
let stopWithGames = makeStop(arrivalDate: now, departureDate: now, games: ["game1"])
#expect(stopWithGames.hasGames == !stopWithGames.games.isEmpty)
let stopWithoutGames = makeStop(arrivalDate: now, departureDate: now, games: [])
#expect(stopWithoutGames.hasGames == !stopWithoutGames.games.isEmpty)
}
// MARK: - Property Tests
@Test("Property: isRestDay defaults to false")
func property_isRestDayDefault() {
let now = TestClock.now
let stop = makeStop(arrivalDate: now, departureDate: now)
#expect(stop.isRestDay == false)
}
@Test("Property: isRestDay can be set to true")
func property_isRestDayTrue() {
let stop = TripStop(
stopNumber: 1,
city: "City",
state: "ST",
arrivalDate: TestClock.now,
departureDate: TestClock.now,
isRestDay: true
)
#expect(stop.isRestDay == true)
}
@Test("Property: optional fields can be nil")
func property_optionalFieldsNil() {
let stop = TripStop(
stopNumber: 1,
city: "City",
state: "ST",
coordinate: nil,
arrivalDate: TestClock.now,
departureDate: TestClock.now,
stadium: nil,
lodging: nil,
notes: nil
)
#expect(stop.coordinate == nil)
#expect(stop.stadium == nil)
#expect(stop.lodging == nil)
#expect(stop.notes == nil)
}
}