80 lines
3.1 KiB
C#
80 lines
3.1 KiB
C#
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Interfaces.Images;
|
|
using ErsatzTV.Core.Interfaces.Streaming;
|
|
using ErsatzTV.Infrastructure.Images;
|
|
using LanguageExt;
|
|
using NSubstitute;
|
|
using NUnit.Framework;
|
|
using Shouldly;
|
|
using SixLabors.ImageSharp;
|
|
using SixLabors.ImageSharp.Formats.Png;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
using static LanguageExt.Prelude;
|
|
using Image = SixLabors.ImageSharp.Image;
|
|
|
|
namespace ErsatzTV.Infrastructure.Tests.Images;
|
|
|
|
[TestFixture]
|
|
public class RemoteLogoCacherTests
|
|
{
|
|
private static readonly Uri Uri = new("https://example.com/logo.png");
|
|
|
|
[Test]
|
|
public async Task Should_Fetch_Validate_And_Cache_Returning_The_Name()
|
|
{
|
|
MemoryStream png = await RealPng(64, 64);
|
|
var fetcher = Substitute.For<IRemoteImageFetcher>();
|
|
fetcher.Fetch(Uri, Arg.Any<CancellationToken>()).Returns(png);
|
|
var validator = Substitute.For<IRemoteImageValidator>();
|
|
validator.Validate(png, Uri, Arg.Any<CancellationToken>()).Returns(Task.CompletedTask);
|
|
var cache = Substitute.For<IImageCache>();
|
|
cache.SaveArtworkToCache(Arg.Any<Stream>(), ArtworkKind.Logo).Returns(Right<BaseError, string>("abc123"));
|
|
|
|
var cacher = new RemoteLogoCacher(fetcher, validator, cache);
|
|
Either<BaseError, string> result = await cacher.CacheFromUrl(Uri, CancellationToken.None);
|
|
|
|
result.IsRight.ShouldBeTrue();
|
|
result.IfRight(name => name.ShouldBe("abc123"));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Should_Return_Left_When_The_Fetch_Throws()
|
|
{
|
|
var fetcher = Substitute.For<IRemoteImageFetcher>();
|
|
fetcher.Fetch(Uri, Arg.Any<CancellationToken>()).Returns<Stream>(_ => throw new TimeoutException("timed out"));
|
|
var cacher = new RemoteLogoCacher(fetcher, Substitute.For<IRemoteImageValidator>(), Substitute.For<IImageCache>());
|
|
|
|
Either<BaseError, string> result = await cacher.CacheFromUrl(Uri, CancellationToken.None);
|
|
|
|
result.IsLeft.ShouldBeTrue();
|
|
result.IfLeft(e => e.Value.ShouldContain("timed out"));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Should_Return_Left_When_Validation_Rejects_A_Bomb()
|
|
{
|
|
MemoryStream png = await RealPng(64, 64);
|
|
var fetcher = Substitute.For<IRemoteImageFetcher>();
|
|
fetcher.Fetch(Uri, Arg.Any<CancellationToken>()).Returns(png);
|
|
var validator = Substitute.For<IRemoteImageValidator>();
|
|
validator.Validate(png, Uri, Arg.Any<CancellationToken>())
|
|
.Returns<Task>(_ => throw new InvalidOperationException("over the 50000000 pixel limit"));
|
|
var cacher = new RemoteLogoCacher(fetcher, validator, Substitute.For<IImageCache>());
|
|
|
|
Either<BaseError, string> result = await cacher.CacheFromUrl(Uri, CancellationToken.None);
|
|
|
|
result.IsLeft.ShouldBeTrue();
|
|
result.IfLeft(e => e.Value.ShouldContain("pixel limit"));
|
|
}
|
|
|
|
private static async Task<MemoryStream> RealPng(int w, int h)
|
|
{
|
|
using var img = new Image<Rgba32>(w, h);
|
|
var ms = new MemoryStream();
|
|
await img.SaveAsync(ms, new PngEncoder());
|
|
ms.Position = 0;
|
|
return ms;
|
|
}
|
|
}
|