language-learning-app/api/conftest.py

67 lines
2.1 KiB
Python
Raw Permalink Normal View History

"""
Session-scoped fixtures that spin up and tear down the test stack.
The test stack uses docker-compose.test.yml which:
- Runs on port 18000 (won't collide with the dev stack on 8000)
- Uses tmpfs for all storage (no data survives after `down`)
- Uses project name "langlearn-test" to stay isolated from dev containers
"""
import pathlib
import subprocess
from dotenv import load_dotenv
import uuid
import httpx
import pytest
PROJECT_ROOT = pathlib.Path(__file__).parent.parent
COMPOSE_FILE = str(PROJECT_ROOT / "docker-compose.test.yml")
ENV_FILE = str(PROJECT_ROOT / ".env.test")
COMPOSE_PROJECT = "langlearn-test"
API_BASE_URL = "http://localhost:18000"
load_dotenv(PROJECT_ROOT / ".env.test")
def _compose(*args: str) -> None:
subprocess.run(
["docker", "compose", "-p", COMPOSE_PROJECT, "-f", COMPOSE_FILE, *args],
cwd=PROJECT_ROOT,
check=True,
)
@pytest.fixture(scope="session", autouse=True)
def docker_stack():
"""Bring the test stack up before the session; tear it down (including volumes) after."""
_compose("--env-file", ENV_FILE, "up", "--build", "--wait", "-d")
yield
_compose("down", "-v")
@pytest.fixture
def client() -> httpx.Client:
"""A plain httpx client pointed at the test API. Not authenticated."""
with httpx.Client(base_url=API_BASE_URL) as c:
yield c
def _random_email() -> str:
return f"user-{uuid.uuid4()}@example.com"
@pytest.fixture
def authd_client() -> httpx.Client:
email = _random_email()
password = "password1234"
with httpx.Client(base_url=API_BASE_URL) as client:
register_response = client.post("/api/auth/register", json={"email": email, "password": password})
assert register_response.json().get("success") is True, f"Failed to register test user: {register_response.text}"
login_response = client.post("/api/auth/login", json={"email": email, "password": password})
assert login_response.status_code == 200, f"Failed to log in test user: {login_response.text}"
token = login_response.json().get("access_token")
client.headers["Authorization"] = f"Bearer {token}"
yield client