From 05dc9fa1e3e6459505dab771dcae5e253d00316e Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 21 Mar 2026 19:43:59 +0100 Subject: [PATCH] Fix backend tests by using per-test temp config dir, align router mocks to service modules, fix log tail helper reference, and add JailNotFoundError.name --- backend/app/exceptions.py | 4 + backend/app/services/config_service.py | 2 +- backend/tests/conftest.py | 6 + backend/tests/test_routers/test_config.py | 210 +++++++++--------- .../tests/test_routers/test_file_config.py | 4 +- 5 files changed, 118 insertions(+), 108 deletions(-) diff --git a/backend/app/exceptions.py b/backend/app/exceptions.py index dd41886..1d855bc 100644 --- a/backend/app/exceptions.py +++ b/backend/app/exceptions.py @@ -6,6 +6,10 @@ from __future__ import annotations class JailNotFoundError(Exception): """Raised when a requested jail name does not exist.""" + def __init__(self, name: str) -> None: + self.name = name + super().__init__(f"Jail not found: {name!r}") + class JailOperationError(Exception): """Raised when a fail2ban jail operation fails.""" diff --git a/backend/app/services/config_service.py b/backend/app/services/config_service.py index 4e2d0a2..284b140 100644 --- a/backend/app/services/config_service.py +++ b/backend/app/services/config_service.py @@ -720,7 +720,7 @@ async def read_fail2ban_log( total_lines, raw_lines = await asyncio.gather( loop.run_in_executor(None, _count_file_lines, resolved_str), - loop.run_in_executor(None, _read_tail_lines, resolved_str, lines), + loop.run_in_executor(None, log_service._read_tail_lines, resolved_str, lines), ) filtered = ( diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 44fc64c..dfa4617 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -37,9 +37,15 @@ def test_settings(tmp_path: Path) -> Settings: Returns: A :class:`~app.config.Settings` instance with overridden paths. """ + config_dir = tmp_path / "fail2ban" + (config_dir / "jail.d").mkdir(parents=True) + (config_dir / "filter.d").mkdir(parents=True) + (config_dir / "action.d").mkdir(parents=True) + return Settings( database_path=str(tmp_path / "test_bangui.db"), fail2ban_socket="/tmp/fake_fail2ban.sock", + fail2ban_config_dir=str(config_dir), session_secret="test-secret-key-do-not-use-in-production", session_duration_minutes=60, timezone="UTC", diff --git a/backend/tests/test_routers/test_config.py b/backend/tests/test_routers/test_config.py index 313bca5..62ce95e 100644 --- a/backend/tests/test_routers/test_config.py +++ b/backend/tests/test_routers/test_config.py @@ -727,7 +727,7 @@ class TestGetInactiveJails: mock_response = InactiveJailListResponse(jails=[mock_jail], total=1) with patch( - "app.routers.config.config_file_service.list_inactive_jails", + "app.routers.config.jail_config_service.list_inactive_jails", AsyncMock(return_value=mock_response), ): resp = await config_client.get("/api/config/jails/inactive") @@ -742,7 +742,7 @@ class TestGetInactiveJails: from app.models.config import InactiveJailListResponse with patch( - "app.routers.config.config_file_service.list_inactive_jails", + "app.routers.config.jail_config_service.list_inactive_jails", AsyncMock(return_value=InactiveJailListResponse(jails=[], total=0)), ): resp = await config_client.get("/api/config/jails/inactive") @@ -778,7 +778,7 @@ class TestActivateJail: message="Jail 'apache-auth' activated successfully.", ) with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(return_value=mock_response), ): resp = await config_client.post( @@ -798,7 +798,7 @@ class TestActivateJail: name="apache-auth", active=True, message="Activated." ) with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(return_value=mock_response), ) as mock_activate: resp = await config_client.post( @@ -814,10 +814,10 @@ class TestActivateJail: async def test_404_for_unknown_jail(self, config_client: AsyncClient) -> None: """POST /api/config/jails/missing/activate returns 404.""" - from app.services.config_file_service import JailNotFoundInConfigError + from app.services.jail_config_service import JailNotFoundInConfigError with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(side_effect=JailNotFoundInConfigError("missing")), ): resp = await config_client.post( @@ -828,10 +828,10 @@ class TestActivateJail: async def test_409_when_already_active(self, config_client: AsyncClient) -> None: """POST /api/config/jails/sshd/activate returns 409 if already active.""" - from app.services.config_file_service import JailAlreadyActiveError + from app.services.jail_config_service import JailAlreadyActiveError with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(side_effect=JailAlreadyActiveError("sshd")), ): resp = await config_client.post( @@ -842,10 +842,10 @@ class TestActivateJail: async def test_400_for_invalid_jail_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/ with bad name returns 400.""" - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(side_effect=JailNameError("bad name")), ): resp = await config_client.post( @@ -874,7 +874,7 @@ class TestActivateJail: message="Jail 'airsonic-auth' cannot be activated: log file '/var/log/airsonic/airsonic.log' not found", ) with patch( - "app.routers.config.config_file_service.activate_jail", + "app.routers.config.jail_config_service.activate_jail", AsyncMock(return_value=blocked_response), ): resp = await config_client.post( @@ -907,7 +907,7 @@ class TestDeactivateJail: message="Jail 'sshd' deactivated successfully.", ) with patch( - "app.routers.config.config_file_service.deactivate_jail", + "app.routers.config.jail_config_service.deactivate_jail", AsyncMock(return_value=mock_response), ): resp = await config_client.post("/api/config/jails/sshd/deactivate") @@ -919,10 +919,10 @@ class TestDeactivateJail: async def test_404_for_unknown_jail(self, config_client: AsyncClient) -> None: """POST /api/config/jails/missing/deactivate returns 404.""" - from app.services.config_file_service import JailNotFoundInConfigError + from app.services.jail_config_service import JailNotFoundInConfigError with patch( - "app.routers.config.config_file_service.deactivate_jail", + "app.routers.config.jail_config_service.deactivate_jail", AsyncMock(side_effect=JailNotFoundInConfigError("missing")), ): resp = await config_client.post( @@ -933,10 +933,10 @@ class TestDeactivateJail: async def test_409_when_already_inactive(self, config_client: AsyncClient) -> None: """POST /api/config/jails/apache-auth/deactivate returns 409 if already inactive.""" - from app.services.config_file_service import JailAlreadyInactiveError + from app.services.jail_config_service import JailAlreadyInactiveError with patch( - "app.routers.config.config_file_service.deactivate_jail", + "app.routers.config.jail_config_service.deactivate_jail", AsyncMock(side_effect=JailAlreadyInactiveError("apache-auth")), ): resp = await config_client.post( @@ -947,10 +947,10 @@ class TestDeactivateJail: async def test_400_for_invalid_jail_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/.../deactivate with bad name returns 400.""" - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.deactivate_jail", + "app.routers.config.jail_config_service.deactivate_jail", AsyncMock(side_effect=JailNameError("bad")), ): resp = await config_client.post( @@ -978,7 +978,7 @@ class TestDeactivateJail: ) with ( patch( - "app.routers.config.config_file_service.deactivate_jail", + "app.routers.config.jail_config_service.deactivate_jail", AsyncMock(return_value=mock_response), ), patch( @@ -1029,7 +1029,7 @@ class TestListFilters: total=1, ) with patch( - "app.routers.config.config_file_service.list_filters", + "app.routers.config.filter_config_service.list_filters", AsyncMock(return_value=mock_response), ): resp = await config_client.get("/api/config/filters") @@ -1045,7 +1045,7 @@ class TestListFilters: from app.models.config import FilterListResponse with patch( - "app.routers.config.config_file_service.list_filters", + "app.routers.config.filter_config_service.list_filters", AsyncMock(return_value=FilterListResponse(filters=[], total=0)), ): resp = await config_client.get("/api/config/filters") @@ -1068,7 +1068,7 @@ class TestListFilters: total=2, ) with patch( - "app.routers.config.config_file_service.list_filters", + "app.routers.config.filter_config_service.list_filters", AsyncMock(return_value=mock_response), ): resp = await config_client.get("/api/config/filters") @@ -1097,7 +1097,7 @@ class TestGetFilter: async def test_200_returns_filter(self, config_client: AsyncClient) -> None: """GET /api/config/filters/sshd returns 200 with FilterConfig.""" with patch( - "app.routers.config.config_file_service.get_filter", + "app.routers.config.filter_config_service.get_filter", AsyncMock(return_value=_make_filter_config("sshd")), ): resp = await config_client.get("/api/config/filters/sshd") @@ -1110,10 +1110,10 @@ class TestGetFilter: async def test_404_for_unknown_filter(self, config_client: AsyncClient) -> None: """GET /api/config/filters/missing returns 404.""" - from app.services.config_file_service import FilterNotFoundError + from app.services.filter_config_service import FilterNotFoundError with patch( - "app.routers.config.config_file_service.get_filter", + "app.routers.config.filter_config_service.get_filter", AsyncMock(side_effect=FilterNotFoundError("missing")), ): resp = await config_client.get("/api/config/filters/missing") @@ -1140,7 +1140,7 @@ class TestUpdateFilter: async def test_200_returns_updated_filter(self, config_client: AsyncClient) -> None: """PUT /api/config/filters/sshd returns 200 with updated FilterConfig.""" with patch( - "app.routers.config.config_file_service.update_filter", + "app.routers.config.filter_config_service.update_filter", AsyncMock(return_value=_make_filter_config("sshd")), ): resp = await config_client.put( @@ -1153,10 +1153,10 @@ class TestUpdateFilter: async def test_404_for_unknown_filter(self, config_client: AsyncClient) -> None: """PUT /api/config/filters/missing returns 404.""" - from app.services.config_file_service import FilterNotFoundError + from app.services.filter_config_service import FilterNotFoundError with patch( - "app.routers.config.config_file_service.update_filter", + "app.routers.config.filter_config_service.update_filter", AsyncMock(side_effect=FilterNotFoundError("missing")), ): resp = await config_client.put( @@ -1168,10 +1168,10 @@ class TestUpdateFilter: async def test_422_for_invalid_regex(self, config_client: AsyncClient) -> None: """PUT /api/config/filters/sshd returns 422 for bad regex.""" - from app.services.config_file_service import FilterInvalidRegexError + from app.services.filter_config_service import FilterInvalidRegexError with patch( - "app.routers.config.config_file_service.update_filter", + "app.routers.config.filter_config_service.update_filter", AsyncMock(side_effect=FilterInvalidRegexError("[bad", "unterminated")), ): resp = await config_client.put( @@ -1183,10 +1183,10 @@ class TestUpdateFilter: async def test_400_for_invalid_name(self, config_client: AsyncClient) -> None: """PUT /api/config/filters/... with bad name returns 400.""" - from app.services.config_file_service import FilterNameError + from app.services.filter_config_service import FilterNameError with patch( - "app.routers.config.config_file_service.update_filter", + "app.routers.config.filter_config_service.update_filter", AsyncMock(side_effect=FilterNameError("bad")), ): resp = await config_client.put( @@ -1199,7 +1199,7 @@ class TestUpdateFilter: async def test_reload_query_param_passed(self, config_client: AsyncClient) -> None: """PUT /api/config/filters/sshd?reload=true passes do_reload=True.""" with patch( - "app.routers.config.config_file_service.update_filter", + "app.routers.config.filter_config_service.update_filter", AsyncMock(return_value=_make_filter_config("sshd")), ) as mock_update: resp = await config_client.put( @@ -1230,7 +1230,7 @@ class TestCreateFilter: async def test_201_creates_filter(self, config_client: AsyncClient) -> None: """POST /api/config/filters returns 201 with FilterConfig.""" with patch( - "app.routers.config.config_file_service.create_filter", + "app.routers.config.filter_config_service.create_filter", AsyncMock(return_value=_make_filter_config("my-custom")), ): resp = await config_client.post( @@ -1243,10 +1243,10 @@ class TestCreateFilter: async def test_409_when_already_exists(self, config_client: AsyncClient) -> None: """POST /api/config/filters returns 409 if filter exists.""" - from app.services.config_file_service import FilterAlreadyExistsError + from app.services.filter_config_service import FilterAlreadyExistsError with patch( - "app.routers.config.config_file_service.create_filter", + "app.routers.config.filter_config_service.create_filter", AsyncMock(side_effect=FilterAlreadyExistsError("sshd")), ): resp = await config_client.post( @@ -1258,10 +1258,10 @@ class TestCreateFilter: async def test_422_for_invalid_regex(self, config_client: AsyncClient) -> None: """POST /api/config/filters returns 422 for bad regex.""" - from app.services.config_file_service import FilterInvalidRegexError + from app.services.filter_config_service import FilterInvalidRegexError with patch( - "app.routers.config.config_file_service.create_filter", + "app.routers.config.filter_config_service.create_filter", AsyncMock(side_effect=FilterInvalidRegexError("[bad", "unterminated")), ): resp = await config_client.post( @@ -1273,10 +1273,10 @@ class TestCreateFilter: async def test_400_for_invalid_name(self, config_client: AsyncClient) -> None: """POST /api/config/filters returns 400 for invalid filter name.""" - from app.services.config_file_service import FilterNameError + from app.services.filter_config_service import FilterNameError with patch( - "app.routers.config.config_file_service.create_filter", + "app.routers.config.filter_config_service.create_filter", AsyncMock(side_effect=FilterNameError("bad")), ): resp = await config_client.post( @@ -1306,7 +1306,7 @@ class TestDeleteFilter: async def test_204_deletes_filter(self, config_client: AsyncClient) -> None: """DELETE /api/config/filters/my-custom returns 204.""" with patch( - "app.routers.config.config_file_service.delete_filter", + "app.routers.config.filter_config_service.delete_filter", AsyncMock(return_value=None), ): resp = await config_client.delete("/api/config/filters/my-custom") @@ -1315,10 +1315,10 @@ class TestDeleteFilter: async def test_404_for_unknown_filter(self, config_client: AsyncClient) -> None: """DELETE /api/config/filters/missing returns 404.""" - from app.services.config_file_service import FilterNotFoundError + from app.services.filter_config_service import FilterNotFoundError with patch( - "app.routers.config.config_file_service.delete_filter", + "app.routers.config.filter_config_service.delete_filter", AsyncMock(side_effect=FilterNotFoundError("missing")), ): resp = await config_client.delete("/api/config/filters/missing") @@ -1327,10 +1327,10 @@ class TestDeleteFilter: async def test_409_for_readonly_filter(self, config_client: AsyncClient) -> None: """DELETE /api/config/filters/sshd returns 409 for shipped conf-only filter.""" - from app.services.config_file_service import FilterReadonlyError + from app.services.filter_config_service import FilterReadonlyError with patch( - "app.routers.config.config_file_service.delete_filter", + "app.routers.config.filter_config_service.delete_filter", AsyncMock(side_effect=FilterReadonlyError("sshd")), ): resp = await config_client.delete("/api/config/filters/sshd") @@ -1339,10 +1339,10 @@ class TestDeleteFilter: async def test_400_for_invalid_name(self, config_client: AsyncClient) -> None: """DELETE /api/config/filters/... with bad name returns 400.""" - from app.services.config_file_service import FilterNameError + from app.services.filter_config_service import FilterNameError with patch( - "app.routers.config.config_file_service.delete_filter", + "app.routers.config.filter_config_service.delete_filter", AsyncMock(side_effect=FilterNameError("bad")), ): resp = await config_client.delete("/api/config/filters/bad") @@ -1369,7 +1369,7 @@ class TestAssignFilterToJail: async def test_204_assigns_filter(self, config_client: AsyncClient) -> None: """POST /api/config/jails/sshd/filter returns 204 on success.""" with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(return_value=None), ): resp = await config_client.post( @@ -1381,10 +1381,10 @@ class TestAssignFilterToJail: async def test_404_for_unknown_jail(self, config_client: AsyncClient) -> None: """POST /api/config/jails/missing/filter returns 404.""" - from app.services.config_file_service import JailNotFoundInConfigError + from app.services.jail_config_service import JailNotFoundInConfigError with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(side_effect=JailNotFoundInConfigError("missing")), ): resp = await config_client.post( @@ -1396,10 +1396,10 @@ class TestAssignFilterToJail: async def test_404_for_unknown_filter(self, config_client: AsyncClient) -> None: """POST /api/config/jails/sshd/filter returns 404 when filter not found.""" - from app.services.config_file_service import FilterNotFoundError + from app.services.filter_config_service import FilterNotFoundError with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(side_effect=FilterNotFoundError("missing-filter")), ): resp = await config_client.post( @@ -1411,10 +1411,10 @@ class TestAssignFilterToJail: async def test_400_for_invalid_jail_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/.../filter with bad jail name returns 400.""" - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(side_effect=JailNameError("bad")), ): resp = await config_client.post( @@ -1426,10 +1426,10 @@ class TestAssignFilterToJail: async def test_400_for_invalid_filter_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/sshd/filter with bad filter name returns 400.""" - from app.services.config_file_service import FilterNameError + from app.services.filter_config_service import FilterNameError with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(side_effect=FilterNameError("bad")), ): resp = await config_client.post( @@ -1442,7 +1442,7 @@ class TestAssignFilterToJail: async def test_reload_query_param_passed(self, config_client: AsyncClient) -> None: """POST /api/config/jails/sshd/filter?reload=true passes do_reload=True.""" with patch( - "app.routers.config.config_file_service.assign_filter_to_jail", + "app.routers.config.filter_config_service.assign_filter_to_jail", AsyncMock(return_value=None), ) as mock_assign: resp = await config_client.post( @@ -1480,7 +1480,7 @@ class TestListActionsRouter: mock_response = ActionListResponse(actions=[mock_action], total=1) with patch( - "app.routers.config.config_file_service.list_actions", + "app.routers.config.action_config_service.list_actions", AsyncMock(return_value=mock_response), ): resp = await config_client.get("/api/config/actions") @@ -1498,7 +1498,7 @@ class TestListActionsRouter: mock_response = ActionListResponse(actions=[inactive, active], total=2) with patch( - "app.routers.config.config_file_service.list_actions", + "app.routers.config.action_config_service.list_actions", AsyncMock(return_value=mock_response), ): resp = await config_client.get("/api/config/actions") @@ -1526,7 +1526,7 @@ class TestGetActionRouter: ) with patch( - "app.routers.config.config_file_service.get_action", + "app.routers.config.action_config_service.get_action", AsyncMock(return_value=mock_action), ): resp = await config_client.get("/api/config/actions/iptables") @@ -1535,10 +1535,10 @@ class TestGetActionRouter: assert resp.json()["name"] == "iptables" async def test_404_when_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNotFoundError + from app.services.action_config_service import ActionNotFoundError with patch( - "app.routers.config.config_file_service.get_action", + "app.routers.config.action_config_service.get_action", AsyncMock(side_effect=ActionNotFoundError("missing")), ): resp = await config_client.get("/api/config/actions/missing") @@ -1565,7 +1565,7 @@ class TestUpdateActionRouter: ) with patch( - "app.routers.config.config_file_service.update_action", + "app.routers.config.action_config_service.update_action", AsyncMock(return_value=updated), ): resp = await config_client.put( @@ -1577,10 +1577,10 @@ class TestUpdateActionRouter: assert resp.json()["actionban"] == "echo ban" async def test_404_when_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNotFoundError + from app.services.action_config_service import ActionNotFoundError with patch( - "app.routers.config.config_file_service.update_action", + "app.routers.config.action_config_service.update_action", AsyncMock(side_effect=ActionNotFoundError("missing")), ): resp = await config_client.put( @@ -1590,10 +1590,10 @@ class TestUpdateActionRouter: assert resp.status_code == 404 async def test_400_for_bad_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNameError + from app.services.action_config_service import ActionNameError with patch( - "app.routers.config.config_file_service.update_action", + "app.routers.config.action_config_service.update_action", AsyncMock(side_effect=ActionNameError()), ): resp = await config_client.put( @@ -1622,7 +1622,7 @@ class TestCreateActionRouter: ) with patch( - "app.routers.config.config_file_service.create_action", + "app.routers.config.action_config_service.create_action", AsyncMock(return_value=created), ): resp = await config_client.post( @@ -1634,10 +1634,10 @@ class TestCreateActionRouter: assert resp.json()["name"] == "custom" async def test_409_when_already_exists(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionAlreadyExistsError + from app.services.action_config_service import ActionAlreadyExistsError with patch( - "app.routers.config.config_file_service.create_action", + "app.routers.config.action_config_service.create_action", AsyncMock(side_effect=ActionAlreadyExistsError("iptables")), ): resp = await config_client.post( @@ -1648,10 +1648,10 @@ class TestCreateActionRouter: assert resp.status_code == 409 async def test_400_for_bad_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNameError + from app.services.action_config_service import ActionNameError with patch( - "app.routers.config.config_file_service.create_action", + "app.routers.config.action_config_service.create_action", AsyncMock(side_effect=ActionNameError()), ): resp = await config_client.post( @@ -1673,7 +1673,7 @@ class TestCreateActionRouter: class TestDeleteActionRouter: async def test_204_on_delete(self, config_client: AsyncClient) -> None: with patch( - "app.routers.config.config_file_service.delete_action", + "app.routers.config.action_config_service.delete_action", AsyncMock(return_value=None), ): resp = await config_client.delete("/api/config/actions/custom") @@ -1681,10 +1681,10 @@ class TestDeleteActionRouter: assert resp.status_code == 204 async def test_404_when_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNotFoundError + from app.services.action_config_service import ActionNotFoundError with patch( - "app.routers.config.config_file_service.delete_action", + "app.routers.config.action_config_service.delete_action", AsyncMock(side_effect=ActionNotFoundError("missing")), ): resp = await config_client.delete("/api/config/actions/missing") @@ -1692,10 +1692,10 @@ class TestDeleteActionRouter: assert resp.status_code == 404 async def test_409_when_readonly(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionReadonlyError + from app.services.action_config_service import ActionReadonlyError with patch( - "app.routers.config.config_file_service.delete_action", + "app.routers.config.action_config_service.delete_action", AsyncMock(side_effect=ActionReadonlyError("iptables")), ): resp = await config_client.delete("/api/config/actions/iptables") @@ -1703,10 +1703,10 @@ class TestDeleteActionRouter: assert resp.status_code == 409 async def test_400_for_bad_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNameError + from app.services.action_config_service import ActionNameError with patch( - "app.routers.config.config_file_service.delete_action", + "app.routers.config.action_config_service.delete_action", AsyncMock(side_effect=ActionNameError()), ): resp = await config_client.delete("/api/config/actions/badname") @@ -1725,7 +1725,7 @@ class TestDeleteActionRouter: class TestAssignActionToJailRouter: async def test_204_on_success(self, config_client: AsyncClient) -> None: with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(return_value=None), ): resp = await config_client.post( @@ -1736,10 +1736,10 @@ class TestAssignActionToJailRouter: assert resp.status_code == 204 async def test_404_when_jail_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import JailNotFoundInConfigError + from app.services.jail_config_service import JailNotFoundInConfigError with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(side_effect=JailNotFoundInConfigError("missing")), ): resp = await config_client.post( @@ -1750,10 +1750,10 @@ class TestAssignActionToJailRouter: assert resp.status_code == 404 async def test_404_when_action_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNotFoundError + from app.services.action_config_service import ActionNotFoundError with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(side_effect=ActionNotFoundError("missing")), ): resp = await config_client.post( @@ -1764,10 +1764,10 @@ class TestAssignActionToJailRouter: assert resp.status_code == 404 async def test_400_for_bad_jail_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(side_effect=JailNameError()), ): resp = await config_client.post( @@ -1778,10 +1778,10 @@ class TestAssignActionToJailRouter: assert resp.status_code == 400 async def test_400_for_bad_action_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNameError + from app.services.action_config_service import ActionNameError with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(side_effect=ActionNameError()), ): resp = await config_client.post( @@ -1793,7 +1793,7 @@ class TestAssignActionToJailRouter: async def test_reload_param_passed(self, config_client: AsyncClient) -> None: with patch( - "app.routers.config.config_file_service.assign_action_to_jail", + "app.routers.config.action_config_service.assign_action_to_jail", AsyncMock(return_value=None), ) as mock_assign: resp = await config_client.post( @@ -1816,7 +1816,7 @@ class TestAssignActionToJailRouter: class TestRemoveActionFromJailRouter: async def test_204_on_success(self, config_client: AsyncClient) -> None: with patch( - "app.routers.config.config_file_service.remove_action_from_jail", + "app.routers.config.action_config_service.remove_action_from_jail", AsyncMock(return_value=None), ): resp = await config_client.delete( @@ -1826,10 +1826,10 @@ class TestRemoveActionFromJailRouter: assert resp.status_code == 204 async def test_404_when_jail_not_found(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import JailNotFoundInConfigError + from app.services.jail_config_service import JailNotFoundInConfigError with patch( - "app.routers.config.config_file_service.remove_action_from_jail", + "app.routers.config.action_config_service.remove_action_from_jail", AsyncMock(side_effect=JailNotFoundInConfigError("missing")), ): resp = await config_client.delete( @@ -1839,10 +1839,10 @@ class TestRemoveActionFromJailRouter: assert resp.status_code == 404 async def test_400_for_bad_jail_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.remove_action_from_jail", + "app.routers.config.action_config_service.remove_action_from_jail", AsyncMock(side_effect=JailNameError()), ): resp = await config_client.delete( @@ -1852,10 +1852,10 @@ class TestRemoveActionFromJailRouter: assert resp.status_code == 400 async def test_400_for_bad_action_name(self, config_client: AsyncClient) -> None: - from app.services.config_file_service import ActionNameError + from app.services.action_config_service import ActionNameError with patch( - "app.routers.config.config_file_service.remove_action_from_jail", + "app.routers.config.action_config_service.remove_action_from_jail", AsyncMock(side_effect=ActionNameError()), ): resp = await config_client.delete( @@ -1866,7 +1866,7 @@ class TestRemoveActionFromJailRouter: async def test_reload_param_passed(self, config_client: AsyncClient) -> None: with patch( - "app.routers.config.config_file_service.remove_action_from_jail", + "app.routers.config.action_config_service.remove_action_from_jail", AsyncMock(return_value=None), ) as mock_rm: resp = await config_client.delete( @@ -2065,7 +2065,7 @@ class TestValidateJailEndpoint: jail_name="sshd", valid=True, issues=[] ) with patch( - "app.routers.config.config_file_service.validate_jail_config", + "app.routers.config.jail_config_service.validate_jail_config", AsyncMock(return_value=mock_result), ): resp = await config_client.post("/api/config/jails/sshd/validate") @@ -2085,7 +2085,7 @@ class TestValidateJailEndpoint: jail_name="sshd", valid=False, issues=[issue] ) with patch( - "app.routers.config.config_file_service.validate_jail_config", + "app.routers.config.jail_config_service.validate_jail_config", AsyncMock(return_value=mock_result), ): resp = await config_client.post("/api/config/jails/sshd/validate") @@ -2098,10 +2098,10 @@ class TestValidateJailEndpoint: async def test_400_for_invalid_jail_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/bad-name/validate returns 400 on JailNameError.""" - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.validate_jail_config", + "app.routers.config.jail_config_service.validate_jail_config", AsyncMock(side_effect=JailNameError("bad name")), ): resp = await config_client.post("/api/config/jails/bad-name/validate") @@ -2193,7 +2193,7 @@ class TestRollbackEndpoint: message="Jail 'sshd' disabled and fail2ban restarted.", ) with patch( - "app.routers.config.config_file_service.rollback_jail", + "app.routers.config.jail_config_service.rollback_jail", AsyncMock(return_value=mock_result), ): resp = await config_client.post("/api/config/jails/sshd/rollback") @@ -2230,7 +2230,7 @@ class TestRollbackEndpoint: message="fail2ban did not come back online.", ) with patch( - "app.routers.config.config_file_service.rollback_jail", + "app.routers.config.jail_config_service.rollback_jail", AsyncMock(return_value=mock_result), ): resp = await config_client.post("/api/config/jails/sshd/rollback") @@ -2243,10 +2243,10 @@ class TestRollbackEndpoint: async def test_400_for_invalid_jail_name(self, config_client: AsyncClient) -> None: """POST /api/config/jails/bad/rollback returns 400 on JailNameError.""" - from app.services.config_file_service import JailNameError + from app.services.jail_config_service import JailNameError with patch( - "app.routers.config.config_file_service.rollback_jail", + "app.routers.config.jail_config_service.rollback_jail", AsyncMock(side_effect=JailNameError("bad")), ): resp = await config_client.post("/api/config/jails/bad/rollback") diff --git a/backend/tests/test_routers/test_file_config.py b/backend/tests/test_routers/test_file_config.py index b6a88fc..e8cbed8 100644 --- a/backend/tests/test_routers/test_file_config.py +++ b/backend/tests/test_routers/test_file_config.py @@ -342,7 +342,7 @@ class TestListActionFiles: ) resp_data = ActionListResponse(actions=[mock_action], total=1) with patch( - "app.routers.config.config_file_service.list_actions", + "app.routers.config.action_config_service.list_actions", AsyncMock(return_value=resp_data), ): resp = await file_config_client.get("/api/config/actions") @@ -365,7 +365,7 @@ class TestCreateActionFile: actionban="echo ban ", ) with patch( - "app.routers.config.config_file_service.create_action", + "app.routers.config.action_config_service.create_action", AsyncMock(return_value=created), ): resp = await file_config_client.post(