From b63c9b095b9c48654796ee669583a228bdd27b9c Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 17 Jul 2026 20:36:38 -0400 Subject: [PATCH] test(artwork): cover playlist placeholder-clear via the tee A playlist that loses its sidecar cover serves the bundled placeholder through Get; those bytes flow through the tee, the runner recognizes the placeholder, and clears the stored hash. This exercises the free deletion path (no GetOrPlaceholder needed, since the playlist reader chain ends in fromAlbumPlaceholder). --- core/artwork/e2e/blurhash_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/core/artwork/e2e/blurhash_test.go b/core/artwork/e2e/blurhash_test.go index 9c834a15c..f34c09c15 100644 --- a/core/artwork/e2e/blurhash_test.go +++ b/core/artwork/e2e/blurhash_test.go @@ -1,9 +1,12 @@ package artworke2e_test import ( + "os" + "path/filepath" "testing/fstest" "time" + "github.com/navidrome/navidrome/model" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -155,6 +158,34 @@ var _ = Describe("BlurHash", func() { }, "10s", "100ms").Should(Succeed()) }) + It("clears a stored playlist hash when it falls back to the placeholder", func() { + // A playlist with a sidecar cover gets a real hash; removing the sidecar makes the reader chain + // fall through to fromAlbumPlaceholder(), whose bytes flow through the tee on Get and clear it. + dir := GinkgoT().TempDir() + m3uPath := filepath.Join(dir, "MyList.m3u") + Expect(os.WriteFile(m3uPath, []byte("#EXTM3U\n"), 0600)).To(Succeed()) + sidecar := filepath.Join(dir, "MyList.png") + Expect(os.WriteFile(sidecar, realPNG("pl-cover").Data, 0600)).To(Succeed()) + + pl := putPlaylist(model.Playlist{ID: "pl-blur", Name: "MyList", Path: m3uPath}) + readArtwork(pl.CoverArtID()) + Eventually(func(g Gomega) { + updated, err := ds.Playlist(ctx).Get(pl.ID) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(updated.BlurHash).ToNot(BeEmpty()) + }, "10s", "100ms").Should(Succeed()) + + // Remove the sidecar: the serve now falls through to the placeholder, captured by the tee. + Expect(os.Remove(sidecar)).To(Succeed()) + Expect(readArtwork(pl.CoverArtID())).To(Equal(placeholderBytes())) + + Eventually(func(g Gomega) { + updated, err := ds.Playlist(ctx).Get(pl.ID) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(updated.BlurHash).To(BeEmpty()) + }, "10s", "100ms").Should(Succeed()) + }) + It("does not persist a blurhash when the served image cannot be decoded", func() { setLayout(fstest.MapFS{ "Artist/Album/01 - Song.mp3": trackFile(1, "Song"),