refactor: complete Task 2/3 geo decouple + exceptions centralization; mark as done

This commit is contained in:
2026-03-21 17:15:02 +01:00
parent 452901913f
commit 5a49106f4d
28 changed files with 803 additions and 571 deletions

View File

@@ -123,7 +123,7 @@ class TestListHistory:
) -> None:
"""No filter returns every record in the database."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history("fake_socket")
@@ -135,7 +135,7 @@ class TestListHistory:
) -> None:
"""The ``range_`` filter excludes bans older than the window."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
# "24h" window should include only the two recent bans
@@ -147,7 +147,7 @@ class TestListHistory:
async def test_jail_filter(self, f2b_db_path: str) -> None:
"""Jail filter restricts results to bans from that jail."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history("fake_socket", jail="nginx")
@@ -157,7 +157,7 @@ class TestListHistory:
async def test_ip_prefix_filter(self, f2b_db_path: str) -> None:
"""IP prefix filter restricts results to matching IPs."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -170,7 +170,7 @@ class TestListHistory:
async def test_combined_filters(self, f2b_db_path: str) -> None:
"""Jail + IP prefix filters applied together narrow the result set."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -182,7 +182,7 @@ class TestListHistory:
async def test_unknown_ip_returns_empty(self, f2b_db_path: str) -> None:
"""Filtering by a non-existent IP returns an empty result set."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -196,7 +196,7 @@ class TestListHistory:
) -> None:
"""``failures`` field is parsed from the JSON ``data`` column."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -210,7 +210,7 @@ class TestListHistory:
) -> None:
"""``matches`` list is parsed from the JSON ``data`` column."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -226,7 +226,7 @@ class TestListHistory:
) -> None:
"""Records with ``data=NULL`` produce failures=0 and matches=[]."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -240,7 +240,7 @@ class TestListHistory:
async def test_pagination(self, f2b_db_path: str) -> None:
"""Pagination returns the correct slice."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.list_history(
@@ -265,7 +265,7 @@ class TestGetIpDetail:
) -> None:
"""Returns ``None`` when the IP has no records in the database."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.get_ip_detail("fake_socket", "99.99.99.99")
@@ -276,7 +276,7 @@ class TestGetIpDetail:
) -> None:
"""Returns an IpDetailResponse with correct totals for a known IP."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.get_ip_detail("fake_socket", "1.2.3.4")
@@ -291,7 +291,7 @@ class TestGetIpDetail:
) -> None:
"""Timeline events are ordered newest-first."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.get_ip_detail("fake_socket", "1.2.3.4")
@@ -304,7 +304,7 @@ class TestGetIpDetail:
async def test_last_ban_at_is_most_recent(self, f2b_db_path: str) -> None:
"""``last_ban_at`` matches the banned_at of the first timeline event."""
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.get_ip_detail("fake_socket", "1.2.3.4")
@@ -316,7 +316,7 @@ class TestGetIpDetail:
self, f2b_db_path: str
) -> None:
"""Geolocation is applied when a geo_enricher is provided."""
from app.services.geo_service import GeoInfo
from app.models.geo import GeoInfo
mock_geo = GeoInfo(
country_code="US",
@@ -327,7 +327,7 @@ class TestGetIpDetail:
fake_enricher = AsyncMock(return_value=mock_geo)
with patch(
"app.services.history_service._get_fail2ban_db_path",
"app.services.history_service.get_fail2ban_db_path",
new=AsyncMock(return_value=f2b_db_path),
):
result = await history_service.get_ip_detail(