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 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