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>
167 lines
5.3 KiB
Swift
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")
|
|
}
|
|
}
|
|
}
|