using System; using System.IO; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Services; using ytLive.ViewModels; namespace ytLive.Tests; /// /// TASK 32·3 — Back on air. Once YouTube completes a broadcast it cannot be resumed, so the only honest /// route back is a NEW broadcast: one click, saved details, no dialog, and a title that says it is a /// resume (otherwise the channel grows two identically named videos). /// // Constructs a MainViewModel directly, and `LayoutPathOverride` is static: it belongs in the serialised // collection with the other direct-construction tests, or a parallel sibling can swap the layout path // underneath it (observed as a one-off failure in a full run, green in isolation). [Collection("MainViewModelLayoutPath")] public class BackOnAirTests { [Fact] public void ResumeTitle_MarksTheFirstReAir() => Assert.Equal("Friday Night Show (resumed)", BroadcastTitle.ForResume("Friday Night Show", 1)); [Fact] public void ResumeTitle_CountsASecondDrop_InsteadOfStackingMarkers() => Assert.Equal("Friday Night Show (resumed 2)", BroadcastTitle.ForResume("Friday Night Show (resumed)", 2)); [Fact] public void ResumeTitle_FitsYouTubesHundredCharacterLimit() { var longTitle = new string('x', 140); var resumed = BroadcastTitle.ForResume(longTitle, 1); Assert.Equal(BroadcastTitle.MaxLength, resumed.Length); Assert.EndsWith(" (resumed)", resumed); } [Fact] public void ResumeTitle_HandlesAnEmptyTitle() => Assert.Equal("LlamaCasty (resumed)", BroadcastTitle.ForResume(" ", 1)); [Fact] public void AfterADrop_TheCreatorIsOfferedOneClickBackOnAir() { var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-backonair-{Guid.NewGuid():N}.db"); MainViewModel.LayoutPathOverride = tempDb; var vm = new MainViewModel(); try { Assert.False(vm.IsOffAirAfterDrop); Assert.False(vm.BackOnAirCommand.CanExecute(null)); vm.MarkOffAirAfterDropForTest("Friday Night Show"); Assert.True(vm.IsOffAirAfterDrop); Assert.True(vm.BackOnAirCommand.CanExecute(null)); Assert.Equal("● Back on air", vm.BackOnAirLabel); // The ordinary start button steps aside so there is exactly one way forward. Assert.False(vm.ShowPrimaryStartButton); } finally { MainViewModel.LayoutPathOverride = null; SqliteConnection.ClearAllPools(); try { File.Delete(tempDb); } catch { /* best-effort cleanup */ } } } }