delayed 2.0.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,267 +14,338 @@ RSpec.describe Delayed::Monitor do
14
14
  }
15
15
  end
16
16
 
17
- it 'emits empty metrics for all default priorities' do
18
- expect { subject.run! }
19
- .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
20
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
21
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
22
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
23
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
24
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
25
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
26
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
27
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
28
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
29
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
30
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
31
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
32
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
33
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
34
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
35
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
36
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
37
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
38
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
39
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
40
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
41
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
42
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
43
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
44
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
45
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
46
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
47
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
48
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
49
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
50
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
51
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
52
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
53
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
54
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
55
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
56
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
57
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
58
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
59
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
60
- end
17
+ describe '#run!' do
18
+ let(:app_local_db_time) { false }
61
19
 
62
- context 'when named priorities are customized' do
63
20
  around do |example|
64
- Delayed::Priority.names = { high: 0, low: 7 }
65
- example.run
66
- ensure
67
- Delayed::Priority.names = nil
68
- end
69
-
70
- it 'emits empty metrics for all custom priorities' do
71
- expect { subject.run! }
72
- .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
73
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
74
- .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
75
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
76
- .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
77
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
78
- .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
79
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
80
- .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
81
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
82
- .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
83
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
84
- .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
85
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
86
- .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
87
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'high')).with_value(0)
88
- .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'low')).with_value(0)
89
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'high')).with_value(0)
90
- .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'low')).with_value(0)
91
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'high')).with_value(0)
92
- .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'low')).with_value(0)
93
- end
94
- end
21
+ if app_local_db_time
22
+ Time.zone = 'US/Central'
23
+ self.default_timezone = :local
24
+ end
95
25
 
96
- context 'when there are jobs in the queue' do
97
- let(:now) { Delayed::Job.db_time_now.change(nsec: 0) }
98
- let(:job_attributes) do
99
- {
100
- run_at: now,
101
- queue: 'default',
102
- handler: "--- !ruby/object:SimpleJob\n",
103
- attempts: 0,
104
- }
26
+ # On PostgreSQL, running examples in a transaction allows CURRENT_TIMESTAMP to remain stable.
27
+ # We can in turn use this to set Timecop to the same time as the DB for deterministic time math.
28
+ Delayed::Job.transaction do
29
+ now = described_class.parse_utc_time(
30
+ Delayed::Job.connection.select_value("SELECT #{described_class.sql_now_in_utc}"),
31
+ )
32
+ Timecop.freeze(now) { example.run }
33
+ end
34
+ ensure
35
+ Time.zone = nil
36
+ self.default_timezone = :utc
105
37
  end
106
- let(:failed_attributes) { { run_at: now - 1.week, attempts: 1, failed_at: now - 1.day, locked_at: now - 1.day } }
107
- let(:p0_attributes) { job_attributes.merge(priority: 1, attempts: 1) }
108
- let(:p10_attributes) { job_attributes.merge(priority: 13, locked_at: now - 1.day) }
109
- let(:p20_attributes) { job_attributes.merge(priority: 23, attempts: 1) }
110
- let(:p30_attributes) { job_attributes.merge(priority: 999, locked_at: now - 1.day) }
111
- let(:p0_payload) { default_payload.merge(priority: 'interactive') }
112
- let(:p10_payload) { default_payload.merge(priority: 'user_visible') }
113
- let(:p20_payload) { default_payload.merge(priority: 'eventual') }
114
- let(:p30_payload) { default_payload.merge(priority: 'reporting') }
115
- let!(:p0_workable_job) { Delayed::Job.create! p0_attributes.merge(run_at: now - 30.seconds) }
116
- let!(:p0_failed_job) { Delayed::Job.create! p0_attributes.merge(failed_attributes) }
117
- let!(:p0_future_job) { Delayed::Job.create! p0_attributes.merge(run_at: now + 1.hour) }
118
- let!(:p0_working_job) { Delayed::Job.create! p0_attributes.merge(locked_at: now - 3.minutes) }
119
- let!(:p10_workable_job) { Delayed::Job.create! p10_attributes.merge(run_at: now - 2.minutes) }
120
- let!(:p10_failed_job) { Delayed::Job.create! p10_attributes.merge(failed_attributes) }
121
- let!(:p10_future_job) { Delayed::Job.create! p10_attributes.merge(run_at: now + 1.hour) }
122
- let!(:p10_working_job) { Delayed::Job.create! p10_attributes.merge(locked_at: now - 7.minutes) }
123
- let!(:p20_workable_job) { Delayed::Job.create! p20_attributes.merge(run_at: now - 1.hour) }
124
- let!(:p20_failed_job) { Delayed::Job.create! p20_attributes.merge(failed_attributes) }
125
- let!(:p20_future_job) { Delayed::Job.create! p20_attributes.merge(run_at: now + 1.hour) }
126
- let!(:p20_working_job) { Delayed::Job.create! p20_attributes.merge(locked_at: now - 9.minutes) }
127
- let!(:p30_workable_job) { Delayed::Job.create! p30_attributes.merge(run_at: now - 6.hours) }
128
- let!(:p30_failed_job) { Delayed::Job.create! p30_attributes.merge(failed_attributes) }
129
- let!(:p30_future_job) { Delayed::Job.create! p30_attributes.merge(run_at: now + 1.hour) }
130
- let!(:p30_working_job) { Delayed::Job.create! p30_attributes.merge(locked_at: now - 11.minutes) }
131
- let!(:p30_workable_job_in_other_queue) { Delayed::Job.create! p30_attributes.merge(run_at: now - 4.hours, queue: 'banana') }
132
38
 
