Module: Zizq::Test

Defined in:
lib/zizq/test.rb,
lib/zizq/test/client.rb,
sig/generated/zizq/test.rbs,
sig/generated/zizq/test/client.rbs

Overview

Test-mode helpers. Activated by setting c.test_mode = true in a Zizq.configure block; Zizq.client then lazily resolves to a Zizq::Test::Client that buffers enqueues instead of dispatching.

Typical use in a test helper:

Zizq.configure do |c|
c.test_mode = true
end

class MyTestCase
setup { Zizq::Test.reset! }
end

In a test:

def 
# Default buffered mode — assert what was enqueued.
SignupService.new.run
assert_equal 2, Zizq::Test.client.pending_jobs.size

# Drain whatever's pending (handler re-enqueues fall through
# naturally — drain loops until pending is empty).
Zizq::Test.dispatch_enqueued_jobs

# Block form: run the work, then drain. Matches ActiveJob's
# `perform_enqueued_jobs do ... end`.
Zizq::Test.dispatch_enqueued_jobs { SignupService.new.run }

# Filter by queue and/or type when only a subset should fire.
Zizq::Test.dispatch_enqueued_jobs(queue: "emails")
Zizq::Test.dispatch_enqueued_jobs(type: SendEmailJob)
end

Defined Under Namespace

Classes: Client

Class Method Summary collapse

Class Method Details

.clientObject

The active test client. Raises if test mode is not enabled — better to fail loudly than return a stale or wrong client.

Returns:

  • (Object)


60
61
62
63
64
65
66
# File 'lib/zizq/test.rb', line 60

def self.client #: () -> Client
  unless Zizq.configuration.test_mode
    raise Client::NotSupported,
      "Zizq.configuration.test_mode is not enabled; Zizq::Test.client has nothing to manage."
  end
  Zizq.client #: Client
end

.disable!Object

Switch back to the real client. The buffered state is dropped along with the test client (the next Zizq.client access builds a fresh Zizq::Client).

Returns:

  • (Object)


54
55
56
# File 'lib/zizq/test.rb', line 54

def self.disable! #: () -> void
  Zizq.configure { |c| c.test_mode = false }
end

.dispatch_enqueued_jobs(**filters, &block) ⇒ void

This method returns an undefined value.

Dispatch pending jobs through the configured dequeue middleware chain (Zizq.configuration.dequeue_middleware — same path the real worker uses, so any registered middlewares run in tests too), looping until no more pending entries match the filters.

  • No block: drain whatever's pending now.
  • With block: yield first (test code enqueues), then drain.
  • only_queues: / except_queues: — String or Array of Strings.
  • only_types: / except_types: — String, Class, or Array of those (Class names match the serialized-format type via .to_s, so passing an ActiveJob class works directly).
  • filter: — a lambda ->(job) returning truthy to keep an entry. Defaults to "pass all". Combines with the named filters via AND.

Returns the number of jobs dispatched. A block exception propagates without draining; a handler exception during drain transitions that entry to dead and re-raises.

Parameters:

  • filters (Object)


92
93
94
95
# File 'lib/zizq/test.rb', line 92

def self.dispatch_enqueued_jobs(**filters, &block) #: (**untyped) ?{ () -> void } -> Integer
  yield if block
  client.drain(**filters)
end

.enable!Object

Switch Zizq into test mode. After this, Zizq.client resolves to a Zizq::Test::Client that buffers enqueues in memory. Typically called once in a test helper.

Returns:

  • (Object)


47
48
49
# File 'lib/zizq/test.rb', line 47

def self.enable! #: () -> void
  Zizq.configure { |c| c.test_mode = true }
end

.enqueued?(job_class, *args, **kwargs) ⇒ Object

Was a job of job_class enqueued (optionally with matching args)?

Zizq::Test.enqueued?(SendEmailJob)                            # any args
Zizq::Test.enqueued?(SendEmailJob, 42, template: "welcome")   # exact args

With no args, matches by class/type only. With args/kwargs, uses the class's own zizq_serialize to compute the expected payload — so it works for both Zizq::Job and AJ classes (AJ's volatile fields like job_id are ignored, only the arguments subset is compared).

For anything fuzzier (matchers, subset matching, custom predicates), drop down to client.enqueued_jobs(only_types: ..., filter: ->(job) { ... }).

Parameters:

  • job_class (Object)
  • args (Object)
  • kwargs (Object)

Returns:

  • (Object)


