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):