using Jellyfin.Plugin.Multilang.Services; using Microsoft.AspNetCore.Http; namespace Jellyfin.Plugin.Multilang.Tests; public sealed class ItemsProxyRequestBuilderTests { private const string UserId = "11111111111111111111111111111111"; private const string OtherUserId = "22222222222222222222222222222222"; [Fact] public void BuildNormalizesRootItemsPathToCurrentUser() { var request = HttpRequest(); var result = ItemsProxyRequestBuilder.Build( request, "/Items?Recursive=true&api_key=old", "token", UserId, multilangEnabled: true); Assert.NotNull(result); Assert.Equal($"/Users/{UserId}/Items", result.Upstream.AbsolutePath); Assert.Contains("api_key=token", result.Upstream.Query); Assert.DoesNotContain("api_key=old", result.Upstream.Query); Assert.DoesNotContain("api_key", result.NormalizedUrlForCache, StringComparison.OrdinalIgnoreCase); Assert.Contains("userId=" + UserId, result.NormalizedUrlForCache); } [Fact] public void BuildReplacesForeignUserIdInUpstreamPath() { var request = HttpRequest(); var result = ItemsProxyRequestBuilder.Build( request, $"/Users/{OtherUserId}/Items?Limit=5", "token", UserId, multilangEnabled: true); Assert.NotNull(result); Assert.Equal($"/Users/{UserId}/Items", result.Upstream.AbsolutePath); } [Fact] public void BuildKeepsLocalListControlsOutOfUpstreamWhenEnabled() { var request = HttpRequest("?mlLocale=fi-FI"); var result = ItemsProxyRequestBuilder.Build( request, "/Items?SortBy=SortName&SortOrder=Descending&StartIndex=20&Limit=10&NameStartsWith=L", "token", UserId, multilangEnabled: true); Assert.NotNull(result); Assert.Equal("SortName", result.Controls.SortBy); Assert.Equal("Descending", result.Controls.SortOrder); Assert.Equal(20, result.Controls.StartIndex); Assert.Equal(10, result.Controls.Limit); Assert.Equal("L", result.Controls.NameStartsWith); Assert.Equal("fi-FI", result.Controls.ClientLocale); Assert.DoesNotContain("SortBy=", result.Upstream.Query); Assert.DoesNotContain("Limit=", result.Upstream.Query); Assert.Contains("|SortName|Descending|L|20|10|fi-FI|", result.CacheKey); } [Fact] public void BuildLeavesSortAndPagingUpstreamWhenMultilangDisabled() { var request = HttpRequest(); var result = ItemsProxyRequestBuilder.Build( request, "/Items?SortBy=SortName&Limit=10", "token", UserId, multilangEnabled: false); Assert.NotNull(result); Assert.Equal(string.Empty, result.Controls.SortBy); Assert.Equal(0, result.Controls.Limit); Assert.Contains("SortBy=SortName", result.Upstream.Query); Assert.Contains("Limit=10", result.Upstream.Query); } [Fact] public void BuildRejectsAbsoluteUrlForAnotherHost() { var request = HttpRequest(); var result = ItemsProxyRequestBuilder.Build( request, "http://other-host:8096/Items", "token", UserId, multilangEnabled: true); Assert.Null(result); } [Fact] public void ParseLocalGenreIdsOnlyReturnsTmdbIds() { Assert.Equal([1, 2], ItemsProxyRequestBuilder.ParseLocalGenreIds("tmdb-1,abc,2,tmdb-2,tmdb-x,1")); } private static HttpRequest HttpRequest(string query = "") { var context = new DefaultHttpContext(); context.Request.Scheme = "http"; context.Request.Host = new HostString("server.local", 8096); context.Request.QueryString = new QueryString(query); return context.Request; } }