"""Unit tests for functions/close_form/handler.py — wall-clock guard.""" import os import sys from datetime import datetime from unittest.mock import patch from zoneinfo import ZoneInfo os.environ.setdefault( "AGGREGATE_FUNCTION_ARN", "arn:aws:lambda:us-east-1:000000000000:function:test-aggregate", ) import importlib.util _handler_path = os.path.join( os.path.dirname(__file__), os.pardir, "functions", "close_form", "handler.py" ) _spec = importlib.util.spec_from_file_location( "close_form_handler", os.path.abspath(_handler_path) ) close_form_handler = importlib.util.module_from_spec(_spec) sys.modules["close_form_handler"] = close_form_handler _spec.loader.exec_module(close_form_handler) ET = ZoneInfo("America/New_York") def _make_datetime(year, month, day, hour, minute=0): return datetime(year, month, day, hour, minute, tzinfo=ET) class TestCloseFormGuard: @patch("close_form_handler.datetime") def test_skipped_on_wednesday(self, mock_dt): """Wednesday 11pm ET -> skipped (not Thursday).""" mock_dt.now.return_value = _make_datetime(2026, 5, 13, 23) # Wednesday result = close_form_handler.lambda_handler({}, None) assert result["status"] == "skipped" @patch("close_form_handler.datetime") def test_skipped_on_friday_after_catchup_window(self, mock_dt): """Friday 4am ET -> skipped (past Thu 23 / Fri 00–03 catch-up window).""" mock_dt.now.return_value = _make_datetime(2026, 5, 15, 4) # Friday 4am result = close_form_handler.lambda_handler({}, None) assert result["status"] == "skipped" @patch("close_form_handler.datetime") def test_skipped_thursday_before_11pm(self, mock_dt): """Thursday 10pm ET -> skipped (too early).""" mock_dt.now.return_value = _make_datetime(2026, 5, 14, 22) # Thursday 10pm result = close_form_handler.lambda_handler({}, None) assert result["status"] == "skipped" @patch("close_form_handler.set_form_status") @patch("close_form_handler.get_form_status", return_value="open") @patch("close_form_handler.current_week", return_value="2026-W19") @patch("close_form_handler._lambda") @patch("close_form_handler.datetime") def test_runs_thursday_at_11pm( self, mock_dt, mock_lam, mock_week, mock_status, mock_set ): """Thursday 11pm ET -> proceeds to close.""" mock_dt.now.return_value = _make_datetime(2026, 5, 14, 23) # Thursday 11pm result = close_form_handler.lambda_handler({}, None) assert result["status"] == "closed" mock_set.assert_called_once() @patch("close_form_handler.set_form_status") @patch("close_form_handler.get_form_status", return_value="open") @patch("close_form_handler.current_week", return_value="2026-W19") @patch("close_form_handler._lambda") @patch("close_form_handler.datetime") def test_runs_thursday_at_1159pm( self, mock_dt, mock_lam, mock_week, mock_status, mock_set ): """Thursday 11:59pm ET -> proceeds to close.""" mock_dt.now.return_value = _make_datetime(2026, 5, 14, 23, 59) result = close_form_handler.lambda_handler({}, None) assert result["status"] == "closed" @patch("close_form_handler.set_form_status") @patch("close_form_handler.get_form_status", return_value="open") @patch("close_form_handler.current_week", return_value="2026-W19") @patch("close_form_handler._lambda") @patch("close_form_handler.datetime") def test_runs_friday_just_after_midnight( self, mock_dt, mock_lam, mock_week, mock_status, mock_set ): """Friday 12:30am ET -> proceeds (delayed EventBridge past Thu 23:59).""" mock_dt.now.return_value = _make_datetime(2026, 5, 15, 0, 30) result = close_form_handler.lambda_handler({}, None) assert result["status"] == "closed" mock_set.assert_called_once() @patch("close_form_handler.get_form_status", return_value="closed") @patch("close_form_handler.current_week", return_value="2026-W19") @patch("close_form_handler.datetime") def test_already_closed(self, mock_dt, mock_week, mock_status): """Thursday 11pm but already closed -> returns already_closed.""" mock_dt.now.return_value = _make_datetime(2026, 5, 14, 23) result = close_form_handler.lambda_handler({}, None) assert result["status"] == "already_closed"