123456789_123456789_123456789_123456789_123456789_

Class: RSpec::Rails::Matchers::ActiveJob::Base Private

Do not use. This class is for internal use only.
Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
RSpec::Rails::Matchers::HaveEnqueuedMail, RSpec::Rails::Matchers::ActiveJob::HaveBeenEnqueued, RSpec::Rails::Matchers::ActiveJob::HaveBeenPerformed, RSpec::Rails::Matchers::ActiveJob::HaveEnqueuedJob, RSpec::Rails::Matchers::ActiveJob::HavePerformedJob
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: RSpec::Rails::Matchers::BaseMatcher
Defined in: rspec-rails/lib/rspec/rails/matchers/active_job.rb

Overview

rubocop: disable Metrics/ClassLength

Constant Summary

::RSpec::Rails::Matchers::BaseMatcher - Inherited

UNDEFINED

Class Method Summary

::RSpec::Rails::Matchers::BaseMatcher - Inherited

.matcher_name, .new,
.underscore

Borrowed from ActiveSupport.

Instance Attribute Summary

::RSpec::Rails::Matchers::BaseMatcher - Inherited

#actual,
#diffable?

::RSpec::Rails::Matchers are not diffable by default.

#expected, #expects_call_stack_jump?, #matcher_name, #matcher_name=, #rescued_exception,
#supports_block_expectations?

