Files
ersatztv/ErsatzTV.Tests/Application/Channels/GetChannelByIdForApiHandlerTests.cs
T
timothyandClaude Opus 4.8 70f357f8f9 feat(api): #288 ChannelDetailResponseModel for edit form + SPA repoints + final regen
Mint ChannelDetailResponseModel (faithful detail DTO exposing the raw editable
field set the channel editor reads: raw FFmpegProfileId/WatermarkId/FallbackFillerId
ids, the mode enums, logo, playoutCount, id) and route GetById/Create/Update through
it, replacing the lean list ChannelResponseModel that resolved the profile to a name
and dropped the editable ids (a functional regression for draftFromChannel). The lean
ChannelResponseModel stays unchanged for GET /api/channels. webEncodedName dropped
(SPA never reads it). Logo is mirrored as a Core ChannelLogoResponseModel since the
Application ArtworkContentTypeModel can't be referenced from Core.

Repoint the hand-written SPA client aliases now that the VMs are gone from the schema:
Channel -> ChannelDetailResponseModel, MediaCollection/SmartCollection -> *ResponseModel,
ProgramSchedule -> ProgramScheduleResponseModel. Fix #288 honest-nullability test fallout
in search.test.ts (null -> [] for now-non-null id arrays). Include the already-on-disk
playouts.ts WithDayNames removal and regenerate v1.json + v1.d.ts + endpoint-index.md
(authoritative final regen; the reset endpoint's {channelNumber}->{id} re-key surfaces
in the generated docs and the OpenApi error-contract test).

Refs #288 #197

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:33:39 +02:00

74 lines
2.7 KiB
C#

using ErsatzTV.Application.Channels;
using ErsatzTV.Core.Api.Channels;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using NSubstitute;
using NUnit.Framework;
using Shouldly;
namespace ErsatzTV.Tests.Application.Channels;
[TestFixture]
public class GetChannelByIdForApiHandlerTests
{
[Test]
public async Task Should_Project_DetailResponseModel_When_Found()
{
IChannelRepository repository = Substitute.For<IChannelRepository>();
repository.GetChannel(7)
.Returns(Option<Channel>.Some(new Channel(Guid.NewGuid())
{
Id = 7,
Number = "7.1",
SortNumber = 7.1,
Name = "Retro Cartoons",
Group = "Kids",
Categories = "animation",
FFmpegProfileId = 3,
FFmpegProfile = new FFmpegProfile { Name = "HLS 720p" },
Artwork = [],
PreferredAudioLanguageCode = "eng",
StreamingMode = StreamingMode.HttpLiveStreamingSegmenter,
WatermarkId = 11,
FallbackFillerId = 12,
IsEnabled = false,
ShowInEpg = false
}));
var handler = new GetChannelByIdForApiHandler(repository);
Option<ChannelDetailResponseModel> result =
await handler.Handle(new GetChannelByIdForApi(7), CancellationToken.None);
result.IsSome.ShouldBeTrue();
ChannelDetailResponseModel channel = result.IfNone(() => throw new InvalidOperationException());
channel.Id.ShouldBe(7);
channel.Number.ShouldBe("7.1");
channel.Name.ShouldBe("Retro Cartoons");
channel.Group.ShouldBe("Kids");
channel.Categories.ShouldBe("animation");
// Detail model exposes the raw editable ids the editor re-drafts (not a resolved name).
channel.FFmpegProfileId.ShouldBe(3);
channel.WatermarkId.ShouldBe(11);
channel.FallbackFillerId.ShouldBe(12);
channel.PreferredAudioLanguageCode.ShouldBe("eng");
channel.StreamingMode.ShouldBe(StreamingMode.HttpLiveStreamingSegmenter);
channel.PlayoutCount.ShouldBe(0);
channel.IsEnabled.ShouldBeFalse();
channel.ShowInEpg.ShouldBeFalse();
}
[Test]
public async Task Should_Return_None_When_Missing()
{
IChannelRepository repository = Substitute.For<IChannelRepository>();
repository.GetChannel(Arg.Any<int>()).Returns(Option<Channel>.None);
var handler = new GetChannelByIdForApiHandler(repository);
Option<ChannelDetailResponseModel> result =
await handler.Handle(new GetChannelByIdForApi(99), CancellationToken.None);
result.IsNone.ShouldBeTrue();
}
}