"""Test sound recordings takedown script.""" from unittest.mock import AsyncMock, patch import pandas as pd import pytest import src.sound_recordings_takedown as srt @patch('src.sound_recordings_takedown.build_eligibility_map') def test_populate_release_eligibility(mock_build_eligibility_map): """Test that populate_release_eligibility correctly populates eligibility and update columns.""" mock_build_eligibility_map.return_value = { '111': 'TRUE', '222': 'FALSE', '333': 'TRUE' } df = pd.DataFrame({ 'Release UPC': ['111', '222', '333'], 'Track Name': ['Song A', 'Song B', 'Song C'] }) result = srt.populate_release_eligibility(df) assert list(result['Release Eligibility']) == ['TRUE', 'FALSE', 'TRUE'] assert list(result['Release Update']) == ['Y', 'Y', 'Y'] @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_release_registry(mock_send): """Test that update_release_registry sends all unique non-null UPCs to the registry.""" mock_send.return_value = {} df = pd.DataFrame({ 'Release UPC': ['111', '222', '333'], }) srt.update_release_registry(df) mock_send.assert_called_once() sent_upcs = mock_send.call_args[0][0] assert set(sent_upcs) == {'111', '222', '333'} @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_release_registry_with_skipped(mock_send): """Test that update_release_registry populates Registry Skip Reason for skipped UPCs.""" mock_send.return_value = {'111': 'Invalid UPC format'} df = pd.DataFrame({ 'Release UPC': ['111', '222', '333'], }) result = srt.update_release_registry(df) assert 'Registry Skip Reason' in result.columns assert result.loc[result['Release UPC'] == '111', 'Registry Skip Reason'].iloc[0] == 'Invalid UPC format' assert result.loc[result['Release UPC'] == '222', 'Registry Skip Reason'].iloc[0] == '' @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_release_registry_no_skipped(mock_send): """Test that update_release_registry does not add Registry Skip Reason column when nothing is skipped.""" mock_send.return_value = {} df = pd.DataFrame({ 'Release UPC': ['111', '222'], }) result = srt.update_release_registry(df) assert 'Registry Skip Reason' not in result.columns @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_release_registry_exception_propagation(mock_send): """Test that update_release_registry propagates exceptions from send_upcs_to_registry.""" mock_send.side_effect = Exception('Registry error') df = pd.DataFrame({ 'Release UPC': ['111', '222'], }) with pytest.raises(Exception, match='Registry error'): srt.update_release_registry(df) @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_isrc_registry(mock_send): """Test that update_isrc_registry sends deduplicated non-null UPCs to the registry.""" mock_send.return_value = {} df = pd.DataFrame({ 'Display UPC': ['111', '222', '111', None] }) srt.update_isrc_registry(df) mock_send.assert_called_once() sent_upcs = mock_send.call_args[0][0] assert set(sent_upcs) == {'111', '222'} @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_isrc_registry_with_skipped(mock_send): """Test that update_isrc_registry populates Registry Skip Reason for skipped UPCs.""" mock_send.return_value = {'222': 'Invalid UPC format'} df = pd.DataFrame({ 'Display UPC': ['111', '222', '333'] }) result = srt.update_isrc_registry(df) assert 'Registry Skip Reason' in result.columns assert result.loc[result['Display UPC'] == '222', 'Registry Skip Reason'].iloc[0] == 'Invalid UPC format' assert result.loc[result['Display UPC'] == '111', 'Registry Skip Reason'].iloc[0] == '' @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_isrc_registry_no_skipped(mock_send): """Test that update_isrc_registry does not add Registry Skip Reason column when nothing is skipped.""" mock_send.return_value = {} df = pd.DataFrame({ 'Display UPC': ['111', '222'] }) result = srt.update_isrc_registry(df) assert 'Registry Skip Reason' not in result.columns @patch('src.sound_recordings_takedown.send_upcs_to_registry') def test_update_isrc_registry_exception_propagation(mock_send): """Test that update_isrc_registry propagates exceptions from send_upcs_to_registry.""" mock_send.side_effect = Exception('Registry error') df = pd.DataFrame({ 'Display UPC': ['111', '222'] }) with pytest.raises(Exception, match='Registry error'): srt.update_isrc_registry(df) @patch('src.sound_recordings_takedown.ows_masters_registry.update_registry') def test_send_upcs_to_registry_success(mock_update): """Test that send_upcs_to_registry calls the registry client with the correct UPCs.""" mock_update.return_value = ([('200', 'OK')], {}) srt.send_upcs_to_registry(['111', '222']) mock_update.assert_called_once_with(['111', '222']) @patch('src.sound_recordings_takedown.ows_masters_registry.update_registry') def test_send_upcs_to_registry_success_multiple_batches(mock_update): """Test that send_upcs_to_registry accepts any successful response from multiple batches.""" mock_update.return_value = ([('200', 'OK'), ('201', 'Created'), ('202', 'Accepted')], {}) srt.send_upcs_to_registry(['111', '222', '333']) mock_update.assert_called_once_with(['111', '222', '333']) @patch('src.sound_recordings_takedown.ows_masters_registry.update_registry') def test_send_upcs_to_registry_no_upcs(mock_update): """Test that send_upcs_to_registry does not call the registry client when given an empty list.""" srt.send_upcs_to_registry([]) mock_update.assert_not_called() @patch('src.sound_recordings_takedown.ows_masters_registry.update_registry') def test_send_upcs_to_registry_failure(mock_update): """Test that send_upcs_to_registry raises an error when the registry client returns a failure status.""" mock_update.return_value = ([('200', 'OK'), ('ERROR', 'Connection timeout')], {}) with pytest.raises(RuntimeError, match="Masters Registry update failed for 1/2 batch"): # noqa srt.send_upcs_to_registry(['111', '222']) @patch('src.sound_recordings_takedown.run_all', new_callable=AsyncMock) def test_build_eligibility_map(mock_run_all): """Test build_eligibility_map returns correct eligibility results.""" mock_run_all.return_value = [ ('111', '200', '{"is_eligible": true}'), ('222', '200', '{"is_eligible": false}'), ('333', 'ERROR', 'timeout'), ('444', '200', 'not-json'), ] result = srt.build_eligibility_map(['111', '222', '333', '444']) assert result == { '111': 'TRUE', '222': 'FALSE', '333': 'ERROR', '444': 'ERROR', } @patch('src.sound_recordings_takedown.run_all', new_callable=AsyncMock) def test_build_eligibility_map_empty(mock_run_all): """Test build_eligibility_map returns empty dict when no UPCs provided.""" result = srt.build_eligibility_map([]) mock_run_all.assert_not_called() assert result == {} @pytest.mark.asyncio @patch('src.sound_recordings_takedown.ows_vectororder.check_product_eligibility') async def test_run_all(mock_check): """Test run_all executes check_product_eligibility for all UPCs and returns results.""" async def fake_check(upc, retries, backoff_factor): return upc, '200', 'OK' mock_check.side_effect = fake_check upcs = ['111', '222', '333'] results = await srt.run_all(upcs, retries=1, backoff_factor=0.5) assert len(results) == 3 assert all(status == '200' for _, status, _ in results) assert {upc for upc, _, _ in results} == set(upcs)