Il simulatore non risolve «mediamtx»; l'app riscrive l'ingest verso 127.0.0.1 e docker-compose espone MEDIAMTX_RTMP_URL client-safe con messaggi d'errore RTMP più chiari. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
3.3 KiB
Swift
88 lines
3.3 KiB
Swift
import XCTest
|
|
|
|
/// Smoke test eseguibile senza @testable import (validazione logica pura).
|
|
final class ApiInstantTests: XCTestCase {
|
|
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
|
|
}
|
|
}
|