From cd5f0a54acd42c0cb8bced8bf6776a0cbc05e9e6 Mon Sep 17 00:00:00 2001 From: Kevin Bataille Date: Mon, 6 Oct 2025 15:19:27 +0200 Subject: [PATCH] feat(notifier): Add username display and improve config error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CROSSFIT_USERNAME from .env to all Telegram notifications - Make session_config exit safely when config file is missing or invalid - Remove default hardcoded sessions, return empty list instead - Update unit tests to reflect new error handling behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/session_notifier.py | 15 +++++---- test/test_session_config.py | 62 ++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/session_notifier.py b/src/session_notifier.py index f3e14cf..b64b251 100644 --- a/src/session_notifier.py +++ b/src/session_notifier.py @@ -39,6 +39,9 @@ class SessionNotifier: # Check environment variable for impossible booking notifications self.notify_impossible = os.environ.get("NOTIFY_IMPOSSIBLE_BOOKING", "true").lower() in ("true", "1", "yes") + # Get crossfit username from environment + self.crossfit_username = os.environ.get("CROSSFIT_USERNAME", "User") + def send_email_notification(self, message): """ Send an email notification with the given message. @@ -114,8 +117,8 @@ class SessionNotifier: session_details (str): Details about the booked session """ # Create messages for both email and Telegram - email_message = f"Session booked: {session_details}" - telegram_message = f"Session booked: {session_details}" + email_message = f"Session booked for {self.crossfit_username}: {session_details}" + telegram_message = f"Session booked for {self.crossfit_username}: {session_details}" # Send notifications through enabled channels if self.enable_email: @@ -133,8 +136,8 @@ class SessionNotifier: days_until (int): Number of days until the session """ # Create messages for both email and Telegram - email_message = f"Session available soon: {session_details} (in {days_until} days)" - telegram_message = f"Session available soon: {session_details} (in {days_until} days)" + email_message = f"Session available soon for {self.crossfit_username}: {session_details} (in {days_until} days)" + telegram_message = f"Session available soon for {self.crossfit_username}: {session_details} (in {days_until} days)" # Send notifications through enabled channels if self.enable_email: @@ -164,8 +167,8 @@ class SessionNotifier: return # Create messages for both email and Telegram - email_message = f"Failed to book session: {session_details}" - telegram_message = f"Failed to book session: {session_details}" + email_message = f"Failed to book session for {self.crossfit_username}: {session_details}" + telegram_message = f"Failed to book session for {self.crossfit_username}: {session_details}" # Send notifications through enabled channels if self.enable_email: diff --git a/test/test_session_config.py b/test/test_session_config.py index 18716c3..ec6100d 100644 --- a/test/test_session_config.py +++ b/test/test_session_config.py @@ -37,18 +37,17 @@ class TestSessionConfig: """Test behavior when the config file is not found""" # Mock the open function to raise FileNotFoundError with patch('builtins.open', side_effect=FileNotFoundError): - with patch('logging.warning') as mock_warning: - sessions = SessionConfig.load_preferred_sessions() + with patch('logging.error') as mock_error: + with pytest.raises(SystemExit) as excinfo: + SessionConfig.load_preferred_sessions() - # Verify warning was logged - mock_warning.assert_called_once() - assert "not found" in mock_warning.call_args[0][0] + # Verify error was logged + assert mock_error.call_count == 2 + assert "not found" in mock_error.call_args_list[0][0][0] + assert "example" in mock_error.call_args_list[1][0][0] - # Verify default sessions are returned - assert len(sessions) == 3 - assert sessions[0] == (2, "18:30", "CONDITIONING") - assert sessions[1] == (4, "17:00", "WEIGHTLIFTING") - assert sessions[2] == (5, "12:30", "HYROX") + # Verify SystemExit was raised with exit code 1 + assert excinfo.value.code == 1 def test_load_preferred_sessions_invalid_json(self): """Test behavior when the config file contains invalid JSON""" @@ -57,18 +56,16 @@ class TestSessionConfig: # Mock the open function to return invalid JSON with patch('builtins.open', mock_open(read_data=invalid_json)): - with patch('logging.warning') as mock_warning: - sessions = SessionConfig.load_preferred_sessions() + with patch('logging.error') as mock_error: + with pytest.raises(SystemExit) as excinfo: + SessionConfig.load_preferred_sessions() - # Verify warning was logged - mock_warning.assert_called_once() - assert "decode" in mock_warning.call_args[0][0] + # Verify error was logged + mock_error.assert_called_once() + assert "decode" in mock_error.call_args[0][0] - # Verify default sessions are returned - assert len(sessions) == 3 - assert sessions[0] == (2, "18:30", "CONDITIONING") - assert sessions[1] == (4, "17:00", "WEIGHTLIFTING") - assert sessions[2] == (5, "12:30", "HYROX") + # Verify SystemExit was raised with exit code 1 + assert excinfo.value.code == 1 def test_load_preferred_sessions_empty_file(self): """Test behavior when the config file is empty""" @@ -79,11 +76,8 @@ class TestSessionConfig: with patch('builtins.open', mock_open(read_data=empty_json)): sessions = SessionConfig.load_preferred_sessions() - # Verify default sessions are returned - assert len(sessions) == 3 - assert sessions[0] == (2, "18:30", "CONDITIONING") - assert sessions[1] == (4, "17:00", "WEIGHTLIFTING") - assert sessions[2] == (5, "12:30", "HYROX") + # Verify empty list is returned + assert sessions == [] def test_load_preferred_sessions_missing_fields(self): """Test behavior when some fields are missing in the JSON data""" @@ -111,18 +105,16 @@ class TestSessionConfig: # Mock the open function to return partial JSON with patch('builtins.open', mock_open(read_data=partial_json)): - with patch('logging.warning') as mock_warning: - sessions = SessionConfig.load_preferred_sessions() + with patch('logging.error') as mock_error: + with pytest.raises(SystemExit) as excinfo: + SessionConfig.load_preferred_sessions() - # Verify warning was logged - mock_warning.assert_called_once() - assert "decode" in mock_warning.call_args[0][0] + # Verify error was logged + mock_error.assert_called_once() + assert "decode" in mock_error.call_args[0][0] - # Verify default sessions are returned - assert len(sessions) == 3 - assert sessions[0] == (2, "18:30", "CONDITIONING") - assert sessions[1] == (4, "17:00", "WEIGHTLIFTING") - assert sessions[2] == (5, "12:30", "HYROX") + # Verify SystemExit was raised with exit code 1 + assert excinfo.value.code == 1 def test_load_preferred_sessions_incorrect_field_types(self): """Test behavior when the config file contains JSON with incorrect field types"""