Make geo lookups non-blocking with bulk DB writes and background tasks
This commit is contained in:
@@ -614,6 +614,108 @@ class TestOriginFilter:
|
||||
assert result.total == 3
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# bans_by_country — background geo resolution (Task 3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestBansbyCountryBackground:
|
||||
"""bans_by_country() with http_session uses cache-only geo and fires a
|
||||
background task for uncached IPs instead of blocking on API calls."""
|
||||
|
||||
async def test_cached_geo_returned_without_api_call(
|
||||
self, mixed_origin_db_path: str
|
||||
) -> None:
|
||||
"""When all IPs are in the cache, lookup_cached_only returns them and
|
||||
no background task is created."""
|
||||
from app.services import geo_service
|
||||
|
||||
# Pre-populate the cache for all three IPs in the fixture.
|
||||
geo_service._cache["10.0.0.1"] = geo_service.GeoInfo( # type: ignore[attr-defined]
|
||||
country_code="DE", country_name="Germany", asn=None, org=None
|
||||
)
|
||||
geo_service._cache["10.0.0.2"] = geo_service.GeoInfo( # type: ignore[attr-defined]
|
||||
country_code="US", country_name="United States", asn=None, org=None
|
||||
)
|
||||
geo_service._cache["10.0.0.3"] = geo_service.GeoInfo( # type: ignore[attr-defined]
|
||||
country_code="JP", country_name="Japan", asn=None, org=None
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"app.services.ban_service._get_fail2ban_db_path",
|
||||
new=AsyncMock(return_value=mixed_origin_db_path),
|
||||
),
|
||||
patch(
|
||||
"app.services.ban_service.asyncio.create_task"
|
||||
) as mock_create_task,
|
||||
):
|
||||
mock_session = AsyncMock()
|
||||
result = await ban_service.bans_by_country(
|
||||
"/fake/sock", "24h", http_session=mock_session
|
||||
)
|
||||
|
||||
# All countries resolved from cache — no background task needed.
|
||||
mock_create_task.assert_not_called()
|
||||
assert result.total == 3
|
||||
# Country counts should reflect the cached data.
|
||||
assert "DE" in result.countries or "US" in result.countries or "JP" in result.countries
|
||||
geo_service.clear_cache()
|
||||
|
||||
async def test_uncached_ips_trigger_background_task(
|
||||
self, mixed_origin_db_path: str
|
||||
) -> None:
|
||||
"""When IPs are NOT in the cache, create_task is called for background
|
||||
resolution and the response returns without blocking."""
|
||||
from app.services import geo_service
|
||||
|
||||
geo_service.clear_cache() # ensure cache is empty
|
||||
|
||||
with (
|
||||
patch(
|
||||
"app.services.ban_service._get_fail2ban_db_path",
|
||||
new=AsyncMock(return_value=mixed_origin_db_path),
|
||||
),
|
||||
patch(
|
||||
"app.services.ban_service.asyncio.create_task"
|
||||
) as mock_create_task,
|
||||
):
|
||||
mock_session = AsyncMock()
|
||||
result = await ban_service.bans_by_country(
|
||||
"/fake/sock", "24h", http_session=mock_session
|
||||
)
|
||||
|
||||
# Background task must have been scheduled for uncached IPs.
|
||||
mock_create_task.assert_called_once()
|
||||
# Response is still valid with empty country map (IPs not cached yet).
|
||||
assert result.total == 3
|
||||
|
||||
async def test_no_background_task_without_http_session(
|
||||
self, mixed_origin_db_path: str
|
||||
) -> None:
|
||||
"""When http_session is None, no background task is created."""
|
||||
from app.services import geo_service
|
||||
|
||||
geo_service.clear_cache()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"app.services.ban_service._get_fail2ban_db_path",
|
||||
new=AsyncMock(return_value=mixed_origin_db_path),
|
||||
),
|
||||
patch(
|
||||
"app.services.ban_service.asyncio.create_task"
|
||||
) as mock_create_task,
|
||||
):
|
||||
result = await ban_service.bans_by_country(
|
||||
"/fake/sock", "24h", http_session=None
|
||||
)
|
||||
|
||||
mock_create_task.assert_not_called()
|
||||
assert result.total == 3
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ban_trend
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user