- CreatePlayoutRequest now carries a PlayoutScheduleKind discriminator, a
nullable ProgramScheduleId, and a ScheduleFile so POST /api/playouts can
create Classic, Block, Sequential, Scripted, or ExternalJson playouts (not
just Classic). ToCommand() validates per-kind requirements and returns
Either<BaseError, CreatePlayout>, surfacing 422 on mismatched fields via the
existing ToErrorResult() mapping.
- Add PUT /api/playouts/{id} (UpdatePlayoutDetailsRequest): DailyRebuildTime is
always applied (null clears the daily reset, matching the Blazor
SchedulePlayoutReset "Do not automatically reset" semantics); ScheduleFile is
only valid for Sequential/Scripted/ExternalJson playouts (422 otherwise) and
dispatches the matching Update*Playout command.
- Playout existence is checked via GetPlayoutById (real 404) before dispatching
UpdatePlayout, since the command's own "Playout does not exist." validation
produces a plain BaseError (422), not NotFoundError -- an existing quirk in
UpdatePlayoutHandler left as-is (out of scope for this slice).
- Extend ApiErrorResponseMetadataTests + PlayoutControllerTests for the new
Update action and the widened Create action (block/sequential/file-kind
validation paths).
- Regenerate ErsatzTV/wwwroot/openapi/v1.json via update-openapi.sh.
490 lines
20 KiB
C#
490 lines
20 KiB
C#
using System.Reflection;
|
|
using ErsatzTV.Application.Playouts;
|
|
using ErsatzTV.Controllers.Api;
|
|
using ErsatzTV.Controllers.Api.Requests;
|
|
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Api.Playouts;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Domain.Filler;
|
|
using ErsatzTV.Core.Errors;
|
|
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 PlayoutControllerTests
|
|
{
|
|
private PlayoutController _controller = null!;
|
|
private IMediator _mediator = null!;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_mediator = Substitute.For<IMediator>();
|
|
_controller = new PlayoutController(_mediator);
|
|
}
|
|
|
|
[Test]
|
|
public void Controller_Should_Expose_Idiomatic_Rest_Routes()
|
|
{
|
|
ShouldHaveActionRoute(nameof(PlayoutController.GetAll), "GET", "/api/playouts");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.GetById), "GET", "/api/playouts/{id:int}");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.GetItems), "GET", "/api/playouts/{id:int}/items");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.GetWarningsCount), "GET", "/api/playouts/warnings/count");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.Create), "POST", "/api/playouts");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.Update), "PUT", "/api/playouts/{id:int}");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.ResetAll), "POST", "/api/playouts/reset-all");
|
|
ShouldHaveActionRoute(nameof(PlayoutController.Delete), "DELETE", "/api/playouts/{id:int}");
|
|
}
|
|
|
|
[Test]
|
|
public void Create_Should_Use_Stable_Request_Dto()
|
|
{
|
|
MethodInfo action = typeof(PlayoutController).GetMethod(nameof(PlayoutController.Create))
|
|
?? throw new AssertionException($"Missing action {nameof(PlayoutController.Create)}");
|
|
|
|
action.GetParameters()[0].ParameterType.ShouldBe(typeof(CreatePlayoutRequest));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Return_201_With_Location_And_Body()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateClassicPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, CreatePlayoutResponse>(new CreatePlayoutResponse(9)));
|
|
PlayoutNameViewModel vm = MakePlayout(9);
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
|
|
IActionResult result = await _controller.Create(
|
|
new CreatePlayoutRequest(3, PlayoutScheduleKind.Classic, 4, null),
|
|
CancellationToken.None);
|
|
|
|
var created = result.ShouldBeOfType<CreatedResult>();
|
|
created.StatusCode.ShouldBe(201);
|
|
created.Location.ShouldBe("/api/playouts/9");
|
|
created.Value.ShouldBe(ToResponse(vm));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Map_Request_To_Classic_Playout_Command()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateClassicPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, CreatePlayoutResponse>(new CreatePlayoutResponse(9)));
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
|
|
|
await _controller.Create(new CreatePlayoutRequest(3, PlayoutScheduleKind.Classic, 4, null), CancellationToken.None);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<CreateClassicPlayout>(c => c.ChannelId == 3 && c.ProgramScheduleId == 4),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Return_404_For_NotFoundError_With_ProblemDetails()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateClassicPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Left<BaseError, CreatePlayoutResponse>(new NotFoundError("missing")));
|
|
|
|
IActionResult result = await _controller.Create(
|
|
new CreatePlayoutRequest(404, PlayoutScheduleKind.Classic, 4, null),
|
|
CancellationToken.None);
|
|
|
|
var notFound = result.ShouldBeOfType<NotFoundObjectResult>();
|
|
var problem = notFound.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(404);
|
|
problem.Title.ShouldBe("Resource not found");
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Return_422_On_Validation_Error_With_ProblemDetails()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateClassicPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Left<BaseError, CreatePlayoutResponse>(BaseError.New("bad")));
|
|
|
|
IActionResult result = await _controller.Create(
|
|
new CreatePlayoutRequest(3, PlayoutScheduleKind.Classic, 4, null),
|
|
CancellationToken.None);
|
|
|
|
var unprocessable = result.ShouldBeOfType<UnprocessableEntityObjectResult>();
|
|
var problem = unprocessable.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(422);
|
|
problem.Title.ShouldBe("Validation failed");
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Return_422_When_Classic_Missing_ProgramScheduleId()
|
|
{
|
|
IActionResult result = await _controller.Create(
|
|
new CreatePlayoutRequest(3, PlayoutScheduleKind.Classic, null, null),
|
|
CancellationToken.None);
|
|
|
|
var unprocessable = result.ShouldBeOfType<UnprocessableEntityObjectResult>();
|
|
var problem = unprocessable.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(422);
|
|
await _mediator.DidNotReceive().Send(Arg.Any<CreateClassicPlayout>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Map_Block_Kind_With_No_Extra_Fields()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateBlockPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, CreatePlayoutResponse>(new CreatePlayoutResponse(9)));
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
|
|
|
await _controller.Create(new CreatePlayoutRequest(3, PlayoutScheduleKind.Block, null, null), CancellationToken.None);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<CreateBlockPlayout>(c => c.ChannelId == 3),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[TestCase(PlayoutScheduleKind.Sequential)]
|
|
[TestCase(PlayoutScheduleKind.Scripted)]
|
|
[TestCase(PlayoutScheduleKind.ExternalJson)]
|
|
public async Task Create_Should_Return_422_For_File_Backed_Kinds_Missing_ScheduleFile(PlayoutScheduleKind kind)
|
|
{
|
|
IActionResult result = await _controller.Create(
|
|
new CreatePlayoutRequest(3, kind, null, null),
|
|
CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<UnprocessableEntityObjectResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Create_Should_Map_Sequential_Kind_With_ScheduleFile()
|
|
{
|
|
_mediator.Send(Arg.Any<CreateSequentialPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, CreatePlayoutResponse>(new CreatePlayoutResponse(9)));
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
|
|
|
await _controller.Create(
|
|
new CreatePlayoutRequest(3, PlayoutScheduleKind.Sequential, null, "/config/schedule.yml"),
|
|
CancellationToken.None);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<CreateSequentialPlayout>(c => c.ChannelId == 3 && c.ScheduleFile == "/config/schedule.yml"),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Return_404_When_Playout_Missing()
|
|
{
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.None);
|
|
|
|
IActionResult result = await _controller.Update(
|
|
404,
|
|
new UpdatePlayoutDetailsRequest(null, null),
|
|
CancellationToken.None);
|
|
|
|
var notFound = result.ShouldBeOfType<NotFoundObjectResult>();
|
|
var problem = notFound.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(404);
|
|
await _mediator.DidNotReceive().Send(Arg.Any<UpdatePlayout>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Apply_DailyRebuildTime_And_Return_200()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9);
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
_mediator.Send(Arg.Any<UpdatePlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, PlayoutNameViewModel>(vm with { DbDailyRebuildTime = TimeSpan.FromHours(4) }));
|
|
|
|
IActionResult result = await _controller.Update(
|
|
9,
|
|
new UpdatePlayoutDetailsRequest(TimeSpan.FromHours(4), null),
|
|
CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<OkObjectResult>();
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<UpdatePlayout>(c => c.PlayoutId == 9 && c.DailyRebuildTime == Some(TimeSpan.FromHours(4))),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Clear_DailyRebuildTime_When_Null()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9);
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
_mediator.Send(Arg.Any<UpdatePlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, PlayoutNameViewModel>(vm));
|
|
|
|
await _controller.Update(9, new UpdatePlayoutDetailsRequest(null, null), CancellationToken.None);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<UpdatePlayout>(c => c.PlayoutId == 9 && c.DailyRebuildTime == Option<TimeSpan>.None),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Return_422_When_ScheduleFile_Set_For_Classic_Playout()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9);
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
|
|
IActionResult result = await _controller.Update(
|
|
9,
|
|
new UpdatePlayoutDetailsRequest(null, "/config/schedule.yml"),
|
|
CancellationToken.None);
|
|
|
|
var unprocessable = result.ShouldBeOfType<UnprocessableEntityObjectResult>();
|
|
var problem = unprocessable.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(422);
|
|
await _mediator.DidNotReceive().Send(Arg.Any<UpdatePlayout>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Update_Should_Dispatch_UpdateSequentialPlayout_For_Sequential_Kind()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9) with { ScheduleKind = PlayoutScheduleKind.Sequential };
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
_mediator.Send(Arg.Any<UpdatePlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, PlayoutNameViewModel>(vm));
|
|
_mediator.Send(Arg.Any<UpdateSequentialPlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, PlayoutNameViewModel>(vm with { ScheduleFile = "/config/new.yml" }));
|
|
|
|
IActionResult result = await _controller.Update(
|
|
9,
|
|
new UpdatePlayoutDetailsRequest(null, "/config/new.yml"),
|
|
CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<OkObjectResult>();
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<UpdateSequentialPlayout>(c => c.PlayoutId == 9 && c.ScheduleFile == "/config/new.yml"),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Delete_Should_Return_204_On_Success()
|
|
{
|
|
_mediator.Send(Arg.Any<DeletePlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Right<BaseError, Unit>(Unit.Default));
|
|
|
|
IActionResult result = await _controller.Delete(9, CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<NoContentResult>();
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<DeletePlayout>(c => c.PlayoutId == 9),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task Delete_Should_Return_404_For_NotFoundError_With_ProblemDetails()
|
|
{
|
|
_mediator.Send(Arg.Any<DeletePlayout>(), Arg.Any<CancellationToken>())
|
|
.Returns(Left<BaseError, Unit>(new NotFoundError("missing")));
|
|
|
|
IActionResult result = await _controller.Delete(404, CancellationToken.None);
|
|
|
|
var notFound = result.ShouldBeOfType<NotFoundObjectResult>();
|
|
var problem = notFound.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(404);
|
|
problem.Title.ShouldBe("Resource not found");
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetById_Should_Return_200_For_Some()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9);
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(vm));
|
|
|
|
IActionResult result = await _controller.GetById(9, CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<OkObjectResult>().Value.ShouldBe(ToResponse(vm));
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetById_Should_Return_404_For_None_With_ProblemDetails()
|
|
{
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.None);
|
|
|
|
IActionResult result = await _controller.GetById(9, CancellationToken.None);
|
|
|
|
var notFound = result.ShouldBeOfType<NotFoundObjectResult>();
|
|
var problem = notFound.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(404);
|
|
problem.Title.ShouldBe("Resource not found");
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetAll_Should_Project_Paged_List_With_BuildStatus()
|
|
{
|
|
var buildStatus = new PlayoutBuildStatus
|
|
{
|
|
LastBuild = new DateTimeOffset(2026, 7, 2, 10, 0, 0, TimeSpan.Zero),
|
|
Success = false,
|
|
Message = "boom"
|
|
};
|
|
PlayoutNameViewModel vm = MakePlayout(9) with
|
|
{
|
|
BuildStatus = buildStatus,
|
|
DbDailyRebuildTime = TimeSpan.FromHours(4)
|
|
};
|
|
_mediator.Send(Arg.Any<GetPagedPlayouts>(), Arg.Any<CancellationToken>())
|
|
.Returns(new PagedPlayoutsViewModel(1, [vm]));
|
|
|
|
PagedPlayoutsResponseModel result = await _controller.GetAll("q", 2, 25, CancellationToken.None);
|
|
|
|
result.TotalCount.ShouldBe(1);
|
|
PlayoutListItemResponseModel item = result.Page.Single();
|
|
item.Id.ShouldBe(9);
|
|
item.ChannelNumber.ShouldBe("101");
|
|
item.ChannelName.ShouldBe("Channel");
|
|
item.ScheduleKind.ShouldBe(PlayoutScheduleKind.Classic);
|
|
item.ScheduleName.ShouldBe("Schedule");
|
|
item.DailyRebuildTime.ShouldBe(TimeSpan.FromHours(4));
|
|
item.BuildStatus.ShouldNotBeNull();
|
|
item.BuildStatus.Success.ShouldBeFalse();
|
|
item.BuildStatus.Message.ShouldBe("boom");
|
|
item.BuildStatus.LastBuild.ShouldBe(buildStatus.LastBuild);
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<GetPagedPlayouts>(q => q.Query == "q" && q.PageNum == 2 && q.PageSize == 25),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetAll_Should_Emit_Null_BuildStatus_When_Absent()
|
|
{
|
|
PlayoutNameViewModel vm = MakePlayout(9) with { BuildStatus = null };
|
|
_mediator.Send(Arg.Any<GetPagedPlayouts>(), Arg.Any<CancellationToken>())
|
|
.Returns(new PagedPlayoutsViewModel(1, [vm]));
|
|
|
|
PagedPlayoutsResponseModel result = await _controller.GetAll("", 0, 100, CancellationToken.None);
|
|
|
|
result.Page.Single().BuildStatus.ShouldBeNull();
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetItems_Should_Project_Items_And_Null_FillerKind_For_Gaps()
|
|
{
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.Some(MakePlayout(9)));
|
|
|
|
var item = new PlayoutItemViewModel(
|
|
"Movie",
|
|
new DateTimeOffset(2026, 7, 2, 12, 0, 0, TimeSpan.Zero),
|
|
new DateTimeOffset(2026, 7, 2, 13, 0, 0, TimeSpan.Zero),
|
|
"1:00:00",
|
|
string.Empty,
|
|
Some(FillerKind.MidRoll));
|
|
var gap = new PlayoutItemViewModel(
|
|
"UNSCHEDULED",
|
|
new DateTimeOffset(2026, 7, 2, 13, 0, 0, TimeSpan.Zero),
|
|
new DateTimeOffset(2026, 7, 2, 13, 30, 0, TimeSpan.Zero),
|
|
"30:00",
|
|
string.Empty,
|
|
Option<FillerKind>.None);
|
|
_mediator.Send(Arg.Any<GetFuturePlayoutItemsById>(), Arg.Any<CancellationToken>())
|
|
.Returns(new PagedPlayoutItemsViewModel(2, [item, gap]));
|
|
|
|
IActionResult actionResult = await _controller.GetItems(9, showFiller: true, 1, 10, CancellationToken.None);
|
|
|
|
var result = actionResult.ShouldBeOfType<OkObjectResult>().Value.ShouldBeOfType<PagedPlayoutItemsResponseModel>();
|
|
result.TotalCount.ShouldBe(2);
|
|
result.Page[0].Title.ShouldBe("Movie");
|
|
result.Page[0].Duration.ShouldBe("1:00:00");
|
|
result.Page[0].FillerKind.ShouldBe(FillerKind.MidRoll);
|
|
result.Page[1].Title.ShouldBe("UNSCHEDULED");
|
|
result.Page[1].FillerKind.ShouldBeNull();
|
|
|
|
await _mediator.Received(1).Send(
|
|
Arg.Is<GetFuturePlayoutItemsById>(q =>
|
|
q.PlayoutId == 9 && q.ShowFiller && q.PageNum == 1 && q.PageSize == 10),
|
|
Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetItems_Should_Return_404_For_Unknown_Playout_With_ProblemDetails()
|
|
{
|
|
_mediator.Send(Arg.Any<GetPlayoutById>(), Arg.Any<CancellationToken>())
|
|
.Returns(Option<PlayoutNameViewModel>.None);
|
|
|
|
IActionResult result = await _controller.GetItems(404, showFiller: false, 0, 100, CancellationToken.None);
|
|
|
|
var notFound = result.ShouldBeOfType<NotFoundObjectResult>();
|
|
var problem = notFound.Value.ShouldBeOfType<ProblemDetails>();
|
|
problem.Status.ShouldBe(404);
|
|
problem.Title.ShouldBe("Resource not found");
|
|
|
|
await _mediator.DidNotReceive().Send(Arg.Any<GetFuturePlayoutItemsById>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetWarningsCount_Should_Return_Count()
|
|
{
|
|
_mediator.Send(Arg.Any<GetPlayoutWarningsCount>(), Arg.Any<CancellationToken>())
|
|
.Returns(7);
|
|
|
|
int result = await _controller.GetWarningsCount(CancellationToken.None);
|
|
|
|
result.ShouldBe(7);
|
|
}
|
|
|
|
[Test]
|
|
public async Task ResetAll_Should_Return_202_And_Send_Command()
|
|
{
|
|
IActionResult result = await _controller.ResetAll(CancellationToken.None);
|
|
|
|
result.ShouldBeOfType<AcceptedResult>();
|
|
await _mediator.Received(1).Send(Arg.Any<ResetAllPlayouts>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
private static PlayoutNameViewModel MakePlayout(int id) =>
|
|
new(
|
|
id,
|
|
PlayoutScheduleKind.Classic,
|
|
"Channel",
|
|
"101",
|
|
ChannelPlayoutMode.Continuous,
|
|
"Schedule",
|
|
string.Empty,
|
|
null,
|
|
new PlayoutBuildStatus());
|
|
|
|
private static PlayoutResponseModel ToResponse(PlayoutNameViewModel vm) =>
|
|
PlayoutResponseModel.From(
|
|
vm.PlayoutId,
|
|
vm.ScheduleKind,
|
|
vm.ChannelName,
|
|
vm.ChannelNumber,
|
|
vm.PlayoutMode,
|
|
vm.ScheduleName,
|
|
vm.ScheduleFile,
|
|
vm.DbDailyRebuildTime,
|
|
vm.BuildStatus is null
|
|
? null
|
|
: new PlayoutBuildStatusResponseModel(
|
|
vm.BuildStatus.LastBuild,
|
|
vm.BuildStatus.Success,
|
|
vm.BuildStatus.Message));
|
|
|
|
private static void ShouldHaveActionRoute(string actionName, string httpMethod, string route)
|
|
{
|
|
MethodInfo action = typeof(PlayoutController).GetMethod(actionName)
|
|
?? throw new AssertionException($"Missing action {actionName}");
|
|
|
|
HttpMethodAttribute attribute = action.GetCustomAttributes<HttpMethodAttribute>(inherit: true).Single();
|
|
attribute.HttpMethods.ShouldContain(httpMethod);
|
|
attribute.Template.ShouldBe(route);
|
|
}
|
|
}
|