mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
165 lines
4.6 KiB
Python
165 lines
4.6 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import time
|
||
|
|
from types import SimpleNamespace
|
||
|
|
from typing import Any, cast
|
||
|
|
from unittest.mock import AsyncMock
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
from starlette.requests import Request
|
||
|
|
|
||
|
|
import src.database as database_module
|
||
|
|
from src.middleware.plugin_middleware import PluginMiddleware
|
||
|
|
from src.modules.notification_email import notification_email_module
|
||
|
|
from src.services.system.config import SystemConfigService
|
||
|
|
|
||
|
|
|
||
|
|
def _build_request(path: str = "/boom") -> Request:
|
||
|
|
scope = {
|
||
|
|
"type": "http",
|
||
|
|
"asgi": {"version": "3.0"},
|
||
|
|
"http_version": "1.1",
|
||
|
|
"method": "GET",
|
||
|
|
"scheme": "http",
|
||
|
|
"path": path,
|
||
|
|
"raw_path": path.encode("utf-8"),
|
||
|
|
"query_string": b"",
|
||
|
|
"headers": [],
|
||
|
|
"client": ("127.0.0.1", 12345),
|
||
|
|
"server": ("testserver", 80),
|
||
|
|
}
|
||
|
|
return Request(scope)
|
||
|
|
|
||
|
|
|
||
|
|
async def _dummy_app(scope, receive, send): # type: ignore[no-untyped-def]
|
||
|
|
return
|
||
|
|
|
||
|
|
|
||
|
|
def test_notification_email_module_validate_config_requires_smtp(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"src.services.email.email_sender.EmailSenderService.is_smtp_configured",
|
||
|
|
lambda _db: False,
|
||
|
|
)
|
||
|
|
assert notification_email_module.validate_config is not None
|
||
|
|
ok, message = notification_email_module.validate_config(object()) # type: ignore[arg-type]
|
||
|
|
assert ok is False
|
||
|
|
assert "SMTP" in message
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_call_error_plugins_skips_when_module_disabled(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
middleware = PluginMiddleware(_dummy_app)
|
||
|
|
send_error = AsyncMock()
|
||
|
|
middleware.plugin_manager = cast(
|
||
|
|
Any,
|
||
|
|
SimpleNamespace(
|
||
|
|
get_plugin=lambda *_args, **_kwargs: SimpleNamespace(
|
||
|
|
enabled=True, send_error=send_error
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
middleware,
|
||
|
|
"_is_notification_email_module_enabled",
|
||
|
|
AsyncMock(return_value=False),
|
||
|
|
)
|
||
|
|
|
||
|
|
await middleware._call_error_plugins(
|
||
|
|
_build_request(),
|
||
|
|
RuntimeError("boom"),
|
||
|
|
start_time=time.time(),
|
||
|
|
)
|
||
|
|
|
||
|
|
send_error.assert_not_awaited()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_call_error_plugins_sends_when_module_enabled(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
middleware = PluginMiddleware(_dummy_app)
|
||
|
|
send_error = AsyncMock()
|
||
|
|
middleware.plugin_manager = cast(
|
||
|
|
Any,
|
||
|
|
SimpleNamespace(
|
||
|
|
get_plugin=lambda *_args, **_kwargs: SimpleNamespace(
|
||
|
|
enabled=True, send_error=send_error
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
middleware,
|
||
|
|
"_is_notification_email_module_enabled",
|
||
|
|
AsyncMock(return_value=True),
|
||
|
|
)
|
||
|
|
request = _build_request("/internal-error")
|
||
|
|
request.state.request_id = "req-1"
|
||
|
|
|
||
|
|
await middleware._call_error_plugins(
|
||
|
|
request,
|
||
|
|
RuntimeError("boom"),
|
||
|
|
start_time=time.time(),
|
||
|
|
)
|
||
|
|
|
||
|
|
send_error.assert_awaited_once()
|
||
|
|
assert send_error.await_args is not None
|
||
|
|
kwargs = send_error.await_args.kwargs
|
||
|
|
assert kwargs["context"]["endpoint"] == "GET /internal-error"
|
||
|
|
assert kwargs["context"]["request_id"] == "req-1"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_notification_email_switch_reads_from_system_config(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
middleware = PluginMiddleware(_dummy_app)
|
||
|
|
request = _build_request("/x")
|
||
|
|
|
||
|
|
class _FakeDb:
|
||
|
|
closed = False
|
||
|
|
|
||
|
|
def close(self) -> None:
|
||
|
|
self.closed = True
|
||
|
|
|
||
|
|
fake_db = _FakeDb()
|
||
|
|
monkeypatch.setattr(database_module, "create_session", lambda: fake_db)
|
||
|
|
monkeypatch.setattr(SystemConfigService, "get_config", lambda *_args, **_kwargs: True)
|
||
|
|
|
||
|
|
enabled = await middleware._is_notification_email_module_enabled(request)
|
||
|
|
assert enabled is True
|
||
|
|
assert fake_db.closed is True
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_call_error_plugins_ignores_http_4xx(
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
middleware = PluginMiddleware(_dummy_app)
|
||
|
|
send_error = AsyncMock()
|
||
|
|
middleware.plugin_manager = cast(
|
||
|
|
Any,
|
||
|
|
SimpleNamespace(
|
||
|
|
get_plugin=lambda *_args, **_kwargs: SimpleNamespace(
|
||
|
|
enabled=True, send_error=send_error
|
||
|
|
)
|
||
|
|
),
|
||
|
|
)
|
||
|
|
monkeypatch.setattr(
|
||
|
|
middleware,
|
||
|
|
"_is_notification_email_module_enabled",
|
||
|
|
AsyncMock(return_value=True),
|
||
|
|
)
|
||
|
|
|
||
|
|
await middleware._call_error_plugins(
|
||
|
|
_build_request(),
|
||
|
|
HTTPException(status_code=400, detail="bad request"),
|
||
|
|
start_time=time.time(),
|
||
|
|
)
|
||
|
|
|
||
|
|
send_error.assert_not_awaited()
|