Module: Wurk::Testing

Defined in:
lib/wurk/testing.rb

Overview

Sidekiq::Testing-compatible test harness (aliased to Sidekiq::Testing). Three modes control how Wurk::Client#raw_push behaves:

:disable — real Redis push (the default; production behavior)
:fake    — payloads collected in the in-memory Wurk::Queues store
:inline  — jobs executed synchronously the instant they're pushed

A block form switches the mode for the duration of the block on the current thread only (fake! { ... }); the no-block form sets it process-globally.

Spec: docs/target/sidekiq-free.md §24.

Defined Under Namespace

Classes: EmptyQueueError, TestModeAlreadySetError

Constant Summary collapse

SUBCLASS =

Walking every class meets classes that redefine these with other arguments.

::Module.instance_method(:<)
CLASS_NAME =
::Module.instance_method(:name)
THREAD_KEY =
:__wurk_testing_mode

Class Method Summary collapse

Class Method Details

.__set_test_mode(new_mode, &block) ⇒ Object

Block → thread-local for the block's duration; no block → global. Nesting block forms (fake! { inline! { ... } }) is rejected, matching Sidekiq 8.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/wurk/testing.rb', line 48

def __set_test_mode(new_mode, &block)
  return @mode = new_mode unless block

  if ::Thread.current[THREAD_KEY]
    raise TestModeAlreadySetError, 'Nested Sidekiq::Testing block modes are not allowed'
  end

  ::Thread.current[THREAD_KEY] = new_mode
  begin
    block.call
  ensure
    ::Thread.current[THREAD_KEY] = nil
  end
end

.deprecated_require! ⇒ Object

require "sidekiq/testing" flips Sidekiq into fake mode as a side effect; a migrated suite that never calls fake! would otherwise push into real Redis while every MyJob.jobs assertion came back empty. Deprecated in Sidekiq 8 in favour of Sidekiq.testing! — hence the warning. A mode already chosen (globally or for this thread) means the suite is explicit: don't override it, and don't warn again.



82
83
84
85
86
87
88
# File 'lib/wurk/testing.rb', line 82

def deprecated_require!
  return if ::Thread.current[THREAD_KEY] || @mode

  ::Kernel.warn '⛔️ `require "sidekiq/testing"` is deprecated and will be removed in Sidekiq 9.0. ' \
                'Use `Sidekiq.testing!(:fake)` instead.'
  fake!
end

.disable! ⇒ Object



30
# File 'lib/wurk/testing.rb', line 30

def disable!(&) = __set_test_mode(:disable, &)

.disabled? ⇒ Boolean

Returns:

  • (Boolean)


34
# File 'lib/wurk/testing.rb', line 34

def disabled? = mode == :disable

.dispatch_push(payloads) ⇒ Object

Route a push through the active test mode (only called when enabled?).



93
94
95
# File 'lib/wurk/testing.rb', line 93

def dispatch_push(payloads)
  inline? ? inline_push(payloads) : fake_push(payloads)
end

.drain_all ⇒ Object

Run every fake job across all classes until the store is empty.



139
140
141
142
143
144
145
146
# File 'lib/wurk/testing.rb', line 139

def drain_all
  count = 0
  while (job = ::Wurk::Queues.shift_any)
    ::Object.const_get(job['class'].to_s).process_job(job)
    count += 1
  end
  count
end

.enabled? ⇒ Boolean

Returns:

  • (Boolean)


35
# File 'lib/wurk/testing.rb', line 35

def enabled?  = !disabled?

.fake! ⇒ Object



31
# File 'lib/wurk/testing.rb', line 31

def fake!(&)    = __set_test_mode(:fake, &)

.fake? ⇒ Boolean

Returns:

  • (Boolean)


36
# File 'lib/wurk/testing.rb', line 36

def fake?     = mode == :fake

.fake_push(payloads) ⇒ Object

Collect payloads into the in-memory store. enqueued_at is stamped now unless the job is scheduled (at), mirroring the real client. Each payload goes through a JSON round-trip first, as upstream does, so a test sees exactly what production would read back from Redis: string keys, JSON-native args — not the symbols or objects it pushed.



102
103
104
105
106
107
108
109
110
# File 'lib/wurk/testing.rb', line 102

def fake_push(payloads)
  now = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
  payloads.each do |payload|
    job = round_trip(payload)
    job['enqueued_at'] = now unless job['at']
    ::Wurk::Queues.push(job['queue'], job['class'], job)
  end
  payloads.last['jid']
end

.inline! ⇒ Object



32
# File 'lib/wurk/testing.rb', line 32

def inline!(&)  = __set_test_mode(:inline, &)

.inline? ⇒ Boolean

Returns:

  • (Boolean)


37
# File 'lib/wurk/testing.rb', line 37

def inline?   = mode == :inline

.inline_push(payloads) ⇒ Object

Execute each payload immediately through the inline server chain, after the same JSON round-trip as #fake_push.



114
115
116
117
118
119
120
# File 'lib/wurk/testing.rb', line 114

def inline_push(payloads)
  payloads.each do |payload|
    job = round_trip(payload)
    ::Object.const_get(job['class'].to_s).process_job(job)
  end
  payloads.last['jid']
end

.mode ⇒ Object

Thread-local override (set by a block) wins over the global mode, so a fake! { ... } block is isolated to the calling thread.



41
42
43
# File 'lib/wurk/testing.rb', line 41

def mode
  ::Thread.current[THREAD_KEY] || @mode || :disable
end

.round_trip(payload) ⇒ Object



122
# File 'lib/wurk/testing.rb', line 122

def round_trip(payload) = ::Wurk.load_json(::Wurk.dump_json(payload))

.server_middleware {|@server_middleware| ... } ⇒ Object

In-process server-middleware chain used by :inline mode. Empty by default — configure with Sidekiq::Testing.server_middleware { |c| ... }. Deliberately NOT the globally configured chain: Sidekiq builds a fresh empty chain here too, and inline tests that suddenly ran batch/limiter/ metrics middleware would diverge from a migrated suite's expectations. perform_inline is the explicit-execution path and does use the real chains (see Wurk::Worker::Setter#perform_inline).

Yields:



70
71
72
73
74
# File 'lib/wurk/testing.rb', line 70

def server_middleware
  @server_middleware ||= ::Wurk::Middleware::Chain.new(::Wurk.configuration)
  yield @server_middleware if block_given?
  @server_middleware
end

.unconsumed_job_queues(listened: nil, config_file: nil) ⇒ Object

Guard test for a queue nothing listens to: { queue => [job classes] } for every loaded job whose queue is outside listened, so a worker would never fetch it. Eager-load the app first; only loaded classes are seen. An ActiveJob whose queue_as block needs arguments cannot be resolved here: it is left out, with a warning naming it. listened takes what worker config takes ('critical,2', ['critical', 2]).

Rails.application.eager_load!
assert_empty Wurk::Testing.unconsumed_job_queues(config_file: 'config/wurk.yml')


133
134
135
136
# File 'lib/wurk/testing.rb', line 133

def unconsumed_job_queues(listened: nil, config_file: nil)
  listening = listened || (config_file ? queues_in_config(config_file) : ::Wurk.configuration.queues)
  job_queues.except(*queue_names(listening))
end