From c03ce8cdb2e7849361ea50db888b7e3080eaafcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 21:32:44 +0000 Subject: [PATCH] test(main,imap): fix failing IMAP tests and add coverage for shutdown exception paths Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/DocuElevate/sessions/05be730d-fcbd-43a5-98be-26d853cf57d0 --- tests/test_api_imap_accounts.py | 15 ++++++++-- tests/test_main.py | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/tests/test_api_imap_accounts.py b/tests/test_api_imap_accounts.py index d872547b..90381cdc 100644 --- a/tests/test_api_imap_accounts.py +++ b/tests/test_api_imap_accounts.py @@ -524,7 +524,10 @@ class TestTestImapConnection: from app.api.imap_accounts import _test_imap_connection mock_mail = MagicMock() - with patch("imaplib.IMAP4_SSL", return_value=mock_mail): + with ( + patch("app.api.imap_accounts.is_private_ip", return_value=False), + patch("imaplib.IMAP4_SSL", return_value=mock_mail), + ): result = _test_imap_connection( "imap.example.com", 993, @@ -541,7 +544,10 @@ class TestTestImapConnection: """An exception raised by IMAP4_SSL returns success=False.""" from app.api.imap_accounts import _test_imap_connection - with patch("imaplib.IMAP4_SSL", side_effect=Exception("auth failed")): + with ( + patch("app.api.imap_accounts.is_private_ip", return_value=False), + patch("imaplib.IMAP4_SSL", side_effect=Exception("auth failed")), + ): result = _test_imap_connection( "imap.example.com", 993, @@ -557,7 +563,10 @@ class TestTestImapConnection: """An OSError returns success=False with a network error message.""" from app.api.imap_accounts import _test_imap_connection - with patch("imaplib.IMAP4", side_effect=OSError("connection refused")): + with ( + patch("app.api.imap_accounts.is_private_ip", return_value=False), + patch("imaplib.IMAP4", side_effect=OSError("connection refused")), + ): result = _test_imap_connection( "bad-host", 143, diff --git a/tests/test_main.py b/tests/test_main.py index b59c2c93..015c499f 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -145,6 +145,58 @@ class TestLifespanEvents: # load_settings_from_db must also have been called mock_load_settings.assert_called_once() + @pytest.mark.asyncio + async def test_lifespan_shutdown_logging_exception_is_silenced(self): + """Exceptions raised by logging.info during shutdown are silently ignored.""" + + def _raise_on_shutdown(msg, *args, **kwargs): + if "shutting down" in str(msg): + raise OSError("stream closed") + + with ( + patch("app.database.init_db"), + patch("app.database.SessionLocal") as mock_session_cls, + patch("app.utils.config_loader.load_settings_from_db"), + patch("app.utils.config_validator.dump_all_settings"), + patch("app.utils.config_validator.check_all_configs", return_value={"email": [], "storage": {}}), + patch("app.utils.notification.init_apprise"), + patch("app.utils.notification.notify_startup"), + patch("app.utils.notification.notify_shutdown"), + patch("app.main.init_sentry"), + patch("app.main.logging.info", side_effect=_raise_on_shutdown), + ): + mock_db = MagicMock() + mock_session_cls.return_value = mock_db + + from app.main import app, lifespan + + # Should complete without raising despite the logging error + async with lifespan(app): + pass + + @pytest.mark.asyncio + async def test_lifespan_shutdown_notify_exception_is_silenced(self): + """Exceptions raised by notify_shutdown during shutdown are silently ignored.""" + with ( + patch("app.database.init_db"), + patch("app.database.SessionLocal") as mock_session_cls, + patch("app.utils.config_loader.load_settings_from_db"), + patch("app.utils.config_validator.dump_all_settings"), + patch("app.utils.config_validator.check_all_configs", return_value={"email": [], "storage": {}}), + patch("app.utils.notification.init_apprise"), + patch("app.utils.notification.notify_startup"), + patch("app.main.notify_shutdown", side_effect=OSError("stream closed")), + patch("app.main.init_sentry"), + ): + mock_db = MagicMock() + mock_session_cls.return_value = mock_db + + from app.main import app, lifespan + + # Should complete without raising despite the notify_shutdown error + async with lifespan(app): + pass + @pytest.mark.unit class TestExceptionHandlers: