import os import datetime import pytest from freezegun import freeze_time from unittest.mock import patch, call from tadas.cli import launcher @patch('tadas.cli.launcher.run_makefile') @pytest.mark.parametrize( 'env, expected_model_version, expected_args, expected_report_date', [ pytest.param( { }, 't2503_reportingdb', ['inference', 'publish'], '', id='all defaults' ), pytest.param( { 'MODEL_VERSION': 't2509_snowflake', }, 't2509_snowflake', ['inference', 'publish'], '', id='specify model by env' ), pytest.param( { 'MODEL_VERSION': 'initial', }, 't2503_reportingdb', ['inference', 'publish'], '', id='initial model version maps to t2503_reportingdb' ), pytest.param( { 'MODEL_VERSION': 'v2025sep', }, 't2509_snowflake', ['inference', 'publish'], '', id='v2025sep model version maps to t2509_snowflake' ), pytest.param( { 'MAKE_TASKS': 'inference,publish,clean', 'TADAS_CLEAN': 'True', }, 't2503_reportingdb', ['inference', 'publish', 'clean'], '', id='specify both clean and publish' ), pytest.param( { 'MAKE_TASKS': 'run,publish', 'TADAS_CLEAN': 'True', }, 't2503_reportingdb', ['run', 'publish'], '', id='use deprecated run task' ), pytest.param( { 'REPORT_DATE': '2025-11-20', 'MODEL_VERSION': 't2511_noamazon', 'MAKE_TASKS': 'inference,publish,clean', }, 't2511_noamazon', ['inference', 'publish', 'clean'], '2025-11-20', id='specify date, model and publish and clean' ), ]) def test_launch(mock_run_makefile, monkeypatch, env, expected_model_version, expected_args, expected_report_date): for key, value in env.items(): monkeypatch.setenv(key, value) mock_run_makefile.return_value.returncode = 0 launcher.launch() assert mock_run_makefile.call_args_list == [ call( tadas_model_version=expected_model_version, args=expected_args, env={'REPORT_DATE': expected_report_date}), ] @pytest.mark.parametrize( 'env, env_str, expected_config', [ pytest.param( { }, '', { 'MODEL_VERSION': 'initial', 'REPORT_DATE': '', 'MAKE_TASKS': ['inference', 'publish'], }, id='all defaults' ), pytest.param( { 'MODEL_VERSION': 't2509_snowflake', }, '', { 'MODEL_VERSION': 't2509_snowflake', }, id='specify model by env' ), pytest.param( { 'MODEL_VERSION': 't2511_snowflake', 'TADAS_TRIGGER_JENKINS': 'yes', }, '', { 'MODEL_VERSION': 't2511_snowflake', 'TADAS_TRIGGER_JENKINS': True, 'MAKE_TASKS': ['inference', 'publish'], }, id='specify model by env and publish to snowflake' ), pytest.param( { 'MAKE_TASKS': 'publish,clean', }, '', { 'MODEL_VERSION': 'initial', 'MAKE_TASKS': ['publish', 'clean'], }, id='specify both clean and publish' ), pytest.param( { 'REPORT_DATE': '2025-11-20', }, 'REPORT_DATE=2025-11-21', { 'REPORT_DATE': '2025-11-21', 'MODEL_VERSION': 'initial', 'MAKE_TASKS': ['inference', 'publish'], }, id='override date in argv' ), pytest.param( { 'REPORT_DATE': '2025-11-20', }, 'REPORT_DATE=2025-11-21&MODEL_VERSION=t2509_snowflake', { 'REPORT_DATE': '2025-11-21', 'MODEL_VERSION': 't2509_snowflake', 'MAKE_TASKS': ['inference', 'publish'], }, id='override date and model_version in argv' ), pytest.param( { 'REPORT_DATE': '2025-11-20', 'MODEL_VERSION': 't2509_snowflake', }, 'REPORT_DATE=&MODEL_VERSION=', { 'REPORT_DATE': '', 'MODEL_VERSION': 'initial', }, id='override with env_str, even when env_str has empty values' ), pytest.param( { }, 'MAKE_TASKS=clean&MODEL_VERSION=initial', { 'REPORT_DATE': '', 'MODEL_VERSION': 'initial', 'MAKE_TASKS': ['clean'], }, id='clean specific version' ), ]) def test_configure(monkeypatch, env, env_str, expected_config): for key, value in env.items(): monkeypatch.setenv(key, value) launcher.configure(env_str=env_str) for key, value in expected_config.items(): assert launcher.config.get(key) == value def test_main_acquires_lock_and_launches(tmp_path, monkeypatch): lock_file = tmp_path / 'launcher.lock' monkeypatch.setattr(launcher, 'LOCK_FILE_PATH', lock_file) monkeypatch.setattr(launcher, 'LOCK_TTL', datetime.timedelta(hours=2)) with patch('tadas.cli.launcher.prepare') as mock_prepare, patch('tadas.cli.launcher.launch') as mock_launch: launcher.main([]) mock_prepare.assert_called_once() mock_launch.assert_called_once() assert not lock_file.exists() def test_main_lock_acquire_exception(tmp_path, monkeypatch, caplog): lock_file = tmp_path / 'launcher.lock' lock_file.write_text('locked') monkeypatch.setattr(launcher, 'LOCK_FILE_PATH', lock_file) monkeypatch.setattr(launcher, 'LOCK_TTL', datetime.timedelta(hours=2)) with patch('tadas.cli.launcher.prepare') as mock_prepare, patch('tadas.cli.launcher.launch') as mock_launch: with caplog.at_level('WARNING'): launcher.main([]) assert "Could not acquire lock" in caplog.text mock_prepare.assert_not_called() mock_launch.assert_not_called() assert lock_file.exists() def test_run_cli(monkeypatch): with patch.object(launcher.subprocess, 'run') as mock_run: launcher.run_cli('test_module', ['arg1', 'arg2']) mock_run.assert_called_once_with( ['python', '-m', 'test_module', 'arg1', 'arg2'], cwd=launcher.PROJECT_ROOT, env=os.environ, ) @pytest.mark.parametrize( 'env_str, expected_result', [ ('REPORT_DATE=2025-05-20&REQUIRED_DSPS=apple,spotify', {'REPORT_DATE': '2025-05-20', 'REQUIRED_DSPS': 'apple,spotify'}), ('REPORT_DATE=2025-05-20&REPORT_DATE=2025-05-21', {'REPORT_DATE': '2025-05-21'}), ] ) def test_parse_env_values(env_str, expected_result): assert launcher.parse_env_values(env_str) == expected_result