qs 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -211,7 +211,7 @@ module Qs::MessageHandler
211
211
  @handler.qs_run
212
212
  end
213
213
 
214
- should "call `run!` and it's callbacks" do
214
+ should "call `run!` and its callbacks" do
215
215
  assert_equal 6, subject.first_before_run_call_order
216
216
  assert_equal 7, subject.second_before_run_call_order
217
217
  assert_equal 8, subject.run_call_order
@@ -236,12 +236,25 @@ module Qs::MessageHandler
236
236
  assert_equal @runner.params, subject.instance_eval{ params }
237
237
  end
238
238
 
239
+ should "call to the runner for its halt helper" do
240
+ capture_runner_meth_args_for(:halt)
241
+ subject.instance_eval{ halt }
242
+
243
+ assert_equal [], @meth_args
244
+ end
245
+
239
246
  private
240
247
 
241
248
  def stub_runner_with_something_for(meth)
242
249
  Assert.stub(@runner, meth){ @something }
243
250
  end
244
251
 
252
+ def capture_runner_meth_args_for(meth)
253
+ Assert.stub(@runner, meth) do |*args|
254
+ @meth_args = args
255
+ end
256
+ end
257
+
245
258
  end
246
259
 
247
260
  class TestMessageHandler
@@ -2,6 +2,7 @@ require 'assert'
2
2
  require 'qs/process'
3
3
 
4
4
  require 'qs/daemon'
5
+ require 'qs/io_pipe'
5
6
  require 'test/support/pid_file_spy'
6
7
 
7
8
  class Qs::Process
@@ -9,22 +10,31 @@ class Qs::Process
9
10
  class UnitTests < Assert::Context
10
11
  desc "Qs::Process"
11
12
  setup do
13
+ @current_env_skip_daemonize = ENV['QS_SKIP_DAEMONIZE']
14
+ ENV.delete('QS_SKIP_DAEMONIZE')
15
+
12
16
  @process_class = Qs::Process
13
17
  end
18
+ teardown do
19
+ ENV['QS_SKIP_DAEMONIZE'] = @current_env_skip_daemonize
20
+ end
14
21
  subject{ @process_class }
15
22
 
16
23
  should "know its wait for signals timeout" do
17
24
  assert_equal 15, WAIT_FOR_SIGNALS_TIMEOUT
18
25
  end
19
26
 
27
+ should "know its signal strings" do
28
+ assert_equal 'H', HALT
29
+ assert_equal 'S', STOP
30
+ assert_equal 'R', RESTART
31
+ end
32
+
20
33
  end
21
34
 
22
35
  class InitTests < UnitTests
23
36
  desc "when init"
24
37
  setup do
25
- @current_env_skip_daemonize = ENV['QS_SKIP_DAEMONIZE']
26
- ENV.delete('QS_SKIP_DAEMONIZE')
27
-
28
38
  @daemon_spy = DaemonSpy.new
29
39
 
30
40
  @pid_file_spy = PIDFileSpy.new(Factory.integer)
@@ -37,9 +47,6 @@ class Qs::Process
37
47
 
38
48
  @process = @process_class.new(@daemon_spy)
39
49
  end
40
- teardown do
41
- ENV['QS_SKIP_DAEMONIZE'] = @current_env_skip_daemonize
42
- end
43
50
  subject{ @process }
44
51
 
45
52
  should have_readers :daemon, :name
@@ -51,7 +58,9 @@ class Qs::Process
51
58
  end
52
59
 
53
60
  should "know its name, pid file, signal io and restart cmd" do
54
- assert_equal "qs: #{@daemon_spy.process_label}", subject.name
61
+ exp = "qs: #{@daemon_spy.process_label}"
62
+ assert_equal exp, subject.name
63
+
55
64
  assert_equal @pid_file_spy, subject.pid_file
56
65
  assert_instance_of Qs::IOPipe, subject.signal_io
57
66
  assert_equal @restart_cmd_spy, subject.restart_cmd
@@ -109,12 +118,12 @@ class Qs::Process
109
118
  @wait_timeout = nil
110
119
  Assert.stub(@process.signal_io, :wait) do |timeout|
111
120
  @wait_timeout = timeout
112
- sleep 0.1
121
+ sleep 2*JOIN_SECONDS
113
122
  false
114
123
  end
115
124
 
116
125
  @thread = Thread.new{ @process.run }
117
- @thread.join(0.1)
126
+ @thread.join(JOIN_SECONDS)
118
127
  end
