Skip to content

test(scheduler): raise interloper-scheduler coverage from 90% to 100% - #318

Merged
aaaaahaaaaa merged 1 commit into
mainfrom
test/interloper-scheduler-coverage
Sep 4, 2026
Merged

test(scheduler): raise interloper-scheduler coverage from 90% to 100%#318
aaaaahaaaaa merged 1 commit into
mainfrom
test/interloper-scheduler-coverage

Conversation

@aaaaahaaaaa

Copy link
Copy Markdown
Contributor

Closes the last 62 uncovered statements in interloper-scheduler. Every module is now at 100%. Tests only: no source file changes.

Coverage

Module Before After
executor.py 76% 100%
reaper.py 78% 100%
renewal.py 93% 100%
controller.py, hooks.py 95% 100%
cron.py, launcher.py 98% 100%
queue.py 100% 100%

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

  • a run that cannot be executed (missing, or with no component) is skipped rather than half-started
  • a component whose kind declares no workload fails the run instead of silently succeeding, and the failure is reported as a RUN_FAILED event
  • both halves of the reaper's failure record survive the other one raising — a broken event save still leaves the run marked failed, which is what unsticks it, and a broken complete is logged rather than escaping
  • 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 (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 KeyboardInterrupt path, Launcher.describe_run's default of "cannot introspect", the hook sweep's first-tick watermark and its four _evaluate guards, and auto_renew defaulting on while skipping a connection whose config will not decode.

Two tests that needed care

A naive cron fire time. croniter normally 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 on config["enabled"], so the job was never selected to be skipped. It needs {"enabled": True} with no cron.

Fake-store typing

_RecordingStore stands in for Store at 17 construction sites. Rather than 17 # ty: ignore comments, there's one _executor(store) helper that casts once — same spirit as the cast(...) 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

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

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@aaaaahaaaaa
aaaaahaaaaa merged commit 5260b5b into main Sep 4, 2026
11 checks passed
@aaaaahaaaaa
aaaaahaaaaa deleted the test/interloper-scheduler-coverage branch September 4, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant