Harden proxy permissions and cache behavior, streamline metadata fetching, remove obsolete rules, and validate the stable SDK with expanded regression coverage. Document backup format changes.
99 lines
4.0 KiB
C#
99 lines
4.0 KiB
C#
using System.Net;
|
|
using Jellyfin.Plugin.Multilang.Data;
|
|
using Jellyfin.Plugin.Multilang.Services.Assets;
|
|
|
|
namespace Jellyfin.Plugin.Multilang.Tests;
|
|
|
|
public sealed class AssetStorageServiceTests
|
|
{
|
|
[Fact]
|
|
public async Task LocalStorageDownloadsAStableAssetPathAndServesIt()
|
|
{
|
|
var root = TestPaths.CreateRoot();
|
|
try
|
|
{
|
|
var store = new TranslationStore(new TestApplicationPaths(root));
|
|
var factory = new TestHttpClientFactory(_ => new HttpResponseMessage(HttpStatusCode.OK)
|
|
{
|
|
Content = new ByteArrayContent([1, 2, 3])
|
|
});
|
|
var service = new AssetStorageService(factory, store);
|
|
|
|
var path = await service.StoreAsync("item", "https://images.example/poster.jpg", true, CancellationToken.None);
|
|
|
|
Assert.NotNull(path);
|
|
Assert.StartsWith(TranslationStore.LocalAssetUrlPrefix, path, StringComparison.Ordinal);
|
|
Assert.True(store.TryNormalizeLocalAssetPath(path, out var fullPath));
|
|
Assert.Equal([1, 2, 3], await File.ReadAllBytesAsync(fullPath));
|
|
Assert.True(service.TryResolveLocalAsset(path[TranslationStore.LocalAssetUrlPrefix.Length..], out var resolved, out var contentType));
|
|
Assert.Equal(fullPath, resolved);
|
|
Assert.Equal("image/jpeg", contentType);
|
|
|
|
store.UpsertAsset("item", "fi", "poster", path, 1);
|
|
var original = await service.StoreAsync("item", "https://images.example/poster.jpg", true, CancellationToken.None);
|
|
store.UpsertAsset("item", "Original", "poster", original!, 1);
|
|
Assert.Equal(path, original);
|
|
Assert.Single(factory.Requests);
|
|
Assert.True(store.IsAssetReferenced("item", path));
|
|
Assert.True(File.Exists(fullPath));
|
|
File.Delete(fullPath);
|
|
Assert.False(File.Exists(fullPath));
|
|
await service.StoreAsync("item", "https://images.example/poster.jpg", true, CancellationToken.None);
|
|
Assert.True(File.Exists(fullPath));
|
|
Assert.Equal(2, factory.Requests.Count);
|
|
}
|
|
finally
|
|
{
|
|
TestPaths.DeleteRoot(root);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UrlStorageDoesNotFetchOrWriteAnAsset()
|
|
{
|
|
var root = TestPaths.CreateRoot();
|
|
try
|
|
{
|
|
var store = new TranslationStore(new TestApplicationPaths(root));
|
|
var factory = new TestHttpClientFactory(_ => throw new InvalidOperationException("URL storage must not fetch."));
|
|
var service = new AssetStorageService(factory, store);
|
|
const string url = "https://images.example/poster.webp";
|
|
|
|
var result = await service.StoreAsync("item", url, false, CancellationToken.None);
|
|
|
|
Assert.Equal(url, result);
|
|
Assert.Empty(factory.Requests);
|
|
Assert.Empty(Directory.EnumerateFiles(store.AssetsDirectory, "*", SearchOption.AllDirectories));
|
|
}
|
|
finally
|
|
{
|
|
TestPaths.DeleteRoot(root);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LocalStorageRejectsAnOversizedResponseBeforeWritingIt()
|
|
{
|
|
var root = TestPaths.CreateRoot();
|
|
try
|
|
{
|
|
var store = new TranslationStore(new TestApplicationPaths(root));
|
|
var factory = new TestHttpClientFactory(_ =>
|
|
{
|
|
var content = new ByteArrayContent([1]);
|
|
content.Headers.ContentLength = 51L * 1024L * 1024L;
|
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = content };
|
|
});
|
|
var service = new AssetStorageService(factory, store);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() => service.StoreAsync("item", "https://images.example/poster.jpg", true, CancellationToken.None));
|
|
|
|
Assert.Empty(Directory.EnumerateFiles(store.AssetsDirectory, "*", SearchOption.AllDirectories));
|
|
}
|
|
finally
|
|
{
|
|
TestPaths.DeleteRoot(root);
|
|
}
|
|
}
|
|
}
|