qs 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +6 -1
  3. data/LICENSE.txt +1 -1
  4. data/bench/config.qs +46 -0
  5. data/bench/queue.rb +8 -0
  6. data/bench/report.rb +114 -0
  7. data/bench/report.txt +11 -0
  8. data/bin/qs +7 -0
  9. data/lib/qs/cli.rb +124 -0
  10. data/lib/qs/client.rb +121 -0
  11. data/lib/qs/config_file.rb +79 -0
  12. data/lib/qs/daemon.rb +350 -0
  13. data/lib/qs/daemon_data.rb +46 -0
  14. data/lib/qs/error_handler.rb +58 -0
  15. data/lib/qs/job.rb +70 -0
  16. data/lib/qs/job_handler.rb +90 -0
  17. data/lib/qs/logger.rb +23 -0
  18. data/lib/qs/payload_handler.rb +136 -0
  19. data/lib/qs/pid_file.rb +42 -0
  20. data/lib/qs/process.rb +136 -0
  21. data/lib/qs/process_signal.rb +20 -0
  22. data/lib/qs/qs_runner.rb +49 -0
  23. data/lib/qs/queue.rb +69 -0
  24. data/lib/qs/redis_item.rb +33 -0
  25. data/lib/qs/route.rb +52 -0
  26. data/lib/qs/runner.rb +26 -0
  27. data/lib/qs/test_helpers.rb +17 -0
  28. data/lib/qs/test_runner.rb +43 -0
  29. data/lib/qs/version.rb +1 -1
  30. data/lib/qs.rb +92 -2
  31. data/qs.gemspec +7 -2
  32. data/test/helper.rb +8 -1
  33. data/test/support/app_daemon.rb +74 -0
  34. data/test/support/config.qs +7 -0
  35. data/test/support/config_files/empty.qs +0 -0
  36. data/test/support/config_files/invalid.qs +1 -0
  37. data/test/support/config_files/valid.qs +7 -0
  38. data/test/support/config_invalid_run.qs +3 -0
  39. data/test/support/config_no_run.qs +0 -0
  40. data/test/support/factory.rb +14 -0
  41. data/test/support/pid_file_spy.rb +19 -0
  42. data/test/support/runner_spy.rb +17 -0
  43. data/test/system/daemon_tests.rb +226 -0
  44. data/test/unit/cli_tests.rb +188 -0
  45. data/test/unit/client_tests.rb +269 -0
  46. data/test/unit/config_file_tests.rb +59 -0
  47. data/test/unit/daemon_data_tests.rb +96 -0
  48. data/test/unit/daemon_tests.rb +702 -0
  49. data/test/unit/error_handler_tests.rb +163 -0
  50. data/test/unit/job_handler_tests.rb +253 -0
  51. data/test/unit/job_tests.rb +132 -0
  52. data/test/unit/logger_tests.rb +38 -0
  53. data/test/unit/payload_handler_tests.rb +276 -0
  54. data/test/unit/pid_file_tests.rb +70 -0
  55. data/test/unit/process_signal_tests.rb +61 -0
  56. data/test/unit/process_tests.rb +371 -0
  57. data/test/unit/qs_runner_tests.rb +166 -0
  58. data/test/unit/qs_tests.rb +217 -0
  59. data/test/unit/queue_tests.rb +132 -0
  60. data/test/unit/redis_item_tests.rb +49 -0
  61. data/test/unit/route_tests.rb +81 -0
  62. data/test/unit/runner_tests.rb +63 -0
  63. data/test/unit/test_helper_tests.rb +61 -0
  64. data/test/unit/test_runner_tests.rb +128 -0
  65. metadata +180 -15
