sidekiq-failures 1.0.4 → 1.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -3
- data/.gitignore +1 -1
- data/CHANGELOG.md +9 -4
- data/README.md +9 -4
- data/lib/sidekiq/failures/sorted_entry.rb +8 -1
- data/lib/sidekiq/failures/version.rb +1 -1
- data/lib/sidekiq/failures/views/failure.erb +37 -29
- data/lib/sidekiq/failures/views/failure_legacy.erb +31 -0
- data/lib/sidekiq/failures/views/failures.erb +106 -98
- data/lib/sidekiq/failures/views/failures_legacy.erb +105 -0
- data/lib/sidekiq/failures/web_extension.rb +118 -31
- data/lib/sidekiq/failures.rb +20 -3
- data/sidekiq-failures.gemspec +0 -1
- data/test/failures_test.rb +1 -1
- data/test/middleware_test.rb +32 -21
- data/test/test_helper.rb +3 -1
- data/test/web_extension_test.rb +57 -32
- metadata +5 -20
data/test/web_extension_test.rb
CHANGED
@@ -5,7 +5,7 @@ module Sidekiq
|
|
5
5
|
describe "WebExtension" do
|
6
6
|
include Rack::Test::Methods
|
7
7
|
|
8
|
-
TOKEN = SecureRandom.base64(32).freeze
|
8
|
+
TOKEN = SecureRandom.base64(defined?(Sidekiq::Web::TOKEN_LENGTH) ? Sidekiq::Web::TOKEN_LENGTH : 32).freeze
|
9
9
|
|
10
10
|
def app
|
11
11
|
Sidekiq::Web
|
@@ -14,8 +14,7 @@ module Sidekiq
|
|
14
14
|
before do
|
15
15
|
env 'rack.session', { csrf: TOKEN }
|
16
16
|
env 'HTTP_X_CSRF_TOKEN', TOKEN
|
17
|
-
Sidekiq.redis
|
18
|
-
Sidekiq.redis {|c| c.flushdb }
|
17
|
+
Sidekiq.redis(&:flushdb)
|
19
18
|
end
|
20
19
|
|
21
20
|
it 'can display home with failures tab' do
|
@@ -41,7 +40,7 @@ module Sidekiq
|
|
41
40
|
|
42
41
|
describe 'when there are failures' do
|
43
42
|
before do
|
44
|
-
create_sample_failure
|
43
|
+
@failure = create_sample_failure
|
45
44
|
get '/failures'
|
46
45
|
end
|
47
46
|
|
@@ -53,15 +52,14 @@ module Sidekiq
|
|
53
52
|
_(last_response.body).must_match(/Failed Jobs/)
|
54
53
|
_(last_response.body).must_match(/HardWorker/)
|
55
54
|
_(last_response.body).must_match(/ArgumentError/)
|
56
|
-
_(last_response.body).wont_match(/No failed jobs found/)
|
57
55
|
end
|
58
56
|
|
59
57
|
it 'can reset counter' do
|
60
58
|
assert_equal failed_count, "1"
|
61
59
|
|
62
60
|
_(last_response.body).must_match(/HardWorker/)
|
61
|
+
post '/failures/all/reset', { authenticity_token: TOKEN }
|
63
62
|
|
64
|
-
post '/failures/all/reset'
|
65
63
|
_(last_response.status).must_equal 302
|
66
64
|
_(last_response.location).must_match(/failures$/)
|
67
65
|
|
@@ -82,7 +80,7 @@ module Sidekiq
|
|
82
80
|
|
83
81
|
_(last_response.body).must_match(/HardWorker/)
|
84
82
|
|
85
|
-
post '/failures/all/delete'
|
83
|
+
post '/failures/all/delete', { authenticity_token: TOKEN }
|
86
84
|
_(last_response.status).must_equal 302
|
87
85
|
_(last_response.location).must_match(/failures$/)
|
88
86
|
|
@@ -102,7 +100,7 @@ module Sidekiq
|
|
102
100
|
assert_equal failed_count, "1"
|
103
101
|
|
104
102
|
_(last_response.body).must_match(/HardWorker/)
|
105
|
-
post '/failures/all/retry'
|
103
|
+
post '/failures/all/retry', { authenticity_token: TOKEN }
|
106
104
|
_(last_response.status).must_equal 302
|
107
105
|
_(last_response.location).must_match(/failures/)
|
108
106
|
|
@@ -116,7 +114,7 @@ module Sidekiq
|
|
116
114
|
|
117
115
|
_(last_response.body).must_match(/HardWorker/)
|
118
116
|
|
119
|
-
post '/failures', { :key => [
|
117
|
+
post '/failures', { authenticity_token: TOKEN, :key => [build_param_key(@failure)], :delete => 'Delete' }
|
120
118
|
_(last_response.status).must_equal 302
|
121
119
|
_(last_response.location).must_match(/failures/)
|
122
120
|
|
@@ -130,7 +128,7 @@ module Sidekiq
|
|
130
128
|
|
131
129
|
_(last_response.body).must_match(/HardWorker/)
|
132
130
|
|
133
|
-
post '/failures', { :key => [
|
131
|
+
post '/failures', { authenticity_token: TOKEN, :key => [build_param_key(@failure)], :retry => 'Retry Now' }
|
134
132
|
_(last_response.status).must_equal 302
|
135
133
|
_(last_response.location).must_match(/failures/)
|
136
134
|
|
@@ -150,8 +148,8 @@ module Sidekiq
|
|
150
148
|
|
151
149
|
describe 'when there is failure' do
|
152
150
|
before do
|
153
|
-
create_sample_failure
|
154
|
-
get "/failures/#{
|
151
|
+
@failure = create_sample_failure
|
152
|
+
get "/failures/#{build_param_key(@failure)}"
|
155
153
|
end
|
156
154
|
|
157
155
|
it 'should be successful' do
|
@@ -168,11 +166,11 @@ module Sidekiq
|
|
168
166
|
it 'can delete failure' do
|
169
167
|
_(last_response.body).must_match(/HardWorker/)
|
170
168
|
|
171
|
-
post "/failures/#{
|
169
|
+
post "/failures/#{build_param_key(@failure)}", { authenticity_token: TOKEN, :delete => 'Delete' }
|
172
170
|
_(last_response.status).must_equal 302
|
173
171
|
_(last_response.location).must_match(/failures/)
|
174
172
|
|
175
|
-
get "/failures/#{
|
173
|
+
get "/failures/#{build_param_key(@failure)}"
|
176
174
|
_(last_response.status).must_equal 302
|
177
175
|
_(last_response.location).must_match(/failures/)
|
178
176
|
end
|
@@ -180,11 +178,11 @@ module Sidekiq
|
|
180
178
|
it 'can retry failure' do
|
181
179
|
_(last_response.body).must_match(/HardWorker/)
|
182
180
|
|
183
|
-
post "/failures/#{
|
181
|
+
post "/failures/#{build_param_key(@failure)}", { authenticity_token: TOKEN, :retry => 'Retry Now' }
|
184
182
|
_(last_response.status).must_equal 302
|
185
183
|
_(last_response.location).must_match(/failures/)
|
186
184
|
|
187
|
-
get "/failures/#{
|
185
|
+
get "/failures/#{build_param_key(@failure)}"
|
188
186
|
_(last_response.status).must_equal 302
|
189
187
|
_(last_response.location).must_match(/failures/)
|
190
188
|
end
|
@@ -192,7 +190,7 @@ module Sidekiq
|
|
192
190
|
if defined? Sidekiq::Pro
|
193
191
|
it 'can filter failure' do
|
194
192
|
create_sample_failure
|
195
|
-
post '/filter/failures', substr: 'new'
|
193
|
+
post '/filter/failures', { authenticity_token: TOKEN, substr: 'new' }
|
196
194
|
|
197
195
|
_(last_response.status).must_equal 200
|
198
196
|
end
|
@@ -203,33 +201,50 @@ module Sidekiq
|
|
203
201
|
describe 'with unescaped data' do
|
204
202
|
describe 'the index page' do
|
205
203
|
before do
|
206
|
-
create_sample_failure(args: ['<h1>omg</h1>'], error_message: '<p>wow</p>')
|
204
|
+
create_sample_failure(args: ['<h1>omg</h1>'], error_message: '<p>wow</p>', error_class: '<script>alert("xss")</script>ArgumentError')
|
207
205
|
get '/failures'
|
208
206
|
end
|
209
207
|
|
210
208
|
it 'can escape arguments' do
|
211
|
-
_(last_response.body).must_match(/"<h1>omg<
|
209
|
+
_(last_response.body).must_match(/"<h1>omg<\/h1>"/)
|
210
|
+
_(last_response.body).wont_match(/<h1>omg<\/h1>/)
|
212
211
|
end
|
213
212
|
|
214
213
|
it 'can escape error message' do
|
215
|
-
_(last_response.body).must_match(
|
214
|
+
_(last_response.body).must_match(/<script>alert\("xss"\)<\/script>ArgumentError: <p>wow<\/p>/)
|
215
|
+
_(last_response.body).wont_match(/<script>alert\("xss"\)<\/script>ArgumentError/)
|
216
|
+
_(last_response.body).wont_match(/<p>wow<\/p>/)
|
216
217
|
end
|
217
218
|
end
|
218
219
|
|
219
220
|
describe 'the details page' do
|
220
221
|
before do
|
221
|
-
failure = create_sample_failure(
|
222
|
-
|
222
|
+
@failure = create_sample_failure(
|
223
|
+
args: ['<h1>omg</h1>', '<script>alert("xss2")</script>'],
|
224
|
+
error_message: '<p>wow</p>',
|
225
|
+
error_class: '<script>alert("xss")</script>ArgumentError'
|
226
|
+
)
|
227
|
+
get "/failures/#{build_param_key(@failure)}"
|
223
228
|
end
|
224
229
|
|
225
|
-
it 'can escape
|
230
|
+
it 'can escape error class' do
|
226
231
|
_(last_response.status).must_equal 200
|
227
|
-
_(last_response.body).must_match(
|
232
|
+
_(last_response.body).must_match(/<script>alert\("xss"\)<\/script>ArgumentError/)
|
233
|
+
_(last_response.body).wont_match(/<script>alert\("xss"\)<\/script>ArgumentError/)
|
228
234
|
end
|
229
235
|
|
230
236
|
it 'can escape error message' do
|
231
237
|
_(last_response.status).must_equal 200
|
232
|
-
_(last_response.body).must_match(
|
238
|
+
_(last_response.body).must_match(/<p>wow<\/p>/)
|
239
|
+
_(last_response.body).wont_match(/<p>wow<\/p>/)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'can escape arguments' do
|
243
|
+
_(last_response.status).must_equal 200
|
244
|
+
_(last_response.body).must_match(/"<h1>omg<\/h1>"/)
|
245
|
+
# _(last_response.body).must_match(/"<script>alert\("xss2"\)<\/script>"/)
|
246
|
+
_(last_response.body).wont_match(/<h1>omg<\/h1>/)
|
247
|
+
_(last_response.body).wont_match(/<script>alert\("xss2"\)<\/script>/)
|
233
248
|
end
|
234
249
|
end
|
235
250
|
end
|
@@ -242,7 +257,6 @@ module Sidekiq
|
|
242
257
|
|
243
258
|
it 'should be successful' do
|
244
259
|
_(last_response.status).must_equal 200
|
245
|
-
_(last_response.body).wont_match(/No failed jobs found/)
|
246
260
|
end
|
247
261
|
end
|
248
262
|
|
@@ -254,40 +268,51 @@ module Sidekiq
|
|
254
268
|
|
255
269
|
it 'should be successful' do
|
256
270
|
_(last_response.status).must_equal 200
|
257
|
-
_(last_response.body).wont_match(/No failed jobs found/)
|
258
271
|
end
|
259
272
|
end
|
260
273
|
end
|
261
274
|
|
262
275
|
def create_sample_failure(data = {})
|
276
|
+
failed_at = Time.now.utc.to_i - 10000
|
277
|
+
enqueued_at = failed_at - 1000
|
278
|
+
|
263
279
|
data = {
|
264
280
|
:queue => 'default',
|
265
281
|
:class => 'HardWorker',
|
266
282
|
:args => ['bob', 5],
|
267
|
-
:jid =>
|
268
|
-
:enqueued_at =>
|
269
|
-
:failed_at =>
|
283
|
+
:jid => SecureRandom.hex(12),
|
284
|
+
:enqueued_at => failed_at.to_f,
|
285
|
+
:failed_at => enqueued_at.to_f,
|
270
286
|
:error_class => 'ArgumentError',
|
271
287
|
:error_message => 'Some new message',
|
272
288
|
:error_backtrace => ["path/file1.rb", "path/file2.rb"]
|
273
289
|
}.merge(data)
|
274
290
|
|
291
|
+
# Store the score so we can use it for retrieving the failure later
|
292
|
+
score = failure_score
|
293
|
+
|
275
294
|
Sidekiq.redis do |c|
|
276
295
|
c.multi do
|
277
|
-
c.zadd(Sidekiq::Failures::LIST_KEY,
|
296
|
+
c.zadd(Sidekiq::Failures::LIST_KEY, score, Sidekiq.dump_json(data))
|
278
297
|
c.set("stat:failed", 1)
|
279
298
|
end
|
280
299
|
end
|
281
300
|
|
301
|
+
# Update data with the score used so tests can reference it
|
302
|
+
data[:score] = score
|
282
303
|
data
|
283
304
|
end
|
284
305
|
|
306
|
+
def build_param_key(failure)
|
307
|
+
"#{failure[:score]}-#{failure[:jid]}"
|
308
|
+
end
|
309
|
+
|
285
310
|
def failed_count
|
286
311
|
Sidekiq.redis { |c| c.get("stat:failed") }
|
287
312
|
end
|
288
313
|
|
289
314
|
def failure_score
|
290
|
-
Time.
|
315
|
+
Time.now.to_f
|
291
316
|
end
|
292
317
|
end
|
293
318
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-failures
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcelo Silveira
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-08-21 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: sidekiq
|
@@ -94,20 +93,6 @@ dependencies:
|
|
94
93
|
- - ">="
|
95
94
|
- !ruby/object:Gem::Version
|
96
95
|
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: pry
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
96
|
description: Keep track of Sidekiq failed jobs
|
112
97
|
email:
|
113
98
|
- marcelo@mhfs.com.br
|
@@ -134,7 +119,9 @@ files:
|
|
134
119
|
- lib/sidekiq/failures/sorted_entry.rb
|
135
120
|
- lib/sidekiq/failures/version.rb
|
136
121
|
- lib/sidekiq/failures/views/failure.erb
|
122
|
+
- lib/sidekiq/failures/views/failure_legacy.erb
|
137
123
|
- lib/sidekiq/failures/views/failures.erb
|
124
|
+
- lib/sidekiq/failures/views/failures_legacy.erb
|
138
125
|
- lib/sidekiq/failures/web_extension.rb
|
139
126
|
- sidekiq-failures.gemspec
|
140
127
|
- test/failures_test.rb
|
@@ -145,7 +132,6 @@ homepage: https://github.com/mhfs/sidekiq-failures/
|
|
145
132
|
licenses:
|
146
133
|
- MIT
|
147
134
|
metadata: {}
|
148
|
-
post_install_message:
|
149
135
|
rdoc_options: []
|
150
136
|
require_paths:
|
151
137
|
- lib
|
@@ -160,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
146
|
- !ruby/object:Gem::Version
|
161
147
|
version: '0'
|
162
148
|
requirements: []
|
163
|
-
rubygems_version: 3.
|
164
|
-
signing_key:
|
149
|
+
rubygems_version: 3.6.2
|
165
150
|
specification_version: 4
|
166
151
|
summary: Keeps track of Sidekiq failed jobs and adds a tab to the Web UI to let you
|
167
152
|
browse them. Makes use of Sidekiq's custom tabs and middleware chain.
|