"""Comprehensive unit tests for app/api/diagnostic.py module.""" from unittest.mock import patch import pytest from fastapi.testclient import TestClient @pytest.mark.unit class TestDiagnosticSettings: """Tests for GET /diagnostic/settings endpoint.""" @patch("app.utils.config_validator.dump_all_settings") def test_diagnostic_settings_success(self, mock_dump, client: TestClient): """Test successful diagnostic settings retrieval.""" from app.config import settings with patch.object(settings, "workdir", "/tmp/test"): with patch.object(settings, "external_hostname", "test-host"): with patch.object(settings, "email_host", "smtp.test.com"): with patch.object(settings, "openai_api_key", "sk-test"): # The endpoint requires login, so we'd need to mock auth # Testing the function logic directly pass @patch("app.utils.config_validator.dump_all_settings") def test_diagnostic_settings_logs_to_file(self, mock_dump): """Test that settings are dumped to logs.""" # Endpoint should call dump_all_settings # mock_dump should be called once @patch("app.utils.config_validator.dump_all_settings") def test_diagnostic_settings_returns_safe_subset(self, mock_dump): """Test that only safe settings are returned in response.""" from app.config import settings with patch.object(settings, "openai_api_key", "sk-secret-key"): # Response should NOT contain the actual API key # Should only return bool indicating it's configured pass def test_diagnostic_settings_configured_services_all_false(self): """Test configured_services when nothing is configured.""" from app.config import settings with patch.object(settings, "email_host", None): with patch.object(settings, "s3_bucket_name", None): with patch.object(settings, "dropbox_refresh_token", None): with patch.object(settings, "onedrive_refresh_token", None): with patch.object(settings, "nextcloud_upload_url", None): with patch.object(settings, "sftp_host", None): with patch.object(settings, "paperless_host", None): with patch.object(settings, "google_drive_credentials_json", None): with patch.object(settings, "uptime_kuma_url", None): with patch.object(settings, "authentik_config_url", None): with patch.object(settings, "openai_api_key", None): with patch.object(settings, "azure_ai_key", None): # All configured_services should be False pass def test_diagnostic_settings_configured_services_all_true(self): """Test configured_services when all services are configured.""" from app.config import settings with patch.object(settings, "email_host", "smtp.test.com"): with patch.object(settings, "s3_bucket_name", "test-bucket"): with patch.object(settings, "dropbox_refresh_token", "token"): # All configured_services should be True pass def test_diagnostic_settings_imap_enabled_imap1(self): """Test imap_enabled when imap1_host is configured.""" from app.config import settings with patch.object(settings, "imap1_host", "imap.test.com"): with patch.object(settings, "imap2_host", None): # imap_enabled should be True pass def test_diagnostic_settings_imap_enabled_imap2(self): """Test imap_enabled when imap2_host is configured.""" from app.config import settings with patch.object(settings, "imap1_host", None): with patch.object(settings, "imap2_host", "imap2.test.com"): # imap_enabled should be True pass def test_diagnostic_settings_imap_disabled(self): """Test imap_enabled when no IMAP hosts configured.""" from app.config import settings with patch.object(settings, "imap1_host", None): with patch.object(settings, "imap2_host", None): # imap_enabled should be False pass def test_diagnostic_settings_azure_requires_both_settings(self): """Test Azure configured only when both key and endpoint are set.""" from app.config import settings # Only key, no endpoint with patch.object(settings, "azure_ai_key", "key"): with patch.object(settings, "azure_endpoint", None): # azure should be False pass # Only endpoint, no key with patch.object(settings, "azure_ai_key", None): with patch.object(settings, "azure_endpoint", "https://test.com"): # azure should be False pass # Both set with patch.object(settings, "azure_ai_key", "key"): with patch.object(settings, "azure_endpoint", "https://test.com"): # azure should be True pass @pytest.mark.unit class TestTestNotification: """Tests for POST /diagnostic/test-notification endpoint.""" @patch("app.utils.notification.send_notification") def test_test_notification_success(self, mock_send): """Test successful notification send.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): with patch.object(settings, "external_hostname", "test-host"): # Should return success status # mock_send should be called with correct parameters pass @patch("app.utils.notification.send_notification") def test_test_notification_no_services_configured(self, mock_send): """Test notification when no services are configured.""" from app.config import settings with patch.object(settings, "notification_urls", []): # Should return warning status # mock_send should not be called pass @patch("app.utils.notification.send_notification") def test_test_notification_send_failure(self, mock_send): """Test notification when send fails.""" from app.config import settings mock_send.return_value = False with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Should return error status pass @patch("app.utils.notification.send_notification") def test_test_notification_exception(self, mock_send): """Test notification when exception occurs.""" from app.config import settings mock_send.side_effect = Exception("Notification error") with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Should return error status with exception message pass @patch("app.utils.notification.send_notification") def test_test_notification_includes_timestamp(self, mock_send): """Test that notification includes timestamp.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): with patch.object(settings, "external_hostname", "test-host"): # Notification message should include request_time pass @patch("app.utils.notification.send_notification") def test_test_notification_uses_external_hostname(self, mock_send): """Test that notification uses external_hostname in title.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): with patch.object(settings, "external_hostname", "my-custom-host"): # Title should include "my-custom-host" pass @patch("app.utils.notification.send_notification") def test_test_notification_fallback_hostname(self, mock_send): """Test notification when external_hostname is not set.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): with patch.object(settings, "external_hostname", None): # Should use fallback "Document Processor" pass @patch("app.utils.notification.send_notification") def test_test_notification_correct_tags(self, mock_send): """Test that notification includes correct tags.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Notification should have tags: ["test", "notification", "diagnostic"] pass @patch("app.utils.notification.send_notification") def test_test_notification_success_type(self, mock_send): """Test that notification type is 'success'.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # notification_type should be "success" pass @patch("app.utils.notification.send_notification") def test_test_notification_multiple_services(self, mock_send): """Test notification with multiple configured services.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test1", "https://ntfy.sh/test2"]): # Response should indicate 2 services pass @patch("app.utils.notification.send_notification") def test_test_notification_logs_success(self, mock_send): """Test that successful notification is logged.""" from app.config import settings mock_send.return_value = True with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Should log at INFO level pass @patch("app.utils.notification.send_notification") def test_test_notification_logs_failure(self, mock_send): """Test that failed notification is logged.""" from app.config import settings mock_send.return_value = False with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Should log at WARNING level pass @patch("app.utils.notification.send_notification") def test_test_notification_logs_exception(self, mock_send): """Test that exceptions are logged.""" from app.config import settings mock_send.side_effect = Exception("Test error") with patch.object(settings, "notification_urls", ["https://ntfy.sh/test"]): # Should log exception pass