rspec-sidekiq 3.0.3 → 5.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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +61 -0
  3. data/LICENSE +12 -0
  4. data/README.md +270 -94
  5. data/lib/rspec/sidekiq/batch.rb +34 -4
  6. data/lib/rspec/sidekiq/configuration.rb +29 -2
  7. data/lib/rspec/sidekiq/helpers/within_sidekiq_retries_exhausted_block.rb +5 -3
  8. data/lib/rspec/sidekiq/helpers.rb +2 -0
  9. data/lib/rspec/sidekiq/matchers/base.rb +368 -0
  10. data/lib/rspec/sidekiq/matchers/be_delayed.rb +19 -3
  11. data/lib/rspec/sidekiq/matchers/be_expired_in.rb +2 -0
  12. data/lib/rspec/sidekiq/matchers/be_processed_in.rb +2 -0
  13. data/lib/rspec/sidekiq/matchers/be_retryable.rb +2 -0
  14. data/lib/rspec/sidekiq/matchers/be_unique.rb +23 -1
  15. data/lib/rspec/sidekiq/matchers/enqueue_sidekiq_job.rb +91 -0
  16. data/lib/rspec/sidekiq/matchers/have_enqueued_sidekiq_job.rb +35 -0
  17. data/lib/rspec/sidekiq/matchers/save_backtrace.rb +2 -0
  18. data/lib/rspec/sidekiq/matchers.rb +16 -8
  19. data/lib/rspec/sidekiq/sidekiq.rb +3 -1
  20. data/lib/rspec/sidekiq/version.rb +3 -1
  21. data/lib/rspec-sidekiq.rb +4 -0
  22. metadata +146 -84
  23. data/.gitattributes +0 -22
  24. data/.gitignore +0 -2
  25. data/.rspec +0 -4
  26. data/.simplecov +0 -5
  27. data/Gemfile +0 -9
  28. data/lib/rspec/sidekiq/matchers/have_enqueued_job.rb +0 -183
  29. data/rspec-sidekiq.gemspec +0 -37
  30. data/spec/rspec/sidekiq/batch_spec.rb +0 -77
  31. data/spec/rspec/sidekiq/helpers/retries_exhausted_spec.rb +0 -40
  32. data/spec/rspec/sidekiq/matchers/be_delayed_spec.rb +0 -238
  33. data/spec/rspec/sidekiq/matchers/be_expired_in_spec.rb +0 -57
  34. data/spec/rspec/sidekiq/matchers/be_processed_in_spec.rb +0 -114
  35. data/spec/rspec/sidekiq/matchers/be_retryable_spec.rb +0 -129
  36. data/spec/rspec/sidekiq/matchers/be_unique_spec.rb +0 -115
  37. data/spec/rspec/sidekiq/matchers/have_enqueued_job_spec.rb +0 -228
  38. data/spec/rspec/sidekiq/matchers/save_backtrace_spec.rb +0 -136
  39. data/spec/rspec/sidekiq/sidekiq_spec.rb +0 -15
  40. data/spec/spec_helper.rb +0 -29
  41. data/spec/support/factories.rb +0 -33
  42. data/spec/support/init.rb +0 -6
  43. data/spec/support/test_action_mailer.rb +0 -6
  44. data/spec/support/test_job.rb +0 -6
  45. data/spec/support/test_resource.rb +0 -16
  46. data/spec/support/test_worker.rb +0 -8
  47. data/spec/support/test_worker_alternative.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dd244416f99c35639675efb4b3473dd9741d512c
4
- data.tar.gz: 6fa5eb3e029086fe4d316a6a9896b7acddb834de
2
+ SHA256:
3
+ metadata.gz: 56ce84afbf67491fa24da1f527bced61c54afa3d62dbb50e6a09a89e8c0bc16c
4
+ data.tar.gz: 5b4a368bbb098d580713009fb9381d66326c37eced31c91eaf85b43158a85db5
5
5
  SHA512:
