277 lines
9.4 KiB
C#
277 lines
9.4 KiB
C#
using System.Reflection;
|
|
using ErsatzTV.Application.ChannelTemplates;
|
|
using ErsatzTV.Controllers.Api;
|
|
using ErsatzTV.Controllers.Api.Requests;
|
|
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Api.ChannelTemplates;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Errors;
|
|
using ErsatzTV.Core.Scheduling;
|
|
using LanguageExt;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.Routing;
|
|
using NSubstitute;
|
|
using NUnit.Framework;
|
|
using Shouldly;
|
|
using static LanguageExt.Prelude;
|
|
using Unit = LanguageExt.Unit;
|
|
|
|
namespace ErsatzTV.Tests.Controllers;
|
|
|
|
[TestFixture]
|
|
public class ChannelTemplateControllerTests
|
|
{
|
|
private ChannelTemplateController _controller = null!;
|
|
private IMediator _mediator = null!;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_mediator = Substitute.For<IMediator>();
|
|
_controller = new ChannelTemplateController(_mediator);
|
|
}
|
|
|
|
[Test]
|
|
public void Controller_Should_Expose_Named_Rest_Routes()
|
|
{
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.GetAll),
|
|
"GET",
|
|
"/api/channel-templates",
|
|
"GetChannelTemplates");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.GetDefault),
|
|
"GET",
|
|
"/api/channel-templates/default",
|
|
"GetDefaultChannelTemplate");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.SetDefault),
|
|
"PUT",
|
|
"/api/channel-templates/default/{id:int}",
|
|
"SetDefaultChannelTemplate");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.GetById),
|
|
"GET",
|
|
"/api/channel-templates/{id:int}",
|
|
"GetChannelTemplateById");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.Create),
|
|
"POST",
|
|
"/api/channel-templates",
|
|
"CreateChannelTemplate");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.Update),
|
|
"PUT",
|
|
"/api/channel-templates/{id:int}",
|
|
"UpdateChannelTemplate");
|
|
ShouldHaveActionRoute(
|
|
nameof(ChannelTemplateController.Delete),
|
|
"DELETE",
|
|
"/api/channel-templates/{id:int}",
|
|
"DeleteChannelTemplate");
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetAll_Should_Return_ChannelTemplates()
|
|
{
|
|
List<ChannelTemplateResponseModel> models = [MakeResponse(1, "Standard", isDefault: true)];
|
|
_mediator.Send(Arg.Any<GetAllChannelTemplates>(), Arg.Any<CancellationToken>())
|
|
.Returns(models);
|
|
|
|
List<ChannelTemplateResponseModel> result = await _controller.GetAll(CancellationToken.None);
|
|
|
|
result.ShouldBe(models);
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetDefault_Should_Return_404_When_Default_Missing()
|
|
{
|
|
_mediator.Send(Arg.Any<GetDefaultChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<ChannelTemplateResponseModel>.None);
|
|
|
|
IActionResult result = await _controller.GetDefault(CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<NotFoundObjectResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Return_201_With_Location_And_Body()
|
|
{
|
|
ChannelTemplateResponseModel vm = MakeResponse(7, "Custom");
|
|
_mediator.Send(Arg.Any<CreateChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, ChannelTemplateResponseModel>(vm));
|
|
|
|
IActionResult result = await _controller.Create(MakeCreateRequest("Custom"), CancellationToken.None);
|
|
|
|
var created = result.ShouldBeOfType<CreatedResult>();
|
|
created.Location.ShouldBe("/api/channel-templates/7");
|
|
created.Value.ShouldBe(vm);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Map_Request_To_Command()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, ChannelTemplateResponseModel>(MakeResponse(7, "Custom")));
|
|
|
|
await _controller.Create(MakeCreateRequest("Custom"), CancellationToken.None);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<CreateChannelTemplate>(c =>
|
|
c.Name == "Custom" &&
|
|
c.FFmpegProfileId == 1 &&
|
|
c.PreRollFillerId == 2 &&
|
|
c.MidRollFillerId == 3 &&
|
|
c.PostRollFillerId == 4 &&
|
|
c.ShuffleScheduleItems),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Return_404_For_NotFoundError()
|
|
{
|
|
_mediator.Send(Arg.Any<UpdateChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Left<BaseError, ChannelTemplateResponseModel>(new NotFoundError("missing")));
|
|
|
|
IActionResult result = await _controller.Update(99, MakeUpdateRequest("Missing"), CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<NotFoundObjectResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SetDefault_Should_Return_200_With_Default_Template()
|
|
{
|
|
ChannelTemplateResponseModel vm = MakeResponse(8, "Default", isDefault: true);
|
|
_mediator.Send(Arg.Any<SetDefaultChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, ChannelTemplateResponseModel>(vm));
|
|
|
|
IActionResult result = await _controller.SetDefault(8, CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<OkObjectResult>().Value.ShouldBe(vm);
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<SetDefaultChannelTemplate>(c => c.ChannelTemplateId == 8),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Delete_Should_Return_204_On_Success()
|
|
{
|
|
_mediator.Send(Arg.Any<DeleteChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, Unit>(Unit.Default));
|
|
|
|
IActionResult result = await _controller.Delete(9, CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<NoContentResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Delete_Should_Return_422_When_System_Template()
|
|
{
|
|
_mediator.Send(Arg.Any<DeleteChannelTemplate>(), Arg.Any<CancellationToken>())
|
|
.Returns(Left<BaseError, Unit>(BaseError.New("System templates cannot be deleted.")));
|
|
|
|
IActionResult result = await _controller.Delete(9, CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<UnprocessableEntityObjectResult>();
|
|
}
|
|
|
|
private static void ShouldHaveActionRoute(string actionName, string httpMethod, string route, string name)
|
|
{
|
|
MethodInfo action = typeof(ChannelTemplateController).GetMethod(actionName)
|
|
?? throw new AssertionException($"Missing action {actionName}");
|
|
|
|
HttpMethodAttribute attribute = action.GetCustomAttributes<HttpMethodAttribute>(inherit: true).Single();
|
|
attribute.HttpMethods.ShouldContain(httpMethod);
|
|
attribute.Template.ShouldBe(route);
|
|
attribute.Name.ShouldBe(name);
|
|
}
|
|
|
|
private static CreateChannelTemplateRequest MakeCreateRequest(string name) =>
|
|
new(
|
|
name,
|
|
"Description",
|
|
1,
|
|
null,
|
|
null,
|
|
2,
|
|
3,
|
|
4,
|
|
ChannelStreamSelectorMode.Default,
|
|
string.Empty,
|
|
string.Empty,
|
|
string.Empty,
|
|
ChannelPlayoutSource.Generated,
|
|
ChannelPlayoutMode.Continuous,
|
|
StreamingMode.TransportStreamHybrid,
|
|
string.Empty,
|
|
ChannelSubtitleMode.None,
|
|
ChannelMusicVideoCreditsMode.None,
|
|
string.Empty,
|
|
ChannelSongVideoMode.Default,
|
|
ChannelTranscodeMode.OnDemand,
|
|
ChannelIdleBehavior.StopOnDisconnect,
|
|
true,
|
|
false,
|
|
FixedStartTimeBehavior.Flexible);
|
|
|
|
private static UpdateChannelTemplateRequest MakeUpdateRequest(string name) =>
|
|
new(
|
|
name,
|
|
"Description",
|
|
1,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
ChannelStreamSelectorMode.Default,
|
|
string.Empty,
|
|
string.Empty,
|
|
string.Empty,
|
|
ChannelPlayoutSource.Generated,
|
|
ChannelPlayoutMode.Continuous,
|
|
StreamingMode.TransportStreamHybrid,
|
|
string.Empty,
|
|
ChannelSubtitleMode.None,
|
|
ChannelMusicVideoCreditsMode.None,
|
|
string.Empty,
|
|
ChannelSongVideoMode.Default,
|
|
ChannelTranscodeMode.OnDemand,
|
|
ChannelIdleBehavior.StopOnDisconnect,
|
|
false,
|
|
false,
|
|
FixedStartTimeBehavior.Flexible);
|
|
|
|
private static ChannelTemplateResponseModel MakeResponse(int id, string name, bool isDefault = false) =>
|
|
new(
|
|
id,
|
|
name,
|
|
"Description",
|
|
false,
|
|
isDefault,
|
|
1,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
ChannelStreamSelectorMode.Default,
|
|
string.Empty,
|
|
string.Empty,
|
|
string.Empty,
|
|
ChannelPlayoutSource.Generated,
|
|
ChannelPlayoutMode.Continuous,
|
|
StreamingMode.TransportStreamHybrid,
|
|
string.Empty,
|
|
ChannelSubtitleMode.None,
|
|
ChannelMusicVideoCreditsMode.None,
|
|
string.Empty,
|
|
ChannelSongVideoMode.Default,
|
|
ChannelTranscodeMode.OnDemand,
|
|
ChannelIdleBehavior.StopOnDisconnect,
|
|
false,
|
|
false,
|
|
FixedStartTimeBehavior.Flexible);
|
|
}
|