qs 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/test/system/daemon_tests.rb
CHANGED
@@ -11,26 +11,39 @@ module Qs::Daemon
|
|
11
11
|
Qs.reset!
|
12
12
|
@qs_test_mode = ENV['QS_TEST_MODE']
|
13
13
|
ENV['QS_TEST_MODE'] = nil
|
14
|
+
Qs.config.dispatcher.queue_name = 'qs-app-dispatcher'
|
15
|
+
Qs.config.event_publisher = 'Daemon System Tests'
|
14
16
|
Qs.init
|
17
|
+
AppQueue.sync_subscriptions
|
15
18
|
@orig_config = AppDaemon.configuration.to_hash
|
16
19
|
end
|
17
20
|
teardown do
|
18
21
|
@daemon_runner.stop if @daemon_runner
|
19
22
|
AppDaemon.configuration.apply(@orig_config) # reset daemon config
|
20
|
-
Qs.redis.with
|
21
|
-
|
23
|
+
Qs.redis.with do |c|
|
24
|
+
keys = c.keys('*qs-app*')
|
25
|
+
c.pipelined{ keys.each{ |k| c.del(k) } }
|
26
|
+
end
|
22
27
|
Qs.client.clear(AppQueue.redis_key)
|
28
|
+
AppQueue.clear_subscriptions
|
23
29
|
Qs.reset!
|
24
30
|
ENV['QS_TEST_MODE'] = @qs_test_mode
|
25
31
|
end
|
26
32
|
|
33
|
+
private
|
34
|
+
|
35
|
+
def setup_app_and_dispatcher_daemon
|
36
|
+
@app_daemon = AppDaemon.new
|
37
|
+
@dispatcher_daemon = DispatcherDaemon.new
|
38
|
+
@daemon_runner = DaemonRunner.new(@app_daemon, @dispatcher_daemon)
|
39
|
+
@app_thread = @daemon_runner.start
|
40
|
+
end
|
41
|
+
|
27
42
|
end
|
28
43
|
|
29
44
|
class RunningDaemonSetupTests < SystemTests
|
30
45
|
setup do
|
31
|
-
|
32
|
-
@daemon_runner = DaemonRunner.new(@daemon)
|
33
|
-
@thread = @daemon_runner.start
|
46
|
+
setup_app_and_dispatcher_daemon
|
34
47
|
end
|
35
48
|
|
36
49
|
end
|
@@ -43,11 +56,11 @@ module Qs::Daemon
|
|
43
56
|
'key' => @key,
|
44
57
|
'value' => @value
|
45
58
|
})
|
46
|
-
@
|
59
|
+
@app_thread.join 0.5
|
47
60
|
end
|
48
61
|
|
49
62
|
should "run the job" do
|
50
|
-
assert_equal @value, Qs.redis.with{ |c| c.get(@key) }
|
63
|
+
assert_equal @value, Qs.redis.with{ |c| c.get("qs-app:#{@key}") }
|
51
64
|
end
|
52
65
|
|
53
66
|
end
|
@@ -57,12 +70,12 @@ module Qs::Daemon
|
|
57
70
|
setup do
|
58
71
|
@error_message = Factory.text
|
59
72
|
AppQueue.add('error', 'error_message' => @error_message)
|
60
|
-
@
|
73
|
+
@app_thread.join 0.5
|
61
74
|
end
|
62
75
|
|
63
76
|
should "run the configured error handler procs" do
|
64
77
|
exp = "RuntimeError: #{@error_message}"
|
65
|
-
assert_equal exp, Qs.redis.with{ |c| c.get('
|
78
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_job_error') }
|
66
79
|
end
|
67
80
|
|
68
81
|
end
|
@@ -71,14 +84,62 @@ module Qs::Daemon
|
|
71
84
|
desc "with a job that times out"
|
72
85
|
setup do
|
73
86
|
AppQueue.add('timeout')
|
74
|
-
@
|
87
|
+
@app_thread.join 1 # let the daemon have time to process the job
|
75
88
|
end
|
76
89
|
|
77
90
|
should "run the configured error handler procs" do
|
78
91
|
handler_class = AppHandlers::Timeout
|
79
92
|
exp = "Qs::TimeoutError: #{handler_class} timed out " \
|
80
93
|
"(#{handler_class.timeout}s)"
|
81
|
-
assert_equal exp, Qs.redis.with{ |c| c.get('
|
94
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_job_error') }
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class BasicEventTests < RunningDaemonSetupTests
|
100
|
+
desc "with a basic event added"
|
101
|
+
setup do
|
102
|
+
@key, @value = [Factory.string, Factory.string]
|
103
|
+
Qs.publish('qs-app', 'basic', {
|
104
|
+
'key' => @key,
|
105
|
+
'value' => @value
|
106
|
+
})
|
107
|
+
@app_thread.join 0.5
|
108
|
+
end
|
109
|
+
|
110
|
+
should "run the event" do
|
111
|
+
assert_equal @value, Qs.redis.with{ |c| c.get("qs-app:#{@key}") }
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
class EventThatErrorsTests < RunningDaemonSetupTests
|
117
|
+
desc "with an event that errors"
|
118
|
+
setup do
|
119
|
+
@error_message = Factory.text
|
120
|
+
Qs.publish('qs-app', 'error', 'error_message' => @error_message)
|
121
|
+
@app_thread.join 0.5
|
122
|
+
end
|
123
|
+
|
124
|
+
should "run the configured error handler procs" do
|
125
|
+
exp = "RuntimeError: #{@error_message}"
|
126
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_event_error') }
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
class TimeoutEventTests < RunningDaemonSetupTests
|
132
|
+
desc "with an event that times out"
|
133
|
+
setup do
|
134
|
+
Qs.publish('qs-app', 'timeout')
|
135
|
+
@app_thread.join 1 # let the daemon have time to process the job
|
136
|
+
end
|
137
|
+
|
138
|
+
should "run the configured error handler procs" do
|
139
|
+
handler_class = AppHandlers::TimeoutEvent
|
140
|
+
exp = "Qs::TimeoutError: #{handler_class} timed out " \
|
141
|
+
"(#{handler_class.timeout}s)"
|
142
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_event_error') }
|
82
143
|
end
|
83
144
|
|
84
145
|
end
|
@@ -87,21 +148,19 @@ module Qs::Daemon
|
|
87
148
|
desc "when no workers are available"
|
88
149
|
setup do
|
89
150
|
AppDaemon.workers 0 # no workers available, don't do this
|
90
|
-
|
91
|
-
@daemon_runner = DaemonRunner.new(@daemon)
|
92
|
-
@thread = @daemon_runner.start
|
151
|
+
setup_app_and_dispatcher_daemon
|
93
152
|
end
|
94
153
|
|
95
154
|
should "shutdown when stopped" do
|
96
|
-
@
|
97
|
-
@
|
98
|
-
assert_false @
|
155
|
+
@app_daemon.stop
|
156
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
157
|
+
assert_false @app_thread.alive?
|
99
158
|
end
|
100
159
|
|
101
160
|
should "shutdown when halted" do
|
102
|
-
@
|
103
|
-
@
|
104
|
-
assert_false @
|
161
|
+
@app_daemon.halt
|
162
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
163
|
+
assert_false @app_thread.alive?
|
105
164
|
end
|
106
165
|
|
107
166
|
end
|
@@ -110,28 +169,31 @@ module Qs::Daemon
|
|
110
169
|
desc "without a shutdown timeout"
|
111
170
|
setup do
|
112
171
|
AppDaemon.shutdown_timeout nil # disable shutdown timeout
|
113
|
-
|
114
|
-
@daemon_runner = DaemonRunner.new(@daemon)
|
115
|
-
@thread = @daemon_runner.start
|
172
|
+
setup_app_and_dispatcher_daemon
|
116
173
|
|
117
174
|
AppQueue.add('slow')
|
118
|
-
|
175
|
+
Qs.publish('qs-app', 'slow')
|
176
|
+
@app_thread.join 1 # let the daemon have time to process the job and event
|
119
177
|
end
|
120
178
|
|
121
|
-
should "shutdown and let the job
|
122
|
-
@
|
123
|
-
@
|
124
|
-
assert_false @
|
125
|
-
assert_equal 'finished', Qs.redis.with{ |c| c.get('slow') }
|
179
|
+
should "shutdown and let the job and event finish" do
|
180
|
+
@app_daemon.stop
|
181
|
+
@app_thread.join 10 # give it time to shutdown, should be faster
|
182
|
+
assert_false @app_thread.alive?
|
183
|
+
assert_equal 'finished', Qs.redis.with{ |c| c.get('qs-app:slow') }
|
184
|
+
assert_equal 'finished', Qs.redis.with{ |c| c.get('qs-app:slow:event') }
|
126
185
|
end
|
127
186
|
|
128
|
-
should "shutdown and not let the job
|
129
|
-
@
|
130
|
-
@
|
131
|
-
assert_false @
|
132
|
-
assert_nil Qs.redis.with{ |c| c.get('slow') }
|
187
|
+
should "shutdown and not let the job or event finish" do
|
188
|
+
@app_daemon.halt
|
189
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
190
|
+
assert_false @app_thread.alive?
|
191
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow') }
|
192
|
+
exp = "Qs::ShutdownError"
|
193
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_job_error') }
|
194
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow:event') }
|
133
195
|
exp = "Qs::ShutdownError"
|
134
|
-
assert_equal exp, Qs.redis.with{ |c| c.get('
|
196
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_event_error') }
|
135
197
|
end
|
136
198
|
|
137
199
|
end
|
@@ -140,86 +202,93 @@ module Qs::Daemon
|
|
140
202
|
desc "with a shutdown timeout"
|
141
203
|
setup do
|
142
204
|
AppDaemon.shutdown_timeout 1
|
143
|
-
|
144
|
-
@daemon_runner = DaemonRunner.new(@daemon)
|
145
|
-
@thread = @daemon_runner.start
|
205
|
+
setup_app_and_dispatcher_daemon
|
146
206
|
|
147
207
|
AppQueue.add('slow')
|
148
|
-
|
208
|
+
Qs.publish('qs-app', 'slow')
|
209
|
+
@app_thread.join 1 # let the daemon have time to process the job and event
|
149
210
|
end
|
150
211
|
|
151
|
-
should "shutdown and not let the job
|
152
|
-
@
|
153
|
-
@
|
154
|
-
assert_false @
|
155
|
-
assert_nil Qs.redis.with{ |c| c.get('slow') }
|
212
|
+
should "shutdown and not let the job or event finish" do
|
213
|
+
@app_daemon.stop
|
214
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
215
|
+
assert_false @app_thread.alive?
|
216
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow') }
|
156
217
|
exp = "Qs::ShutdownError"
|
157
|
-
assert_equal exp, Qs.redis.with{ |c| c.get('
|
218
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_job_error') }
|
219
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow:event') }
|
220
|
+
exp = "Qs::ShutdownError"
|
221
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_event_error') }
|
158
222
|
end
|
159
223
|
|
160
|
-
should "shutdown and not let the job
|
161
|
-
@
|
162
|
-
@
|
163
|
-
assert_false @
|
164
|
-
assert_nil Qs.redis.with{ |c| c.get('slow') }
|
224
|
+
should "shutdown and not let the job or event finish" do
|
225
|
+
@app_daemon.halt
|
226
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
227
|
+
assert_false @app_thread.alive?
|
228
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow') }
|
229
|
+
exp = "Qs::ShutdownError"
|
230
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_job_error') }
|
231
|
+
assert_nil Qs.redis.with{ |c| c.get('qs-app:slow:event') }
|
165
232
|
exp = "Qs::ShutdownError"
|
166
|
-
assert_equal exp, Qs.redis.with{ |c| c.get('
|
233
|
+
assert_equal exp, Qs.redis.with{ |c| c.get('qs-app:last_event_error') }
|
167
234
|
end
|
168
235
|
|
169
236
|
end
|
170
237
|
|
171
|
-
class
|
172
|
-
desc "with a
|
238
|
+
class ShutdownWithUnprocessedQueueItemTests < SystemTests
|
239
|
+
desc "with a queue item that gets picked up but doesn't get processed"
|
173
240
|
setup do
|
174
241
|
Assert.stub(Qs::PayloadHandler, :new){ sleep 5 }
|
175
242
|
|
176
243
|
AppDaemon.shutdown_timeout 1
|
177
244
|
AppDaemon.workers 2
|
178
|
-
|
179
|
-
@daemon_runner = DaemonRunner.new(@daemon)
|
180
|
-
@thread = @daemon_runner.start
|
245
|
+
setup_app_and_dispatcher_daemon
|
181
246
|
|
182
247
|
AppQueue.add('slow')
|
183
248
|
AppQueue.add('slow')
|
184
249
|
AppQueue.add('basic')
|
185
|
-
@
|
250
|
+
@app_thread.join 1 # let the daemon have time to process jobs
|
186
251
|
end
|
187
252
|
|
188
|
-
should "shutdown and requeue the
|
189
|
-
@
|
190
|
-
@
|
191
|
-
assert_false @
|
192
|
-
|
193
|
-
|
194
|
-
names = serialized_payloads.map{ |sp| Qs::Job.parse(Qs.deserialize(sp)).name }
|
253
|
+
should "shutdown and requeue the queue item" do
|
254
|
+
@app_daemon.stop
|
255
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
256
|
+
assert_false @app_thread.alive?
|
257
|
+
encoded_payloads = Qs.redis.with{ |c| c.lrange(AppQueue.redis_key, 0, 3) }
|
258
|
+
names = encoded_payloads.map{ |sp| Qs::Payload.deserialize(sp).name }
|
195
259
|
assert_equal ['basic', 'slow', 'slow'], names
|
196
260
|
end
|
197
261
|
|
198
|
-
should "shutdown and requeue the
|
199
|
-
@
|
200
|
-
@
|
201
|
-
assert_false @
|
202
|
-
|
203
|
-
|
204
|
-
names = serialized_payloads.map{ |sp| Qs::Job.parse(Qs.deserialize(sp)).name }
|
262
|
+
should "shutdown and requeue the queue item" do
|
263
|
+
@app_daemon.halt
|
264
|
+
@app_thread.join 2 # give it time to shutdown, should be faster
|
265
|
+
assert_false @app_thread.alive?
|
266
|
+
encoded_payloads = Qs.redis.with{ |c| c.lrange(AppQueue.redis_key, 0, 3) }
|
267
|
+
names = encoded_payloads.map{ |sp| Qs::Payload.deserialize(sp).name }
|
205
268
|
assert_equal ['basic', 'slow', 'slow'], names
|
206
269
|
end
|
207
270
|
|
208
271
|
end
|
209
272
|
|
210
273
|
class DaemonRunner
|
211
|
-
def initialize(
|
212
|
-
@
|
213
|
-
@
|
274
|
+
def initialize(app_daemon, dispatcher_daemon = nil)
|
275
|
+
@app_daemon = app_daemon
|
276
|
+
@dispatcher_daemon = dispatcher_daemon
|
277
|
+
@app_thread = nil
|
278
|
+
@dispatcher_thread = nil
|
214
279
|
end
|
215
280
|
|
216
281
|
def start
|
217
|
-
@
|
282
|
+
@app_thread = @app_daemon.start
|
283
|
+
@dispatcher_thread = @dispatcher_daemon.start if @dispatcher_daemon
|
284
|
+
@app_thread
|
218
285
|
end
|
219
286
|
|
220
287
|
def stop
|
221
|
-
@
|
222
|
-
@
|
288
|
+
@app_daemon.halt
|
289
|
+
@dispatcher_daemon.halt if @dispatcher_daemon
|
290
|
+
@app_thread.join if @app_thread
|
291
|
+
@dispatcher_thread.join if @dispatcher_thread
|
223
292
|
end
|
224
293
|
end
|
225
294
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'assert'
|
2
|
+
require 'qs'
|
3
|
+
|
4
|
+
require 'test/support/app_daemon'
|
5
|
+
|
6
|
+
class Qs::Queue
|
7
|
+
|
8
|
+
class SystemTests < Assert::Context
|
9
|
+
desc "Qs::Queue"
|
10
|
+
setup do
|
11
|
+
Qs.reset!
|
12
|
+
@qs_test_mode = ENV['QS_TEST_MODE']
|
13
|
+
ENV['QS_TEST_MODE'] = nil
|
14
|
+
Qs.init
|
15
|
+
|
16
|
+
@event = Qs::Event.new('qs-app', 'basic')
|
17
|
+
@other_queue = Qs::Queue.new{ name(Factory.string) }
|
18
|
+
@other_queue.event(@event.channel, @event.name, Factory.string)
|
19
|
+
end
|
20
|
+
teardown do
|
21
|
+
Qs.redis.with do |c|
|
22
|
+
keys = c.keys('*qs-app*')
|
23
|
+
c.pipelined{ keys.each{ |k| c.del(k) } }
|
24
|
+
end
|
25
|
+
Qs.reset!
|
26
|
+
ENV['QS_TEST_MODE'] = @qs_test_mode
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class SyncSubscriptionsTests < SystemTests
|
32
|
+
desc "sync_subscriptions"
|
33
|
+
setup do
|
34
|
+
AppQueue.sync_subscriptions
|
35
|
+
end
|
36
|
+
|
37
|
+
should "store subscriptions for the queue in redis" do
|
38
|
+
AppQueue.event_route_names.each do |route_name|
|
39
|
+
redis_key = Qs::Event::SubscribersRedisKey.new(route_name)
|
40
|
+
smembers = Qs.redis.with{ |c| c.smembers(redis_key) }
|
41
|
+
assert_includes AppQueue.name, smembers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
should "allow adding a new queues subscriptions but preserve the existing" do
|
46
|
+
@other_queue.sync_subscriptions
|
47
|
+
|
48
|
+
smembers = Qs.redis.with{ |c| c.smembers(@event.subscribers_redis_key) }
|
49
|
+
assert_equal 2, smembers.size
|
50
|
+
assert_includes AppQueue.name, smembers
|
51
|
+
assert_includes @other_queue.name, smembers
|
52
|
+
end
|
53
|
+
|
54
|
+
should "remove subscriptions if a queue no longer subscribes to the event" do
|
55
|
+
route_names = AppQueue.event_route_names.reject{ |n| n == @event.route_name }
|
56
|
+
Assert.stub(AppQueue, :event_route_names){ route_names }
|
57
|
+
AppQueue.sync_subscriptions
|
58
|
+
|
59
|
+
redis_key = Qs::Event.new('qs-app', 'basic').subscribers_redis_key
|
60
|
+
smembers = Qs.redis.with{ |c| c.smembers(redis_key) }
|
61
|
+
assert_not_includes AppQueue.name, smembers
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class ClearSubscriptionsTests < SystemTests
|
67
|
+
desc "clear_subscriptions"
|
68
|
+
setup do
|
69
|
+
AppQueue.sync_subscriptions
|
70
|
+
@other_queue.sync_subscriptions
|
71
|
+
AppQueue.clear_subscriptions
|
72
|
+
end
|
73
|
+
|
74
|
+
should "remove the queue from all of its events subscribers" do
|
75
|
+
AppQueue.event_route_names.each do |route_name|
|
76
|
+
redis_key = Qs::Event::SubscribersRedisKey.new(route_name)
|
77
|
+
smembers = Qs.redis.with{ |c| c.smembers(redis_key) }
|
78
|
+
assert_not_includes AppQueue.name, smembers
|
79
|
+
end
|
80
|
+
|
81
|
+
smembers = Qs.redis.with{ |c| c.smembers(@event.subscribers_redis_key) }
|
82
|
+
assert_equal [@other_queue.name], smembers
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/test/unit/client_tests.rb
CHANGED
@@ -48,10 +48,12 @@ module Qs::Client
|
|
48
48
|
subject{ @client }
|
49
49
|
|
50
50
|
should have_readers :redis_config, :redis
|
51
|
-
should have_imeths :enqueue, :push
|
51
|
+
should have_imeths :enqueue, :publish, :publish_as, :push
|
52
52
|
should have_imeths :block_dequeue
|
53
53
|
should have_imeths :append, :prepend
|
54
54
|
should have_imeths :clear
|
55
|
+
should have_imeths :sync_subscriptions, :clear_subscriptions
|
56
|
+
should have_imeths :event_subscribers
|
55
57
|
|
56
58
|
should "know its redis config" do
|
57
59
|
assert_equal @redis_config, subject.redis_config
|
@@ -63,16 +65,51 @@ module Qs::Client
|
|
63
65
|
|
64
66
|
should "build a job, enqueue it and return it using `enqueue`" do
|
65
67
|
result = subject.enqueue(@queue, @job_name, @job_params)
|
66
|
-
|
67
|
-
assert_equal @
|
68
|
-
assert_equal @
|
69
|
-
assert_equal
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
call = subject.enqueue_calls.last
|
69
|
+
assert_equal @queue, call.queue
|
70
|
+
assert_equal @job_name, call.job.name
|
71
|
+
assert_equal @job_params, call.job.params
|
72
|
+
assert_equal call.job, result
|
73
|
+
end
|
74
|
+
|
75
|
+
should "enqueue a dispatch job and return its event using `publish`" do
|
76
|
+
event_channel = Factory.string
|
77
|
+
event_name = Factory.string
|
78
|
+
event_params = @job_params
|
79
|
+
result = subject.publish(event_channel, event_name, event_params)
|
80
|
+
|
81
|
+
call = subject.enqueue_calls.last
|
82
|
+
assert_equal Qs.dispatcher_queue, call.queue
|
83
|
+
|
84
|
+
dispatch_job = Factory.dispatch_job({
|
85
|
+
:event_channel => event_channel,
|
86
|
+
:event_name => event_name,
|
87
|
+
:event_params => event_params
|
88
|
+
})
|
89
|
+
assert_equal dispatch_job.name, call.job.name
|
90
|
+
assert_equal dispatch_job.params, call.job.params
|
91
|
+
assert_equal call.job.event, result
|
92
|
+
end
|
93
|
+
|
94
|
+
should "enqueue a dispatch job with a custom publisher using `publish_as`" do
|
95
|
+
publisher = Factory.string
|
96
|
+
channel = Factory.string
|
97
|
+
name = Factory.string
|
98
|
+
params = @job_params
|
99
|
+
result = subject.publish_as(publisher, channel, name, params)
|
100
|
+
|
101
|
+
call = subject.enqueue_calls.last
|
102
|
+
assert_equal Qs.dispatcher_queue, call.queue
|
103
|
+
|
104
|
+
dispatch_job = Factory.dispatch_job({
|
105
|
+
:event_channel => channel,
|
106
|
+
:event_name => name,
|
107
|
+
:event_params => params,
|
108
|
+
:event_publisher => publisher
|
109
|
+
})
|
110
|
+
assert_equal dispatch_job.name, call.job.name
|
111
|
+
assert_equal dispatch_job.params, call.job.params
|
112
|
+
assert_equal call.job.event, result
|
76
113
|
end
|
77
114
|
|
78
115
|
should "raise a not implemented error using `push`" do
|
@@ -88,8 +125,8 @@ module Qs::Client
|
|
88
125
|
@connection_spy = HellaRedis::ConnectionSpy.new(@client.redis_config)
|
89
126
|
Assert.stub(@client, :redis){ @connection_spy }
|
90
127
|
|
91
|
-
@queue_redis_key
|
92
|
-
@
|
128
|
+
@queue_redis_key = Factory.string
|
129
|
+
@encoded_payload = Factory.string
|
93
130
|
end
|
94
131
|
|
95
132
|
should "block pop from the front of a list using `block_dequeue`" do
|
@@ -101,22 +138,22 @@ module Qs::Client
|
|
101
138
|
assert_equal args, call.args
|
102
139
|
end
|
103
140
|
|
104
|
-
should "add a
|
105
|
-
subject.append(@queue_redis_key, @
|
141
|
+
should "add a encoded payload to the end of a list using `append`" do
|
142
|
+
subject.append(@queue_redis_key, @encoded_payload)
|
106
143
|
|
107
144
|
call = @connection_spy.redis_calls.last
|
108
|
-
assert_equal :lpush,
|
109
|
-
assert_equal @queue_redis_key,
|
110
|
-
assert_equal @
|
145
|
+
assert_equal :lpush, call.command
|
146
|
+
assert_equal @queue_redis_key, call.args.first
|
147
|
+
assert_equal @encoded_payload, call.args.last
|
111
148
|
end
|
112
149
|
|
113
|
-
should "add a
|
114
|
-
subject.prepend(@queue_redis_key, @
|
150
|
+
should "add a encoded payload to the front of a list using `prepend`" do
|
151
|
+
subject.prepend(@queue_redis_key, @encoded_payload)
|
115
152
|
|
116
153
|
call = @connection_spy.redis_calls.last
|
117
|
-
assert_equal :rpush,
|
118
|
-
assert_equal @queue_redis_key,
|
119
|
-
assert_equal @
|
154
|
+
assert_equal :rpush, call.command
|
155
|
+
assert_equal @queue_redis_key, call.args.first
|
156
|
+
assert_equal @encoded_payload, call.args.last
|
120
157
|
end
|
121
158
|
|
122
159
|
should "del a list using `clear`" do
|
@@ -134,6 +171,96 @@ module Qs::Client
|
|
134
171
|
assert_equal :ping, call.command
|
135
172
|
end
|
136
173
|
|
174
|
+
should "return the events subscriber set using `event_subscribers`" do
|
175
|
+
smembers_key = nil
|
176
|
+
smembers = Factory.integer(3).times.map{ Factory.string }
|
177
|
+
Assert.stub(@connection_spy.redis_spy, :smembers) do |key|
|
178
|
+
smembers_key = key
|
179
|
+
smembers
|
180
|
+
end
|
181
|
+
|
182
|
+
event = Factory.event
|
183
|
+
result = subject.event_subscribers(event)
|
184
|
+
|
185
|
+
assert_equal event.subscribers_redis_key, smembers_key
|
186
|
+
assert_equal smembers, result
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class SubscriptionsSetupTests < RedisCallTests
|
192
|
+
setup do
|
193
|
+
@event_subs_keys = Factory.integer(3).times.map{ Factory.string }
|
194
|
+
@keys_pattern = nil
|
195
|
+
Assert.stub(@connection_spy.redis_spy, :keys) do |pattern|
|
196
|
+
@keys_pattern = pattern
|
197
|
+
@event_subs_keys
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
class SyncSubscriptionsTests < SubscriptionsSetupTests
|
204
|
+
desc "sync_subscriptions"
|
205
|
+
setup do
|
206
|
+
Factory.integer(3).times.map{ @queue.event_route_names << Factory.string }
|
207
|
+
subject.sync_subscriptions(@queue)
|
208
|
+
end
|
209
|
+
|
210
|
+
should "run in a pipelined transaction" do
|
211
|
+
calls = @connection_spy.redis_calls[0, 2]
|
212
|
+
assert_equal [:pipelined, :multi], calls.map(&:command)
|
213
|
+
end
|
214
|
+
|
215
|
+
should "find all event subscribers keys" do
|
216
|
+
assert_equal Qs::Event::SubscribersRedisKey.new('*'), @keys_pattern
|
217
|
+
end
|
218
|
+
|
219
|
+
should "remove the queue from all events subscribers first" do
|
220
|
+
calls = @connection_spy.redis_calls[2, @event_subs_keys.size]
|
221
|
+
assert_equal @event_subs_keys.size, calls.size
|
222
|
+
assert_equal [:srem], calls.map(&:command).uniq
|
223
|
+
exp = @event_subs_keys.map{ |key| [key, @queue.name] }
|
224
|
+
assert_equal exp, calls.map(&:args)
|
225
|
+
end
|
226
|
+
|
227
|
+
should "remove and add the queue from events subscribers" do
|
228
|
+
start_at = 2 + @event_subs_keys.size
|
229
|
+
calls = @connection_spy.redis_calls[start_at..-1]
|
230
|
+
exp = @queue.event_route_names.size
|
231
|
+
assert_equal exp, calls.size
|
232
|
+
assert_equal [:sadd], calls.map(&:command).uniq
|
233
|
+
exp = @queue.event_route_names.map do |name|
|
234
|
+
[Qs::Event::SubscribersRedisKey.new(name), @queue.name]
|
235
|
+
end
|
236
|
+
assert_equal exp, calls.map(&:args)
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
class ClearSubscriptionsTests < SubscriptionsSetupTests
|
242
|
+
desc "clear_subscriptions"
|
243
|
+
setup do
|
244
|
+
subject.clear_subscriptions(@queue)
|
245
|
+
end
|
246
|
+
|
247
|
+
should "run in a pipelined transaction" do
|
248
|
+
calls = @connection_spy.redis_calls[0, 2]
|
249
|
+
assert_equal [:pipelined, :multi], calls.map(&:command)
|
250
|
+
end
|
251
|
+
|
252
|
+
should "find all event subscribers keys" do
|
253
|
+
assert_equal Qs::Event::SubscribersRedisKey.new('*'), @keys_pattern
|
254
|
+
end
|
255
|
+
|
256
|
+
should "remove the queue from all events subscribers" do
|
257
|
+
calls = @connection_spy.redis_calls[2..-1]
|
258
|
+
assert_equal @event_subs_keys.size, calls.size
|
259
|
+
assert_equal [:srem], calls.map(&:command).uniq
|
260
|
+
exp = @event_subs_keys.map{ |key| [key, @queue.name] }
|
261
|
+
assert_equal exp, calls.map(&:args)
|
262
|
+
end
|
263
|
+
|
137
264
|
end
|
138
265
|
|
139
266
|
class QsClientTests < UnitTests
|
@@ -167,25 +294,25 @@ module Qs::Client
|
|
167
294
|
assert_equal @connection_spy, subject.redis
|
168
295
|
end
|
169
296
|
|
170
|
-
should "add jobs to the
|
297
|
+
should "add jobs to the queues redis list using `enqueue`" do
|
171
298
|
subject.enqueue(@queue, @job_name, @job_params)
|
172
299
|
|
173
300
|
call = @connection_spy.redis_calls.last
|
174
301
|
assert_equal :lpush, call.command
|
175
302
|
assert_equal @queue.redis_key, call.args.first
|
176
|
-
|
177
|
-
assert_equal @job_name,
|
178
|
-
assert_equal @job_params,
|
303
|
+
job = Qs::Payload.deserialize(call.args.last)
|
304
|
+
assert_equal @job_name, job.name
|
305
|
+
assert_equal @job_params, job.params
|
179
306
|
end
|
180
307
|
|
181
|
-
should "add
|
182
|
-
|
183
|
-
subject.push(@queue.name,
|
308
|
+
should "add a payload hash to the queues redis list using `push`" do
|
309
|
+
payload_hash = { Factory.string => Factory.string }
|
310
|
+
subject.push(@queue.name, payload_hash)
|
184
311
|
|
185
312
|
call = @connection_spy.redis_calls.last
|
186
313
|
assert_equal :lpush, call.command
|
187
314
|
assert_equal @queue.redis_key, call.args.first
|
188
|
-
assert_equal Qs.
|
315
|
+
assert_equal Qs.encode(payload_hash), call.args.last
|
189
316
|
end
|
190
317
|
|
191
318
|
end
|
@@ -206,10 +333,10 @@ module Qs::Client
|
|
206
333
|
class TestClientInitTests < TestClientTests
|
207
334
|
desc "when init"
|
208
335
|
setup do
|
209
|
-
@
|
210
|
-
Assert.stub(Qs, :
|
211
|
-
|
212
|
-
|
336
|
+
@encoded_hash = nil
|
337
|
+
Assert.stub(Qs, :encode){ |hash| @encoded_hash = hash }
|
338
|
+
@serialized_job = nil
|
339
|
+
Assert.stub(Qs::Payload, :serialize){ |job| @serialized_job = job }
|
213
340
|
|
214
341
|
@client = @client_class.new(@redis_config)
|
215
342
|
end
|
@@ -237,24 +364,34 @@ module Qs::Client
|
|
237
364
|
assert_equal job, result
|
238
365
|
end
|
239
366
|
|
240
|
-
should "serialize the
|
367
|
+
should "serialize the job when enqueueing" do
|
241
368
|
subject.enqueue(@queue, @job_name, @job_params)
|
242
369
|
|
243
370
|
job = @queue.enqueued_jobs.last
|
244
|
-
assert_equal job
|
371
|
+
assert_equal job, @serialized_job
|
245
372
|
end
|
246
373
|
|
247
|
-
should "track all the
|
248
|
-
|
374
|
+
should "track all the payload hashes pushed onto a queue" do
|
375
|
+
payload_hash = { Factory.string => Factory.string }
|
376
|
+
subject.push(@queue.name, payload_hash)
|
249
377
|
|
250
378
|
pushed_item = subject.pushed_items.last
|
251
379
|
assert_instance_of Qs::TestClient::PushedItem, pushed_item
|
252
|
-
assert_equal @queue.name,
|
253
|
-
assert_equal
|
380
|
+
assert_equal @queue.name, pushed_item.queue_name
|
381
|
+
assert_equal payload_hash, pushed_item.payload_hash
|
382
|
+
end
|
383
|
+
|
384
|
+
should "encode the payload hashes when pushing them onto a queue" do
|
385
|
+
payload_hash = { Factory.string => Factory.string }
|
386
|
+
subject.push(@queue.name, payload_hash)
|
387
|
+
|
388
|
+
pushed_item = subject.pushed_items.last
|
389
|
+
assert_equal pushed_item.payload_hash, @encoded_hash
|
254
390
|
end
|
255
391
|
|
256
392
|
should "clear its pushed items when reset" do
|
257
|
-
|
393
|
+
payload_hash = { Factory.string => Factory.string }
|
394
|
+
subject.push(@queue.name, payload_hash)
|
258
395
|
assert_not_empty subject.pushed_items
|
259
396
|
subject.reset!
|
260
397
|
assert_empty subject.pushed_items
|
@@ -265,12 +402,14 @@ module Qs::Client
|
|
265
402
|
class FakeClient
|
266
403
|
include Qs::Client
|
267
404
|
|
268
|
-
attr_reader :
|
405
|
+
attr_reader :enqueue_calls
|
269
406
|
|
270
407
|
def enqueue!(queue, job)
|
271
|
-
@
|
272
|
-
@
|
408
|
+
@enqueue_calls ||= []
|
409
|
+
EnqueueCall.new(queue, job).tap{ |c| @enqueue_calls << c }
|
273
410
|
end
|
411
|
+
|
412
|
+
EnqueueCall = Struct.new(:queue, :job)
|
274
413
|
end
|
275
414
|
|
276
415
|
end
|