Skip to content

Commit f6b2620

Browse files
committed
Fixup have_enqueued_job matcher on job retries
Previously we were checking only job counts, so if one job was performed and one job was added - matcher failed. Check by unique id (`#hash`) instead. We cannot use `job['job_id']` here, because job retains `job_id` on retry.
1 parent 620a869 commit f6b2620

File tree

4 files changed

+46
-6
lines changed

4 files changed

+46
-6
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
- 'main'
66
- '*-maintenance'
77
- '*-dev'
8+
- '*'
89
pull_request:
910
branches:
1011
- '*'

lib/rspec/rails/matchers/active_job.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,25 @@ def initialize(job)
230230
def matches?(proc)
231231
raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc
232232

233-
original_enqueued_jobs_count = queue_adapter.enqueued_jobs.count
233+
original_enqueued_jobs_hashes = queue_adapter.enqueued_jobs.map(&:hash)
234234
proc.call
235-
in_block_jobs = queue_adapter.enqueued_jobs.drop(original_enqueued_jobs_count)
236235

237-
check(in_block_jobs)
236+
in_block_jobs = queue_adapter.enqueued_jobs.each_with_object({}) do |job, memo|
237+
memo[job.hash] ||= {job: job, count: 0}
238+
memo[job.hash][:count] += 1
239+
end
240+
241+
original_enqueued_jobs_hashes.each do |job_hash|
242+
in_block_jobs[job_hash][:count] -= 1 if in_block_jobs.key?(job_hash)
243+
end
244+
245+
in_block_jobs = in_block_jobs.flat_map do |hash, job_and_count|
246+
count, job = job_and_count.values_at(:count, :job)
247+
248+
Array.new(count, job) if count.positive?
249+
end
250+
251+
check(in_block_jobs.compact)
238252
end
239253

240254
def does_not_match?(proc)

rspec-rails.gemspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ Gem::Specification.new do |s|
4646
# get released.
4747
%w[core expectations mocks support].each do |name|
4848
if ENV['RSPEC_CI']
49-
s.add_runtime_dependency "rspec-#{name}", ENV.fetch('RSPEC_VERSION', '3.11.0.pre')
49+
s.add_runtime_dependency "rspec-#{name}", ENV.fetch('RSPEC_VERSION', '3.12.0.pre')
5050
elsif RSpec::Rails::Version::STRING =~ /pre/ # prerelease builds
51-
expected_rspec_version = "3.11.0.pre"
51+
expected_rspec_version = "3.12.0.pre"
5252
s.add_runtime_dependency "rspec-#{name}", "= #{expected_rspec_version}"
5353
else
54-
expected_rspec_version = "3.10.0"
54+
expected_rspec_version = "3.11.0"
5555
s.add_runtime_dependency "rspec-#{name}", "~> #{expected_rspec_version.split(".")[0..1].join(".")}"
5656
end
5757
end

spec/rspec/rails/matchers/active_job_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ def self.name; "LoggingJob"; end
9898
expect { }.not_to have_enqueued_job
9999
end
100100

101+
context "when job is retried" do
102+
include ActiveJob::TestHelper
103+
104+
let(:retried_job) do
105+
Class.new(ActiveJob::Base) do
106+
retry_on StandardError, wait: 5, queue: :retry
107+
108+
def self.name; "RetriedJob"; end
109+
def perform; raise StandardError; end
110+
end
111+
end
112+
113+
before do
114+
stub_const("RetriedJob", retried_job)
115+
queue_adapter.perform_enqueued_jobs = true
116+
end
117+
118+
it "passes with reenqueued job" do
119+
time = Time.current.change(usec: 0)
120+
travel_to time do
121+
expect { retried_job.perform_later }.to have_enqueued_job(retried_job).on_queue(:retry).at(time + 5)
122+
end
123+
end
124+
end
125+
101126
it "fails when job is not enqueued" do
102127
expect {
103128
expect { }.to have_enqueued_job

0 commit comments

Comments
 (0)