// // TripPreferencesTests.swift // SportsTimeTests // // TDD specification tests for TripPreferences and related enums. // import Testing import Foundation import CoreLocation @testable import SportsTime @Suite("TripPreferences") struct TripPreferencesTests { // MARK: - Specification Tests: totalDriverHoursPerDay @Test("totalDriverHoursPerDay: uses default 8 hours when maxDrivingHoursPerDriver is nil") func totalDriverHoursPerDay_default() { let prefs = TripPreferences( numberOfDrivers: 1, maxDrivingHoursPerDriver: nil ) #expect(prefs.totalDriverHoursPerDay == 8.0) } @Test("totalDriverHoursPerDay: multiplies by number of drivers") func totalDriverHoursPerDay_multipleDrivers() { let prefs = TripPreferences( numberOfDrivers: 2, maxDrivingHoursPerDriver: nil ) #expect(prefs.totalDriverHoursPerDay == 16.0) // 8 * 2 } @Test("totalDriverHoursPerDay: uses custom maxDrivingHoursPerDriver") func totalDriverHoursPerDay_custom() { let prefs = TripPreferences( numberOfDrivers: 2, maxDrivingHoursPerDriver: 6.0 ) #expect(prefs.totalDriverHoursPerDay == 12.0) // 6 * 2 } // MARK: - Specification Tests: effectiveTripDuration @Test("effectiveTripDuration: uses tripDuration when set") func effectiveTripDuration_explicit() { let prefs = TripPreferences( startDate: Date(), endDate: Date().addingTimeInterval(86400 * 14), tripDuration: 5 ) #expect(prefs.effectiveTripDuration == 5) } @Test("effectiveTripDuration: calculates from date range when tripDuration is nil") func effectiveTripDuration_calculated() { let calendar = Calendar.current let startDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 15))! let endDate = calendar.date(from: DateComponents(year: 2026, month: 6, day: 22))! let prefs = TripPreferences( startDate: startDate, endDate: endDate, tripDuration: nil ) #expect(prefs.effectiveTripDuration == 8) // 8 days inclusive (15th through 22nd) } @Test("effectiveTripDuration: minimum is 1") func effectiveTripDuration_minimum() { let date = Date() let prefs = TripPreferences( startDate: date, endDate: date, tripDuration: nil ) #expect(prefs.effectiveTripDuration >= 1) } // MARK: - Invariant Tests @Test("Invariant: totalDriverHoursPerDay > 0") func invariant_totalDriverHoursPositive() { // With 1 driver and default let prefs1 = TripPreferences(numberOfDrivers: 1) #expect(prefs1.totalDriverHoursPerDay > 0) // With multiple drivers let prefs2 = TripPreferences(numberOfDrivers: 3, maxDrivingHoursPerDriver: 4) #expect(prefs2.totalDriverHoursPerDay > 0) } @Test("Invariant: effectiveTripDuration >= 1") func invariant_effectiveTripDurationMinimum() { let testCases: [Int?] = [nil, 1, 5, 10] for duration in testCases { let prefs = TripPreferences(tripDuration: duration) #expect(prefs.effectiveTripDuration >= 1) } } } // MARK: - LeisureLevel Tests @Suite("LeisureLevel") struct LeisureLevelTests { @Test("restDaysPerWeek: packed = 0.5") func restDaysPerWeek_packed() { #expect(LeisureLevel.packed.restDaysPerWeek == 0.5) } @Test("restDaysPerWeek: moderate = 1.5") func restDaysPerWeek_moderate() { #expect(LeisureLevel.moderate.restDaysPerWeek == 1.5) } @Test("restDaysPerWeek: relaxed = 2.5") func restDaysPerWeek_relaxed() { #expect(LeisureLevel.relaxed.restDaysPerWeek == 2.5) } @Test("maxGamesPerWeek: packed = 7") func maxGamesPerWeek_packed() { #expect(LeisureLevel.packed.maxGamesPerWeek == 7) } @Test("maxGamesPerWeek: moderate = 5") func maxGamesPerWeek_moderate() { #expect(LeisureLevel.moderate.maxGamesPerWeek == 5) } @Test("maxGamesPerWeek: relaxed = 3") func maxGamesPerWeek_relaxed() { #expect(LeisureLevel.relaxed.maxGamesPerWeek == 3) } @Test("Invariant: restDaysPerWeek increases from packed to relaxed") func invariant_restDaysOrdering() { #expect(LeisureLevel.packed.restDaysPerWeek < LeisureLevel.moderate.restDaysPerWeek) #expect(LeisureLevel.moderate.restDaysPerWeek < LeisureLevel.relaxed.restDaysPerWeek) } @Test("Invariant: maxGamesPerWeek decreases from packed to relaxed") func invariant_maxGamesOrdering() { #expect(LeisureLevel.packed.maxGamesPerWeek > LeisureLevel.moderate.maxGamesPerWeek) #expect(LeisureLevel.moderate.maxGamesPerWeek > LeisureLevel.relaxed.maxGamesPerWeek) } } // MARK: - RoutePreference Tests @Suite("RoutePreference") struct RoutePreferenceTests { @Test("scenicWeight: direct = 0.0") func scenicWeight_direct() { #expect(RoutePreference.direct.scenicWeight == 0.0) } @Test("scenicWeight: scenic = 1.0") func scenicWeight_scenic() { #expect(RoutePreference.scenic.scenicWeight == 1.0) } @Test("scenicWeight: balanced = 0.5") func scenicWeight_balanced() { #expect(RoutePreference.balanced.scenicWeight == 0.5) } @Test("Invariant: scenicWeight is in range [0, 1]") func invariant_scenicWeightRange() { for pref in RoutePreference.allCases { #expect(pref.scenicWeight >= 0.0) #expect(pref.scenicWeight <= 1.0) } } } // MARK: - LocationInput Tests @Suite("LocationInput") struct LocationInputTests { @Test("isResolved: true when coordinate is set") func isResolved_true() { let input = LocationInput( name: "New York", coordinate: .init(latitude: 40.7, longitude: -74.0) ) #expect(input.isResolved == true) } @Test("isResolved: false when coordinate is nil") func isResolved_false() { let input = LocationInput(name: "New York", coordinate: nil) #expect(input.isResolved == false) } @Test("Invariant: isResolved equals (coordinate != nil)") func invariant_isResolvedConsistent() { let withCoord = LocationInput(name: "A", coordinate: .init(latitude: 0, longitude: 0)) let withoutCoord = LocationInput(name: "B", coordinate: nil) #expect(withCoord.isResolved == (withCoord.coordinate != nil)) #expect(withoutCoord.isResolved == (withoutCoord.coordinate != nil)) } } // MARK: - PlanningMode Tests @Suite("PlanningMode") struct PlanningModeTests { @Test("Property: all cases have displayName") func property_allHaveDisplayName() { for mode in PlanningMode.allCases { #expect(!mode.displayName.isEmpty) } } @Test("Property: all cases have description") func property_allHaveDescription() { for mode in PlanningMode.allCases { #expect(!mode.description.isEmpty) } } @Test("Property: all cases have iconName") func property_allHaveIconName() { for mode in PlanningMode.allCases { #expect(!mode.iconName.isEmpty) } } @Test("Property: id equals rawValue") func property_idEqualsRawValue() { for mode in PlanningMode.allCases { #expect(mode.id == mode.rawValue) } } } // MARK: - TravelMode Tests @Suite("TravelMode") struct TravelModeTests { @Test("Property: all cases have displayName") func property_allHaveDisplayName() { for mode in TravelMode.allCases { #expect(!mode.displayName.isEmpty) } } @Test("Property: all cases have iconName") func property_allHaveIconName() { for mode in TravelMode.allCases { #expect(!mode.iconName.isEmpty) } } } // MARK: - LodgingType Tests @Suite("LodgingType") struct LodgingTypeTests { @Test("Property: all cases have displayName") func property_allHaveDisplayName() { for type in LodgingType.allCases { #expect(!type.displayName.isEmpty) } } @Test("Property: all cases have iconName") func property_allHaveIconName() { for type in LodgingType.allCases { #expect(!type.iconName.isEmpty) } } }