133
- around do |example|
134
- Timecop.freeze(now) { example.run }
135
- end
39
+ let(:now) { Delayed::Job.db_time_now }
136
40
 
137
- it 'emits the expected results for each metric' do
41
+ it 'emits empty metrics for all default priorities' do
138
42
  expect { subject.run! }
139
43
  .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
140
- .and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(4)
141
- .and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(1)
142
- .and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(1)
143
- .and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(3)
144
- .and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(1)
145
- .and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(1)
146
- .and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(1)
147
- .and emit_notification("delayed.job.max_age").with_payload(p0_payload).with_value(30.seconds)
148
- .and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).with_value(3.minutes)
149
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(30.0.seconds / 1.minute * 100)
150
- .and emit_notification("delayed.job.count").with_payload(p10_payload).with_value(4)
151
- .and emit_notification("delayed.job.future_count").with_payload(p10_payload).with_value(1)
152
- .and emit_notification("delayed.job.locked_count").with_payload(p10_payload).with_value(1)
153
- .and emit_notification("delayed.job.erroring_count").with_payload(p10_payload).with_value(0)
154
- .and emit_notification("delayed.job.failed_count").with_payload(p10_payload).with_value(1)
155
- .and emit_notification("delayed.job.working_count").with_payload(p10_payload).with_value(1)
156
- .and emit_notification("delayed.job.workable_count").with_payload(p10_payload).with_value(1)
157
- .and emit_notification("delayed.job.max_age").with_payload(p10_payload).with_value(2.minutes)
158
- .and emit_notification("delayed.job.max_lock_age").with_payload(p10_payload).with_value(7.minutes)
159
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p10_payload).with_value(2.0.minutes / 3.minutes * 100)
160
- .and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(4)
161
- .and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(1)
162
- .and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(1)
163
- .and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(3)
164
- .and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(1)
165
- .and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(1)
166
- .and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(1)
167
- .and emit_notification("delayed.job.max_age").with_payload(p20_payload).with_value(1.hour)
168
- .and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).with_value(9.minutes)
169
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(1.hour / 1.5.hours * 100)
170
- .and emit_notification("delayed.job.count").with_payload(p30_payload).with_value(4)
171
- .and emit_notification("delayed.job.future_count").with_payload(p30_payload).with_value(1)
172
- .and emit_notification("delayed.job.locked_count").with_payload(p30_payload).with_value(1)
173
- .and emit_notification("delayed.job.erroring_count").with_payload(p30_payload).with_value(0)
174
- .and emit_notification("delayed.job.failed_count").with_payload(p30_payload).with_value(1)
175
- .and emit_notification("delayed.job.working_count").with_payload(p30_payload).with_value(1)
176
- .and emit_notification("delayed.job.workable_count").with_payload(p30_payload).with_value(1)
177
- .and emit_notification("delayed.job.max_age").with_payload(p30_payload).with_value(6.hours)
178
- .and emit_notification("delayed.job.max_lock_age").with_payload(p30_payload).with_value(11.minutes)
179
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p30_payload).with_value(100) # 6 hours / 4 hours (overflow)
180
- .and emit_notification("delayed.job.workable_count").with_payload(p30_payload.merge(queue: 'banana')).with_value(1)
181
- .and emit_notification("delayed.job.max_age").with_payload(p30_payload.merge(queue: 'banana')).with_value(4.hours)
44
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
45
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
46
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
47
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
48
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
49
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
50
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
51
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
52
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
53
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
54
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
55
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
56
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
57
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
58
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
59
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
60
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
61
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
62
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
63
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
64
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
65
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
66
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
67
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
68
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'interactive')).with_value(0)
69
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'user_visible')).with_value(0)
70
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'eventual')).with_value(0)
71
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'reporting')).with_value(0)
72
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'interactive')).approximately.with_value(0)
73
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'user_visible')).approximately.with_value(0)
74
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'eventual')).approximately.with_value(0)
75
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'reporting')).approximately.with_value(0)
76
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'interactive')).approximately.with_value(0)
77
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'user_visible')).approximately.with_value(0)
78
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'eventual')).approximately.with_value(0)
79
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'reporting')).approximately.with_value(0)
80
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'interactive')).approximately.with_value(0)
81
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'user_visible')).approximately.with_value(0)
82
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'eventual')).approximately.with_value(0)
83
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'reporting')).approximately.with_value(0)
182
84
  end
