fix(art): re-seed a bundled background when the stored bytes differ

The creator's profile still showed the previous Starting art after the replacement, with
`Art.DefaultVersion = 4` recorded — so the version gate had already declared itself done. It
compared a constant against the embedded resource, and those two were compiled at different
moments (version 4 landed with the OLD image embedded; the corrected image reached the assembly
one build later). A version bump can always be raced that way.

The seed now compares BYTES: `LayoutStore.AssetMatches` hashes the bundled resource and checks the
row the background already points at. Same bytes → leave it untouched (so this is idempotent and
never churns the database). Different bytes → re-stamp. The version constant and its setting are
gone, because a heal that only runs once can silently skip the one launch it was needed for.

`SceneThumbnailTests` planted its own art on a background still flagged as default art — in the
real app that state only ever means stale bundled art, because Browse… sets the path and clears
the flag. The fixture now models a creator's own picture.

Tests: 572 green, 0 warnings. Verified the profile's Starting asset hash (167CDD3B…, the
two-category render) differs from the shipped `starting-background.jpg` (5FC6DD22…), so the next
launch re-seeds it.
This commit is contained in:
2026-11-06 12:21:24 -08:00
parent 3db964e5cf
commit 70e2c48136
5 changed files with 36 additions and 43 deletions
+10 -7
View File
@@ -10,9 +10,10 @@ namespace ytLive.Tests;
/// <summary>
/// 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 — and the first
/// attempt at a version heal silently did nothing because it iterated `AllBackgrounds()`, which returns
/// only the LIVE scene's rows.
/// 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.
/// </summary>
[Collection("RealApp")]
public sealed class BackgroundDefaultArtTests
@@ -35,8 +36,8 @@ public sealed class BackgroundDefaultArtTests
Assert.False(string.IsNullOrWhiteSpace(firstId), "the bundled Starting art never seeded");
var firstBytes = AssetLength(tempDb, firstId!);
// Simulate a profile stamped with the OLD art: point the Starting background at a planted
// asset and mark the layout as an older art vintage. The heal must re-stamp the shipped art.
// 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();
@@ -49,8 +50,6 @@ public sealed class BackgroundDefaultArtTests
plant.ExecuteNonQuery();
}
SqliteConnection.ClearAllPools();
using (var store = new LayoutStore(tempDb))
store.SaveDefaultArtVersion(1);
var second = new MainViewModel();
var secondId = StartingBackgroundId(second);
@@ -64,6 +63,10 @@ public sealed class BackgroundDefaultArtTests
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
{
+5
View File
@@ -263,6 +263,11 @@ public sealed class SceneThumbnailTests
var id = LayoutStore.Instance!.UpsertAsset(bytes, 320, 180);
Assert.NotNull(id);
bg!.AssetId = id;
// The creator's own picture, not the shipped art: a default-art background pointing at
// other bytes is re-seeded by StampDefaultBackgroundAsset (2026-11-06), and this fixture
// needs its planted art to stay put. Browse… always sets the path alongside the asset.
bg.UseDefaultBackground = false;
bg.CustomBackgroundPath = Path.Combine(Path.GetTempPath(), $"ytLive-missing-{i}.jpg");
backgroundIds[scene.Name] = id;
}
builder.SaveLayoutNow();