using ErsatzTV.Core.Images; using NUnit.Framework; using Shouldly; namespace ErsatzTV.Core.Tests.Images; [TestFixture] public class RemoteImageDecodeBudgetTests { private static readonly Uri Uri = new("https://example.com/logo.png"); // the product is the real bound: 2500x2500 x600 is affordable on each axis alone but not together [Test] public void Should_Reject_Dimensions_And_Frames_Affordable_Alone_But_Not_Together() { ((long)2500 * 2500).ShouldBeLessThanOrEqualTo(RemoteImageDecodeBudget.MaxRemoteDecodedPixels); 600.ShouldBeLessThanOrEqualTo(RemoteImageDecodeBudget.MaxRemoteFrames); InvalidOperationException ex = Should.Throw( () => RemoteImageDecodeBudget.EnsureDecodeAffordable(2500, 2500, 600, Uri)); ex.Message.ShouldContain("pixel limit"); } [Test] public void Should_Reject_Too_Many_Frames_Even_When_Each_Is_Tiny() => Should.Throw( () => RemoteImageDecodeBudget.EnsureDecodeAffordable(8, 8, RemoteImageDecodeBudget.MaxRemoteFrames + 1, Uri)) .Message.ShouldContain("frame limit"); [Test] public void Should_Reject_A_Single_Oversized_Frame() => Should.Throw( () => RemoteImageDecodeBudget.EnsureDimensionsAffordable(30000, 30000, Uri)) .Message.ShouldContain("pixel limit"); [Test] public void Should_Allow_A_Single_Large_Still_Within_Budget() => Should.NotThrow(() => RemoteImageDecodeBudget.EnsureDecodeAffordable(7680, 4320, 1, Uri)); [Test] public void Should_Charge_At_Least_One_Frame_When_Header_Reports_None() => Should.Throw( () => RemoteImageDecodeBudget.EnsureDecodeAffordable(30000, 30000, 0, Uri)); [Test] public void Should_Afford_Fewer_Frames_As_Frames_Get_Larger() { RemoteImageDecodeBudget.AffordableFrames(8, 8).ShouldBe(RemoteImageDecodeBudget.MaxRemoteFrames); RemoteImageDecodeBudget.AffordableFrames(1000, 1000).ShouldBe(50); RemoteImageDecodeBudget.AffordableFrames(7000, 7000).ShouldBe(1); } }