"""Unit tests for the Airflow MWAA connector.""" from unittest.mock import MagicMock, patch import pytest from flask import Flask, Response from abacus_contract.blueprints import dag def _mk_client(): """Create a Flask test client with the blueprint registered.""" app = Flask(__name__) app.register_blueprint(dag.dag_api) return app.test_client() def test_get_dag_run_time_defaults_to_30_days(): """No ?days_back param means we pass 30 to the logic layer.""" client = _mk_client() sentinel_logic_result = object() with ( patch( 'abacus_contract.blueprints.dag.dag_run_times.get_average_dag_run_time' ) as mock_logic, patch('abacus_contract.blueprints.dag.flaskify') as mock_flaskify, ): mock_logic.return_value = sentinel_logic_result mock_flaskify.side_effect = lambda x: Response('wrapped-default', status=200) resp = client.get('/dag_run_time/my_dag') # Assert wiring & defaults mock_logic.assert_called_once_with('my_dag', days_back=30) mock_flaskify.assert_called_once_with(sentinel_logic_result) assert resp.status_code == 200 assert resp.get_data(as_text=True) == 'wrapped-default' @pytest.mark.parametrize( 'query,expected_days', [ ('?days_back=7', 7), ('?days_back=1', 1), ('?days_back=0', 0), ('?days_back=-5', -5), # Flask's type=int will pass through negative ints ], ) def test_get_dag_run_time_respects_days_back_query(query, expected_days): """Ensure days_back query param is passed to logic layer.""" client = _mk_client() sentinel_logic_result = object() with ( patch( 'abacus_contract.blueprints.dag.dag_run_times.get_average_dag_run_time' ) as mock_logic, patch('abacus_contract.blueprints.dag.flaskify') as mock_flaskify, ): mock_logic.return_value = sentinel_logic_result mock_flaskify.side_effect = lambda x: Response('wrapped-param', status=201) resp = client.get(f'/dag_run_time/example{query}') mock_logic.assert_called_once_with('example', days_back=expected_days) mock_flaskify.assert_called_once_with(sentinel_logic_result) # Ensure we return whatever flaskify returns assert resp.status_code == 201 assert resp.get_data(as_text=True) == 'wrapped-param' def test_get_dag_run_time_invalid_days_back_falls_back_to_default(): """Flask's request.args.get(..., type=int, default=30) returns the default when conversion fails. Ensure we pass 30 to the logic layer.""" client = _mk_client() with ( patch( 'abacus_contract.blueprints.dag.dag_run_times.get_average_dag_run_time' ) as mock_logic, patch('abacus_contract.blueprints.dag.flaskify') as mock_flaskify, ): mock_logic.return_value = {'ok': True} mock_flaskify.side_effect = lambda x: Response('wrapped-invalid', status=200) resp = client.get('/dag_run_time/some_dag?days_back=abc') mock_logic.assert_called_once_with('some_dag', days_back=30) mock_flaskify.assert_called_once() assert resp.status_code == 200 assert resp.get_data(as_text=True) == 'wrapped-invalid' def test_get_dag_run_time_disallows_post(): """Route only declares GET; POST should be 405.""" client = _mk_client() resp = client.post('/dag_run_time/anything') assert resp.status_code == 405