Files
LlamaCasty/ytLive.Tests/LayoutStoreSnapshotTests.cs
T
gramps c4da06b56b feat(37): reset one scene to its factory snapshot
The per-scene reset restores that scene's settings, layers, webcam placement,
and background-path settings while leaving other scenes' current edits alone.
A focused integration test covers the restore/isolation behavior.
611 tests green, clean build with 0 warnings.

Reference: OBS scene collections store scenes and sources as a saved set:
https://obsproject.com/kb/scene-collections
2026-11-10 17:52:20 -08:00

144 lines
5.2 KiB
C#

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<Source>()).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<Source>()).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<Source>()).Name);
Assert.Equal("Edited BRB", Assert.Single(scenes.Single(s => s.Id == brb.Id).Elements.OfType<Source>()).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;
}
}