rspec-sidekiq 5.0.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2bbe8c4c2097c67abf526af45ba5a3652fa83e898ccb82732f3639b83cc7d33
4
- data.tar.gz: ec606df39e62123fd43d595c412f9dd70edc5c93389395b6d6e58f53deccf32a
3
+ metadata.gz: 56ce84afbf67491fa24da1f527bced61c54afa3d62dbb50e6a09a89e8c0bc16c
4
+ data.tar.gz: 5b4a368bbb098d580713009fb9381d66326c37eced31c91eaf85b43158a85db5
5
5
  SHA512:
6
- metadata.gz: 7a601baf7161f039f0fb8b78a8ea3bed432b418431eae0b2e149365922f88d7f7c543450433175c4c16ba974ab91b8050ba1dc927619bbc2162a229f34de94ad
7
- data.tar.gz: 2964a340caee3c386a99eb8a69a79552782ff2e9cfcdacd9140d50232939138f95297b313620ec658f1e21fa044d946e3f030d8a8a212b3b1af2e1c89a175c6a
6
+ metadata.gz: 4ec18b3d6ceff7390807d7358259b44f236ca6d81b1b9175c069b6c4912041b914082042a5396c7cf1eb0b602a5448d9aacd9e23b3cee211ad7222c3ec3719af
7
+ data.tar.gz: 4f0232e7b482f7c565a5cca98be9b7143e51adbe6015329f9d052e393fd857fe97e2449fb53f9e3d209ff6319c54e23e9e2919e7003a44bfc00834692039eed0
data/CHANGES.md CHANGED
@@ -1,5 +1,11 @@
1
1
  Unreleased
2
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)
3
9
 
4
10
  5.0.0
