sidekiq-expected_failures 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a990ba6ca757a09aaa97bb9f2121d0523898db5f
4
- data.tar.gz: 2b69b65792f7c63c9c45582aa368429640bd085d
3
+ metadata.gz: 39b85cee8dfd3e0fffa64d50b1f17848679128fb
4
+ data.tar.gz: 380d9ca0d19df7f64736f323852a7a5fc6f16097
5
5
  SHA512:
6
- metadata.gz: 9a52f48d151ce12a511d9284908de664312a51ac5ad2443b2dc3f44940874533a028c9ec7b0cb16aca1be72128efdb40ae9792e54fca27a9c92b51f9b7c481fb
7
- data.tar.gz: 7812162b47a775fd2d4dafd2bb1735c167c8c937be124cfdb6e7cbecf411b9f5d3ad3877678382e7e643b65c2a92a460212419460e76df465719b93c8ee91f1f
6
+ metadata.gz: 214cbb3d4236cb160cf07f6f24698141afe1281587fdf1b5c71cedb9fc2925365108665187219269653ad17e884980b8283c689f3f3bdff762164ddd45f83c1b
7
+ data.tar.gz: 4dd36c9fd6221d7b322de26414eb6b8b5b309b58e70d060fc12fa0e361c8b04f1d5f0e8f89c73b4fc611636819c2ac80870a9aaec92ba577badc9766600fe267
data/.gitignore CHANGED
@@ -19,3 +19,5 @@ tmp
19
19
  .ruby-version
20
20
  .ruby-gemset
21
21
  .coveralls.yml
22
+ *.gemfile.lock
23
+ .DS_Store
@@ -3,9 +3,14 @@ sudo: false
3
3
  cache: bundler
4
4
 
5
5
  rvm:
6
- - 2.3.4
7
- - 2.4.1
8
- - 2.2.7
6
+ - 2.2.9
7
+ - 2.3.6
8
+ - 2.4.3
9
+ - 2.5.0
10
+
11
+ gemfile:
12
+ - gemfiles/sidekiq_4.gemfile
13
+ - gemfiles/sidekiq_5.gemfile
9
14
 
10
15
  services:
11
16
  - redis-server
@@ -0,0 +1,7 @@
1
+ appraise 'sidekiq-4' do
2
+ gem 'sidekiq', '~> 4.2', '>= 4.2.10'
3
+ end
4
+
5
+ appraise 'sidekiq-5' do
6
+ gem 'sidekiq', '~> 5.1', '>= 5.1'
7
+ end
@@ -1,8 +1,17 @@
1
- ## 0.3.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
@@ -1,9 +1,2 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
-
4
- gem "minitest"
5
-
6
- platforms :rbx do
7
- gem "rubysl", "~> 2.0" # if using anything in the ruby standard library
8
- gem "rubinius-developer_tools", "~> 2.0.0" # if using any of coverage, debugger, profiler
9
- end
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
- ![](http://i.imgur.com/7Fe8voD.jpg)
57
+ ![](img/interface.gif?raw=true)
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).
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 4.2", ">= 4.2.10"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 5.1", ">= 5.1"
6
+
7
+ gemspec path: "../"
@@ -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
- def self.clear(dates)
45
- dates.each do |date|
46
- Sidekiq.redis do |c|
47
- c.multi do |m|
48
- m.srem("expected:dates", date)
49
- m.del("expected:#{date}")
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
- def setup_exceptions(worker)
30
- @handled_exceptions = worker.class.get_sidekiq_options['expected_failures'] || Sidekiq.expected_failures
31
- end
29
+ def setup_exceptions(worker)
30
+ @handled_exceptions = worker.class.get_sidekiq_options['expected_failures'] || Sidekiq.expected_failures
31
+ end
32
32
 
33
- def exception_intervals(ex)
34
- [handled_exceptions[ex.class]].flatten.compact
35
- end
33
+ def exception_intervals(ex)
34
+ [handled_exceptions[ex.class]].flatten.compact
35
+ end
36
36
 
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)
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
- def today
50
- Date.today.to_s
51
- end
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,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module ExpectedFailures
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  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
- job["args"].each_with_index { |argument, index| data << "data-#{index+1}='#{h argument.inspect}'" }
12
- "<a href='#' #{data.join(' ')} title='#{job["worker"]}'>Details</a>"
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
- app.get "/expected_failures" do
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.7.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
@@ -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
- html += "<tr><th>Argument #" + key + "</th><td>" + value + "</td></tr>";
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
- $('#expected tbody td:last-child > a').live('click', function(e){
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
- e.preventDefault();
44
+ event.preventDefault();
16
45
  });
17
46
 
18
- $('#filter-jobs select, #clear-jobs select').live('change', function(e){
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?date=#{@date}" } %>
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
- <% @jobs.each do |job| %>
92
- <tr>
93
- <td><%= Time.parse(job['failed_at']).strftime('%m/%d/%Y %H:%M:%S') %></td>
94
- <td><%= job["worker"] %></td>
95
- <td><%= job["exception"] %> <small>(<%= job["error"]%>)</small></td>
96
- <td><a href="<%= "#{root_path}/queues/#{job["queue"]}"%>"><%= job["queue"] %></a></td>
97
- <td><%= link_to_details(job) %></td>
98
- </tr>
99
- <% end %>
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.3.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: 2017-06-14 00:00:00.000000000 Z
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.7.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.7.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
@@ -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
@@ -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
@@ -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
@@ -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