Most matchers are value matchers (i.e. meant to work with ‘expect(value)`) rather than block matchers (i.e. meant to work with `expect { }`), so this defaults to false.

Instance Method Summary

::RSpec::Rails::Matchers::BaseMatcher - Inherited

#actual_formatted,
#description

Generates a description using ::RSpec::Matchers::EnglishPhrasing.

#expected_formatted,
#match_unless_raises

Used to wrap a block of code that will indicate failure by raising one of the named exceptions.

#matches?

Indicates if the match is successful.

#assert_ivars, #present_ivars

::RSpec::Rails::Matchers::BaseMatcher::DefaultFailureMessages - Included

#failure_message

Provides a good generic failure message.

#failure_message_when_negated

Provides a good generic negative failure message.

::RSpec::Rails::Matchers::BaseMatcher::HashFormatting - Included

#improve_hash_formatting

‘{ :a => 5, :b => 2 }.inspect` produces:

::RSpec::Matchers::Composable - Included

#&
#===

Delegates to #matches?.

#and

Creates a compound and expectation.

#description_of

Returns the description of the given object in a way that is aware of composed matchers.

#or

Creates a compound or expectation.

#values_match?

This provides a generic way to fuzzy-match an expected value against an actual value.

#|
#should_enumerate?

We should enumerate arrays as long as they are not recursive.

#surface_descriptions_in

Transforms the given data structure (typically a hash or array) into a new data structure that, when #inspect is called on it, will provide descriptions of any contained matchers rather than the normal #inspect output.

#unreadable_io?,
#with_matchers_cloned

Historically, a single matcher instance was only checked against a single value.

Instance Attribute Details

#skip_signature_verification?Boolean (readonly, private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 194

def skip_signature_verification?
  !RSpec::Mocks.configuration.verify_partial_doubles? ||
    RSpec::Mocks.configuration.temporarily_suppress_partial_double_verification
end

#supports_block_expectations?Boolean (readonly)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 104

def supports_block_expectations?
  true
end

Instance Method Details

#arguments_match?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 170

def arguments_match?(job)
  if @args.any?
    args = serialize_and_deserialize_arguments(@args)
    deserialized_args = deserialize_arguments(job)
    RSpec::Mocks::ArgumentListMatcher.new(*args).args_match?(*deserialized_args)
  else
    true
  end
end

#at(time_or_date)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 39

def at(time_or_date)
  case time_or_date
  when Time then @at = Time.at(time_or_date.to_f)
  else
    @at = time_or_date
  end
  self
end

#at_least(count)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 53

def at_least(count)
  set_expected_number(:at_least, count)
  self
end

#at_match?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 220

def at_match?(job)
  return true unless @at
  return job[:at].nil? if @at == :no_wait
  return false unless job[:at]

  scheduled_at = Time.at(job[:at])
  values_match?(@at, scheduled_at) || check_for_inprecise_value(scheduled_at)
end

#at_most(count)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 58

def at_most(count)
  set_expected_number(:at_most, count)
  self
end

#at_priority(priority)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 34

def at_priority(priority)
  @priority = priority.to_i
  self
end

#base_job_message(job) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 145

def base_job_message(job)
  msg_parts = []
  msg_parts << "with #{deserialize_arguments(job)}" if job[:args].any?
  msg_parts << "on queue #{job[:queue]}" if job[:queue]
  msg_parts << "at #{Time.at(job[:at])}" if job[:at]
  msg_parts <<
    if job[:priority]
      "with priority #{job[:priority]}"
    else
      "with no priority specified"
    end

  "#{job[:job].name} job".tap do |msg|
    msg << " #{msg_parts.join(', ')}" if msg_parts.any?
  end
end

#base_message (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 135

def base_message
  "#{message_expectation_modifier} #{@expected_number} jobs,".tap do |msg|
    msg << " with #{@args}," if @args.any?
    msg << " on queue #{@queue}," if @queue
    msg << " at #{@at.inspect}," if @at
    msg << " with priority #{@priority}," if @priority
    msg << " but #{self.class::MESSAGE_EXPECTATION_ACTION} #{@matching_jobs_count}"
  end
end

#check(jobs) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 110

def check(jobs)
  @matching_jobs, @unmatching_jobs = jobs.partition do |job|
    if matches_constraints?(job)
      args = deserialize_arguments(job)
      @block.call(*args)
      true
    else
      false
    end
  end

  if (signature_mismatch = detect_args_signature_mismatch(@matching_jobs))
    @failure_message = signature_mismatch
    return false
  end

  @matching_jobs_count = @matching_jobs.size

  case @expectation_type
  when :exactly then @expected_number == @matching_jobs_count
  when :at_most then @expected_number >= @matching_jobs_count
  when :at_least then @expected_number <= @matching_jobs_count
  end
end

#check_args_signature_mismatch(job_class, job_method, args) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 199

def check_args_signature_mismatch(job_class, job_method, args)
  signature = Support::MethodSignature.new(job_class.public_instance_method(job_method))
  verifier = Support::StrictSignatureVerifier.new(signature, args)

  unless verifier.valid?
    "Incorrect arguments passed to #{job_class.name}: #{verifier.error_message}"
  end
end

#check_for_inprecise_value(scheduled_at) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 229

def check_for_inprecise_value(scheduled_at)
  return unless Time === @at && values_match?(@at.change(usec: 0), scheduled_at)

  RSpec.warn_with((<<-WARNING).gsub(/^\s+\|/, '').chomp)
  |[WARNING] Your expected `at(...)` value does not match the job scheduled_at value
  |unless microseconds are removed. This precision error often occurs when checking
  |values against `Time.current` / `Time.now` which have usec precision, but Rails
  |uses `n.seconds.from_now` internally which has a usec count of `0`.
  |
  |Use `change(usec: 0)` to correct these values. For example:
  |
  |`Time.current.change(usec: 0)`
  |
  |Note: RSpec cannot do this for you because jobs can be scheduled with usec
  |precision and we do not know whether it is on purpose or not.
  |
  |
  WARNING
  false
end

#deserialize_arguments(job) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 267

def deserialize_arguments(job)
  ::ActiveJob::Arguments.deserialize(job[:args])
rescue ::ActiveJob::DeserializationError
  job[:args]
end

#detect_args_signature_mismatch(jobs) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 180

def detect_args_signature_mismatch(jobs)
  return if skip_signature_verification?

  jobs.each do |job|
    args = deserialize_arguments(job)

    if (signature_mismatch = check_args_signature_mismatch(job.fetch(:job), :perform, args))
      return signature_mismatch
    end
  end

  nil
end

#exactly(count)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 48

def exactly(count)
  set_expected_number(:exactly, count)
  self
end

#failure_message

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 79

def failure_message
  return @failure_message if defined?(@failure_message)

  "expected to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}".tap do |msg|
    if @unmatching_jobs.any?
      msg << "\nQueued jobs:"
      @unmatching_jobs.each do |job|
        msg << "\n  #{base_job_message(job)}"
      end
    end
  end
end

#failure_message_when_negated

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 92

def failure_message_when_negated
  "expected not to #{self.class::FAILURE_MESSAGE_EXPECTATION_ACTION} #{base_message}"
end

#job_matches?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 166

def job_matches?(job)
  @job ? @job == job[:job] : true
end

#matches_constraints?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 162

def matches_constraints?(job)
  job_matches?(job) && arguments_match?(job) && queue_match?(job) && at_match?(job) && priority_match?(job)
end

#message_expectation_modifier

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 96

def message_expectation_modifier
  case @expectation_type
  when :exactly then "exactly"
  when :at_most then "at most"
  when :at_least then "at least"
  end
end

#on_queue(queue)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 29

def on_queue(queue)
  @queue = queue.to_s
  self
end

#once

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 67

def once
  exactly(:once)
end

#priority_match?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 214

def priority_match?(job)
  return true unless @priority

  @priority == job[:priority]
end

#queue_adapter (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 273

def queue_adapter
  ::ActiveJob::Base.queue_adapter
end

#queue_match?(job) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 208

def queue_match?(job)
  return true unless @queue

  @queue == job[:queue]
end

#serialize_and_deserialize_arguments(args) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 260

def serialize_and_deserialize_arguments(args)
  serialized = ::ActiveJob::Arguments.serialize(args)
  ::ActiveJob::Arguments.deserialize(serialized)
rescue ::ActiveJob::SerializationError
  args
end

#set_expected_number(relativity, count) (private)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 250

def set_expected_number(relativity, count)
  @expectation_type = relativity
  @expected_number = case count
                     when :once then 1
                     when :twice then 2
                     when :thrice then 3
                     else Integer(count)
                     end
end

#thrice

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 75

def thrice
  exactly(:thrice)
end

#times

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 63

def times
  self
end

#twice

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 71

def twice
  exactly(:twice)
end

#with(*args, &block)

[ GitHub ]

  
# File 'rspec-rails/lib/rspec/rails/matchers/active_job.rb', line 23

def with(*args, &block)
  @args = args
  @block = block if block.present?
  self
end