Files
Sportstime/SportsTimeTests/Services/DeepLinkHandlerTests.swift
Trey t 8162b4a029 refactor(tests): TDD rewrite of all unit tests with spec documentation
Complete rewrite of unit test suite using TDD methodology:

Planning Engine Tests:
- GameDAGRouterTests: Beam search, anchor games, transitions
- ItineraryBuilderTests: Stop connection, validators, EV enrichment
- RouteFiltersTests: Region, time window, scoring filters
- ScenarioA/B/C/D PlannerTests: All planning scenarios
- TravelEstimatorTests: Distance, duration, travel days
- TripPlanningEngineTests: Orchestration, caching, preferences

Domain Model Tests:
- AchievementDefinitionsTests, AnySportTests, DivisionTests
- GameTests, ProgressTests, RegionTests, StadiumTests
- TeamTests, TravelSegmentTests, TripTests, TripPollTests
- TripPreferencesTests, TripStopTests, SportTests

Service Tests:
- FreeScoreAPITests, RouteDescriptionGeneratorTests
- SuggestedTripsGeneratorTests

Export Tests:
- ShareableContentTests (card types, themes, dimensions)

Bug fixes discovered through TDD:
- ShareCardDimensions: mapSnapshotSize exceeded available width (960x480)
- ScenarioBPlanner: Added anchor game validation filter

All tests include:
- Specification tests (expected behavior)
- Invariant tests (properties that must always hold)
- Edge case tests (boundary conditions)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:07:41 -06:00

167 lines
5.3 KiB
Swift

//
// DeepLinkHandlerTests.swift
// SportsTimeTests
//
// TDD specification tests for DeepLinkHandler URL parsing.
//
import Testing
import Foundation
@testable import SportsTime
@Suite("DeepLinkHandler")
@MainActor
struct DeepLinkHandlerTests {
private let handler = DeepLinkHandler.shared
// MARK: - Specification Tests: parseURL
/// - Expected Behavior: Valid poll URL returns .poll with share code
@Test("parseURL: sportstime://poll/{code} returns poll destination")
func parseURL_validPollURL() {
let url = URL(string: "sportstime://poll/ABC123")!
let result = handler.parseURL(url)
if case .poll(let shareCode) = result {
#expect(shareCode == "ABC123")
} else {
Issue.record("Expected .poll destination")
}
}
/// - Expected Behavior: Poll URL normalizes code to uppercase
@Test("parseURL: normalizes share code to uppercase")
func parseURL_normalizesToUppercase() {
let url = URL(string: "sportstime://poll/abc123")!
let result = handler.parseURL(url)
if case .poll(let shareCode) = result {
#expect(shareCode == "ABC123")
} else {
Issue.record("Expected .poll destination")
}
}
/// - Expected Behavior: Invalid scheme returns nil
@Test("parseURL: wrong scheme returns nil")
func parseURL_wrongScheme() {
let httpURL = URL(string: "http://poll/ABC123")!
#expect(handler.parseURL(httpURL) == nil)
let httpsURL = URL(string: "https://poll/ABC123")!
#expect(handler.parseURL(httpsURL) == nil)
}
/// - Expected Behavior: Empty path returns nil
@Test("parseURL: empty path returns nil")
func parseURL_emptyPath() {
let url = URL(string: "sportstime://")!
#expect(handler.parseURL(url) == nil)
}
/// - Expected Behavior: Unknown path returns nil
@Test("parseURL: unknown path returns nil")
func parseURL_unknownPath() {
let url = URL(string: "sportstime://unknown/ABC123")!
#expect(handler.parseURL(url) == nil)
}
/// - Expected Behavior: Poll path without code returns nil
@Test("parseURL: poll path without code returns nil")
func parseURL_pollWithoutCode() {
let url = URL(string: "sportstime://poll")!
#expect(handler.parseURL(url) == nil)
let urlWithSlash = URL(string: "sportstime://poll/")!
#expect(handler.parseURL(urlWithSlash) == nil)
}
// MARK: - Specification Tests: Share Code Validation
/// - Expected Behavior: Share code must be exactly 6 characters
@Test("parseURL: validates share code length")
func parseURL_validateCodeLength() {
// Too short
let shortURL = URL(string: "sportstime://poll/ABC12")!
#expect(handler.parseURL(shortURL) == nil)
// Too long
let longURL = URL(string: "sportstime://poll/ABC1234")!
#expect(handler.parseURL(longURL) == nil)
// Exactly 6 - valid
let validURL = URL(string: "sportstime://poll/ABC123")!
#expect(handler.parseURL(validURL) != nil)
}
// MARK: - Invariant Tests
/// - Invariant: Only sportstime:// scheme is valid
@Test("Invariant: scheme must be sportstime")
func invariant_schemeMustBeSportstime() {
let validURL = URL(string: "sportstime://poll/ABC123")!
#expect(handler.parseURL(validURL) != nil)
// Various invalid schemes
let schemes = ["http", "https", "file", "ftp", "app"]
for scheme in schemes {
let url = URL(string: "\(scheme)://poll/ABC123")!
#expect(handler.parseURL(url) == nil, "Scheme '\(scheme)' should be rejected")
}
}
/// - Invariant: Path must start with recognized destination type
@Test("Invariant: path must be recognized destination")
func invariant_pathMustBeRecognized() {
// Only 'poll' is recognized
let pollURL = URL(string: "sportstime://poll/ABC123")!
#expect(handler.parseURL(pollURL) != nil)
// Other paths should fail
let otherPaths = ["trip", "game", "stadium", "user", "settings"]
for path in otherPaths {
let url = URL(string: "sportstime://\(path)/ABC123")!
#expect(handler.parseURL(url) == nil, "Path '\(path)' should not be recognized")
}
}
}
// MARK: - DeepLinkDestination Tests
@Suite("DeepLinkDestination")
@MainActor
struct DeepLinkDestinationTests {
// MARK: - Specification Tests: Equatable
@Test("Equatable: poll destinations with same code are equal")
func equatable_sameCode() {
let dest1 = DeepLinkDestination.poll(shareCode: "ABC123")
let dest2 = DeepLinkDestination.poll(shareCode: "ABC123")
#expect(dest1 == dest2)
}
@Test("Equatable: poll destinations with different codes are not equal")
func equatable_differentCodes() {
let dest1 = DeepLinkDestination.poll(shareCode: "ABC123")
let dest2 = DeepLinkDestination.poll(shareCode: "XYZ789")
#expect(dest1 != dest2)
}
// MARK: - Specification Tests: Properties
@Test("poll: shareCode returns associated value")
func poll_shareCode() {
let dest = DeepLinkDestination.poll(shareCode: "TEST99")
if case .poll(let code) = dest {
#expect(code == "TEST99")
} else {
Issue.record("Expected poll case")
}
}
}