import re from collections.abc import AsyncIterator from pathlib import Path import pytest from httpx import ASGITransport, AsyncClient from porchlight.app import create_app from porchlight.config import Settings from porchlight.rate_limit import limiter @pytest.fixture def settings(tmp_path: Path) -> Settings: return Settings( issuer="http://localhost:8000", sqlite_path=":memory:", session_https_only=False, signing_key_path=str(tmp_path / "keys"), ) @pytest.fixture async def client(settings: Settings) -> AsyncIterator[AsyncClient]: app = create_app(settings) transport = ASGITransport(app=app) async with app.router.lifespan_context(app), AsyncClient(transport=transport, base_url=settings.issuer) as ac: yield ac @pytest.fixture(autouse=True) def _reset_rate_limiter() -> None: """Reset the rate limiter storage before each test.""" limiter.reset() async def get_csrf_token(client: AsyncClient) -> str: """Get a CSRF token by visiting the login page. Returns the token string. The session cookie is automatically stored in the client's cookie jar (httpx.AsyncClient persists cookies). """ resp = await client.get("/login") match = re.search(r'name="csrf-token" content="([^"]+)"', resp.text) assert match, "CSRF meta tag not found in page" return match.group(1)