from __future__ import annotations import importlib import sys from pathlib import Path import asyncio import httpx import pytest _ROOT = Path(__file__).resolve().parent.parent _SOFIIA_PATH = _ROOT / "services" / "sofiia-console" if str(_SOFIIA_PATH) not in sys.path: sys.path.insert(0, str(_SOFIIA_PATH)) @pytest.fixture def sofiia_module(tmp_path, monkeypatch): """Reload sofiia-console app with isolated env and DB path.""" monkeypatch.setenv("SOFIIA_DATA_DIR", str(tmp_path / "sofiia-data")) monkeypatch.setenv("ENV", "dev") monkeypatch.delenv("SOFIIA_CONSOLE_API_KEY", raising=False) monkeypatch.setenv("ROUTER_URL", "http://router.local:8000") monkeypatch.delenv("NODE_ID", raising=False) # Python 3.9 + pytest-asyncio strict mode may not have a default loop. loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) import app.db as db_mod # type: ignore import app.idempotency as idem_mod # type: ignore import app.main as main_mod # type: ignore importlib.reload(db_mod) importlib.reload(idem_mod) importlib.reload(main_mod) main_mod._rate_buckets.clear() store = idem_mod.get_idempotency_store() if hasattr(store, "reset"): store.reset() return main_mod @pytest.fixture def sofiia_client(sofiia_module): class _LocalClient: def __init__(self, app): self.app = app def request(self, method: str, path: str, **kwargs): async def _do(): transport = httpx.ASGITransport(app=self.app) async with httpx.AsyncClient( transport=transport, base_url="http://testserver", follow_redirects=True, ) as client: return await client.request(method, path, **kwargs) return asyncio.run(_do()) def get(self, path: str, **kwargs): return self.request("GET", path, **kwargs) def post(self, path: str, **kwargs): return self.request("POST", path, **kwargs) return _LocalClient(sofiia_module.app)