Class: DevDoc::Test::Lints::CronScheduleChecker
- Inherits:
-
Object
- Object
- DevDoc::Test::Lints::CronScheduleChecker
- Defined in:
- lib/dev_doc/test/lints/cron_schedule.rb
Overview
Framework-agnostic check: reads a sidekiq-cron-style schedules.yml
and returns a list of offender descriptions for any cron expression
that fires less often than max_interval_seconds, and optionally
checks that each job's staleness threshold is greater than the cron
interval.
Wrapped by the Minitest module CronSchedule below — see that
module for the rationale and examples. Tests cover the checker
directly so they don't have to mock a test framework.
Constant Summary collapse
- DEFAULT_MAX_INTERVAL_SECONDS =
1 day. Weekly (and longer) crons are always wrong: no staleness threshold can usefully exceed the cron interval, so a weekly cron can't be paired with a meaningful self-throttle. Daily cron is the most generous interval the pattern supports (works for weekly-desired refreshes like Myki). Hourly+ is always safe.
86_400- MissingFile =
Sentinel returned by
#offenderswhen the schedules file doesn't exist. Distinguishable from[](file present, all good) so the Minitest wrapper canskiprather thanassert. Module.new
- DEFAULT_STALENESS_CONSTANT =
Default constant name to look up in job source files when checking that the staleness threshold is larger than the cron interval.
'STALENESS_THRESHOLD'.freeze
- SAMPLE_ANCHOR =
Anchored reference for sampling cron fires. A fixed Monday so weekday-restricted crons behave the same regardless of when the test suite runs.
Time.utc(2025, 1, 6).freeze
- SAMPLE_COUNT =
How many consecutive fires to sample. 100 covers:
- sub-hourly crons (max gap surfaces within minutes) - business-hours patterns like `0 9-17 * * *` (overnight gap visible after the first day) - weekly/monthly (max gap visible within a few cycles) - yearly (still gets ≥2 fires) 100
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(schedules_path, max_interval_seconds: DEFAULT_MAX_INTERVAL_SECONDS) ⇒ CronScheduleChecker
constructor
A new instance of CronScheduleChecker.
-
#offenders ⇒ Object
Returns an Array
of offender descriptions (one per invalid or too-infrequent cron), []if all crons are compliant, orMissingFileif the schedules file doesn't exist. -
#staleness_offenders(jobs_path: nil, constant: DEFAULT_STALENESS_CONSTANT) ⇒ Object
Checks that each job's staleness threshold constant is strictly greater than the cron interval.
Constructor Details
#initialize(schedules_path, max_interval_seconds: DEFAULT_MAX_INTERVAL_SECONDS) ⇒ CronScheduleChecker
Returns a new instance of CronScheduleChecker.
100 101 102 103 |
# File 'lib/dev_doc/test/lints/cron_schedule.rb', line 100 def initialize(schedules_path, max_interval_seconds: DEFAULT_MAX_INTERVAL_SECONDS) @schedules_path = schedules_path @max_interval_seconds = max_interval_seconds end |
Class Method Details
.format_seconds(seconds) ⇒ Object
146 147 148 149 150 151 152 |
# File 'lib/dev_doc/test/lints/cron_schedule.rb', line 146 def self.format_seconds(seconds) return "#{seconds}s" if seconds < 60 return "#{seconds / 60}m" if seconds < 3600 return "#{seconds / 3600}h" if seconds < 86_400 "#{seconds / 86_400}d" end |
Instance Method Details
#offenders ⇒ Object
Returns an Array[] if all crons are
compliant, or MissingFile if the schedules file doesn't
exist.
109 110 111 112 113 |
# File 'lib/dev_doc/test/lints/cron_schedule.rb', line 109 def offenders return MissingFile unless @schedules_path.exist? schedule_entries.filter_map { |name, cron, _opts| interval_offense(name, cron) } end |
#staleness_offenders(jobs_path: nil, constant: DEFAULT_STALENESS_CONSTANT) ⇒ Object
Checks that each job's staleness threshold constant is strictly
greater than the cron interval. Returns an Array[] if all are compliant, or MissingFile
if the schedules file doesn't exist.
Parameters:
jobs_path - Pathname/String pointing to `app/jobs/` (or any
directory tree to search for job source files).
If nil or the path doesn't exist, returns `[]`
(nothing to check — silently skipped so projects
without job-level constants don't have to opt out).
constant - Name of the constant to look up (default:
`STALENESS_THRESHOLD`). Projects that use a
different convention (e.g. `STALE_AFTER`) can
override this.
The constant value is extracted by grepping the job source files for
the pattern CONSTANT = <number>.<unit> (e.g. 1.hour, 45.minutes).
Jobs that use a per-record stale? method instead of a job-level
constant produce no constant to inspect and are skipped silently.
135 136 137 138 139 140 141 142 143 144 |
# File 'lib/dev_doc/test/lints/cron_schedule.rb', line 135 def staleness_offenders(jobs_path: nil, constant: DEFAULT_STALENESS_CONSTANT) return MissingFile unless @schedules_path.exist? jobs_root = jobs_path && Pathname.new(jobs_path) return [] unless jobs_root&.exist? schedule_entries.filter_map do |name, cron, opts| staleness_offense(name, cron, opts['class'], jobs_root, constant) end end |