using System;
using System.IO;
using System.Linq;
using Microsoft.Data.Sqlite;
using Xunit;
using ytLive.Services;
using ytLive.ViewModels;
namespace ytLive.Tests;
///
/// 2026-11-06: replacing a bundled background reached new installs only. Default art is hash-keyed into the
/// layout database on first seed, so an existing profile kept the old image for ever. A version-stamp heal
/// failed twice (first it iterated `AllBackgrounds()`, which returns only the LIVE scene's rows; then the
/// constant and the embedded resource were built at different moments). The seed now compares BYTES, which
/// is what this test pins: a profile already pointing at other art is re-stamped without any version bump.
///
[Collection("RealApp")]
public sealed class BackgroundDefaultArtTests
{
private readonly RealAppHost _app;
public BackgroundDefaultArtTests(RealAppHost app) => _app = app;
[Fact]
public void ReplacingTheBundledArt_ReSeedsAnExistingProfile() => _app.Run(() =>
{
// The bundled art is a WPF resource, so this needs a real Application to resolve the pack URI.
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-art-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = tempDb;
try
{
// First launch: the seeded art is whatever ships today.
var first = new MainViewModel();
var firstId = StartingBackgroundId(first);
Assert.False(string.IsNullOrWhiteSpace(firstId), "the bundled Starting art never seeded");
var firstBytes = AssetLength(tempDb, firstId!);
// Simulate a profile stamped with the OLD art. No version flag is touched — the seed has to
// notice the bytes are not the shipped ones by itself.
using (var connection = new SqliteConnection($"Data Source={tempDb}"))
{
connection.Open();
using var plant = connection.CreateCommand();
plant.CommandText = """
INSERT OR REPLACE INTO Asset (Id, Hash, Data, PixelWidth, PixelHeight)
VALUES ('old-art', 'deadbeef', zeroblob(1024), 1920, 1080);
UPDATE Source SET AssetId = 'old-art' WHERE IsBackground = 1;
""";
plant.ExecuteNonQuery();
}
SqliteConnection.ClearAllPools();
var second = new MainViewModel();
var secondId = StartingBackgroundId(second);
Assert.NotNull(secondId);
// Re-stamped to the SHIPPED art — which, being the same bytes, is the same asset row the
// first launch created. What matters is that the planted "old art" is gone.
Assert.NotEqual("old-art", secondId);
Assert.Equal(firstId, secondId);
Assert.Equal(File.ReadAllBytes(Path.Combine(
AppContext.BaseDirectory, "..", "..", "..", "..", "Assets", "starting-background.jpg")).Length,
AssetLength(tempDb, secondId!));
Assert.NotEqual(firstBytes, 0);
// Idempotent: a third launch finds the shipped bytes already in place and must not re-seed.
var third = new MainViewModel();
Assert.Equal(secondId, StartingBackgroundId(third));
}
finally
{
MainViewModel.LayoutPathOverride = null;
SqliteConnection.ClearAllPools();
try { File.Delete(tempDb); } catch { /* best-effort cleanup */ }
}
});
private static string? StartingBackgroundId(MainViewModel vm)
=> vm.Scenes
.Where(scene => ytLive.Models.SceneCatalog.Is(scene.Name, ytLive.Models.SceneCatalog.Starting))
.SelectMany(scene => scene.Elements.OfType())
.FirstOrDefault(element => element.IsBackground)?.AssetId;
private static int AssetLength(string dbPath, string assetId)
{
using var connection = new SqliteConnection($"Data Source={dbPath}");
connection.Open();
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT length(Data) FROM Asset WHERE Id = $id;";
cmd.Parameters.AddWithValue("$id", assetId);
return Convert.ToInt32(cmd.ExecuteScalar());
}
}