mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-03-21 22:56:47 +00:00
117 lines
3.2 KiB
Python
117 lines
3.2 KiB
Python
"""
|
|
Integration test fixtures — no mocks, real services.
|
|
|
|
All services (PostgreSQL, Redis, Hatchet, Garage, server, workers) are
|
|
expected to be running via docker-compose.integration.yml.
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import boto3
|
|
import httpx
|
|
import pytest
|
|
import pytest_asyncio
|
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
|
|
SERVER_URL = os.environ.get("SERVER_URL", "http://server:1250")
|
|
GARAGE_ENDPOINT = os.environ.get("GARAGE_ENDPOINT", "http://garage:3900")
|
|
DATABASE_URL = os.environ.get(
|
|
"DATABASE_URL_ASYNC",
|
|
os.environ.get(
|
|
"DATABASE_URL",
|
|
"postgresql+asyncpg://reflector:reflector@postgres:5432/reflector",
|
|
),
|
|
)
|
|
GARAGE_KEY_ID = os.environ.get("TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID", "")
|
|
GARAGE_KEY_SECRET = os.environ.get("TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY", "")
|
|
BUCKET_NAME = "reflector-media"
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def api_client():
|
|
"""HTTP client pointed at the running server."""
|
|
async with httpx.AsyncClient(
|
|
base_url=f"{SERVER_URL}/v1",
|
|
timeout=httpx.Timeout(30.0),
|
|
) as client:
|
|
yield client
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def s3_client():
|
|
"""Boto3 S3 client pointed at Garage."""
|
|
return boto3.client(
|
|
"s3",
|
|
endpoint_url=GARAGE_ENDPOINT,
|
|
aws_access_key_id=GARAGE_KEY_ID,
|
|
aws_secret_access_key=GARAGE_KEY_SECRET,
|
|
region_name="garage",
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def db_engine():
|
|
"""SQLAlchemy async engine for direct DB operations."""
|
|
engine = create_async_engine(DATABASE_URL)
|
|
yield engine
|
|
await engine.dispose()
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def test_records_dir():
|
|
"""Path to the test audio files directory."""
|
|
return Path(__file__).parent.parent / "records"
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def bucket_name():
|
|
"""S3 bucket name used for integration tests."""
|
|
return BUCKET_NAME
|
|
|
|
|
|
async def _poll_transcript_status(
|
|
client: httpx.AsyncClient,
|
|
transcript_id: str,
|
|
target: str | tuple[str, ...],
|
|
error: str = "error",
|
|
max_wait: int = 300,
|
|
interval: int = 3,
|
|
) -> dict:
|
|
"""
|
|
Poll GET /transcripts/{id} until status matches target or error.
|
|
|
|
target can be a single status string or a tuple of acceptable statuses.
|
|
Returns the transcript dict on success, raises on timeout or error status.
|
|
"""
|
|
targets = (target,) if isinstance(target, str) else target
|
|
elapsed = 0
|
|
status = None
|
|
while elapsed < max_wait:
|
|
resp = await client.get(f"/transcripts/{transcript_id}")
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
status = data.get("status")
|
|
|
|
if status in targets:
|
|
return data
|
|
if status == error:
|
|
raise AssertionError(
|
|
f"Transcript {transcript_id} reached error status: {data}"
|
|
)
|
|
|
|
await asyncio.sleep(interval)
|
|
elapsed += interval
|
|
|
|
raise TimeoutError(
|
|
f"Transcript {transcript_id} did not reach status '{target}' "
|
|
f"within {max_wait}s (last status: {status})"
|
|
)
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
def poll_transcript_status():
|
|
"""Returns the poll_transcript_status async helper function."""
|
|
return _poll_transcript_status
|