from __future__ import annotations from collections.abc import AsyncIterator import hashlib import hmac import os import time import httpx import pytest from sqlalchemy import text from core.config.settings import config from core.db.session import AsyncSessionLocal @pytest.fixture(scope="session") def api_base_url() -> str: return os.environ.get("ERYAO_TEST_BASE_URL", "http://localhost:5775") @pytest.fixture(scope="session") def test_verify_code() -> str: return os.environ.get("ERYAO_TEST__CODE", "123456") @pytest.fixture def unique_test_email() -> str: base_email = os.environ.get("ERYAO_TEST__EMAIL", "test@example.com").strip().lower() if "@" in base_email: name, domain = base_email.split("@", 1) else: name, domain = base_email, "example.com" return f"{name}+it{int(time.time() * 1000)}@{domain}" @pytest.fixture def test_identity(unique_test_email: str, test_verify_code: str) -> dict[str, str]: return {"email": unique_test_email, "code": test_verify_code} @pytest.fixture async def api_client(api_base_url: str) -> AsyncIterator[httpx.AsyncClient]: async with httpx.AsyncClient(base_url=api_base_url, timeout=30.0) as client: try: health = await client.get("/health") if health.status_code != 200: pytest.skip(f"API not ready: /health={health.status_code}") except Exception as exc: pytest.skip(f"API unavailable: {exc}") yield client @pytest.fixture async def db_cleanup() -> AsyncIterator[list[str]]: emails: list[str] = [] yield emails if not emails: return hmac_key = config.points_policy.register_bonus_hmac_key.get_secret_value().strip() email_hashes = [ hmac.new( hmac_key.encode("utf-8"), email.encode("utf-8"), hashlib.sha256 ).hexdigest() for email in emails ] async with AsyncSessionLocal() as session: await session.execute( text( "DELETE FROM points_audit_ledger WHERE lower(coalesce(user_email_snapshot, '')) = ANY(:emails)" ), {"emails": emails}, ) await session.execute( text( "DELETE FROM register_bonus_claims WHERE email_hash = ANY(:email_hashes)" ), {"email_hashes": email_hashes}, ) await session.execute( text("DELETE FROM auth.users WHERE lower(email) = ANY(:emails)"), {"emails": emails}, ) await session.commit()