92 lines
2.4 KiB
Python
92 lines
2.4 KiB
Python
"""PostgreSQL and session-lifecycle integration coverage."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from alembic import command
|
|
from alembic.config import Config
|
|
from sqlalchemy import text
|
|
|
|
from ragcore.config import Settings
|
|
from ragcore.infrastructure.database import (
|
|
create_database_engine,
|
|
create_session_factory,
|
|
session_scope,
|
|
)
|
|
|
|
REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def event_loop_policy():
|
|
if sys.platform == "win32":
|
|
return asyncio.WindowsSelectorEventLoopPolicy()
|
|
return asyncio.get_event_loop_policy()
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def alembic_config() -> Config:
|
|
return Config(str(REPOSITORY_ROOT / "alembic.ini"))
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def session_factory():
|
|
engine = create_database_engine(Settings(env="test"))
|
|
try:
|
|
yield create_session_factory(engine)
|
|
finally:
|
|
await engine.dispose()
|
|
|
|
|
|
def test_empty_database_can_upgrade_downgrade_and_upgrade(alembic_config: Config) -> None:
|
|
command.upgrade(alembic_config, "head")
|
|
command.downgrade(alembic_config, "base")
|
|
command.upgrade(alembic_config, "head")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_postgresql_session_executes_query(session_factory) -> None:
|
|
async with session_scope(session_factory) as session:
|
|
result = await session.execute(text("SELECT 1"))
|
|
|
|
assert result.scalar_one() == 1
|
|
|
|
|
|
class _RecordingSession:
|
|
def __init__(self) -> None:
|
|
self.closed = False
|
|
self.rolled_back = False
|
|
|
|
async def __aenter__(self) -> _RecordingSession:
|
|
return self
|
|
|
|
async def __aexit__(self, *_: object) -> None:
|
|
self.closed = True
|
|
|
|
async def rollback(self) -> None:
|
|
self.rolled_back = True
|
|
|
|
|
|
class _RecordingFactory:
|
|
def __init__(self, session: _RecordingSession) -> None:
|
|
self.session = session
|
|
|
|
def __call__(self) -> _RecordingSession:
|
|
return self.session
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_session_scope_rolls_back_and_closes_on_failure() -> None:
|
|
recording_session = _RecordingSession()
|
|
|
|
with pytest.raises(RuntimeError, match="boom"):
|
|
async with session_scope(_RecordingFactory(recording_session)):
|
|
raise RuntimeError("boom")
|
|
|
|
assert recording_session.rolled_back
|
|
assert recording_session.closed |