119
128
  teardown do
120
129
  # manually unstub or the process thread will hang forever
@@ -177,7 +186,7 @@ class Qs::Process
177
186
  setup do
178
187
  Assert.stub(@process, :daemonize?){ true }
179
188
  @thread = Thread.new{ @process.run }
180
- @thread.join(0.1)
189
+ @thread.join(JOIN_SECONDS)
181
190
  end
182
191
 
183
192
  should "daemonize the process" do
@@ -190,8 +199,9 @@ class Qs::Process
190
199
  desc "and run with a halt signal"
191
200
  setup do
192
201
  @thread = Thread.new{ @process.run }
202
+ @thread.join(JOIN_SECONDS)
193
203
  @process.signal_io.write(HALT)
194
- @thread.join(0.1)
204
+ @thread.join(JOIN_SECONDS)
195
205
  end
196
206
 
197
207
  should "halt its daemon" do
@@ -217,8 +227,9 @@ class Qs::Process
217
227
  desc "and run with a stop signal"
218
228
  setup do
219
229
  @thread = Thread.new{ @process.run }
230
+ @thread.join(JOIN_SECONDS)
220
231
  @process.signal_io.write(STOP)
221
- @thread.join(0.1)
232
+ @thread.join(JOIN_SECONDS)
222
233
  end
223
234
 
224
235
  should "stop its daemon" do
@@ -244,8 +255,9 @@ class Qs::Process
244
255
  desc "and run with a restart signal"
245
256
  setup do
246
257
  @thread = Thread.new{ @process.run }
258
+ @thread.join(JOIN_SECONDS)
247
259
  @process.signal_io.write(RESTART)
248
- @thread.join(0.1)
260
+ @thread.join(JOIN_SECONDS)
249
261
  end
250
262
 
251
263
  should "stop its daemon" do
@@ -253,10 +265,6 @@ class Qs::Process
253
265
  assert_equal [true], @daemon_spy.stop_args
254
266
  end
255
267
 
256
- should "set the env var to skip daemonize" do
257
- assert_equal 'yes', ENV['QS_SKIP_DAEMONIZE']
258
- end
259
-
260
268
  should "run the restart cmd" do
261
269
  assert_true @restart_cmd_spy.run_called
262
270
  end
@@ -266,15 +274,16 @@ class Qs::Process
266
274
  class RunWithDaemonCrashTests < RunSetupTests
267
275
  desc "and run with the daemon crashing"
268
276
  setup do
269
- # lower the time it sleeps so it loops and restarts the daemon quicker
270
277
  Assert.stub(@process.signal_io, :wait) do |timeout|
271
- sleep 0.1
278
+ sleep JOIN_SECONDS * 0.5 # ensure this has time to run before the thread
279
+ # joins below
272
280
  false
273
281
  end
274
282
 
275
283
  @thread = Thread.new{ @process.run }
284
+ @thread.join(JOIN_SECONDS)
276
285
  @daemon_spy.start_called = false
277
- @thread.join(0.1)
286
+ @thread.join(JOIN_SECONDS)
278
287
  end
279
288
  teardown do
280
289
  # manually unstub or the process thread will hang forever
@@ -294,7 +303,7 @@ class Qs::Process
294
303
  Assert.stub(::Signal, :trap){ raise ArgumentError }
295
304
 
296
305
  @thread = Thread.new{ @process.run }
297
- @thread.join(0.1)
306
+ @thread.join(JOIN_SECONDS)
298
307
  end
299
308
 
300
309
  should "start normally" do
@@ -343,10 +352,26 @@ class Qs::Process
343
352
  assert_equal [Gem.ruby, $0, ARGV].flatten, subject.argv
344
353
  end
345
354
 
346
- should "change the dir and run a kernel exec when run" do
347
- subject.run
348
- assert_equal [subject.dir], @chdir_called_with
349
- assert_equal subject.argv, @exec_called_with
355
+ if RUBY_VERSION == '1.8.7'
356
+
357
+ should "set env vars, change the dir and kernel exec when run" do
358
+ subject.run
359
+
360
+ assert_equal 'yes', ENV['QS_SKIP_DAEMONIZE']
361
+ assert_equal [subject.dir], @chdir_called_with
362
+ assert_equal subject.argv, @exec_called_with
363
+ end
364
+
365
+ else
366
+
367
+ should "kernel exec when run" do
368
+ subject.run
369
+
370
+ env = { 'QS_SKIP_DAEMONIZE' => 'yes' }
371
+ options = { :chdir => subject.dir }
372
+ assert_equal ([env] + subject.argv + [options]), @exec_called_with
373
+ end
374
+
350
375
  end
