using System; using System.IO; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Services; using ytLive.ViewModels; namespace ytLive.Tests; /// /// Session-teardown integration (2026-09-01, first native launch): a frame-pump death /// must roll the WHOLE session back — record-only sessions used to stay IsRecording=true /// with a dead encoder (no live session to flip StreamStatus), and StopStream never /// cleared the intent pills. "Stop ends everything" (creator ruling): pills slide off /// with the session; re-arming is one click. Real-App + temp-DB pattern. /// [Collection("RealApp")] public sealed class SessionTeardownTests { private readonly RealAppHost _app; public SessionTeardownTests(RealAppHost app) => _app = app; [Fact] public void Pump_Failure_Rolls_Back_Recording_And_Clears_Pills() { _app.Run(Run); } private void Run() { var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-teardown-{Guid.NewGuid():N}.db"); MainViewModel.LayoutPathOverride = tempDb; var window = default(MainWindow); try { using (var schema = new LayoutStore(tempDb)) { } SqliteConnection.ClearAllPools(); window = new MainWindow(); var vm = (MainViewModel)window.DataContext; window.Show(); window.UpdateLayout(); // The exact 2026-09-01 zombie: record-only, intent lit, reality true, // encoder already dead. Sign-in absent — the guard must also prove no // close-out API call is attempted for a record-only session (StopStream // completes without throwing offline). vm.RecordPillOn = true; vm.IsRecording = true; vm.OnFramePumpFailed(this, "test: ffmpeg exited"); Assert.False(vm.IsRecording, "pump death must roll IsRecording back — no zombie session"); Assert.False(vm.RecordPillOn, "Stop ends everything — the REC pill slides off with the session"); Assert.False(vm.OnAirPillOn, "both pills clear"); Assert.False(vm.ShowEndStreamButton, "no End button once nothing is running"); Assert.Equal(ytLive.Models.StreamStatus.Offline, vm.StreamStatus); // No dead ends (2026-09-01): after a stop the top bar must still offer a // reachable Start — pills cleared + signed out used to blank the button. Assert.True(vm.ShowPrimaryStartButton, "a stopped session must leave a Start button"); } finally { if (window != null) window.Close(); MainViewModel.LayoutPathOverride = null; SqliteConnection.ClearAllPools(); try { File.Delete(tempDb); } catch { /* best-effort */ } } } }