sidekiq-expected_failures 0.3.0 → 0.4.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/.gitignore +2 -0
- data/.travis.yml +8 -3
- data/Appraisals +7 -0
- data/CHANGELOG.md +17 -8
- data/Gemfile +0 -7
- data/README.md +3 -1
- data/gemfiles/sidekiq_4.gemfile +7 -0
- data/gemfiles/sidekiq_5.gemfile +7 -0
- data/lib/sidekiq/expected_failures.rb +7 -9
- data/lib/sidekiq/expected_failures/middleware.rb +18 -18
- data/lib/sidekiq/expected_failures/version.rb +1 -1
- data/lib/sidekiq/expected_failures/web.rb +15 -4
- data/sidekiq-expected_failures.gemspec +5 -2
- data/web/assets/expected.js +38 -5
- data/web/views/expected_failures.erb +44 -12
- metadata +22 -15
- data/test/lib/expected_failures_test.rb +0 -47
- data/test/lib/middleware_test.rb +0 -149
- data/test/lib/web_test.rb +0 -166
- data/test/test_helper.rb +0 -34
- data/test/test_workers.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39b85cee8dfd3e0fffa64d50b1f17848679128fb
|
4
|
+
data.tar.gz: 380d9ca0d19df7f64736f323852a7a5fc6f16097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 214cbb3d4236cb160cf07f6f24698141afe1281587fdf1b5c71cedb9fc2925365108665187219269653ad17e884980b8283c689f3f3bdff762164ddd45f83c1b
|
7
|
+
data.tar.gz: 4dd36c9fd6221d7b322de26414eb6b8b5b309b58e70d060fc12fa0e361c8b04f1d5f0e8f89c73b4fc611636819c2ac80870a9aaec92ba577badc9766600fe267
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.4.0 (March 6, 2018)
|
2
|
+
|
3
|
+
Interface changes & maintenance, API untouched.
|
4
|
+
|
5
|
+
- reworked routes - `date` param removed in favor of named `day/:date` subroute
|
6
|
+
- simple search / filter added in UI panel (hijacks search command)
|
7
|
+
- tiny css tweaks
|
8
|
+
- run test on travis for sidekiq `4.2` and >= `5.1` and all newest rubies (`2.2` - `2.5`)
|
9
|
+
|
10
|
+
## 0.3.0 (June 14, 2017)
|
2
11
|
|
3
12
|
- require Sidekiq >= 4.2.0 (after Sinatra dependency was removed)
|
4
13
|
|
5
|
-
## 0.2.5
|
14
|
+
## 0.2.5 (December 14, 2015)
|
6
15
|
|
7
16
|
- add csrf tag for sidekiq >= 3.4.2
|
8
17
|
- **[BREAKING CHANGE]** don't load `sidekiq/web` automagically at
|
@@ -14,21 +23,21 @@ require 'sidekiq/web'
|
|
14
23
|
require 'sidekiq/expected_failures/web'
|
15
24
|
```
|
16
25
|
|
17
|
-
## 0.2.4
|
26
|
+
## 0.2.4 (July 23, 2014)
|
18
27
|
|
19
28
|
- `Sidekiq::ExpectedFailures.clear_old` can now accept argument - will remove failures
|
20
29
|
that are n days old (1 by default) - useful if you want to clear some of old failures
|
21
30
|
using cronjob
|
22
31
|
|
23
|
-
## 0.2.3
|
32
|
+
## 0.2.3 (May 07, 2014)
|
24
33
|
|
25
34
|
- removed (unnecessary) dependency on `Sidekiq::Util` (now Sidekiq 3.0 compatible)
|
26
35
|
|
27
|
-
## 0.2.2
|
36
|
+
## 0.2.2 (February 14, 2014)
|
28
37
|
|
29
38
|
- rescue load error of `sidekiq/web` (this allows client only usage)
|
30
39
|
|
31
|
-
## 0.2.1
|
40
|
+
## 0.2.1 (December 18, 2013 )
|
32
41
|
|
33
42
|
- added JSON stats path in case you would like to fetch this data from external service.
|
34
43
|
It works similar to sidekiq's _stats_. You can visit: `expected_failures/stats` to
|
@@ -45,7 +54,7 @@ require 'sidekiq/expected_failures/web'
|
|
45
54
|
}
|
46
55
|
```
|
47
56
|
|
48
|
-
## 0.2.0
|
57
|
+
## 0.2.0 (December 01, 2013)
|
49
58
|
|
50
59
|
- [**breaking change**] ability to use Sidekiq's build-in `handle_exception`
|
51
60
|
method - in case you want to use airbrake or other exception notify service.
|
@@ -79,6 +88,6 @@ discarded (for that worker).
|
|
79
88
|
|
80
89
|
- small front-end adjustments
|
81
90
|
|
82
|
-
## 0.0.1
|
91
|
+
## 0.0.1 (October 29, 2013)
|
83
92
|
|
84
93
|
- Initial release
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -54,10 +54,12 @@ sidekiq-expected_failures utilizes sidekiq's [ExceptionHandler module][1] - so y
|
|
54
54
|
|
55
55
|
This is how web interface looks like:
|
56
56
|
|
57
|
-

