Fix module-level asyncio locks in jail_service
Initialize jail_service locks lazily to avoid import-time event loop binding and add regression tests for lock creation.
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models.ban import ActiveBanListResponse, JailBannedIpsResponse
|
||||
from app.models.geo import GeoDetail, GeoInfo
|
||||
from app.models.jail import JailDetailResponse, JailListResponse
|
||||
from app.services import jail_service
|
||||
from app.services.jail_service import JailNotFoundError, JailOperationError
|
||||
@@ -270,6 +272,28 @@ class TestListJails:
|
||||
assert jail.idle is False
|
||||
|
||||
|
||||
class TestLockInitialization:
|
||||
"""Regression tests for asyncio lock creation in jail_service."""
|
||||
|
||||
async def test_reload_all_lock_is_lazy_initialised(self) -> None:
|
||||
"""The reload-all lock should be created lazily on first use."""
|
||||
jail_service._reload_all_lock = None
|
||||
|
||||
lock = _ = jail_service._get_reload_all_lock()
|
||||
|
||||
assert isinstance(lock, asyncio.Lock)
|
||||
assert jail_service._reload_all_lock is lock
|
||||
|
||||
async def test_backend_cmd_lock_is_lazy_initialised(self) -> None:
|
||||
"""The backend capability probe lock should be created lazily on first use."""
|
||||
jail_service._backend_cmd_lock = None
|
||||
|
||||
lock = _ = jail_service._get_backend_cmd_lock()
|
||||
|
||||
assert isinstance(lock, asyncio.Lock)
|
||||
assert jail_service._backend_cmd_lock is lock
|
||||
|
||||
|
||||
class TestGetJail:
|
||||
"""Unit tests for :func:`~app.services.jail_service.get_jail`."""
|
||||
|
||||
@@ -771,6 +795,30 @@ class TestLookupIp:
|
||||
assert result["ip"] == "1.2.3.4"
|
||||
assert "sshd" in result["currently_banned_in"]
|
||||
|
||||
async def test_geo_enricher_returns_geo_detail(self) -> None:
|
||||
"""lookup_ip converts GeoInfo from the enricher into GeoDetail."""
|
||||
responses = {
|
||||
"get|--all|banned|1.2.3.4": (0, []),
|
||||
"status": _make_global_status("sshd"),
|
||||
"get|sshd|banip": (0, ["1.2.3.4", "5.6.7.8"]),
|
||||
}
|
||||
|
||||
async def _enricher(ip: str) -> GeoInfo:
|
||||
return GeoInfo(country_code="DE", country_name="Germany", asn="AS123", org="Acme")
|
||||
|
||||
with _patch_client(responses):
|
||||
result = await jail_service.lookup_ip(
|
||||
_SOCKET,
|
||||
"1.2.3.4",
|
||||
geo_enricher=_enricher,
|
||||
)
|
||||
|
||||
assert isinstance(result["geo"], GeoDetail)
|
||||
assert result["geo"].country_code == "DE"
|
||||
assert result["geo"].country_name == "Germany"
|
||||
assert result["geo"].asn == "AS123"
|
||||
assert result["geo"].org == "Acme"
|
||||
|
||||
async def test_invalid_ip_raises(self) -> None:
|
||||
"""lookup_ip raises ValueError for invalid IP."""
|
||||
with pytest.raises(ValueError, match="Invalid IP"):
|
||||
|
||||
Reference in New Issue
Block a user