132
133
134
# File 'lib/zizq/test.rb', line 132

def self.enqueued?(job_class, *args, **kwargs) #: (Class, *untyped, **untyped) -> bool
  enqueued_count(job_class, *args, **kwargs) > 0
end

.enqueued_count(job_class, *args, **kwargs) ⇒ Object

How many times was a job of job_class enqueued (optionally with matching args)? Same argument semantics as #enqueued?.

Parameters:

  • job_class (Object)
  • args (Object)
  • kwargs (Object)

Returns:

  • (Object)


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/zizq/test.rb', line 138

def self.enqueued_count(job_class, *args, **kwargs) #: (Class, *untyped, **untyped) -> Integer
  type = job_class.to_s
  return enqueued_raw_count(type: type) if args.empty? && kwargs.empty?

  unless job_class.respond_to?(:zizq_serialize)
    raise ArgumentError,
      "#{job_class} doesn't implement zizq_serialize — " \
      "include Zizq::Job or extend Zizq::ActiveJobConfig, " \
      "or use Zizq::Test.enqueued_raw? for raw enqueues."
  end

  expected = job_class.zizq_serialize(*args, **kwargs)
  client.enqueued_jobs(only_types: type).count do |job|
    payloads_equivalent?(expected, job.payload)
  end
end

.enqueued_raw?(queue: nil, type: nil, payload: nil) ⇒ Object

Was a raw job (queue + type + payload) enqueued? Each kwarg is optional; unspecified means "don't filter on this axis."

Zizq::Test.enqueued_raw?(type: "send_email")
Zizq::Test.enqueued_raw?(type: "send_email", payload: { user_id: 42 })
Zizq::Test.enqueued_raw?(queue: "emails", type: "send_email")

Parameters:

  • queue: (Object) (defaults to: nil)
  • type: (Object) (defaults to: nil)
  • payload: (Object) (defaults to: nil)

Returns:

  • (Object)


161
162
163
# File 'lib/zizq/test.rb', line 161

def self.enqueued_raw?(queue: nil, type: nil, payload: nil) #: (?queue: String?, ?type: String?, ?payload: untyped) -> bool
  enqueued_raw_count(queue: queue, type: type, payload: payload) > 0
end

.enqueued_raw_count(queue: nil, type: nil, payload: nil) ⇒ Object

How many raw jobs match (queue + type + payload)? Same argument semantics as #enqueued_raw?.

Parameters:

  • queue: (Object) (defaults to: nil)
  • type: (Object) (defaults to: nil)
  • payload: (Object) (defaults to: nil)

Returns:

  • (Object)


167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/zizq/test.rb', line 167

def self.enqueued_raw_count(queue: nil, type: nil, payload: nil) #: (?queue: String?, ?type: String?, ?payload: untyped) -> Integer
  filters = {}
  filters[:only_queues] = queue if queue
  filters[:only_types]  = type  if type
  unless payload.nil?
    # Normalize the assertion-side payload the same way enqueue
    # normalizes the buffered one, so symbol-keyed test payloads
    # still match the (string-keyed) wire-format buffer.
    normalized = Client.normalize_payload(payload)
    filters[:filter] = ->(job) { job.payload == normalized }
  end
  client.enqueued_jobs(**filters).size
end

.payloads_equivalent?(expected, actual) ⇒ Object

Heuristic payload comparison that handles both Zizq::Job's serialized format ({"args" => [...], "kwargs" => {...}}) and ActiveJob's ({"job_class" => ..., "arguments" => [...], "job_id" => ..., "enqueued_at" => ..., ...}). The AJ shape always has an "arguments" key while Zizq::Job's doesn't, so we use that to pick whether to compare the full hash or only the arguments subset (dropping AJ's volatile per-enqueue fields).

Parameters:

  • expected (Object)
  • actual (Object)

Returns:

  • (Object)


188
189
190
191
192
193
194
# File 'lib/zizq/test.rb', line 188

def self.payloads_equivalent?(expected, actual) #: (untyped, untyped) -> bool
  if expected.is_a?(Hash) && expected.key?("arguments")
    actual.is_a?(Hash) && actual["arguments"] == expected["arguments"]
  else
    actual == expected
  end
end

.reset!Object

Reset buffered state between tests. Keeps the configured test_mode flag.

Returns:

  • (Object)


70
71
72
# File 'lib/zizq/test.rb', line 70

def self.reset! #: () -> void
  client.clear!
end