@@ -0,0 +1,163 @@
1
+ require 'assert'
2
+ require 'qs/error_handler'
3
+
4
+ require 'qs/daemon_data'
5
+ require 'qs/queue'
6
+
7
+ class Qs::ErrorHandler
8
+
9
+ class UnitTests < Assert::Context
10
+ desc "Qs::ErrorHandler"
11
+ setup do
12
+ @exception = Factory.exception
13
+ @daemon_data = Qs::DaemonData.new
14
+ @queue_name = Factory.string
15
+ @queue_redis_key = Qs::Queue::RedisKey.new(@queue_name)
16
+ @context_hash = {
17
+ :daemon_data => @daemon_data,
18
+ :queue_redis_key => @queue_redis_key,
19
+ :serialized_payload => Factory.string,
20
+ :job => Factory.string,
21
+ :handler_class => Factory.string
22
+ }
23
+
24
+ @handler_class = Qs::ErrorHandler
25
+ end
26
+ subject{ @handler_class }
27
+
28
+ end
29
+
30
+ class InitTests < UnitTests
31
+ desc "when init"
32
+ setup do
33
+ @call_count = 0
34
+ @first_called_at = nil
35
+ @second_called_at = nil
36
+ @args_passed_to_error_proc = nil
37
+ first_error_proc = proc do |*args|
38
+ @args_passed_to_error_proc = args
39
+ @first_called_at = (@call_count += 1)
40
+ end
41
+ second_error_proc = proc{ @second_called_at = (@call_count += 1) }
42
+ Assert.stub(@daemon_data, :error_procs){ [first_error_proc, second_error_proc] }
43
+
44
+ @handler = @handler_class.new(@exception, @context_hash)
45
+ end
46
+ subject{ @handler }
47
+
48
+ should have_readers :exception, :context
49
+ should have_imeths :run
50
+
51
+ should "know its exception and context" do
52
+ assert_equal @exception, subject.exception
53
+ exp = Qs::ErrorContext.new(@context_hash)
54
+ assert_equal exp, subject.context
55
+ end
56
+
57
+ should "know its error procs" do
58
+ assert_equal @daemon_data.error_procs.reverse, subject.error_procs
59
+ end
60
+
61
+ end
62
+
63
+ class RunTests < InitTests
64
+ desc "and run"
65
+ setup do
66
+ @handler.run
67
+ end
68
+
69
+ should "pass its exception and context to the error procs" do
70
+ assert_not_nil @args_passed_to_error_proc
71
+ assert_includes subject.exception, @args_passed_to_error_proc
72
+ assert_includes subject.context, @args_passed_to_error_proc
73
+ end
74
+
75
+ should "call each of its error procs" do
76
+ assert_equal 1, @second_called_at
77
+ assert_equal 2, @first_called_at
78
+ end
79
+
80
+ end
81
+
82
+ class RunAndErrorProcThrowsExceptionTests < UnitTests
83
+ desc "run with an error proc that throws an exception"
84
+ setup do
85
+ @proc_exception = Factory.exception
86
+ error_proc = proc{ raise @proc_exception }
87
+ Assert.stub(@daemon_data, :error_procs){ [error_proc] }
88
+
89
+ @handler = @handler_class.new(@exception, @context_hash).tap(&:run)
90
+ end
91
+ subject{ @handler }
92
+
93
+ should "set its exception to the exception thrown by the error proc" do
94
+ assert_equal @proc_exception, subject.exception
95
+ end
96
+
97
+ end
98
+
99
+ class RunWithMultipleErrorProcsThatThrowExceptionsTests < UnitTests
100
+ desc "run with multiple error procs that throw an exception"
101
+ setup do
102
+ @first_caught_exception = nil
103
+ @second_caught_exception = nil
104
+ @third_caught_exception = nil
105
+
106
+ @third_proc_exception = Factory.exception
107
+ third_proc = proc do |exception, context|
108
+ @third_caught_exception = exception
109
+ raise @third_proc_exception
110
+ end
111
+
112
+ @second_proc_exception = Factory.exception
113
+ second_proc = proc do |exception, context|
114
+ @second_caught_exception = exception
115
+ raise @second_proc_exception
116
+ end
117
+
118
+ first_proc = proc{ |exception, context| @first_caught_exception = exception }
119
+
120
+ Assert.stub(@daemon_data, :error_procs){ [first_proc, second_proc, third_proc] }
121
+ @handler = @handler_class.new(@exception, @context_hash).tap(&:run)
122
+ end
123
+ subject{ @handler }
124
+
125
+ should "call each proc, passing the previously raised exception to the next" do
126
+ assert_equal @exception, @third_caught_exception
127
+ assert_equal @third_proc_exception, @second_caught_exception
128
+ assert_equal @second_proc_exception, @first_caught_exception
129
+ end
130
+
131
+ end
132
+
133
+ class ErrorContextTests < UnitTests
134
+ desc "ErrorContext"
135
+ setup do
136
+ @context = Qs::ErrorContext.new(@context_hash)
137
+ end
138
+ subject{ @context }
139
+
140
+ should have_readers :daemon_data
141
+ should have_readers :queue_name, :serialized_payload
142
+ should have_readers :job, :handler_class
143
+
144
+ should "know its attributes" do
145
+ assert_equal @context_hash[:daemon_data], subject.daemon_data
146
+ exp = Qs::Queue::RedisKey.parse_name(@context_hash[:queue_redis_key])
147
+ assert_equal exp, subject.queue_name
148
+ assert_equal @context_hash[:serialized_payload], subject.serialized_payload
149
+ assert_equal @context_hash[:job], subject.job
150
+ assert_equal @context_hash[:handler_class], subject.handler_class
151
+ end
152
+
153
+ should "know if it equals another context" do
154
+ exp = Qs::ErrorContext.new(@context_hash)
155
+ assert_equal exp, subject
156
+
157
+ exp = Qs::ErrorContext.new({})
158
+ assert_not_equal exp, subject
159
+ end
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,253 @@
1
+ require 'assert'
2
+ require 'qs/job_handler'
3
+
4
+ module Qs::JobHandler
5
+
6
+ class UnitTests < Assert::Context
7
+ desc "Qs::JobHandler"
8
+ setup do
9
+ @handler_class = Class.new{ include Qs::JobHandler }
10
+ end
11
+ subject{ @handler_class }
12
+
13
+ should have_imeths :timeout
14
+ should have_imeths :before_callbacks, :after_callbacks
15
+ should have_imeths :before_init_callbacks, :after_init_callbacks
16
+ should have_imeths :before_run_callbacks, :after_run_callbacks
17
+ should have_imeths :before, :after
18
+ should have_imeths :before_init, :after_init
19
+ should have_imeths :before_run, :after_run
20
+ should have_imeths :prepend_before, :prepend_after
21
+ should have_imeths :prepend_before_init, :prepend_after_init
22
+ should have_imeths :prepend_before_run, :prepend_after_run
23
+
24
+ should "allow reading/writing its timeout" do
25
+ assert_nil subject.timeout
26
+ value = Factory.integer
27
+ subject.timeout(value)
28
+ assert_equal value, subject.timeout
29
+ end
30
+
31
+ should "convert timeout values to floats" do
32
+ value = Factory.float.to_s
33
+ subject.timeout(value)
34
+ assert_equal value.to_f, subject.timeout
35
+ end
36
+
37
+ should "return an empty array by default using `before_callbacks`" do
38
+ assert_equal [], subject.before_callbacks
39
+ end
40
+
41
+ should "return an empty array by default using `after_callbacks`" do
42
+ assert_equal [], subject.after_callbacks
43
+ end
44
+
45
+ should "return an empty array by default using `before_init_callbacks`" do
46
+ assert_equal [], subject.before_init_callbacks
47
+ end
48
+
49
+ should "return an empty array by default using `after_init_callbacks`" do
50
+ assert_equal [], subject.after_init_callbacks
51
+ end
52
+
53
+ should "return an empty array by default using `before_run_callbacks`" do
54
+ assert_equal [], subject.before_run_callbacks
55
+ end
56
+
57
+ should "return an empty array by default using `after_run_callbacks`" do
58
+ assert_equal [], subject.after_run_callbacks
59
+ end
60
+
61
+ should "append a block to the before callbacks using `before`" do
62
+ subject.before_callbacks << proc{ Factory.string }
63
+ block = Proc.new{ Factory.string }
64
+ subject.before(&block)
65
+ assert_equal block, subject.before_callbacks.last
66
+ end
67
+
68
+ should "append a block to the after callbacks using `after`" do
69
+ subject.after_callbacks << proc{ Factory.string }
70
+ block = Proc.new{ Factory.string }
71
+ subject.after(&block)
72
+ assert_equal block, subject.after_callbacks.last
73
+ end
74
+
75
+ should "append a block to the before init callbacks using `before_init`" do
76
+ subject.before_init_callbacks << proc{ Factory.string }
77
+ block = Proc.new{ Factory.string }
78
+ subject.before_init(&block)
79
+ assert_equal block, subject.before_init_callbacks.last
80
+ end
81
+
82
+ should "append a block to the after init callbacks using `after_init`" do
83
+ subject.after_init_callbacks << proc{ Factory.string }
84
+ block = Proc.new{ Factory.string }
85
+ subject.after_init(&block)
86
+ assert_equal block, subject.after_init_callbacks.last
87
+ end
88
+
89
+ should "append a block to the before run callbacks using `before_run`" do
90
+ subject.before_run_callbacks << proc{ Factory.string }
91
+ block = Proc.new{ Factory.string }
92
+ subject.before_run(&block)
93
+ assert_equal block, subject.before_run_callbacks.last
94
+ end
95
+
96
+ should "append a block to the after run callbacks using `after_run`" do
97
+ subject.after_run_callbacks << proc{ Factory.string }
98
+ block = Proc.new{ Factory.string }
99
+ subject.after_run(&block)
100
+ assert_equal block, subject.after_run_callbacks.last
101
+ end
102
+
103
+ should "prepend a block to the before callbacks using `prepend_before`" do
104
+ subject.before_callbacks << proc{ Factory.string }
105
+ block = Proc.new{ Factory.string }
106
+ subject.prepend_before(&block)
107
+ assert_equal block, subject.before_callbacks.first
108
+ end
109
+
110
+ should "prepend a block to the after callbacks using `prepend_after`" do
111
+ subject.after_callbacks << proc{ Factory.string }
112
+ block = Proc.new{ Factory.string }
113
+ subject.prepend_after(&block)
114
+ assert_equal block, subject.after_callbacks.first
115
+ end
116
+
117
+ should "prepend a block to the before init callbacks using `prepend_before_init`" do
118
+ subject.before_init_callbacks << proc{ Factory.string }
119
+ block = Proc.new{ Factory.string }
120
+ subject.prepend_before_init(&block)
121
+ assert_equal block, subject.before_init_callbacks.first
122
+ end
123
+
124
+ should "prepend a block to the after init callbacks using `prepend_after_init`" do
125
+ subject.after_init_callbacks << proc{ Factory.string }
126
+ block = Proc.new{ Factory.string }
127
+ subject.prepend_after_init(&block)
128
+ assert_equal block, subject.after_init_callbacks.first
129
+ end
130
+
131
+ should "prepend a block to the before run callbacks using `prepend_before_run`" do
132
+ subject.before_run_callbacks << proc{ Factory.string }
133
+ block = Proc.new{ Factory.string }
134
+ subject.prepend_before_run(&block)
135
+ assert_equal block, subject.before_run_callbacks.first
136
+ end
137
+
138
+ should "prepend a block to the after run callbacks using `prepend_after_run`" do
139
+ subject.after_run_callbacks << proc{ Factory.string }
140
+ block = Proc.new{ Factory.string }
141
+ subject.prepend_after_run(&block)
142
+ assert_equal block, subject.after_run_callbacks.first
143
+ end
144
+
145
+ end
146
+
147
+ class InitTests < UnitTests
148
+ desc "when init"
149
+ setup do
150
+ @runner = FakeRunner.new
151
+ @handler = TestJobHandler.new(@runner)
152
+ end
153
+ subject{ @handler }
154
+
155
+ should have_imeths :init, :init!, :run, :run!
156
+
157
+ should "know its job, params and logger" do
158
+ assert_equal @runner.job, subject.public_job
159
+ assert_equal @runner.params, subject.public_params
160
+ assert_equal @runner.logger, subject.public_logger
161
+ end
162
+
163
+ should "call `init!` and its before/after init callbacks using `init`" do
164
+ subject.init
165
+ assert_equal 1, subject.first_before_init_call_order
166
+ assert_equal 2, subject.second_before_init_call_order
167
+ assert_equal 3, subject.init_call_order
168
+ assert_equal 4, subject.first_after_init_call_order
169
+ assert_equal 5, subject.second_after_init_call_order
170
+ end
171
+
172
+ should "call `run!` and its before/after run callbacks using `run`" do
173
+ subject.run
174
+ assert_equal 1, subject.first_before_run_call_order
175
+ assert_equal 2, subject.second_before_run_call_order
176
+ assert_equal 3, subject.run_call_order
177
+ assert_equal 4, subject.first_after_run_call_order
178
+ assert_equal 5, subject.second_after_run_call_order
179
+ end
180
+
181
+ should "have a custom inspect" do
182
+ reference = '0x0%x' % (subject.object_id << 1)
183
+ expected = "#<#{subject.class}:#{reference} " \
184
+ "@job=#{@runner.job.inspect}>"
185
+ assert_equal expected, subject.inspect
186
+ end
187
+
188
+ should "raise a not implemented error when `run!` by default" do
189
+ assert_raises(NotImplementedError){ @handler_class.new(@runner).run! }
190
+ end
191
+
192
+ end
193
+
194
+ class TestJobHandler
195
+ include Qs::JobHandler
196
+
197
+ attr_reader :first_before_init_call_order, :second_before_init_call_order
198
+ attr_reader :first_after_init_call_order, :second_after_init_call_order
199
+ attr_reader :first_before_run_call_order, :second_before_run_call_order
200
+ attr_reader :first_after_run_call_order, :second_after_run_call_order
201
+ attr_reader :init_call_order, :run_call_order
202
+
203
+ before_init{ @first_before_init_call_order = next_call_order }
204
+ before_init{ @second_before_init_call_order = next_call_order }
205
+
206
+ after_init{ @first_after_init_call_order = next_call_order }
207
+ after_init{ @second_after_init_call_order = next_call_order }
208
+
209
+ before_run{ @first_before_run_call_order = next_call_order }
210
+ before_run{ @second_before_run_call_order = next_call_order }
211
+
212
+ after_run{ @first_after_run_call_order = next_call_order }
213
+ after_run{ @second_after_run_call_order = next_call_order }
214
+
215
+ def init!
216
+ @init_call_order = next_call_order
217
+ end
218
+
219
+ def run!
220
+ @run_call_order = next_call_order
221
+ end
222
+
223
+ def public_job
224
+ job
225
+ end
226
+
227
+ def public_params
228
+ params
229
+ end
230
+
231
+ def public_logger
232
+ logger
233
+ end
234
+
235
+ private
236
+
237
+ def next_call_order
238
+ @order ||= 0
239
+ @order += 1
240
+ end
241
+ end
242
+
243
+ class FakeRunner
244
+ attr_accessor :job, :params, :logger
245
+
246
+ def initialize
247
+ @job = Factory.string
248
+ @params = Factory.string
249
+ @logger = Factory.string
250
+ end
251
+ end
252
+
253
+ end
@@ -0,0 +1,132 @@
1
+ require 'assert'
2
+ require 'qs/job'
3
+
4
+ class Qs::Job
5
+
6
+ class UnitTests < Assert::Context
7
+ desc "Qs::Job"
8
+ setup do
9
+ @name = Factory.string
10
+ @params = { Factory.string => Factory.string }
11
+ @created_at = Factory.time
12
+
13
+ @job_class = Qs::Job
14
+ end
15
+ subject{ @job_class }
16
+
17
+ should have_imeths :parse
18
+
19
+ should "parse a job from a payload hash" do
20
+ payload = {
21
+ 'name' => @name,
22
+ 'params' => @params,
23
+ 'created_at' => @created_at.to_i
24
+ }
25
+ job = subject.parse(payload)
26
+ assert_instance_of subject, job
27
+ assert_equal payload['name'], job.name
28
+ assert_equal payload['params'], job.params
29
+ assert_equal Time.at(payload['created_at']), job.created_at
30
+ end
31
+
32
+ end
33
+
34
+ class InitTests < UnitTests
35
+ desc "when init"
36
+ setup do
37
+ @current_time = Factory.time
38
+ Assert.stub(Time, :now).with{ @current_time }
39
+
40
+ @job = @job_class.new(@name, @params, @created_at)
41
+ end
42
+ subject{ @job }
43
+
44
+ should have_readers :name, :params, :created_at
45
+ should have_imeths :to_payload
46
+
47
+ should "know its name, params and created at" do
48
+ assert_equal @name, subject.name
49
+ assert_equal @params, subject.params
50
+ assert_equal @created_at, subject.created_at
51
+ end
52
+
53
+ should "default its created at" do
54
+ job = @job_class.new(@name, @params)
55
+ assert_equal @current_time, job.created_at
56
+ end
57
+
58
+ should "return a payload hash using `to_payload`" do
59
+ payload_hash = subject.to_payload
60
+ expected = {
61
+ 'name' => @name,
62
+ 'params' => @params,
63
+ 'created_at' => @created_at.to_i
64
+ }
65
+ assert_equal expected, payload_hash
66
+ end
67
+
68
+ should "convert job names to strings using `to_payload`" do
69
+ job = @job_class.new(@name.to_sym, @params)
70
+ assert_equal @name, job.to_payload['name']
71
+ end
72
+
73
+ should "convert stringify its params using `to_payload`" do
74
+ params = { Factory.string.to_sym => Factory.string }
75
+ job = @job_class.new(@name, params)
76
+ exp = StringifyParams.new(params)
77
+ assert_equal exp, job.to_payload['params']
78
+ end
79
+
80
+ should "raise an error when given an invalid name or params" do
81
+ assert_raises(Qs::BadJobError){ @job_class.new(nil, @params) }
82
+ assert_raises(Qs::BadJobError){ @job_class.new(@name, nil) }
83
+ assert_raises(Qs::BadJobError){ @job_class.new(@name, Factory.string) }
84
+ end
85
+
86
+ should "have a custom inspect" do
87
+ reference = '0x0%x' % (subject.object_id << 1)
88
+ expected = "#<Qs::Job:#{reference} " \
89
+ "@name=#{subject.name.inspect} " \
90
+ "@params=#{subject.params.inspect} " \
91
+ "@created_at=#{subject.created_at.inspect}>"
92
+ assert_equal expected, subject.inspect
93
+ end
94
+
95
+ should "be comparable" do
96
+ matching = @job_class.new(@name, @params, @created_at)
97
+ assert_equal matching, subject
98
+ non_matching = @job_class.new(Factory.string, @params, @created_at)
99
+ assert_not_equal non_matching, subject
100
+ params = { Factory.string => Factory.string }
101
+ non_matching = @job_class.new(@name, params, @created_at)
102
+ assert_not_equal non_matching, subject
103
+ non_matching = @job_class.new(@name, @params, Factory.time)
104
+ assert_not_equal non_matching, subject
105
+ end
106
+
107
+ end
108
+
109
+ class StringifyParamsTests < UnitTests
110
+ desc "StringifyParams"
111
+ subject{ StringifyParams }
112
+
113
+ should have_imeths :new
114
+
115
+ should "convert all hash keys to strings" do
116
+ key, value = Factory.string.to_sym, Factory.string
117
+ result = subject.new({
118
+ key => value,
119
+ :hash => { key => [value] },
120
+ :array => [{ key => value }]
121
+ })
122
+ exp = {
123
+ key.to_s => value,
124
+ 'hash' => { key.to_s => [value] },
125
+ 'array' => [{ key.to_s => value }]
126
+ }
127
+ assert_equal exp, result
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,38 @@
1
+ require 'assert'
2
+ require 'qs/logger'
3
+
4
+ class Qs::Logger
5
+
6
+ class UnitTests < Assert::Context
7
+ desc "Qs::Logger"
8
+ setup do
9
+ @real_logger = Factory.string
10
+ end
11
+ subject{ Qs::Logger }
12
+
13
+ should "set its loggers correctly in summary mode" do
14
+ logger = subject.new(@real_logger, false)
15
+ assert_equal @real_logger, logger.summary
16
+ assert_instance_of Qs::NullLogger, logger.verbose
17
+ end
18
+
19
+ should "set its loggers correctly in verbose mode" do
20
+ logger = subject.new(@real_logger, true)
21
+ assert_instance_of Qs::NullLogger, logger.summary
22
+ assert_equal @real_logger, logger.verbose
23
+ end
24
+
25
+ end
26
+
27
+ class NullLoggerTests < Assert::Context
28
+ desc "Qs::NullLogger"
29
+ setup do
30
+ @null_logger = Qs::NullLogger.new
31
+ end
32
+ subject{ @null_logger }
33
+
34
+ should have_imeths :debug, :info, :error
35
+
36
+ end
37
+
38
+ end