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"),