using ErsatzTV.Controllers.Api.Requests; using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; using NUnit.Framework; namespace ErsatzTV.Tests.Support; /// /// One selection-type matrix, shared by every fixture that exercises a tagged-union selection /// (rerun collections and playlist items). Both consumers of /// MediaCollections.Mapper.ProjectMediaItemToViewModel are proved against the SAME data, so /// widening the shared switch cannot be discharged for the second consumer by inspection alone — /// which is the method that produced #671 in the first place. /// internal static class SelectionSeedData { public const int SelectedId = 42; /// /// Derived from production rather than hand-listed, so a newly-supported type joins the matrix /// automatically and trips the default: arms below until someone teaches them about it. /// Note this is the RERUN-COLLECTION predicate, used for playlist items as a deliberate /// SUPERSET: ReplacePlaylistItemsHandler.CollectionTypeMustBeValid has no /// RemoteStream case, so a RemoteStream playlist item cannot be created through the write /// API today and the playlist fixture seeds that row directly. Covering it is forward-looking, /// not a claim that the two sets are equivalent — split this if they ever legitimately diverge. /// public static IEnumerable SupportedSelectionTypes => Enum.GetValues().Where(RerunCollectionRequestMapping.IsSupportedSelectionType); /// /// The exact projected name per type. Pinning the whole string — rather than merely asserting /// "not a placeholder" — is what makes a missing NESTED include leg visible: dropping /// Episode → Season → Show still yields the placeholder-free "s??e04 - Selected episode", and /// dropping MusicVideo → Artist still yields "Selected music video". Both would sail past a /// looser assertion while having lost real data. /// public static string ExpectedName(CollectionType collectionType) => collectionType switch { CollectionType.Collection => "Selected collection", CollectionType.MultiCollection => "Selected multi collection", CollectionType.SmartCollection => "Selected smart collection", CollectionType.TelevisionShow => "Selected show (2020)", CollectionType.TelevisionSeason => "Parent show (2020) - Season 3", CollectionType.Artist => "Selected artist", CollectionType.Movie => "Selected movie (2019)", CollectionType.Episode => "Episode's show - s02e04 - Selected episode", CollectionType.MusicVideo => "Video's artist - Selected music video", CollectionType.OtherVideo => "Selected other video", CollectionType.Song => "Song artist - Selected song", CollectionType.Image => "Selected image", CollectionType.RemoteStream => "Selected remote stream", _ => throw new AssertionException($"No expected name pinned for {collectionType}") }; public static async Task SeedSelection(TvContext context, CollectionType collectionType) { switch (collectionType) { case CollectionType.Collection: context.Collections.Add(new Collection { Id = SelectedId, Name = "Selected collection", MediaItems = [] }); break; case CollectionType.MultiCollection: context.MultiCollections.Add(new MultiCollection { Id = SelectedId, Name = "Selected multi collection" }); break; case CollectionType.SmartCollection: context.SmartCollections.Add(new SmartCollection { Id = SelectedId, Name = "Selected smart collection", Query = "tag:family" }); break; case CollectionType.TelevisionShow: context.Shows.Add(new Show { Id = SelectedId, ShowMetadata = [new ShowMetadata { Title = "Selected show", Year = 2020 }] }); break; case CollectionType.TelevisionSeason: context.Seasons.Add(new Season { Id = SelectedId, SeasonNumber = 3, Show = new Show { Id = 900, ShowMetadata = [new ShowMetadata { Title = "Parent show", Year = 2020 }] } }); break; case CollectionType.Artist: context.Artists.Add(new Artist { Id = SelectedId, ArtistMetadata = [new ArtistMetadata { Title = "Selected artist" }] }); break; case CollectionType.Movie: context.Movies.Add(new Movie { Id = SelectedId, MovieMetadata = [new MovieMetadata { Title = "Selected movie", Year = 2019 }] }); break; case CollectionType.Episode: context.Episodes.Add(new Episode { Id = SelectedId, EpisodeMetadata = [new EpisodeMetadata { Title = "Selected episode", EpisodeNumber = 4 }], Season = new Season { Id = 901, SeasonNumber = 2, Show = new Show { Id = 902, ShowMetadata = [new ShowMetadata { Title = "Episode's show", Year = 2018 }] } } }); break; case CollectionType.MusicVideo: context.MusicVideos.Add(new MusicVideo { Id = SelectedId, MusicVideoMetadata = [new MusicVideoMetadata { Title = "Selected music video" }], Artist = new Artist { Id = 903, ArtistMetadata = [new ArtistMetadata { Title = "Video's artist" }] } }); break; case CollectionType.OtherVideo: context.OtherVideos.Add(new OtherVideo { Id = SelectedId, OtherVideoMetadata = [new OtherVideoMetadata { Title = "Selected other video" }] }); break; case CollectionType.Song: context.Songs.Add(new Song { Id = SelectedId, SongMetadata = [new SongMetadata { Title = "Selected song", Artists = ["Song artist"] }] }); break; case CollectionType.Image: context.Images.Add(new Image { Id = SelectedId, ImageMetadata = [new ImageMetadata { Title = "Selected image" }] }); break; case CollectionType.RemoteStream: context.RemoteStreams.Add(new RemoteStream { Id = SelectedId, Url = "http://example.invalid/stream", RemoteStreamMetadata = [new RemoteStreamMetadata { Title = "Selected remote stream" }] }); break; default: throw new AssertionException( $"{collectionType} is a supported selection type but this suite does not know how " + "to seed it — teach SeedSelection about it rather than narrowing the matrix."); } await context.SaveChangesAsync(); } /// /// Assigns the one foreign key the tagged union uses for this type. Shared so the rerun and /// playlist fixtures cannot disagree about which slot a type occupies. /// public static void ApplySelection( CollectionType collectionType, Action setCollectionId, Action setMultiCollectionId, Action setSmartCollectionId, Action setMediaItemId) { switch (collectionType) { case CollectionType.Collection: setCollectionId(SelectedId); break; case CollectionType.MultiCollection: setMultiCollectionId(SelectedId); break; case CollectionType.SmartCollection: setSmartCollectionId(SelectedId); break; default: setMediaItemId(SelectedId); break; } } }