Release 0.2.5: harden proxy and streamline metadata fetching
Remove obsolete rule formats, batch SQLite work, preserve artwork source URLs, and coordinate refresh workers with maintenance. Add regression coverage and document backup format changes.
This commit is contained in:
@@ -16,14 +16,13 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
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.Upstream.Query, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.DoesNotContain("api_key", result.NormalizedUrlForCache, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("userId=" + UserId, result.NormalizedUrlForCache);
|
||||
}
|
||||
@@ -36,7 +35,6 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
var result = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
$"/Users/{OtherUserId}/Items?Limit=5",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: true);
|
||||
|
||||
@@ -52,7 +50,6 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
var result = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
"/Items?SortBy=SortName&SortOrder=Descending&StartIndex=20&Limit=10&NameStartsWith=L",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: true);
|
||||
|
||||
@@ -65,7 +62,7 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
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);
|
||||
Assert.DoesNotContain("SortBy=", result.CacheKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -76,20 +73,18 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
var first = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
"/Items?SortBy=SortName&StartIndex=0&Limit=100",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: true);
|
||||
var second = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
"/Items?SortBy=SortName&StartIndex=100&Limit=100",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: true);
|
||||
|
||||
Assert.NotNull(first);
|
||||
Assert.NotNull(second);
|
||||
Assert.Equal(first.NormalizedUrlForCache, second.NormalizedUrlForCache);
|
||||
Assert.NotEqual(first.CacheKey, second.CacheKey);
|
||||
Assert.Equal(first.CacheKey, second.CacheKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -100,7 +95,6 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
var result = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
"/Items?SortBy=SortName&Limit=10",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: false);
|
||||
|
||||
@@ -119,7 +113,6 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
var result = ItemsProxyRequestBuilder.Build(
|
||||
request,
|
||||
"http://other-host:8096/Items",
|
||||
"token",
|
||||
UserId,
|
||||
multilangEnabled: true);
|
||||
|
||||
@@ -132,6 +125,18 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
Assert.Equal([1, 2], ItemsProxyRequestBuilder.ParseLocalGenreIds("tmdb-1,abc,2,tmdb-2,tmdb-x,1"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenreFilteringRetainsTheFieldsNeededForLocalFiltering()
|
||||
{
|
||||
var result = ItemsProxyRequestBuilder.Build(HttpRequest(), "/Items?GenreIds=tmdb-18&Fields=Overview", UserId, true);
|
||||
Assert.NotNull(result);
|
||||
var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(result.Upstream.Query);
|
||||
Assert.False(query.ContainsKey("GenreIds"));
|
||||
Assert.Equal(1, query["Fields"].Count);
|
||||
Assert.Contains("GenreItems", query["Fields"].ToString());
|
||||
Assert.Contains("Overview", query["Fields"].ToString());
|
||||
}
|
||||
|
||||
private static HttpRequest HttpRequest(string query = "")
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
@@ -140,4 +145,40 @@ public sealed class ItemsProxyRequestBuilderTests
|
||||
context.Request.QueryString = new QueryString(query);
|
||||
return context.Request;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("//other-host:8096/Items")]
|
||||
[InlineData("/\\other-host/Items")]
|
||||
[InlineData("https://server.local:8096/Items")]
|
||||
[InlineData("/Multilang/ItemsProxy")]
|
||||
[InlineData("/System/Configuration")]
|
||||
[InlineData("/Items/Filters")]
|
||||
[InlineData("/Items/../System/Info")]
|
||||
public void RejectsUnsupportedDestinations(string url)
|
||||
=> Assert.Null(ItemsProxyRequestBuilder.Build(HttpRequest(), url, UserId, true));
|
||||
|
||||
[Fact]
|
||||
public void EquivalentQueryOrderSharesCacheAndInFlightKeys()
|
||||
{
|
||||
var first = ItemsProxyRequestBuilder.Build(HttpRequest(), "/Items?Fields=Overview&Recursive=true", UserId, true)!;
|
||||
var second = ItemsProxyRequestBuilder.Build(HttpRequest(), "/Items?Recursive=true&Fields=Overview", UserId, true)!;
|
||||
Assert.Equal(first.CacheKey, second.CacheKey);
|
||||
Assert.Equal(first.NormalizedUrlForCache, second.NormalizedUrlForCache);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BasePathAndDashedUserIdsAreNormalized()
|
||||
{
|
||||
var request = HttpRequest();
|
||||
request.PathBase = "/jellyfin";
|
||||
var result = ItemsProxyRequestBuilder.Build(request, "/jellyfin/Users/22222222-2222-2222-2222-222222222222/Items", UserId, true)!;
|
||||
Assert.Equal($"/jellyfin/Users/{UserId}/Items", result.Upstream.AbsolutePath);
|
||||
Assert.Null(ItemsProxyRequestBuilder.Build(request, "/Items", UserId, true));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/Items/Resume?StartIndex=100&Limit=20")]
|
||||
[InlineData("/Shows/NextUp?StartIndex=100&Limit=20")]
|
||||
public void BoundedListsKeepUpstreamPaging(string url)
|
||||
=> Assert.False(ItemsProxyRequestBuilder.Build(HttpRequest(), url, UserId, true)!.Controls.ApplyLocalPaging);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user