"""Functional tests for GET artists handler.""" import json from operator import attrgetter from unittest.mock import MagicMock from owsrequest import request from artist import response from artist.constants import errors from artist.constants import headers from artist.logic import artists as artists_logic from artist.models import artist as artist_model from tests.factories.artist import ArtistFactory from tests.testutils import db def strip_header(key, headers): """Remove a particular header to test header validation.""" del (headers[key]) return headers def sort_artists(artists): """Sort a list of artist models by name and id.""" return sorted(artists, key=attrgetter('name', 'artist_id')) def test_artists_missing_grass_account_type(client, valid_headers_for_vendor): """Test that a request is rejected without Grass account type header.""" response = client.get( '/artists', headers=strip_header( headers.GRASS_ACCOUNT_TYPE, valid_headers_for_vendor)) response_json = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 assert headers.GRASS_ACCOUNT_TYPE, response_json['message'] def test_artists_missing_grass_account_id(client, valid_headers_for_vendor): """Test that a request is rejected without Grass account ID header.""" response = client.get( '/artists', headers=strip_header( headers.GRASS_ACCOUNT_ID, valid_headers_for_vendor)) response_json = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 assert headers.GRASS_ACCOUNT_ID in response_json['message'] def test_artists_missing_content_type(client, valid_headers_for_vendor): """Test that a request is rejected without the content type header.""" res = client.get( '/artists', headers=strip_header(headers.CONTENT_TYPE, valid_headers_for_vendor)) res_json = json.loads(res.data.decode('utf-8')) assert res.status_code == 400 assert headers.CONTENT_TYPE in res_json['message'] @db.test_schema def test_artists_with_valid_headers(client, valid_headers_for_vendor): """Test that a request is accepted with all of the required headers.""" res = client.get('/artists', headers=valid_headers_for_vendor) assert res.status_code == 200 @db.test_schema def test_artists_json(client, valid_headers_for_vendor, account_id): """Test the response body from GET /artists. Should contain the expected artist data. """ seed_artists = ArtistFactory.build_batch(10, vendor_id=account_id) db.seed_models(seed_artists) sorted_artists = sort_artists(seed_artists) res = client.get('/artists', headers=valid_headers_for_vendor) res_json = json.loads(res.data.decode('utf-8')) items = res_json.get('items') assert len(items) == len(seed_artists) for index, item in enumerate(items): seed_artist = sorted_artists[index] assert item == { 'id': seed_artist.artist_id, 'name': seed_artist.name, 'artist_type': seed_artist.artist_type} @db.test_schema def test_artists_paged_items(client, valid_headers_for_vendor, account_id): """Test that artists are paginated with the given offset and limit.""" offset = 2 limit = 5 total = 10 seed_artists = ArtistFactory.build_batch(total, vendor_id=account_id) db.seed_models(seed_artists) sorted_artists = sort_artists(seed_artists) res = client.get( '/artists?page_offset={}&page_limit={}'.format(offset, limit), headers=valid_headers_for_vendor) res_json = json.loads(res.data.decode('utf-8')) items = res_json.get('items') assert len(items) == limit for index, item in enumerate(items): assert item.get('id') == sorted_artists[index + offset].artist_id @db.test_schema def test_artists_pagination_body(client, valid_headers_for_vendor, account_id): """Test artist pagination. Test that the JSON in the response body contains the correct pagination information. """ offset = 2 limit = 5 total = 10 seed_artists = ArtistFactory.build_batch(total, vendor_id=account_id) db.seed_models(seed_artists) res = client.get( '/artists?page_offset={}&page_limit={}'.format(offset, limit), headers=valid_headers_for_vendor) res_json = json.loads(res.data.decode('utf-8')) assert res_json.get('pagination') == { 'offset': offset, 'limit': limit, 'total_records': total} @db.test_schema def test_artists_default_paged_items( client, valid_headers_for_vendor, account_id): """Test that the expected default limit and offset are used. Should happen when no pagination params are explicitly given. """ total = artist_model.DEFAULT_PAGE_LIMIT + 1 seed_artists = ArtistFactory.build_batch(total, vendor_id=account_id) db.seed_models(seed_artists) sorted_artists = sort_artists(seed_artists) res = client.get('/artists', headers=valid_headers_for_vendor) res_json = json.loads(res.data.decode('utf-8')) items = res_json.get('items') assert len(items) == artist_model.DEFAULT_PAGE_LIMIT for index, item in enumerate(items): assert item.get('id') == sorted_artists[index].artist_id @db.test_schema def test_artists_default_pagination( client, valid_headers_for_vendor, account_id): """Test default artists pagination. Test that the JSON in the response body contains the correct default pagination values when none are explicitly given in the request. """ total = artist_model.DEFAULT_PAGE_LIMIT + 1 seed_artists = ArtistFactory.build_batch(total, vendor_id=account_id) db.seed_models(seed_artists) res = client.get('/artists', headers=valid_headers_for_vendor) res_json = json.loads(res.data.decode('utf-8')) assert res_json.get('pagination') == { 'offset': 0, 'limit': artist_model.DEFAULT_PAGE_LIMIT, 'total_records': total} @db.test_schema def test_artists_subaccount_items( client, valid_headers_for_subaccount, mocker): """Test artists for subaccount. Test that the artists for the parent account are returned when a subaccount id is given. """ mock_response = MagicMock(status_code=200) mock_response.json = MagicMock(return_value={'vendor_id': 7123}) mocker.patch.object(request, 'get', return_value=mock_response) vendor_artists = ArtistFactory.build_batch(10, vendor_id=7123) unwanted_artist = ArtistFactory.build(vendor_id=9999) db.seed_models(vendor_artists + [unwanted_artist]) res = client.get('/artists', headers=valid_headers_for_subaccount) res_json = json.loads(res.data.decode('utf-8')) items = res_json.get('items') assert len(items) == len(vendor_artists) @db.test_schema def test_artists_artist_type_items( mocker, client, valid_headers_for_subaccount, account_id): """Test artists can be filtered by artist type. Test that the artists returned are filtered by artist type if supplied """ mock_response = MagicMock(status_code=200) mock_response.json = MagicMock(return_value={'vendor_id': 7123}) mocker.patch.object(request, 'get', return_value=mock_response) artist_type = 'artist' wanted_artists = ArtistFactory.build_batch(10, vendor_id=7123) unwanted_artist = ArtistFactory.build( vendor_id=7123, artist_type='tv_artist') db.seed_models(wanted_artists + [unwanted_artist]) res = client.get('/artists?artist_type={}'.format( artist_type), headers=valid_headers_for_subaccount) res_json = json.loads(res.data.decode('utf-8')) items = res_json.get('items') assert len(items) == len(wanted_artists) @db.test_schema def test_artists_subaccount_ows_account_error( client, valid_headers_for_subaccount, mocker): """Test response when a call to ows-account fails.""" mock_response = MagicMock(status_code=500) mocker.patch.object(request, 'get', return_value=mock_response) res = client.get('/artists', headers=valid_headers_for_subaccount) assert res.status_code == 500 @db.test_schema def test_artists_subaccount_not_found( mocker, client, valid_headers_for_subaccount): """Test that the API returns an error response. Should happen when the given subaccount id is not found by ows-account. """ mock_response = MagicMock(status_code=404) mocker.patch.object(request, 'get', return_value=mock_response) res = client.get('/artists', headers=valid_headers_for_subaccount) res_json = json.loads(res.data.decode('utf-8')) assert res.status_code == 400 assert res_json == { 'code': errors.VALIDATION_ERROR, 'message': artists_logic.SUBACCOUNT_NOT_FOUND_MESSAGE} @db.test_schema def test_artists_missing_account_vendor_id( client, valid_headers_for_subaccount, mocker): """Test that the API returns an error response. Should happen if ows-account returns a successful response for a subaccount vendor_id lookup but no vendor_id. """ mock_response = MagicMock(status_code=200) mock_response.json = MagicMock(return_value={}) mocker.patch.object(request, 'get', return_value=mock_response) artists_response = client.get( '/artists', headers=valid_headers_for_subaccount) artists_json = json.loads(artists_response.data.decode('utf-8')) assert artists_response.status_code == 500 assert artists_json == { 'code': response.ERROR_CODE_INTERNAL_ERROR, 'message': artists_logic.SUBACCOUNT_LOOKUP_FAILED_MESSAGE } @db.test_schema def test_artists_page_limit_zero( client, valid_headers_for_vendor): """Test that the API returns an error if an invalid page limit is given.""" response = client.get( '/artists?page_limit=0', headers=valid_headers_for_vendor) response_json = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 assert response_json == { 'code': 'validation_error', 'message': {'page_limit': "'0' does not match '^[1-9]\\\\d*$'"}}