6
- metadata.gz: fe360471b5c79f74e4af320881d97d0d26ec0cf837a8309e8b898221d71c63bce2d2a750d32b9427dfb931dc4a83837e4c2e3e6b1ddd6a5ed17050205f7e4aa3
7
- data.tar.gz: 1d0913303fbdd4c72aea0248e815d4842a5c8fe9d0a338c6cad7cf0dbd59fc7145899718d51185444c72023772605e30099326c8481345927f083c9db91df7e6
6
+ metadata.gz: 4ec18b3d6ceff7390807d7358259b44f236ca6d81b1b9175c069b6c4912041b914082042a5396c7cf1eb0b602a5448d9aacd9e23b3cee211ad7222c3ec3719af
7
+ data.tar.gz: 4f0232e7b482f7c565a5cca98be9b7143e51adbe6015329f9d052e393fd857fe97e2449fb53f9e3d209ff6319c54e23e9e2919e7003a44bfc00834692039eed0
data/CHANGES.md CHANGED
@@ -1,3 +1,64 @@
1
+ Unreleased
2
+ ---
3
+ * Add `until` matcher for Sidekiq Enterprise unique jobs (#232, #147)
4
+ * Add `with_context` matcher for matching against job contexts (#222)
5
+ * Support for Sidekiq 8 (#233)
6
+ * Add `frozen_string_literal: true` (#220)
7
+ * Fix queue always nil in default_retries_exhausted_message (#229)
8
+ * Fix pattern matching warnings on Ruby 2.7 (#227)
9
+
10
+ 5.0.0
11
+ ---
12
+ * [BREAKING] Make `have_enqueued_sidekiq_job()` match jobs with any arguments (same as `enqueue_sidekiq_job()` or `have_enqueued_sidekiq_job(any_args)`) (#215)
13
+ * Add support for expected number of jobs to both `enqueue_sidekiq_job` and `have_enqueued_sidekiq_job` (#219)
14
+
15
+ 4.2.0
16
+ ---
17
+ * Add warning about `have_enqueued_sidekiq_job` with no arguments (default will change in next major release) (#216, #217)
18
+
19
+ 4.1.0
20
+ ---
21
+ * Add Composable support to `enqueue_sidekiq_job` and
22
+ `have_enqueued_sidekiq_job` (#211)
23
+ * Add `immediately` to specify that a job should be enqueued without any delay (#210)
24
+
25
+ 4.0.2
26
+ ---
27
+ * Explicitly require forwardable (#204)
28
+
29
+ 4.0.1
30
+ ---
31
+ * Restore the old normalizing expected args behavior with symbols (#205)
32
+ * fixes an unintentional breaking change in 4.0.0
33
+
34
+ 4.0.0
35
+ ---
36
+ * [BREAKING] Dropped support for matching jobs on ActiveJob's private API args, (e.g. `_aj_globalid` and `_aj_ruby2_keywords`). `_aj_globalid` can be replaced with the object itself, e.g. `have_enqueued_sidekiq_job(user)`.
37
+ * [BREAKING] Dropped support for Ruby 2.6
38
+ * [BREAKING] Mocking Sidekiq::Batch is now _opt in_ (was opt-out) by default
39
+ * [BREAKING] Dropped the `have_enqueued_job` matcher (deprecated since v3)
40
+ * [BREAKING] Dropped support for Sidekiq <5
41
+ * [BREAKING] Dropped support for Rails 5
42
+ * Add support for Sidekiq 7 (#192)
43
+ * Add support for builtin argument matchers from rspec mocks for
44
+ `have_enqueued_sidekiq_job` (#200)
45
+ * Add `#on` to `have_enqueued_sidekiq_job` to support testing queue at enqueue time (#197)
46
+ * Add `enqueue_sidekiq_job` for block-syntax style expectations
47
+ * Clarified `have_enqueued_sidekiq_job` error message to make it clear that the "actual arguments" list is an array of argument-lists across all enqueued jobs. (#195)
48
+ * Fix `in` and `at` evaluation to match Sidekiq (#194)
49
+ * Fix `be_delayed` argument matcher (#196)
50
+ * Fix argument matching ignoring order (#200)
51
+ * Fix `be_delayed` time math when using `delay_for` (#198)
52
+
53
+ 3.1.0
54
+ ---
55
+ * Add support for latest ruby and Rails 5 (coding-bunny #156)
56
+ * Support `Class#method` notation in batch callback (caalberts #155)
57
+ * Fix bug with time comparison in delayed jobs (geeosh #153 with help from col)
58
+ * remove usage of `Integer#blank?` (lanej #152)
59
+ * Add `NullBatch#description` (dsantosmerino #139)
60
+ * README updates (BenMusch #142, akihikodaki #141, wpolicarpo #160)
61
+
1
62
  3.0.3
2
63
  ---
3
64
  * Re-splat arguments for the have_enqueued_job alias (aprescott #136)
data/LICENSE CHANGED
@@ -1,5 +1,17 @@
1
1
  The MIT License (MIT)
2
2
 
3
+ Copyright (c) 2023 Will Spurgin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
+
11
+ Elements of this software and associated documentation files are covered by the following license:
12
+
13
+ The MIT License (MIT)
14
+
3
15
  Copyright (c) 2014, 2015 Phil Ostler
4
16
 
5
17
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
data/README.md CHANGED
@@ -1,23 +1,7 @@
1
- **Welcome @wpolicarpo and @packrat386 as new maintainers for `rspec-sidekiq`!**
1
+ [![Gem Version](https://badge.fury.io/rb/rspec-sidekiq.svg)](https://badge.fury.io/rb/rspec-sidekiq)
2
+ [![Github Actions CI][github_actions_badge]][github_actions]
2
3
 
3
- # RSpec for Sidekiq
4
-
5
- [![RubyGems][gem_version_badge]][ruby_gems]
6
- [![Code Climate][code_climate_badge]][code_climate]
7
- [![Travis CI][travis_ci_badge]][travis_ci]
8
- [![Coveralls][coveralls_badge]][coveralls]
9
- [![Gemnasium][gemnasium_badge]][gemnasium]
10
-
11
- ***Simple testing of Sidekiq jobs via a collection of matchers and helpers***
12
-
13
- [RubyGems][ruby_gems] |
14
- [Code Climate][code_climate] |
15
- [GitHub][github] |
16
- [Travis CI][travis_ci] |
17
- [Coveralls][coveralls] |
18
- [Gemnasium][gemnasium] |
19
- [RubyDoc][ruby_doc] |
20
- [Ruby Toolbox][ruby_toolbox]
4
+ Simple testing of Sidekiq jobs via a collection of matchers and helpers.
21
5
 
22
6
  [Jump to Matchers &raquo;](#matchers) | [Jump to Helpers &raquo;](#helpers)
23
7
 
@@ -48,36 +32,184 @@ end
48
32
  ```
49
33
 
50
34
  ## Matchers
51
- * [be_delayed](#be_delayed)
52
- * [be_processed_in](#be_processed_in)
53
- * [be_retryable](#be_retryable)
54
- * [be_unique](#be_unique)
55
- * [have_enqueued_sidekiq_job](#have_enqueued_sidekiq_job)
35
+ * [```enqueue_sidekiq_job```](#enqueue_sidekiq_job)
36
+ * [```have_enqueued_sidekiq_job```](#have_enqueued_sidekiq_job)
37
+ * [```be_processed_in```](#be_processed_in)
38
+ * [```be_retryable```](#be_retryable)
39
+ * [```save_backtrace```](#save_backtrace)
40
+ * [```be_unique```](#be_unique)
41
+ * [```be_expired_in```](#be_expired_in)
42
+ * [```be_delayed``` (_deprecated_)](#be_delayed)
43
+
44
+ ### ```enqueue_sidekiq_job```
45
+
46
+ *Describes that the block should enqueue a job*. Optionally specify the
47
+ specific job class, arguments, timing, and other context
56
48
 
57
- ### be_delayed
58
- *Describes a method that should be invoked asynchronously (See [Sidekiq Delayed Extensions][sidekiq_wiki_delayed_extensions])*
59
49
  ```ruby
60
- Object.delay.is_nil? # delay
61
- expect(Object.method :is_nil?).to be_delayed
62
- Object.delay.is_a? Object # delay with argument
63
- expect(Object.method :is_a?).to be_delayed(Object)
50
+ # Basic
51
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job
64
52
 
65
- Object.delay_for(1.hour).is_nil? # delay for
66
- expect(Object.method :is_nil?).to be_delayed.for 1.hour
67
- Object.delay_for(1.hour).is_a? Object # delay for with argument
68
- expect(Object.method :is_a?).to be_delayed(Object).for 1.hour
53
+ # A specific job class
54
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job(AwesomeJob)
69
55
 
70
- Object.delay_until(1.hour.from_now).is_nil? # delay until
71
- expect(Object.method :is_nil?).to be_delayed.until 1.hour.from_now
72
- Object.delay_until(1.hour.from_now).is_a? Object # delay until with argument
73
- expect(Object.method :is_a?).to be_delayed(Object).until 1.hour.from_now
56
+ # with specific arguments
57
+ expect { AwesomeJob.perform_async "Awesome!" }.to enqueue_sidekiq_job.with("Awesome!")
74
58
 
75
- #Rails Mailer
76
- MyMailer.delay.some_mail
77
- expect(MyMailer.instance_method :some_mail).to be_delayed
59
+ # On a specific queue
60
+ expect { AwesomeJob.set(queue: "high").perform_async }.to enqueue_sidekiq_job.on("high")
61
+
62
+ # At a specific datetime
63
+ specific_time = 1.hour.from_now
64
+ expect { AwesomeJob.perform_at(specific_time) }.to enqueue_sidekiq_job.at(specific_time)
65
+
66
+ # In a specific interval (be mindful of freezing or managing time here)
67
+ freeze_time do
68
+ expect { AwesomeJob.perform_in(1.hour) }.to enqueue_sidekiq_job.in(1.hour)
69
+ end
70
+
71
+ # A specific number of times
72
+
73
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.once
74
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.exactly(1).time
75
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.exactly(:once)
76
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_least(1).time
77
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_least(:once)
78
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(2).times
79
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(:twice)
80
+ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(:thrice)
81
+
82
+ # With specific context:
83
+ # Useful for testing anything `set` on the job, including
84
+ # overrides to things like `retry`
85
+ expect {
86
+ AwesomeJob.set(trace_id: "something").perform_async
87
+ }.to enqueue_sidekiq_job.with_context(trace_id: anything)
88
+
89
+ expect {
90
+ AwesomeJob.set(retry: 5).perform_async
91
+ }.to enqueue_sidekiq_job.with_context(retry: 5)
92
+
93
+ # Combine and chain them as desired
94
+ expect { AwesomeJob.perform_at(specific_time, "Awesome!") }.to(
95
+ enqueue_sidekiq_job(AwesomeJob)
96
+ .with("Awesome!")
97
+ .on("default")
98
+ .at(specific_time)
99
+ )
100
+
101
+ # Also composable
102
+ expect do
103
+ AwesomeJob.perform_async
104
+ OtherJob.perform_async
105
+ end.to enqueue_sidekiq_job(AwesomeJob).and enqueue_sidekiq_job(OtherJob)
106
+ ```
107
+
108
+ ### ```have_enqueued_sidekiq_job```
109
+
110
+ Describes that there should be an enqueued job (with the specified arguments):
111
+
112
+ ```ruby
113
+ AwesomeJob.perform_async 'Awesome', true
114
+ # test with...
115
+ expect(AwesomeJob).to have_enqueued_sidekiq_job
116
+ expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true)
117
+ ```
118
+
119
+ You can use the built-in RSpec args matchers too:
120
+ ```ruby
121
+ AwesomeJob.perform_async({"something" => "Awesome", "extra" => "stuff"})
122
+
123
+ # using built-in matchers from rspec-mocks:
124
+ expect(AwesomeJob).to have_enqueued_sidekiq_job(hash_including("something" => "Awesome"))
125
+ expect(AwesomeJob).to have_enqueued_sidekiq_job(any_args)
126
+ expect(AwesomeJob).to have_enqueued_sidekiq_job(hash_excluding("bad_stuff" => anything))
127
+
128
+ # composable as well
129
+ expect(AwesomeJob).to have_enqueued_sidekiq_job(any_args).and have_enqueued_sidekiq_job(hash_including("something" => "Awesome"))
130
+ ```
131
+
132
+ You can specify the number of jobs enqueued:
133
+
134
+ ```ruby
135
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.once
136
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.exactly(1).time
137
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.exactly(:once)
138
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_least(1).time
139
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_least(:once)
140
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(2).times
141
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(:twice)
142
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(:thrice)
143
+ ```
144
+
145
+ Likewise, specify what should be in the context:
146
+ ```ruby
147
+ AwesomeJob.set(trace_id: "something").perform_async
148
+
149
+ expect(AwesomeJob).to have_enqueued_sidekiq_job.with_context(trace_id: anything)
150
+ ```
151
+
152
+ #### Testing scheduled jobs
153
+
154
+ *Use chainable matchers `#at`, `#in` and `#immediately`*
155
+
156
+ ```ruby
157
+ time = 5.minutes.from_now
158
+ AwesomeJob.perform_at time, 'Awesome', true
159
+ # test with...
160
+ expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).at(time)
161
+ ```
162
+ ```ruby
163
+ AwesomeJob.perform_in 5.minutes, 'Awesome', true
164
+ # test with...
165
+ expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).in(5.minutes)
166
+ ```
167
+
168
+ ```ruby
169
+ # Job scheduled for a date in the past are enqueued immediately.
170
+ AwesomeJob.perform_later 5.minutes.ago, 'Awesome', true # equivalent to: AwesomeJob.perform_async 'Awesome', true
171
+ # test with...
172
+ expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).immediately
78
173
  ```
79
174
 
80
- ### be_processed_in
175
+ #### Testing queue set for job
176
+
177
+ Use the chainable `#on` matcher
178
+
179
+ ```ruby
180
+ class AwesomeJob
181
+ include Sidekiq::Job
182
+
183
+ sidekiq_options queue: :low
184
+ end
185
+
186
+ AwesomeJob.perform_async("a little awesome")
187
+
188
+ # test with..
189
+ expect(AwesomeJob).to have_enqueued_sidekiq_job("a little awesome").on("low")
190
+
191
+ # Setting the queue when enqueuing
192
+ AwesomeJob.set(queue: "high").perform_async("Very Awesome!")
193
+
194
+ expect(AwesomeJob).to have_enqueued_sidekiq_job("Very Awesome!").on("high")
195
+ ```
196
+
197
+ #### Testing ActiveMailer jobs
198
+
199
+ ```ruby
200
+ user = User.first
201
+ AwesomeActionMailer.invite(user, true).deliver_later
202
+
203
+ expect(Sidekiq::Worker).to have_enqueued_sidekiq_job(
204
+ "AwesomeActionMailer",
205
+ "invite",
206
+ "deliver_now",
207
+ user,
208
+ true
209
+ )
210
+ ```
211
+
212
+ ### ```be_processed_in```
81
213
  *Describes the queue that a job should be processed in*
82
214
  ```ruby
83
215
  sidekiq_options queue: :download
@@ -86,8 +218,12 @@ expect(AwesomeJob).to be_processed_in :download # or
86
218
  it { is_expected.to be_processed_in :download }
87
219
  ```
88
220
 
89
- ### be_retryable
90
- *Describes if a job should retry when there is a failure in it's execution*
221
+ ### ```be_retryable```
222
+ *Describes if a job should retry when there is a failure in its execution*
223
+
224
+ Note: this only tests against the `retry` option in the job's Sidekiq options.
225
+ To test an enqueued job's retry, i.e. `AwesomeJob.set(retry: 5)`, use
226
+ `with_context`
91
227
  ```ruby
92
228
  sidekiq_options retry: 5
93
229
  # test with...
@@ -101,14 +237,14 @@ expect(AwesomeJob).to be_retryable false # or
101
237
  it { is_expected.to be_retryable false }
102
238
  ```
103
239
 
104
- ### save_backtrace
105
- *Describes if a job should save the error backtrace when there is a failure in it's execution*
240
+ ### ```save_backtrace```
241
+ *Describes if a job should save the error backtrace when there is a failure in its execution*
106
242
  ```ruby
107
243
  sidekiq_options backtrace: 5
108
244
  # test with...
109
245
  expect(AwesomeJob).to save_backtrace # or
110
246
  it { is_expected.to save_backtrace }
111
- # ...or alternatively specifiy the number of lines that should be saved
247
+ # ...or alternatively specify the number of lines that should be saved
112
248
  expect(AwesomeJob).to save_backtrace 5 # or
113
249
  it { is_expected.to save_backtrace 5 }
114
250
  # ...or when it should not save the backtrace
@@ -118,16 +254,35 @@ it { is_expected.to_not save_backtrace } # or
118
254
  it { is_expected.to save_backtrace false }
119
255
  ```
120
256
 
121
- ### be_unique
122
- *Describes when a job should be unique within it's queue*
257
+ ### ```be_unique```
258
+
259
+ :warning: This is intended to for Sidekiq Enterprise unique job implementation.
260
+ There is _limited_ support for Sidekiq Unique Jobs, but compatibility is not
261
+ guaranteed.
262
+
263
+ *Describes when a job should be unique within its queue*
123
264
  ```ruby
124
- sidekiq_options unique: true
265
+ sidekiq_options unique_for: 1.hour
125
266
  # test with...
126
267
  expect(AwesomeJob).to be_unique
127
268
  it { is_expected.to be_unique }
269
+
270
+ # specify a specific interval
271
+ sidekiq_options unique_for: 1.hour
272
+ it { is_expected.to be_unique.for(1.hour) }
128
273
  ```
129
274
 
130
- ### be_expired_in
275
+ #### `until` sub-matcher
276
+
277
+ :warning: This sub-matcher only works for Sidekiq Enterprise
278
+
279
+ ```ruby
280
+ sidekiq_options unique_for: 1.hour, unique_until: :start
281
+ it { is_expected.to be_unique.until(:start) }
282
+ ```
283
+
284
+
285
+ ### ```be_expired_in```
131
286
  *Describes when a job should expire*
132
287
  ```ruby
133
288
  sidekiq_options expires_in: 1.hour
@@ -136,33 +291,32 @@ it { is_expected.to be_expired_in 1.hour }
136
291
  it { is_expected.to_not be_expired_in 2.hours }
137
292
  ```
138
293
 
139
- ### have_enqueued__sidekiq_job
140
- *Describes that there should be an enqueued job with the specified arguments*
294
+ ### ```be_delayed```
141
295
 
142
- **Note:** When using rspec-rails >= 3.4, use `have_enqueued_sidekiq_job` instead to
143
- prevent a name clash with rspec-rails' ActiveJob matcher.
296
+ **This matcher is deprecated**. Use of it with Sidekiq 7+ will raise an error.
297
+ Sidekiq 7 [dropped Delayed
298
+ Extensions](https://github.com/sidekiq/sidekiq/issues/5076).
144
299
 
300
+ *Describes a method that should be invoked asynchronously (See [Sidekiq Delayed Extensions][sidekiq_wiki_delayed_extensions])*
145
301
  ```ruby
146
- AwesomeJob.perform_async 'Awesome', true
147
- # test with...
148
- expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true)
302
+ Object.delay.is_nil? # delay
303
+ expect(Object.method :is_nil?).to be_delayed
304
+ Object.delay.is_a? Object # delay with argument
305
+ expect(Object.method :is_a?).to be_delayed(Object)
149
306
 
150
- # Code written with older versions of the gem may use the deprecated
151
- # have_enqueued_job matcher.
152
- expect(AwesomeJob).to have_enqueued_job('Awesome', true)
153
- ```
307
+ Object.delay_for(1.hour).is_nil? # delay for
308
+ expect(Object.method :is_nil?).to be_delayed.for 1.hour
309
+ Object.delay_for(1.hour).is_a? Object # delay for with argument
310
+ expect(Object.method :is_a?).to be_delayed(Object).for 1.hour
154
311
 
155
- #### Testing scheduled jobs
156
- *Use chainable matchers `#at` and `#in`*
157
- ```ruby
158
- Awesomejob.perform_at 5.minutes.from_now, 'Awesome', true
159
- # test with...
160
- expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).at(5.minutes.from_now)
161
- ```
162
- ```ruby
163
- Awesomejob.perform_in 5.minutes, 'Awesome', true
164
- # test with...
165
- expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).in(5.minutes)
312
+ Object.delay_until(1.hour.from_now).is_nil? # delay until
313
+ expect(Object.method :is_nil?).to be_delayed.until 1.hour.from_now
314
+ Object.delay_until(1.hour.from_now).is_a? Object # delay until with argument
315
+ expect(Object.method :is_a?).to be_delayed(Object).until 1.hour.from_now
316
+
317
+ #Rails Mailer
318
+ MyMailer.delay.some_mail
319
+ expect(MyMailer.instance_method :some_mail).to be_delayed
166
320
  ```
167
321
 
168
322
  ## Example matcher usage
@@ -184,11 +338,38 @@ end
184
338
  ```
185
339
 
186
340
  ## Helpers
187
- * [Batches (Sidekiq Pro)](#batches)
341
+ * [Batches (Sidekiq Pro) _experimental_](#batches)
188
342
  * [`within_sidekiq_retries_exhausted_block`](#within_sidekiq_retries_exhausted_block)
189
343
 
190
344
  ### Batches
191
- If you are using Sidekiq Batches ([Sidekiq Pro feature][sidekiq_wiki_batches]), rspec-sidekiq replaces the implementation (using the NullObject pattern) enabling testing without a Redis instance. Mocha and RSpec stubbing is supported here.
345
+
346
+ If you are using Sidekiq Batches ([Sidekiq Pro feature][sidekiq_wiki_batches]),
347
+ You can *opt-in* with `stub_batches` to make `rspec-sidekiq` mock the
348
+ implementation (using a NullObject pattern). This enables testing without a
349
+ Redis instance. Mocha and RSpec stubbing is supported here.
350
+
351
+ :warning: **Caution**: Opting-in to this feature, while allowing you to test without
352
+ having Redis, _does not_ provide the exact API that `Sidekiq::Batch` does. As
353
+ such it can cause surprises.
354
+
355
+
356
+ ```ruby
357
+ RSpec.describe "Using mocked batches", stub_batches: true do
358
+ it "uses mocked batches" do
359
+ batch = Sidekiq::Batch.new
360
+ batch.jobs do
361
+ SomeJob.perform_async 123
362
+ end
363
+
364
+ expect(SomeJob).to have_enqueued_sidekiq_job
365
+
366
+ # Caution, the NullObject pattern means that the mocked Batch implementation
367
+ # responds to anything... even if it's not on the true `Sidekiq::Batch` API
368
+ # For example, the following fails
369
+ expect { batch.foobar! }.to raise_error(NoMethodError)
370
+ end
371
+ end
372
+ ```
192
373
 
193
374
  ### within_sidekiq_retries_exhausted_block
194
375
  ```ruby
@@ -202,10 +383,15 @@ FooClass.within_sidekiq_retries_exhausted_block {
202
383
  ```
203
384
 
204
385
  ## Testing
205
- ```bundle exec rspec spec```
386
+ ```
387
+ bundle exec rspec spec
388
+ ```
206
389
 
207
390
  ## Maintainers
208
- * @wpolicarpo
391
+ * @wspurgin
392
+
393
+ ### Alumni
394
+
209
395
  * @packrat386
210
396
  * @philostler
211
397
 
@@ -214,20 +400,10 @@ Please do! If there's a feature missing that you'd love to see then get in on th
214
400
 
215
401
  Issues/Pull Requests/Comments all welcome...
216
402
 
217
- [code_climate]: https://codeclimate.com/github/philostler/rspec-sidekiq
218
- [code_climate_badge]: https://codeclimate.com/github/philostler/rspec-sidekiq.svg
219
- [coveralls]: https://coveralls.io/r/philostler/rspec-sidekiq
220
- [coveralls_badge]: https://img.shields.io/coveralls/philostler/rspec-sidekiq.svg?branch=develop
221
- [gem_version_badge]: https://badge.fury.io/rb/rspec-sidekiq.svg
222
- [gemnasium]: https://gemnasium.com/philostler/rspec-sidekiq
223
- [gemnasium_badge]: https://gemnasium.com/philostler/rspec-sidekiq.svg
224
- [github]: http://github.com/philostler/rspec-sidekiq
225
- [ruby_doc]: http://rubydoc.info/gems/rspec-sidekiq/frames
226
- [ruby_gems]: http://rubygems.org/gems/rspec-sidekiq
227
- [ruby_toolbox]: http://www.ruby-toolbox.com/projects/rspec-sidekiq
228
- [travis_ci]: http://travis-ci.org/philostler/rspec-sidekiq
229
- [travis_ci_badge]: https://travis-ci.org/philostler/rspec-sidekiq.svg?branch=develop
230
-
231
- [rspec_sidekiq_wiki_faq_&_troubleshooting]: https://github.com/philostler/rspec-sidekiq/wiki/FAQ-&-Troubleshooting
232
- [sidekiq_wiki_batches]: https://github.com/mperham/sidekiq/wiki/Batches
233
- [sidekiq_wiki_delayed_extensions]: https://github.com/mperham/sidekiq/wiki/Delayed-Extensions
403
+ [github]: http://github.com/wspurgin/rspec-sidekiq
404
+ [github_actions]: https://github.com/wspurgin/rspec-sidekiq/actions
405
+ [github_actions_badge]: https://github.com/wspurgin/rspec-sidekiq/actions/workflows/main.yml/badge.svg
406
+
407
+ [rspec_sidekiq_wiki_faq_&_troubleshooting]: https://github.com/wspurgin/rspec-sidekiq/wiki/FAQ-&-Troubleshooting
408
+ [sidekiq_wiki_batches]: https://github.com/sidekiq/sidekiq/wiki/Batches
409
+ [sidekiq_wiki_delayed_extensions]: https://github.com/sidekiq/sidekiq/wiki/Delayed-Extensions
@@ -1,4 +1,7 @@
1
- require 'rspec/core'
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core"
4
+ require "rspec/support/fuzzy_matcher"
2
5
 
3
6
  if defined? Sidekiq::Batch
4
7
  module RSpec
@@ -9,7 +12,26 @@ if defined? Sidekiq::Batch
9
12
  end
10
13
  end
11
14
 
15
+ ##
16
+ # Sidekiq::Batch is a Sidekiq::Pro feature. However the general consensus is
17
+ # that, by defeault, you can't test without redis. RSpec::Sidekiq includes
18
+ # a "null object" pattern implementation to mock Batches. This will mock
19
+ # Sidekiq::Batch and prevent it from using Redis.
20
+ #
21
+ # This is _opt-in_ only feature.
22
+ #
23
+ # RSpec.describe "Using mocked batches", stub_batches: true do
24
+ # it "uses mocked batches" do
25
+ # batch = Sidekiq::Batch.new
26
+ # batch.jobs do
27
+ # SomeJob.perform_async 123
28
+ # end
29
+ #
30
+ # expect(SomeJob).to have_enqueued_sidekiq_job
31
+ # end
32
+ # end
12
33
  class NullBatch < NullObject
34
+ attr_accessor :description
13
35
  attr_reader :bid
14
36
 
15
37
  def initialize(bid = nil)
@@ -45,9 +67,17 @@ if defined? Sidekiq::Batch
45
67
  def join
46
68
  ::Sidekiq::Worker.drain_all
47
69
 
48
- @callbacks.each do |event, callback_class, options|
70
+ @callbacks.each do |event, callback, options|
49
71
  if event != :success || failures == 0
50
- callback_class.new.send("on_#{event}", self, options)
72
+ case callback
73
+ when Class
74
+ callback.new.send("on_#{event}", self, options)
75
+ when String
76
+ klass, meth = callback.split('#')
77
+ klass.constantize.new.send(meth, self, options)
78
+ else
79
+ raise ArgumentError, 'Unsupported callback notation'
80
+ end
51
81
  end
52
82
  end
53
83
  end
@@ -62,7 +92,7 @@ if defined? Sidekiq::Batch
62
92
  # :nocov:
63
93
  RSpec.configure do |config|
64
94
  config.before(:each) do |example|
65
- next if example.metadata[:stub_batches] == false
95
+ next unless example.metadata[:stub_batches] == true
66
96
 
67
97
  if mocked_with_mocha?
68
98
  Sidekiq::Batch.stubs(:new) { RSpec::Sidekiq::NullBatch.new }
@@ -1,12 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "set"
5
+
1
6
  module RSpec
2
7
  module Sidekiq
3
8
  class Configuration
4
- attr_accessor :clear_all_enqueued_jobs, :enable_terminal_colours, :warn_when_jobs_not_processed_by_sidekiq
9
+ attr_accessor :clear_all_enqueued_jobs,
10
+ :enable_terminal_colours,
11
+ :warn_when_jobs_not_processed_by_sidekiq
5
12
 
6
13
  def initialize
7
- @clear_all_enqueued_jobs = true
14
+ # Display settings defaults
8
15
  @enable_terminal_colours = true
16
+
17
+ # Functional settings defaults
18
+ @clear_all_enqueued_jobs = true
9
19
  @warn_when_jobs_not_processed_by_sidekiq = true
20
+ @silence_warnings = Set.new
21
+ end
22
+
23
+ def sidekiq_gte_7?
24
+ Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("7.0.0")
25
+ end
26
+
27
+ def sidekiq_gte_8?
28
+ Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
29
+ end
30
+
31
+ def silence_warning(symbol)
32
+ @silence_warnings << symbol
33
+ end
34
+
35
+ def warn_for?(symbol)
36
+ !@silence_warnings.include?(symbol)
10
37
  end
11
38
  end
12
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sidekiq
2
4
  module Worker
3
5
  module ClassMethods
@@ -8,15 +10,15 @@ module Sidekiq
8
10
 
9
11
  def default_retries_exhausted_message
10
12
  {
11
- 'queue' => get_sidekiq_options[:worker],
13
+ 'queue' => get_sidekiq_options['queue'],
12
14
  'class' => name,
13
15
  'args' => [],
14
- 'error_message' => 'An error occured'
16
+ 'error_message' => 'An error occurred'
15
17
  }
16
18
  end
17
19
 
18
20
  def default_retries_exhausted_exception
19
- StandardError.new('An error occured')
21
+ StandardError.new('An error occurred')
20
22
  end
21
23
  end
22
24
  end