"""Beatport API wrapper tests.""" from gzip import GzipFile from io import BytesIO import itertools from tempfile import TemporaryFile from unittest.mock import MagicMock from unittest.mock import patch import pytest from feed_ingestion.flows.beatport.beatport_api import BeatportAPI @pytest.fixture def beatport_api(): """Beatport API Wrapper fixture.""" yield BeatportAPI( 'test-supplier', 'test-client-id', 'secret', 'username', 'test') @pytest.fixture def beatport_api_authenticated(): """Yield BeatportAPI Wrapper fixture (authenticated).""" beatport_api = BeatportAPI( 'test-supplier', 'test-client-id', 'secret', 'username', 'test') beatport_api._token = 'token' yield beatport_api @pytest.fixture def mock_oauth_success(): """Yield Beatport API oauth success response.""" requests_path = ( 'feed_ingestion.flows.beatport.beatport_api.requests') with patch(requests_path) as requests: response_body = {'access_token': 'token', 'expires_in': 28800} response_mock = MagicMock() response_mock.status_code = 200 response_mock.json = MagicMock(return_value=response_body) requests.post = MagicMock(return_value=response_mock) yield requests @pytest.fixture def mock_oauth_failure(): """Yield Beatport API oauth failure response.""" requests_path = ( 'feed_ingestion.flows.beatport.beatport_api.requests') with patch(requests_path) as requests: response_mock = MagicMock() response_mock.status_code = 401 requests.post = MagicMock(return_value=response_mock) yield requests @pytest.fixture def mock_call_sales_export_job(): """Yield Beatport API oauth success response.""" requests_path = ( 'feed_ingestion.flows.beatport.beatport_api.BeatportAPI.' '_call_sales_export_job') with patch(requests_path) as requests: response_body = 123 response_mock = MagicMock() response_mock.status_code = 200 response_mock.json = MagicMock(return_value=response_body) requests.post = MagicMock(return_value=response_mock) yield requests @pytest.fixture def mock_get_child_job_id(): """Yield Beatport API oauth success response.""" requests_path = ( 'feed_ingestion.flows.beatport.beatport_api.' 'BeatportAPI._get_child_job_id') with patch(requests_path) as requests: response_body = 123 response_mock = MagicMock() response_mock.status_code = 200 response_mock.json = MagicMock(return_value=response_body) requests.post = MagicMock(return_value=response_mock) yield requests @pytest.fixture def mock_get_download_url(): """Yield Beatport API oauth success response.""" requests_path = ( 'feed_ingestion.flows.beatport.beatport_api.' 'BeatportAPI._get_download_url') with patch(requests_path) as requests: response_body = 'download-url' response_mock = MagicMock() response_mock.status_code = 200 response_mock.json = MagicMock(return_value=response_body) requests.post = MagicMock(return_value=response_mock) yield requests @pytest.fixture def mock_content(): """Mock Tracks Resource response.""" content = ( b'"message": "APITrackData",' b'"version": 4,' b'"release_title": "0",' b'"label_name": "test-label",' b'"track_isrc": "0",' b'"track_title": "0",' b'"track_remix_version": "Test",' b'"country": "USA",' b'"units_sold": 2,' b'"units_voided": 1') out = BytesIO() with GzipFile(fileobj=out, mode='wb') as f: f.write(content) yield out.getvalue() @pytest.fixture def mock_session(mock_content): """Mock requests session.""" session_path = ( 'feed_ingestion.flows.beatport.beatport_api.requests.Session') def iter_content_mock(chunk_size=128): it = iter(mock_content) while True: chunk = bytes(itertools.islice(it, chunk_size)) if not chunk: return yield chunk with patch(session_path) as mock_session: session_instance = mock_session.return_value response_mock = MagicMock() response_mock.iter_content = iter_content_mock response_mock.content = mock_content session_instance.send = MagicMock(return_value=response_mock) yield session_instance def test_version_api_v1(): """Test Beatport API choosing version.""" beatport_api = BeatportAPI( 'test-supplier', 'test-client-id', 'secret', 'username', 'test', 'v1') assert beatport_api.version == 'v1' def test_version_api_default(): """Test Beatport API choosing default version.""" beatport_api = BeatportAPI( 'test-supplier', 'test-client-id', 'secret', 'username', 'test') assert beatport_api.version == 'v4' def test_token_success(mock_oauth_success, beatport_api): """Test Beatport API token request success scenario.""" beatport_api._token = None token = beatport_api.token assert token == 'token' assert beatport_api._token == 'token' def test_token_failure(mock_oauth_failure, beatport_api): """Test Beatport API token request failure scenario.""" beatport_api._token = None with pytest.raises(Exception): beatport_api.token def test_token_cache(mock_oauth_success, beatport_api): """Should not call Beatport API if token is already cached.""" beatport_api._token = 'cached-token' token = beatport_api.token mock_oauth_success.post.assert_not_called() assert token == 'cached-token' def test_download_to_file( beatport_api_authenticated, mock_session, mock_content, mock_call_sales_export_job, mock_get_child_job_id, mock_get_download_url ): """Test download_file writes a temporary file with expected data.""" with TemporaryFile() as fd: beatport_api_authenticated.download_file(fd, '2022-06-12') assert mock_session.get.called fd.seek(0) for line in fd: assert line in mock_content