Files
jellyfin-multilang/tests/Jellyfin.Plugin.Multilang.Tests/AssetStorageServiceTests.cs
T
ajp_anton e36775f1a3 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.
2026-09-09 01:18:43 +00:00

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);
}
}
}