183
85
 
184
86
  context 'when named priorities are customized' do
185
87
  around do |example|
186
- Delayed::Priority.names = { high: 0, low: 20 }
88
+ Delayed::Priority.names = { high: 0, low: 7 }
187
89
  example.run
188
90
  ensure
189
91
  Delayed::Priority.names = nil
190
92
  end
191
- let(:p0_payload) { default_payload.merge(priority: 'high') }
192
- let(:p20_payload) { default_payload.merge(priority: 'low') }
93
+
94
+ it 'emits empty metrics for all custom priorities' do
95
+ expect { subject.run! }
96
+ .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
97
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
98
+ .and emit_notification("delayed.job.count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
99
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
100
+ .and emit_notification("delayed.job.future_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
101
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
102
+ .and emit_notification("delayed.job.locked_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
103
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
104
+ .and emit_notification("delayed.job.erroring_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
105
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
106
+ .and emit_notification("delayed.job.failed_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
107
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
108
+ .and emit_notification("delayed.job.working_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
109
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'high')).with_value(0)
110
+ .and emit_notification("delayed.job.workable_count").with_payload(default_payload.merge(priority: 'low')).with_value(0)
111
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'high')).approximately.with_value(0)
112
+ .and emit_notification("delayed.job.max_age").with_payload(default_payload.merge(priority: 'low')).approximately.with_value(0)
113
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'high')).approximately.with_value(0)
114
+ .and emit_notification("delayed.job.max_lock_age").with_payload(default_payload.merge(priority: 'low')).approximately.with_value(0)
115
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'high')).with_value(0)
116
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(default_payload.merge(priority: 'low')).with_value(0)
117
+ end
118
+ end
119
+
120
+ context 'when there are jobs in the queue' do
121
+ let(:job_attributes) do
122
+ {
123
+ run_at: now,
124
+ queue: 'default',
125
+ handler: "--- !ruby/object:SimpleJob\n",
126
+ attempts: 0,
127
+ }
128
+ end
129
+ let(:failed_attributes) { { run_at: now - 1.week, attempts: 1, failed_at: now - 1.day, locked_at: now - 1.day } }
130
+ let(:p0_attributes) { job_attributes.merge(priority: 1, attempts: 1) }
131
+ let(:p10_attributes) { job_attributes.merge(priority: 13, locked_at: now - 1.day) }
132
+ let(:p20_attributes) { job_attributes.merge(priority: 23, attempts: 1) }
133
+ let(:p30_attributes) { job_attributes.merge(priority: 999, locked_at: now - 1.day) }
134
+ let(:p0_payload) { default_payload.merge(priority: 'interactive') }
135
+ let(:p10_payload) { default_payload.merge(priority: 'user_visible') }
136
+ let(:p20_payload) { default_payload.merge(priority: 'eventual') }
137
+ let(:p30_payload) { default_payload.merge(priority: 'reporting') }
138
+ let!(:p0_workable_job) { Delayed::Job.create! p0_attributes.merge(run_at: now - 30.seconds) }
139
+ let!(:p0_failed_job) { Delayed::Job.create! p0_attributes.merge(failed_attributes) }
140
+ let!(:p0_future_job) { Delayed::Job.create! p0_attributes.merge(run_at: now + 1.hour) }
141
+ let!(:p0_working_job) { Delayed::Job.create! p0_attributes.merge(locked_at: now - 3.minutes) }
142
+ let!(:p10_workable_job) { Delayed::Job.create! p10_attributes.merge(run_at: now - 2.minutes) }
143
+ let!(:p10_failed_job) { Delayed::Job.create! p10_attributes.merge(failed_attributes) }
144
+ let!(:p10_future_job) { Delayed::Job.create! p10_attributes.merge(run_at: now + 1.hour) }
145
+ let!(:p10_working_job) { Delayed::Job.create! p10_attributes.merge(locked_at: now - 7.minutes) }
146
+ let!(:p20_workable_job) { Delayed::Job.create! p20_attributes.merge(run_at: now - 1.hour) }
147
+ let!(:p20_failed_job) { Delayed::Job.create! p20_attributes.merge(failed_attributes) }
148
+ let!(:p20_future_job) { Delayed::Job.create! p20_attributes.merge(run_at: now + 1.hour) }
149
+ let!(:p20_working_job) { Delayed::Job.create! p20_attributes.merge(locked_at: now - 9.minutes) }
150
+ let!(:p30_workable_job) { Delayed::Job.create! p30_attributes.merge(run_at: now - 6.hours) }
151
+ let!(:p30_failed_job) { Delayed::Job.create! p30_attributes.merge(failed_attributes) }
152
+ let!(:p30_future_job) { Delayed::Job.create! p30_attributes.merge(run_at: now + 1.hour) }
153
+ let!(:p30_working_job) { Delayed::Job.create! p30_attributes.merge(locked_at: now - 11.minutes) }
154
+ let!(:p30_workable_job_in_other_queue) { Delayed::Job.create! p30_attributes.merge(run_at: now - 4.hours, queue: 'banana') }
193
155
 
