using System; using System.IO; using System.Linq; using Microsoft.Data.Sqlite; using Xunit; using ytLive.Models; using ytLive.Services; namespace ytLive.Tests; public sealed class LayoutStoreSnapshotTests { [Fact] public void SameBuild_OpensCurrentLayout_WithoutChangingDefaultSnapshot() { var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-snapshot-{Guid.NewGuid():N}.db"); const string buildId = "build-a"; try { using (var store = new LayoutStore(path)) { var factory = MakeScene("Factory title"); store.Save(new[] { factory }, null, null); store.PrepareLayoutSnapshots(buildId); store.Save(new[] { MakeScene("Creator edit") }, null, null); } using (var reopened = new LayoutStore(path)) { reopened.PrepareLayoutSnapshots(buildId); var current = Assert.Single(reopened.Load()); Assert.Equal("Creator edit", Assert.Single(current.Elements.OfType()).Name); using var connection = GetConnection(path); using var command = connection.CreateCommand(); command.CommandText = "SELECT Value FROM LayoutSnapshotState WHERE Key = 'CurrentBuildId';"; Assert.Equal(buildId, command.ExecuteScalar()); command.CommandText = "SELECT Name FROM DefaultSource;"; Assert.Equal("Factory title", command.ExecuteScalar()); } } finally { SqliteConnection.ClearAllPools(); try { File.Delete(path); } catch { /* best-effort cleanup */ } } } [Fact] public void DifferentBuild_RestoresTheDefaultSnapshotBeforeLoading() { var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-snapshot-{Guid.NewGuid():N}.db"); try { using (var store = new LayoutStore(path)) { store.Save(new[] { MakeScene("Factory title") }, null, null); store.PrepareLayoutSnapshots("build-a"); store.Save(new[] { MakeScene("Creator edit") }, null, null); } using (var reopened = new LayoutStore(path)) { reopened.PrepareLayoutSnapshots("build-b"); var restored = Assert.Single(reopened.Load()); Assert.Equal("Factory title", Assert.Single(restored.Elements.OfType()).Name); using var connection = GetConnection(path); using var command = connection.CreateCommand(); command.CommandText = "SELECT Value FROM LayoutSnapshotState WHERE Key = 'CurrentBuildId';"; Assert.Equal("build-b", command.ExecuteScalar()); } } finally { SqliteConnection.ClearAllPools(); try { File.Delete(path); } catch { /* best-effort cleanup */ } } } [Fact] public void ResetOneScene_RestoresItsFactoryLayers_AndKeepsOtherSceneEdits() { var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-snapshot-{Guid.NewGuid():N}.db"); var starting = MakeScene(SceneCatalog.Starting, "Factory starting"); var brb = MakeScene(SceneCatalog.Brb, "Factory BRB"); try { using (var store = new LayoutStore(path)) { store.Save(new[] { starting, brb }, null, null); store.PrepareLayoutSnapshots("build-a"); store.Save(new[] { MakeScene(starting.Id, starting.Name, "Edited starting"), MakeScene(brb.Id, brb.Name, "Edited BRB"), }, null, null); } using (var reopened = new LayoutStore(path)) { reopened.PrepareLayoutSnapshots("build-a"); Assert.True(reopened.RestoreDefaultScene(starting.Id)); var scenes = reopened.Load(); Assert.Equal("Factory starting", Assert.Single(scenes.Single(s => s.Id == starting.Id).Elements.OfType()).Name); Assert.Equal("Edited BRB", Assert.Single(scenes.Single(s => s.Id == brb.Id).Elements.OfType()).Name); } } finally { SqliteConnection.ClearAllPools(); try { File.Delete(path); } catch { /* best-effort cleanup */ } } } private static Scene MakeScene(string sourceName) => MakeScene(SceneCatalog.Starting, sourceName); private static Scene MakeScene(string sceneName, string sourceName) => new() { Name = sceneName, Elements = { new Source { Name = sourceName, Type = SourceType.TextOverlay } }, }; private static Scene MakeScene(string id, string sceneName, string sourceName) => new() { Id = id, Name = sceneName, Elements = { new Source { Name = sourceName, Type = SourceType.TextOverlay } }, }; private static SqliteConnection GetConnection(string path) { var connection = new SqliteConnection($"Data Source={path}"); connection.Open(); return connection; } }