using ErsatzTV.Application.Resolutions; using ErsatzTV.Controllers.Api; using ErsatzTV.Controllers.Api.Requests; using ErsatzTV.Core; using ErsatzTV.Core.Api.Settings; using ErsatzTV.Core.Errors; using LanguageExt; using MediatR; using Microsoft.AspNetCore.Mvc; using NSubstitute; using NUnit.Framework; using Shouldly; using static LanguageExt.Prelude; namespace ErsatzTV.Tests.Controllers; [TestFixture] public class ResolutionControllerTests { private ResolutionController _controller = null!; private IMediator _mediator = null!; [SetUp] public void SetUp() { _mediator = Substitute.For(); _controller = new ResolutionController(_mediator); } [Test] public async Task GetAll_Should_Map_View_Models_To_Response_Models() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns( new List { new(1, "1920x1080", 1920, 1080, false), new(2, "1280x720", 1280, 720, true) }); List result = await _controller.GetAll(CancellationToken.None); result.ShouldBe( [ new ResolutionResponseModel(1, "1920x1080", 1920, 1080, false), new ResolutionResponseModel(2, "1280x720", 1280, 720, true) ]); } [Test] public async Task Create_Should_Return_201_With_Location_And_Body() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.None); _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.Some(new ResolutionViewModel(9, "640x480", 640, 480, true))); IActionResult result = await _controller.Create(new CreateResolutionRequest(640, 480), CancellationToken.None); var created = result.ShouldBeOfType(); created.StatusCode.ShouldBe(201); created.Location.ShouldBe("/api/settings/resolutions/9"); created.Value.ShouldBe(new ResolutionResponseModel(9, "640x480", 640, 480, true)); await _mediator.Received(1).Send( Arg.Is(c => c.Width == 640 && c.Height == 480), Arg.Any()); } [Test] public async Task Create_Should_Return_422_When_Resolution_Is_Not_Unique() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.Some(BaseError.New("Resolution width and height must be unique"))); IActionResult result = await _controller.Create(new CreateResolutionRequest(1920, 1080), CancellationToken.None); result.ShouldBeOfType(); } [Test] public async Task Delete_Should_Return_204_On_Success() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.None); IActionResult result = await _controller.Delete(9, CancellationToken.None); result.ShouldBeOfType(); } [Test] public async Task Delete_Should_Return_404_For_Unknown_Resolution() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.Some(new NotFoundError("Resolution 42 does not exist."))); IActionResult result = await _controller.Delete(42, CancellationToken.None); result.ShouldBeOfType(); } [Test] public async Task Delete_Should_Return_422_For_NonCustom_Resolution() { _mediator.Send(Arg.Any(), Arg.Any()) .Returns(Option.Some(BaseError.New("Resolution 3 is not a custom resolution."))); IActionResult result = await _controller.Delete(3, CancellationToken.None); result.ShouldBeOfType(); } }