194
156
  it 'emits the expected results for each metric' do
195
157
  expect { subject.run! }
196
158
  .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
197
- .and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(8)
198
- .and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(2)
199
- .and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(2)
159
+ .and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(4)
160
+ .and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(1)
161
+ .and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(1)
200
162
  .and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(3)
201
- .and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(2)
202
- .and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(2)
203
- .and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(2)
204
- .and emit_notification("delayed.job.max_age").with_payload(p0_payload).with_value(2.minutes)
205
- .and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).with_value(7.minutes)
206
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(0)
207
- .and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(8)
208
- .and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(2)
209
- .and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(2)
163
+ .and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(1)
164
+ .and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(1)
165
+ .and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(1)
166
+ .and emit_notification("delayed.job.max_age").with_payload(p0_payload).approximately.with_value(30.seconds)
167
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).approximately.with_value(3.minutes)
168
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).approximately.with_value(30.0.seconds / 1.minute * 100)
169
+ .and emit_notification("delayed.job.count").with_payload(p10_payload).with_value(4)
170
+ .and emit_notification("delayed.job.future_count").with_payload(p10_payload).with_value(1)
171
+ .and emit_notification("delayed.job.locked_count").with_payload(p10_payload).with_value(1)
172
+ .and emit_notification("delayed.job.erroring_count").with_payload(p10_payload).with_value(0)
173
+ .and emit_notification("delayed.job.failed_count").with_payload(p10_payload).with_value(1)
174
+ .and emit_notification("delayed.job.working_count").with_payload(p10_payload).with_value(1)
175
+ .and emit_notification("delayed.job.workable_count").with_payload(p10_payload).with_value(1)
176
+ .and emit_notification("delayed.job.max_age").with_payload(p10_payload).approximately.with_value(2.minutes)
177
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p10_payload).approximately.with_value(7.minutes)
178
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p10_payload).approximately.with_value(2.0.minutes / 3.minutes * 100)
179
+ .and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(4)
180
+ .and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(1)
181
+ .and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(1)
210
182
  .and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(3)
