Files
MatchLiveTv/native/ios/MatchLiveTvTests/ApiInstantTests.swift
T
Emiliano FrascaroandCursor db0e842ca7 Prepara iOS 2.0.13 build 37 per lo Store con icona chiara e test del calendario.
Apple ha chiuso il treno 2.0.12: servono versione nuova, monogramma visibile sul nero e copertura dei casi dopo l'orario previsto.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 21:57:56 +02:00

126 lines
4.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
@testable import MatchLiveTv
/// Logica calendario/API reale + smoke parser (RTMP/overlay) senza dipendenze di rete.
final class ApiInstantTests: XCTestCase {
func testParseApiInstantRailsOffsetWithMillis() {
let now = iso("2026-06-06T16:00:00Z")
XCTAssertTrue(ApiInstant.isScheduledOnCalendar("2026-06-06T20:00:00.000+02:00", now: now))
}
func testIsScheduledOnCalendarKeepsThisMorningAfterKickoff() {
let kickoff = "2026-09-06T07:00:00Z"
let now = iso("2026-09-06T08:30:00Z")
XCTAssertTrue(ApiInstant.isScheduledOnCalendar(kickoff, now: now))
XCTAssertFalse(ApiInstant.isScheduledFuture(kickoff, now: now))
}
func testIsScheduledOnCalendarHidesYesterday() {
let yesterday = "2026-09-05T10:00:00Z"
let now = iso("2026-09-06T08:30:00Z")
XCTAssertFalse(ApiInstant.isScheduledOnCalendar(yesterday, now: now))
}
func testConcurrentStreamForbiddenShowsApiMessage() {
let body = """
{"error":"Hai già una diretta in corso con questo account. Chiudila prima di avviarne unaltra.","error_code":"user_concurrent_stream"}
"""
XCTAssertEqual(
APIError.http(403, body).errorDescription,
"Hai già una diretta in corso con questo account. Chiudila prima di avviarne unaltra."
)
}
private func iso(_ value: String) -> Date {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime]
guard let date = f.date(from: value) else {
XCTFail("Data ISO non valida: \(value)")
return Date()
}
return date
}
func testOverlayKindMapping() {
XCTAssertEqual(OverlayKindMapping.fromApi("basket"), "basket")
XCTAssertEqual(OverlayKindMapping.fromApi("none"), "none")
XCTAssertEqual(OverlayKindMapping.fromApi(nil), "volley")
}
func testRTMPUrlParser() {
let target = RTMPUrlParserLogic.parse("rtmp://stream.example.com/live/match_abc-123")
XCTAssertEqual(target?.connectURL, "rtmp://stream.example.com/live")
XCTAssertEqual(target?.streamName, "match_abc-123")
}
func testRtmpIngestUrlRewritesDockerHostForLocalApi() {
let resolved = RtmpIngestUrlLogic.resolve(
"rtmp://mediamtx:1935/live/match_abc",
apiBaseUrl: "http://localhost:3000"
)
XCTAssertEqual(resolved, "rtmp://127.0.0.1:1935/live/match_abc")
}
func testRtmpIngestUrlKeepsProductionHost() {
let url = "rtmp://www.matchlivetv.it:1935/live/match_abc"
let resolved = RtmpIngestUrlLogic.resolve(url, apiBaseUrl: "https://www.matchlivetv.it")
XCTAssertEqual(resolved, url)
}
}
enum OverlayKindMapping {
static func fromApi(_ value: String?) -> String {
switch value?.lowercased() {
case "none", "timer": return "none"
case "basket": return "basket"
case "timed": return "timed"
case "racket": return "racket"
default: return "volley"
}
}
}
enum RTMPUrlParserLogic {
struct Target {
let connectURL: String
let streamName: String
}
static func parse(_ urlString: String) -> Target? {
guard let url = URL(string: urlString), url.scheme?.hasPrefix("rtmp") == true else { return nil }
let pathParts = url.path.split(separator: "/").map(String.init)
guard pathParts.count >= 2 else { return nil }
let app = pathParts.dropLast().joined(separator: "/")
let streamName = pathParts.last ?? ""
var connect = "rtmp://\(url.host ?? "")"
if let port = url.port { connect += ":\(port)" }
connect += "/\(app)"
return Target(connectURL: connect, streamName: streamName)
}
}
/// Copia della logica `RtmpIngestUrl` per test senza bundle Info.plist.
enum RtmpIngestUrlLogic {
private static let dockerInternalHosts: Set<String> = ["mediamtx", "host.docker.internal"]
static func resolve(_ urlString: String, apiBaseUrl: String) -> String {
guard var components = URLComponents(string: urlString),
let host = components.host?.lowercased() else {
return urlString
}
let apiHost = URL(string: apiBaseUrl)?.host?.lowercased()
let isLocalApi = apiHost.map { $0 == "localhost" || $0 == "127.0.0.1" || $0.hasSuffix(".local") } ?? false
guard dockerInternalHosts.contains(host) || isLocalApi else {
return urlString
}
let ingestHost: String
if let apiHost, apiHost != "localhost", apiHost != "127.0.0.1" {
ingestHost = apiHost
} else {
ingestHost = "127.0.0.1"
}
components.host = ingestHost
if components.port == nil { components.port = 1935 }
return components.url?.absoluteString ?? urlString
}
}