351
376
 
352
377
  end
@@ -1,6 +1,7 @@
1
1
  require 'assert'
2
2
  require 'qs/qs_runner'
3
3
 
4
+ require 'much-timeout'
4
5
  require 'qs'
5
6
  require 'qs/message_handler'
6
7
 
@@ -23,6 +24,10 @@ class Qs::QsRunner
23
24
  assert_true subject < Qs::Runner
24
25
  end
25
26
 
27
+ should "know its TimeoutInterrupt" do
28
+ assert_true TimeoutInterrupt < Interrupt
29
+ end
30
+
26
31
  end
27
32
 
28
33
  class InitTests < UnitTests
@@ -47,9 +52,9 @@ class Qs::QsRunner
47
52
  class RunSetupTests < InitTests
48
53
  desc "and run"
49
54
  setup do
50
- @timeout_called_with = nil
51
- Assert.stub(OptionalTimeout, :new) do |*args, &block|
52
- @timeout_called_with = args
55
+ @optional_timeout_called_with = nil
56
+ Assert.stub(MuchTimeout, :optional_timeout) do |*args, &block|
57
+ @optional_timeout_called_with = args
53
58
  block.call
54
59
  end
55
60
  end
@@ -63,7 +68,8 @@ class Qs::QsRunner
63
68
  end
64
69
 
65
70
  should "run the handler in an optional timeout" do
66
- assert_equal [@runner.timeout], @timeout_called_with
71
+ exp = [@runner.timeout, TimeoutInterrupt]
72
+ assert_equal exp, @optional_timeout_called_with
67
73
  end
68
74
 
69
75
  should "run the handler's before callbacks" do
@@ -83,50 +89,124 @@ class Qs::QsRunner
83
89
 
84
90
  end
85
91
 
86
- class RunWithTimeoutErrorTests < RunSetupTests
92
+ class RunWithInitHaltTests < UnitTests
93
+ desc "with a handler that halts on init"
87
94
  setup do
88
- Assert.stub(OptionalTimeout, :new){ raise Qs::TimeoutError }
95
+ @runner = @runner_class.new(@handler_class, :params => {
96
+ 'halt' => 'init'
97
+ })
98
+ @handler = @runner.handler
99
+ @response = @runner.run
89
100
  end
101
+ subject{ @runner }
90
102
 
91
- should "raise a timeout error with a good message" do
92
- exception = nil
93
- begin; @runner.run; rescue StandardError => exception; end
103
+ should "run the before and after callbacks despite the halt" do
104
+ assert_not_nil @handler.first_before_call_order
105
+ assert_not_nil @handler.second_before_call_order
106
+ assert_not_nil @handler.first_after_call_order
107
+ assert_not_nil @handler.second_after_call_order
108
+ end
94
109
 
95
- assert_kind_of Qs::TimeoutError, exception
96
- exp = "#{@handler_class} timed out (#{@runner.timeout}s)"
97
- assert_equal exp, exception.message
110
+ should "stop processing when the halt is called" do
111
+ assert_not_nil @handler.init_call_order
112
+ assert_nil @handler.run_call_order
98
113
  end
99
114
 
100
115
  end
101
116
 
102
- class OptionalTimeoutTests < UnitTests
103
- desc "OptionalTimeout"
117
+ class RunWithRunHaltTests < UnitTests
118
+ desc "when run with a handler that halts on run"
104
119
  setup do
105
- @timeout_called_with = nil
106
- Assert.stub(SystemTimer, :timeout_after) do |*args, &block|
107
- @timeout_called_with = args
108
- block.call
109
- end
120
+ @runner = @runner_class.new(@handler_class, :params => {
121
+ 'halt' => 'run'
122
+ })
123
+ @handler = @runner.handler
124
+ @response = @runner.run
110
125
  end
111
- subject{ OptionalTimeout }
126
+ subject{ @runner }
112
127
 
113
- should have_imeths :new
128
+ should "run the before and after callbacks despite the halt" do
129
+ assert_not_nil @handler.first_before_call_order
130
+ assert_not_nil @handler.second_before_call_order
131
+ assert_not_nil @handler.first_after_call_order
132
+ assert_not_nil @handler.second_after_call_order
133
+ end
114
134
 
