From fb8769c5770f2374085364b8eee8f2ac1157d5cc Mon Sep 17 00:00:00 2001 From: busya Date: Thu, 23 Jul 2026 12:32:50 +0300 Subject: [PATCH] fix(tests): repair 39 broken tests after auth/authz hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clean Release API (33 tests): added get_current_user dependency override with mock admin user in _make_client — router now requires has_permission - WebSocket endpoints (23 tests): added _authorize_websocket mock alongside existing _authenticate_websocket mock — RBAC check was added before accept() - Lifespan (1 test): relaxed commit assert_called_once → assert_called — RBAC permission catalog sync also calls commit on the same mock All 7445 backend tests pass (0 failures). --- backend/tests/api/test_clean_release.py | 12 +++++++++++- backend/tests/api/test_clean_release_v2.py | 12 +++++++++++- backend/tests/test_app_lifespan.py | 2 +- backend/tests/test_app_ws_endpoint.py | 13 +++++++++++++ backend/tests/test_app_ws_events.py | 10 ++++++++++ 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/backend/tests/api/test_clean_release.py b/backend/tests/api/test_clean_release.py index 96555a2e0..64f98d73b 100644 --- a/backend/tests/api/test_clean_release.py +++ b/backend/tests/api/test_clean_release.py @@ -52,7 +52,7 @@ def _make_repo(overrides: dict | None = None) -> MagicMock: def _make_client(repo: MagicMock | None = None) -> TestClient: from src.api.routes.clean_release import router - from src.dependencies import get_clean_release_repository + from src.dependencies import get_clean_release_repository, get_current_user app = FastAPI() app.include_router(router) @@ -60,7 +60,17 @@ def _make_client(repo: MagicMock | None = None) -> TestClient: if repo is None: repo = _make_repo() + # Bypass auth: return a mock admin user (satisfies router-level has_permission) + mock_user = MagicMock() + mock_user.username = "test-user" + admin_role = MagicMock() + admin_role.is_admin = True + admin_role.name = "Admin" + admin_role.permissions = [] + mock_user.roles = [admin_role] + app.dependency_overrides[get_clean_release_repository] = lambda: repo + app.dependency_overrides[get_current_user] = lambda: mock_user return TestClient(app) diff --git a/backend/tests/api/test_clean_release_v2.py b/backend/tests/api/test_clean_release_v2.py index 79dccb209..44bdcbb8b 100644 --- a/backend/tests/api/test_clean_release_v2.py +++ b/backend/tests/api/test_clean_release_v2.py @@ -38,7 +38,7 @@ def _make_repo(overrides: dict | None = None) -> MagicMock: def _make_client(repo: MagicMock | None = None) -> TestClient: from src.api.routes.clean_release_v2 import router - from src.dependencies import get_clean_release_repository + from src.dependencies import get_clean_release_repository, get_current_user app = FastAPI() app.include_router(router) @@ -46,7 +46,17 @@ def _make_client(repo: MagicMock | None = None) -> TestClient: if repo is None: repo = _make_repo() + # Bypass auth: return a mock admin user (satisfies router-level has_permission) + mock_user = MagicMock() + mock_user.username = "test-user" + admin_role = MagicMock() + admin_role.is_admin = True + admin_role.name = "Admin" + admin_role.permissions = [] + mock_user.roles = [admin_role] + app.dependency_overrides[get_clean_release_repository] = lambda: repo + app.dependency_overrides[get_current_user] = lambda: mock_user return TestClient(app) diff --git a/backend/tests/test_app_lifespan.py b/backend/tests/test_app_lifespan.py index 9d90e4300..0af6b842a 100644 --- a/backend/tests/test_app_lifespan.py +++ b/backend/tests/test_app_lifespan.py @@ -206,7 +206,7 @@ class TestLifespan: ): async with lifespan(mock_app): pass - mock_db.commit.assert_called_once() + mock_db.commit.assert_called() # ≥1: RBAC permission sync also calls commit mock_db.close.assert_called() # #endregion Test.AppModule.TestLifespanStuckNoRuns diff --git a/backend/tests/test_app_ws_endpoint.py b/backend/tests/test_app_ws_endpoint.py index 1b622a3f0..40ccd68d3 100644 --- a/backend/tests/test_app_ws_endpoint.py +++ b/backend/tests/test_app_ws_endpoint.py @@ -81,6 +81,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[log]) @@ -105,6 +106,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[plug, super]) @@ -129,6 +131,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[dbg, inf]) @@ -153,6 +156,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[]) @@ -175,6 +179,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[]) @@ -198,6 +203,7 @@ class TestWebSocketEndpointFull: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[term]) @@ -252,6 +258,7 @@ class TestWebSocketMainLoopCoverage: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[]) @@ -290,6 +297,7 @@ class TestWebSocketMainLoopCoverage: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[]) @@ -319,6 +327,7 @@ class TestWebSocketMainLoopCoverage: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, patch("src.app.logger") as mock_logger, ): @@ -345,6 +354,7 @@ class TestWebSocketMainLoopExceptions: async def ss(t): return sq with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = _make_task_manager_mock(task=task, logs=[]) @@ -365,6 +375,7 @@ class TestWebSocketMainLoopExceptions: async def sub(): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_task_events = sub; tm.unsubscribe_task_events = MagicMock() @@ -384,6 +395,7 @@ class TestWebSocketMainLoopExceptions: async def sub(): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_maintenance_events = sub; tm.unsubscribe_maintenance_events = MagicMock() @@ -403,6 +415,7 @@ class TestWebSocketMainLoopExceptions: async def sub(e): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_dataset_events = sub; tm.unsubscribe_dataset_events = MagicMock() diff --git a/backend/tests/test_app_ws_events.py b/backend/tests/test_app_ws_events.py index 794694cd6..db5366c06 100644 --- a/backend/tests/test_app_ws_events.py +++ b/backend/tests/test_app_ws_events.py @@ -40,6 +40,7 @@ class TestTaskEventsWebSocket: async def sub(): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_task_events = sub; tm.unsubscribe_task_events = MagicMock() @@ -77,6 +78,7 @@ class TestMaintenanceEventsWebSocket: async def sub(): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_maintenance_events = sub; tm.unsubscribe_maintenance_events = MagicMock() @@ -115,6 +117,7 @@ class TestDatasetWebSocket: async def sub(e): return q with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.app.get_task_manager") as mg, ): tm = MagicMock(); tm.subscribe_dataset_events = sub; tm.unsubscribe_dataset_events = MagicMock() @@ -146,6 +149,7 @@ class TestTranslateRunWebSocket: ws.send_json = AsyncMock(); ws.accept = AsyncMock() with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"), @@ -173,6 +177,7 @@ class TestTranslateRunWebSocket: ws.send_json = AsyncMock(); ws.accept = AsyncMock() with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal", side_effect=Exception("DB failed")), ): await translate_run_websocket(ws, "run-1") @@ -193,6 +198,7 @@ class TestTranslateRunWebSocket: with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"), @@ -229,6 +235,7 @@ class TestTranslateRunWebSocket: with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"), @@ -260,6 +267,7 @@ class TestTranslateRunWebSocket: with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"), @@ -290,6 +298,7 @@ class TestTranslateRunWebSocket: with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"), @@ -318,6 +327,7 @@ class TestTranslateRunWebSocket: with ( patch("src.app._authenticate_websocket", return_value=True), + patch("src.app._authorize_websocket", return_value=True), patch("src.core.database.SessionLocal") as msl, patch("src.plugins.translate.orchestrator_aggregator.TranslationResultAggregator") as ma, patch("src.plugins.translate.events.TranslationEventLog"),