using System; using System.Linq; using Jellyfin.Plugin.Floatplane.Api.Models; using Jellyfin.Plugin.Floatplane.Playback; using Xunit; namespace Jellyfin.Plugin.Floatplane.Tests; public class DeliveryResolverTests { private static ResolvedVariant[] Variants() => Fixture.Json("delivery-info.json").Resolve().ToArray(); [Fact] public void SelectVariant_exact_level() { var v = DeliveryResolver.SelectVariant(Variants(), "720"); Assert.NotNull(v); Assert.Equal("720-avc1", v!.Name); } [Fact] public void SelectVariant_falls_back_to_not_taller() { // 2160 is denied → best usable ≤ 2160 is 1080 var v = DeliveryResolver.SelectVariant(Variants(), "2160"); Assert.Equal("1080-avc1", v!.Name); var v2 = DeliveryResolver.SelectVariant(Variants(), "600"); Assert.Equal("480-avc1", v2!.Name); } [Fact] public void SelectVariant_falls_back_to_best_when_nothing_fits() { var v = DeliveryResolver.SelectVariant(Variants(), "240"); Assert.Equal("1080-avc1", v!.Name); } [Fact] public void SelectVariant_returns_null_without_usable_variants() { var disabled = Variants().Select(v => new ResolvedVariant(new DeliveryVariant { Name = v.Name, Url = string.Empty, Enabled = false }, v.Url)).ToList(); Assert.Null(DeliveryResolver.SelectVariant(disabled, "1080")); Assert.Null(DeliveryResolver.SelectVariant(Array.Empty(), "1080")); } [Fact] public void TokenExpiry_reads_exp_from_jwt() { var url = Variants().First(v => v.Height == 1080).Url; var exp = DeliveryResolver.TokenExpiry(url); Assert.Equal(DateTimeOffset.FromUnixTimeSeconds(1792935296), exp); } [Theory] [InlineData("https://cdn.example/x.m3u8")] [InlineData("https://cdn.example/x.m3u8?token=not-a-jwt")] [InlineData("not a url")] public void TokenExpiry_returns_null_without_jwt(string url) { Assert.Null(DeliveryResolver.TokenExpiry(url)); } }