From cc3b42eaf690284afe91e3ffeb6d52d7aa856bf9 Mon Sep 17 00:00:00 2001 From: IK Date: Thu, 10 Sep 2026 05:46:49 +0700 Subject: [PATCH] =?UTF-8?q?R12:=20Fix=20readiness=20=E2=80=94=20stale/fail?= =?UTF-8?q?ed=20community=5Fscheduler=20blocks=20ready?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 'ready = ready and healthy' for community_scheduler check - Add 'ready = False' for community_scheduler exception path - Add 3 unit tests: success=ready, stale=not_ready, failed=not_ready - Monitoring now correctly reports community import health --- apps/api/app/readiness.py | 2 + apps/api/tests/test_readiness.py | 67 +++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/apps/api/app/readiness.py b/apps/api/app/readiness.py index fe85fec..1340ceb 100644 --- a/apps/api/app/readiness.py +++ b/apps/api/app/readiness.py @@ -78,7 +78,9 @@ def readiness_report( "status": "ready" if healthy else ("stale" if stale else latest_community.status), "last_started_at": started.isoformat(), } + ready = ready and healthy except Exception: components["community_scheduler"] = {"status": "unknown"} + ready = False return ready, components diff --git a/apps/api/tests/test_readiness.py b/apps/api/tests/test_readiness.py index 2187622..4dcc1de 100644 --- a/apps/api/tests/test_readiness.py +++ b/apps/api/tests/test_readiness.py @@ -6,7 +6,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import Session from app.database import Base -from app.models import ImportStatus, OfficialRecordImport +from app.models import ImportStatus, CommunityImportRun, OfficialRecordImport from app.readiness import readiness_report @@ -72,3 +72,68 @@ def test_unavailable_storage_and_stale_import_fail_readiness() -> None: assert ready is False assert components["minio"]["status"] == "unavailable" assert components["official_import"]["status"] == "stale" + + +def test_community_scheduler_success_does_not_block_readiness() -> None: + engine = create_engine("sqlite://") + Base.metadata.create_all(engine) + now = datetime.now(timezone.utc) + with Session(engine) as session: + session.add(CommunityImportRun( + source_system="rf4db", + started_at=now - timedelta(minutes=30), + status="success", + source_url="fixture://rf4db", + rows_seen=5, rows_created=5, rows_updated=0, error_summary=None, + )) + session.commit() + ready, components = readiness_report( + session, AvailableStorage(), import_required=False, + import_interval_seconds=3600, community_import_interval_seconds=1800, now=now, + ) + assert ready is True + assert components["community_scheduler"]["status"] == "ready" + + +def test_community_scheduler_stale_or_failed_blocks_readiness() -> None: + engine = create_engine("sqlite://") + Base.metadata.create_all(engine) + now = datetime.now(timezone.utc) + with Session(engine) as session: + # Stale run + session.add(CommunityImportRun( + source_system="rf4db", + started_at=now - timedelta(hours=2), + status="success", + source_url="fixture://rf4db", + rows_seen=5, rows_created=5, rows_updated=0, error_summary=None, + )) + session.commit() + ready, components = readiness_report( + session, AvailableStorage(), import_required=False, + import_interval_seconds=3600, community_import_interval_seconds=1800, now=now, + ) + assert ready is False + assert components["community_scheduler"]["status"] == "stale" + + +def test_community_scheduler_failed_status_blocks_readiness() -> None: + engine = create_engine("sqlite://") + Base.metadata.create_all(engine) + now = datetime.now(timezone.utc) + with Session(engine) as session: + session.add(CommunityImportRun( + source_system="rf4db", + started_at=now - timedelta(minutes=30), + status="failed", + source_url="fixture://rf4db", + rows_seen=0, rows_created=0, rows_updated=0, + error_summary="ConnectionError", + )) + session.commit() + ready, components = readiness_report( + session, AvailableStorage(), import_required=False, + import_interval_seconds=3600, community_import_interval_seconds=1800, now=now, + ) + assert ready is False + assert components["community_scheduler"]["status"] == "failed"