115
- should "use a system timer timeout when provided a non-`nil` value" do
116
- value = Factory.integer
117
- block_run = false
135
+ should "stop processing when the halt is called" do
136
+ assert_not_nil @handler.init_call_order
137
+ assert_not_nil @handler.run_call_order
138
+ end
118
139
 
119
- subject.new(value){ block_run = true }
120
- assert_equal [value, Qs::TimeoutError], @timeout_called_with
121
- assert_true block_run
140
+ end
141
+
142
+ class RunWithBeforeHaltTests < UnitTests
143
+ desc "when run with a handler that halts in an after callback"
144
+ setup do
145
+ @runner = @runner_class.new(@handler_class, :params => {
146
+ 'halt' => 'before'
147
+ })
148
+ @handler = @runner.handler
149
+ @response = @runner.run
122
150
  end
151
+ subject{ @runner }
123
152
 
124
- should "call the block when provided a `nil` value" do
125
- block_run = false
153
+ should "stop processing when the halt is called" do
154
+ assert_not_nil @handler.first_before_call_order
155
+ assert_nil @handler.second_before_call_order
156
+ end
126
157
 
127
- subject.new(nil){ block_run = true }
128
- assert_nil @timeout_called_with
129
- assert_true block_run
158
+ should "not run the after callbacks b/c of the halt" do
159
+ assert_nil @handler.first_after_call_order
160
+ assert_nil @handler.second_after_call_order
161
+ end
162
+
163
+ should "not run the handler's init and run b/c of the halt" do
164
+ assert_nil @handler.init_call_order
165
+ assert_nil @handler.run_call_order
166
+ end
167
+
168
+ end
169
+
170
+ class RunWithAfterHaltTests < UnitTests
171
+ desc "when run with a handler that halts in an after callback"
172
+ setup do
173
+ @runner = @runner_class.new(@handler_class, :params => {
174
+ 'halt' => 'after'
175
+ })
176
+ @handler = @runner.handler
177
+ @response = @runner.run
178
+ end
179
+ subject{ @runner }
180
+
181
+ should "run the before callback despite the halt" do
182
+ assert_not_nil @handler.first_before_call_order
183
+ assert_not_nil @handler.second_before_call_order
184
+ end
185
+
186
+ should "run the handler's init and run despite the halt" do
187
+ assert_not_nil @handler.init_call_order
188
+ assert_not_nil @handler.run_call_order
189
+ end
190
+
191
+ should "stop processing when the halt is called" do
192
+ assert_not_nil @handler.first_after_call_order
193
+ assert_nil @handler.second_after_call_order
194
+ end
195
+
196
+ end
197
+
198
+ class RunWithTimeoutInterruptTests < RunSetupTests
199
+ setup do
200
+ Assert.stub(MuchTimeout, :optional_timeout){ raise TimeoutInterrupt }
201
+ end
202
+
203
+ should "raise a timeout error with a good message" do
204
+ exception = assert_raises(Qs::TimeoutError) do
205
+ @runner.run
206
+ end
207
+
208
+ exp = "#{@handler_class} timed out (#{@runner.timeout}s)"
209
+ assert_equal exp, exception.message
130
210
  end
131
211
 
132
212
  end
@@ -141,24 +221,30 @@ class Qs::QsRunner
141
221
 
142
222
  timeout Factory.integer
143
223
 
144
- before{ @first_before_call_order = next_call_order }
224
+ before{ @first_before_call_order = next_call_order; halt_if('before') }
145
225
  before{ @second_before_call_order = next_call_order }
146
226
 
147
- after{ @first_after_call_order = next_call_order }
227
+ after{ @first_after_call_order = next_call_order; halt_if('after') }
148
228
  after{ @second_after_call_order = next_call_order }
149
229
 
150
230
  def init!
151
231
  @init_call_order = next_call_order
232
+ halt_if('init')
152
233
  end
153
234
 
154
235
  def run!
155
236
  @run_call_order = next_call_order
237
+ halt_if('run')
156
238
  end
157
239
 
158
240
  private
159
241
 
160
242
  def next_call_order; @order ||= 0; @order += 1; end
161
243
 
244
+ def halt_if(value)
245
+ halt if params['halt'] == value
246
+ end
247
+
162
248
  end
163
249
 
164
250
  end
@@ -2,7 +2,6 @@ require 'assert'
2
2
  require 'qs'
