Release: dev → main (Discord poster picker, chunk import deadline, CI repairs) #260
@@ -85,6 +85,29 @@ def db_sync(sync_engine):
|
||||
session.rollback()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def celery_conf():
|
||||
"""Set Celery config keys for one test: `celery_conf(task_always_eager=True)`.
|
||||
The values they had before the test come back after it.
|
||||
|
||||
Use this, not `monkeypatch.setattr(celery.conf, ...)`. Celery serves config
|
||||
keys through `__getattr__`, so monkeypatch sees no attribute to restore and
|
||||
deletes the key on undo instead; the delete raises, and every later undo of
|
||||
that test is skipped, leaking its patches into the tests after it.
|
||||
"""
|
||||
from backend.app.celery_app import celery
|
||||
|
||||
saved: dict[str, object] = {}
|
||||
|
||||
def _set(**values: object) -> None:
|
||||
for key in values:
|
||||
saved.setdefault(key, celery.conf[key])
|
||||
celery.conf.update(values)
|
||||
|
||||
yield _set
|
||||
celery.conf.update(saved)
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def app():
|
||||
return create_app()
|
||||
|
||||
@@ -11,15 +11,14 @@ import pytest
|
||||
from PIL import Image
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from backend.app.celery_app import celery
|
||||
from backend.app.models import ImageRecord, LibraryAuditRun
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def disable_celery_eager(monkeypatch):
|
||||
monkeypatch.setattr(celery.conf, "task_always_eager", False)
|
||||
def disable_celery_eager(celery_conf):
|
||||
celery_conf(task_always_eager=False)
|
||||
|
||||
|
||||
def _sha256_min_dim_token(min_w: int, min_h: int) -> str:
|
||||
|
||||
@@ -167,7 +167,7 @@ async def test_finalize_soft_limited_is_noop_without_running_event(db_sync):
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_download_source_catches_soft_limit_and_salvages_event(
|
||||
db_sync, monkeypatch,
|
||||
db_sync, monkeypatch, celery_conf,
|
||||
):
|
||||
"""End-to-end wiring: when the inner run raises SoftTimeLimitExceeded,
|
||||
download_source's handler must flip the in-flight event to error
|
||||
@@ -178,8 +178,7 @@ async def test_download_source_catches_soft_limit_and_salvages_event(
|
||||
import backend.app.tasks.download as dl
|
||||
from backend.app.models import DownloadEvent
|
||||
|
||||
monkeypatch.setattr(celery.conf, "task_always_eager", True)
|
||||
monkeypatch.setattr(celery.conf, "task_eager_propagates", False)
|
||||
celery_conf(task_always_eager=True, task_eager_propagates=False)
|
||||
# This test exercises the soft-limit salvage path, not the per-platform
|
||||
# serialization. Neutralize the Redis lock so it can't defer/recurse under
|
||||
# eager mode (and so it doesn't contend with test_platform_lock).
|
||||
|
||||
@@ -20,10 +20,9 @@ pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _eager_celery(monkeypatch):
|
||||
def _eager_celery(celery_conf):
|
||||
"""task_always_eager so signals fire in-process during the test."""
|
||||
monkeypatch.setattr(celery.conf, "task_always_eager", True)
|
||||
monkeypatch.setattr(celery.conf, "task_eager_propagates", False)
|
||||
celery_conf(task_always_eager=True, task_eager_propagates=False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -15,9 +15,8 @@ pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _eager_celery(monkeypatch):
|
||||
monkeypatch.setattr(celery.conf, "task_always_eager", True)
|
||||
monkeypatch.setattr(celery.conf, "task_eager_propagates", False)
|
||||
def _eager_celery(celery_conf):
|
||||
celery_conf(task_always_eager=True, task_eager_propagates=False)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -10,16 +10,14 @@ from pathlib import Path
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from backend.app.celery_app import celery
|
||||
from backend.app.models import BackupRun, ImportSettings
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _eager_celery(monkeypatch):
|
||||
monkeypatch.setattr(celery.conf, "task_always_eager", True)
|
||||
monkeypatch.setattr(celery.conf, "task_eager_propagates", False)
|
||||
def _eager_celery(celery_conf):
|
||||
celery_conf(task_always_eager=True, task_eager_propagates=False)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -9,7 +9,6 @@ from PIL import Image
|
||||
from sqlalchemy import select
|
||||
|
||||
import backend.app.tasks.library_audit # noqa: F401 — celery registration
|
||||
from backend.app import celery_app
|
||||
from backend.app.models import ImageRecord, LibraryAuditRun
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
@@ -37,7 +36,7 @@ def _mk_image(db_sync, tmp_path, *, mode, color, name):
|
||||
|
||||
|
||||
def test_scan_library_for_rule_populates_matched_ids_for_transparency(
|
||||
db_sync, tmp_path, monkeypatch,
|
||||
db_sync, tmp_path, celery_conf,
|
||||
):
|
||||
transparent_rec, _ = _mk_image(
|
||||
db_sync, tmp_path, mode="RGBA", color=(0, 0, 0, 0), name="trans.png",
|
||||
@@ -53,10 +52,10 @@ def test_scan_library_for_rule_populates_matched_ids_for_transparency(
|
||||
db_sync.commit()
|
||||
audit_id = audit.id
|
||||
|
||||
monkeypatch.setattr(celery_app.celery.conf, "task_always_eager", True)
|
||||
celery_conf(task_always_eager=True)
|
||||
from backend.app.tasks.library_audit import scan_library_for_rule
|
||||
scan_library_for_rule.run(audit_id)
|
||||
monkeypatch.setattr(celery_app.celery.conf, "task_always_eager", False)
|
||||
celery_conf(task_always_eager=False)
|
||||
|
||||
matched = db_sync.execute(
|
||||
select(LibraryAuditRun.matched_ids).where(LibraryAuditRun.id == audit_id)
|
||||
@@ -124,7 +123,7 @@ def test_scan_resumes_and_accumulates_matched(db_sync, tmp_path, monkeypatch):
|
||||
|
||||
|
||||
def test_scan_library_for_rule_skips_missing_files_gracefully(
|
||||
db_sync, tmp_path, monkeypatch,
|
||||
db_sync, tmp_path, celery_conf,
|
||||
):
|
||||
rec, path = _mk_image(
|
||||
db_sync, tmp_path, mode="RGBA", color=(0, 0, 0, 0), name="ghost.png",
|
||||
@@ -138,10 +137,10 @@ def test_scan_library_for_rule_skips_missing_files_gracefully(
|
||||
db_sync.commit()
|
||||
audit_id = audit.id
|
||||
|
||||
monkeypatch.setattr(celery_app.celery.conf, "task_always_eager", True)
|
||||
celery_conf(task_always_eager=True)
|
||||
from backend.app.tasks.library_audit import scan_library_for_rule
|
||||
scan_library_for_rule.run(audit_id)
|
||||
monkeypatch.setattr(celery_app.celery.conf, "task_always_eager", False)
|
||||
celery_conf(task_always_eager=False)
|
||||
|
||||
status = db_sync.execute(
|
||||
select(LibraryAuditRun.status).where(LibraryAuditRun.id == audit_id)
|
||||
|
||||
Reference in New Issue
Block a user