"""Test fixtures shared across unit + integration tests. v0.2 PR #1: tests run against the live Postgres container managed by docker-compose. Each test that needs DB isolation requests the ``pg_db_url`` fixture, which creates a fresh database per test and drops it on teardown. Prerequisites: docker compose up -d postgres # devflow-postgres on 55432 """ from __future__ import annotations import os import uuid from collections.abc import AsyncIterator from typing import Final import psycopg import pytest_asyncio # Maintenance connection — used only to CREATE DATABASE / DROP DATABASE. # `postgres` is the bootstrap DB present on every Postgres install. _MAINTENANCE_DSN: Final[str] = os.environ.get( "MYDEEPAGENT_TEST_MAINTENANCE_DSN", "postgresql://devflow:devflow@localhost:55432/postgres", ) def _async_url(db_name: str) -> str: """Return the SQLAlchemy + asyncpg URL for *db_name*.""" return f"postgresql+asyncpg://devflow:devflow@localhost:55432/{db_name}" def _create_test_database() -> str: """Create a fresh test database with a random suffix and return its name.""" db_name = f"test_{uuid.uuid4().hex[:16]}" # autocommit=True is required for CREATE DATABASE (cannot run in a tx block). with psycopg.connect(_MAINTENANCE_DSN, autocommit=True) as conn: with conn.cursor() as cur: cur.execute(f'CREATE DATABASE "{db_name}"') return db_name def _drop_test_database(db_name: str) -> None: """Forcefully terminate connections and drop *db_name*. Idempotent.""" with psycopg.connect(_MAINTENANCE_DSN, autocommit=True) as conn: with conn.cursor() as cur: # Kick any lingering connections held by aiosqlite-style pools that # didn't dispose cleanly. WITH (FORCE) is Postgres 13+. cur.execute( """ SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = %s AND pid <> pg_backend_pid() """, (db_name,), ) cur.execute(f'DROP DATABASE IF EXISTS "{db_name}"') @pytest_asyncio.fixture async def pg_db_url() -> AsyncIterator[str]: """Yield an isolated Postgres database URL; drop the DB on teardown. Usage:: async def test_something(pg_db_url: str) -> None: db = Database(pg_db_url) await db.init_schema() ... The returned URL uses the asyncpg driver. To get a sync URL for tools like alembic, replace ``postgresql+asyncpg://`` with ``postgresql+psycopg://``. """ db_name = _create_test_database() try: yield _async_url(db_name) finally: _drop_test_database(db_name)