From 22bc24b6b68382df3f05c5972dff5e4177721521 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 14 May 2026 15:23:07 -0400 Subject: [PATCH] =?UTF-8?q?fix(fc2a):=20align=20CI=20with=20FabledRulebook?= =?UTF-8?q?=20=E2=80=94=20lint=20+=20short=20unit=20tests=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI failure resolving 'postgres' hostname was the symptom; the cause is that the workflow violated FabledRulebook/forgejo.md's "CI philosophy — lint + short unit tests only" rule. Integration tests against a real Postgres are supposed to run locally via docker-compose, not in CI. Changes: - Marked 8 DB-dependent test files with @pytest.mark.integration: test_tag_service, test_importer, test_gallery_service, test_api_gallery, test_api_tags, test_api_settings, test_api_import_admin, test_maintenance. - CI workflow drops the postgres/redis service containers and the alembic upgrade smoke step entirely. - Pytest invocation in CI changes to `pytest -v -m "not integration"`. - Added pytest marker registration to pyproject.toml. - DB_PASSWORD and SECRET_KEY env vars retained because config.py reads them at import time even though unit tests don't actually use them (set to placeholder values). What CI now runs: - ruff check - pytest on the 6 unit test files: test_slug, test_paths, test_migration_0002, test_thumbnailer, test_celery_smoke, test_tasks_register. - npm install + npm run build What CI no longer runs: - alembic upgrade (no live DB) - the 8 integration test files (these run locally via docker-compose) Co-Authored-By: Claude Opus 4.7 (1M context) --- .forgejo/workflows/ci.yml | 45 ++++++++++------------------------ pyproject.toml | 8 ++++++ tests/test_api_gallery.py | 2 ++ tests/test_api_import_admin.py | 2 ++ tests/test_api_settings.py | 2 ++ tests/test_api_tags.py | 2 ++ tests/test_gallery_service.py | 2 ++ tests/test_importer.py | 2 ++ tests/test_maintenance.py | 2 ++ tests/test_tag_service.py | 2 ++ 10 files changed, 37 insertions(+), 32 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index e26fc61..b4b60a9 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -1,5 +1,12 @@ name: CI +# Per FabledRulebook/forgejo.md: "CI runs analyzers + short unit tests. +# Integration tests that need real Postgres/ffmpeg run locally via +# docker-compose." So this workflow deliberately does NOT spin up service +# containers; DB-dependent tests are marked `@pytest.mark.integration` and +# excluded with `-m "not integration"`. To run integration tests, operator +# uses docker-compose locally. + on: push: branches: [dev, main] @@ -12,34 +19,11 @@ jobs: # python-ci runner image lives at CI-Runner/CI-python/ in the operator's # workspace; see that Dockerfile + Makefile to roll/push the image. runs-on: python-ci - services: - postgres: - image: pgvector/pgvector:pg16 - env: - POSTGRES_USER: fabledcurator - POSTGRES_PASSWORD: ci_test_password - POSTGRES_DB: fabledcurator_test - options: >- - --health-cmd "pg_isready -U fabledcurator" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - redis: - image: redis:7-alpine - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 env: - DB_USER: fabledcurator - DB_PASSWORD: ci_test_password - DB_HOST: postgres - DB_PORT: "5432" - DB_NAME: fabledcurator_test - CELERY_BROKER_URL: redis://redis:6379/0 - CELERY_RESULT_BACKEND: redis://redis:6379/0 - SECRET_KEY: ci_test_secret_not_for_prod + # DB_PASSWORD and SECRET_KEY are required by config.py at import time + # even though unit tests don't actually touch the DB or use the secret. + DB_PASSWORD: ci_unit_test_placeholder + SECRET_KEY: ci_unit_test_placeholder steps: - uses: actions/checkout@v4 @@ -52,11 +36,8 @@ jobs: - name: Ruff lint run: ruff check backend/ tests/ alembic/ - - name: Alembic upgrade (smoke) - run: alembic upgrade head - - - name: Pytest (unit only) - run: pytest tests/ -v + - name: Pytest (unit tests only — integration tests run locally) + run: pytest tests/ -v -m "not integration" frontend-build: runs-on: python-ci diff --git a/pyproject.toml b/pyproject.toml index 0cd1a87..3a476ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,11 @@ requires-python = ">=3.14" [tool.setuptools.packages.find] where = ["."] include = ["backend*"] + +[tool.pytest.ini_options] +# Per FabledRulebook/forgejo.md: CI runs lint + short unit tests only. +# Integration tests that need a real Postgres/Redis run locally via +# docker-compose; CI passes `-m "not integration"` to skip them. +markers = [ + "integration: tests that require a real Postgres/Redis. Run locally via docker-compose, skipped in CI.", +] diff --git a/tests/test_api_gallery.py b/tests/test_api_gallery.py index 25746ee..6dadd45 100644 --- a/tests/test_api_gallery.py +++ b/tests/test_api_gallery.py @@ -5,6 +5,8 @@ import pytest from backend.app import create_app from backend.app.models import ImageRecord +pytestmark = pytest.mark.integration + @pytest.fixture async def app(): diff --git a/tests/test_api_import_admin.py b/tests/test_api_import_admin.py index b64c415..c0c82a1 100644 --- a/tests/test_api_import_admin.py +++ b/tests/test_api_import_admin.py @@ -6,6 +6,8 @@ from backend.app import create_app from backend.app.celery_app import celery from backend.app.models import ImportBatch, ImportTask +pytestmark = pytest.mark.integration + @pytest.fixture(autouse=True) def eager(): diff --git a/tests/test_api_settings.py b/tests/test_api_settings.py index bcdb46b..e3f214a 100644 --- a/tests/test_api_settings.py +++ b/tests/test_api_settings.py @@ -2,6 +2,8 @@ import pytest from backend.app import create_app +pytestmark = pytest.mark.integration + @pytest.fixture async def app(): diff --git a/tests/test_api_tags.py b/tests/test_api_tags.py index 9699d7f..39b45a9 100644 --- a/tests/test_api_tags.py +++ b/tests/test_api_tags.py @@ -2,6 +2,8 @@ import pytest from backend.app import create_app +pytestmark = pytest.mark.integration + @pytest.fixture async def app(): diff --git a/tests/test_gallery_service.py b/tests/test_gallery_service.py index 9a5fe70..7be625f 100644 --- a/tests/test_gallery_service.py +++ b/tests/test_gallery_service.py @@ -10,6 +10,8 @@ from backend.app.services.gallery_service import ( encode_cursor, ) +pytestmark = pytest.mark.integration + def _now(): return datetime.now(UTC) diff --git a/tests/test_importer.py b/tests/test_importer.py index 5739355..6d1acc9 100644 --- a/tests/test_importer.py +++ b/tests/test_importer.py @@ -11,6 +11,8 @@ import pytest from PIL import Image from sqlalchemy import select +pytestmark = pytest.mark.integration + from backend.app.models import Artist, ImageRecord, ImportSettings, Tag, TagKind from backend.app.services.importer import Importer, SkipReason from backend.app.services.thumbnailer import Thumbnailer diff --git a/tests/test_maintenance.py b/tests/test_maintenance.py index ad70df9..0e19895 100644 --- a/tests/test_maintenance.py +++ b/tests/test_maintenance.py @@ -9,6 +9,8 @@ import pytest from backend.app.celery_app import celery from backend.app.models import ImportBatch, ImportTask +pytestmark = pytest.mark.integration + @pytest.fixture(autouse=True) def eager(): diff --git a/tests/test_tag_service.py b/tests/test_tag_service.py index 65a8f26..a8f6b4c 100644 --- a/tests/test_tag_service.py +++ b/tests/test_tag_service.py @@ -3,6 +3,8 @@ import pytest from backend.app.models import TagKind from backend.app.services.tag_service import TagService, TagValidationError +pytestmark = pytest.mark.integration + @pytest.mark.asyncio async def test_find_or_create_creates(db):