From 4d09c8641cfb0df6fd7faf6a8012b719e6717998 Mon Sep 17 00:00:00 2001 From: Nikolay Tatarinov Date: Fri, 6 Mar 2026 18:08:45 +0300 Subject: [PATCH] test: enhance admin API tests with database session mocking - Introduced a mock database session for admin API tests to prevent real database access during CI. - Updated test cases for the `/api/admin/me` endpoint to ensure consistent behavior without a real database. - Improved error handling and clarity in tests by utilizing dependency overrides for session management. - Ensured proper cleanup of dependency overrides after tests to maintain test isolation. --- tests/test_admin_api.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/tests/test_admin_api.py b/tests/test_admin_api.py index 7a78f72..514055c 100644 --- a/tests/test_admin_api.py +++ b/tests/test_admin_api.py @@ -4,8 +4,10 @@ from unittest.mock import ANY, MagicMock, patch import pytest from fastapi.testclient import TestClient +from sqlalchemy.orm import Session from duty_teller.api.app import app +from duty_teller.api.dependencies import get_db_session @pytest.fixture @@ -13,15 +15,31 @@ def client(): return TestClient(app) +def _override_get_db_session(mock_session: MagicMock): + """Dependency override that returns mock_session (no real DB). Used as get_db_session override.""" + + def _override() -> Session: + return mock_session + + return _override + + # --- GET /api/admin/me --- @patch("duty_teller.api.dependencies.config.MINI_APP_SKIP_AUTH", True) def test_admin_me_skip_auth_returns_is_admin_false(client): """With MINI_APP_SKIP_AUTH, GET /api/admin/me returns is_admin: false (no real user).""" - r = client.get("/api/admin/me") - assert r.status_code == 200 - assert r.json() == {"is_admin": False} + # Override get_db_session so the endpoint does not open the real DB (CI has no data/ dir). + mock_session = MagicMock() + mock_session.query.return_value.filter.return_value.first.return_value = None + app.dependency_overrides[get_db_session] = _override_get_db_session(mock_session) + try: + r = client.get("/api/admin/me") + assert r.status_code == 200 + assert r.json() == {"is_admin": False} + finally: + app.dependency_overrides.pop(get_db_session, None) @patch("duty_teller.api.app.is_admin_for_telegram_user") @@ -245,9 +263,8 @@ def test_admin_reassign_400_when_user_not_found( ) mock_session = MagicMock() mock_session.get.return_value = None # User not found - with patch("duty_teller.api.app.get_db_session") as mock_db: - mock_db.return_value.__enter__.return_value = mock_session - mock_db.return_value.__exit__.return_value = None + app.dependency_overrides[get_db_session] = _override_get_db_session(mock_session) + try: r = client.patch( "/api/admin/duties/1", json={"user_id": 999}, @@ -255,6 +272,8 @@ def test_admin_reassign_400_when_user_not_found( "X-Telegram-Init-Data": "auth_date=1&user=%7B%22id%22%3A1%7D&hash=x" }, ) + finally: + app.dependency_overrides.pop(get_db_session, None) assert r.status_code == 400 mock_update.assert_not_called() mock_invalidate.assert_not_called() @@ -301,9 +320,8 @@ def test_admin_reassign_200_updates_and_invalidates( mock_update_duty_user.return_value = updated_duty mock_session = MagicMock() mock_session.get.return_value = SimpleNamespace(id=2) # User exists - with patch("duty_teller.api.app.get_db_session") as mock_db: - mock_db.return_value.__enter__.return_value = mock_session - mock_db.return_value.__exit__.return_value = None + app.dependency_overrides[get_db_session] = _override_get_db_session(mock_session) + try: r = client.patch( "/api/admin/duties/1", json={"user_id": 2}, @@ -311,6 +329,8 @@ def test_admin_reassign_200_updates_and_invalidates( "X-Telegram-Init-Data": "auth_date=1&user=%7B%22id%22%3A1%7D&hash=x" }, ) + finally: + app.dependency_overrides.pop(get_db_session, None) assert r.status_code == 200 data = r.json() assert data["id"] == 1