using System.Collections.Generic; using FizzWare.NBuilder; using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; using NzbDrone.Core.Exceptions; using NzbDrone.Core.History; using NzbDrone.Core.ImportLists.Exclusions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Music; using NzbDrone.Core.Music.Commands; using NzbDrone.Core.Music.Events; using NzbDrone.Core.RootFolders; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.MusicTests { [TestFixture] public class RefreshArtistServiceFixture : CoreTest { private Artist _artist; private Album _album1; private Album _album2; private List _albums; private List _remoteAlbums; [SetUp] public void Setup() { _album1 = Builder.CreateNew() .With(s => s.ForeignAlbumId = "1") .Build(); _album2 = Builder.CreateNew() .With(s => s.ForeignAlbumId = "2") .Build(); _albums = new List { _album1, _album2 }; _remoteAlbums = _albums.JsonClone(); _remoteAlbums.ForEach(x => x.Id = 0); var metadata = Builder.CreateNew().Build(); _artist = Builder.CreateNew() .With(a => a.Metadata = metadata) .Build(); Mocker.GetMock(MockBehavior.Strict) .Setup(s => s.GetArtists(new List { _artist.Id })) .Returns(new List { _artist }); Mocker.GetMock(MockBehavior.Strict) .Setup(s => s.InsertMany(It.IsAny>())); Mocker.GetMock() .Setup(s => s.GetArtistInfo(It.IsAny(), It.IsAny())) .Callback(() => { throw new ArtistNotFoundException(_artist.ForeignArtistId); }); Mocker.GetMock() .Setup(x => x.GetFilesByArtist(It.IsAny())) .Returns(new List()); Mocker.GetMock() .Setup(x => x.GetByArtist(It.IsAny(), It.IsAny())) .Returns(new List()); Mocker.GetMock() .Setup(x => x.FindByForeignId(It.IsAny>())) .Returns(new List()); Mocker.GetMock() .Setup(x => x.All()) .Returns(new List()); Mocker.GetMock() .Setup(x => x.ShouldMonitorNewAlbum(It.IsAny(), It.IsAny>(), It.IsAny())) .Returns(true); } private void GivenNewArtistInfo(Artist artist) { Mocker.GetMock() .Setup(s => s.GetArtistInfo(_artist.ForeignArtistId, _artist.MetadataProfileId)) .Returns(artist); } private void GivenArtistFiles() { Mocker.GetMock() .Setup(x => x.GetFilesByArtist(It.IsAny())) .Returns(Builder.CreateListOfSize(1).BuildList()); } private void GivenAlbumsForRefresh(List albums) { Mocker.GetMock(MockBehavior.Strict) .Setup(s => s.GetAlbumsForRefresh(It.IsAny(), It.IsAny>())) .Returns(albums); } private void AllowArtistUpdate() { Mocker.GetMock(MockBehavior.Strict) .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); } [Test] public void should_not_publish_artist_updated_event_if_metadata_not_updated() { var newArtistInfo = _artist.JsonClone(); newArtistInfo.Metadata = _artist.Metadata.Value.JsonClone(); newArtistInfo.Albums = _remoteAlbums; GivenNewArtistInfo(newArtistInfo); GivenAlbumsForRefresh(_albums); AllowArtistUpdate(); Subject.Execute(new RefreshArtistCommand(_artist.Id)); VerifyEventNotPublished(); VerifyEventPublished(); } [Test] public void should_publish_artist_updated_event_if_metadata_updated() { var newArtistInfo = _artist.JsonClone(); newArtistInfo.Metadata = _artist.Metadata.Value.JsonClone(); newArtistInfo.Metadata.Value.Images = new List { new MediaCover.MediaCover(MediaCover.MediaCoverTypes.Logo, "dummy") }; newArtistInfo.Albums = _remoteAlbums; GivenNewArtistInfo(newArtistInfo); GivenAlbumsForRefresh(new List()); AllowArtistUpdate(); Subject.Execute(new RefreshArtistCommand(_artist.Id)); VerifyEventPublished(); VerifyEventPublished(); } [Test] public void should_call_new_album_monitor_service_when_adding_album() { var newAlbum = Builder.CreateNew() .With(x => x.Id = 0) .With(x => x.ForeignAlbumId = "3") .Build(); _remoteAlbums.Add(newAlbum); var newAuthorInfo = _artist.JsonClone(); newAuthorInfo.Metadata = _artist.Metadata.Value.JsonClone(); newAuthorInfo.Albums = _remoteAlbums; GivenNewArtistInfo(newAuthorInfo); GivenAlbumsForRefresh(_albums); AllowArtistUpdate(); Subject.Execute(new RefreshArtistCommand(_artist.Id)); Mocker.GetMock() .Verify(x => x.ShouldMonitorNewAlbum(newAlbum, _albums, _artist.MonitorNewItems), Times.Once()); } [Test] public void should_log_error_and_delete_if_musicbrainz_id_not_found_and_author_has_no_files() { Mocker.GetMock() .Setup(x => x.DeleteArtist(It.IsAny(), It.IsAny(), It.IsAny())); Subject.Execute(new RefreshArtistCommand(_artist.Id)); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.IsAny(), It.IsAny()), Times.Never()); Mocker.GetMock() .Verify(v => v.DeleteArtist(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); ExceptionVerification.ExpectedErrors(1); ExceptionVerification.ExpectedWarns(1); } [Test] public void should_log_error_but_not_delete_if_musicbrainz_id_not_found_and_artist_has_files() { GivenArtistFiles(); GivenAlbumsForRefresh(new List()); Subject.Execute(new RefreshArtistCommand(_artist.Id)); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.IsAny(), It.IsAny()), Times.Never()); Mocker.GetMock() .Verify(v => v.DeleteArtist(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); ExceptionVerification.ExpectedErrors(2); } [Test] public void should_update_if_musicbrainz_id_changed_and_no_clash() { var newArtistInfo = _artist.JsonClone(); newArtistInfo.Metadata = _artist.Metadata.Value.JsonClone(); newArtistInfo.Albums = _remoteAlbums; newArtistInfo.ForeignArtistId = _artist.ForeignArtistId + 1; newArtistInfo.Metadata.Value.Id = 100; GivenNewArtistInfo(newArtistInfo); var seq = new MockSequence(); Mocker.GetMock(MockBehavior.Strict) .Setup(x => x.FindById(newArtistInfo.ForeignArtistId)) .Returns(default(Artist)); // Make sure that the artist is updated before we refresh the albums Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.GetAlbumsForRefresh(It.IsAny(), It.IsAny>())) .Returns(new List()); // Update called twice for a move/merge Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); Subject.Execute(new RefreshArtistCommand(_artist.Id)); Mocker.GetMock() .Verify(v => v.UpdateArtist(It.Is(s => s.ArtistMetadataId == 100 && s.ForeignArtistId == newArtistInfo.ForeignArtistId), It.IsAny()), Times.Exactly(2)); } [Test] public void should_merge_if_musicbrainz_id_changed_and_new_id_already_exists() { var existing = _artist; var clash = _artist.JsonClone(); clash.Id = 100; clash.Metadata = existing.Metadata.Value.JsonClone(); clash.Metadata.Value.Id = 101; clash.Metadata.Value.ForeignArtistId = clash.Metadata.Value.ForeignArtistId + 1; Mocker.GetMock(MockBehavior.Strict) .Setup(x => x.FindById(clash.Metadata.Value.ForeignArtistId)) .Returns(clash); var newArtistInfo = clash.JsonClone(); newArtistInfo.Metadata = clash.Metadata.Value.JsonClone(); newArtistInfo.Albums = _remoteAlbums; GivenNewArtistInfo(newArtistInfo); var seq = new MockSequence(); // Make sure that the artist is updated before we refresh the albums Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.GetAlbumsByArtist(existing.Id)) .Returns(_albums); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.UpdateMany(It.IsAny>())); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.DeleteArtist(existing.Id, It.IsAny(), false)); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.UpdateArtist(It.Is(a => a.Id == clash.Id), It.IsAny())) .Returns((Artist a, bool updated) => a); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.GetAlbumsForRefresh(clash.ArtistMetadataId, It.IsAny>())) .Returns(_albums); // Update called twice for a move/merge Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) .Setup(x => x.UpdateArtist(It.IsAny(), It.IsAny())) .Returns((Artist a, bool updated) => a); Subject.Execute(new RefreshArtistCommand(_artist.Id)); // the retained artist gets updated Mocker.GetMock() .Verify(v => v.UpdateArtist(It.Is(s => s.Id == clash.Id), It.IsAny()), Times.Exactly(2)); // the old one gets removed Mocker.GetMock() .Verify(v => v.DeleteArtist(existing.Id, false, false)); Mocker.GetMock() .Verify(v => v.UpdateMany(It.Is>(x => x.Count == _albums.Count))); ExceptionVerification.ExpectedWarns(1); } } }