211
- .and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(2)
212
- .and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(2)
213
- .and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(2)
214
- .and emit_notification("delayed.job.max_age").with_payload(p20_payload).with_value(6.hours)
215
- .and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).with_value(11.minutes)
216
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(0)
217
- .and emit_notification("delayed.job.workable_count").with_payload(p20_payload.merge(queue: 'banana')).with_value(1)
218
- .and emit_notification("delayed.job.max_age").with_payload(p20_payload.merge(queue: 'banana')).with_value(4.hours)
183
+ .and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(1)
184
+ .and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(1)
185
+ .and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(1)
186
+ .and emit_notification("delayed.job.max_age").with_payload(p20_payload).approximately.with_value(1.hour)
187
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).approximately.with_value(9.minutes)
188
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).approximately.with_value(1.hour / 1.5.hours * 100)
189
+ .and emit_notification("delayed.job.count").with_payload(p30_payload).with_value(4)
190
+ .and emit_notification("delayed.job.future_count").with_payload(p30_payload).with_value(1)
191
+ .and emit_notification("delayed.job.locked_count").with_payload(p30_payload).with_value(1)
192
+ .and emit_notification("delayed.job.erroring_count").with_payload(p30_payload).with_value(0)
193
+ .and emit_notification("delayed.job.failed_count").with_payload(p30_payload).with_value(1)
194
+ .and emit_notification("delayed.job.working_count").with_payload(p30_payload).with_value(1)
195
+ .and emit_notification("delayed.job.workable_count").with_payload(p30_payload).with_value(1)
196
+ .and emit_notification("delayed.job.max_age").with_payload(p30_payload).approximately.with_value(6.hours)
197
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p30_payload).approximately.with_value(11.minutes)
198
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p30_payload).approximately.with_value(100) # 6 hours / 4 hours (overflow)
199
+ .and emit_notification("delayed.job.workable_count").with_payload(p30_payload.merge(queue: 'banana')).with_value(1)
200
+ .and emit_notification("delayed.job.max_age").with_payload(p30_payload.merge(queue: 'banana')).approximately.with_value(4.hours)
219
201
  end
220
202
 
221
- context 'when alert thresholds are specified' do
203
+ context 'when named priorities are customized' do
222
204
  around do |example|
223
- Delayed::Priority.alerts = { high: { age: 3.hours }, low: { age: 1.year } }
205
+ Delayed::Priority.names = { high: 0, low: 20 }
224
206
  example.run
225
207
  ensure
226
- Delayed::Priority.alerts = nil
208
+ Delayed::Priority.names = nil
227
209
  end
210
+ let(:p0_payload) { default_payload.merge(priority: 'high') }
211
+ let(:p20_payload) { default_payload.merge(priority: 'low') }
228
212
 
229
- it 'emits the expected alert_age_percent results' do
213
+ it 'emits the expected results for each metric' do
230
214
  expect { subject.run! }
231
- .to emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).with_value(2.0.minutes / 3.hours * 100)
232
- .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).with_value(6.0.hours / 1.year * 100)
215
+ .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
216
+ .and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(8)
217
+ .and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(2)
218
+ .and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(2)
219
+ .and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(3)
220
+ .and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(2)
221
+ .and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(2)
222
+ .and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(2)
223
+ .and emit_notification("delayed.job.max_age").with_payload(p0_payload).approximately.with_value(2.minutes)
224
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).approximately.with_value(7.minutes)
225
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).approximately.with_value(0)
226
+ .and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(8)
227
+ .and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(2)
228
+ .and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(2)
229
+ .and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(3)
230
+ .and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(2)
231
+ .and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(2)
232
+ .and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(2)
233
+ .and emit_notification("delayed.job.max_age").with_payload(p20_payload).approximately.with_value(6.hours)
234
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).approximately.with_value(11.minutes)
235
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).approximately.with_value(0)
236
+ .and emit_notification("delayed.job.workable_count").with_payload(p20_payload.merge(queue: 'banana')).with_value(1)
237
+ .and emit_notification("delayed.job.max_age").with_payload(p20_payload.merge(queue: 'banana')).approximately.with_value(4.hours)
238
+ end
239
+
240
+ context 'when alert thresholds are specified' do
241
+ around do |example|
242
+ Delayed::Priority.alerts = { high: { age: 3.hours }, low: { age: 1.year } }
243
+ example.run
244
+ ensure
245
+ Delayed::Priority.alerts = nil
246
+ end
247
+
248
+ it 'emits the expected alert_age_percent results' do
249
+ expect { subject.run! }
250
+ .to emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).approximately.with_value(2.0.minutes / 3.hours * 100)
251
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).approximately.with_value(6.0.hours / 1.year * 100)
252
+ end
233
253
  end
