package extdata import ( "context" "errors" "github.com/navidrome/navidrome/core/agents" "github.com/navidrome/navidrome/model" "github.com/stretchr/testify/mock" ) // --- Shared Mock Implementations --- // mockArtistRepo mocks model.ArtistRepository type mockArtistRepo struct { mock.Mock model.ArtistRepository } func newMockArtistRepo() *mockArtistRepo { return &mockArtistRepo{} } // SetData sets up basic Get expectations. func (m *mockArtistRepo) SetData(artists model.Artists) { for _, a := range artists { artistCopy := a m.On("Get", artistCopy.ID).Return(&artistCopy, nil) } } // Get implements model.ArtistRepository. func (m *mockArtistRepo) Get(id string) (*model.Artist, error) { args := m.Called(id) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*model.Artist), args.Error(1) } // GetAll implements model.ArtistRepository. func (m *mockArtistRepo) GetAll(options ...model.QueryOptions) (model.Artists, error) { argsSlice := make([]interface{}, len(options)) for i, v := range options { argsSlice[i] = v } args := m.Called(argsSlice...) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(model.Artists), args.Error(1) } // SetError is a helper to set up a generic error for GetAll. func (m *mockArtistRepo) SetError(hasError bool) { if hasError { m.On("GetAll", mock.Anything).Return(nil, errors.New("mock repo error")) } } // FindByName is a helper to set up a GetAll expectation for finding by name. func (m *mockArtistRepo) FindByName(name string, artist model.Artist) { m.On("GetAll", mock.MatchedBy(func(opt model.QueryOptions) bool { return opt.Filters != nil })).Return(model.Artists{artist}, nil).Once() } // mockMediaFileRepo mocks model.MediaFileRepository type mockMediaFileRepo struct { mock.Mock model.MediaFileRepository } func newMockMediaFileRepo() *mockMediaFileRepo { return &mockMediaFileRepo{} } // SetData sets up basic Get expectations. func (m *mockMediaFileRepo) SetData(mediaFiles model.MediaFiles) { for _, mf := range mediaFiles { mfCopy := mf m.On("Get", mfCopy.ID).Return(&mfCopy, nil) } } // Get implements model.MediaFileRepository. func (m *mockMediaFileRepo) Get(id string) (*model.MediaFile, error) { args := m.Called(id) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*model.MediaFile), args.Error(1) } // GetAll implements model.MediaFileRepository. func (m *mockMediaFileRepo) GetAll(options ...model.QueryOptions) (model.MediaFiles, error) { argsSlice := make([]interface{}, len(options)) for i, v := range options { argsSlice[i] = v } args := m.Called(argsSlice...) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(model.MediaFiles), args.Error(1) } // SetError is a helper to set up a generic error for GetAll. func (m *mockMediaFileRepo) SetError(hasError bool) { if hasError { m.On("GetAll", mock.Anything).Return(nil, errors.New("mock repo error")) } } // FindByMBID is a helper to set up a GetAll expectation for finding by MBID. func (m *mockMediaFileRepo) FindByMBID(mbid string, mediaFile model.MediaFile) { m.On("GetAll", mock.MatchedBy(func(opt model.QueryOptions) bool { return opt.Filters != nil })).Return(model.MediaFiles{mediaFile}, nil).Once() } // FindByArtistAndTitle is a helper to set up a GetAll expectation for finding by artist/title. func (m *mockMediaFileRepo) FindByArtistAndTitle(artistID string, title string, mediaFile model.MediaFile) { m.On("GetAll", mock.MatchedBy(func(opt model.QueryOptions) bool { return opt.Filters != nil })).Return(model.MediaFiles{mediaFile}, nil).Once() } // mockSimilarArtistAgent mocks agents implementing ArtistTopSongsRetriever and ArtistSimilarRetriever type mockSimilarArtistAgent struct { mock.Mock agents.Interface // Embed to satisfy methods not explicitly mocked } func (m *mockSimilarArtistAgent) AgentName() string { return "mockSimilar" } func (m *mockSimilarArtistAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) { args := m.Called(ctx, id, artistName, mbid, count) if args.Get(0) != nil { return args.Get(0).([]agents.Song), args.Error(1) } return nil, args.Error(1) } func (m *mockSimilarArtistAgent) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) { args := m.Called(ctx, id, name, mbid, limit) if args.Get(0) != nil { return args.Get(0).([]agents.Artist), args.Error(1) } return nil, args.Error(1) } // mockCombinedAgents mocks the main Agents interface used by Provider type mockCombinedAgents struct { topSongsAgent agents.ArtistTopSongsRetriever similarAgent agents.ArtistSimilarRetriever agents.Interface // Embed to satisfy non-overridden methods } func (m *mockCombinedAgents) AgentName() string { return "mockCombined" } func (m *mockCombinedAgents) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) { if m.similarAgent != nil { return m.similarAgent.GetSimilarArtists(ctx, id, name, mbid, limit) } return nil, agents.ErrNotFound } func (m *mockCombinedAgents) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) { if m.topSongsAgent != nil { return m.topSongsAgent.GetArtistTopSongs(ctx, id, artistName, mbid, count) } return nil, agents.ErrNotFound } // --- Stubs for other Agents interface methods --- func (m *mockCombinedAgents) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*agents.AlbumInfo, error) { if m.topSongsAgent != nil { if ar, ok := m.topSongsAgent.(agents.AlbumInfoRetriever); ok { return ar.GetAlbumInfo(ctx, name, artist, mbid) } } return nil, agents.ErrNotFound } func (m *mockCombinedAgents) GetArtistMBID(ctx context.Context, id string, name string) (string, error) { if m.topSongsAgent != nil { if ar, ok := m.topSongsAgent.(agents.ArtistMBIDRetriever); ok { return ar.GetArtistMBID(ctx, id, name) } } if m.similarAgent != nil { if ar, ok := m.similarAgent.(agents.ArtistMBIDRetriever); ok { return ar.GetArtistMBID(ctx, id, name) } } return "", agents.ErrNotFound } func (m *mockCombinedAgents) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) { if m.topSongsAgent != nil { if ar, ok := m.topSongsAgent.(agents.ArtistURLRetriever); ok { return ar.GetArtistURL(ctx, id, name, mbid) } } if m.similarAgent != nil { if ar, ok := m.similarAgent.(agents.ArtistURLRetriever); ok { return ar.GetArtistURL(ctx, id, name, mbid) } } return "", agents.ErrNotFound } func (m *mockCombinedAgents) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) { if m.topSongsAgent != nil { if ar, ok := m.topSongsAgent.(agents.ArtistBiographyRetriever); ok { return ar.GetArtistBiography(ctx, id, name, mbid) } } if m.similarAgent != nil { if ar, ok := m.similarAgent.(agents.ArtistBiographyRetriever); ok { return ar.GetArtistBiography(ctx, id, name, mbid) } } return "", agents.ErrNotFound } func (m *mockCombinedAgents) GetArtistImages(ctx context.Context, id, name, mbid string) ([]agents.ExternalImage, error) { if m.topSongsAgent != nil { if ar, ok := m.topSongsAgent.(agents.ArtistImageRetriever); ok { return ar.GetArtistImages(ctx, id, name, mbid) } } if m.similarAgent != nil { if ar, ok := m.similarAgent.(agents.ArtistImageRetriever); ok { return ar.GetArtistImages(ctx, id, name, mbid) } } return nil, agents.ErrNotFound }