Files
calminer/tests/e2e/conftest.py
zwitschi 8dedfb8f26
Some checks failed
Build and Push Docker Image / build-and-push (push) Failing after 6s
Deploy to Server / deploy (push) Failing after 2s
feat: Refactor database configuration to use granular environment variables; update Docker and CI/CD workflows accordingly
2025-10-23 19:17:24 +02:00

124 lines
3.6 KiB
Python

import os
import subprocess
import time
from typing import Dict, Generator
import pytest
# type: ignore[import]
from playwright.sync_api import Browser, Page, Playwright, sync_playwright
import httpx
from sqlalchemy.engine import make_url
# Use a different port for the test server to avoid conflicts
TEST_PORT = 8001
BASE_URL = f"http://localhost:{TEST_PORT}"
@pytest.fixture(scope="session", autouse=True)
def live_server() -> Generator[str, None, None]:
"""Launch a live test server in a separate process."""
env = _prepare_database_environment(os.environ.copy())
process = subprocess.Popen(
[
"uvicorn",
"main:app",
"--host",
"127.0.0.1",
f"--port={TEST_PORT}",
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
env=env,
)
deadline = time.perf_counter() + 30
last_error: Exception | None = None
while time.perf_counter() < deadline:
if process.poll() is not None:
raise RuntimeError("uvicorn server exited before becoming ready")
try:
response = httpx.get(BASE_URL, timeout=1.0)
if response.status_code < 500:
break
except Exception as exc: # noqa: BLE001
last_error = exc
time.sleep(0.5)
else:
process.terminate()
process.wait(timeout=5)
raise TimeoutError(
"Timed out waiting for uvicorn test server to start"
) from last_error
try:
yield BASE_URL
finally:
if process.poll() is None:
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
process.wait(timeout=5)
@pytest.fixture(scope="session")
def playwright_instance() -> Generator[Playwright, None, None]:
"""Provide a Playwright instance for the test session."""
with sync_playwright() as p:
yield p
@pytest.fixture(scope="session")
def browser(
playwright_instance: Playwright,
) -> Generator[Browser, None, None]:
"""Provide a browser instance for the test session."""
browser = playwright_instance.chromium.launch()
yield browser
browser.close()
@pytest.fixture()
def page(browser: Browser, live_server: str) -> Generator[Page, None, None]:
"""Provide a new page for each test."""
page = browser.new_page(base_url=live_server)
page.goto("/")
page.wait_for_load_state("networkidle")
yield page
page.close()
def _prepare_database_environment(env: Dict[str, str]) -> Dict[str, str]:
"""Ensure granular database env vars are available for the app under test."""
required = ("DATABASE_HOST", "DATABASE_USER", "DATABASE_NAME")
if all(env.get(key) for key in required):
return env
legacy_url = env.get("DATABASE_URL")
if not legacy_url:
return env
url = make_url(legacy_url)
env.setdefault("DATABASE_DRIVER", url.drivername)
if url.host:
env.setdefault("DATABASE_HOST", url.host)
if url.port:
env.setdefault("DATABASE_PORT", str(url.port))
if url.username:
env.setdefault("DATABASE_USER", url.username)
if url.password:
env.setdefault("DATABASE_PASSWORD", url.password)
if url.database:
env.setdefault("DATABASE_NAME", url.database)
query_options = dict(url.query) if url.query else {}
options = query_options.get("options")
if isinstance(options, str) and "search_path=" in options:
env.setdefault("DATABASE_SCHEMA", options.split("search_path=")[-1])
return env