"""Artist Model Tests. Testing the persistence of the artist model """ from operator import attrgetter from unittest import mock import pytest from artist import response from artist.constants import account as account_constants from artist.constants import errors from artist.models import account from artist.models import artist as artist_model from tests.factories.artist import ArtistFactory from tests.testutils import db ERROR_CODE_NOT_FOUND = 'not_found_error' OWNERSHIP_ERROR = 'ownership_error' def sort_artists(artists): """Sort a list of artist models by name and id.""" return sorted(artists, key=attrgetter('name', 'artist_id')) @pytest.fixture def test_vendor_id(): """Fixture for vendor id.""" return 7123 @db.test_schema def test_list_by_vendor_id_items(test_vendor_id): """Test the data in the returned artist objects.""" test_artist = ArtistFactory.build(vendor_id=test_vendor_id) db.seed_models(test_artist) result = artist_model.list_by_vendor_id(test_vendor_id) result_artists = result.message.get('items') assert len(result_artists) == 1 result_artist = result_artists[0] assert result_artist.artist_id == test_artist.artist_id assert result_artist.vendor_id == test_artist.vendor_id assert result_artist.name == test_artist.name assert result_artist.artist_type == test_artist.artist_type @db.test_schema def test_list_by_vendor_id_filtering(test_vendor_id): """Test that artists are properly filtered by vendor_id.""" wanted_artists = ArtistFactory.build_batch(5, vendor_id=test_vendor_id) unwanted_artist = ArtistFactory.build(vendor_id=1234) db.seed_models(wanted_artists + [unwanted_artist]) result = artist_model.list_by_vendor_id(test_vendor_id) result_artists = result.message.get('items') assert len(result_artists) == len(wanted_artists) @db.test_schema def test_list_by_vendor_id_artist_type_filtering(test_vendor_id): """Test that artists are properly filtered by artist type.""" wanted_artists = ArtistFactory.build_batch(5, vendor_id=test_vendor_id) unwanted_artist = ArtistFactory.build( vendor_id=1234, artist_type='tv_artist') db.seed_models(wanted_artists + [unwanted_artist]) result = artist_model.list_by_vendor_id(test_vendor_id) result_artists = result.message.get('items') assert len(result_artists) == len(wanted_artists) @db.test_schema def test_list_by_vendor_id_page_offset(test_vendor_id): """Test that thee returned artists start at the given page offset.""" test_artists = ArtistFactory.build_batch(10, vendor_id=test_vendor_id) db.seed_models(test_artists) sorted_artists = sort_artists(test_artists) result = artist_model.list_by_vendor_id(test_vendor_id, page_offset=3) result_artists = result.message.get('items') assert len(result_artists) == 7 assert result_artists[0].artist_id == sorted_artists[3].artist_id @db.test_schema def test_list_by_vendor_id_page_limit(test_vendor_id): """Test that the returned artists end at the given page limit.""" test_artists = ArtistFactory.build_batch(10, vendor_id=test_vendor_id) db.seed_models(test_artists) sorted_artists = sort_artists(test_artists) result = artist_model.list_by_vendor_id(test_vendor_id, page_limit=2) result_artists = result.message.get('items') assert len(result_artists) == 2 assert result_artists[1].artist_id == sorted_artists[1].artist_id @db.test_schema def test_list_by_vendor_id_order(test_vendor_id): """Test that the results are sorted by artist name.""" unsorted_artists = [ ArtistFactory.build(vendor_id=test_vendor_id, name='Artist X'), ArtistFactory.build(vendor_id=test_vendor_id, name='someone else'), ArtistFactory.build(vendor_id=test_vendor_id, name='Artist a'), ArtistFactory.build(vendor_id=test_vendor_id, name='artist x'), ArtistFactory.build(vendor_id=test_vendor_id, name='Artist a')] db.seed_models(unsorted_artists) sorted_artists = sort_artists(unsorted_artists) result = artist_model.list_by_vendor_id(test_vendor_id) result_artists = result.message.get('items') assert len(result_artists) == len(sorted_artists) for index, result_artist in enumerate(result_artists): assert result_artist.artist_id == sorted_artists[index].artist_id @db.test_schema def test_upsert_artist_create_persistence(test_vendor_id): """Test that an artist is persisted to the database and is returned.""" artist_name = 'Joe Artist' artist_type = 'film_collection' result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id) artist_id = result.message.get('id') artist_from_db = db.get_artist_by_id(artist_id) assert artist_from_db.name == artist_name assert artist_from_db.artist_type == artist_type @db.test_schema def test_upsert_artist_create_response(test_vendor_id): """Test that the response from creating a new artist. Should contain all required attributes. """ artist_name = 'Joe Artist' artist_type = 'film_collection' result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id) assert result.status == 201 assert isinstance(result.message.get('id'), int) assert result.message.get('name') == artist_name assert result.message.get('artist_type') == artist_type @db.test_schema def test_upsert_artist_find_persistence(context, mocker, test_vendor_id): """Test that an artist is persisted to the database and is returned.""" unsorted_artists = [ ArtistFactory.build(artist_id=1234, vendor_id=test_vendor_id, name='Joe Artist'), ] db.seed_models(unsorted_artists) artist_name = 'Joe Artist' artist_type = 'artist' with context: mocker.spy(context.g.ows.log, 'warning') result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id, ) artist_id = result.message.get('id') artist_from_db = db.get_artist_by_id(artist_id) assert artist_from_db.artist_id == 1234 assert artist_from_db.name == artist_name assert artist_from_db.artist_type == artist_type context.g.ows.log.warning.assert_called() @db.test_schema def test_upsert_artist_find_response(context, mocker, test_vendor_id): """Test that the response from creating a new artist. Should contain all required attributes. """ unsorted_artists = [ ArtistFactory.build(artist_id=1234, vendor_id=test_vendor_id, name='Joe Artist'), ] db.seed_models(unsorted_artists) artist_name = 'Joe Artist' artist_type = 'artist' with context: mocker.spy(context.g.ows.log, 'warning') result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id, ) assert result.status == 201 assert isinstance(result.message.get('id'), int) assert result.message.get('name') == artist_name assert result.message.get('artist_type') == artist_type context.g.ows.log.warning.assert_called() @db.test_schema def test_upsert_artist_default_type(test_vendor_id): """Test that artist type defaults to "artist" if empty value is given.""" artist_name = 'Joe Artist' result = artist_model.upsert_artist( name=artist_name, artist_type=None, vendor_id=test_vendor_id) artist_id = result.message.get('id') artist_from_db = db.get_artist_by_id(artist_id) message_artist_type = result.message.get('artist_type') assert artist_from_db.artist_type == artist_model.DEFAULT_ARTIST_TYPE assert message_artist_type == artist_model.DEFAULT_ARTIST_TYPE @db.test_schema def test_upsert_artist_country(test_vendor_id): """Test that the new artist is save in the DB with a default country ID.""" result = artist_model.upsert_artist( name='Team America', artist_type='film_collection', vendor_id=test_vendor_id) artist_from_db = db.get_artist_by_id(result.message.get('id')) assert artist_from_db.country_id == artist_model.DEFAULT_COUNTRY_ID @db.test_schema def test_fetch_artist_by_id_success(test_vendor_id): """Test that an artist is returned for a given artist ID.""" artist_name = 'Joe Blow' artist_type = 'artist' result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id) artist_id = result.message.get('id') found_artist = artist_model.fetch_artist_by_id(artist_id, test_vendor_id) assert found_artist[0].get('name') == artist_name assert found_artist[0].get('artist_type') == artist_type @db.test_schema def test_filter_artists_success(test_vendor_id): """Test that artists are returned for a given vendor ID.""" artist_model.upsert_artist( name='Joe Rock', artist_type='artist', vendor_id=test_vendor_id) artist_model.upsert_artist( name='Not Joe Rock', artist_type='artist', vendor_id=test_vendor_id) artist_model.upsert_artist( name='Joe Rock', artist_type='artist', vendor_id=test_vendor_id + 1) found_artists = artist_model.filter_artists( vendor_id=test_vendor_id ) assert found_artists.status == 200 assert len(found_artists.message['items']) == 2 assert found_artists.message['items'][0].get('name') == 'Joe Rock' assert found_artists.message['items'][0].get('id') == 1 assert found_artists.message['items'][1].get('name') == 'Not Joe Rock' assert found_artists.message['items'][1].get('id') == 2 @db.test_schema def test_filter_artists_case_sensitive(test_vendor_id): """Test that artist matching is case sensitive.""" artist_name = 'joe rock' artist_model.upsert_artist( name=artist_name, artist_type='artist_type', vendor_id=test_vendor_id ) with mock.patch('sqlalchemy.func.binary', side_effect=lambda x: x): found_artists = artist_model.filter_artists( artist_name=artist_name.upper(), vendor_id=test_vendor_id ) assert found_artists.status == 200 assert not found_artists.message['items'] @db.test_schema def test_filter_artists_diacritic_sensitive(test_vendor_id): """Test that matching treats characters with diacritics as different.""" artist_name = 'Joe Rock' artist_model.upsert_artist( name=artist_name, artist_type='artist_type', vendor_id=test_vendor_id ) with mock.patch('sqlalchemy.func.binary', side_effect=lambda x: x): found_artists = artist_model.filter_artists( artist_name='Jöe Röck', vendor_id=test_vendor_id ) assert found_artists.status == 200 assert not len(found_artists.message['items']) @db.test_schema def test_filter_artists_not_found(test_vendor_id): """Test that an empty list is return when no artist is found.""" with mock.patch('sqlalchemy.func.binary', side_effect=lambda x: x): found_artist = artist_model.filter_artists( artist_name='Joe Pop', vendor_id=test_vendor_id ) assert found_artist.status == 200 assert found_artist.message['items'] == [] @db.test_schema def test_filter_artists_by_unique_artist_id(test_vendor_id): """Test you can filter with unique_artist_id.""" artist_model.upsert_artist( name='Joe Rock', artist_type='artist', vendor_id=test_vendor_id, unique_artist_id=123 ) artist_model.upsert_artist( name='Not Joe Rock', artist_type='artist', vendor_id=test_vendor_id, unique_artist_id=456 ) with mock.patch('sqlalchemy.func.binary', side_effect=lambda x: x): found_artists = artist_model.filter_artists( unique_artist_id=123, vendor_id=test_vendor_id ) assert found_artists.status == 200 assert len(found_artists.message['items']) == 1 @db.test_schema def test_fetch_artist_by_id_no_vendor_id_success(test_vendor_id): """Test that an artist is returned when no vendor id is present.""" artist_name = 'Joe Blow' artist_type = 'artist' result = artist_model.upsert_artist( name=artist_name, artist_type=artist_type, vendor_id=test_vendor_id) artist_id = result.message.get('id') found_artist = artist_model.fetch_artist_by_id(artist_id) assert found_artist[0].get('name') == artist_name assert found_artist[0].get('artist_type') == artist_type @db.test_schema def test_fetch_artist_by_id_not_found(): """Test that an artist is returned for a given artist ID.""" found_artist = artist_model.fetch_artist_by_id(1) assert found_artist.status == 404 assert found_artist.errors.get('code') == ERROR_CODE_NOT_FOUND @db.test_schema def test_update_artist_success( mocker, artist_object, artist_update_data): """Test the update_artist method.""" artist_update_data['country_id'] = 1 artist_update_data['name'] = 'test' artist_update_data['unique_artist_id'] = 1337 artist_object.vendor_id = 12345 db.seed_models(artist_object) artist_result = artist_model.update_artist( artist_object.artist_id, artist_update_data) assert artist_result.status == 200 assert artist_result.message['id'] == artist_object.artist_id assert artist_result.message['country_id'] == 1 assert artist_result.message['name'] == 'test' # Check properties not returned in the response. with db.db_session() as session: updated_artist = session.query( artist_model.Artist).get(artist_object.artist_id) assert updated_artist.unique_artist_id == 1337 @db.test_schema def test_update_artist_with_invalid_artist( artist_object, artist_update_data): """Test the update_artist method if invalid artist_id is provided.""" artist_id = 123 models = artist_object db.seed_models(models) artist_result = artist_model.update_artist(artist_id, artist_update_data) assert artist_result.errors.get('code') == \ 'not_found_error' assert artist_result.status == 404 @db.test_schema def test_check_ownership_success( artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_object.vendor_id = account_id artist_object.artist_id = artist_id models = artist_object db.seed_models(models) artist_ownership_check_result = \ artist_model.check_ownership( artist_id, account_constants.VENDOR_TYPE, account_id) assert artist_ownership_check_result.status == 200 @db.test_schema def test_check_ownership_invalid_account_type( artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_ownership_check_result = \ artist_model.check_ownership(artist_id, 'test', account_id) assert artist_ownership_check_result.status == 400 assert artist_ownership_check_result.errors['code'] == \ errors.ERROR_CODE_BAD_GRASS_REQUEST assert artist_ownership_check_result.errors['message'] == \ 'Invalid account type' @db.test_schema def test_check_ownership_for_subaccount( mocker, artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_object.vendor_id = account_id artist_object.artist_id = artist_id models = artist_object db.seed_models(models) mocker.patch.object( account, 'get_vendor_id_for_subaccount_id', return_value=response.Response( status=200, message=account_id)) artist_ownership_check_result = \ artist_model.check_ownership( artist_id, account_constants.SUBACCOUNT_TYPE, account_id) assert artist_ownership_check_result.status == 200 @db.test_schema def test_check_ownership_for_invalid_subaccount( mocker, artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_object.artist_id = artist_id models = artist_object db.seed_models(models) mocker.patch.object( account, 'get_vendor_id_for_subaccount_id', return_value=response.create_fatal_response( message=errors.ERROR_SUBACCOUNT_LOOKUP_FAILED_MESSAGE ) ) artist_ownership_check_result = \ artist_model.check_ownership( artist_id, account_constants.SUBACCOUNT_TYPE, account_id) assert artist_ownership_check_result.status == 500 assert artist_ownership_check_result.errors['code'] == \ errors.INTERNAL_ERROR assert artist_ownership_check_result.errors['message'] == \ errors.ERROR_SUBACCOUNT_LOOKUP_FAILED_MESSAGE @db.test_schema def test_check_ownership_for_subaccount_not_exists( mocker, artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_object.artist_id = artist_id models = artist_object db.seed_models(models) mocker.patch.object( account, 'get_vendor_id_for_subaccount_id', return_value=response.Response(status=404)) artist_ownership_check_result = \ artist_model.check_ownership( artist_id, account_constants.SUBACCOUNT_TYPE, account_id) assert artist_ownership_check_result.status == 400 assert artist_ownership_check_result.errors['code'] == \ errors.VALIDATION_ERROR assert artist_ownership_check_result.errors['message'] == \ errors.ERROR_SUBACCOUNT_NOT_FOUND_MESSAGE @db.test_schema def test_check_ownership_invalid_owner( artist_id, account_id, artist_object): """Test check_ownership of artist.""" artist_object.artist_id = artist_id models = artist_object db.seed_models(models) artist_ownership_check_result =\ artist_model.check_ownership( artist_id, account_constants.VENDOR_TYPE, account_id) assert artist_ownership_check_result.status == 400 assert artist_ownership_check_result.errors['message'] == \ "The vendor ID provided does not match the artist's vendor ID." assert artist_ownership_check_result.errors['code'] == \ errors.OWNERSHIP_ERROR @db.test_schema def test_check_ownership_invalid_artist( artist_id, account_id, artist_object): """Test check_ownership of artist.""" models = artist_object db.seed_models(models) artist_ownership_check_result = \ artist_model.check_ownership( artist_id, account_constants.VENDOR_TYPE, account_id) assert artist_ownership_check_result.status == 404 assert artist_ownership_check_result.errors.get('code') == \ 'not_found_error' @db.test_schema def test_get_artist_genres(): """Test getting an artist genres.""" db.seed_release({ 'release_id': 1, 'artist_id': 1, 'genre_id': 1, 'last_updated': 0}) db.seed_genre({'genre_id': 1, 'genre': 'Rock'}) artist_id = 1 result = artist_model.get_artist_genres(artist_id) assert result assert len(result.message['items']) == 1 assert result.message['items'][0] == 'Rock' @db.test_schema def test_fetch_full_artists(): """Test fetching full artists.""" artists = [ { 'artist_id': 1, 'name': 'One', 'vendor_id': 1234, 'last_updated': '2000-01-01' }, { 'artist_id': 2, 'name': 'Two', 'vendor_id': 1234, 'last_updated': '2000-01-01' }, { 'artist_id': 3, 'name': 'Three', 'vendor_id': 456, 'last_updated': '2000-01-01' } ] releases = [ { 'release_id': 1, 'artist_id': 1, 'genre_id': 1, 'last_updated': '2000-01-01' }, { 'release_id': 2, 'artist_id': 1, 'genre_id': 2, 'last_updated': '2000-01-01' }, { 'release_id': 3, 'artist_id': 2, 'genre_id': 1, 'last_updated': '2000-01-01' } ] genres = [ {'genre_id': 1, 'genre': 'Rock'}, {'genre_id': 2, 'genre': 'Punk'} ] for artist in artists: db.seed_artist(artist) for release in releases: db.seed_release(release) for genre in genres: db.seed_genre(genre) vendor_id = 1234 result = artist_model.fetch_full_artists(vendor_id) assert len(result.message['items']) == 2 assert { 'artist_id': 1, 'name': 'One', 'genres': ['Rock', 'Punk'] } in result.message['items'] assert { 'artist_id': 2, 'name': 'Two', 'genres': ['Rock'] } in result.message['items'] assert result.message['pagination'] == { 'offset': 0, 'limit': 50, 'total_records': 2 } @db.test_schema def test_fetch_full_artists_bulk(): """Test bulk fetching full artists.""" artists = [ { 'artist_id': 1, 'name': 'One', 'vendor_id': 1234, 'last_updated': '2000-01-01' }, { 'artist_id': 2, 'name': 'Two', 'vendor_id': 1234, 'last_updated': '2000-01-01' }, { 'artist_id': 3, 'name': 'Three', 'vendor_id': 456, 'last_updated': '2000-01-01' }, { 'artist_id': 4, 'name': 'Four', 'vendor_id': 1234, 'last_updated': '2000-01-01' } ] releases = [ { 'release_id': 1, 'artist_id': 1, 'genre_id': 1, 'last_updated': '2000-01-01' }, { 'release_id': 2, 'artist_id': 1, 'genre_id': 2, 'last_updated': '2000-01-01' }, { 'release_id': 3, 'artist_id': 2, 'genre_id': 1, 'last_updated': '2000-01-01' } ] genres = [ {'genre_id': 1, 'genre': 'Rock'}, {'genre_id': 2, 'genre': 'Punk'} ] for artist in artists: db.seed_artist(artist) for release in releases: db.seed_release(release) for genre in genres: db.seed_genre(genre) vendor_id = 1234 artist_ids = [1, 2] result = artist_model.fetch_full_artists_bulk(vendor_id, artist_ids) assert len(result.message['items']) == 2 assert { 'artist_id': 1, 'name': 'One', 'genres': ['Rock', 'Punk'] } in result.message['items'] assert { 'artist_id': 2, 'name': 'Two', 'genres': ['Rock'] } in result.message['items'] @db.test_schema def test_get_artist_document(): """Test getting an artist details.""" artists = [ { 'artist_id': 1, 'name': 'One', 'vendor_id': 1234, 'last_updated': '2000-01-01' }, { 'artist_id': 2, 'name': 'Two', 'vendor_id': 1235, 'last_updated': '2000-01-01' } ] releases = [ { 'release_id': 1, 'artist_id': 1, 'subaccount_id': 1, 'last_updated': '2000-01-01' }, { 'release_id': 2, 'artist_id': 2, 'last_updated': '2000-01-01' }, { 'release_id': 3, 'artist_id': 1, 'subaccount_id': 2, 'last_updated': '2000-01-01' } ] for artist in artists: db.seed_artist(artist) for release in releases: db.seed_release(release) artist_id = 1 vendor_id = 1234 result = artist_model.get_artist_document(artist_id, vendor_id) assert result assert len(result.message) == 4 assert result.message['subaccount_id'] == ['1', '2']