234
254
  end
235
- end
236
255
 
237
- context 'when worker queues are specified' do
238
- around do |example|
239
- Delayed::Worker.queues = %w(banana gram)
240
- Delayed::Priority.names = { interactive: 0 } # avoid splitting by priority for simplicity
241
- Delayed::Priority.alerts = { interactive: { age: 8.hours } }
242
- example.run
243
- ensure
244
- Delayed::Priority.names = nil
245
- Delayed::Worker.queues = []
256
+ context 'when worker queues are specified' do
257
+ around do |example|
258
+ Delayed::Worker.queues = %w(banana gram)
259
+ Delayed::Priority.names = { interactive: 0 } # avoid splitting by priority for simplicity
260
+ Delayed::Priority.alerts = { interactive: { age: 8.hours } }
261
+ example.run
262
+ ensure
263
+ Delayed::Priority.names = nil
264
+ Delayed::Worker.queues = []
265
+ end
266
+ let(:banana_payload) { default_payload.merge(queue: 'banana', priority: 'interactive') }
267
+ let(:gram_payload) { default_payload.merge(queue: 'gram', priority: 'interactive') }
268
+
269
+ it 'emits the expected results for each queue' do
270
+ expect { subject.run! }
271
+ .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
272
+ .and emit_notification("delayed.job.count").with_payload(banana_payload).with_value(1)
273
+ .and emit_notification("delayed.job.future_count").with_payload(banana_payload).with_value(0)
274
+ .and emit_notification("delayed.job.locked_count").with_payload(banana_payload).with_value(0)
275
+ .and emit_notification("delayed.job.erroring_count").with_payload(banana_payload).with_value(0)
276
+ .and emit_notification("delayed.job.failed_count").with_payload(banana_payload).with_value(0)
277
+ .and emit_notification("delayed.job.working_count").with_payload(banana_payload).with_value(0)
278
+ .and emit_notification("delayed.job.workable_count").with_payload(banana_payload).with_value(1)
279
+ .and emit_notification("delayed.job.max_age").with_payload(banana_payload).approximately.with_value(4.hours)
280
+ .and emit_notification("delayed.job.max_lock_age").with_payload(banana_payload).approximately.with_value(0)
281
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(banana_payload).approximately.with_value(4.0.hours / 8.hours * 100)
282
+ .and emit_notification("delayed.job.count").with_payload(gram_payload).with_value(0)
283
+ .and emit_notification("delayed.job.future_count").with_payload(gram_payload).with_value(0)
284
+ .and emit_notification("delayed.job.locked_count").with_payload(gram_payload).with_value(0)
285
+ .and emit_notification("delayed.job.erroring_count").with_payload(gram_payload).with_value(0)
286
+ .and emit_notification("delayed.job.failed_count").with_payload(gram_payload).with_value(0)
287
+ .and emit_notification("delayed.job.working_count").with_payload(gram_payload).with_value(0)
288
+ .and emit_notification("delayed.job.workable_count").with_payload(gram_payload).with_value(0)
289
+ .and emit_notification("delayed.job.max_age").with_payload(gram_payload).approximately.with_value(0)
290
+ .and emit_notification("delayed.job.max_lock_age").with_payload(gram_payload).approximately.with_value(0)
291
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(gram_payload).approximately.with_value(0)
292
+ end
246
293
  end
247
- let(:banana_payload) { default_payload.merge(queue: 'banana', priority: 'interactive') }
248
- let(:gram_payload) { default_payload.merge(queue: 'gram', priority: 'interactive') }
249
294
 
