Files
Sportstime/SportsTimeTests/Features/Progress/VisitListTests.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

117 lines
3.8 KiB
Swift

import XCTest
import SwiftData
@testable import SportsTime
@MainActor
final class VisitListTests: XCTestCase {
var modelContainer: ModelContainer!
var modelContext: ModelContext!
override func setUp() async throws {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
modelContainer = try ModelContainer(
for: StadiumVisit.self, Achievement.self, UserPreferences.self,
configurations: config
)
modelContext = modelContainer.mainContext
}
override func tearDown() async throws {
modelContainer = nil
modelContext = nil
}
func test_VisitsForStadium_ReturnsAllVisitsSortedByDate() async throws {
// Given: Multiple visits to the same stadium
let stadiumId = "yankee-stadium"
let visit1 = StadiumVisit(
stadiumId: stadiumId,
stadiumNameAtVisit: "Yankee Stadium",
visitDate: Date().addingTimeInterval(-86400 * 30), // 30 days ago
sport: .mlb,
visitType: .game,
dataSource: .fullyManual
)
let visit2 = StadiumVisit(
stadiumId: stadiumId,
stadiumNameAtVisit: "Yankee Stadium",
visitDate: Date().addingTimeInterval(-86400 * 7), // 7 days ago
sport: .mlb,
visitType: .game,
dataSource: .fullyManual
)
let visit3 = StadiumVisit(
stadiumId: stadiumId,
stadiumNameAtVisit: "Yankee Stadium",
visitDate: Date(), // today
sport: .mlb,
visitType: .tour,
dataSource: .fullyManual
)
modelContext.insert(visit1)
modelContext.insert(visit2)
modelContext.insert(visit3)
try modelContext.save()
// When: Fetching visits for that stadium
let descriptor = FetchDescriptor<StadiumVisit>(
predicate: #Predicate { $0.stadiumId == stadiumId },
sortBy: [SortDescriptor(\.visitDate, order: .reverse)]
)
let visits = try modelContext.fetch(descriptor)
// Then: All visits returned, most recent first
XCTAssertEqual(visits.count, 3, "Should return all 3 visits")
XCTAssertEqual(visits[0].visitType, .tour, "Most recent visit should be first")
XCTAssertEqual(visits[2].visitType, .game, "Oldest visit should be last")
}
func test_VisitCountForStadium_ReturnsCorrectCount() async throws {
// Given: 3 visits to one stadium, 1 to another
let stadium1 = "yankee-stadium"
let stadium2 = "fenway-park"
for i in 0..<3 {
let visit = StadiumVisit(
stadiumId: stadium1,
stadiumNameAtVisit: "Yankee Stadium",
visitDate: Date().addingTimeInterval(Double(-i * 86400)),
sport: .mlb,
visitType: .game,
dataSource: .fullyManual
)
modelContext.insert(visit)
}
let fenwayVisit = StadiumVisit(
stadiumId: stadium2,
stadiumNameAtVisit: "Fenway Park",
visitDate: Date(),
sport: .mlb,
visitType: .game,
dataSource: .fullyManual
)
modelContext.insert(fenwayVisit)
try modelContext.save()
// When: Counting visits per stadium
let yankeeDescriptor = FetchDescriptor<StadiumVisit>(
predicate: #Predicate { $0.stadiumId == stadium1 }
)
let fenwayDescriptor = FetchDescriptor<StadiumVisit>(
predicate: #Predicate { $0.stadiumId == stadium2 }
)
let yankeeCount = try modelContext.fetchCount(yankeeDescriptor)
let fenwayCount = try modelContext.fetchCount(fenwayDescriptor)
// Then: Correct counts
XCTAssertEqual(yankeeCount, 3)
XCTAssertEqual(fenwayCount, 1)
}
}