using System.Net; using YMhut.Box.Core.Media; namespace YMhut.Box.Tests; [TestClass] public sealed class RemoteMediaResolverTests { [TestMethod] public async Task ResolvesRedirectToDirectMedia() { var resolver = CreateResolver(request => { if (request.RequestUri?.AbsolutePath == "/api") { return Redirect("/media/demo.mp4"); } return Text(HttpStatusCode.OK, string.Empty, "video/mp4"); }); var result = await resolver.ResolveMediaAsync("https://example.test/api", RemoteMediaKind.Video, cacheBust: false); Assert.AreEqual("https://example.test/media/demo.mp4", result.Uri.AbsoluteUri); Assert.IsTrue(result.IsDirectMedia); Assert.AreEqual("video/mp4", result.ContentType); Assert.AreEqual(".mp4", result.SuggestedExtension); } [TestMethod] public async Task ExtractsPlainTextUrl() { var resolver = CreateResolver(request => { if (request.RequestUri?.Host == "cdn.test") { return Text(HttpStatusCode.OK, string.Empty, "video/mp4"); } return Text(HttpStatusCode.OK, "https://cdn.test/random/clip.mp4", "text/plain"); }); var result = await resolver.ResolveMediaAsync("https://example.test/api", RemoteMediaKind.Video, cacheBust: false); Assert.AreEqual("https://cdn.test/random/clip.mp4", result.Uri.AbsoluteUri); Assert.IsTrue(result.IsDirectMedia); Assert.AreEqual(".mp4", result.SuggestedExtension); } [TestMethod] public async Task ExtractsNestedJsonUrl() { var resolver = CreateResolver(request => { if (request.RequestUri?.Host == "cdn.test") { return Text(HttpStatusCode.OK, string.Empty, "image/webp"); } return Text(HttpStatusCode.OK, """ { "data": { "url": "https://cdn.test/images/pic.webp" } } """, "application/json"); }); var result = await resolver.ResolveMediaAsync("https://example.test/api", RemoteMediaKind.Image, cacheBust: false); Assert.AreEqual("https://cdn.test/images/pic.webp", result.Uri.AbsoluteUri); Assert.IsTrue(result.IsDirectMedia); Assert.AreEqual(".webp", result.SuggestedExtension); } [TestMethod] public async Task KeepsHtmlWithoutMediaUrlAsNonDirect() { var resolver = CreateResolver(_ => Text(HttpStatusCode.OK, "No media here", "text/html")); var result = await resolver.ResolveMediaAsync("https://example.test/page", RemoteMediaKind.Video, cacheBust: false); Assert.AreEqual("https://example.test/page", result.Uri.AbsoluteUri); Assert.IsFalse(result.IsDirectMedia); Assert.AreEqual("text/html", result.ContentType); } [TestMethod] public async Task TreatsDirectMediaContentTypeAsPlayable() { var resolver = CreateResolver(_ => Text(HttpStatusCode.OK, string.Empty, "audio/mpeg")); var result = await resolver.ResolveMediaAsync("https://example.test/random", RemoteMediaKind.Audio, cacheBust: false); Assert.AreEqual("https://example.test/random", result.Uri.AbsoluteUri); Assert.IsTrue(result.IsDirectMedia); Assert.AreEqual(".mp3", result.SuggestedExtension); } private static RemoteMediaResolver CreateResolver(Func responseFactory) { return new RemoteMediaResolver(() => new StubHttpHandler(responseFactory)); } private static HttpResponseMessage Redirect(string location) { var response = new HttpResponseMessage(HttpStatusCode.Redirect); response.Headers.Location = new Uri(location, UriKind.RelativeOrAbsolute); return response; } private static HttpResponseMessage Text(HttpStatusCode statusCode, string content, string contentType) { return new HttpResponseMessage(statusCode) { Content = new StringContent(content, System.Text.Encoding.UTF8, contentType) }; } private sealed class StubHttpHandler(Func responseFactory) : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = responseFactory(request); response.RequestMessage = request; return Task.FromResult(response); } } }