package pluginsimport ("github.com/navidrome/navidrome/core/sonic""github.com/navidrome/navidrome/model""github.com/navidrome/navidrome/plugins/capabilities". "github.com/onsi/ginkgo/v2". "github.com/onsi/gomega")var _ = Describe("SonicSimilarityPlugin", Ordered, func() {var (manager *Managerprovider sonic.Provider)BeforeAll(func() {manager, _ = createTestManagerWithPlugins(nil, "test-sonic-similarity"+PackageExtension)var ok boolprovider, ok = manager.LoadSonicSimilarity("test-sonic-similarity")Expect(ok).To(BeTrue())})Describe("PluginNames", func() {It("reports the sonic similarity capability", func() {names := manager.PluginNames(string(CapabilitySonicSimilarity))Expect(names).To(ContainElement("test-sonic-similarity"))})})Describe("GetSonicSimilarTracks", func() {It("returns similar tracks from the plugin", func() {mf := &model.MediaFile{ID: "track-1",Title: "Yesterday",Artist: "The Beatles",}results, err := provider.GetSonicSimilarTracks(GinkgoT().Context(), mf, 3)Expect(err).ToNot(HaveOccurred())Expect(results).To(HaveLen(3))Expect(results[0].Song.Name).To(Equal("Similar to Yesterday #1"))Expect(results[0].Song.Artists).To(HaveLen(1))Expect(results[0].Song.Artists[0].Name).To(Equal("The Beatles"))Expect(results[0].Similarity).To(Equal(1.0))Expect(results[1].Similarity).To(Equal(0.9))Expect(results[2].Similarity).To(Equal(0.8))})})Describe("FindSonicPath", func() {It("returns a path between two tracks from the plugin", func() {startMf := &model.MediaFile{ID: "track-1",Title: "Yesterday",Artist: "The Beatles",}endMf := &model.MediaFile{ID: "track-2",Title: "Tomorrow Never Knows",Artist: "The Beatles",}results, err := provider.FindSonicPath(GinkgoT().Context(), startMf, endMf, 3)Expect(err).ToNot(HaveOccurred())Expect(results).To(HaveLen(3))Expect(results[0].Song.Name).To(Equal("Path Yesterday to Tomorrow Never Knows #1"))Expect(results[0].Song.Artists).To(HaveLen(1))Expect(results[0].Song.Artists[0].Name).To(Equal("The Beatles"))Expect(results[0].Similarity).To(Equal(1.0))Expect(results[1].Similarity).To(Equal(0.95))Expect(results[2].Similarity).To(Equal(0.9))})})})var _ = Describe("SonicSimilarityPlugin error handling", Ordered, func() {var (errorManager *ManagererrorProvider sonic.Provider)BeforeAll(func() {errorManager, _ = createTestManagerWithPlugins(map[string]map[string]string{"test-sonic-similarity": {"error": "simulated plugin error",},}, "test-sonic-similarity"+PackageExtension)var ok boolerrorProvider, ok = errorManager.LoadSonicSimilarity("test-sonic-similarity")Expect(ok).To(BeTrue())})It("returns error from GetSonicSimilarTracks", func() {mf := &model.MediaFile{ID: "track-1", Title: "Test"}_, err := errorProvider.GetSonicSimilarTracks(GinkgoT().Context(), mf, 3)Expect(err).To(HaveOccurred())Expect(err.Error()).To(ContainSubstring("simulated plugin error"))})It("returns error from FindSonicPath", func() {startMf := &model.MediaFile{ID: "track-1", Title: "Start"}endMf := &model.MediaFile{ID: "track-2", Title: "End"}_, err := errorProvider.FindSonicPath(GinkgoT().Context(), startMf, endMf, 3)Expect(err).To(HaveOccurred())Expect(err.Error()).To(ContainSubstring("simulated plugin error"))})})var _ = Describe("mediaFileToSongRef multi-artist", func() {It("fills Artists (with IDs) from role=artist participants", func() {mf := &model.MediaFile{ID: "x", Title: "Collab", Participants: model.Participants{model.RoleArtist: model.ParticipantList{{Artist: model.Artist{ID: "ar-drake", Name: "Drake", MbzArtistID: "m-drake"}},{Artist: model.Artist{ID: "ar-future", Name: "Future", MbzArtistID: "m-future"}},},}}ref := mediaFileToSongRef(mf)Expect(ref.Artists).To(Equal([]capabilities.ArtistRef{{ID: "ar-drake", Name: "Drake", MBID: "m-drake", Role: "artist"},{ID: "ar-future", Name: "Future", MBID: "m-future", Role: "artist"},}))})It("leaves Artists nil when the track has no role=artist participants", func() {mf := &model.MediaFile{ID: "x", Title: "Solo", Artist: "Drake"}ref := mediaFileToSongRef(mf)Expect(ref.Artists).To(BeNil())})})