// // 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() { // formattedDateRange uses DateFormatter with system timezone, so create dates // at noon to ensure the calendar day is stable across US timezones. let date = TestFixtures.date(year: 2026, month: 6, day: 15, hour: 12) 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() { // formattedDateRange uses DateFormatter with system timezone, so create dates // at noon to ensure the calendar day is stable across US timezones. let arrival = TestFixtures.date(year: 2026, month: 6, day: 15, hour: 12) let departure = TestFixtures.date(year: 2026, month: 6, day: 18, hour: 12) 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) } }