Skip to content

Commit

Permalink
Fixup have_enqueued_job matcher on job retries
Browse files Browse the repository at this point in the history
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.

Also we need to count jobs before & after because multiple
`#perform_later` in rails-6.0 create jobs with the same `#hash`, at
least in some cases, using `:test` AJ adapter.
  • Loading branch information
ojab authored and pirj committed Oct 27, 2022
1 parent b347d05 commit 304f624
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 3 deletions.
21 changes: 18 additions & 3 deletions lib/rspec/rails/matchers/active_job.rb
Expand Up @@ -230,11 +230,26 @@ def initialize(job)
def matches?(proc)
raise ArgumentError, "have_enqueued_job and enqueue_job only support block expectations" unless Proc === proc

original_enqueued_jobs_count = queue_adapter.enqueued_jobs.count
original_enqueued_jobs_hashes = queue_adapter.enqueued_jobs.map(&:hash)

proc.call
in_block_jobs = queue_adapter.enqueued_jobs.drop(original_enqueued_jobs_count)

check(in_block_jobs)
in_block_jobs = queue_adapter.enqueued_jobs.each_with_object({}) do |job, jobs|
jobs[job.hash] ||= { job: job, count: 0 }
jobs[job.hash][:count] += 1
end

original_enqueued_jobs_hashes.each do |job_hash|
in_block_jobs[job_hash][:count] -= 1 if in_block_jobs.key?(job_hash)
end

in_block_jobs = in_block_jobs.each_value.flat_map do |job_and_count|
count, job = job_and_count.values_at(:count, :job)

Array.new(count, job) if count.positive?
end

check(in_block_jobs.compact)
end

def does_not_match?(proc)
Expand Down
25 changes: 25 additions & 0 deletions spec/rspec/rails/matchers/active_job_spec.rb
Expand Up @@ -98,6 +98,31 @@ def self.name; "LoggingJob"; end
expect { }.not_to have_enqueued_job
end

context "when job is retried" do
include ActiveJob::TestHelper

let(:retried_job) do
Class.new(ActiveJob::Base) do
retry_on StandardError, wait: 5, queue: :retry

def self.name; "RetriedJob"; end
def perform; raise StandardError; end
end
end

before do
stub_const("RetriedJob", retried_job)
queue_adapter.perform_enqueued_jobs = true
end

it "passes with reenqueued job" do
time = Time.current.change(usec: 0)
travel_to time do
expect { retried_job.perform_later }.to have_enqueued_job(retried_job).on_queue(:retry).at(time + 5)
end
end
end

it "fails when job is not enqueued" do
expect {
expect { }.to have_enqueued_job
Expand Down

0 comments on commit 304f624

Please sign in to comment.