rspec-sidekiq 5.2.0 → 5.3.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/CHANGES.md +9 -0
- data/README.md +185 -34
- data/lib/rspec/sidekiq/batch.rb +4 -0
- data/lib/rspec/sidekiq/helpers/within_sidekiq_retries_exhausted_block.rb +4 -0
- data/lib/rspec/sidekiq/matchers/base.rb +86 -49
- data/lib/rspec/sidekiq/matchers/have_job.rb +280 -0
- data/lib/rspec/sidekiq/matchers/have_job_options.rb +119 -0
- data/lib/rspec/sidekiq/matchers/save_backtrace.rb +2 -2
- data/lib/rspec/sidekiq/matchers.rb +2 -0
- data/lib/rspec/sidekiq/named_queues/job_store.rb +67 -0
- data/lib/rspec/sidekiq/named_queues/null_dead_set.rb +17 -0
- data/lib/rspec/sidekiq/named_queues/null_retry_set.rb +17 -0
- data/lib/rspec/sidekiq/named_queues/null_scheduled_set.rb +17 -0
- data/lib/rspec/sidekiq/named_queues/null_set.rb +72 -0
- data/lib/rspec/sidekiq/named_queues.rb +55 -0
- data/lib/rspec/sidekiq/version.rb +1 -1
- data/lib/rspec-sidekiq.rb +6 -1
- metadata +20 -138
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 829c5fcd191be48f662173034da4820f8dd5d405c85a865cc272048b1cee3b95
|
|
4
|
+
data.tar.gz: 07d430fa03b32c89f6d859c4fc44034ecc517d4eeec623d552854d5deaa142f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2a20b0ba6848989deea4759a0dfaec982c05f9543b60d42343d4b7d69e22e38f175596e53a00f911ad94732a9b6fca7904703f60b35bd014b00180d905b266a
|
|
7
|
+
data.tar.gz: bc0b993e2fe9350b2f72c8b5e7711b2c642a8098566edebfef5dd00b2f3a5aadef6a014de61831ff05a255376824e441076e29ff1ca0491f56e5fce98d52f57b
|
data/CHANGES.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
Unreleased
|
|
2
2
|
---
|
|
3
3
|
|
|
4
|
+
5.3.0
|
|
5
|
+
---
|
|
6
|
+
* Add `have_job` matcher and named queue stubs (`stub_named_queues`) (#279)
|
|
7
|
+
* Add `have_job_option` and `have_job_options` matchers for job option assertions (#278)
|
|
8
|
+
* Add `respond_to_missing?` to `NullBatch` for proper method reflection (#275)
|
|
9
|
+
* Support RSpec matchers with `at` and `in` evaluators (#272)
|
|
10
|
+
* Allow `on` to accept either a `String` or `Symbol` queue name (#273)
|
|
11
|
+
* Avoid `require "sidekiq/testing"` deprecation on Sidekiq 8.1.1+ (#280)
|
|
12
|
+
|
|
4
13
|
5.2.0
|
|
5
14
|
---
|
|
6
15
|
* Add `never` matcher for aliasing `exactly(0)` (#256)
|
data/README.md
CHANGED
|
@@ -3,21 +3,26 @@
|
|
|
3
3
|
|
|
4
4
|
Simple testing of Sidekiq jobs via a collection of matchers and helpers.
|
|
5
5
|
|
|
6
|
-
[Jump to Matchers
|
|
6
|
+
[Jump to Matchers »](#matchers) | [Jump to Helpers »](#helpers)
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
|
+
|
|
9
10
|
```ruby
|
|
10
11
|
# Gemfile
|
|
11
12
|
group :test do
|
|
12
13
|
gem 'rspec-sidekiq'
|
|
13
14
|
end
|
|
14
15
|
```
|
|
15
|
-
rspec-sidekiq requires ```sidekiq/testing``` by default so there is no need to include the line ```require "sidekiq/testing"``` inside your ```spec_helper.rb```.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
rspec-sidekiq requires `sidekiq/testing` by default so there is no need to include the line `require "sidekiq/testing"` inside your `spec_helper.rb`.
|
|
18
|
+
|
|
19
|
+
> [!IMPORTANT]
|
|
20
|
+
> This has the effect of not pushing enqueued jobs to Redis but to a `job` array to enable testing ([see the FAQ & Troubleshooting Wiki page][rspec_sidekiq_wiki_faq_&_troubleshooting]). Thus, only include `gem "rspec-sidekiq"` in environments where this behaviour is required, such as the `test` group.*
|
|
18
21
|
|
|
19
22
|
## Configuration
|
|
20
|
-
|
|
23
|
+
|
|
24
|
+
If you wish to modify the default behaviour, add the following to your `spec_helper.rb` file
|
|
25
|
+
|
|
21
26
|
```ruby
|
|
22
27
|
RSpec::Sidekiq.configure do |config|
|
|
23
28
|
# Clears all job queues before each example
|
|
@@ -32,16 +37,20 @@ end
|
|
|
32
37
|
```
|
|
33
38
|
|
|
34
39
|
## Matchers
|
|
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
40
|
|
|
44
|
-
|
|
41
|
+
* [`enqueue_sidekiq_job`](#enqueue_sidekiq_job)
|
|
42
|
+
* [`have_enqueued_sidekiq_job`](#have_enqueued_sidekiq_job)
|
|
43
|
+
* [`have_job`](#have_job)
|
|
44
|
+
* [`have_job_option`](#have_job_option)
|
|
45
|
+
* [`have_job_options`](#have_job_options)
|
|
46
|
+
* [`be_processed_in`](#be_processed_in)
|
|
47
|
+
* [`be_retryable`](#be_retryable)
|
|
48
|
+
* [`save_backtrace`](#save_backtrace)
|
|
49
|
+
* [`be_unique`](#be_unique)
|
|
50
|
+
* [`be_expired_in`](#be_expired_in)
|
|
51
|
+
* [`be_delayed` (_deprecated_)](#be_delayed)
|
|
52
|
+
|
|
53
|
+
### `enqueue_sidekiq_job`
|
|
45
54
|
|
|
46
55
|
*Describes that the block should enqueue a job*. Optionally specify the
|
|
47
56
|
specific job class, arguments, timing, and other context
|
|
@@ -108,7 +117,7 @@ expect do
|
|
|
108
117
|
end.to enqueue_sidekiq_job(AwesomeJob).and enqueue_sidekiq_job(OtherJob)
|
|
109
118
|
```
|
|
110
119
|
|
|
111
|
-
###
|
|
120
|
+
### `have_enqueued_sidekiq_job`
|
|
112
121
|
|
|
113
122
|
Describes that there should be an enqueued job (with the specified arguments):
|
|
114
123
|
|
|
@@ -120,6 +129,7 @@ expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true)
|
|
|
120
129
|
```
|
|
121
130
|
|
|
122
131
|
You can use the built-in RSpec args matchers too:
|
|
132
|
+
|
|
123
133
|
```ruby
|
|
124
134
|
AwesomeJob.perform_async({"something" => "Awesome", "extra" => "stuff"})
|
|
125
135
|
|
|
@@ -146,6 +156,7 @@ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(:thrice)
|
|
|
146
156
|
```
|
|
147
157
|
|
|
148
158
|
Likewise, specify what should be in the context:
|
|
159
|
+
|
|
149
160
|
```ruby
|
|
150
161
|
AwesomeJob.set(trace_id: "something").perform_async
|
|
151
162
|
|
|
@@ -162,6 +173,7 @@ AwesomeJob.perform_at time, 'Awesome', true
|
|
|
162
173
|
# test with...
|
|
163
174
|
expect(AwesomeJob).to have_enqueued_sidekiq_job('Awesome', true).at(time)
|
|
164
175
|
```
|
|
176
|
+
|
|
165
177
|
```ruby
|
|
166
178
|
AwesomeJob.perform_in 5.minutes, 'Awesome', true
|
|
167
179
|
# test with...
|
|
@@ -212,8 +224,88 @@ expect(Sidekiq::Worker).to have_enqueued_sidekiq_job(
|
|
|
212
224
|
)
|
|
213
225
|
```
|
|
214
226
|
|
|
215
|
-
###
|
|
227
|
+
### `have_job`
|
|
228
|
+
|
|
229
|
+
Describes that a Sidekiq set (ScheduledSet, RetrySet, DeadSet) should contain a job. Typically used together with [`stub_named_queues`](#stub_named_queues).
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
# Match any job in the set
|
|
233
|
+
expect(Sidekiq::ScheduledSet.new).to have_job
|
|
234
|
+
|
|
235
|
+
# Match a specific job class
|
|
236
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob)
|
|
237
|
+
|
|
238
|
+
# With specific arguments
|
|
239
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).with('arg')
|
|
240
|
+
|
|
241
|
+
# A specific number of times
|
|
242
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).once
|
|
243
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).twice
|
|
244
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).exactly(3).times
|
|
245
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).at_least(1).time
|
|
246
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).at_most(2).times
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### Testing retry jobs
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
expect(Sidekiq::RetrySet.new)
|
|
253
|
+
.to have_job(AwesomeJob)
|
|
254
|
+
.with('arg')
|
|
255
|
+
.with_error('something went wrong')
|
|
256
|
+
.with_error_class(RuntimeError)
|
|
257
|
+
.with_retry_count(2)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Testing dead jobs
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
expect(Sidekiq::DeadSet.new)
|
|
264
|
+
.to have_job(AwesomeJob)
|
|
265
|
+
.with('arg')
|
|
266
|
+
.died_within(1.hour)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Scanning with a pattern
|
|
270
|
+
|
|
271
|
+
Use `.scanning(pattern)` to filter by a glob-style pattern matched against the job's JSON representation:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).scanning("*some_trace_id*")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### `have_job_option`
|
|
278
|
+
|
|
279
|
+
Describes a single Sidekiq option set on a job class:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
class AwesomeJob
|
|
283
|
+
include Sidekiq::Job
|
|
284
|
+
sidekiq_options retry: 5, queue: 'critical', dead: false
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
expect(AwesomeJob).to have_job_option(:retry, 5)
|
|
288
|
+
expect(AwesomeJob).to have_job_option(:queue, 'critical')
|
|
289
|
+
expect(AwesomeJob).to have_job_option(:dead, false)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `have_job_options`
|
|
293
|
+
|
|
294
|
+
Describes multiple Sidekiq options set on a job class:
|
|
295
|
+
|
|
296
|
+
```ruby
|
|
297
|
+
class AwesomeJob
|
|
298
|
+
include Sidekiq::Job
|
|
299
|
+
sidekiq_options retry: 5, queue: 'critical', backtrace: true
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
expect(AwesomeJob).to have_job_options(retry: 5, queue: 'critical', backtrace: true)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `be_processed_in`
|
|
306
|
+
|
|
216
307
|
*Describes the queue that a job should be processed in*
|
|
308
|
+
|
|
217
309
|
```ruby
|
|
218
310
|
sidekiq_options queue: :download
|
|
219
311
|
# test with...
|
|
@@ -221,12 +313,14 @@ expect(AwesomeJob).to be_processed_in :download # or
|
|
|
221
313
|
it { is_expected.to be_processed_in :download }
|
|
222
314
|
```
|
|
223
315
|
|
|
224
|
-
###
|
|
316
|
+
### `be_retryable`
|
|
317
|
+
|
|
225
318
|
*Describes if a job should retry when there is a failure in its execution*
|
|
226
319
|
|
|
227
320
|
Note: this only tests against the `retry` option in the job's Sidekiq options.
|
|
228
321
|
To test an enqueued job's retry, i.e. `AwesomeJob.set(retry: 5)`, use
|
|
229
322
|
`with_context`
|
|
323
|
+
|
|
230
324
|
```ruby
|
|
231
325
|
sidekiq_options retry: 5
|
|
232
326
|
# test with...
|
|
@@ -240,8 +334,10 @@ expect(AwesomeJob).to be_retryable false # or
|
|
|
240
334
|
it { is_expected.to be_retryable false }
|
|
241
335
|
```
|
|
242
336
|
|
|
243
|
-
###
|
|
337
|
+
### `save_backtrace`
|
|
338
|
+
|
|
244
339
|
*Describes if a job should save the error backtrace when there is a failure in its execution*
|
|
340
|
+
|
|
245
341
|
```ruby
|
|
246
342
|
sidekiq_options backtrace: 5
|
|
247
343
|
# test with...
|
|
@@ -257,13 +353,15 @@ it { is_expected.to_not save_backtrace } # or
|
|
|
257
353
|
it { is_expected.to save_backtrace false }
|
|
258
354
|
```
|
|
259
355
|
|
|
260
|
-
###
|
|
356
|
+
### `be_unique`
|
|
261
357
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
358
|
+
> [!CAUTION]
|
|
359
|
+
> This is intended to for Sidekiq Enterprise unique job implementation.
|
|
360
|
+
> There is _limited_ support for Sidekiq Unique Jobs, but compatibility is not
|
|
361
|
+
> guaranteed.
|
|
265
362
|
|
|
266
363
|
*Describes when a job should be unique within its queue*
|
|
364
|
+
|
|
267
365
|
```ruby
|
|
268
366
|
sidekiq_options unique_for: 1.hour
|
|
269
367
|
# test with...
|
|
@@ -277,16 +375,18 @@ it { is_expected.to be_unique.for(1.hour) }
|
|
|
277
375
|
|
|
278
376
|
#### `until` sub-matcher
|
|
279
377
|
|
|
280
|
-
|
|
378
|
+
> [!CAUTION]
|
|
379
|
+
> This sub-matcher only works for Sidekiq Enterprise
|
|
281
380
|
|
|
282
381
|
```ruby
|
|
283
382
|
sidekiq_options unique_for: 1.hour, unique_until: :start
|
|
284
383
|
it { is_expected.to be_unique.until(:start) }
|
|
285
384
|
```
|
|
286
385
|
|
|
386
|
+
### `be_expired_in`
|
|
287
387
|
|
|
288
|
-
### ```be_expired_in```
|
|
289
388
|
*Describes when a job should expire*
|
|
389
|
+
|
|
290
390
|
```ruby
|
|
291
391
|
sidekiq_options expires_in: 1.hour
|
|
292
392
|
# test with...
|
|
@@ -294,13 +394,14 @@ it { is_expected.to be_expired_in 1.hour }
|
|
|
294
394
|
it { is_expected.to_not be_expired_in 2.hours }
|
|
295
395
|
```
|
|
296
396
|
|
|
297
|
-
###
|
|
397
|
+
### `be_delayed`
|
|
298
398
|
|
|
299
399
|
**This matcher is deprecated**. Use of it with Sidekiq 7+ will raise an error.
|
|
300
400
|
Sidekiq 7 [dropped Delayed
|
|
301
401
|
Extensions](https://github.com/sidekiq/sidekiq/issues/5076).
|
|
302
402
|
|
|
303
403
|
*Describes a method that should be invoked asynchronously (See [Sidekiq Delayed Extensions][sidekiq_wiki_delayed_extensions])*
|
|
404
|
+
|
|
304
405
|
```ruby
|
|
305
406
|
Object.delay.is_nil? # delay
|
|
306
407
|
expect(Object.method :is_nil?).to be_delayed
|
|
@@ -323,6 +424,7 @@ expect(MyMailer.instance_method :some_mail).to be_delayed
|
|
|
323
424
|
```
|
|
324
425
|
|
|
325
426
|
## Example matcher usage
|
|
427
|
+
|
|
326
428
|
```ruby
|
|
327
429
|
require 'spec_helper'
|
|
328
430
|
|
|
@@ -341,9 +443,56 @@ end
|
|
|
341
443
|
```
|
|
342
444
|
|
|
343
445
|
## Helpers
|
|
446
|
+
|
|
447
|
+
* [`stub_named_queues`](#stub_named_queues)
|
|
344
448
|
* [Batches (Sidekiq Pro) _experimental_](#batches)
|
|
345
449
|
* [`within_sidekiq_retries_exhausted_block`](#within_sidekiq_retries_exhausted_block)
|
|
346
450
|
|
|
451
|
+
### `stub_named_queues`
|
|
452
|
+
|
|
453
|
+
If you need to test jobs in Sidekiq's named sets (ScheduledSet, RetrySet, DeadSet) without
|
|
454
|
+
a Redis instance, opt-in with `stub_named_queues: true`. This replaces those sets with
|
|
455
|
+
in-memory implementations backed by a `JobStore`.
|
|
456
|
+
|
|
457
|
+
```ruby
|
|
458
|
+
RSpec.describe "Scheduled jobs", stub_named_queues: true do
|
|
459
|
+
it "schedules a job" do
|
|
460
|
+
AwesomeJob.perform_at(1.hour.from_now, 'arg')
|
|
461
|
+
|
|
462
|
+
expect(Sidekiq::ScheduledSet.new).to have_job(AwesomeJob).with('arg')
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
it "tracks retry jobs via the job store" do
|
|
466
|
+
store = RSpec::Sidekiq::NamedQueues.job_store
|
|
467
|
+
store.add_retry(
|
|
468
|
+
"class" => "AwesomeJob",
|
|
469
|
+
"args" => ["arg"],
|
|
470
|
+
"error_message" => "boom",
|
|
471
|
+
"error_class" => "RuntimeError",
|
|
472
|
+
"retry_count" => 1
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
expect(Sidekiq::RetrySet.new)
|
|
476
|
+
.to have_job(AwesomeJob)
|
|
477
|
+
.with_error('boom')
|
|
478
|
+
.with_retry_count(1)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it "tracks dead jobs via the job store" do
|
|
482
|
+
store = RSpec::Sidekiq::NamedQueues.job_store
|
|
483
|
+
store.add_dead(
|
|
484
|
+
"class" => "AwesomeJob",
|
|
485
|
+
"args" => ["arg"],
|
|
486
|
+
"failed_at" => Time.now.to_f
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
expect(Sidekiq::DeadSet.new)
|
|
490
|
+
.to have_job(AwesomeJob)
|
|
491
|
+
.died_within(1.minute)
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
```
|
|
495
|
+
|
|
347
496
|
### Batches
|
|
348
497
|
|
|
349
498
|
If you are using Sidekiq Batches ([Sidekiq Pro feature][sidekiq_wiki_batches]),
|
|
@@ -351,10 +500,10 @@ You can *opt-in* with `stub_batches` to make `rspec-sidekiq` mock the
|
|
|
351
500
|
implementation (using a NullObject pattern). This enables testing without a
|
|
352
501
|
Redis instance. Mocha and RSpec stubbing is supported here.
|
|
353
502
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
503
|
+
> [!CAUTION]
|
|
504
|
+
> Opting-in to this feature, while allowing you to test without
|
|
505
|
+
> having Redis, _does not_ provide the exact API that `Sidekiq::Batch` does. As
|
|
506
|
+
> such it can cause surprises.
|
|
358
507
|
|
|
359
508
|
```ruby
|
|
360
509
|
RSpec.describe "Using mocked batches", stub_batches: true do
|
|
@@ -375,6 +524,7 @@ end
|
|
|
375
524
|
```
|
|
376
525
|
|
|
377
526
|
### within_sidekiq_retries_exhausted_block
|
|
527
|
+
|
|
378
528
|
```ruby
|
|
379
529
|
sidekiq_retries_exhausted do |msg|
|
|
380
530
|
bar('hello')
|
|
@@ -386,11 +536,13 @@ FooClass.within_sidekiq_retries_exhausted_block {
|
|
|
386
536
|
```
|
|
387
537
|
|
|
388
538
|
## Testing
|
|
539
|
+
|
|
389
540
|
```
|
|
390
541
|
bundle exec rspec
|
|
391
542
|
```
|
|
392
543
|
|
|
393
544
|
## Maintainers
|
|
545
|
+
|
|
394
546
|
* [@wspurgin]
|
|
395
547
|
* [@ydah]
|
|
396
548
|
|
|
@@ -400,18 +552,17 @@ bundle exec rspec
|
|
|
400
552
|
* [@philostler]
|
|
401
553
|
|
|
402
554
|
## Contribute
|
|
555
|
+
|
|
403
556
|
Please do! If there's a feature missing that you'd love to see then get in on the action!
|
|
404
557
|
|
|
405
558
|
Issues/Pull Requests/Comments all welcome...
|
|
406
559
|
|
|
560
|
+
[@packrat386]: https://github.com/packrat386
|
|
561
|
+
[@philostler]: https://github.com/philostler
|
|
562
|
+
[@wspurgin]: https://github.com/wspurgin
|
|
563
|
+
[@ydah]: https://github.com/ydah
|
|
407
564
|
[github_actions]: https://github.com/wspurgin/rspec-sidekiq/actions
|
|
408
565
|
[github_actions_badge]: https://github.com/wspurgin/rspec-sidekiq/actions/workflows/main.yml/badge.svg
|
|
409
|
-
|
|
410
566
|
[rspec_sidekiq_wiki_faq_&_troubleshooting]: https://github.com/wspurgin/rspec-sidekiq/wiki/FAQ-&-Troubleshooting
|
|
411
567
|
[sidekiq_wiki_batches]: https://github.com/sidekiq/sidekiq/wiki/Batches
|
|
412
568
|
[sidekiq_wiki_delayed_extensions]: https://github.com/sidekiq/sidekiq/wiki/Delayed-Extensions
|
|
413
|
-
|
|
414
|
-
[@wspurgin]: https://github.com/wspurgin
|
|
415
|
-
[@ydah]: https://github.com/ydah
|
|
416
|
-
[@packrat386]: https://github.com/packrat386
|
|
417
|
-
[@philostler]: https://github.com/philostler
|
data/lib/rspec/sidekiq/batch.rb
CHANGED
|
@@ -4,6 +4,10 @@ module Sidekiq
|
|
|
4
4
|
module Worker
|
|
5
5
|
module ClassMethods
|
|
6
6
|
def within_sidekiq_retries_exhausted_block(user_msg = {}, exception = default_retries_exhausted_exception, &block)
|
|
7
|
+
unless sidekiq_retries_exhausted_block
|
|
8
|
+
raise ArgumentError, 'Define `sidekiq_retries_exhausted` before calling `within_sidekiq_retries_exhausted_block`'
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
block.call
|
|
8
12
|
sidekiq_retries_exhausted_block.call(default_retries_exhausted_message.merge(user_msg), exception)
|
|
9
13
|
end
|
|
@@ -3,6 +3,54 @@
|
|
|
3
3
|
module RSpec
|
|
4
4
|
module Sidekiq
|
|
5
5
|
module Matchers
|
|
6
|
+
# @api private
|
|
7
|
+
module CountExpectation
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def set_expected_count(relativity, n)
|
|
11
|
+
n =
|
|
12
|
+
case n
|
|
13
|
+
when Integer then n
|
|
14
|
+
when :once then 1
|
|
15
|
+
when :twice then 2
|
|
16
|
+
when :thrice then 3
|
|
17
|
+
else raise ArgumentError, "Unsupported #{n} in '#{relativity} #{n}'. Use either an Integer, :once, :twice, or :thrice."
|
|
18
|
+
end
|
|
19
|
+
@expected_count = [relativity, n]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def count_message
|
|
23
|
+
case expected_count[0]
|
|
24
|
+
when :positive
|
|
25
|
+
"a"
|
|
26
|
+
when :exactly
|
|
27
|
+
expected_count[1]
|
|
28
|
+
else
|
|
29
|
+
"#{expected_count[0].to_s.gsub('_', ' ')} #{expected_count[1]}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
module ArgumentNormalization
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def normalize_arguments(args)
|
|
39
|
+
case args
|
|
40
|
+
when Array
|
|
41
|
+
args.map { |x| normalize_arguments(x) }
|
|
42
|
+
when Hash
|
|
43
|
+
args.each_with_object({}) do |(key, value), hash|
|
|
44
|
+
hash[key.to_s] = normalize_arguments(value)
|
|
45
|
+
end
|
|
46
|
+
when Symbol
|
|
47
|
+
args.to_s
|
|
48
|
+
else
|
|
49
|
+
args
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
6
54
|
# @api private
|
|
7
55
|
class JobOptionParser
|
|
8
56
|
attr_reader :job
|
|
@@ -19,6 +67,8 @@ module RSpec
|
|
|
19
67
|
|
|
20
68
|
def at_evaluator(value)
|
|
21
69
|
return value.nil? if job["at"].to_s.empty?
|
|
70
|
+
return value.matches?(Time.at(job["at"])) if value.respond_to?(:matches?)
|
|
71
|
+
|
|
22
72
|
value == Time.at(job["at"]).to_i
|
|
23
73
|
end
|
|
24
74
|
|
|
@@ -50,17 +100,19 @@ module RSpec
|
|
|
50
100
|
end
|
|
51
101
|
|
|
52
102
|
def unwrapped_arguments
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return deserialized_active_job_args if active_job?
|
|
103
|
+
@unwrapped_arguments ||= begin
|
|
104
|
+
args = job["args"]
|
|
56
105
|
|
|
57
|
-
|
|
106
|
+
active_job? ? deserialized_active_job_args : args
|
|
107
|
+
end
|
|
58
108
|
end
|
|
59
109
|
|
|
60
110
|
private
|
|
61
111
|
|
|
62
112
|
def active_job?
|
|
63
|
-
if
|
|
113
|
+
return @active_job if defined?(@active_job)
|
|
114
|
+
|
|
115
|
+
@active_job = if RSpec::Sidekiq.configuration.sidekiq_gte_8?
|
|
64
116
|
job["class"] == "Sidekiq::ActiveJob::Wrapper"
|
|
65
117
|
else
|
|
66
118
|
job["class"] == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
|
|
@@ -108,12 +160,30 @@ module RSpec
|
|
|
108
160
|
@context ||= job.except("args")
|
|
109
161
|
end
|
|
110
162
|
|
|
163
|
+
def matches_arguments?(expected_args)
|
|
164
|
+
job_arguments.matches?(expected_args)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def matches_options?(expected_options)
|
|
168
|
+
job_option_parser.matches?(expected_options)
|
|
169
|
+
end
|
|
170
|
+
|
|
111
171
|
def ==(other)
|
|
112
172
|
super(other) unless other.is_a?(EnqueuedJob)
|
|
113
173
|
|
|
114
174
|
jid == other.jid
|
|
115
175
|
end
|
|
116
176
|
alias_method :eql?, :==
|
|
177
|
+
|
|
178
|
+
private
|
|
179
|
+
|
|
180
|
+
def job_arguments
|
|
181
|
+
@job_arguments ||= JobArguments.new(job)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def job_option_parser
|
|
185
|
+
@job_option_parser ||= JobOptionParser.new(self)
|
|
186
|
+
end
|
|
117
187
|
end
|
|
118
188
|
|
|
119
189
|
class EnqueuedJobs
|
|
@@ -159,15 +229,11 @@ module RSpec
|
|
|
159
229
|
end
|
|
160
230
|
|
|
161
231
|
def arguments_matches?(job, arguments)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
job_arguments.matches?(arguments)
|
|
232
|
+
job.matches_arguments?(arguments)
|
|
165
233
|
end
|
|
166
234
|
|
|
167
235
|
def options_matches?(job, options)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
parser.matches?(options)
|
|
236
|
+
job.matches_options?(options)
|
|
171
237
|
end
|
|
172
238
|
|
|
173
239
|
def unwrap_jobs(jobs)
|
|
@@ -180,6 +246,8 @@ module RSpec
|
|
|
180
246
|
class Base
|
|
181
247
|
include RSpec::Mocks::ArgumentMatchers
|
|
182
248
|
include RSpec::Matchers::Composable
|
|
249
|
+
include CountExpectation
|
|
250
|
+
include ArgumentNormalization
|
|
183
251
|
|
|
184
252
|
attr_reader :expected_arguments, :expected_options, :klass, :actual_jobs, :expected_count
|
|
185
253
|
|
|
@@ -195,12 +263,12 @@ module RSpec
|
|
|
195
263
|
end
|
|
196
264
|
|
|
197
265
|
def at(timestamp)
|
|
198
|
-
@expected_options["at"] = timestamp.to_time.to_i
|
|
266
|
+
@expected_options["at"] = matcher?(timestamp) ? timestamp : timestamp.to_time.to_i
|
|
199
267
|
self
|
|
200
268
|
end
|
|
201
269
|
|
|
202
270
|
def in(interval)
|
|
203
|
-
@expected_options["at"] = (Time.now.to_f + interval.to_f).to_i
|
|
271
|
+
@expected_options["at"] = matcher?(interval) ? interval : (Time.now.to_f + interval.to_f).to_i
|
|
204
272
|
self
|
|
205
273
|
end
|
|
206
274
|
|
|
@@ -210,7 +278,7 @@ module RSpec
|
|
|
210
278
|
end
|
|
211
279
|
|
|
212
280
|
def on(queue)
|
|
213
|
-
@expected_options["queue"] = queue
|
|
281
|
+
@expected_options["queue"] = normalize_arguments(queue)
|
|
214
282
|
self
|
|
215
283
|
end
|
|
216
284
|
|
|
@@ -274,18 +342,6 @@ module RSpec
|
|
|
274
342
|
self
|
|
275
343
|
end
|
|
276
344
|
|
|
277
|
-
def set_expected_count(relativity, n)
|
|
278
|
-
n =
|
|
279
|
-
case n
|
|
280
|
-
when Integer then n
|
|
281
|
-
when :once then 1
|
|
282
|
-
when :twice then 2
|
|
283
|
-
when :thrice then 3
|
|
284
|
-
else raise ArgumentError, "Unsupported #{n} in '#{relativity} #{n}'. Use either an Integer, :once, :twice, or :thrice."
|
|
285
|
-
end
|
|
286
|
-
@expected_count = [relativity, n]
|
|
287
|
-
end
|
|
288
|
-
|
|
289
345
|
def description
|
|
290
346
|
"#{common_message} with arguments #{expected_arguments}"
|
|
291
347
|
end
|
|
@@ -332,17 +388,6 @@ module RSpec
|
|
|
332
388
|
raise NotImplementedError
|
|
333
389
|
end
|
|
334
390
|
|
|
335
|
-
def count_message
|
|
336
|
-
case expected_count[0]
|
|
337
|
-
when :positive
|
|
338
|
-
"a"
|
|
339
|
-
when :exactly
|
|
340
|
-
expected_count[1]
|
|
341
|
-
else
|
|
342
|
-
"#{expected_count[0].to_s.gsub('_', ' ')} #{expected_count[1]}"
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
|
|
346
391
|
def failure_message_when_negated
|
|
347
392
|
message = ["expected not to #{common_message} but enqueued #{actual_jobs.count}"]
|
|
348
393
|
message << " arguments: #{expected_arguments}" if expected_arguments.any?
|
|
@@ -354,18 +399,10 @@ module RSpec
|
|
|
354
399
|
RSpec::Support::ObjectFormatter.format(thing)
|
|
355
400
|
end
|
|
356
401
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
args.each_with_object({}) do |(key, value), hash|
|
|
362
|
-
hash[key.to_s] = normalize_arguments(value)
|
|
363
|
-
end
|
|
364
|
-
elsif args.is_a?(Symbol)
|
|
365
|
-
args.to_s
|
|
366
|
-
else
|
|
367
|
-
args
|
|
368
|
-
end
|
|
402
|
+
private
|
|
403
|
+
|
|
404
|
+
def matcher?(object)
|
|
405
|
+
object.respond_to?(:matches?)
|
|
369
406
|
end
|
|
370
407
|
end
|
|
371
408
|
end
|