import Testing import Foundation import MediaKit @testable import JellyfinKit @Suite("Leiter bestellen") struct AdaptiveURLTests { private func url(_ text: String) -> URL { URL(string: text)! } private func werte(_ url: URL, _ name: String) -> [String] { (URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? []) .filter { $0.name.lowercased() == name.lowercased() } .compactMap(\.value) } /// `main.m3u8` ist die Liste der Schnipsel **einer** Stufe. Auf ihr kann /// der Parameter nichts bewirken — es gibt dort keine Varianten. @Test("Aus der Schnipselliste wird die Stufenliste") func mainBecomesMaster() { let ergebnis = JellyfinBackend.adaptive(url("http://h/videos/abc/main.m3u8?Tag=1")) #expect(ergebnis.lastPathComponent == "master.m3u8") // Die übrige Abfrage bleibt unangetastet. #expect(werte(ergebnis, "Tag") == ["1"]) } @Test("Eine Stufenliste bleibt, wie sie ist") func masterStays() { let ergebnis = JellyfinBackend.adaptive(url("http://h/videos/abc/master.m3u8")) #expect(ergebnis.lastPathComponent == "master.m3u8") } /// Die Falle, an der `maxHeight` schon einmal hängen geblieben ist: bei /// zwei gleichnamigen Werten gilt beim Server der erste. Ein /// danebengestellter zweiter bewirkt nichts. @Test("Der Parameter steht genau einmal da") func parameterAppearsOnce() { let ergebnis = JellyfinBackend.adaptive( url("http://h/videos/abc/master.m3u8?enableAdaptiveBitrateStreaming=false&Tag=1") ) #expect(werte(ergebnis, "enableAdaptiveBitrateStreaming") == ["true"]) #expect(werte(ergebnis, "Tag") == ["1"]) } @Test("Ohne vorhandenen Wert kommt er dazu") func parameterIsAdded() { let ergebnis = JellyfinBackend.adaptive(url("http://h/videos/abc/master.m3u8?Tag=1")) #expect(werte(ergebnis, "enableAdaptiveBitrateStreaming") == ["true"]) } /// Groß- und Kleinschreibung ist beim Server gleichgültig — beim /// Herausfiltern darf sie es nicht sein, sonst steht er doch zweimal da. @Test("Auch anders geschrieben wird er ersetzt") func parameterIsReplacedRegardlessOfCase() { let ergebnis = JellyfinBackend.adaptive( url("http://h/videos/abc/master.m3u8?EnableAdaptiveBitrateStreaming=false") ) let alle = (URLComponents(url: ergebnis, resolvingAgainstBaseURL: false)?.queryItems ?? []) .filter { $0.name.lowercased() == "enableadaptivebitratestreaming" } #expect(alle.count == 1) #expect(alle.first?.value == "true") } /// Die Höhe gehört weiterhin dran: die Leiter ist die Abstufung, die /// Obergrenze bleibt unsere. @Test("Höhenbegrenzung und Leiter vertragen sich") func heightAndLadderCoexist() { let begrenzt = JellyfinBackend.limiting( height: "1280x720", in: url("http://h/videos/abc/main.m3u8") ) let ergebnis = JellyfinBackend.adaptive(begrenzt) #expect(werte(ergebnis, "maxHeight") == ["720"]) #expect(werte(ergebnis, "enableAdaptiveBitrateStreaming") == ["true"]) #expect(ergebnis.lastPathComponent == "master.m3u8") } }