#!/usr/bin/env python3 """ Unit tests for SessionNotifier class """ import pytest import os import asyncio from unittest.mock import patch, MagicMock # Add the parent directory to the path import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from session_notifier import SessionNotifier @pytest.fixture def email_credentials(): return { "from": "test@example.com", "to": "recipient@example.com", "password": "password123" } @pytest.fixture def telegram_credentials(): return { "token": "telegram_token", "chat_id": "123456789" } @pytest.fixture def session_notifier(email_credentials, telegram_credentials): return SessionNotifier( email_credentials=email_credentials, telegram_credentials=telegram_credentials, enable_email=True, enable_telegram=True ) @pytest.mark.asyncio async def test_notify_session_booking(session_notifier, email_credentials, telegram_credentials): """Test session booking notification with both email and Telegram enabled""" # Mock the email and Telegram notification methods with patch.object(session_notifier, 'send_email_notification') as mock_email, \ patch.object(session_notifier, 'send_telegram_notification', new=MagicMock()) as mock_telegram: # Set up the mock for the async method mock_telegram.return_value = asyncio.Future() mock_telegram.return_value.set_result(None) # Call the method to test await session_notifier.notify_session_booking("Test session") # Verify both notification methods were called mock_email.assert_called_once_with("Session booked: Test session") mock_telegram.assert_called_once_with("Session booked: Test session") @pytest.mark.asyncio async def test_notify_session_booking_email_only(session_notifier, email_credentials): """Test session booking notification with only email enabled""" # Disable Telegram notifications session_notifier.enable_telegram = False # Mock the email notification method with patch.object(session_notifier, 'send_email_notification') as mock_email, \ patch.object(session_notifier, 'send_telegram_notification') as mock_telegram: # Call the method to test await session_notifier.notify_session_booking("Test session") # Verify only email notification was called mock_email.assert_called_once_with("Session booked: Test session") mock_telegram.assert_not_called() @pytest.mark.asyncio async def test_notify_session_booking_telegram_only(session_notifier, telegram_credentials): """Test session booking notification with only Telegram enabled""" # Disable email notifications session_notifier.enable_email = False # Mock the Telegram notification method with patch.object(session_notifier, 'send_telegram_notification', new=MagicMock()) as mock_telegram: # Set up the mock for the async method mock_telegram.return_value = asyncio.Future() mock_telegram.return_value.set_result(None) # Call the method to test await session_notifier.notify_session_booking("Test session") # Verify only Telegram notification was called mock_telegram.assert_called_once_with("Session booked: Test session") @pytest.mark.asyncio async def test_notify_upcoming_session(session_notifier, email_credentials, telegram_credentials): """Test upcoming session notification with both email and Telegram enabled""" # Mock the email and Telegram notification methods with patch.object(session_notifier, 'send_email_notification') as mock_email, \ patch.object(session_notifier, 'send_telegram_notification', new=MagicMock()) as mock_telegram: # Set up the mock for the async method mock_telegram.return_value = asyncio.Future() mock_telegram.return_value.set_result(None) # Call the method to test await session_notifier.notify_upcoming_session("Test session", 3) # Verify both notification methods were called mock_email.assert_called_once_with("Session available soon: Test session (in 3 days)") mock_telegram.assert_called_once_with("Session available soon: Test session (in 3 days)") @pytest.mark.asyncio async def test_notify_impossible_booking_enabled(session_notifier, email_credentials, telegram_credentials): """Test impossible booking notification when notifications are enabled""" # Set the notify_impossible attribute to True session_notifier.notify_impossible = True # Mock the email and Telegram notification methods with patch.object(session_notifier, 'send_email_notification') as mock_email, \ patch.object(session_notifier, 'send_telegram_notification', new=MagicMock()) as mock_telegram: # Set up the mock for the async method mock_telegram.return_value = asyncio.Future() mock_telegram.return_value.set_result(None) # Call the method to test await session_notifier.notify_impossible_booking("Test session") # Verify both notification methods were called mock_email.assert_called_once_with("Failed to book session: Test session") mock_telegram.assert_called_once_with("Failed to book session: Test session") @pytest.mark.asyncio async def test_notify_impossible_booking_disabled(session_notifier, email_credentials, telegram_credentials): """Test impossible booking notification when notifications are disabled""" # Mock the email and Telegram notification methods with patch.object(session_notifier, 'send_email_notification') as mock_email, \ patch.object(session_notifier, 'send_telegram_notification', new=MagicMock()) as mock_telegram: # Set up the mock for the async method mock_telegram.return_value = asyncio.Future() mock_telegram.return_value.set_result(None) # Call the method to test with notify_if_impossible=False await session_notifier.notify_impossible_booking("Test session", notify_if_impossible=False) # Verify neither notification method was called mock_email.assert_not_called() mock_telegram.assert_not_called() @pytest.mark.asyncio async def test_send_email_notification_success(session_notifier, email_credentials): """Test successful email notification""" # Mock the smtplib.SMTP_SSL class with patch('smtplib.SMTP_SSL') as mock_smtp: # Set up the mock to return a context manager mock_smtp_instance = MagicMock() mock_smtp.return_value.__enter__.return_value = mock_smtp_instance # Set up environment variable for SMTP server with patch.dict(os.environ, {"SMTP_SERVER": "smtp.example.com"}): # Call the method to test session_notifier.send_email_notification("Test email") # Verify SMTP methods were called mock_smtp.assert_called_once_with("smtp.example.com", 465) mock_smtp_instance.login.assert_called_once_with( email_credentials["from"], email_credentials["password"] ) mock_smtp_instance.send_message.assert_called_once() @pytest.mark.asyncio async def test_send_email_notification_failure(session_notifier, email_credentials): """Test email notification failure""" # Mock the smtplib.SMTP_SSL class to raise an exception with patch('smtplib.SMTP_SSL', side_effect=Exception("SMTP error")): # Set up environment variable for SMTP server with patch.dict(os.environ, {"SMTP_SERVER": "smtp.example.com"}): # Verify the method raises an exception with pytest.raises(Exception): session_notifier.send_email_notification("Test email") if __name__ == "__main__": pytest.main([__file__, "-v"])