"""Unit tests for Peloton tasks.""" from unittest.mock import MagicMock from unittest.mock import patch from garcon_contrib.dynamo_feed_status import garcon_feed_status import pytest from feed_ingestion.flows.peloton import config from feed_ingestion.flows.peloton import tasks _date = '2026-05-15' def test_bootstrap(): """Bootstrap normalises to the reporting month and builds file keys.""" result = tasks.bootstrap(MagicMock(), _date, reload=None) assert result['feed_name'] == 'peloton' assert result['date'] == '2026-05-01' assert result['s3_dir_path'] == \ 's3://dev-cucumbers/Peloton/archives/2026-05/' assert result['secrets_path'] == 'swf-peloton' assert result['staging_raw_table'] == 'staging_raw_peloton_monthly' # one entry per country, in config order assert [f['country'] for f in result['source_files']] == config.countries us = next(f for f in result['source_files'] if f['country'] == 'US') assert us['file_name'] == 'PMY5_M_US_20260501_20260531.txt' assert us['source_key_name'] == \ 'peloton/in/monthly/PMY5_M_US_20260501_20260531.txt' assert us['destination_key_name'] == \ 'Peloton/archives/2026-05/PMY5_M_US_20260501_20260531.txt' def test_bootstrap_uses_last_day_of_month(): """Filenames use the actual last calendar day of the month.""" result = tasks.bootstrap(MagicMock(), '2026-02-10', reload=None) at = next(f for f in result['source_files'] if f['country'] == 'AT') assert at['file_name'] == 'PMY5_M_AT_20260201_20260228.txt' @pytest.fixture def mock_task_status(): """Patch task_status used by the check_status decorator.""" with patch('feed_ingestion.tasks.task_status') as task_status: task_status.is_completed_task.return_value = False yield task_status @pytest.fixture def mock_s3_tasks(): """Patch the s3_tasks helper used by grab_drop_files.""" with patch('feed_ingestion.flows.peloton.tasks.s3_tasks') as mock_s3: yield mock_s3 @pytest.fixture def mock_set_overall_status(): """Patch the overall status setter used by grab_drop_files.""" path = ( 'feed_ingestion.flows.peloton.tasks.garcon_feed_status.' 'set_overall_status') with patch(path) as overall_status: yield overall_status @pytest.fixture def source_files(): """Return the per-country source files built by bootstrap.""" return tasks.bootstrap(MagicMock(), _date, reload=None)['source_files'] def _copy_result(destination_key_name, found=True, size=100): """Build the copy helper response keyed by the destination file name.""" file_name = destination_key_name.split('/')[-1] return {file_name: found, 'file_size': size} def test_grab_drop_files_all_present( mock_s3_tasks, mock_task_status, mock_set_overall_status, source_files): """When every country file is present all are copied and archived.""" mock_s3_tasks.copy_file_from_sme_s3_to_theocrhard.side_effect = ( lambda *a, **kwargs: _copy_result(kwargs['destination_key_name'])) result = tasks.grab_drop_files( MagicMock(), 'peloton', '2026-05-01', source_files) assert mock_s3_tasks.copy_file_from_sme_s3_to_theocrhard.call_count == \ len(config.countries) assert result['source_files_dict']['files'] == [ {'file_name': f['file_name'], 'file_size': 100, 'found': True} for f in source_files] mock_set_overall_status.assert_not_called() mock_task_status.mark_completed_task.assert_called_once() def test_grab_drop_files_stops_when_file_missing( mock_s3_tasks, mock_task_status, mock_set_overall_status, source_files): """A missing country file stops the flow and marks source unavailable.""" mock_s3_tasks.copy_file_from_sme_s3_to_theocrhard.side_effect = ( lambda *a, **kwargs: _copy_result( kwargs['destination_key_name'], found='_US_' not in kwargs['destination_key_name'])) result = tasks.grab_drop_files( MagicMock(), 'peloton', '2026-05-01', source_files) assert result['stop'] is True assert result['missing_files'] == ['PMY5_M_US_20260501_20260531.txt'] mock_set_overall_status.assert_called_once_with( 'peloton', '2026-05-01', garcon_feed_status.STATUS_NOT_AVAILABLE) mock_task_status.mark_completed_task.assert_not_called() def test_grab_drop_files_skips_when_completed( mock_s3_tasks, mock_task_status, source_files): """A resumed run where the task is already complete copies nothing.""" mock_task_status.is_completed_task.return_value = True result = tasks.grab_drop_files( MagicMock(), 'peloton', '2026-05-01', source_files) mock_s3_tasks.copy_file_from_sme_s3_to_theocrhard.assert_not_called() assert result is None