feat: service scaffold, k8s test infra, health
FastAPI + SQLAlchemy async + Alembic scaffold, src-layout (mirrors the
sowai-fiscal lib's own convention), pyproject wired to sowai-fiscal@v0.1.0
via git+https (uv.lock pins the commit). Makefile mirrors auto/Makefile's
k8s-test workflow: syncs into /app/fiscal-svc in the SAME auto-tests pod,
against a dedicated fiscal_svc_test database on the shared Postgres
sidecar, serialized by the SAME lock file the auto uses on purpose so the
two repos' test runs never race in the pod. Dockerfile installs git (the
git+https dependency needs it at uv sync time) and splits the dependency
layer from the project's own editable install for build caching.
GET /v1/health -> {"status": "ok"}, verified green via `make k8s-test`.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
import pytest_asyncio
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
|
||||
from fiscal_svc.core.config import settings
|
||||
from fiscal_svc.core.db import Base
|
||||
|
||||
# Ported verbatim from `auto/backend/tests/conftest.py` (Task 1). Note:
|
||||
# pytest-asyncio >=0.23 runs the whole test session on a single event loop
|
||||
# when asyncio_default_fixture_loop_scope/asyncio_default_test_loop_scope are
|
||||
# set to "session" (see pyproject.toml [tool.pytest.ini_options]) -- that
|
||||
# replaces the old pattern of redefining the `event_loop` fixture, which no
|
||||
# longer controls fixture/test loop assignment on pytest-asyncio 1.x and
|
||||
# caused a "Future attached to a different loop" RuntimeError when the
|
||||
# session-scoped `test_engine` below was created on a different loop than
|
||||
# each test.
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
async def test_engine():
|
||||
engine = create_async_engine(settings.test_database_url, echo=False)
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
yield engine
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def db_session(test_engine) -> AsyncGenerator[AsyncSession, None]:
|
||||
session_maker = async_sessionmaker(test_engine, expire_on_commit=False)
|
||||
async with session_maker() as session:
|
||||
yield session
|
||||
await session.rollback()
|
||||
@@ -0,0 +1,13 @@
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
import pytest
|
||||
|
||||
from fiscal_svc.main import app
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_check_returns_ok():
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
response = await client.get("/v1/health")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "ok"}
|
||||
Reference in New Issue
Block a user