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:
jonatanritter
2026-07-22 16:12:11 -03:00
commit 923848af33
23 changed files with 2223 additions and 0 deletions
View File
+36
View File
@@ -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()
+13
View File
@@ -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"}