diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_contributions_api.py | 45 | ||||
| -rw-r--r-- | tests/test_ingestion.py | 88 | ||||
| -rw-r--r-- | tests/test_migrations.py | 9 | ||||
| -rw-r--r-- | tests/test_polling_api.py | 4 |
4 files changed, 143 insertions, 3 deletions
diff --git a/tests/test_contributions_api.py b/tests/test_contributions_api.py index e568feb..29b19fb 100644 --- a/tests/test_contributions_api.py +++ b/tests/test_contributions_api.py @@ -7,6 +7,11 @@ from srht_contrib.main import create_app from srht_contrib.models import ContributionEvent, TrackedActor +class _Closable: + def close(self) -> None: + return None + + def test_read_only_contribution_routes_are_public_and_write_routes_require_api_key(settings, db_engine, session_factory) -> None: app = create_app(settings, engine=db_engine, session_factory=session_factory) with TestClient(app) as open_client: @@ -72,6 +77,24 @@ def test_public_read_registers_actor_for_lazy_indexing(client: TestClient, db_se assert tracked_actor is not None assert tracked_actor.is_active is True assert tracked_actor.last_requested_at is not None + assert tracked_actor.priority_boosted_at is None + + +class RecordingPriorityPoller: + def __init__(self) -> None: + service = type("Service", (), {"client": _Closable()})() + self.todo_service = service + self.git_service = service + self.calls: list[tuple[str, bool]] = [] + + def track_actor_request(self, db, actor: str, *, update_last_requested: bool = True, prioritize: bool = False): + self.calls.append((actor, prioritize)) + + def poll_all(self, db, actor: str) -> int: + return 0 + + def poll_tracked_actors(self, db, default_actor: str | None = None) -> dict[str, int]: + return {} def test_contribution_stats_api(client: TestClient, db_session) -> None: @@ -148,3 +171,25 @@ def test_contribution_routes_use_settings_backed_alias_resolution(settings, db_e assert response.status_code == 200 assert response.json()["actor"] == "~ccleberg" + + +def test_contribution_route_passes_explicit_priority_signal(settings, db_engine, session_factory) -> None: + poller = RecordingPriorityPoller() + app = create_app(settings, engine=db_engine, session_factory=session_factory, poller=poller) + + with TestClient(app) as client: + response = client.get("/api/contributions/~ccleberg?from=2026-03-28&to=2026-03-30&prioritize_self=true") + + assert response.status_code == 200 + assert poller.calls == [("~ccleberg", True)] + + +def test_contribution_stats_route_keeps_non_prioritized_registration_by_default(settings, db_engine, session_factory) -> None: + poller = RecordingPriorityPoller() + app = create_app(settings, engine=db_engine, session_factory=session_factory, poller=poller) + + with TestClient(app) as client: + response = client.get("/api/contributions/~ccleberg/stats?from=2026-03-28&to=2026-03-30") + + assert response.status_code == 200 + assert poller.calls == [("~ccleberg", False)] diff --git a/tests/test_ingestion.py b/tests/test_ingestion.py index 6a1a162..6c06359 100644 --- a/tests/test_ingestion.py +++ b/tests/test_ingestion.py @@ -711,6 +711,94 @@ def test_poll_tracked_actors_limits_to_due_batch_size(db_session) -> None: assert actors["~c"].poll_attempts == 0 +def test_track_actor_request_prioritize_marks_actor_boosted_and_due_now(db_session) -> None: + settings = make_settings(INDEXED_ACTOR_REPOLL_SECONDS=3600) + poller = PollerService(todo_service=RecordingTodoService(events_by_call=[[]]), git_service=EmptyGitService(), settings=settings) + future_due = datetime.now(tz=UTC) + timedelta(hours=2) + db_session.add( + TrackedActor( + actor="~self", + is_active=True, + discovery_state="indexed", + queued_for_discovery_at=datetime.now(tz=UTC) - timedelta(hours=1), + next_poll_after=future_due, + recent_backfill_status="completed", + ) + ) + db_session.commit() + + tracked_actor = poller.track_actor_request(db_session, "~self", prioritize=True) + + assert tracked_actor.priority_boosted_at is not None + assert tracked_actor.next_poll_after is not None + assert tracked_actor.next_poll_after <= tracked_actor.priority_boosted_at + + +def test_poll_tracked_actors_prioritizes_boosted_due_actor_first(db_session) -> None: + settings = make_settings(DISCOVERY_BATCH_SIZE=1, INDEXED_ACTOR_REPOLL_SECONDS=3600) + todo_service = RecordingTodoService(events_by_call=[[]]) + poller = PollerService(todo_service=todo_service, git_service=EmptyGitService(), settings=settings) + + now = datetime.now(tz=UTC) + db_session.add_all( + [ + TrackedActor( + actor="~normal", + is_active=True, + discovery_state="queued", + queued_for_discovery_at=now - timedelta(minutes=10), + next_poll_after=now - timedelta(minutes=10), + recent_backfill_status="completed", + ), + TrackedActor( + actor="~self", + is_active=True, + discovery_state="queued", + queued_for_discovery_at=now - timedelta(minutes=1), + next_poll_after=now - timedelta(minutes=1), + priority_boosted_at=now, + recent_backfill_status="completed", + ), + ] + ) + db_session.commit() + + results = poller.poll_tracked_actors(db_session) + + assert list(results) == ["~self"] + remaining = { + actor.actor: actor.discovery_state + for actor in db_session.scalars(select(TrackedActor).order_by(TrackedActor.actor)).all() + } + assert remaining["~self"] == "indexed" + assert remaining["~normal"] == "queued" + + +def test_successful_poll_clears_temporary_priority_boost(db_session) -> None: + settings = make_settings(INDEXED_ACTOR_REPOLL_SECONDS=3600) + poller = PollerService(todo_service=RecordingTodoService(events_by_call=[[]]), git_service=EmptyGitService(), settings=settings) + now = datetime.now(tz=UTC) + db_session.add( + TrackedActor( + actor="~self", + is_active=True, + discovery_state="queued", + queued_for_discovery_at=now - timedelta(minutes=1), + next_poll_after=now - timedelta(minutes=1), + priority_boosted_at=now - timedelta(seconds=30), + recent_backfill_status="completed", + ) + ) + db_session.commit() + + poller.poll_all(db_session, "~self") + + tracked_actor = db_session.scalar(select(TrackedActor).where(TrackedActor.actor == "~self")) + assert tracked_actor is not None + assert tracked_actor.discovery_state == "indexed" + assert tracked_actor.priority_boosted_at is None + + def test_enqueue_actors_staggers_without_polling(tmp_path, monkeypatch) -> None: database_path = tmp_path / "enqueue.db" username_path = tmp_path / "srht_usernames.txt" diff --git a/tests/test_migrations.py b/tests/test_migrations.py index 3ce842b..e646ccf 100644 --- a/tests/test_migrations.py +++ b/tests/test_migrations.py @@ -122,7 +122,14 @@ def test_alembic_upgrade_adopts_legacy_schema(tmp_path) -> None: assert "discovered_repositories" in inspector.get_table_names() assert "tracked_actors" in inspector.get_table_names() assert "service_backfill_states" in inspector.get_table_names() - assert {"discovery_state", "queued_for_discovery_at", "next_poll_after", "last_claimed_at", "poll_attempts"} <= tracked_actor_columns + assert { + "discovery_state", + "queued_for_discovery_at", + "priority_boosted_at", + "next_poll_after", + "last_claimed_at", + "poll_attempts", + } <= tracked_actor_columns def test_alembic_prefers_database_url_from_environment(tmp_path, monkeypatch) -> None: diff --git a/tests/test_polling_api.py b/tests/test_polling_api.py index b752524..94188da 100644 --- a/tests/test_polling_api.py +++ b/tests/test_polling_api.py @@ -21,7 +21,7 @@ class InsertingPoller: self.git_service = service self.tracked_poll_calls: list[str] = [] - def track_actor_request(self, db, actor: str, *, update_last_requested: bool = True): + def track_actor_request(self, db, actor: str, *, update_last_requested: bool = True, prioritize: bool = False): tracked_actor = db.scalar(select(TrackedActor).where(TrackedActor.actor == actor)) if tracked_actor is None: tracked_actor = TrackedActor(actor=actor, is_active=True) @@ -61,7 +61,7 @@ class FailingPoller: self.todo_service = service self.git_service = service - def track_actor_request(self, db, actor: str, *, update_last_requested: bool = True): + def track_actor_request(self, db, actor: str, *, update_last_requested: bool = True, prioritize: bool = False): tracked_actor = db.scalar(select(TrackedActor).where(TrackedActor.actor == actor)) if tracked_actor is None: tracked_actor = TrackedActor(actor=actor, is_active=True) |