3
3
 
4
4
  require 'hella-redis/connection_spy'
5
- require 'ns-options/assert_macros'
6
5
  require 'qs/queue'
7
6
 
8
7
  module Qs
@@ -24,7 +23,7 @@ module Qs
24
23
  should have_imeths :encode, :decode
25
24
  should have_imeths :sync_subscriptions, :clear_subscriptions
26
25
  should have_imeths :event_subscribers
27
- should have_imeths :client, :redis, :redis_config
26
+ should have_imeths :client, :redis, :redis_connect_hash
28
27
  should have_imeths :dispatcher_queue, :dispatcher_job_name
29
28
  should have_imeths :event_publisher
30
29
  should have_imeths :published_events
@@ -33,20 +32,23 @@ module Qs
33
32
  assert_instance_of Config, subject.config
34
33
  end
35
34
 
36
- should "allow configuring its config" do
35
+ should "configure its config" do
37
36
  yielded = nil
38
37
  subject.configure{ |c| yielded = c }
39
38
  assert_same subject.config, yielded
40
39
  end
41
40
 
42
- should "not have a client or redis connection by default" do
43
- assert_nil subject.client
41
+ should "have a null client by default" do
42
+ assert_instance_of NullClient, subject.client
43
+ end
44
+
45
+ should "not have a redis connection by default" do
44
46
  assert_nil subject.redis
45
47
  end
46
48
 
47
49
  should "know its redis config" do
48
- expected = subject.config.redis.to_hash
49
- assert_equal expected, subject.redis_config
50
+ exp = subject.config.redis_connect_hash
51
+ assert_equal exp, subject.redis_connect_hash
50
52
  end
51
53
 
52
54
  end
@@ -56,9 +58,9 @@ module Qs
56
58
  setup do
57
59
  @module.config.encoder = proc{ |v| v.to_s }
58
60
  @module.config.decoder = proc{ |v| v.to_i }
59
- @module.config.redis.ip = Factory.string
60
- @module.config.redis.port = Factory.integer
61
- @module.config.redis.db = Factory.integer
61
+ @module.config.redis_ip = Factory.string
62
+ @module.config.redis_port = Factory.integer
63
+ @module.config.redis_db = Factory.integer
62
64
 
63
65
  @dispatcher_queue_spy = nil
64
66
  Assert.stub(DispatcherQueue, :new) do |*args|
@@ -73,30 +75,26 @@ module Qs
73
75
  @module.init
74
76
  end
75
77
 
76
- should "set its configured redis url" do
77
- expected = RedisUrl.new(
78
- subject.config.redis.ip,
79
- subject.config.redis.port,
80
- subject.config.redis.db
81
- )
82
- assert_equal expected, subject.config.redis.url
78
+ should "validate its config" do
79
+ assert_true subject.config.valid?
83
80
  end
84
81
 
85
82
  should "build a dispatcher queue" do
86
83
  assert_equal @dispatcher_queue_spy, subject.dispatcher_queue
87
- exp = subject.config.dispatcher_queue_class
88
- assert_equal exp, @dispatcher_queue_spy.queue_class
89
- dispatcher_config = subject.config.dispatcher
90
- assert_equal dispatcher_config.queue_name, @dispatcher_queue_spy.queue_name
91
- assert_equal dispatcher_config.job_name, @dispatcher_queue_spy.job_name
92
- exp = dispatcher_config.job_handler_class_name
93
- assert_equal exp, @dispatcher_queue_spy.job_handler_class_name
84
+
85
+ c = subject.config
86
+ spy = @dispatcher_queue_spy
87
+ assert_equal c.dispatcher_queue_class, spy.queue_class
88
+ assert_equal c.dispatcher_queue_name, spy.queue_name
89
+ assert_equal c.dispatcher_job_name, spy.job_name
90
+ assert_equal c.dispatcher_job_handler_class_name, spy.job_handler_class_name
94
91
  end
95
92
 
96
93
  should "build a client" do
97
- assert_equal @client_spy, subject.client
98
- assert_equal @client_spy.redis, subject.redis
99
- assert_equal subject.redis_config, @client_spy.redis_config
94
+ assert_equal @client_spy, subject.client
95
+ assert_equal @client_spy.redis, subject.redis
96
+
97
+ assert_equal subject.redis_connect_hash, @client_spy.redis_connect_hash
100
98
  end
101
99
 
102
100
  should "call enqueue on its client using `enqueue`" do
