"""Unit tests for BandsInTown tasks workflow.""" from datetime import datetime import re from unittest.mock import MagicMock from unittest.mock import patch import pytest from feed_ingestion.flows.bandsintown import config from feed_ingestion.flows.bandsintown import tasks _date = '2025-05-20' _feed_name = 'bandsintown' _report_name = 'artist_optins' _date_dt = datetime.strptime(_date, '%Y-%m-%d').date() @pytest.fixture def expected_bootstrap_response(): """Response for bootstrap task.""" file_name = config.reports[_report_name]['file_pattern'].format( date=_date_dt ) drop_file_pattern = rf'.*\/{file_name}' return { 'date': _date, 'file_name': file_name, 'feed_name': '_'.join([config.feed_name, _report_name]), 'report_name': _report_name, 's3_drop_path': config.s3['drop'].format(date=_date_dt), 's3_staging_path': config.s3['staging'].format(date=_date_dt), 'staging_raw_table': config.reports[_report_name][ 'staging_raw_table'], 'temp_table_name': config.temp_table_name.format( report_name=_report_name, date=_date_dt), 'sf_kwargs': { 'date': _date, 'file_pattern': '.*{}.*'.format( re.escape(file_name.replace('.gpg', '')) ), 'report_name': _report_name, 'error_limit': config.snowflake_error_limit }, 'drop_file_pattern': drop_file_pattern } @patch('feed_ingestion.flows.bandsintown.tasks.garcon_feed_status') def test_bootstrap(feed_status_mock, expected_bootstrap_response): """Test bootstrap task.""" context = { 'activity': MagicMock(), 'date': _date, 'report_name': _report_name, 'reload': None } result = tasks.bootstrap(**context) assert result == expected_bootstrap_response @pytest.fixture def mock_set_overall_status(): """Yield overall status.""" overall_status_path = ( 'feed_ingestion.flows.bandsintown.tasks.garcon_feed_status.' 'set_overall_status') with patch(overall_status_path) as overall_status: yield overall_status @pytest.fixture def mock_get_source_file_content(tmp_path): """Yield overall status.""" path = 'feed_ingestion.flows.bandsintown.tasks.get_source_file_content' file = tmp_path / 'sample.txt' file.write_text('sample content') with patch(path) as mock: mock.return_value = str(file) yield mock @pytest.fixture def mock_s3utils(): """Yield overall status.""" path = 'feed_ingestion.flows.bandsintown.tasks.s3utils' with patch(path) as mock: yield mock @pytest.fixture def mock_boto3(): """Yield overall status.""" path = 'boto3.s3.inject.upload_fileobj' with patch(path) as mock: yield mock def test_grab_drop_files( mock_set_overall_status, mock_get_source_file_content, mock_s3utils, mock_boto3 ): """Test grab_drop_files.""" activity = MagicMock() result = tasks.grab_drop_file( activity=activity, feed_name=_feed_name, date=_date, file_name=config.reports[_report_name]['file_pattern'].format( date=_date_dt), s3_drop_path=config.s3['drop'].format(date=_date_dt), s3_staging_path=config.s3['staging'].format(date=_date_dt) ) assert result != {'stop': True} mock_get_source_file_content.assert_called_once() mock_s3utils.delete_s3_obj.assert_called_once() mock_boto3.assert_called_once() assert result == { 'staging_file_name': 'artist_optins_export_v1_2025-05-20.gz' } @patch('feed_ingestion.flows.bandsintown.tasks.BandsInTownSF') @patch('feed_ingestion.flows.bandsintown.tasks.merge_configs') @patch('feed_ingestion.flows.bandsintown.tasks.get_sf_config') def test_delete_old_snapshots( mock_get_sf_config, mock_merge_configs, mock_bandsintown_sf): """Test delete_old_snapshots task.""" # Setup mocks activity = MagicMock() staging_raw_table = 'raw_bandsintown_artist_optins' sfdb_params = {'db': 'test_db', 'schema': 'test_schema'} secrets_path = '/test/secrets' mock_sf_config = {'db': 'test_db', 'schema': 'test_schema'} mock_get_sf_config.return_value = mock_sf_config mock_merge_configs.return_value = mock_sf_config mock_sf_instance = MagicMock() mock_bandsintown_sf.return_value = mock_sf_instance # Execute task tasks.delete_old_snapshots( activity=activity, staging_raw_table=staging_raw_table, sfdb_params=sfdb_params, secrets_path=secrets_path ) # Verify mock_get_sf_config.assert_called_once_with(secrets_path) mock_merge_configs.assert_called_once_with(mock_sf_config, sfdb_params) mock_bandsintown_sf.assert_called_once_with(mock_sf_config) mock_sf_instance.delete_old_snapshots.assert_called_once_with( staging_raw_table)