250
- it 'emits the expected results for each queue' do
251
- expect { subject.run! }
252
- .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
253
- .and emit_notification("delayed.job.count").with_payload(banana_payload).with_value(1)
254
- .and emit_notification("delayed.job.future_count").with_payload(banana_payload).with_value(0)
255
- .and emit_notification("delayed.job.locked_count").with_payload(banana_payload).with_value(0)
256
- .and emit_notification("delayed.job.erroring_count").with_payload(banana_payload).with_value(0)
257
- .and emit_notification("delayed.job.failed_count").with_payload(banana_payload).with_value(0)
258
- .and emit_notification("delayed.job.working_count").with_payload(banana_payload).with_value(0)
259
- .and emit_notification("delayed.job.workable_count").with_payload(banana_payload).with_value(1)
260
- .and emit_notification("delayed.job.max_age").with_payload(banana_payload).with_value(4.hours)
261
- .and emit_notification("delayed.job.max_lock_age").with_payload(banana_payload).with_value(0)
262
- .and emit_notification("delayed.job.alert_age_percent").with_payload(banana_payload).with_value(4.0.hours / 8.hours * 100)
263
- .and emit_notification("delayed.job.count").with_payload(gram_payload).with_value(0)
264
- .and emit_notification("delayed.job.future_count").with_payload(gram_payload).with_value(0)
265
- .and emit_notification("delayed.job.locked_count").with_payload(gram_payload).with_value(0)
266
- .and emit_notification("delayed.job.erroring_count").with_payload(gram_payload).with_value(0)
267
- .and emit_notification("delayed.job.failed_count").with_payload(gram_payload).with_value(0)
268
- .and emit_notification("delayed.job.working_count").with_payload(gram_payload).with_value(0)
269
- .and emit_notification("delayed.job.workable_count").with_payload(gram_payload).with_value(0)
270
- .and emit_notification("delayed.job.max_age").with_payload(gram_payload).with_value(0)
271
- .and emit_notification("delayed.job.max_lock_age").with_payload(gram_payload).with_value(0)
272
- .and emit_notification("delayed.job.alert_age_percent").with_payload(gram_payload).with_value(0)
295
+ context 'when using app-local timezone for DB timestamps' do
296
+ let(:app_local_db_time) { true }
297
+
298
+ it 'emits the expected results for each metric' do
299
+ expect { subject.run! }
300
+ .to emit_notification("delayed.monitor.run").with_payload(default_payload.except(:queue))
301
+ .and emit_notification("delayed.job.count").with_payload(p0_payload).with_value(4)
302
+ .and emit_notification("delayed.job.future_count").with_payload(p0_payload).with_value(1)
303
+ .and emit_notification("delayed.job.locked_count").with_payload(p0_payload).with_value(1)
304
+ .and emit_notification("delayed.job.erroring_count").with_payload(p0_payload).with_value(3)
305
+ .and emit_notification("delayed.job.failed_count").with_payload(p0_payload).with_value(1)
306
+ .and emit_notification("delayed.job.working_count").with_payload(p0_payload).with_value(1)
307
+ .and emit_notification("delayed.job.workable_count").with_payload(p0_payload).with_value(1)
308
+ .and emit_notification("delayed.job.max_age").with_payload(p0_payload).approximately.with_value(30.seconds)
309
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p0_payload).approximately.with_value(3.minutes)
310
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p0_payload).approximately.with_value(30.0.seconds / 1.minute * 100)
311
+ .and emit_notification("delayed.job.count").with_payload(p10_payload).with_value(4)
312
+ .and emit_notification("delayed.job.future_count").with_payload(p10_payload).with_value(1)
313
+ .and emit_notification("delayed.job.locked_count").with_payload(p10_payload).with_value(1)
314
+ .and emit_notification("delayed.job.erroring_count").with_payload(p10_payload).with_value(0)
315
+ .and emit_notification("delayed.job.failed_count").with_payload(p10_payload).with_value(1)
316
+ .and emit_notification("delayed.job.working_count").with_payload(p10_payload).with_value(1)
317
+ .and emit_notification("delayed.job.workable_count").with_payload(p10_payload).with_value(1)
318
+ .and emit_notification("delayed.job.max_age").with_payload(p10_payload).approximately.with_value(2.minutes)
319
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p10_payload).approximately.with_value(7.minutes)
320
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p10_payload).approximately.with_value(2.0.minutes / 3.minutes * 100)
321
+ .and emit_notification("delayed.job.count").with_payload(p20_payload).with_value(4)
322
+ .and emit_notification("delayed.job.future_count").with_payload(p20_payload).with_value(1)
323
+ .and emit_notification("delayed.job.locked_count").with_payload(p20_payload).with_value(1)
324
+ .and emit_notification("delayed.job.erroring_count").with_payload(p20_payload).with_value(3)
325
+ .and emit_notification("delayed.job.failed_count").with_payload(p20_payload).with_value(1)
326
+ .and emit_notification("delayed.job.working_count").with_payload(p20_payload).with_value(1)
327
+ .and emit_notification("delayed.job.workable_count").with_payload(p20_payload).with_value(1)
328
+ .and emit_notification("delayed.job.max_age").with_payload(p20_payload).approximately.with_value(1.hour)
329
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p20_payload).approximately.with_value(9.minutes)
330
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p20_payload).approximately.with_value(1.hour / 1.5.hours * 100)
331
+ .and emit_notification("delayed.job.count").with_payload(p30_payload).with_value(4)
332
+ .and emit_notification("delayed.job.future_count").with_payload(p30_payload).with_value(1)
333
+ .and emit_notification("delayed.job.locked_count").with_payload(p30_payload).with_value(1)
334
+ .and emit_notification("delayed.job.erroring_count").with_payload(p30_payload).with_value(0)
335
+ .and emit_notification("delayed.job.failed_count").with_payload(p30_payload).with_value(1)
336
+ .and emit_notification("delayed.job.working_count").with_payload(p30_payload).with_value(1)
337
+ .and emit_notification("delayed.job.workable_count").with_payload(p30_payload).with_value(1)
338
+ .and emit_notification("delayed.job.max_age").with_payload(p30_payload).approximately.with_value(6.hours)
339
+ .and emit_notification("delayed.job.max_lock_age").with_payload(p30_payload).approximately.with_value(11.minutes)
340
+ .and emit_notification("delayed.job.alert_age_percent").with_payload(p30_payload).approximately.with_value(100) # 6 hours / 4 hours (overflow)
341
+ .and emit_notification("delayed.job.workable_count").with_payload(p30_payload.merge(queue: 'banana')).with_value(1)
342
+ .and emit_notification("delayed.job.max_age").with_payload(p30_payload.merge(queue: 'banana')).approximately.with_value(4.hours)
343
+ end
273
344
  end