@@ -149,14 +147,12 @@ module Qs
149
147
 
150
148
  should "use the configured encoder using `encode`" do
151
149
  value = Factory.integer
152
- result = subject.encode(value)
153
- assert_equal value.to_s, result
150
+ assert_equal value.to_s, subject.encode(value)
154
151
  end
155
152
 
156
153
  should "use the configured decoder using `decode`" do
157
154
  value = Factory.integer.to_s
158
- result = subject.decode(value)
159
- assert_equal value.to_i, result
155
+ assert_equal value.to_i, subject.decode(value)
160
156
  end
161
157
 
162
158
  should "demeter its clients subscription methods" do
@@ -180,24 +176,28 @@ module Qs
180
176
  end
181
177
 
182
178
  should "know its dispatcher job name and event publisher" do
183
- exp = subject.config.dispatcher.job_name
179
+ exp = subject.config.dispatcher_job_name
184
180
  assert_equal exp, subject.dispatcher_job_name
181
+
185
182
  exp = subject.config.event_publisher
186
183
  assert_equal exp, subject.event_publisher
187
184
  end
188
185
 
189
186
  should "return the dispatcher queue published events using `published_events`" do
190
- queue = subject.dispatcher_queue
191
- published_events = Factory.integer(3).times.map{ Factory.string }
192
- Assert.stub(queue, :published_events){ published_events }
193
- assert_equal queue.published_events, subject.published_events
187
+ exp = Factory.integer(3).times.map{ Factory.string }
188
+ Assert.stub(subject.dispatcher_queue, :published_events){ exp }
189
+
190
+ assert_equal exp, subject.published_events
194
191
  end
195
192
 
196
193
  should "not reset its attributes when init again" do
194
+ config = subject.config
197
195
  queue = subject.dispatcher_queue
198
196
  client = subject.client
199
197
  redis = subject.redis
200
198
  subject.init
199
+
200
+ assert_same config, subject.config
201
201
  assert_same queue, subject.dispatcher_queue
202
202
  assert_same client, subject.client
203
203
  assert_same redis, subject.redis
@@ -205,10 +205,11 @@ module Qs
205
205
 
206
206
  should "reset itself using `reset!`" do
207
207
  subject.reset!
208
- assert_nil subject.config.redis.url
208
+
209
+ assert_false subject.config.valid?
209
210
  assert_nil subject.dispatcher_queue
210
- assert_nil subject.client
211
211
  assert_nil subject.redis
212
+ assert_instance_of NullClient, subject.client
212
213
  assert_raises(NoMethodError){ subject.encode(Factory.integer) }
213
214
  assert_raises(NoMethodError){ subject.decode(Factory.integer) }
214
215
  end
@@ -216,54 +217,115 @@ module Qs
216
217
  end
217
218
 
218
219
  class ConfigTests < UnitTests
219
- include NsOptions::AssertMacros
220
-
221
220
  desc "Config"
222
221
  setup do
223
222
  @config = Config.new
224
223
  end
225
224
  subject{ @config }
226
225
 
227
- should have_options :encoder, :decoder, :timeout
228
- should have_options :event_publisher
229
- should have_namespace :dispatcher, :redis
230
- should have_accessors :dispatcher_queue_class
226
+ should have_accessors :encoder, :decoder, :timeout, :event_publisher
227
+ should have_accessors :dispatcher_queue_class, :dispatcher_queue_name
228
+ should have_accessors :dispatcher_job_name, :dispatcher_job_handler_class_name
229
+ should have_accessors :redis_ip, :redis_port, :redis_db, :redis_ns
230
+ should have_accessors :redis_driver, :redis_timeout, :redis_size, :redis_url
231
+ should have_imeths :redis_connect_hash, :valid?, :validate!
231
232
 
232
- should "know its default decoder/encoder" do
233
+ should "know its default attr values" do
233
234
  payload = { Factory.string => Factory.string }
234
235
 
235
236
  exp = JSON.dump(payload)