|
58
58
|
|
59
59
|
It logs each failed jobs to to redis list (per day) and keep global counters (per exception class as a single redis hash). If you would like to get that counter as JSON response (for some external API usage for example) you can use path `expected_failures/stats`.
|
60
60
|
|
61
|
+
To activate naive filter/search (filters by exception, exception message or argument - simple contains case-insensitive match) press `F3` or `Cmd` / `Ctrl` + `F`.
|
62
|
+
|
61
63
|
### Default expected failures
|
62
64
|
|
63
65
|
You can configure defaults for all your workers (overridden completely by specifying `expected_failures` hash inside `sidekiq_options` - per worker).
|
@@ -3,7 +3,6 @@ require "sidekiq/expected_failures/middleware"
|
|
3
3
|
|
4
4
|
|
5
5
|
module Sidekiq
|
6
|
-
|
7
6
|
def self.expected_failures=(exceptions)
|
8
7
|
@expected_failures = exceptions
|
9
8
|
end
|
@@ -13,7 +12,6 @@ module Sidekiq
|
|
13
12
|
end
|
14
13
|
|
15
14
|
module ExpectedFailures
|
16
|
-
|
17
15
|
def self.dates
|
18
16
|
Sidekiq.redis do |c|
|
19
17
|
c.smembers "expected:dates"
|
@@ -41,16 +39,16 @@ module Sidekiq
|
|
41
39
|
|
42
40
|
private
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
42
|
+
def self.clear(dates)
|
43
|
+
dates.each do |date|
|
44
|
+
Sidekiq.redis do |c|
|
45
|
+
c.multi do |m|
|
46
|
+
m.srem("expected:dates", date)
|
47
|
+
m.del("expected:#{date}")
|
51
48
|
end
|
52
49
|
end
|
53
50
|
end
|
51
|
+
end
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -26,29 +26,29 @@ module Sidekiq
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def setup_exceptions(worker)
|
30
|
+
@handled_exceptions = worker.class.get_sidekiq_options['expected_failures'] || Sidekiq.expected_failures
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
def exception_intervals(ex)
|
34
|
+
[handled_exceptions[ex.class]].flatten.compact
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
37
|
+
def log_exception(data, ex, msg)
|
38
|
+
result = Sidekiq.redis do |conn|
|
39
|
+
conn.multi do |m|
|
40
|
+
m.lpush("expected:#{today}", Sidekiq.dump_json(data))
|
41
|
+
m.sadd("expected:dates", today)
|
42
|
+
m.hincrby("expected:count", data[:exception], 1)
|
44
43
|
end
|
45
|
-
|
46
|
-
handle_exception(ex, msg) if exception_intervals(ex).include?(result[0])
|
47
44
|
end
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
handle_exception(ex, msg) if exception_intervals(ex).include?(result[0])
|
47
|
+
end
|
48
|
+
|
49
|
+
def today
|
50
|
+
Date.today.to_s
|
51
|
+
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
module Sidekiq
|
2
2
|
module ExpectedFailures
|
3
3
|
module Web
|
4
|
-
|
5
4
|
def self.registered(app)
|
6
5
|
web_dir = File.expand_path("../../../../web", __FILE__)
|
7
6
|
|
8
7
|
app.helpers do
|
9
8
|
def link_to_details(job)
|
10
9
|
data = []
|
11
|
-
|
12
|
-
|
10
|
+
search = ""
|
11
|
+
job["args"].each_with_index do |argument, index|
|
12
|
+
data << "data-#{index+1}='#{h argument.inspect}'"
|
13
|
+
search << h(argument.inspect)
|
14
|
+
end
|
15
|
+
"<a href='#' data-search='#{search.downcase}' #{data.join(' ')} title='#{job["worker"]}'>Details</a>"
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
@@ -25,7 +28,7 @@ module Sidekiq
|
|
25
28
|
json(failures: Sidekiq::ExpectedFailures.counters)
|
26
29
|
end
|
27
30
|
|
28
|
-
|
31
|
+
panel = proc do
|
29
32
|
@dates = Sidekiq::ExpectedFailures.dates
|
30
33
|
@count = (params[:count] || 50).to_i
|
31
34
|
|
@@ -42,6 +45,14 @@ module Sidekiq
|
|
42
45
|
|
43
46
|
erb File.read(File.join(web_dir, "views/expected_failures.erb"))
|
44
47
|
end
|
48
|
+
|
49
|
+
app.get "/expected_failures/day/:date" do
|
50
|
+
self.instance_exec(&panel)
|
51
|
+
end
|
52
|
+
|
53
|
+
app.get "/expected_failures" do
|
54
|
+
self.instance_exec(&panel)
|
55
|
+
end
|
45
56
|
end
|
46
57
|
end
|
47
58
|
end
|
@@ -12,7 +12,9 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.homepage = "https://github.com/emq/sidekiq-expected_failures"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
15
|
-
spec.files = `git ls-files`.split(
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features|img)/})
|
17
|
+
end
|
16
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
20
|
spec.require_paths = ["lib"]
|
@@ -25,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
25
27
|
spec.add_development_dependency "rack-test"
|
26
28
|
spec.add_development_dependency "timecop", "~> 0.7.0"
|
27
29
|
spec.add_development_dependency "mocha", "~> 1.0.0"
|
28
|
-
spec.add_development_dependency "coveralls", "~> 0.
|
30
|
+
spec.add_development_dependency "coveralls", "~> 0.8.0"
|
29
31
|
spec.add_development_dependency "minitest", "~> 5.7", ">= 5.7.0"
|
32
|
+
spec.add_development_dependency "appraisal"
|
30
33
|
end
|
data/web/assets/expected.js
CHANGED
@@ -1,21 +1,54 @@
|
|
1
1
|
$(function() {
|
2
2
|
function mapDataAttributes(element) {
|
3
|
-
html = "";
|
3
|
+
var html = "";
|
4
4
|
$.each(element.data(), function( key, value ) {
|
5
|
-
|
5
|
+
if (key != 'search') {
|
6
|
+
html += "<tr><th>Argument #" + key + "</th><td>" + value + "</td></tr>";
|
7
|
+
}
|
6
8
|
});
|
7
9
|
return html;
|
8
10
|
}
|
9
11
|
|
10
|
-
|
12
|
+
window.addEventListener("keydown", function (event) {
|
13
|
+
if (event.keyCode === 114 || ((event.ctrlKey || event.metaKey) && event.keyCode === 70)) {
|
14
|
+
$('#search').toggleClass('hidden').focus();
|
15
|
+
event.preventDefault();
|
16
|
+
}
|
17
|
+
});
|
18
|
+
|
19
|
+
$('#search').live('keyup', function(event) {
|
20
|
+
var query = $(this).val().toLowerCase();
|
21
|
+
if (query.length) {
|
22
|
+
$('.search-warning').remove();
|
23
|
+
$('#expected tbody tr').each(function(index) {
|
24
|
+
if ($(this).find('[data-search*="' + query + '"]').length) {
|
25
|
+
$(this).show();
|
26
|
+
} else {
|
27
|
+
$(this).hide();
|
28
|
+
}
|
29
|
+
});
|
30
|
+
|
31
|
+
if (!($('#expected tbody tr:visible').length)) {
|
32
|
+
$('#expected tbody').append('<tr class="search-warning"><td colspan="5">Nothing found!</td></tr>');
|
33
|
+
}
|
34
|
+
} else {
|
35
|
+
$('#expected tbody tr').show();
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
$('#expected tbody td:last-child > a').live('click', function(event){
|
11
40
|
$('#job-details .modal-body table tbody').html(mapDataAttributes($(this)));
|
12
41
|
$('#job-details .modal-title').text($(this).attr('title'));
|
13
42
|
$('#job-details').modal('show');
|
14
43
|
|
15
|
-
|
44
|
+
event.preventDefault();
|
16
45
|
});
|
17
46
|
|
18
|
-
$('#
|
47
|
+
$('#clear-jobs select').live('change', function(event) {
|
19
48
|
$(this).parent('form').submit();
|
20
49
|
});
|
50
|
+
|
51
|
+
$('#filter-jobs select').live('change', function(event) {
|
52
|
+
location.href = $(this).val();
|
53
|
+
});
|
21
54
|
});
|
@@ -2,6 +2,33 @@
|
|
2
2
|
<%= @javascript %>
|
3
3
|
</script>
|
4
4
|
|
5
|
+
<style type="text/css">
|
6
|
+
@media screen and (min-width: 800px) {
|
7
|
+
.dl-horizontal dt {
|
8
|
+
width: 320px;
|
9
|
+
}
|
10
|
+
|
11
|
+
.dl-horizontal dd {
|
12
|
+
margin-left: 330px;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
.modal-dialog {
|
17
|
+
min-width: 85vw;
|
18
|
+
}
|
19
|
+
|
20
|
+
.simple-search {
|
21
|
+
width: 100%;
|
22
|
+
margin-bottom: 10px;
|
23
|
+
}
|
24
|
+
|
25
|
+
.search-warning {
|
26
|
+
text-align: center;
|
27
|
+
font-weight: 700;
|
28
|
+
padding: 10px;
|
29
|
+
}
|
30
|
+
</style>
|
31
|
+
|
5
32
|
<h3>Expected failures log
|
6
33
|
<% if @date %>
|
7
34
|
<small>(<%= @date %>)</small>
|
@@ -71,14 +98,17 @@
|
|
71
98
|
<label>Choose date:</label>
|
72
99
|
<select name="date">
|
73
100
|
<% @dates.each do |date, count| %>
|
74
|
-
<option value="<%= date %>" <%= "selected" if date == @date %>><%= date %> (<%= count %>)</option>
|
101
|
+
<option value="<%= "#{root_path}expected_failures/day/#{date}" %>" <%= "selected" if date == @date %>><%= date %> (<%= count %>)</option>
|
75
102
|
<% end %>
|
76
103
|
</select>
|
77
104
|
</form>
|
78
105
|
|
79
106
|
<p class="clearfix"></p>
|
80
107
|
|
81
|
-
<%= erb :_paging, locals: { url: "#{root_path}expected_failures
|
108
|
+
<%= erb :_paging, locals: { url: "#{root_path}expected_failures/day/#{@date}" } %>
|
109
|
+
|
110
|
+
<p class="clearfix"></p>
|
111
|
+
<input autocomplete="off" type="text" id="search" class="simple-search hidden" placeholder="Search visible results" />
|
82
112
|
|
83
113
|
<table id="expected" class="queues table table-hover table-bordered table-striped table-white">
|
84
114
|
<thead>
|
@@ -88,18 +118,20 @@
|
|
88
118
|
<th>Queue</th>
|
89
119
|
<th>Arguments</th>
|
90
120
|
</thead>
|
91
|
-
|
92
|
-
|
93
|
-
<
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
121
|
+
<tbody>
|
122
|
+
<% @jobs.each do |job| %>
|
123
|
+
<tr>
|
124
|
+
<td><%= Time.parse(job['failed_at']).strftime('%m/%d/%Y %H:%M:%S') %></td>
|
125
|
+
<td><%= job["worker"] %></td>
|
126
|
+
<td><div data-search="<%= job['exception'].to_s.downcase %><%= h job["error"].to_s.downcase %>"><%= job["exception"] %> <small>(<%= h job["error"]%>)</small></div></td>
|
127
|
+
<td><a href="<%= "#{root_path}/queues/#{job["queue"]}"%>"><%= job["queue"] %></a></td>
|
128
|
+
<td><%= link_to_details(job) %></td>
|
129
|
+
</tr>
|
130
|
+
<% end %>
|
131
|
+
</tbody>
|
100
132
|
</table>
|
101
133
|
|
102
|
-
<%= erb :_paging, locals: { url: "#{root_path}expected_failures/#{@date}" } %>
|
134
|
+
<%= erb :_paging, locals: { url: "#{root_path}expected_failures/day/#{@date}" } %>
|
103
135
|
|
104
136
|
<% else %>
|
105
137
|
<p class="clearfix"></p>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-expected_failures
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rafal Wojsznis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.8.0
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
124
|
+
version: 0.8.0
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: minitest
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,6 +142,20 @@ dependencies:
|
|
142
142
|
- - ">="
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: 5.7.0
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: appraisal
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
145
159
|
description: If you don't rely on sidekiq' retry behavior, you handle exceptions on
|
146
160
|
your own and want to keep track of them - this thing is for you.
|
147
161
|
email:
|
@@ -152,22 +166,20 @@ extra_rdoc_files: []
|
|
152
166
|
files:
|
153
167
|
- ".gitignore"
|
154
168
|
- ".travis.yml"
|
169
|
+
- Appraisals
|
155
170
|
- CHANGELOG.md
|
156
171
|
- Gemfile
|
157
172
|
- LICENSE.txt
|
158
173
|
- README.md
|
159
174
|
- Rakefile
|
175
|
+
- gemfiles/sidekiq_4.gemfile
|
176
|
+
- gemfiles/sidekiq_5.gemfile
|
160
177
|
- lib/sidekiq-expected_failures.rb
|
161
178
|
- lib/sidekiq/expected_failures.rb
|
162
179
|
- lib/sidekiq/expected_failures/middleware.rb
|
163
180
|
- lib/sidekiq/expected_failures/version.rb
|
164
181
|
- lib/sidekiq/expected_failures/web.rb
|
165
182
|
- sidekiq-expected_failures.gemspec
|
166
|
-
- test/lib/expected_failures_test.rb
|
167
|
-
- test/lib/middleware_test.rb
|
168
|
-
- test/lib/web_test.rb
|
169
|
-
- test/test_helper.rb
|
170
|
-
- test/test_workers.rb
|
171
183
|
- web/assets/bootstrap.js
|
172
184
|
- web/assets/expected.js
|
173
185
|
- web/views/expected_failures.erb
|
@@ -196,9 +208,4 @@ signing_key:
|
|
196
208
|
specification_version: 4
|
197
209
|
summary: If you don't rely on sidekiq' retry behavior, you handle exceptions on your
|
198
210
|
own and want to keep track of them - this thing is for you.
|
199
|
-
test_files:
|
200
|
-
- test/lib/expected_failures_test.rb
|
201
|
-
- test/lib/middleware_test.rb
|
202
|
-
- test/lib/web_test.rb
|
203
|
-
- test/test_helper.rb
|
204
|
-
- test/test_workers.rb
|
211
|
+
test_files: []
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
module Sidekiq
|
4
|
-
module ExpectedFailures
|
5
|
-
describe "clear_old (helper method)" do
|
6
|
-
before do
|
7
|
-
Sidekiq.redis = REDIS
|
8
|
-
Sidekiq.redis {|c| c.flushdb }
|
9
|
-
Timecop.freeze(Time.local(2014, 6, 10))
|
10
|
-
|
11
|
-
# fresh failure
|
12
|
-
Sidekiq.redis do |c|
|
13
|
-
c.lpush("expected:2014-06-10", Sidekiq.dump_json({}))
|
14
|
-
c.sadd("expected:dates", "2014-06-10")
|
15
|
-
end
|
16
|
-
|
17
|
-
# 1 day old
|
18
|
-
Sidekiq.redis do |c|
|
19
|
-
c.lpush("expected:2014-06-09", Sidekiq.dump_json({}))
|
20
|
-
c.sadd("expected:dates", "2014-06-09")
|
21
|
-
end
|
22
|
-
|
23
|
-
# 3 days old
|
24
|
-
Sidekiq.redis do |c|
|
25
|
-
c.lpush("expected:2014-06-07", Sidekiq.dump_json({}))
|
26
|
-
c.sadd("expected:dates", "2014-06-07")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
after do
|
31
|
-
Timecop.return
|
32
|
-
end
|
33
|
-
|
34
|
-
it "clears failures older than 1 day by default" do
|
35
|
-
Sidekiq::ExpectedFailures.clear_old
|
36
|
-
assert_equal ["2014-06-10"], redis("smembers", "expected:dates")
|
37
|
-
assert_nil redis("get", "expected:2014-06-09")
|
38
|
-
assert_nil redis("get", "expected:2014-06-07")
|
39
|
-
end
|
40
|
-
|
41
|
-
it "can be called with days_old argument" do
|
42
|
-
Sidekiq::ExpectedFailures.clear_old(2)
|
43
|
-
assert_equal ["2014-06-09", "2014-06-10"], redis("smembers", "expected:dates").sort
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/test/lib/middleware_test.rb
DELETED
@@ -1,149 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
module Sidekiq
|
4
|
-
module ExpectedFailures
|
5
|
-
describe "Middleware" do
|
6
|
-
before do
|
7
|
-
$invokes = 0
|
8
|
-
Sidekiq.redis = REDIS
|
9
|
-
Sidekiq.redis { |c| c.flushdb }
|
10
|
-
Timecop.freeze(Time.local(2013, 1, 10))
|
11
|
-
Sidekiq.expected_failures = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
after { Timecop.return }
|
15
|
-
|
16
|
-
let(:msg) { {'class' => 'RandomStuff', 'args' => ['custom_argument'], 'retry' => false} }
|
17
|
-
let(:handler){ Sidekiq::ExpectedFailures::Middleware.new }
|
18
|
-
|
19
|
-
it 'does not handle exception by default' do
|
20
|
-
assert_raises RuntimeError do
|
21
|
-
handler.call(RegularWorker.new, msg, 'default') do
|
22
|
-
raise "Whooo, hold on there!"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'can can be configured to handle exceptions by default' do
|
28
|
-
Sidekiq.expected_failures = { VeryOwn::CustomException => nil }
|
29
|
-
|
30
|
-
handler.call(RegularWorker.new, msg, 'default') do
|
31
|
-
raise VeryOwn::CustomException
|
32
|
-
end
|
33
|
-
|
34
|
-
assert_raises RuntimeError do
|
35
|
-
handler.call(RegularWorker.new, msg, 'default') do
|
36
|
-
raise "This is not handled by default"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'respects build-in rescue and ensure blocks' do
|
42
|
-
assert_equal 0, $invokes
|
43
|
-
|
44
|
-
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
45
|
-
begin
|
46
|
-
raise ZeroDivisionError.new("We go a problem, sir")
|
47
|
-
rescue ZeroDivisionError => e
|
48
|
-
$invokes += 1
|
49
|
-
raise e # and now this should be caught by middleware
|
50
|
-
ensure
|
51
|
-
$invokes += 1
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
assert_equal 2, $invokes
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'handles all specified exceptions' do
|
59
|
-
handler.call(MultipleExceptionWorker.new, msg, 'default') do
|
60
|
-
raise NotImplementedError
|
61
|
-
end
|
62
|
-
|
63
|
-
handler.call(MultipleExceptionWorker.new, msg, 'default') do
|
64
|
-
raise VeryOwn::CustomException
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'logs exceptions' do
|
69
|
-
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
70
|
-
raise ZeroDivisionError
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
assert_equal(['2013-01-10'], redis("smembers", "expected:dates"))
|
75
|
-
assert_match(/custom_argument/, redis("lrange", "expected:2013-01-10", 0, -1)[0])
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'increments own counters per exception class' do
|
79
|
-
2.times do
|
80
|
-
handler.call(MultipleExceptionWorker.new, msg, 'default') do
|
81
|
-
raise VeryOwn::CustomException
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
5.times do
|
86
|
-
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
87
|
-
raise ZeroDivisionError
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
assert_equal 2, redis("hget", "expected:count", "VeryOwn::CustomException").to_i
|
92
|
-
assert_equal 5, redis("hget", "expected:count", "ZeroDivisionError").to_i
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'logs multiple exceptions' do
|
96
|
-
make_some_noise = lambda do |x|
|
97
|
-
x.times do
|
98
|
-
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
99
|
-
raise ZeroDivisionError
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
make_some_noise.call(10)
|
105
|
-
|
106
|
-
Timecop.freeze(Time.local(2013, 5, 15))
|
107
|
-
make_some_noise.call(5)
|
108
|
-
|
109
|
-
assert_equal 10, redis("llen", "expected:2013-01-10")
|
110
|
-
assert_equal 5, redis("llen", "expected:2013-05-15")
|
111
|
-
assert_equal(['2013-05-15', '2013-01-10'].sort, redis("smembers", "expected:dates").sort)
|
112
|
-
end
|
113
|
-
|
114
|
-
describe 'exception notify' do
|
115
|
-
|
116
|
-
it 'can be configured to notify once' do
|
117
|
-
exception = ZeroDivisionError.new
|
118
|
-
handler.expects(:handle_exception).with(exception, msg).once.returns(true)
|
119
|
-
|
120
|
-
50.times do
|
121
|
-
handler.call(CustomizedWorker.new, msg, 'default') do
|
122
|
-
raise exception
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'can be configured to notify multiple number of times' do
|
128
|
-
handler.expects(:handle_exception).times(3).returns(true)
|
129
|
-
|
130
|
-
60.times do
|
131
|
-
handler.call(CustomizedWorker.new, msg, 'default') do
|
132
|
-
raise VeryOwn::CustomException
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'can be configured not to notify at all' do
|
138
|
-
handler.expects(:handle_exception).never
|
139
|
-
|
140
|
-
60.times do
|
141
|
-
handler.call(CustomizedWorker.new, msg, 'default') do
|
142
|
-
raise NotImplementedError
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
data/test/lib/web_test.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
module Sidekiq
|
4
|
-
describe "WebExtension" do
|
5
|
-
include Rack::Test::Methods
|
6
|
-
|
7
|
-
def app
|
8
|
-
Sidekiq::Web
|
9
|
-
end
|
10
|
-
|
11
|
-
def create_sample_counter
|
12
|
-
redis("hset", "expected:count", "StandardError", 5)
|
13
|
-
redis("hset", "expected:count", "Custom::Error", 10)
|
14
|
-
end
|
15
|
-
|
16
|
-
def create_sample_failure
|
17
|
-
data = {
|
18
|
-
failed_at: Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
|
19
|
-
args: [{"hash" => "options", "more" => "options"}, 123],
|
20
|
-
exception: "ArgumentError",
|
21
|
-
error: "Some error message",
|
22
|
-
worker: "HardWorker",
|
23
|
-
queue: "api_calls"
|
24
|
-
}
|
25
|
-
|
26
|
-
Sidekiq.redis do |c|
|
27
|
-
c.lpush("expected:2013-09-10", Sidekiq.dump_json(data))
|
28
|
-
c.sadd("expected:dates", "2013-09-10")
|
29
|
-
end
|
30
|
-
|
31
|
-
Sidekiq.redis do |c|
|
32
|
-
c.lpush("expected:2013-09-09", Sidekiq.dump_json(data))
|
33
|
-
c.sadd("expected:dates", "2013-09-09")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
before do
|
38
|
-
Sidekiq.redis = REDIS
|
39
|
-
Sidekiq.redis {|c| c.flushdb }
|
40
|
-
Timecop.freeze(Time.local(2013, 9, 10))
|
41
|
-
end
|
42
|
-
|
43
|
-
after { Timecop.return }
|
44
|
-
|
45
|
-
it 'can display home with failures tab' do
|
46
|
-
get '/'
|
47
|
-
last_response.status.must_equal(200)
|
48
|
-
last_response.body.must_match(/Sidekiq/)
|
49
|
-
last_response.body.must_match(/Expected Failures/)
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'can display failures page without any failures' do
|
53
|
-
get '/expected_failures'
|
54
|
-
last_response.status.must_equal(200)
|
55
|
-
last_response.body.must_match(/Expected Failures/)
|
56
|
-
last_response.body.must_match(/No failed jobs found/)
|
57
|
-
end
|
58
|
-
|
59
|
-
describe 'when there are failures' do
|
60
|
-
before do
|
61
|
-
create_sample_failure
|
62
|
-
get '/expected_failures'
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'should be successful' do
|
66
|
-
last_response.status.must_equal(200)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'lists failed jobs' do
|
70
|
-
last_response.body.must_match(/HardWorker/)
|
71
|
-
last_response.body.must_match(/api_calls/)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'can remove all failed jobs' do
|
75
|
-
get '/expected_failures'
|
76
|
-
last_response.body.must_match(/HardWorker/)
|
77
|
-
|
78
|
-
post '/expected_failures/clear', { what: 'all' }
|
79
|
-
last_response.status.must_equal(302)
|
80
|
-
last_response.location.must_match(/expected_failures$/)
|
81
|
-
|
82
|
-
get '/expected_failures'
|
83
|
-
last_response.body.must_match(/No failed jobs found/)
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'can remove failed jobs older than 1 day' do
|
87
|
-
get '/expected_failures'
|
88
|
-
last_response.body.must_match(/2013-09-10/)
|
89
|
-
last_response.body.must_match(/2013-09-09/)
|
90
|
-
|
91
|
-
post '/expected_failures/clear', { what: 'old' }
|
92
|
-
last_response.status.must_equal(302)
|
93
|
-
last_response.location.must_match(/expected_failures$/)
|
94
|
-
|
95
|
-
get '/expected_failures'
|
96
|
-
last_response.body.wont_match(/2013-09-09/)
|
97
|
-
last_response.body.must_match(/2013-09-10/)
|
98
|
-
|
99
|
-
assert_nil redis("get", "expected:2013-09-09")
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
describe 'counter' do
|
104
|
-
describe 'when empty' do
|
105
|
-
it 'does not display counter div' do
|
106
|
-
create_sample_failure
|
107
|
-
get '/expected_failures'
|
108
|
-
last_response.body.wont_match(/dl-horizontal/)
|
109
|
-
last_response.body.wont_match(/All counters/i)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
describe 'when not empty' do
|
114
|
-
before { create_sample_counter }
|
115
|
-
|
116
|
-
it 'displays counters' do
|
117
|
-
get '/expected_failures'
|
118
|
-
last_response.body.must_match(/dl-horizontal/)
|
119
|
-
last_response.body.must_match(/All counters/i)
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'can clear counters' do
|
123
|
-
get '/expected_failures'
|
124
|
-
last_response.body.must_match(/Custom::Error/)
|
125
|
-
|
126
|
-
post '/expected_failures/clear', { what: 'counters' }
|
127
|
-
last_response.status.must_equal(302)
|
128
|
-
last_response.location.must_match(/expected_failures$/)
|
129
|
-
|
130
|
-
get '/expected_failures'
|
131
|
-
last_response.body.wont_match(/Custom::Error/)
|
132
|
-
|
133
|
-
assert_nil redis("get", "expected:count")
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
describe 'stats' do
|
139
|
-
describe 'when there are no errors' do
|
140
|
-
before do
|
141
|
-
get '/expected_failures/stats'
|
142
|
-
@response = Sidekiq.load_json(last_response.body)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'can return failures json without any failures' do
|
146
|
-
last_response.status.must_equal(200)
|
147
|
-
assert_equal({}, @response['failures'])
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
describe 'when there are errors' do
|
152
|
-
before do
|
153
|
-
create_sample_counter
|
154
|
-
get '/expected_failures/stats'
|
155
|
-
@response = Sidekiq.load_json(last_response.body)
|
156
|
-
end
|
157
|
-
|
158
|
-
it 'can return json with failures' do
|
159
|
-
last_response.status.must_equal(200)
|
160
|
-
assert_equal "5", @response['failures']['StandardError']
|
161
|
-
assert_equal "10", @response['failures']['Custom::Error']
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'coveralls'
|
2
|
-
Coveralls.wear! do
|
3
|
-
add_filter "/test/"
|
4
|
-
end
|
5
|
-
|
6
|
-
Encoding.default_external = Encoding::UTF_8
|
7
|
-
Encoding.default_internal = Encoding::UTF_8
|
8
|
-
|
9
|
-
ENV['RACK_ENV'] = 'test'
|
10
|
-
|
11
|
-
require "minitest/autorun"
|
12
|
-
require "minitest/pride"
|
13
|
-
|
14
|
-
require "mocha/api"
|
15
|
-
require "timecop"
|
16
|
-
require "rack/test"
|
17
|
-
|
18
|
-
require "sidekiq"
|
19
|
-
require "sidekiq/web"
|
20
|
-
|
21
|
-
require "sidekiq-expected_failures"
|
22
|
-
require "sidekiq/expected_failures/web"
|
23
|
-
|
24
|
-
require_relative "test_workers"
|
25
|
-
|
26
|
-
Sidekiq.logger.level = Logger::ERROR
|
27
|
-
|
28
|
-
REDIS = Sidekiq::RedisConnection.create(url: "redis://localhost/15")
|
29
|
-
|
30
|
-
def redis(command, *args)
|
31
|
-
Sidekiq.redis do |c|
|
32
|
-
c.send(command, *args)
|
33
|
-
end
|
34
|
-
end
|
data/test/test_workers.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module VeryOwn
|
2
|
-
class CustomException < StandardError; end
|
3
|
-
end
|
4
|
-
|
5
|
-
class RegularWorker
|
6
|
-
include ::Sidekiq::Worker
|
7
|
-
end
|
8
|
-
|
9
|
-
class SingleExceptionWorker
|
10
|
-
include ::Sidekiq::Worker
|
11
|
-
sidekiq_options expected_failures: { ZeroDivisionError => nil }
|
12
|
-
end
|
13
|
-
|
14
|
-
class MultipleExceptionWorker
|
15
|
-
include ::Sidekiq::Worker
|
16
|
-
sidekiq_options expected_failures: { NotImplementedError => nil, VeryOwn::CustomException => nil }
|
17
|
-
end
|
18
|
-
|
19
|
-
class CustomizedWorker
|
20
|
-
include ::Sidekiq::Worker
|
21
|
-
sidekiq_options expected_failures: {
|
22
|
-
NotImplementedError => nil,
|
23
|
-
VeryOwn::CustomException => [10, 20, 50],
|
24
|
-
ZeroDivisionError => 5
|
25
|
-
}
|
26
|
-
end
|