274
345
  end
275
346
  end
276
347
 
277
- describe '#query_for' do
348
+ describe 'SQL' do
278
349
  let(:monitor) { described_class.new }
279
350
  let(:queries) { [] }
280
351
  let(:now) { '2025-11-10 17:20:13 UTC' }
@@ -289,30 +360,28 @@ RSpec.describe Delayed::Monitor do
289
360
  value = value.value if value.is_a?(ActiveModel::Attribute)
290
361
  sql = sql.sub(/(\?|\$\d)/, ActiveRecord::Base.connection.quote(value))
291
362
  end
292
- queries << sql
363
+ queries << QueryUnderTest.for(sql)
364
+ queries << "---"
293
365
  end
294
366
  end
295
367
 
296
- def queries_for(metric)
297
- monitor.query_for(metric)
298
- queries.map { |q| QueryUnderTest.for(q) }
368
+ def query_descriptions
369
+ described_class::METRICS.each do |metric|
370
+ queries << "-- QUERIES FOR `#{metric}`:"
371
+ queries << "---------------------------------"
372
+ monitor.query_for(metric)
373
+ queries << "-- (no new queries)" unless queries.last == '---'
374
+ end
375
+ queries.dup.map { |query| query.try(:full_description) || query }
299
376
  end
300
377
 
301
- described_class::METRICS.each do |metric|
302
- context "('#{metric}')" do
303
- it "runs the expected #{current_adapter} query for #{metric}" do
304
- expect(queries_for(metric).map(&:formatted).join("\n")).to match_snapshot
305
- end
306
-
307
- it "produces the expected #{current_adapter} query plan for #{metric}" do
308
- expect(queries_for(metric).map(&:explain).join("\n")).to match_snapshot
309
- end
378
+ it "runs the expected #{current_adapter} queries with the expected plans" do
379
+ expect(query_descriptions.join("\n")).to match_snapshot
380
+ end
310
381
 
311
- context 'when using the legacy index', :with_legacy_table_index do
312
- it "[legacy index] produces the expected #{current_adapter} query plan for #{metric}" do
313
- expect(queries_for(metric).map(&:explain).join("\n")).to match_snapshot
314
- end
315
- end
382
+ context 'when using the legacy index', :with_legacy_table_index do
383
+ it "[legacy index] runs the expected #{current_adapter} queries with the expected plans" do
384
+ expect(query_descriptions.join("\n")).to match_snapshot
316
385
  end
317
386
  end
318
387
  end