ci: add Forgejo CI (lint + unit + Postgres integration) + ci-requirements.md
Single-repo CI now that plugins are bundled in-tree. Three lanes on push to dev/main, modeled on FabledCurator's canonical ci.yml (minus frontend/Redis): - lint: ruff check steward/ plugins/ tests/ (no dep install) - unit: pytest -m 'not integration' — whole current suite (testing=True mocks DB) - integration: postgres:16-alpine service via socket-discovered bridge IP; a boot-and-migrate test creates the real app, running core + all bundled-plugin migrations, then round-trips the DB — guards the folded-in migration graph. Registers the 'integration' pytest marker; adds tests/integration/test_boot.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
"""Integration: boot the real app against a live Postgres.
|
||||
|
||||
Creating the app with testing=False runs the core Alembic migrations AND every
|
||||
bundled plugin's migrations (discovered across all plugin roots). This is the
|
||||
canonical guard that the folded-in plugin migration graph resolves end-to-end —
|
||||
a pure-unit test can't catch a broken plugin revision chain.
|
||||
|
||||
Requires STEWARD_DATABASE_URL pointing at a live Postgres; the integration CI
|
||||
lane provides it. Skipped automatically when unset so the unit lane stays green.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("STEWARD_DATABASE_URL"),
|
||||
reason="integration test needs a live Postgres (STEWARD_DATABASE_URL)",
|
||||
)
|
||||
def test_real_boot_runs_all_migrations():
|
||||
from sqlalchemy import text
|
||||
|
||||
from steward.app import create_app
|
||||
|
||||
# testing=False → init_db + run_core_migrations (core + all bundled-plugin
|
||||
# migrations) + load_settings_sync + load_plugins all execute here.
|
||||
app = create_app(testing=False)
|
||||
assert app is not None
|
||||
|
||||
async def _roundtrip():
|
||||
async with app.db_sessionmaker() as session:
|
||||
# app_settings exists only if core migrations actually applied.
|
||||
result = await session.execute(text("SELECT COUNT(*) FROM app_settings"))
|
||||
return result.scalar()
|
||||
|
||||
count = asyncio.run(_roundtrip())
|
||||
assert count is not None
|
||||
Reference in New Issue
Block a user