aboutsummaryrefslogtreecommitdiffstats
path: root/activejob
diff options
context:
space:
mode:
Diffstat (limited to 'activejob')
-rw-r--r--activejob/CHANGELOG.md55
-rw-r--r--activejob/activejob.gemspec3
-rw-r--r--activejob/lib/active_job/arguments.rb33
-rw-r--r--activejob/lib/active_job/core.rb40
-rw-r--r--activejob/lib/active_job/exceptions.rb29
-rw-r--r--activejob/lib/active_job/execution.rb6
-rw-r--r--activejob/lib/active_job/logging.rb32
-rw-r--r--activejob/lib/active_job/queue_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/async_adapter.rb2
-rw-r--r--activejob/lib/active_job/queue_adapters/backburner_adapter.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb20
-rw-r--r--activejob/lib/active_job/railtie.rb4
-rw-r--r--activejob/lib/active_job/test_helper.rb263
-rw-r--r--activejob/test/cases/argument_serialization_test.rb11
-rw-r--r--activejob/test/cases/exceptions_test.rb8
-rw-r--r--activejob/test/cases/logging_test.rb106
-rw-r--r--activejob/test/cases/test_helper_test.rb687
-rw-r--r--activejob/test/integration/queuing_test.rb12
-rw-r--r--activejob/test/jobs/multiple_kwargs_job.rb9
-rw-r--r--activejob/test/jobs/retry_job.rb1
-rw-r--r--activejob/test/support/integration/adapters/backburner.rb3
-rw-r--r--activejob/test/support/integration/adapters/que.rb7
-rw-r--r--activejob/test/support/integration/adapters/queue_classic.rb7
-rw-r--r--activejob/test/support/integration/adapters/sneakers.rb16
-rw-r--r--activejob/test/support/integration/helper.rb2
26 files changed, 1171 insertions, 193 deletions
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 8526741383..8bbecd5a5a 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,23 +1,52 @@
-* Move `enqueue`/`enqueue_at` notifications to an around callback.
+* Restore HashWithIndifferentAccess support to ActiveJob::Arguments.deserialize.
- Improves timing accuracy over the old after callback by including
- time spent writing to the adapter's IO implementation.
+ *Gannon McGibbon*
- *Zach Kemp*
+* Include deserialized arguments in job instances returned from
+ `assert_enqueued_with` and `assert_performed_with`
-* Allow `queue` option to `assert_no_enqueued_jobs`.
+ *Alan Wu*
- Example:
- ```
- def test_no_logging
- assert_no_enqueued_jobs queue: 'default' do
- LoggingJob.set(queue: :some_queue).perform_later
- end
- end
- ```
+* Allow `assert_enqueued_with`/`assert_performed_with` methods to accept
+ a proc for the `args` argument. This is useful to check if only a subset of arguments
+ matches your expectations.
+
+ *Edouard Chin*
+
+* `ActionDispatch::IntegrationTest` includes `ActiveJob::TestHelper` module by default.
+
+ *Ricardo Díaz*
+
+* Added `enqueue_retry.active_job`, `retry_stopped.active_job`, and `discard.active_job` hooks.
+
+ *steves*
+
+* Allow `assert_performed_with` to be called without a block.
+
+ *bogdanvlviv*
+
+* Execution of `assert_performed_jobs`, and `assert_no_performed_jobs`
+ without a block should respect passed `:except`, `:only`, and `:queue` options.
+
+ *bogdanvlviv*
+
+* Allow `:queue` option to job assertions and helpers.
*bogdanvlviv*
+* Allow `perform_enqueued_jobs` to be called without a block.
+
+ Performs all of the jobs that have been enqueued up to this point in the test.
+
+ *Kevin Deisz*
+
+* Move `enqueue`/`enqueue_at` notifications to an around callback.
+
+ Improves timing accuracy over the old after callback by including
+ time spent writing to the adapter's IO implementation.
+
+ *Zach Kemp*
+
* Allow call `assert_enqueued_with` with no block.
Example:
diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec
index be6292f737..cd7ac210ec 100644
--- a/activejob/activejob.gemspec
+++ b/activejob/activejob.gemspec
@@ -25,6 +25,9 @@ Gem::Specification.new do |s|
"changelog_uri" => "https://github.com/rails/rails/blob/v#{version}/activejob/CHANGELOG.md"
}
+ # NOTE: Please read our dependency guidelines before updating versions:
+ # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves
+
s.add_dependency "activesupport", version
s.add_dependency "globalid", ">= 0.3.6"
end
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index 86bb0c5540..a86a82c6e3 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -24,18 +24,20 @@ module ActiveJob
module Arguments
extend self
# :nodoc:
- TYPE_WHITELIST = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
+ PERMITTED_TYPES = [ NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass ]
- # Serializes a set of arguments. Whitelisted types are returned
- # as-is. Arrays/Hashes are serialized element by element.
- # All other types are serialized using GlobalID.
+ # Serializes a set of arguments. Intrinsic types that can safely be
+ # serialized without mutation are returned as-is. Arrays/Hashes are
+ # serialized element by element. All other types are serialized using
+ # GlobalID.
def serialize(arguments)
arguments.map { |argument| serialize_argument(argument) }
end
- # Deserializes a set of arguments. Whitelisted types are returned
- # as-is. Arrays/Hashes are deserialized element by element.
- # All other types are deserialized using GlobalID.
+ # Deserializes a set of arguments. Intrinsic types that can safely be
+ # deserialized without mutation are returned as-is. Arrays/Hashes are
+ # deserialized element by element. All other types are deserialized using
+ # GlobalID.
def deserialize(arguments)
arguments.map { |argument| deserialize_argument(argument) }
rescue
@@ -45,11 +47,11 @@ module ActiveJob
private
# :nodoc:
- GLOBALID_KEY = "_aj_globalid".freeze
+ GLOBALID_KEY = "_aj_globalid"
# :nodoc:
- SYMBOL_KEYS_KEY = "_aj_symbol_keys".freeze
+ SYMBOL_KEYS_KEY = "_aj_symbol_keys"
# :nodoc:
- WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access".freeze
+ WITH_INDIFFERENT_ACCESS_KEY = "_aj_hash_with_indifferent_access"
# :nodoc:
OBJECT_SERIALIZER_KEY = "_aj_serialized"
@@ -60,11 +62,11 @@ module ActiveJob
OBJECT_SERIALIZER_KEY, OBJECT_SERIALIZER_KEY.to_sym,
WITH_INDIFFERENT_ACCESS_KEY, WITH_INDIFFERENT_ACCESS_KEY.to_sym,
]
- private_constant :RESERVED_KEYS
+ private_constant :RESERVED_KEYS, :GLOBALID_KEY, :SYMBOL_KEYS_KEY, :WITH_INDIFFERENT_ACCESS_KEY
def serialize_argument(argument)
case argument
- when *TYPE_WHITELIST
+ when *PERMITTED_TYPES
argument
when GlobalID::Identification
convert_to_global_id_hash(argument)
@@ -88,7 +90,7 @@ module ActiveJob
case argument
when String
GlobalID::Locator.locate(argument) || argument
- when *TYPE_WHITELIST
+ when *PERMITTED_TYPES
argument
when Array
argument.map { |arg| deserialize_argument(arg) }
@@ -145,7 +147,10 @@ module ActiveJob
end
def transform_symbol_keys(hash, symbol_keys)
- hash.transform_keys do |key|
+ # NOTE: HashWithIndifferentAccess#transform_keys always
+ # returns stringified keys with indifferent access
+ # so we call #to_h here to ensure keys are symbolized.
+ hash.to_h.transform_keys do |key|
if symbol_keys.include?(key)
key.to_sym
else
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index 61d402cfca..62bb5861bb 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -6,35 +6,33 @@ module ActiveJob
module Core
extend ActiveSupport::Concern
- included do
- # Job arguments
- attr_accessor :arguments
- attr_writer :serialized_arguments
+ # Job arguments
+ attr_accessor :arguments
+ attr_writer :serialized_arguments
- # Timestamp when the job should be performed
- attr_accessor :scheduled_at
+ # Timestamp when the job should be performed
+ attr_accessor :scheduled_at
- # Job Identifier
- attr_accessor :job_id
+ # Job Identifier
+ attr_accessor :job_id
- # Queue in which the job will reside.
- attr_writer :queue_name
+ # Queue in which the job will reside.
+ attr_writer :queue_name
- # Priority that the job will have (lower is more priority).
- attr_writer :priority
+ # Priority that the job will have (lower is more priority).
+ attr_writer :priority
- # ID optionally provided by adapter
- attr_accessor :provider_job_id
+ # ID optionally provided by adapter
+ attr_accessor :provider_job_id
- # Number of times this job has been executed (which increments on every retry, like after an exception).
- attr_accessor :executions
+ # Number of times this job has been executed (which increments on every retry, like after an exception).
+ attr_accessor :executions
- # I18n.locale to be used during the job.
- attr_accessor :locale
+ # I18n.locale to be used during the job.
+ attr_accessor :locale
- # Timezone to be used during the job.
- attr_accessor :timezone
- end
+ # Timezone to be used during the job.
+ attr_accessor :timezone
# These methods will be included into any Active Job object, adding
# helpers for de/serialization and creation of job instances.
diff --git a/activejob/lib/active_job/exceptions.rb b/activejob/lib/active_job/exceptions.rb
index 1e57dbcb1c..bc9e168971 100644
--- a/activejob/lib/active_job/exceptions.rb
+++ b/activejob/lib/active_job/exceptions.rb
@@ -9,6 +9,7 @@ module ActiveJob
module ClassMethods
# Catch the exception and reschedule job for re-execution after so many seconds, for a specific number of attempts.
+ # The number of attempts includes the total executions of a job, not just the retried executions.
# If the exception keeps getting raised beyond the specified number of attempts, the exception is allowed to
# bubble up to the underlying queuing system, which may have its own retry mechanism or place it in a
# holding queue for inspection.
@@ -21,7 +22,8 @@ module ActiveJob
# as a computing proc that the number of executions so far as an argument, or as a symbol reference of
# <tt>:exponentially_longer</tt>, which applies the wait algorithm of <tt>(executions ** 4) + 2</tt>
# (first wait 3s, then 18s, then 83s, etc)
- # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts)
+ # * <tt>:attempts</tt> - Re-enqueues the job the specified number of times (default: 5 attempts),
+ # attempts here refers to the total number of times the job is executed, not just retried executions
# * <tt>:queue</tt> - Re-enqueues the job on a different queue
# * <tt>:priority</tt> - Re-enqueues the job with a different priority
#
@@ -45,13 +47,14 @@ module ActiveJob
def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil)
rescue_from(*exceptions) do |error|
if executions < attempts
- logger.error "Retrying #{self.class} in #{wait} seconds, due to a #{error.class}. The original exception was #{error.cause.inspect}."
- retry_job wait: determine_delay(wait), queue: queue, priority: priority
+ retry_job wait: determine_delay(wait), queue: queue, priority: priority, error: error
else
if block_given?
- yield self, error
+ instrument :retry_stopped, error: error do
+ yield self, error
+ end
else
- logger.error "Stopped retrying #{self.class} due to a #{error.class}, which reoccurred on #{executions} attempts. The original exception was #{error.cause.inspect}."
+ instrument :retry_stopped, error: error
raise error
end
end
@@ -78,10 +81,8 @@ module ActiveJob
# end
def discard_on(*exceptions)
rescue_from(*exceptions) do |error|
- if block_given?
- yield self, error
- else
- logger.error "Discarded #{self.class} due to a #{error.class}. The original exception was #{error.cause.inspect}."
+ instrument :discard, error: error do
+ yield self, error if block_given?
end
end
end
@@ -109,7 +110,9 @@ module ActiveJob
# end
# end
def retry_job(options = {})
- enqueue options
+ instrument :enqueue_retry, options.slice(:error, :wait) do
+ enqueue options
+ end
end
private
@@ -130,5 +133,11 @@ module ActiveJob
raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
end
end
+
+ def instrument(name, error: nil, wait: nil, &block)
+ payload = { job: self, adapter: self.class.queue_adapter, error: error, wait: wait }
+
+ ActiveSupport::Notifications.instrument("#{name}.active_job", payload, &block)
+ end
end
end
diff --git a/activejob/lib/active_job/execution.rb b/activejob/lib/active_job/execution.rb
index d75be376ec..f5a343311f 100644
--- a/activejob/lib/active_job/execution.rb
+++ b/activejob/lib/active_job/execution.rb
@@ -31,11 +31,11 @@ module ActiveJob
#
# MyJob.new(*args).perform_now
def perform_now
+ # Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
+ self.executions = (executions || 0) + 1
+
deserialize_arguments_if_needed
run_callbacks :perform do
- # Guard against jobs that were persisted before we started counting executions by zeroing out nil counters
- self.executions = (executions || 0) + 1
-
perform(*arguments)
end
rescue => exception
diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb
index 9ffd60ad53..416be83c24 100644
--- a/activejob/lib/active_job/logging.rb
+++ b/activejob/lib/active_job/logging.rb
@@ -88,6 +88,38 @@ module ActiveJob
end
end
+ def enqueue_retry(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+ wait = event.payload[:wait]
+
+ info do
+ if ex
+ "Retrying #{job.class} in #{wait.to_i} seconds, due to a #{ex.class}."
+ else
+ "Retrying #{job.class} in #{wait.to_i} seconds."
+ end
+ end
+ end
+
+ def retry_stopped(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+
+ error do
+ "Stopped retrying #{job.class} due to a #{ex.class}, which reoccurred on #{job.executions} attempts."
+ end
+ end
+
+ def discard(event)
+ job = event.payload[:job]
+ ex = event.payload[:error]
+
+ error do
+ "Discarded #{job.class} due to a #{ex.class}."
+ end
+ end
+
private
def queue_name(event)
event.payload[:adapter].class.name.demodulize.remove("Adapter") + "(#{event.payload[:job].queue_name})"
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 006a683b85..954bfd1dd1 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -22,6 +22,8 @@ module ActiveJob
_queue_adapter
end
+ # Returns string denoting the name of the configured queue adapter.
+ # By default returns +"async"+.
def queue_adapter_name
_queue_adapter_name
end
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index 00c7b407b1..3e3a474fbb 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -121,7 +121,7 @@ module ActiveJob
autoload :SuckerPunchAdapter
autoload :TestAdapter
- ADAPTER = "Adapter".freeze
+ ADAPTER = "Adapter"
private_constant :ADAPTER
class << self
diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb
index ebf6f384e3..53a7e3d53e 100644
--- a/activejob/lib/active_job/queue_adapters/async_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb
@@ -31,7 +31,7 @@ module ActiveJob
# jobs. Since jobs share a single thread pool, long-running jobs will block
# short-lived jobs. Fine for dev/test; bad for production.
class AsyncAdapter
- # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ThreadPoolExecutor.html] for executor options.
+ # See {Concurrent::ThreadPoolExecutor}[https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadPoolExecutor.html] for executor options.
def initialize(**executor_options)
@scheduler = Scheduler.new(**executor_options)
end
diff --git a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
index 0ba93c6e0b..7dc49310ac 100644
--- a/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/backburner_adapter.rb
@@ -16,12 +16,12 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :backburner
class BackburnerAdapter
def enqueue(job) #:nodoc:
- Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name
+ Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority)
end
def enqueue_at(job, timestamp) #:nodoc:
delay = timestamp - Time.current.to_f
- Backburner::Worker.enqueue JobWrapper, [ job.serialize ], queue: job.queue_name, delay: delay
+ Backburner::Worker.enqueue(JobWrapper, [job.serialize], queue: job.queue_name, pri: job.priority, delay: delay)
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb
index 885f9ff01c..f73ad444ba 100644
--- a/activejob/lib/active_job/queue_adapters/test_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -12,7 +12,7 @@ module ActiveJob
#
# Rails.application.config.active_job.queue_adapter = :test
class TestAdapter
- attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject)
+ attr_accessor(:perform_enqueued_jobs, :perform_enqueued_at_jobs, :filter, :reject, :queue)
attr_writer(:enqueued_jobs, :performed_jobs)
# Provides a store of all the enqueued jobs with the TestAdapter so you can check them.
@@ -29,14 +29,14 @@ module ActiveJob
return if filtered?(job)
job_data = job_to_hash(job)
- enqueue_or_perform(perform_enqueued_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_jobs, job, job_data)
end
def enqueue_at(job, timestamp) #:nodoc:
return if filtered?(job)
job_data = job_to_hash(job, at: timestamp)
- enqueue_or_perform(perform_enqueued_at_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_at_jobs, job, job_data)
end
private
@@ -44,7 +44,7 @@ module ActiveJob
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
end
- def enqueue_or_perform(perform, job, job_data)
+ def perform_or_enqueue(perform, job, job_data)
if perform
performed_jobs << job_data
Base.execute job.serialize
@@ -54,12 +54,20 @@ module ActiveJob
end
def filtered?(job)
+ filtered_queue?(job) || filtered_job_class?(job)
+ end
+
+ def filtered_queue?(job)
+ if queue
+ job.queue_name != queue.to_s
+ end
+ end
+
+ def filtered_job_class?(job)
if filter
!Array(filter).include?(job.class)
elsif reject
Array(reject).include?(job.class)
- else
- false
end
end
end
diff --git a/activejob/lib/active_job/railtie.rb b/activejob/lib/active_job/railtie.rb
index d0294854d3..ecc0908d5f 100644
--- a/activejob/lib/active_job/railtie.rb
+++ b/activejob/lib/active_job/railtie.rb
@@ -30,6 +30,10 @@ module ActiveJob
send(k, v) if respond_to? k
end
end
+
+ ActiveSupport.on_load(:action_dispatch_integration_test) do
+ include ActiveJob::TestHelper
+ end
end
initializer "active_job.set_reloader_hook" do |app|
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 04cde28a96..0deb68d0d2 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -52,7 +52,7 @@ module ActiveJob
queue_adapter_changed_jobs.each { |klass| klass.disable_test_adapter }
end
- # Specifies the queue adapter to use with all active job test helpers.
+ # Specifies the queue adapter to use with all Active Job test helpers.
#
# Returns an instance of the queue adapter and defaults to
# <tt>ActiveJob::QueueAdapters::TestAdapter</tt>.
@@ -75,7 +75,7 @@ module ActiveJob
# assert_enqueued_jobs 2
# end
#
- # If a block is passed, that block will cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be enqueued.
#
# def test_jobs_again
@@ -89,7 +89,7 @@ module ActiveJob
# end
# end
#
- # The number of times a specific job was enqueued can be asserted.
+ # Asserts the number of times a specific job was enqueued by passing +:only+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, only: LoggingJob do
@@ -98,7 +98,7 @@ module ActiveJob
# end
# end
#
- # The number of times a job except specific class was enqueued can be asserted.
+ # Asserts the number of times a job except specific class was enqueued by passing +:except+ option.
#
# def test_logging_job
# assert_enqueued_jobs 1, except: HelloJob do
@@ -107,7 +107,7 @@ module ActiveJob
# end
# end
#
- # The number of times a job is enqueued to a specific queue can also be asserted.
+ # Asserts the number of times a job is enqueued to a specific queue by passing +:queue+ option.
#
# def test_logging_job
# assert_enqueued_jobs 2, queue: 'default' do
@@ -117,14 +117,18 @@ module ActiveJob
# end
def assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
if block_given?
- original_count = enqueued_jobs_size(only: only, except: except, queue: queue)
+ original_count = enqueued_jobs_with(only: only, except: except, queue: queue)
+
yield
- new_count = enqueued_jobs_size(only: only, except: except, queue: queue)
- assert_equal number, new_count - original_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+
+ new_count = enqueued_jobs_with(only: only, except: except, queue: queue)
+
+ actual_count = new_count - original_count
else
- actual_count = enqueued_jobs_size(only: only, except: except, queue: queue)
- assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
+ actual_count = enqueued_jobs_with(only: only, except: except, queue: queue)
end
+
+ assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
end
# Asserts that no jobs have been enqueued.
@@ -135,7 +139,7 @@ module ActiveJob
# assert_enqueued_jobs 1
# end
#
- # If a block is passed, that block should not cause any job to be enqueued.
+ # If a block is passed, asserts that the block will not cause any job to be enqueued.
#
# def test_jobs_again
# assert_no_enqueued_jobs do
@@ -143,7 +147,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs of a specific kind are enqueued:
+ # Asserts that no jobs of a specific kind are enqueued by passing +:only+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs only: LoggingJob do
@@ -151,7 +155,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs except specific class are enqueued:
+ # Asserts that no jobs except specific class are enqueued by passing +:except+ option.
#
# def test_no_logging
# assert_no_enqueued_jobs except: HelloJob do
@@ -159,7 +163,7 @@ module ActiveJob
# end
# end
#
- # It can be asserted that no jobs are enqueued to a specific queue:
+ # Asserts that no jobs are enqueued to a specific queue by passing +:queue+ option
#
# def test_no_logging
# assert_no_enqueued_jobs queue: 'default' do
@@ -176,7 +180,7 @@ module ActiveJob
# Asserts that the number of performed jobs matches the given number.
# If no block is passed, <tt>perform_enqueued_jobs</tt>
- # must be called around the job call.
+ # must be called around or after the job call.
#
# def test_jobs
# assert_performed_jobs 0
@@ -186,13 +190,14 @@ module ActiveJob
# end
# assert_performed_jobs 1
#
- # perform_enqueued_jobs do
- # HelloJob.perform_later('yves')
- # assert_performed_jobs 2
- # end
+ # HelloJob.perform_later('yves')
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_jobs 2
# end
#
- # If a block is passed, that block should cause the specified number of
+ # If a block is passed, asserts that the block will cause the specified number of
# jobs to be performed.
#
# def test_jobs_again
@@ -206,7 +211,7 @@ module ActiveJob
# end
# end
#
- # The block form supports filtering. If the :only option is specified,
+ # This method also supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will be performed.
#
# def test_hello_job
@@ -216,7 +221,7 @@ module ActiveJob
# end
# end
#
- # Also if the :except option is specified,
+ # Also if the +:except+ option is specified,
# then the job(s) except specific class will be performed.
#
# def test_hello_job
@@ -237,17 +242,30 @@ module ActiveJob
# end
# end
# end
- def assert_performed_jobs(number, only: nil, except: nil)
+ #
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will be performed.
+ #
+ # def test_assert_performed_jobs_with_queue_option
+ # assert_performed_jobs 1, queue: :some_queue do
+ # HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ # HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ # end
+ # end
+ def assert_performed_jobs(number, only: nil, except: nil, queue: nil, &block)
if block_given?
original_count = performed_jobs.size
- perform_enqueued_jobs(only: only, except: except) { yield }
+
+ perform_enqueued_jobs(only: only, except: except, queue: queue, &block)
+
new_count = performed_jobs.size
- assert_equal number, new_count - original_count,
- "#{number} jobs expected, but #{new_count - original_count} were performed"
+
+ performed_jobs_size = new_count - original_count
else
- performed_jobs_size = performed_jobs.size
- assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
+ performed_jobs_size = performed_jobs_with(only: only, except: except, queue: queue)
end
+
+ assert_equal number, performed_jobs_size, "#{number} jobs expected, but #{performed_jobs_size} were performed"
end
# Asserts that no jobs have been performed.
@@ -261,7 +279,7 @@ module ActiveJob
# end
# end
#
- # If a block is passed, that block should not cause any job to be performed.
+ # If a block is passed, asserts that the block will not cause any job to be performed.
#
# def test_jobs_again
# assert_no_performed_jobs do
@@ -269,7 +287,7 @@ module ActiveJob
# end
# end
#
- # The block form supports filtering. If the :only option is specified,
+ # The block form supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will not be performed.
#
# def test_no_logging
@@ -278,7 +296,7 @@ module ActiveJob
# end
# end
#
- # Also if the :except option is specified,
+ # Also if the +:except+ option is specified,
# then the job(s) except specific class will not be performed.
#
# def test_no_logging
@@ -287,11 +305,20 @@ module ActiveJob
# end
# end
#
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will not be performed.
+ #
+ # def test_assert_no_performed_jobs_with_queue_option
+ # assert_no_performed_jobs queue: :some_queue do
+ # HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ # end
+ # end
+ #
# Note: This assertion is simply a shortcut for:
#
# assert_performed_jobs 0, &block
- def assert_no_performed_jobs(only: nil, except: nil, &block)
- assert_performed_jobs 0, only: only, except: except, &block
+ def assert_no_performed_jobs(only: nil, except: nil, queue: nil, &block)
+ assert_performed_jobs 0, only: only, except: except, queue: queue, &block
end
# Asserts that the job has been enqueued with the given arguments.
@@ -304,7 +331,23 @@ module ActiveJob
# assert_enqueued_with(job: MyJob, at: Date.tomorrow.noon)
# end
#
- # If a block is passed, that block should cause the job to be
+ #
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_enqueued_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ #
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ # assert_enqueued_with(job: MyJob, args: expected_args, queue: 'low')
+ # end
+ #
+ #
+ # If a block is passed, asserts that the block will cause the job to be
# enqueued with the given arguments.
#
# def test_assert_enqueued_with
@@ -318,7 +361,7 @@ module ActiveJob
# end
def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
+ expected_args = prepare_args_for_assertion(expected)
if block_given?
original_enqueued_jobs_count = enqueued_jobs.count
@@ -331,14 +374,56 @@ module ActiveJob
end
matching_job = jobs.find do |enqueued_job|
- serialized_args.all? { |key, value| value == enqueued_job[key] }
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
end
assert matching_job, "No enqueued job found with #{expected}"
instantiate_job(matching_job)
end
- # Asserts that the job passed in the block has been performed with the given arguments.
+ # Asserts that the job has been performed with the given arguments.
+ #
+ # def test_assert_performed_with
+ # MyJob.perform_later(1,2,3)
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high')
+ #
+ # MyJob.set(wait_until: Date.tomorrow.noon).perform_later
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, at: Date.tomorrow.noon)
+ # end
+ #
+ # The +args+ argument also accepts a proc which will get passed the actual
+ # job's arguments. Your proc needs to returns a boolean value determining if
+ # the job's arguments matches your expectation. This is useful to check only
+ # for a subset of arguments.
+ #
+ # def test_assert_performed_with
+ # expected_args = ->(job_args) do
+ # assert job_args.first.key?(:foo)
+ # end
+ # MyJob.perform_later(foo: 'bar', other_arg: 'No need to check in the test')
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_with(job: MyJob, args: expected_args, queue: 'high')
+ # end
+ #
+ # If a block is passed, that block performs all of the jobs that were
+ # enqueued throughout the duration of the block and asserts that
+ # the job has been performed with the given arguments in the block.
#
# def test_assert_performed_with
# assert_performed_with(job: MyJob, args: [1,2,3], queue: 'high') do
@@ -349,20 +434,39 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
- original_performed_jobs_count = performed_jobs.count
+ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil, &block)
expected = { job: job, args: args, at: at, queue: queue }.compact
- serialized_args = serialize_args_for_assertion(expected)
- perform_enqueued_jobs { yield }
- in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
- matching_job = in_block_jobs.find do |in_block_job|
- serialized_args.all? { |key, value| value == in_block_job[key] }
+ expected_args = prepare_args_for_assertion(expected)
+
+ if block_given?
+ original_performed_jobs_count = performed_jobs.count
+
+ perform_enqueued_jobs(&block)
+
+ jobs = performed_jobs.drop(original_performed_jobs_count)
+ else
+ jobs = performed_jobs
end
+
+ matching_job = jobs.find do |enqueued_job|
+ deserialized_job = deserialize_args_for_assertion(enqueued_job)
+
+ expected_args.all? do |key, value|
+ if value.respond_to?(:call)
+ value.call(deserialized_job[key])
+ else
+ value == deserialized_job[key]
+ end
+ end
+ end
+
assert matching_job, "No performed job found with #{expected}"
instantiate_job(matching_job)
end
- # Performs all enqueued jobs in the duration of the block.
+ # Performs all enqueued jobs. If a block is given, performs all of the jobs
+ # that were enqueued throughout the duration of the block. If a block is
+ # not given, performs all of the enqueued jobs up to this point in the test.
#
# def test_perform_enqueued_jobs
# perform_enqueued_jobs do
@@ -371,6 +475,14 @@ module ActiveJob
# assert_performed_jobs 1
# end
#
+ # def test_perform_enqueued_jobs_without_block
+ # MyJob.perform_later(1, 2, 3)
+ #
+ # perform_enqueued_jobs
+ #
+ # assert_performed_jobs 1
+ # end
+ #
# This method also supports filtering. If the +:only+ option is specified,
# then only the listed job(s) will be performed.
#
@@ -393,24 +505,42 @@ module ActiveJob
# assert_performed_jobs 1
# end
#
- def perform_enqueued_jobs(only: nil, except: nil)
+ # If the +:queue+ option is specified,
+ # then only the job(s) enqueued to a specific queue will be performed.
+ #
+ # def test_perform_enqueued_jobs_with_queue
+ # perform_enqueued_jobs queue: :some_queue do
+ # MyJob.set(queue: :some_queue).perform_later(1, 2, 3) # will be performed
+ # HelloJob.set(queue: :other_queue).perform_later(1, 2, 3) # will not be performed
+ # end
+ # assert_performed_jobs 1
+ # end
+ #
+ def perform_enqueued_jobs(only: nil, except: nil, queue: nil)
+ return flush_enqueued_jobs(only: only, except: except, queue: queue) unless block_given?
+
validate_option(only: only, except: except)
+
old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
old_filter = queue_adapter.filter
old_reject = queue_adapter.reject
+ old_queue = queue_adapter.queue
begin
queue_adapter.perform_enqueued_jobs = true
queue_adapter.perform_enqueued_at_jobs = true
queue_adapter.filter = only
queue_adapter.reject = except
+ queue_adapter.queue = queue
+
yield
ensure
queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
queue_adapter.filter = old_filter
queue_adapter.reject = old_reject
+ queue_adapter.queue = old_queue
end
end
@@ -432,31 +562,58 @@ module ActiveJob
performed_jobs.clear
end
- def enqueued_jobs_size(only: nil, except: nil, queue: nil)
+ def jobs_with(jobs, only: nil, except: nil, queue: nil)
validate_option(only: only, except: except)
- enqueued_jobs.count do |job|
+
+ jobs.count do |job|
job_class = job.fetch(:job)
+
if only
next false unless Array(only).include?(job_class)
elsif except
next false if Array(except).include?(job_class)
end
+
if queue
next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
end
+
+ yield job if block_given?
+
true
end
end
- def serialize_args_for_assertion(args)
- args.dup.tap do |serialized_args|
- serialized_args[:args] = ActiveJob::Arguments.serialize(serialized_args[:args]) if serialized_args[:args]
- serialized_args[:at] = serialized_args[:at].to_f if serialized_args[:at]
+ def enqueued_jobs_with(only: nil, except: nil, queue: nil, &block)
+ jobs_with(enqueued_jobs, only: only, except: except, queue: queue, &block)
+ end
+
+ def performed_jobs_with(only: nil, except: nil, queue: nil, &block)
+ jobs_with(performed_jobs, only: only, except: except, queue: queue, &block)
+ end
+
+ def flush_enqueued_jobs(only: nil, except: nil, queue: nil)
+ enqueued_jobs_with(only: only, except: except, queue: queue) do |payload|
+ instantiate_job(payload).perform_now
+ queue_adapter.performed_jobs << payload
+ end
+ end
+
+ def prepare_args_for_assertion(args)
+ args.dup.tap do |arguments|
+ arguments[:at] = arguments[:at].to_f if arguments[:at]
+ end
+ end
+
+ def deserialize_args_for_assertion(job)
+ job.dup.tap do |new_job|
+ new_job[:args] = ActiveJob::Arguments.deserialize(new_job[:args]) if new_job[:args]
end
end
def instantiate_job(payload)
- job = payload[:job].new(*payload[:args])
+ args = ActiveJob::Arguments.deserialize(payload[:args])
+ job = payload[:job].new(*args)
job.scheduled_at = Time.at(payload[:at]) if payload.key?(:at)
job.queue_name = payload[:queue]
job
diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb
index e5f1f087fe..8b2981926f 100644
--- a/activejob/test/cases/argument_serialization_test.rb
+++ b/activejob/test/cases/argument_serialization_test.rb
@@ -73,6 +73,7 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
string_key = { "a" => 1, "_aj_symbol_keys" => [] }
another_string_key = { "a" => 1 }
indifferent_access = { "a" => 1, "_aj_hash_with_indifferent_access" => true }
+ indifferent_access_symbol_key = symbol_key.with_indifferent_access
assert_equal(
{ a: 1 },
@@ -90,6 +91,10 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
{ "a" => 1 },
ActiveJob::Arguments.deserialize([indifferent_access]).first
)
+ assert_equal(
+ { a: 1 },
+ ActiveJob::Arguments.deserialize([indifferent_access_symbol_key]).first
+ )
end
test "should maintain hash with indifferent access" do
@@ -121,8 +126,10 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
end
test "should not allow reserved hash keys" do
- ["_aj_globalid", :_aj_globalid, "_aj_symbol_keys", :_aj_symbol_keys,
- "_aj_hash_with_indifferent_access", :_aj_hash_with_indifferent_access].each do |key|
+ ["_aj_globalid", :_aj_globalid,
+ "_aj_symbol_keys", :_aj_symbol_keys,
+ "_aj_hash_with_indifferent_access", :_aj_hash_with_indifferent_access,
+ "_aj_serialized", :_aj_serialized].each do |key|
assert_raises ActiveJob::SerializationError do
ActiveJob::Arguments.serialize [key => 1]
end
diff --git a/activejob/test/cases/exceptions_test.rb b/activejob/test/cases/exceptions_test.rb
index 47d4e3c0c2..37bb65538a 100644
--- a/activejob/test/cases/exceptions_test.rb
+++ b/activejob/test/cases/exceptions_test.rb
@@ -2,6 +2,7 @@
require "helper"
require "jobs/retry_job"
+require "models/person"
class ExceptionsTest < ActiveJob::TestCase
setup do
@@ -131,4 +132,11 @@ class ExceptionsTest < ActiveJob::TestCase
assert_equal [ "Raised SecondDiscardableErrorOfTwo for the 1st time" ], JobBuffer.values
end
end
+
+ test "successfully retry job throwing DeserializationError" do
+ perform_enqueued_jobs do
+ RetryJob.perform_later Person.new(404), 5
+ assert_equal ["Raised ActiveJob::DeserializationError for the 5 time"], JobBuffer.values
+ end
+ end
end
diff --git a/activejob/test/cases/logging_test.rb b/activejob/test/cases/logging_test.rb
index a1107a07fd..48ef39aaca 100644
--- a/activejob/test/cases/logging_test.rb
+++ b/activejob/test/cases/logging_test.rb
@@ -8,9 +8,11 @@ require "jobs/logging_job"
require "jobs/overridden_logging_job"
require "jobs/nested_job"
require "jobs/rescue_job"
+require "jobs/retry_job"
require "models/person"
class LoggingTest < ActiveSupport::TestCase
+ include ActiveJob::TestHelper
include ActiveSupport::LogSubscriber::TestHelper
include ActiveSupport::Logger::Severity
@@ -59,13 +61,17 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_uses_job_name_as_tag
- LoggingJob.perform_later "Dummy"
- assert_match(/\[LoggingJob\]/, @logger.messages)
+ perform_enqueued_jobs do
+ LoggingJob.perform_later "Dummy"
+ assert_match(/\[LoggingJob\]/, @logger.messages)
+ end
end
def test_uses_job_id_as_tag
- LoggingJob.perform_later "Dummy"
- assert_match(/\[LOGGING-JOB-ID\]/, @logger.messages)
+ perform_enqueued_jobs do
+ LoggingJob.perform_later "Dummy"
+ assert_match(/\[LOGGING-JOB-ID\]/, @logger.messages)
+ end
end
def test_logs_correct_queue_name
@@ -78,19 +84,23 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_globalid_parameter_logging
- person = Person.new(123)
- LoggingJob.perform_later person
- assert_match(%r{Enqueued.*gid://aj/Person/123}, @logger.messages)
- assert_match(%r{Dummy, here is it: #<Person:.*>}, @logger.messages)
- assert_match(%r{Performing.*gid://aj/Person/123}, @logger.messages)
+ perform_enqueued_jobs do
+ person = Person.new(123)
+ LoggingJob.perform_later person
+ assert_match(%r{Enqueued.*gid://aj/Person/123}, @logger.messages)
+ assert_match(%r{Dummy, here is it: #<Person:.*>}, @logger.messages)
+ assert_match(%r{Performing.*gid://aj/Person/123}, @logger.messages)
+ end
end
def test_globalid_nested_parameter_logging
- person = Person.new(123)
- LoggingJob.perform_later(person: person)
- assert_match(%r{Enqueued.*gid://aj/Person/123}, @logger.messages)
- assert_match(%r{Dummy, here is it: .*#<Person:.*>}, @logger.messages)
- assert_match(%r{Performing.*gid://aj/Person/123}, @logger.messages)
+ perform_enqueued_jobs do
+ person = Person.new(123)
+ LoggingJob.perform_later(person: person)
+ assert_match(%r{Enqueued.*gid://aj/Person/123}, @logger.messages)
+ assert_match(%r{Dummy, here is it: .*#<Person:.*>}, @logger.messages)
+ assert_match(%r{Performing.*gid://aj/Person/123}, @logger.messages)
+ end
end
def test_enqueue_job_logging
@@ -102,22 +112,26 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_perform_job_logging
- LoggingJob.perform_later "Dummy"
- assert_match(/Performing LoggingJob \(Job ID: .*?\) from .*? with arguments:.*Dummy/, @logger.messages)
- assert_match(/Dummy, here is it: Dummy/, @logger.messages)
- assert_match(/Performed LoggingJob \(Job ID: .*?\) from .*? in .*ms/, @logger.messages)
+ perform_enqueued_jobs do
+ LoggingJob.perform_later "Dummy"
+ assert_match(/Performing LoggingJob \(Job ID: .*?\) from .*? with arguments:.*Dummy/, @logger.messages)
+ assert_match(/Dummy, here is it: Dummy/, @logger.messages)
+ assert_match(/Performed LoggingJob \(Job ID: .*?\) from .*? in .*ms/, @logger.messages)
+ end
end
def test_perform_nested_jobs_logging
- NestedJob.perform_later
- assert_match(/\[LoggingJob\] \[.*?\]/, @logger.messages)
- assert_match(/\[ActiveJob\] Enqueued NestedJob \(Job ID: .*\) to/, @logger.messages)
- assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performing NestedJob \(Job ID: .*?\) from/, @logger.messages)
- assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Enqueued LoggingJob \(Job ID: .*?\) to .* with arguments: "NestedJob"/, @logger.messages)
- assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performing LoggingJob \(Job ID: .*?\) from .* with arguments: "NestedJob"/, @logger.messages)
- assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Dummy, here is it: NestedJob/, @logger.messages)
- assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performed LoggingJob \(Job ID: .*?\) from .* in/, @logger.messages)
- assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performed NestedJob \(Job ID: .*?\) from .* in/, @logger.messages)
+ perform_enqueued_jobs do
+ NestedJob.perform_later
+ assert_match(/\[LoggingJob\] \[.*?\]/, @logger.messages)
+ assert_match(/\[ActiveJob\] Enqueued NestedJob \(Job ID: .*\) to/, @logger.messages)
+ assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performing NestedJob \(Job ID: .*?\) from/, @logger.messages)
+ assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Enqueued LoggingJob \(Job ID: .*?\) to .* with arguments: "NestedJob"/, @logger.messages)
+ assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performing LoggingJob \(Job ID: .*?\) from .* with arguments: "NestedJob"/, @logger.messages)
+ assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Dummy, here is it: NestedJob/, @logger.messages)
+ assert_match(/\[ActiveJob\].*\[LoggingJob\] \[LOGGING-JOB-ID\] Performed LoggingJob \(Job ID: .*?\) from .* in/, @logger.messages)
+ assert_match(/\[ActiveJob\] \[NestedJob\] \[NESTED-JOB-ID\] Performed NestedJob \(Job ID: .*?\) from .* in/, @logger.messages)
+ end
end
def test_enqueue_at_job_logging
@@ -146,9 +160,45 @@ class LoggingTest < ActiveSupport::TestCase
end
def test_job_error_logging
- RescueJob.perform_later "other"
+ perform_enqueued_jobs { RescueJob.perform_later "other" }
rescue RescueJob::OtherError
assert_match(/Performing RescueJob \(Job ID: .*?\) from .*? with arguments:.*other/, @logger.messages)
assert_match(/Error performing RescueJob \(Job ID: .*?\) from .*? in .*ms: RescueJob::OtherError \(Bad hair\):\n.*\brescue_job\.rb:\d+:in `perform'/, @logger.messages)
end
+
+ def test_enqueue_retry_logging
+ perform_enqueued_jobs do
+ RetryJob.perform_later "DefaultsError", 2
+ assert_match(/Retrying RetryJob in 3 seconds, due to a DefaultsError\./, @logger.messages)
+ end
+ end
+
+ def test_enqueue_retry_logging_on_retry_job
+ perform_enqueued_jobs { RescueJob.perform_later "david" }
+ assert_match(/Retrying RescueJob in 0 seconds\./, @logger.messages)
+ end
+
+ def test_retry_stopped_logging
+ perform_enqueued_jobs do
+ RetryJob.perform_later "CustomCatchError", 6
+ assert_match(/Stopped retrying RetryJob due to a CustomCatchError, which reoccurred on \d+ attempts\./, @logger.messages)
+ end
+ end
+
+ def test_retry_stopped_logging_without_block
+ perform_enqueued_jobs do
+ begin
+ RetryJob.perform_later "DefaultsError", 6
+ rescue DefaultsError
+ assert_match(/Stopped retrying RetryJob due to a DefaultsError, which reoccurred on \d+ attempts\./, @logger.messages)
+ end
+ end
+ end
+
+ def test_discard_logging
+ perform_enqueued_jobs do
+ RetryJob.perform_later "DiscardableError", 2
+ assert_match(/Discarded RetryJob due to a DiscardableError\./, @logger.messages)
+ end
+ end
end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index d0a21a5da3..8dc32037ff 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -8,6 +8,7 @@ require "jobs/logging_job"
require "jobs/nested_job"
require "jobs/rescue_job"
require "jobs/inherited_job"
+require "jobs/multiple_kwargs_job"
require "models/person"
class EnqueuedJobsTest < ActiveJob::TestCase
@@ -475,23 +476,23 @@ class EnqueuedJobsTest < ActiveJob::TestCase
def test_assert_enqueued_with_returns
job = assert_enqueued_with(job: LoggingJob) do
- LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3)
+ LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3, keyword: true)
end
assert_instance_of LoggingJob, job
assert_in_delta 5.minutes.from_now, job.scheduled_at, 1
assert_equal "default", job.queue_name
- assert_equal [1, 2, 3], job.arguments
+ assert_equal [1, 2, 3, { keyword: true }], job.arguments
end
def test_assert_enqueued_with_with_no_block_returns
- LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3)
+ LoggingJob.set(wait_until: 5.minutes.from_now).perform_later(1, 2, 3, keyword: true)
job = assert_enqueued_with(job: LoggingJob)
assert_instance_of LoggingJob, job
assert_in_delta 5.minutes.from_now, job.scheduled_at, 1
assert_equal "default", job.queue_name
- assert_equal [1, 2, 3], job.arguments
+ assert_equal [1, 2, 3, { keyword: true }], job.arguments
end
def test_assert_enqueued_with_failure
@@ -503,7 +504,7 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_raise ActiveSupport::TestCase::Assertion do
LoggingJob.perform_later
- assert_enqueued_with(job: LoggingJob) {}
+ assert_enqueued_with(job: LoggingJob) { }
end
error = assert_raise ActiveSupport::TestCase::Assertion do
@@ -537,6 +538,29 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_enqueued_with_selective_args
+ args = ->(job_args) do
+ assert_equal 1, job_args.first[:argument1]
+ assert job_args.first[:argument2].key?(:b)
+ end
+
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_enqueued_with_selective_args_fails
+ args = ->(job_args) do
+ false
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+ end
+
def test_assert_enqueued_with_with_no_block_args
assert_raise ArgumentError do
NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
@@ -555,6 +579,12 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_enqueued_with(job: HelloJob, at: Date.tomorrow.noon)
end
+ def test_assert_enqueued_with_with_hash_arg
+ assert_enqueued_with(job: MultipleKwargsJob, args: [{ argument1: 1, argument2: { a: 1, b: 2 } }]) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
def test_assert_enqueued_with_with_global_id_args
ricardo = Person.new(9)
assert_enqueued_with(job: HelloJob, args: [ricardo]) do
@@ -610,7 +640,7 @@ class EnqueuedJobsTest < ActiveJob::TestCase
end
class PerformedJobsTest < ActiveJob::TestCase
- def test_performed_enqueue_jobs_with_only_option_doesnt_leak_outside_the_block
+ def test_perform_enqueued_jobs_with_only_option_doesnt_leak_outside_the_block
assert_nil queue_adapter.filter
perform_enqueued_jobs only: HelloJob do
assert_equal HelloJob, queue_adapter.filter
@@ -618,7 +648,13 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_nil queue_adapter.filter
end
- def test_performed_enqueue_jobs_with_except_option_doesnt_leak_outside_the_block
+ def test_perform_enqueued_jobs_without_block_with_only_option_doesnt_leak
+ perform_enqueued_jobs only: HelloJob
+
+ assert_nil queue_adapter.filter
+ end
+
+ def test_perform_enqueued_jobs_with_except_option_doesnt_leak_outside_the_block
assert_nil queue_adapter.reject
perform_enqueued_jobs except: HelloJob do
assert_equal HelloJob, queue_adapter.reject
@@ -626,6 +662,150 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_nil queue_adapter.reject
end
+ def test_perform_enqueued_jobs_without_block_with_except_option_doesnt_leak
+ perform_enqueued_jobs except: HelloJob
+
+ assert_nil queue_adapter.reject
+ end
+
+ def test_perform_enqueued_jobs_with_queue_option_doesnt_leak_outside_the_block
+ assert_nil queue_adapter.queue
+ perform_enqueued_jobs queue: :some_queue do
+ assert_equal :some_queue, queue_adapter.queue
+ end
+ assert_nil queue_adapter.queue
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_queue_option_doesnt_leak
+ perform_enqueued_jobs queue: :some_queue
+
+ assert_nil queue_adapter.reject
+ end
+
+ def test_perform_enqueued_jobs_with_block
+ perform_enqueued_jobs do
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+ end
+
+ assert_performed_jobs 2
+ end
+
+ def test_perform_enqueued_jobs_without_block
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 2
+ end
+
+ def test_perform_enqueued_jobs_with_block_with_only_option
+ perform_enqueued_jobs only: LoggingJob do
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+ end
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_only_option
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs only: LoggingJob
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob
+ end
+
+ def test_perform_enqueued_jobs_with_block_with_except_option
+ perform_enqueued_jobs except: HelloJob do
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+ end
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_except_option
+ HelloJob.perform_later("kevin")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs except: HelloJob
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob
+ end
+
+ def test_perform_enqueued_jobs_with_block_with_queue_option
+ perform_enqueued_jobs queue: :some_queue do
+ HelloJob.set(queue: :some_queue).perform_later("kevin")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.perform_later("bogdan")
+ end
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_queue_option
+ HelloJob.set(queue: :some_queue).perform_later("kevin")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs queue: :some_queue
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue
+ end
+
+ def test_perform_enqueued_jobs_with_block_with_only_and_queue_options
+ perform_enqueued_jobs only: HelloJob, queue: :other_queue do
+ HelloJob.set(queue: :some_queue).perform_later("kevin")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("bogdan")
+ end
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: HelloJob, queue: :other_queue
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_only_and_queue_options
+ HelloJob.set(queue: :some_queue).perform_later("kevin")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("bogdan")
+
+ perform_enqueued_jobs only: HelloJob, queue: :other_queue
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: HelloJob, queue: :other_queue
+ end
+
+ def test_perform_enqueued_jobs_with_block_with_except_and_queue_options
+ perform_enqueued_jobs except: HelloJob, queue: :other_queue do
+ HelloJob.set(queue: :other_queue).perform_later("kevin")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("bogdan")
+ end
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob, queue: :other_queue
+ end
+
+ def test_perform_enqueued_jobs_without_block_with_except_and_queue_options
+ HelloJob.set(queue: :other_queue).perform_later("kevin")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("bogdan")
+
+ perform_enqueued_jobs except: HelloJob, queue: :other_queue
+
+ assert_performed_jobs 1
+ assert_performed_jobs 1, only: LoggingJob, queue: :other_queue
+ end
+
def test_assert_performed_jobs
assert_nothing_raised do
assert_performed_jobs 1 do
@@ -731,6 +911,28 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_jobs_without_block_with_only_option
+ HelloJob.perform_later("jeremy")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, only: HelloJob
+ end
+
+ def test_assert_performed_jobs_without_block_with_only_option_failure
+ LoggingJob.perform_later("jeremy")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, only: HelloJob
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
def test_assert_performed_jobs_with_except_option
assert_nothing_raised do
assert_performed_jobs 1, except: LoggingJob do
@@ -740,6 +942,28 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_performed_jobs_without_block_with_except_option
+ HelloJob.perform_later("jeremy")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, except: HelloJob
+ end
+
+ def test_assert_performed_jobs_without_block_with_except_option_failure
+ HelloJob.perform_later("jeremy")
+ HelloJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, except: HelloJob
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
def test_assert_performed_jobs_with_only_and_except_option
error = assert_raise ArgumentError do
assert_performed_jobs 1, only: HelloJob, except: HelloJob do
@@ -751,6 +975,19 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_match(/`:only` and `:except`/, error.message)
end
+ def test_assert_performed_jobs_without_block_with_only_and_except_options
+ error = assert_raise ArgumentError do
+ HelloJob.perform_later("jeremy")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, only: HelloJob, except: HelloJob
+ end
+
+ assert_match(/`:only` and `:except`/, error.message)
+ end
+
def test_assert_performed_jobs_with_only_option_as_array
assert_nothing_raised do
assert_performed_jobs 2, only: [HelloJob, LoggingJob] do
@@ -876,6 +1113,134 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_match(/`:only` and `:except`/, error.message)
end
+ def test_assert_performed_jobs_with_queue_option
+ assert_performed_jobs 1, queue: :some_queue do
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ end
+ end
+
+ def test_assert_performed_jobs_with_queue_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ end
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_performed_jobs_without_block_with_queue_option
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, queue: :some_queue
+ end
+
+ def test_assert_performed_jobs_without_block_with_queue_option_failure
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, queue: :some_queue
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_performed_jobs_with_only_and_queue_options
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ def test_assert_performed_jobs_with_only_and_queue_options_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_performed_jobs_without_block_with_only_and_queue_options
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue
+ end
+
+ def test_assert_performed_jobs_without_block_with_only_and_queue_options_failure
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, only: HelloJob, queue: :some_queue
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_performed_jobs_with_except_and_queue_options
+ assert_performed_jobs 1, except: HelloJob, queue: :other_queue do
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("jeremy")
+ end
+ end
+
+ def test_assert_performed_jobs_with_except_and_queue_options_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, except: HelloJob, queue: :other_queue do
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_performed_jobs_without_block_with_except_and_queue_options
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_performed_jobs 1, except: HelloJob, queue: :other_queue
+ end
+
+ def test_assert_performed_jobs_without_block_with_except_and_queue_options_failure
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ LoggingJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_jobs 1, except: HelloJob, queue: :other_queue
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
def test_assert_no_performed_jobs_with_only_option
assert_nothing_raised do
assert_no_performed_jobs only: HelloJob do
@@ -884,6 +1249,26 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_no_performed_jobs_without_block_with_only_option
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs only: HelloJob
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_only_option_failure
+ HelloJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs only: HelloJob
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
def test_assert_no_performed_jobs_with_except_option
assert_nothing_raised do
assert_no_performed_jobs except: LoggingJob do
@@ -892,6 +1277,26 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
+ def test_assert_no_performed_jobs_without_block_with_except_option
+ HelloJob.perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs except: HelloJob
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_except_option_failure
+ LoggingJob.perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs except: HelloJob
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
def test_assert_no_performed_jobs_with_only_and_except_option
error = assert_raise ArgumentError do
assert_no_performed_jobs only: HelloJob, except: HelloJob do
@@ -902,6 +1307,19 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_match(/`:only` and `:except`/, error.message)
end
+ def test_assert_no_performed_jobs_without_block_with_only_and_except_options
+ error = assert_raise ArgumentError do
+ HelloJob.perform_later("jeremy")
+ LoggingJob.perform_later("bogdan")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs only: HelloJob, except: HelloJob
+ end
+
+ assert_match(/`:only` and `:except`/, error.message)
+ end
+
def test_assert_no_performed_jobs_with_only_option_as_array
assert_nothing_raised do
assert_no_performed_jobs only: [HelloJob, RescueJob] do
@@ -962,24 +1380,165 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_match(/`:only` and `:except`/, error.message)
end
- def test_assert_performed_job
+ def test_assert_no_performed_jobs_with_queue_option
+ assert_no_performed_jobs queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+ end
+ end
+
+ def test_assert_no_performed_jobs_with_queue_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs queue: :some_queue do
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_queue_option
+ HelloJob.set(queue: :other_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs queue: :some_queue
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_queue_option_failure
+ HelloJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs queue: :some_queue
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_with_only_and_queue_options
+ assert_no_performed_jobs only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ def test_assert_no_performed_jobs_with_only_and_queue_options_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs only: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_only_and_queue_options
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs only: HelloJob, queue: :some_queue
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_only_and_queue_options_failure
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs only: HelloJob, queue: :some_queue
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_with_except_and_queue_options
+ assert_no_performed_jobs except: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("jeremy")
+ end
+ end
+
+ def test_assert_no_performed_jobs_with_except_and_queue_options_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs except: HelloJob, queue: :some_queue do
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_except_and_queue_options
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :other_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ assert_no_performed_jobs except: HelloJob, queue: :some_queue
+ end
+
+ def test_assert_no_performed_jobs_without_block_with_except_and_queue_options_failure
+ HelloJob.set(queue: :other_queue).perform_later("bogdan")
+ HelloJob.set(queue: :some_queue).perform_later("bogdan")
+ LoggingJob.set(queue: :some_queue).perform_later("jeremy")
+
+ perform_enqueued_jobs
+
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_performed_jobs except: HelloJob, queue: :some_queue
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
+ def test_assert_performed_with
assert_performed_with(job: NestedJob, queue: "default") do
NestedJob.perform_later
end
end
- def test_assert_performed_job_returns
- job = assert_performed_with(job: NestedJob, queue: "default") do
- NestedJob.perform_later
+ def test_assert_performed_with_without_block
+ NestedJob.perform_later
+
+ perform_enqueued_jobs
+
+ assert_performed_with(job: NestedJob, queue: "default")
+ end
+
+ def test_assert_performed_with_returns
+ job = assert_performed_with(job: LoggingJob, queue: "default") do
+ LoggingJob.perform_later(keyword: :sym)
end
- assert_instance_of NestedJob, job
+ assert_instance_of LoggingJob, job
assert_nil job.scheduled_at
- assert_equal [], job.arguments
+ assert_equal [{ keyword: :sym }], job.arguments
assert_equal "default", job.queue_name
end
- def test_assert_performed_job_failure
+ def test_assert_performed_with_without_block_returns
+ LoggingJob.perform_later(keyword: :sym)
+
+ perform_enqueued_jobs
+
+ job = assert_performed_with(job: LoggingJob, queue: "default")
+
+ assert_instance_of LoggingJob, job
+ assert_nil job.scheduled_at
+ assert_equal [{ keyword: :sym }], job.arguments
+ assert_equal "default", job.queue_name
+ end
+
+ def test_assert_performed_with_failure
assert_raise ActiveSupport::TestCase::Assertion do
assert_performed_with(job: LoggingJob) do
HelloJob.perform_later
@@ -993,7 +1552,23 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
- def test_assert_performed_job_with_at_option
+ def test_assert_performed_with_without_block_failure
+ HelloJob.perform_later
+
+ perform_enqueued_jobs
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: LoggingJob)
+ end
+
+ HelloJob.set(queue: "important").perform_later
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: HelloJob, queue: "low")
+ end
+ end
+
+ def test_assert_performed_with_with_at_option
assert_performed_with(job: HelloJob, at: Date.tomorrow.noon) do
HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
end
@@ -1005,14 +1580,66 @@ class PerformedJobsTest < ActiveJob::TestCase
end
end
- def test_assert_performed_job_with_global_id_args
+ def test_assert_performed_with_without_block_with_at_option
+ HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
+
+ perform_enqueued_jobs
+
+ assert_performed_with(job: HelloJob, at: Date.tomorrow.noon)
+
+ HelloJob.set(wait_until: Date.tomorrow.noon).perform_later
+
+ perform_enqueued_jobs
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: HelloJob, at: Date.today.noon)
+ end
+ end
+
+ def test_assert_performed_with_with_hash_arg
+ assert_performed_with(job: MultipleKwargsJob, args: [{ argument1: 1, argument2: { a: 1, b: 2 } }]) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_performed_with_selective_args
+ args = ->(job_args) do
+ assert_equal 1, job_args.first[:argument1]
+ assert job_args.first[:argument2].key?(:b)
+ end
+
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+
+ def test_assert_performed_with_selective_args_fails
+ args = ->(job_args) do
+ false
+ end
+
+ assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: MultipleKwargsJob, args: args) do
+ MultipleKwargsJob.perform_later(argument2: { b: 2, a: 1 }, argument1: 1)
+ end
+ end
+ end
+
+ def test_assert_performed_with_with_global_id_args
ricardo = Person.new(9)
assert_performed_with(job: HelloJob, args: [ricardo]) do
HelloJob.perform_later(ricardo)
end
end
- def test_assert_performed_job_failure_with_global_id_args
+ def test_assert_performed_with_without_bllock_with_global_id_args
+ ricardo = Person.new(9)
+ HelloJob.perform_later(ricardo)
+ perform_enqueued_jobs
+ assert_performed_with(job: HelloJob, args: [ricardo])
+ end
+
+ def test_assert_performed_with_failure_with_global_id_args
ricardo = Person.new(9)
wilma = Person.new(11)
error = assert_raise ActiveSupport::TestCase::Assertion do
@@ -1024,7 +1651,19 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_equal "No performed job found with {:job=>HelloJob, :args=>[#{wilma.inspect}]}", error.message
end
- def test_assert_performed_job_does_not_change_jobs_count
+ def test_assert_performed_with_without_block_failure_with_global_id_args
+ ricardo = Person.new(9)
+ wilma = Person.new(11)
+ HelloJob.perform_later(ricardo)
+ perform_enqueued_jobs
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_performed_with(job: HelloJob, args: [wilma])
+ end
+
+ assert_equal "No performed job found with {:job=>HelloJob, :args=>[#{wilma.inspect}]}", error.message
+ end
+
+ def test_assert_performed_with_does_not_change_jobs_count
assert_performed_with(job: HelloJob) do
HelloJob.perform_later
end
@@ -1035,6 +1674,18 @@ class PerformedJobsTest < ActiveJob::TestCase
assert_equal 2, queue_adapter.performed_jobs.count
end
+
+ def test_assert_performed_with_without_block_does_not_change_jobs_count
+ HelloJob.perform_later
+ perform_enqueued_jobs
+ assert_performed_with(job: HelloJob)
+
+ perform_enqueued_jobs
+ HelloJob.perform_later
+ assert_performed_with(job: HelloJob)
+
+ assert_equal 2, queue_adapter.performed_jobs.count
+ end
end
class OverrideQueueAdapterTest < ActiveJob::TestCase
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 32afb5ca62..96253773c7 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -137,4 +137,16 @@ class QueuingTest < ActiveSupport::TestCase
assert job_executed "#{@id}.2"
assert job_executed_at("#{@id}.2") < job_executed_at("#{@id}.1")
end
+
+ test "should run job with higher priority first in Backburner" do
+ skip unless adapter_is?(:backburner)
+
+ jobs_manager.tube.pause(3)
+ TestJob.set(priority: 20).perform_later "#{@id}.1"
+ TestJob.set(priority: 10).perform_later "#{@id}.2"
+ wait_for_jobs_to_finish_for(10.seconds)
+ assert job_executed "#{@id}.1"
+ assert job_executed "#{@id}.2"
+ assert job_executed_at("#{@id}.2") < job_executed_at("#{@id}.1")
+ end
end
diff --git a/activejob/test/jobs/multiple_kwargs_job.rb b/activejob/test/jobs/multiple_kwargs_job.rb
new file mode 100644
index 0000000000..b355c4ce1a
--- /dev/null
+++ b/activejob/test/jobs/multiple_kwargs_job.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "../support/job_buffer"
+
+class MultipleKwargsJob < ActiveJob::Base
+ def perform(argument1:, argument2:)
+ JobBuffer.add("Job with argument1: #{argument1}, argument2: #{argument2}")
+ end
+end
diff --git a/activejob/test/jobs/retry_job.rb b/activejob/test/jobs/retry_job.rb
index 1383fffd7d..68dc17e16c 100644
--- a/activejob/test/jobs/retry_job.rb
+++ b/activejob/test/jobs/retry_job.rb
@@ -24,6 +24,7 @@ class RetryJob < ActiveJob::Base
retry_on ExponentialWaitTenAttemptsError, wait: :exponentially_longer, attempts: 10
retry_on CustomWaitTenAttemptsError, wait: ->(executions) { executions * 2 }, attempts: 10
retry_on(CustomCatchError) { |job, error| JobBuffer.add("Dealt with a job that failed to retry in a custom way after #{job.arguments.second} attempts. Message: #{error.message}") }
+ retry_on(ActiveJob::DeserializationError) { |job, error| JobBuffer.add("Raised #{error.class} for the #{job.executions} time") }
discard_on DiscardableError
discard_on FirstDiscardableErrorOfTwo, SecondDiscardableErrorOfTwo
diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb
index eb179011d9..1163ae8178 100644
--- a/activejob/test/support/integration/adapters/backburner.rb
+++ b/activejob/test/support/integration/adapters/backburner.rb
@@ -8,7 +8,8 @@ module BackburnerJobsManager
end
unless can_run?
puts "Cannot run integration tests for backburner. To be able to run integration tests for backburner you need to install and start beanstalkd.\n"
- exit
+ status = ENV["CI"] ? false : true
+ exit status
end
end
diff --git a/activejob/test/support/integration/adapters/que.rb b/activejob/test/support/integration/adapters/que.rb
index 2a771b08c7..f231e5e12d 100644
--- a/activejob/test/support/integration/adapters/que.rb
+++ b/activejob/test/support/integration/adapters/que.rb
@@ -18,8 +18,8 @@ module QueJobsManager
user = uri.user || ENV["USER"]
pass = uri.password
db = uri.path[1..-1]
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database if exists "#{db}"' -U #{user} -t template1}
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'drop database if exists "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'create database "#{db}"' -U #{user} -t template1}
Que.connection = Sequel.connect(que_url)
Que.migrate!
@@ -32,7 +32,8 @@ module QueJobsManager
rescue Sequel::DatabaseConnectionError
puts "Cannot run integration tests for que. To be able to run integration tests for que you need to install and start postgresql.\n"
- exit
+ status = ENV["CI"] ? false : true
+ exit status
end
def stop_workers
diff --git a/activejob/test/support/integration/adapters/queue_classic.rb b/activejob/test/support/integration/adapters/queue_classic.rb
index 1b0685a971..2b5375461a 100644
--- a/activejob/test/support/integration/adapters/queue_classic.rb
+++ b/activejob/test/support/integration/adapters/queue_classic.rb
@@ -17,8 +17,8 @@ module QueueClassicJobsManager
user = uri.user || ENV["USER"]
pass = uri.password
db = uri.path[1..-1]
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'drop database if exists "#{db}"' -U #{user} -t template1}
- %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -c 'create database "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'drop database if exists "#{db}"' -U #{user} -t template1}
+ %x{#{"PGPASSWORD=\"#{pass}\"" if pass} psql -X -c 'create database "#{db}"' -U #{user} -t template1}
QC::Setup.create
QC.default_conn_adapter.disconnect
@@ -30,7 +30,8 @@ module QueueClassicJobsManager
rescue PG::ConnectionBad
puts "Cannot run integration tests for queue_classic. To be able to run integration tests for queue_classic you need to install and start postgresql.\n"
- exit
+ status = ENV["CI"] ? false : true
+ exit status
end
def stop_workers
diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb
index 965e6e2e6c..eb8d4cc2d5 100644
--- a/activejob/test/support/integration/adapters/sneakers.rb
+++ b/activejob/test/support/integration/adapters/sneakers.rb
@@ -1,19 +1,8 @@
# frozen_string_literal: true
require "sneakers/runner"
-require "sneakers/publisher"
require "timeout"
-module Sneakers
- class Publisher
- def safe_ensure_connected
- @mutex.synchronize do
- ensure_connection! unless connected?
- end
- end
- end
-end
-
module SneakersJobsManager
def setup
ActiveJob::Base.queue_adapter = :sneakers
@@ -29,7 +18,8 @@ module SneakersJobsManager
log: Rails.root.join("log/sneakers.log").to_s
unless can_run?
puts "Cannot run integration tests for sneakers. To be able to run integration tests for sneakers you need to install and start rabbitmq.\n"
- exit
+ status = ENV["CI"] ? false : true
+ exit status
end
end
@@ -79,7 +69,7 @@ module SneakersJobsManager
def bunny_publisher
@bunny_publisher ||= begin
p = ActiveJob::QueueAdapters::SneakersAdapter::JobWrapper.send(:publisher)
- p.safe_ensure_connected
+ p.ensure_connection!
p
end
end
diff --git a/activejob/test/support/integration/helper.rb b/activejob/test/support/integration/helper.rb
index a02d874e2e..c5fa2b136f 100644
--- a/activejob/test/support/integration/helper.rb
+++ b/activejob/test/support/integration/helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-puts "\n\n*** rake aj:integration:#{ENV['AJ_ADAPTER']} ***\n"
+puts "\n\n*** rake test:integration:#{ENV['AJ_ADAPTER']} ***\n"
ENV["RAILS_ENV"] = "test"
ActiveJob::Base.queue_name_prefix = nil