// // 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") } } }