236
- encoded_payload = subject.encoder.call(payload)
237
- assert_equal exp, encoded_payload
238
- exp = JSON.load(exp)
239
- assert_equal exp, subject.decoder.call(encoded_payload)
237
+ assert_equal exp, Config::DEFAULT_ENCODER.call(payload)
238
+
239
+ encoded_payload = exp
240
+ exp = JSON.load(encoded_payload)
241
+ assert_equal exp, Config::DEFAULT_DECODER.call(encoded_payload)
242
+
243
+ assert_equal Queue, Config::DEFAULT_DISPATCHER_QUEUE_CLASS
244
+ assert_equal 'dispatcher', Config::DEFAULT_DISPATCHER_QUEUE_NAME
245
+ assert_equal 'run_dispatch_job', Config::DEFAULT_DISPATCHER_JOB_NAME
246
+
247
+ exp = DispatcherQueue::RunDispatchJob.to_s
248
+ assert_equal exp, Config::DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME
249
+
250
+ assert_equal '127.0.0.1', Config::DEFAULT_REDIS_IP
251
+ assert_equal 6379, Config::DEFAULT_REDIS_PORT
252
+ assert_equal 0, Config::DEFAULT_REDIS_DB
253
+ assert_equal 'qs', Config::DEFAULT_REDIS_NS
254
+ assert_equal 'ruby', Config::DEFAULT_REDIS_DRIVER
255
+ assert_equal 1, Config::DEFAULT_REDIS_TIMEOUT
256
+ assert_equal 4, Config::DEFAULT_REDIS_SIZE
240
257
  end
241
258
 
242
- should "know its default timeout" do
259
+ should "default its attrs" do
260
+ c = subject.class
261
+
262
+ assert_equal c::DEFAULT_ENCODER, subject.encoder
263
+ assert_equal c::DEFAULT_DECODER, subject.decoder
264
+
243
265
  assert_nil subject.timeout
266
+ assert_nil subject.event_publisher
267
+
268
+ assert_equal c::DEFAULT_DISPATCHER_QUEUE_CLASS, subject.dispatcher_queue_class
269
+ assert_equal c::DEFAULT_DISPATCHER_QUEUE_NAME, subject.dispatcher_queue_name
270
+ assert_equal c::DEFAULT_DISPATCHER_JOB_NAME, subject.dispatcher_job_name
271
+
272
+ exp = c::DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME
273
+ assert_equal exp, subject.dispatcher_job_handler_class_name
274
+
275
+ assert_equal c::DEFAULT_REDIS_IP, subject.redis_ip
276
+ assert_equal c::DEFAULT_REDIS_PORT, subject.redis_port
277
+ assert_equal c::DEFAULT_REDIS_DB, subject.redis_db
278
+ assert_equal c::DEFAULT_REDIS_NS, subject.redis_ns
279
+ assert_equal c::DEFAULT_REDIS_DRIVER, subject.redis_driver
280
+ assert_equal c::DEFAULT_REDIS_TIMEOUT, subject.redis_timeout
281
+ assert_equal c::DEFAULT_REDIS_SIZE, subject.redis_size
282
+
283
+ assert_nil subject.redis_url
244
284
  end
245
285
 
246
- should "not have a default event publisher" do
247
- assert_nil subject.event_publisher
286
+ should "know its redis connect hash" do
287
+ exp = {
288
+ :ip => subject.redis_ip,
289
+ :port => subject.redis_port,
290
+ :db => subject.redis_db,
291
+ :redis_ns => subject.redis_ns,
292
+ :driver => subject.redis_driver,
293
+ :timeout => subject.redis_timeout,
294
+ :size => subject.redis_size,
295
+ :url => subject.redis_url
296
+ }
297
+ assert_equal exp, subject.redis_connect_hash
248
298
  end
249
299
 
250
- should "know its default dispatcher options" do
251
- assert_equal Queue, subject.dispatcher_queue_class
252
- assert_equal 'dispatcher', subject.dispatcher.queue_name
253
- assert_equal 'run_dispatch_job', subject.dispatcher.job_name
254
- exp = DispatcherQueue::RunDispatchJob.to_s
255
- assert_equal exp, subject.dispatcher.job_handler_class_name
300
+ should "not be valid until validate! has been called" do
301
+ assert_false subject.valid?
302
+
303
+ subject.validate!
304
+ assert_true subject.valid?
256
305
  end
257
306
 
