Files
Sportstime/SportsTimeTests/TripOptionsGroupingTests.swift
Trey t 22772fa57f feat(store): add In-App Purchase system with Pro subscription
Implement freemium model with StoreKit 2:
- StoreManager singleton for purchase/restore/entitlements
- ProFeature enum defining gated features
- PaywallView and OnboardingPaywallView for upsell UI
- ProGate view modifier and ProBadge component

Feature gating:
- Trip saving: 1 free trip, then requires Pro
- PDF export: Pro only with badge indicator
- Progress tab: Shows ProLockedView for free users
- Settings: Subscription management section

Also fixes pre-existing test issues with StadiumVisit
and ItineraryOption model signature changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:41:40 -06:00

103 lines
3.5 KiB
Swift

//
// TripOptionsGroupingTests.swift
// SportsTimeTests
//
import Testing
import Foundation
import CoreLocation
@testable import SportsTime
struct TripOptionsGroupingTests {
// Helper to create mock ItineraryStop
private func makeStop(city: String, games: [String] = []) -> ItineraryStop {
ItineraryStop(
city: city,
state: "XX",
coordinate: CLLocationCoordinate2D(latitude: 0, longitude: 0),
games: games,
arrivalDate: Date(),
departureDate: Date(),
location: LocationInput(name: city, coordinate: nil),
firstGameStart: nil
)
}
// Helper to create mock ItineraryOption
private func makeOption(stops: [(city: String, games: [String])], totalMiles: Double = 500) -> ItineraryOption {
let itineraryStops = stops.map { makeStop(city: $0.city, games: $0.games) }
return ItineraryOption(
rank: 1,
stops: itineraryStops,
travelSegments: [],
totalDrivingHours: totalMiles / 60,
totalDistanceMiles: totalMiles,
geographicRationale: "Test"
)
}
@Test func groupByCityCountDescending() {
let options = [
makeOption(stops: [("NYC", []), ("Boston", [])]), // 2 cities
makeOption(stops: [("LA", []), ("SF", []), ("Seattle", [])]), // 3 cities
makeOption(stops: [("Chicago", [])]), // 1 city
]
let grouped = TripOptionsGrouper.groupByCityCount(options, ascending: false)
#expect(grouped.count == 3)
#expect(grouped[0].header == "3 cities")
#expect(grouped[1].header == "2 cities")
#expect(grouped[2].header == "1 city")
}
@Test func groupByGameCountAscending() {
let options = [
makeOption(stops: [("NYC", ["g1", "g2", "g3"])]), // 3 games
makeOption(stops: [("LA", ["g1"])]), // 1 game
makeOption(stops: [("Chicago", ["g1", "g2"])]), // 2 games
]
let grouped = TripOptionsGrouper.groupByGameCount(options, ascending: true)
#expect(grouped.count == 3)
#expect(grouped[0].header == "1 game")
#expect(grouped[1].header == "2 games")
#expect(grouped[2].header == "3 games")
}
@Test func groupByMileageRangeDescending() {
let options = [
makeOption(stops: [("NYC", [])], totalMiles: 300), // 0-500
makeOption(stops: [("LA", [])], totalMiles: 1200), // 1000-1500
makeOption(stops: [("Chicago", [])], totalMiles: 2500), // 2000+
]
let grouped = TripOptionsGrouper.groupByMileageRange(options, ascending: false)
#expect(grouped[0].header == "2000+ mi")
#expect(grouped[1].header == "1000-1500 mi")
#expect(grouped[2].header == "0-500 mi")
}
@Test func groupByMileageRangeAscending() {
let options = [
makeOption(stops: [("NYC", [])], totalMiles: 300),
makeOption(stops: [("LA", [])], totalMiles: 1200),
makeOption(stops: [("Chicago", [])], totalMiles: 2500),
]
let grouped = TripOptionsGrouper.groupByMileageRange(options, ascending: true)
#expect(grouped[0].header == "0-500 mi")
#expect(grouped[1].header == "1000-1500 mi")
#expect(grouped[2].header == "2000+ mi")
}
@Test func emptyOptionsReturnsEmptyGroups() {
let grouped = TripOptionsGrouper.groupByCityCount([], ascending: false)
#expect(grouped.isEmpty)
}
}