5
11
  ---
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- **Welcome @wspurgin as new maintainer for `rspec-sidekiq`!**
2
-
3
1
  [![Gem Version](https://badge.fury.io/rb/rspec-sidekiq.svg)](https://badge.fury.io/rb/rspec-sidekiq)
4
2
  [![Github Actions CI][github_actions_badge]][github_actions]
5
3
 
@@ -81,6 +79,17 @@ expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(2).times
81
79
  expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(:twice)
82
80
  expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.at_most(:thrice)
83
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
+
84
93
  # Combine and chain them as desired
85
94
  expect { AwesomeJob.perform_at(specific_time, "Awesome!") }.to(
86
95
  enqueue_sidekiq_job(AwesomeJob)
@@ -133,6 +142,13 @@ expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(:twice)
133
142
  expect(AwesomeJob).to have_enqueued_sidekiq_job.at_most(:thrice)
134
143
  ```
135
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
+
136
152
  #### Testing scheduled jobs
137
153
 
138
154
  *Use chainable matchers `#at`, `#in` and `#immediately`*
@@ -204,6 +220,10 @@ it { is_expected.to be_processed_in :download }
204
220
 
205
221
  ### ```be_retryable```
206
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`
207
227
  ```ruby
208
228
  sidekiq_options retry: 5
209
229
  # test with...
@@ -235,14 +255,33 @@ it { is_expected.to save_backtrace false }
235
255
  ```
236
256
 
237
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
+
238
263
  *Describes when a job should be unique within its queue*
239
264
  ```ruby
240
- sidekiq_options unique: true
265
+ sidekiq_options unique_for: 1.hour
241
266
  # test with...
242
267
  expect(AwesomeJob).to be_unique
243
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) }
273
+ ```
274
+
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) }
244
282
  ```
245
283
 
284
+
246
285
  ### ```be_expired_in```
247
286
  *Describes when a job should expire*
248
287
  ```ruby
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rubygems"
2
4
  require "set"
3
5
 
@@ -22,6 +24,10 @@ module RSpec
22
24
  Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("7.0.0")
23
25
  end
24
26
 
27
+ def sidekiq_gte_8?
28
+ Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
29
+ end
30
+
25
31
  def silence_warning(symbol)
26
32
  @silence_warnings << symbol
27
33
  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
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec/core'
2
4
  require 'rspec/sidekiq/helpers/within_sidekiq_retries_exhausted_block'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -26,7 +28,7 @@ module RSpec
26
28
  # send to custom evaluator
27
29
  at_evaluator(value)
28
30
  else
29
- job.context.has_key?(key) && job.context[key] == value
31
+ job.context.has_key?(key) && RSpec::Support::FuzzyMatcher.values_match?(value, job.context[key])
30
32
  end
31
33
  end
32
34
  end
@@ -58,7 +60,11 @@ module RSpec
58
60
  private
59
61
 
60
62
  def active_job?
61
- job["class"] == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
63
+ if RSpec::Sidekiq.configuration.sidekiq_gte_8?
64
+ job["class"] == "Sidekiq::ActiveJob::Wrapper"
65
+ else
66
+ job["class"] == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
67
+ end
62
68
  end
63
69
 
64
70
  def deserialized_active_job_args
@@ -121,13 +127,13 @@ module RSpec
121
127
  def includes?(arguments, options, count)
122
128
  matching = jobs.filter { |job| matches?(job, arguments, options) }
123
129
 
124
- case count
125
- in [:exactly, n]
126
- matching.size == n
127
- in [:at_least, n]
128
- matching.size >= n
129
- in [:at_most, n]
130
- matching.size <= n
130
+ case count[0]
131
+ when :exactly
132
+ matching.size == count[1]
133
+ when :at_least
134
+ matching.size >= count[1]
135
+ when :at_most
136
+ matching.size <= count[1]
131
137
  else
132
138
  matching.size > 0
133
139
  end
@@ -243,6 +249,26 @@ module RSpec
243
249
  end
244
250
  alias :time :times
245
251
 
252
+ def with_context(**kwargs)
253
+ raise ArgumentError, "Must specify keyword arguments to with_context" if kwargs.empty?
254
+
255
+ # gather keys and compare against currently set expected_options
256
+ # Someone could have accidentally used with_context and other
257
+ # chainables with different expectations. Better to explicitly
258
+ # inform loudly of clashes than let them overwrite silently
259
+ normalized = normalize_arguments(kwargs)
260
+ already_set = normalized.keys & @expected_options.keys
261
+ if already_set.any?
262
+ prettied = already_set.map { |key| "'#{key}'" }
263
+ raise ArgumentError, "There are already expectations against #{prettied.join(",")}. Did you already call other context chainables like `on` or `at`?"
264
+ end
265
+
266
+ # We're good, no accidental overwrites of expectations
267
+ @expected_options.merge!(normalized)
268
+
269
+ self
270
+ end
271
+
246
272
  def set_expected_count(relativity, n)
247
273
  n =
248
274
  case n
@@ -275,13 +301,13 @@ module RSpec
275
301
  message << "but enqueued only jobs"
276
302
  if expected_arguments
277
303
  job_messages = actual_jobs.map do |job|
278
- base = " -JID:#{job.jid} with arguments:"
279
- base << "\n -#{formatted(job.args)}"
304
+ base = [" -JID:#{job.jid} with arguments:"]
305
+ base << " -#{formatted(job.args)}"
280
306
  if expected_options.any?
281
- base << "\n with context: #{formatted(job.context)}"
307
+ base << " with context: #{formatted(job.context)}"
282
308
  end
283
309
 
284
- base
310
+ base.join("\n")
285
311
  end
286
312
 
287
313
  message << job_messages.join("\n")
@@ -302,13 +328,13 @@ module RSpec
302
328
  end
303
329
 
304
330
  def count_message
305
- case expected_count
306
- in [:positive, _]
331
+ case expected_count[0]
332
+ when :positive
307
333
  "a"
308
- in [:exactly, n]
309
- n
310
- in [relativity, n]
311
- "#{relativity.to_s.gsub('_', ' ')} #{n}"
334
+ when :exactly
335
+ expected_count[1]
336
+ else
337
+ "#{expected_count[0].to_s.gsub('_', ' ')} #{expected_count[1]}"
312
338
  end
313
339
  end
314
340
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -25,6 +27,9 @@ module RSpec
25
27
  if !interval_matches? && @expected_interval
26
28
  "expected #{@klass} to be unique for #{@expected_interval} seconds, "\
27
29
  "but its interval was #{actual_interval} seconds"
30
+ elsif !expiration_matches?
31
+ "expected #{@klass} to be unique until #{@expected_expiration}, "\
32
+ "but its unique_until was #{actual_expiration || 'not specified'}"
28
33
  else
29
34
  "expected #{@klass} to be unique in the queue"
30
35
  end
@@ -33,7 +38,7 @@ module RSpec
33
38
  def matches?(job)
34
39
  @klass = job.is_a?(Class) ? job : job.class
35
40
  @actual = @klass.get_sidekiq_options[unique_key]
36
- !!(value_matches? && interval_matches?)
41
+ !!(value_matches? && interval_matches? && expiration_matches?)
37
42
  end
38
43
 
39
44
  def for(interval)
@@ -41,6 +46,11 @@ module RSpec
41
46
  self
42
47
  end
43
48
 
49
+ def until(expiration)
50
+ @expected_expiration = expiration
51
+ self
52
+ end
53
+
44
54
  def interval_specified?
45
55
  @expected_interval
46
56
  end
@@ -49,6 +59,10 @@ module RSpec
49
59
  !interval_specified? || actual_interval == @expected_interval
50
60
  end
51
61
 
62
+ def expiration_matches?
63
+ @expected_expiration.nil? || actual_expiration == @expected_expiration
64
+ end
65
+
52
66
  def failure_message_when_negated
53
67
  "expected #{@klass} to not be unique in the queue"
54
68
  end
@@ -59,6 +73,10 @@ module RSpec
59
73
  @klass.get_sidekiq_options['unique_job_expiration']
60
74
  end
61
75
 
76
+ def actual_expiration
77
+ fail 'until is not supported for SidekiqUniqueJobs'
78
+ end
79
+
62
80
  def value_matches?
63
81
  [true, :all].include?(@actual)
64
82
  end
@@ -73,6 +91,10 @@ module RSpec
73
91
  @actual
74
92
  end
75
93
 
94
+ def actual_expiration
95
+ @klass.get_sidekiq_options['unique_until']
96
+ end
97
+
76
98
  def value_matches?
77
99
  @actual && @actual > 0
78
100
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -72,6 +74,9 @@ module RSpec
72
74
  # expect { AwesomeJob.perform_async }.to enqueue_sidekiq_job.immediately
73
75
  # expect { AwesomeJob.perform_at(1.hour.ago) }.to enqueue_sidekiq_job.immediately
74
76
  #
77
+ # # With specific context
78
+ # expect { AwesomeJob.set(trace_id: "something").perform_async }.to enqueue_sidekiq_job.with_context(trace_id: anything)
79
+ #
75
80
  # ## Composable
76
81
  #
77
82
  # expect do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  module Matchers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rspec/core"
2
4
  require "rspec/matchers"
3
5
  require "rspec/mocks/argument_list_matcher"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
5
  class << self
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RSpec
2
4
  module Sidekiq
3
- VERSION = "5.0.0"
5
+ VERSION = "5.1.0"
4
6
  end
5
7
  end
data/lib/rspec-sidekiq.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  require 'sidekiq'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aidan Coyle
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-05-15 00:00:00.000000000 Z
13
+ date: 2025-03-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec-core
@@ -63,7 +63,7 @@ dependencies:
63
63
  version: '5'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '8'
66
+ version: '9'
67
67
  type: :runtime
68
68
  prerelease: false
69
69
  version_requirements: !ruby/object:Gem::Requirement
@@ -73,7 +73,7 @@ dependencies:
73
73
  version: '5'
74
74
  - - "<"
75
75
  - !ruby/object:Gem::Version
76
- version: '8'
76
+ version: '9'
77
77
  - !ruby/object:Gem::Dependency
78
78
  name: pry
79
79
  requirement: !ruby/object:Gem::Requirement
@@ -228,6 +228,20 @@ dependencies:
228
228
  - - ">="
229
229
  - !ruby/object:Gem::Version
230
230
  version: '0'
231
+ - !ruby/object:Gem::Dependency
232
+ name: railties
233
+ requirement: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ type: :development
239
+ prerelease: false
240
+ version_requirements: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: '0'
231
245
  description: Simple testing of Sidekiq jobs via a collection of matchers and helpers
232
246
  email:
233
247
  executables: []
@@ -257,7 +271,8 @@ files:
257
271
  homepage: http://github.com/wspurgin/rspec-sidekiq
258
272
  licenses:
259
273
  - MIT
260
- metadata: {}
274
+ metadata:
275
+ changelog_uri: https://github.com/wspurgin/rspec-sidekiq/blob/main/CHANGES.md
261
276
  post_install_message:
262
277
  rdoc_options: []
263
278
  require_paths:
@@ -273,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
273
288
  - !ruby/object:Gem::Version
274
289
  version: '0'
275
290
  requirements: []
276
- rubygems_version: 3.3.26
291
+ rubygems_version: 3.5.22
277
292
  signing_key:
278
293
  specification_version: 4
279
294
  summary: RSpec for Sidekiq