258
- should "know its default redis options" do
259
- assert_equal '127.0.0.1', subject.redis.ip
260
- assert_equal 6379, subject.redis.port
261
- assert_equal 0, subject.redis.db
262
- assert_equal 'qs', subject.redis.redis_ns
263
- assert_equal 'ruby', subject.redis.driver
264
- assert_equal 1, subject.redis.timeout
265
- assert_equal 4, subject.redis.size
266
- assert_nil subject.redis.url
307
+ should "set its redis url on validate!" do
308
+ assert_nil subject.redis_url
309
+ subject.validate!
310
+
311
+ exp = RedisUrl.new(subject.redis_ip, subject.redis_port, subject.redis_db)
312
+ assert_equal exp, subject.redis_url
313
+ end
314
+
315
+ should "only be able to be validated once" do
316
+ subject.validate!
317
+
318
+ exp = RedisUrl.new(subject.redis_ip, subject.redis_port, subject.redis_db)
319
+ assert_equal exp, subject.redis_url
320
+
321
+ new_ip = Factory.string
322
+ subject.redis_ip = new_ip
323
+ subject.validate!
324
+
325
+ orig_exp = exp
326
+ new_exp = RedisUrl.new(new_ip, subject.redis_port, subject.redis_db)
327
+ assert_not_equal new_exp, subject.redis_url
328
+ assert_equal orig_exp, subject.redis_url
267
329
  end
268
330
 
269
331
  end
@@ -276,8 +338,8 @@ module Qs
276
338
  ip = Factory.string
277
339
  port = Factory.integer
278
340
  db = Factory.integer
279
- expected = "redis://#{ip}:#{port}/#{db}"
280
- assert_equal expected, subject.new(ip, port, db)
341
+ exp = "redis://#{ip}:#{port}/#{db}"
342
+ assert_equal exp, subject.new(ip, port, db)
281
343
  end
282
344
 
283
345
  should "not return a url with an ip, port or db" do
@@ -288,6 +350,49 @@ module Qs
288
350
 
289
351
  end
290
352
 
353
+ class NullClientTests < UnitTests
354
+ desc "NullClient"
355
+ setup do
356
+ @null_client = NullClient.new
357
+ end
358
+ subject{ @null_client }
359
+
360
+ should have_imeths :enqueue, :publish, :publish_as, :push
361
+ should have_imeths :sync_subscriptions, :clear_subscriptions
362
+ should have_imeths :event_subscribers
363
+
364
+ should "raise uninitialized errors" do
365
+ queue = Qs::Queue.new{ name Factory.string }
366
+ job_name = Factory.string
367
+ job_params = { Factory.string => Factory.string }
368
+ assert_raises(UninitializedError) do
369
+ subject.enqueue(queue, job_name, job_params)
370
+ end
371
+
372
+ event_channel = Factory.string
373
+ event_name = Factory.string
374
+ event_params = { Factory.string => Factory.string }
375
+ assert_raises(UninitializedError) do
376
+ subject.publish(event_channel, event_name, event_params)
377
+ end
378
+
379
+ event_publisher = Factory.string
380
+ assert_raises(UninitializedError) do
381
+ subject.publish_as(event_publisher, event_channel, event_name, event_params)
382
+ end
383
+
384
+ payload = { Factory.string => Factory.string }
385
+ assert_raises(UninitializedError){ subject.push(queue.name, payload) }
386
+
387
+ assert_raises(UninitializedError){ subject.sync_subscriptions(queue) }
388
+ assert_raises(UninitializedError){ subject.clear_subscriptions(queue) }
389
+
390
+ event = Factory.event
391
+ assert_raises(UninitializedError){ subject.event_subscribers(event) }
392
+ end
393
+
394
+ end
395
+
291
396
  class DispatcherQueueSpy
292
397
  attr_reader :queue_class, :queue_name
293
398
  attr_reader :job_name, :job_handler_class_name
@@ -302,17 +407,17 @@ module Qs
302
407
  end
303
408
 
304
409
  class ClientSpy
305
- attr_reader :redis_config, :redis
410
+ attr_reader :redis_connect_hash, :redis
306
411
  attr_reader :enqueue_calls, :publish_calls, :push_calls
307
412
  attr_reader :sync_subscriptions_calls, :clear_subscriptions_calls
308
413
  attr_reader :event_subscribers_calls
309
414
 
310
415
  def initialize(redis_confg)
311
- @redis_config = redis_confg
312
- @redis = Factory.string
313
- @enqueue_calls = []
314
- @publish_calls = []
315
- @push_calls = []
416
+ @redis_connect_hash = redis_confg
417
+ @redis = Factory.string
418
+ @enqueue_calls = []
419
+ @publish_calls = []
420
+ @push_calls = []
316
421
  @sync_subscriptions_calls = []
317
422
  @clear_subscriptions_calls = []
318
423
  @event_subscribers_calls = []