Fix ActiveJob Test adapter not respecting retry attempts:

- ### Problem

  Given the below example the test adapter will retry the job
  indefinitely:

  ```ruby
    class BuggyJob < ActiveJob::Base
      retry_on(Exception,  attempts: 2)

      def perform
        raise "error"
      end
    end

    BuggyJob.perform_later
    perform_enqueued_jobs
  ```

  The problem is that when the job get retried, the
  `exception_executions` variable is not serialized/deserialized,
  resulting in ActiveJob to not be able to determine how many time
  this job was retried.

  The solution in this PR is to deserialize the whole job in the test
  adapter, and reserialize it before retrying.

  Fix #38391
This commit is contained in:
Edouard CHIN 2020-02-05 23:59:03 -04:00
parent 5613b37b26
commit d35cf4c05d
4 changed files with 33 additions and 8 deletions

@ -41,7 +41,11 @@ def enqueue_at(job, timestamp) #:nodoc:
private
def job_to_hash(job, extras = {})
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
job.serialize.tap do |job_data|
job_data[:job] = job.class
job_data[:args] = job_data.fetch("arguments")
job_data[:queue] = job_data.fetch("queue_name")
end.merge(extras)
end
def perform_or_enqueue(perform, job, job_data)

@ -674,10 +674,9 @@ def deserialize_args_for_assertion(job)
end
def instantiate_job(payload)
args = ActiveJob::Arguments.deserialize(payload[:args])
job = payload[:job].new(*args)
job = payload[:job].deserialize(payload)
job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
job.queue_name = payload[:queue]
job.send(:deserialize_arguments_if_needed)
job
end

@ -7,6 +7,7 @@
require "jobs/logging_job"
require "jobs/nested_job"
require "jobs/rescue_job"
require "jobs/raising_job"
require "jobs/inherited_job"
require "jobs/multiple_kwargs_job"
require "models/person"
@ -660,7 +661,7 @@ def test_assert_enqueued_with_failure_with_global_id_args
end
end
assert_match(/No enqueued job found with {:job=>HelloJob, :args=>\[#{wilma.inspect}\]}/, error.message)
assert_match(/Potential matches: {:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
assert_match(/Potential matches: {.*?:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
end
def test_assert_enqueued_with_failure_with_no_block_with_global_id_args
@ -672,7 +673,7 @@ def test_assert_enqueued_with_failure_with_no_block_with_global_id_args
end
assert_match(/No enqueued job found with {:job=>HelloJob, :args=>\[#{wilma.inspect}\]}/, error.message)
assert_match(/Potential matches: {:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
assert_match(/Potential matches: {.*?:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
end
def test_assert_enqueued_with_does_not_change_jobs_count
@ -1825,7 +1826,7 @@ def test_assert_performed_with_failure_with_global_id_args
end
end
assert_match(/No performed job found with {:job=>HelloJob, :args=>\[#{wilma.inspect}\]}/, error.message)
assert_match(/Potential matches: {:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
assert_match(/Potential matches: {.*?:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
end
def test_assert_performed_with_without_block_failure_with_global_id_args
@ -1838,7 +1839,7 @@ def test_assert_performed_with_without_block_failure_with_global_id_args
end
assert_match(/No performed job found with {:job=>HelloJob, :args=>\[#{wilma.inspect}\]}/, error.message)
assert_match(/Potential matches: {:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
assert_match(/Potential matches: {.*?:job=>HelloJob, :args=>\[#<Person.* @id=\"9\"\>\], :queue=>\"default\"}/, error.message)
end
def test_assert_performed_with_does_not_change_jobs_count
@ -1864,6 +1865,16 @@ def test_assert_performed_with_without_block_does_not_change_jobs_count
assert_equal 2, queue_adapter.performed_jobs.count
end
test "TestAdapter respect max attempts" do
RaisingJob.perform_later
assert_raises(RaisingJob::MyError) do
perform_enqueued_jobs
end
assert_equal 2, queue_adapter.enqueued_jobs.count
end
end
class OverrideQueueAdapterTest < ActiveJob::TestCase

@ -0,0 +1,11 @@
# frozen_string_literal: true
class RaisingJob < ActiveJob::Base
MyError = Class.new(StandardError)
retry_on(MyError, attempts: 2)
def perform
raise MyError
end
end