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.
@@ -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 = 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 => [failure_score], :delete => 'Delete' }
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 => [failure_score], :retry => 'Retry Now' }
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/#{failure_score}"
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/#{failure_score}", :delete => 'Delete'
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/#{failure_score}"
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/#{failure_score}", :retry => 'Retry Now'
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/#{failure_score}"
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(/&quot;&lt;h1&gt;omg&lt;&#x2F;h1&gt;&quot;/)
209
+ _(last_response.body).must_match(/&quot;&lt;h1&gt;omg&lt;\/h1&gt;&quot;/)
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(/ArgumentError: &lt;p&gt;wow&lt;&#x2F;p&gt;/)
214
+ _(last_response.body).must_match(/&lt;script&gt;alert\(&quot;xss&quot;\)&lt;\/script&gt;ArgumentError: &lt;p&gt;wow&lt;\/p&gt;/)
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(args: ['<h1>omg</h1>'], error_message: '<p>wow</p>')
222
- get "/failures/#{failure[:jid]}"
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 arguments' do
230
+ it 'can escape error class' do
226
231
  _(last_response.status).must_equal 200
227
- _(last_response.body).must_match(/<th>Error Message<\/th>\n <td>&lt;p&gt;wow&lt;&#x2F;p&gt;<\/td>/)
232
+ _(last_response.body).must_match(/&lt;script&gt;alert\(&quot;xss&quot;\)&lt;\/script&gt;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(/<th>Error Message<\/th>\n <td>&lt;p&gt;wow&lt;&#x2F;p&gt;<\/td>/)
238
+ _(last_response.body).must_match(/&lt;p&gt;wow&lt;\/p&gt;/)
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(/&quot;&lt;h1&gt;omg&lt;\/h1&gt;&quot;/)
245
+ # _(last_response.body).must_match(/&quot;&lt;script&gt;alert\(&quot;xss2&quot;\)&lt;\/script&gt;&quot;/)
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 => 1,
268
- :enqueued_at => Time.now.utc.to_f,
269
- :failed_at => Time.now.utc.to_f,
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, failure_score, Sidekiq.dump_json(data))
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.at(1).to_f
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
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: 2022-09-03 00:00:00.000000000 Z
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.0.3.1
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.