qs 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bench/config.qs +4 -27
- data/bench/dispatcher.qs +24 -0
- data/bench/report.rb +80 -10
- data/bench/report.txt +10 -3
- data/bench/setup.rb +55 -0
- data/lib/qs.rb +75 -15
- data/lib/qs/client.rb +73 -22
- data/lib/qs/daemon.rb +21 -21
- data/lib/qs/daemon_data.rb +4 -4
- data/lib/qs/dispatch_job.rb +36 -0
- data/lib/qs/dispatch_job_handler.rb +79 -0
- data/lib/qs/dispatcher_queue.rb +19 -0
- data/lib/qs/error_handler.rb +12 -12
- data/lib/qs/event.rb +82 -0
- data/lib/qs/event_handler.rb +34 -0
- data/lib/qs/event_handler_test_helpers.rb +17 -0
- data/lib/qs/job.rb +19 -31
- data/lib/qs/job_handler.rb +6 -63
- data/lib/qs/{test_helpers.rb → job_handler_test_helpers.rb} +2 -2
- data/lib/qs/message.rb +29 -0
- data/lib/qs/message_handler.rb +84 -0
- data/lib/qs/payload.rb +98 -0
- data/lib/qs/payload_handler.rb +106 -54
- data/lib/qs/queue.rb +39 -6
- data/lib/qs/queue_item.rb +33 -0
- data/lib/qs/route.rb +7 -7
- data/lib/qs/runner.rb +6 -5
- data/lib/qs/test_runner.rb +41 -13
- data/lib/qs/version.rb +1 -1
- data/qs.gemspec +1 -1
- data/test/helper.rb +1 -1
- data/test/support/app_daemon.rb +77 -11
- data/test/support/factory.rb +34 -0
- data/test/system/daemon_tests.rb +146 -77
- data/test/system/queue_tests.rb +87 -0
- data/test/unit/client_tests.rb +184 -45
- data/test/unit/daemon_data_tests.rb +4 -4
- data/test/unit/daemon_tests.rb +32 -32
- data/test/unit/dispatch_job_handler_tests.rb +163 -0
- data/test/unit/dispatch_job_tests.rb +75 -0
- data/test/unit/dispatcher_queue_tests.rb +42 -0
- data/test/unit/error_handler_tests.rb +9 -9
- data/test/unit/event_handler_test_helpers_tests.rb +55 -0
- data/test/unit/event_handler_tests.rb +63 -0
- data/test/unit/event_tests.rb +162 -0
- data/test/unit/{test_helper_tests.rb → job_handler_test_helper_tests.rb} +13 -19
- data/test/unit/job_handler_tests.rb +17 -210
- data/test/unit/job_tests.rb +49 -79
- data/test/unit/message_handler_tests.rb +235 -0
- data/test/unit/message_tests.rb +64 -0
- data/test/unit/payload_handler_tests.rb +285 -86
- data/test/unit/payload_tests.rb +139 -0
- data/test/unit/qs_runner_tests.rb +6 -6
- data/test/unit/qs_tests.rb +167 -28
- data/test/unit/queue_item_tests.rb +51 -0
- data/test/unit/queue_tests.rb +126 -18
- data/test/unit/route_tests.rb +12 -13
- data/test/unit/runner_tests.rb +10 -10
- data/test/unit/test_runner_tests.rb +117 -24
- metadata +51 -21
- data/bench/queue.rb +0 -8
- data/lib/qs/redis_item.rb +0 -33
- data/test/unit/redis_item_tests.rb +0 -49
@@ -59,14 +59,14 @@ class Qs::DaemonData
|
|
59
59
|
end
|
60
60
|
|
61
61
|
should "build a routes lookup hash" do
|
62
|
-
expected = @routes.inject({}){ |h, r| h.merge(r.
|
62
|
+
expected = @routes.inject({}){ |h, r| h.merge(r.id => r) }
|
63
63
|
assert_equal expected, subject.routes
|
64
64
|
end
|
65
65
|
|
66
66
|
should "allow looking up a route using `route_for`" do
|
67
|
-
|
68
|
-
route = subject.route_for(
|
69
|
-
assert_equal
|
67
|
+
exp_route = @routes.choice
|
68
|
+
route = subject.route_for(exp_route.id)
|
69
|
+
assert_equal exp_route, route
|
70
70
|
end
|
71
71
|
|
72
72
|
should "raise a not found error using `route_for` with an invalid name" do
|
data/test/unit/daemon_tests.rb
CHANGED
@@ -6,7 +6,7 @@ require 'ns-options/assert_macros'
|
|
6
6
|
require 'thread'
|
7
7
|
require 'qs/client'
|
8
8
|
require 'qs/queue'
|
9
|
-
require 'qs/
|
9
|
+
require 'qs/queue_item'
|
10
10
|
|
11
11
|
module Qs::Daemon
|
12
12
|
|
@@ -275,8 +275,8 @@ module Qs::Daemon
|
|
275
275
|
@daemon = @daemon_class.new
|
276
276
|
@thread = @daemon.start
|
277
277
|
|
278
|
-
@
|
279
|
-
@client_spy.append(@queue.redis_key, @
|
278
|
+
@encoded_payload = Factory.string
|
279
|
+
@client_spy.append(@queue.redis_key, @encoded_payload)
|
280
280
|
end
|
281
281
|
subject{ @daemon }
|
282
282
|
|
@@ -285,7 +285,7 @@ module Qs::Daemon
|
|
285
285
|
assert_equal :block_dequeue, call.command
|
286
286
|
exp = [subject.signals_redis_key, subject.queue_redis_keys, 0].flatten
|
287
287
|
assert_equal exp, call.args
|
288
|
-
exp = Qs::
|
288
|
+
exp = Qs::QueueItem.new(@queue.redis_key, @encoded_payload)
|
289
289
|
assert_equal exp, @worker_pool_spy.work_items.first
|
290
290
|
end
|
291
291
|
|
@@ -353,15 +353,15 @@ module Qs::Daemon
|
|
353
353
|
@daemon = @daemon_class.new
|
354
354
|
@thread = @daemon.start
|
355
355
|
|
356
|
-
@
|
357
|
-
@worker_pool_spy.work_proc.call(@
|
356
|
+
@queue_item = Qs::QueueItem.new(Factory.string, Factory.string)
|
357
|
+
@worker_pool_spy.work_proc.call(@queue_item)
|
358
358
|
end
|
359
359
|
subject{ @daemon }
|
360
360
|
|
361
361
|
should "build and run a payload handler" do
|
362
362
|
assert_not_nil @ph_spy
|
363
363
|
assert_equal subject.daemon_data, @ph_spy.daemon_data
|
364
|
-
assert_equal @
|
364
|
+
assert_equal @queue_item, @ph_spy.queue_item
|
365
365
|
end
|
366
366
|
|
367
367
|
end
|
@@ -373,27 +373,27 @@ module Qs::Daemon
|
|
373
373
|
@thread = @daemon.start
|
374
374
|
|
375
375
|
@exception = Factory.exception
|
376
|
-
@
|
376
|
+
@queue_item = Qs::QueueItem.new(Factory.string, Factory.string)
|
377
377
|
@callback = @worker_pool_spy.on_worker_error_callbacks.first
|
378
378
|
end
|
379
379
|
subject{ @daemon }
|
380
380
|
|
381
|
-
should "requeue the
|
382
|
-
@
|
383
|
-
@callback.call('worker', @exception, @
|
381
|
+
should "requeue the queue item if it wasn't started" do
|
382
|
+
@queue_item.started = false
|
383
|
+
@callback.call('worker', @exception, @queue_item)
|
384
384
|
call = @client_spy.calls.detect{ |c| c.command == :prepend }
|
385
385
|
assert_not_nil call
|
386
|
-
assert_equal @
|
387
|
-
assert_equal @
|
386
|
+
assert_equal @queue_item.queue_redis_key, call.args.first
|
387
|
+
assert_equal @queue_item.encoded_payload, call.args.last
|
388
388
|
end
|
389
389
|
|
390
|
-
should "not requeue the
|
391
|
-
@
|
392
|
-
@callback.call('worker', @exception, @
|
390
|
+
should "not requeue the queue item if it was started" do
|
391
|
+
@queue_item.started = true
|
392
|
+
@callback.call('worker', @exception, @queue_item)
|
393
393
|
assert_nil @client_spy.calls.detect{ |c| c.command == :prepend }
|
394
394
|
end
|
395
395
|
|
396
|
-
should "do nothing if not passed a
|
396
|
+
should "do nothing if not passed a queue item" do
|
397
397
|
assert_nothing_raised{ @callback.call(@exception, nil) }
|
398
398
|
end
|
399
399
|
|
@@ -402,8 +402,8 @@ module Qs::Daemon
|
|
402
402
|
class StopTests < StartTests
|
403
403
|
desc "and then stopped"
|
404
404
|
setup do
|
405
|
-
@
|
406
|
-
@worker_pool_spy.add_work(@
|
405
|
+
@queue_item = Qs::QueueItem.new(@queue.redis_key, Factory.string)
|
406
|
+
@worker_pool_spy.add_work(@queue_item)
|
407
407
|
|
408
408
|
@daemon.stop true
|
409
409
|
end
|
@@ -416,8 +416,8 @@ module Qs::Daemon
|
|
416
416
|
should "requeue any work left on the pool" do
|
417
417
|
call = @client_spy.calls.last
|
418
418
|
assert_equal :prepend, call.command
|
419
|
-
assert_equal @
|
420
|
-
assert_equal @
|
419
|
+
assert_equal @queue_item.queue_redis_key, call.args.first
|
420
|
+
assert_equal @queue_item.encoded_payload, call.args.last
|
421
421
|
end
|
422
422
|
|
423
423
|
should "stop the work loop thread" do
|
@@ -449,8 +449,8 @@ module Qs::Daemon
|
|
449
449
|
class HaltTests < StartTests
|
450
450
|
desc "and then halted"
|
451
451
|
setup do
|
452
|
-
@
|
453
|
-
@worker_pool_spy.add_work(@
|
452
|
+
@queue_item = Qs::QueueItem.new(@queue.redis_key, Factory.string)
|
453
|
+
@worker_pool_spy.add_work(@queue_item)
|
454
454
|
|
455
455
|
@daemon.halt true
|
456
456
|
end
|
@@ -463,8 +463,8 @@ module Qs::Daemon
|
|
463
463
|
should "requeue any work left on the pool" do
|
464
464
|
call = @client_spy.calls.last
|
465
465
|
assert_equal :prepend, call.command
|
466
|
-
assert_equal @
|
467
|
-
assert_equal @
|
466
|
+
assert_equal @queue_item.queue_redis_key, call.args.first
|
467
|
+
assert_equal @queue_item.encoded_payload, call.args.last
|
468
468
|
end
|
469
469
|
|
470
470
|
should "stop the work loop thread" do
|
@@ -501,8 +501,8 @@ module Qs::Daemon
|
|
501
501
|
|
502
502
|
# cause the daemon to loop, its sleeping on the original block_dequeue
|
503
503
|
# call that happened before the stub
|
504
|
-
@
|
505
|
-
@client_spy.append(@
|
504
|
+
@queue_item = Qs::QueueItem.new(@queue.redis_key, Factory.string)
|
505
|
+
@client_spy.append(@queue_item.queue_redis_key, @queue_item.encoded_payload)
|
506
506
|
end
|
507
507
|
|
508
508
|
should "shutdown the worker pool" do
|
@@ -513,8 +513,8 @@ module Qs::Daemon
|
|
513
513
|
should "requeue any work left on the pool" do
|
514
514
|
call = @client_spy.calls.last
|
515
515
|
assert_equal :prepend, call.command
|
516
|
-
assert_equal @
|
517
|
-
assert_equal @
|
516
|
+
assert_equal @queue_item.queue_redis_key, call.args.first
|
517
|
+
assert_equal @queue_item.encoded_payload, call.args.last
|
518
518
|
end
|
519
519
|
|
520
520
|
should "stop the work loop thread" do
|
@@ -667,11 +667,11 @@ module Qs::Daemon
|
|
667
667
|
TestHandler = Class.new
|
668
668
|
|
669
669
|
class PayloadHandlerSpy
|
670
|
-
attr_reader :daemon_data, :
|
670
|
+
attr_reader :daemon_data, :queue_item, :run_called
|
671
671
|
|
672
|
-
def initialize(daemon_data,
|
672
|
+
def initialize(daemon_data, queue_item)
|
673
673
|
@daemon_data = daemon_data
|
674
|
-
@
|
674
|
+
@queue_item = queue_item
|
675
675
|
@run_called = false
|
676
676
|
end
|
677
677
|
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'assert'
|
2
|
+
require 'qs/dispatch_job_handler'
|
3
|
+
|
4
|
+
require 'qs/job_handler_test_helpers'
|
5
|
+
|
6
|
+
module Qs::DispatchJobHandler
|
7
|
+
|
8
|
+
class UnitTests < Assert::Context
|
9
|
+
include Qs::JobHandler::TestHelpers
|
10
|
+
|
11
|
+
desc "Qs::DispatchJobHandler"
|
12
|
+
setup do
|
13
|
+
Qs.init
|
14
|
+
@handler_class = Class.new do
|
15
|
+
include Qs::DispatchJobHandler
|
16
|
+
end
|
17
|
+
end
|
18
|
+
teardown do
|
19
|
+
Qs.reset!
|
20
|
+
end
|
21
|
+
subject{ @handler_class }
|
22
|
+
|
23
|
+
should "be a job handler" do
|
24
|
+
assert_includes Qs::JobHandler, subject
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class InitSetupTests < UnitTests
|
30
|
+
desc "when init"
|
31
|
+
setup do
|
32
|
+
@job = Factory.dispatch_job(:publisher => Factory.string)
|
33
|
+
@queue_names = Factory.integer(3).times.map{ Factory.string }
|
34
|
+
Assert.stub(Qs, :event_subscribers){ @queue_names }
|
35
|
+
|
36
|
+
@push_calls = []
|
37
|
+
Assert.stub(Qs, :push){ |*args| @push_calls << PushCall.new(*args) }
|
38
|
+
|
39
|
+
@logger_spy = LoggerSpy.new
|
40
|
+
@runner_args = {
|
41
|
+
:job => @job,
|
42
|
+
:params => @job.params,
|
43
|
+
:logger => @logger_spy
|
44
|
+
}
|
45
|
+
end
|
46
|
+
subject{ @handler }
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class InitTests < InitSetupTests
|
51
|
+
setup do
|
52
|
+
@runner = test_runner(@handler_class, @runner_args)
|
53
|
+
@handler = @runner.handler
|
54
|
+
end
|
55
|
+
|
56
|
+
should "know its event and subscribed queue names" do
|
57
|
+
assert_equal @job.event, subject.event
|
58
|
+
assert_equal @queue_names, subject.subscribed_queue_names
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class RunTests < InitTests
|
64
|
+
desc "and run"
|
65
|
+
setup do
|
66
|
+
@runner.run
|
67
|
+
end
|
68
|
+
|
69
|
+
should "push the events payload to all of the subscribed queue names" do
|
70
|
+
assert_equal @queue_names, @push_calls.map(&:queue_name)
|
71
|
+
exp = Qs::Payload.event_hash(subject.event)
|
72
|
+
assert_equal [exp], @push_calls.map(&:payload).uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
should "log the queues it dispatches to" do
|
76
|
+
exp = [
|
77
|
+
"Dispatching #{subject.event.route_name}",
|
78
|
+
" params: #{subject.event.params.inspect}",
|
79
|
+
" publisher: #{subject.event.publisher}",
|
80
|
+
" published at: #{subject.event.published_at}",
|
81
|
+
"Found #{subject.subscribed_queue_names.size} subscribed queue(s):",
|
82
|
+
@queue_names.map{ |queue_name| " => #{queue_name}" }
|
83
|
+
].flatten.join("\n")
|
84
|
+
assert_equal exp, @logger_spy.messages.join("\n")
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class RunWithDispatchesThatErrorTests < InitSetupTests
|
90
|
+
desc "and run with dispatches that error"
|
91
|
+
setup do
|
92
|
+
@fail_queue_names = Factory.integer(3).times.map{ Factory.string }
|
93
|
+
@dispatch_error = RuntimeError.new(Factory.text)
|
94
|
+
payload_hash = Qs::Payload.event_hash(@job.event)
|
95
|
+
@fail_queue_names.each do |queue_name|
|
96
|
+
Assert.stub(Qs, :push).with(queue_name, payload_hash) do
|
97
|
+
raise @dispatch_error
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@success_queue_names = @queue_names.dup
|
102
|
+
# add the fail queue names to the front to test that they don't cause
|
103
|
+
# the other queues not to be dispatched to
|
104
|
+
@queue_names = @fail_queue_names + @success_queue_names
|
105
|
+
|
106
|
+
@runner = test_runner(@handler_class, @runner_args)
|
107
|
+
@handler = @runner.handler
|
108
|
+
|
109
|
+
@exception = nil
|
110
|
+
begin; @runner.run; rescue => @exception; end
|
111
|
+
end
|
112
|
+
|
113
|
+
should "raise a dispatch error after trying to dispatch to every queue" do
|
114
|
+
assert_equal @success_queue_names, @push_calls.map(&:queue_name)
|
115
|
+
|
116
|
+
assert_instance_of DispatchError, @exception
|
117
|
+
descriptions = @fail_queue_names.map do |queue_name|
|
118
|
+
"#{queue_name} - #{@dispatch_error.class}: #{@dispatch_error.message}"
|
119
|
+
end
|
120
|
+
exp = "#{subject.event.route_name} event wasn't dispatched to:\n" \
|
121
|
+
" #{descriptions.join("\n ")}"
|
122
|
+
assert_equal exp, @exception.message
|
123
|
+
exp = @fail_queue_names.map do |queue_name|
|
124
|
+
FailedDispatch.new(queue_name, @dispatch_error)
|
125
|
+
end
|
126
|
+
assert_equal exp, @exception.failed_dispatches
|
127
|
+
end
|
128
|
+
|
129
|
+
should "log the queues it dispatches to and the errors it encounters" do
|
130
|
+
exp = [
|
131
|
+
"Dispatching #{subject.event.route_name}",
|
132
|
+
" params: #{subject.event.params.inspect}",
|
133
|
+
" publisher: #{subject.event.publisher}",
|
134
|
+
" published at: #{subject.event.published_at}",
|
135
|
+
"Found #{subject.subscribed_queue_names.size} subscribed queue(s):",
|
136
|
+
@fail_queue_names.map{ |queue_name| " => #{queue_name} (failed)" },
|
137
|
+
@success_queue_names.map{ |queue_name| " => #{queue_name}" },
|
138
|
+
"Failed to dispatch the event to #{@fail_queue_names.size} subscribed queues",
|
139
|
+
@fail_queue_names.map do |queue_name|
|
140
|
+
[ queue_name,
|
141
|
+
" #{@dispatch_error.class}: #{@dispatch_error.message}",
|
142
|
+
" #{@dispatch_error.backtrace.first}"
|
143
|
+
]
|
144
|
+
end
|
145
|
+
].flatten.join("\n")
|
146
|
+
assert_equal exp, @logger_spy.messages.join("\n")
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
PushCall = Struct.new(:queue_name, :payload)
|
152
|
+
|
153
|
+
class LoggerSpy
|
154
|
+
attr_reader :messages
|
155
|
+
|
156
|
+
def initialize
|
157
|
+
@messages = []
|
158
|
+
end
|
159
|
+
|
160
|
+
def info(message); @messages << message; end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'assert'
|
2
|
+
require 'qs/dispatch_job'
|
3
|
+
|
4
|
+
require 'qs/event'
|
5
|
+
require 'qs/job'
|
6
|
+
|
7
|
+
class Qs::DispatchJob
|
8
|
+
|
9
|
+
class UnitTests < Assert::Context
|
10
|
+
desc "Qs::DispatchJob"
|
11
|
+
setup do
|
12
|
+
@job_class = Qs::DispatchJob
|
13
|
+
end
|
14
|
+
subject{ @job_class }
|
15
|
+
|
16
|
+
should "be a job" do
|
17
|
+
assert Qs::DispatchJob < Qs::Job
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class InitTests < UnitTests
|
23
|
+
desc "when init"
|
24
|
+
setup do
|
25
|
+
@event_channel = Factory.string
|
26
|
+
@event_name = Factory.string
|
27
|
+
@event_params = { Factory.string => Factory.string }
|
28
|
+
@event_publisher = Factory.string
|
29
|
+
@created_at = Factory.time
|
30
|
+
@job = @job_class.new(@event_channel, @event_name, {
|
31
|
+
:event_params => @event_params,
|
32
|
+
:event_publisher => @event_publisher,
|
33
|
+
:created_at => @created_at
|
34
|
+
})
|
35
|
+
end
|
36
|
+
teardown do
|
37
|
+
Qs.reset!
|
38
|
+
end
|
39
|
+
subject{ @job }
|
40
|
+
|
41
|
+
should have_imeths :event
|
42
|
+
|
43
|
+
should "know its name, params and created at" do
|
44
|
+
assert_equal Qs.dispatcher_job_name, subject.name
|
45
|
+
exp = {
|
46
|
+
'event_channel' => @event_channel,
|
47
|
+
'event_name' => @event_name,
|
48
|
+
'event_params' => @event_params,
|
49
|
+
'event_publisher' => @event_publisher
|
50
|
+
}
|
51
|
+
assert_equal exp, subject.params
|
52
|
+
assert_equal @created_at, subject.created_at
|
53
|
+
end
|
54
|
+
|
55
|
+
should "default its event params and event publisher" do
|
56
|
+
Qs.config.event_publisher = Factory.string
|
57
|
+
job = @job_class.new(@event_channel, @event_name)
|
58
|
+
assert_equal({}, job.params['event_params'])
|
59
|
+
assert_equal Qs.event_publisher, job.params['event_publisher']
|
60
|
+
end
|
61
|
+
|
62
|
+
should "know how to build an event from its params" do
|
63
|
+
event = subject.event
|
64
|
+
exp = Qs::Event.new(@event_channel, @event_name, {
|
65
|
+
:params => @event_params,
|
66
|
+
:publisher => @event_publisher,
|
67
|
+
:published_at => @created_at
|
68
|
+
})
|
69
|
+
assert_equal exp, event
|
70
|
+
assert_same event, subject.event
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'assert'
|
2
|
+
require 'qs/dispatcher_queue'
|
3
|
+
|
4
|
+
module Qs::DispatcherQueue
|
5
|
+
|
6
|
+
class UnitTests < Assert::Context
|
7
|
+
desc "Qs::DispatcherQueue"
|
8
|
+
subject{ Qs::DispatcherQueue }
|
9
|
+
|
10
|
+
should have_imeths :new
|
11
|
+
|
12
|
+
should "build a dispatcher queue" do
|
13
|
+
options = {
|
14
|
+
:queue_class => Class.new(Qs::Queue),
|
15
|
+
:queue_name => Factory.string,
|
16
|
+
:job_name => Factory.string,
|
17
|
+
:job_handler_class_name => Factory.string
|
18
|
+
}
|
19
|
+
dispatcher_queue = subject.new(options)
|
20
|
+
assert_instance_of options[:queue_class], dispatcher_queue
|
21
|
+
assert_equal options[:queue_name], dispatcher_queue.name
|
22
|
+
|
23
|
+
route = dispatcher_queue.routes.last
|
24
|
+
assert_instance_of Qs::Route, route
|
25
|
+
exp = Qs::Message::RouteId.new(Qs::Job::PAYLOAD_TYPE, options[:job_name])
|
26
|
+
assert_equal exp, route.id
|
27
|
+
assert_equal options[:job_handler_class_name], route.handler_class_name
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class RunDispatchJobTests < UnitTests
|
33
|
+
desc "RunDispatchJob"
|
34
|
+
subject{ RunDispatchJob }
|
35
|
+
|
36
|
+
should "be a dispatch job handler" do
|
37
|
+
assert_includes Qs::DispatchJobHandler, subject
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|