test(scheduler): raise interloper-scheduler coverage from 90% to 100% - #318
Merged
Conversation
Closes the last 62 uncovered statements in interloper-scheduler; every module is now fully covered. The two real gaps were the executor (76%) and the reaper (78%), and both are the failure machinery rather than the happy path — the parts that decide whether a stuck run gets unstuck. Covered now: a run that cannot be executed is skipped rather than half-started, a component whose kind declares no workload fails the run instead of silently succeeding, and both halves of the reaper's failure record (the RUN_FAILED event and the terminal status) survive the other one raising. A launcher whose `describe_run` throws falls through to the dispatch timeout rather than leaving the run dispatched forever. The rest is the platform side of graph assembly: the transitive upstream walk that joins dependencies as non-materializable context, loading each row once and never recomputing it, and the failed-only retry that skips the lineage's prior successes while a whole-run retry recomputes everything. Also covered: the reaper's hourly usage reconciliation (advisory, and a failure there must not take the loop down), the controller's KeyboardInterrupt path, `Launcher.describe_run`'s default of "cannot introspect", the hook sweep's first-tick watermark and its four evaluate guards, `auto_renew` defaulting on and skipping a connection whose config will not decode, and a cron fire time that comes back naive — which must be anchored in the job's zone rather than read as UTC. The two tests that needed care are noted where they sit: the naive datetime carries a `noqa: DTZ001` because producing one is the point, and a job with no cron expression still needs `enabled` in its config or the sweep never selects it to skip. By Digitl
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last 62 uncovered statements in
interloper-scheduler. Every module is now at 100%. Tests only: no source file changes.Coverage
executor.pyreaper.pyrenewal.pycontroller.py,hooks.pycron.py,launcher.pyqueue.pyThe layout already mirrored the package one-to-one (8 flat modules, 8 test files), so there was nothing to move.
What the gaps actually were
Both real gaps — the executor and the reaper — were failure machinery, not happy paths: the code that decides whether a stuck run gets unstuck. That's now covered:
RUN_FAILEDeventcompleteis logged rather than escapingdescribe_runthrows falls through to the dispatch timeout rather than leaving the run dispatched foreverThe rest is the platform side of graph assembly: the transitive upstream walk that joins dependencies as non-materializable context (loaded once, never recomputed), and the failed-only retry that skips the lineage's prior successes while a whole-run retry recomputes everything.
Plus the reaper's hourly usage reconciliation (advisory only — and a failure there must not take the loop down), the controller's
KeyboardInterruptpath,Launcher.describe_run's default of "cannot introspect", the hook sweep's first-tick watermark and its four_evaluateguards, andauto_renewdefaulting on while skipping a connection whose config will not decode.Two tests that needed care
A naive cron fire time.
croniternormally returns aware datetimes; a naive one must be anchored in the job's zone, not read as UTC. The test asserts 02:00 Berlin → 00:00 UTC and carries a# noqa: DTZ001, because producing a naive datetime is precisely the branch under test.A job with no cron expression. My first attempt gave it
config={}and the branch stayed uncovered — the sweep filters onconfig["enabled"], so the job was never selected to be skipped. It needs{"enabled": True}with nocron.Fake-store typing
_RecordingStorestands in forStoreat 17 construction sites. Rather than 17# ty: ignorecomments, there's one_executor(store)helper that casts once — same spirit as thecast(...)approach in the api and db suites.Verification
uv run ruff check,uv run ty check,uv run pytest packages/interloper-scheduler(111 passed, 100%) and the full workspace suite (2574 passed, 2 skipped) all green; the scheduler suite is stable across three runs. Pre-commit ran the same three hooks on the commit.By Digitl