mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
* refactor: extract matchSongsToLibrary to core/matcher package Move the song-to-library matching algorithm from core/external into its own core/matcher package. The Matcher struct exposes a single public method MatchSongsToLibrary that implements a multi-phase matching algorithm (ID > MBID > ISRC > fuzzy title+artist). Includes pre-sanitization optimization for the fuzzy matching loop. No behavioral changes — the algorithm is identical to the version in core/external/provider_matching.go. * refactor: inject matcher.Matcher via Wire instead of creating it inline Add *matcher.Matcher as a dependency of external.NewProvider, wired via Google Wire. Update all provider test files to pass matcher.New(ds). This eliminates tight coupling so future consumers can reuse the matcher without depending on the external package. * refactor: remove old provider_matching files Delete core/external/provider_matching.go and its tests. All matching logic now lives in core/matcher/. * test(matcher): restore test coverage lost in extraction Port back 23 specs that existed in the old provider_matching_test.go but were dropped during the extraction. Covers specificity levels, fuzzy matching thresholds, fuzzy album matching, duration matching, and deduplication edge cases. * test(matcher): extract matchFieldInAnd/matchFieldInEq helpers The four inline mock.MatchedBy closures in setupAllPhaseExpectations all followed the same squirrel.And -> squirrel.Eq -> field-name-check pattern. Extract into two small helpers to reduce duplication and make the setup functions read as a concise list of phase expectations. * refactor(matcher): address PR #5348 review feedback - sanitizedTrack now holds *model.MediaFile instead of a value copy. Since MediaFile is a large struct (~74 fields), this avoids the per-track copy into sanitized[] and a second copy when findBestMatch assigns the winner. loadTracksByTitleAndArtist updated to iterate by index and pass &tracks[i]. - loadTracksByISRC now sorts results (starred desc, rating desc, year asc, compilation asc) so that when multiple library tracks share an ISRC the most relevant one is picked deterministically, matching the sort order already used by loadTracksByTitleAndArtist. - Restored the four worked examples (MBID Priority, ISRC Priority, Specificity Ranking, Fuzzy Title Matching) in the MatchSongsToLibrary godoc that were dropped during the extraction. - matcher_test.go: tests now enforce expectations via AssertExpectations in a DeferCleanup. The old setupAllPhaseExpectations helper was replaced with per-phase helpers (expectIDPhase/expectMBIDPhase/expectISRCPhase + allowOtherPhases) so each test deterministically verifies which matching phases fire. This surfaced (and fixes) a latent issue copilot flagged: the old .Once() expectations were not actually asserted, so tests would silently pass even when phases short-circuited unexpectedly.
429 lines
16 KiB
Go
429 lines
16 KiB
Go
package external_test
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"errors"
|
||
"net/url"
|
||
"time"
|
||
|
||
"github.com/navidrome/navidrome/conf"
|
||
"github.com/navidrome/navidrome/conf/configtest"
|
||
"github.com/navidrome/navidrome/core/agents"
|
||
. "github.com/navidrome/navidrome/core/external"
|
||
"github.com/navidrome/navidrome/core/matcher"
|
||
"github.com/navidrome/navidrome/log"
|
||
"github.com/navidrome/navidrome/model"
|
||
"github.com/navidrome/navidrome/tests"
|
||
. "github.com/onsi/ginkgo/v2"
|
||
. "github.com/onsi/gomega"
|
||
"github.com/stretchr/testify/mock"
|
||
)
|
||
|
||
var _ = Describe("Provider - ArtistImage", func() {
|
||
var ds *tests.MockDataStore
|
||
var provider Provider
|
||
var mockArtistRepo *mockArtistRepo
|
||
var mockAlbumRepo *mockAlbumRepo
|
||
var mockMediaFileRepo *mockMediaFileRepo
|
||
var mockImageAgent *mockArtistImageAgent
|
||
var agentsCombined *mockAgents
|
||
var ctx context.Context
|
||
|
||
BeforeEach(func() {
|
||
DeferCleanup(configtest.SetupConfig())
|
||
conf.Server.Agents = "mockImage" // Configure only the mock agent
|
||
ctx = GinkgoT().Context()
|
||
|
||
mockArtistRepo = newMockArtistRepo()
|
||
mockAlbumRepo = newMockAlbumRepo()
|
||
mockMediaFileRepo = newMockMediaFileRepo()
|
||
|
||
ds = &tests.MockDataStore{
|
||
MockedArtist: mockArtistRepo,
|
||
MockedAlbum: mockAlbumRepo,
|
||
MockedMediaFile: mockMediaFileRepo,
|
||
}
|
||
|
||
mockImageAgent = newMockArtistImageAgent()
|
||
|
||
// Use the mockAgents from helper, setting the specific agent
|
||
agentsCombined = &mockAgents{
|
||
imageAgent: mockImageAgent,
|
||
}
|
||
|
||
provider = NewProvider(ds, agentsCombined, matcher.New(ds))
|
||
|
||
// Default mocks for successful Get calls
|
||
mockArtistRepo.On("Get", "artist-1").Return(&model.Artist{ID: "artist-1", Name: "Artist One"}, nil).Maybe()
|
||
mockAlbumRepo.On("Get", "album-1").Return(&model.Album{ID: "album-1", Name: "Album One", AlbumArtistID: "artist-1"}, nil).Maybe()
|
||
mockMediaFileRepo.On("Get", "mf-1").Return(&model.MediaFile{ID: "mf-1", Title: "Track One", ArtistID: "artist-1"}, nil).Maybe()
|
||
// Default mock for non-existent entities
|
||
mockArtistRepo.On("Get", "not-found").Return(nil, model.ErrNotFound).Maybe()
|
||
mockAlbumRepo.On("Get", "not-found").Return(nil, model.ErrNotFound).Maybe()
|
||
mockMediaFileRepo.On("Get", "not-found").Return(nil, model.ErrNotFound).Maybe()
|
||
|
||
// Default successful image agent response
|
||
mockImageAgent.On("GetArtistImages", mock.Anything, "artist-1", "Artist One", "").
|
||
Return([]agents.ExternalImage{
|
||
{URL: "http://example.com/large.jpg", Size: 1000},
|
||
{URL: "http://example.com/medium.jpg", Size: 500},
|
||
{URL: "http://example.com/small.jpg", Size: 200},
|
||
}, nil).Maybe()
|
||
})
|
||
|
||
AfterEach(func() {
|
||
mockArtistRepo.AssertExpectations(GinkgoT())
|
||
mockAlbumRepo.AssertExpectations(GinkgoT())
|
||
mockMediaFileRepo.AssertExpectations(GinkgoT())
|
||
mockImageAgent.AssertExpectations(GinkgoT())
|
||
})
|
||
|
||
It("returns the largest image URL when successful", func() {
|
||
// Arrange
|
||
expectedURL, _ := url.Parse("http://example.com/large.jpg")
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns ErrNotFound if the artist is not found in the DB", func() {
|
||
// Arrange
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "not-found")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(model.ErrNotFound))
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "not-found")
|
||
mockImageAgent.AssertNotCalled(GinkgoT(), "GetArtistImages", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
|
||
})
|
||
|
||
It("returns the agent error if the agent fails", func() {
|
||
// Arrange
|
||
agentErr := errors.New("agent failure")
|
||
mockImageAgent.Mock = mock.Mock{} // Reset default expectation
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-1", "Artist One", "").Return(nil, agentErr).Once()
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(model.ErrNotFound)) // Corrected Expectation: The provider maps agent errors (other than canceled) to ErrNotFound if no image was found/populated
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns ErrNotFound if the agent returns ErrNotFound", func() {
|
||
// Arrange
|
||
mockImageAgent.Mock = mock.Mock{} // Reset default expectation
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-1", "Artist One", "").Return(nil, agents.ErrNotFound).Once()
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(model.ErrNotFound))
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns ErrNotFound if the agent returns no images", func() {
|
||
// Arrange
|
||
mockImageAgent.Mock = mock.Mock{} // Reset default expectation
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-1", "Artist One", "").Return([]agents.ExternalImage{}, nil).Once()
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(model.ErrNotFound)) // Implementation maps empty result to ErrNotFound
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns context error if context is canceled before agent call", func() {
|
||
// Arrange
|
||
cctx, cancelCtx := context.WithCancel(context.Background())
|
||
mockArtistRepo.Mock = mock.Mock{} // Reset default expectation for artist repo as well
|
||
mockArtistRepo.On("Get", "artist-1").Return(&model.Artist{ID: "artist-1", Name: "Artist One"}, nil).Run(func(args mock.Arguments) {
|
||
cancelCtx() // Cancel context *during* the DB call simulation
|
||
}).Once()
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(cctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(context.Canceled))
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
})
|
||
|
||
It("derives artist ID from MediaFile ID", func() {
|
||
// Arrange: Add mocks for the initial GetEntityByID lookups
|
||
mockArtistRepo.On("Get", "mf-1").Return(nil, model.ErrNotFound).Once()
|
||
mockAlbumRepo.On("Get", "mf-1").Return(nil, model.ErrNotFound).Once()
|
||
// Default mocks for MediaFileRepo.Get("mf-1") and ArtistRepo.Get("artist-1") handle the rest
|
||
expectedURL, _ := url.Parse("http://example.com/large.jpg")
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "mf-1")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "mf-1") // GetEntityByID sequence
|
||
mockAlbumRepo.AssertCalled(GinkgoT(), "Get", "mf-1") // GetEntityByID sequence
|
||
mockMediaFileRepo.AssertCalled(GinkgoT(), "Get", "mf-1")
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1") // Should be called after getting MF
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("derives artist ID from Album ID", func() {
|
||
// Arrange: Add mock for the initial GetEntityByID lookup
|
||
mockArtistRepo.On("Get", "album-1").Return(nil, model.ErrNotFound).Once()
|
||
// Default mocks for AlbumRepo.Get("album-1") and ArtistRepo.Get("artist-1") handle the rest
|
||
expectedURL, _ := url.Parse("http://example.com/large.jpg")
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "album-1")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "album-1") // GetEntityByID sequence
|
||
mockAlbumRepo.AssertCalled(GinkgoT(), "Get", "album-1")
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1") // Should be called after getting Album
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns ErrNotFound if derived artist is not found", func() {
|
||
// Arrange
|
||
// Add mocks for the initial GetEntityByID lookups
|
||
mockArtistRepo.On("Get", "mf-bad-artist").Return(nil, model.ErrNotFound).Once()
|
||
mockAlbumRepo.On("Get", "mf-bad-artist").Return(nil, model.ErrNotFound).Once()
|
||
mockMediaFileRepo.On("Get", "mf-bad-artist").Return(&model.MediaFile{ID: "mf-bad-artist", ArtistID: "not-found"}, nil).Once()
|
||
// Add expectation for the recursive GetEntityByID call for the MediaFileRepo
|
||
mockMediaFileRepo.On("Get", "not-found").Return(nil, model.ErrNotFound).Maybe()
|
||
// The default mocks for ArtistRepo/AlbumRepo handle the final "not-found" lookups
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "mf-bad-artist")
|
||
|
||
// Assert
|
||
Expect(err).To(MatchError(model.ErrNotFound))
|
||
Expect(imgURL).To(BeNil())
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "mf-bad-artist") // GetEntityByID sequence
|
||
mockAlbumRepo.AssertCalled(GinkgoT(), "Get", "mf-bad-artist") // GetEntityByID sequence
|
||
mockMediaFileRepo.AssertCalled(GinkgoT(), "Get", "mf-bad-artist")
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "not-found")
|
||
mockImageAgent.AssertNotCalled(GinkgoT(), "GetArtistImages", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
|
||
})
|
||
|
||
It("handles different image orders from agent", func() {
|
||
// Arrange
|
||
mockImageAgent.Mock = mock.Mock{} // Reset default expectation
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-1", "Artist One", "").
|
||
Return([]agents.ExternalImage{
|
||
{URL: "http://example.com/small.jpg", Size: 200},
|
||
{URL: "http://example.com/large.jpg", Size: 1000},
|
||
{URL: "http://example.com/medium.jpg", Size: 500},
|
||
}, nil).Once()
|
||
expectedURL, _ := url.Parse("http://example.com/large.jpg")
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL)) // Still picks the largest
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("handles agent returning only one image", func() {
|
||
// Arrange
|
||
mockImageAgent.Mock = mock.Mock{} // Reset default expectation
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-1", "Artist One", "").
|
||
Return([]agents.ExternalImage{
|
||
{URL: "http://example.com/medium.jpg", Size: 500},
|
||
}, nil).Once()
|
||
expectedURL, _ := url.Parse("http://example.com/medium.jpg")
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-1")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-1")
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-1", "Artist One", "")
|
||
})
|
||
|
||
It("returns cached URL and does not call agent when info is not expired", func() {
|
||
// Arrange: artist has a cached image URL with recent ExternalInfoUpdatedAt
|
||
recentTime := time.Now().Add(-1 * time.Minute)
|
||
cachedArtist := &model.Artist{
|
||
ID: "artist-cached",
|
||
Name: "Cached Artist",
|
||
LargeImageUrl: "http://example.com/cached-large.jpg",
|
||
ExternalInfoUpdatedAt: &recentTime,
|
||
}
|
||
mockArtistRepo.On("Get", "artist-cached").Return(cachedArtist, nil).Maybe()
|
||
expectedURL, _ := url.Parse("http://example.com/cached-large.jpg")
|
||
|
||
// Capture log output
|
||
var logBuf bytes.Buffer
|
||
log.SetOutput(&logBuf)
|
||
defer log.SetOutput(GinkgoWriter)
|
||
log.SetLevel(log.LevelDebug)
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-cached")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockImageAgent.AssertNotCalled(GinkgoT(), "GetArtistImages", mock.Anything, "artist-cached", mock.Anything, mock.Anything)
|
||
|
||
// Assert: background refresh was NOT enqueued
|
||
Expect(logBuf.String()).ToNot(ContainSubstring("Artist image info expired, enqueuing background refresh"))
|
||
|
||
})
|
||
|
||
It("returns stale URL and enqueues refresh when info is expired", func() {
|
||
// Arrange
|
||
conf.Server.DevArtistInfoTimeToLive = 1 * time.Nanosecond
|
||
expiredTime := time.Now().Add(-1 * time.Hour)
|
||
staleArtist := &model.Artist{
|
||
ID: "artist-expired",
|
||
Name: "Expired Artist",
|
||
LargeImageUrl: "http://example.com/expired-large.jpg",
|
||
ExternalInfoUpdatedAt: &expiredTime,
|
||
}
|
||
mockArtistRepo.On("Get", "artist-expired").Return(staleArtist, nil).Maybe()
|
||
expectedURL, _ := url.Parse("http://example.com/expired-large.jpg")
|
||
|
||
// Capture log output
|
||
var logBuf bytes.Buffer
|
||
log.SetOutput(&logBuf)
|
||
defer log.SetOutput(GinkgoWriter)
|
||
log.SetLevel(log.LevelDebug)
|
||
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-expired")
|
||
|
||
// Assert: returns stale URL immediately, no agent call
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockImageAgent.AssertNotCalled(GinkgoT(), "GetArtistImages", mock.Anything, "artist-expired", mock.Anything, mock.Anything)
|
||
|
||
// Assert: background refresh was enqueued
|
||
Expect(logBuf.String()).To(ContainSubstring("Artist image info expired, enqueuing background refresh"))
|
||
})
|
||
|
||
Context("Unicode handling in artist names", func() {
|
||
var artistWithEnDash *model.Artist
|
||
var expectedURL *url.URL
|
||
|
||
const (
|
||
originalArtistName = "Run–D.M.C." // Artist name with en dash
|
||
normalizedArtistName = "Run-D.M.C." // Normalized version with hyphen
|
||
)
|
||
|
||
BeforeEach(func() {
|
||
// Test with en dash (–) in artist name like "Run–D.M.C."
|
||
artistWithEnDash = &model.Artist{ID: "artist-endash", Name: originalArtistName}
|
||
mockArtistRepo.Mock = mock.Mock{} // Reset default expectations
|
||
mockArtistRepo.On("Get", "artist-endash").Return(artistWithEnDash, nil).Once()
|
||
|
||
expectedURL, _ = url.Parse("http://example.com/rundmc.jpg")
|
||
|
||
// Mock the image agent to return an image for the artist
|
||
mockImageAgent.On("GetArtistImages", ctx, "artist-endash", mock.AnythingOfType("string"), "").
|
||
Return([]agents.ExternalImage{
|
||
{URL: "http://example.com/rundmc.jpg", Size: 1000},
|
||
}, nil).Once()
|
||
|
||
})
|
||
|
||
When("DevPreserveUnicodeInExternalCalls is true", func() {
|
||
BeforeEach(func() {
|
||
conf.Server.DevPreserveUnicodeInExternalCalls = true
|
||
})
|
||
It("preserves Unicode characters in artist names", func() {
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-endash")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-endash")
|
||
// This is the key assertion: ensure the original Unicode name is used
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-endash", originalArtistName, "")
|
||
})
|
||
})
|
||
|
||
When("DevPreserveUnicodeInExternalCalls is false", func() {
|
||
BeforeEach(func() {
|
||
conf.Server.DevPreserveUnicodeInExternalCalls = false
|
||
})
|
||
|
||
It("normalizes Unicode characters", func() {
|
||
// Act
|
||
imgURL, err := provider.ArtistImage(ctx, "artist-endash")
|
||
|
||
// Assert
|
||
Expect(err).ToNot(HaveOccurred())
|
||
Expect(imgURL).To(Equal(expectedURL))
|
||
mockArtistRepo.AssertCalled(GinkgoT(), "Get", "artist-endash")
|
||
// This assertion ensures the normalized name is used (en dash → hyphen)
|
||
mockImageAgent.AssertCalled(GinkgoT(), "GetArtistImages", ctx, "artist-endash", normalizedArtistName, "")
|
||
})
|
||
})
|
||
})
|
||
})
|
||
|
||
// mockArtistImageAgent implementation using testify/mock
|
||
// This remains local as it's specific to testing the ArtistImage functionality
|
||
type mockArtistImageAgent struct {
|
||
mock.Mock
|
||
agents.ArtistImageRetriever // Embed interface
|
||
}
|
||
|
||
// Constructor for the mock agent
|
||
func newMockArtistImageAgent() *mockArtistImageAgent {
|
||
mock := new(mockArtistImageAgent)
|
||
// Set default AgentName if needed, although usually called via mockAgents
|
||
mock.On("AgentName").Return("mockImage").Maybe()
|
||
return mock
|
||
}
|
||
|
||
func (m *mockArtistImageAgent) AgentName() string {
|
||
args := m.Called()
|
||
return args.String(0)
|
||
}
|
||
|
||
func (m *mockArtistImageAgent) GetArtistImages(ctx context.Context, id, artistName, mbid string) ([]agents.ExternalImage, error) {
|
||
args := m.Called(ctx, id, artistName, mbid)
|
||
// Need careful type assertion for potentially nil slice
|
||
var res []agents.ExternalImage
|
||
if args.Get(0) != nil {
|
||
res = args.Get(0).([]agents.ExternalImage)
|
||
}
|
||
return res, args.Error(1)
|
||
}
|
||
|
||
// Ensure mockAgent implements the interface
|
||
var _ agents.ArtistImageRetriever = (*mockArtistImageAgent)(nil)
|