roundhouse-x 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +16 -0
  4. data/3.0-Upgrade.md +70 -0
  5. data/Changes.md +1127 -0
  6. data/Gemfile +27 -0
  7. data/LICENSE +7 -0
  8. data/README.md +52 -0
  9. data/Rakefile +9 -0
  10. data/bin/roundhouse +19 -0
  11. data/bin/roundhousectl +93 -0
  12. data/lib/generators/roundhouse/templates/worker.rb.erb +9 -0
  13. data/lib/generators/roundhouse/templates/worker_spec.rb.erb +6 -0
  14. data/lib/generators/roundhouse/templates/worker_test.rb.erb +8 -0
  15. data/lib/generators/roundhouse/worker_generator.rb +49 -0
  16. data/lib/roundhouse/actor.rb +39 -0
  17. data/lib/roundhouse/api.rb +859 -0
  18. data/lib/roundhouse/cli.rb +396 -0
  19. data/lib/roundhouse/client.rb +210 -0
  20. data/lib/roundhouse/core_ext.rb +105 -0
  21. data/lib/roundhouse/exception_handler.rb +30 -0
  22. data/lib/roundhouse/fetch.rb +154 -0
  23. data/lib/roundhouse/launcher.rb +98 -0
  24. data/lib/roundhouse/logging.rb +104 -0
  25. data/lib/roundhouse/manager.rb +236 -0
  26. data/lib/roundhouse/middleware/chain.rb +149 -0
  27. data/lib/roundhouse/middleware/i18n.rb +41 -0
  28. data/lib/roundhouse/middleware/server/active_record.rb +13 -0
  29. data/lib/roundhouse/middleware/server/logging.rb +40 -0
  30. data/lib/roundhouse/middleware/server/retry_jobs.rb +206 -0
  31. data/lib/roundhouse/monitor.rb +124 -0
  32. data/lib/roundhouse/paginator.rb +42 -0
  33. data/lib/roundhouse/processor.rb +159 -0
  34. data/lib/roundhouse/rails.rb +24 -0
  35. data/lib/roundhouse/redis_connection.rb +77 -0
  36. data/lib/roundhouse/scheduled.rb +115 -0
  37. data/lib/roundhouse/testing/inline.rb +28 -0
  38. data/lib/roundhouse/testing.rb +193 -0
  39. data/lib/roundhouse/util.rb +68 -0
  40. data/lib/roundhouse/version.rb +3 -0
  41. data/lib/roundhouse/web.rb +264 -0
  42. data/lib/roundhouse/web_helpers.rb +249 -0
  43. data/lib/roundhouse/worker.rb +90 -0
  44. data/lib/roundhouse.rb +177 -0
  45. data/roundhouse.gemspec +27 -0
  46. data/test/config.yml +9 -0
  47. data/test/env_based_config.yml +11 -0
  48. data/test/fake_env.rb +0 -0
  49. data/test/fixtures/en.yml +2 -0
  50. data/test/helper.rb +49 -0
  51. data/test/test_api.rb +521 -0
  52. data/test/test_cli.rb +389 -0
  53. data/test/test_client.rb +294 -0
  54. data/test/test_exception_handler.rb +55 -0
  55. data/test/test_fetch.rb +206 -0
  56. data/test/test_logging.rb +34 -0
  57. data/test/test_manager.rb +169 -0
  58. data/test/test_middleware.rb +160 -0
  59. data/test/test_monitor.rb +258 -0
  60. data/test/test_processor.rb +176 -0
  61. data/test/test_rails.rb +23 -0
  62. data/test/test_redis_connection.rb +127 -0
  63. data/test/test_retry.rb +390 -0
  64. data/test/test_roundhouse.rb +87 -0
  65. data/test/test_scheduled.rb +120 -0
  66. data/test/test_scheduling.rb +75 -0
  67. data/test/test_testing.rb +78 -0
  68. data/test/test_testing_fake.rb +240 -0
  69. data/test/test_testing_inline.rb +65 -0
  70. data/test/test_util.rb +18 -0
  71. data/test/test_web.rb +605 -0
  72. data/test/test_web_helpers.rb +52 -0
  73. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  74. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  75. data/web/assets/images/logo.png +0 -0
  76. data/web/assets/images/status/active.png +0 -0
  77. data/web/assets/images/status/idle.png +0 -0
  78. data/web/assets/images/status-sd8051fd480.png +0 -0
  79. data/web/assets/javascripts/application.js +83 -0
  80. data/web/assets/javascripts/dashboard.js +300 -0
  81. data/web/assets/javascripts/locales/README.md +27 -0
  82. data/web/assets/javascripts/locales/jquery.timeago.ar.js +96 -0
  83. data/web/assets/javascripts/locales/jquery.timeago.bg.js +18 -0
  84. data/web/assets/javascripts/locales/jquery.timeago.bs.js +49 -0
  85. data/web/assets/javascripts/locales/jquery.timeago.ca.js +18 -0
  86. data/web/assets/javascripts/locales/jquery.timeago.cs.js +18 -0
  87. data/web/assets/javascripts/locales/jquery.timeago.cy.js +20 -0
  88. data/web/assets/javascripts/locales/jquery.timeago.da.js +18 -0
  89. data/web/assets/javascripts/locales/jquery.timeago.de.js +18 -0
  90. data/web/assets/javascripts/locales/jquery.timeago.el.js +18 -0
  91. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +20 -0
  92. data/web/assets/javascripts/locales/jquery.timeago.en.js +20 -0
  93. data/web/assets/javascripts/locales/jquery.timeago.es.js +18 -0
  94. data/web/assets/javascripts/locales/jquery.timeago.et.js +18 -0
  95. data/web/assets/javascripts/locales/jquery.timeago.fa.js +22 -0
  96. data/web/assets/javascripts/locales/jquery.timeago.fi.js +28 -0
  97. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +16 -0
  98. data/web/assets/javascripts/locales/jquery.timeago.fr.js +17 -0
  99. data/web/assets/javascripts/locales/jquery.timeago.he.js +18 -0
  100. data/web/assets/javascripts/locales/jquery.timeago.hr.js +49 -0
  101. data/web/assets/javascripts/locales/jquery.timeago.hu.js +18 -0
  102. data/web/assets/javascripts/locales/jquery.timeago.hy.js +18 -0
  103. data/web/assets/javascripts/locales/jquery.timeago.id.js +18 -0
  104. data/web/assets/javascripts/locales/jquery.timeago.it.js +16 -0
  105. data/web/assets/javascripts/locales/jquery.timeago.ja.js +19 -0
  106. data/web/assets/javascripts/locales/jquery.timeago.ko.js +17 -0
  107. data/web/assets/javascripts/locales/jquery.timeago.lt.js +20 -0
  108. data/web/assets/javascripts/locales/jquery.timeago.mk.js +20 -0
  109. data/web/assets/javascripts/locales/jquery.timeago.nl.js +20 -0
  110. data/web/assets/javascripts/locales/jquery.timeago.no.js +18 -0
  111. data/web/assets/javascripts/locales/jquery.timeago.pl.js +31 -0
  112. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +16 -0
  113. data/web/assets/javascripts/locales/jquery.timeago.pt.js +16 -0
  114. data/web/assets/javascripts/locales/jquery.timeago.ro.js +18 -0
  115. data/web/assets/javascripts/locales/jquery.timeago.rs.js +49 -0
  116. data/web/assets/javascripts/locales/jquery.timeago.ru.js +34 -0
  117. data/web/assets/javascripts/locales/jquery.timeago.sk.js +18 -0
  118. data/web/assets/javascripts/locales/jquery.timeago.sl.js +44 -0
  119. data/web/assets/javascripts/locales/jquery.timeago.sv.js +18 -0
  120. data/web/assets/javascripts/locales/jquery.timeago.th.js +20 -0
  121. data/web/assets/javascripts/locales/jquery.timeago.tr.js +16 -0
  122. data/web/assets/javascripts/locales/jquery.timeago.uk.js +34 -0
  123. data/web/assets/javascripts/locales/jquery.timeago.uz.js +19 -0
  124. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +20 -0
  125. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +20 -0
  126. data/web/assets/stylesheets/application.css +746 -0
  127. data/web/assets/stylesheets/bootstrap.css +9 -0
  128. data/web/locales/cs.yml +68 -0
  129. data/web/locales/da.yml +68 -0
  130. data/web/locales/de.yml +69 -0
  131. data/web/locales/el.yml +68 -0
  132. data/web/locales/en.yml +77 -0
  133. data/web/locales/es.yml +69 -0
  134. data/web/locales/fr.yml +69 -0
  135. data/web/locales/hi.yml +75 -0
  136. data/web/locales/it.yml +69 -0
  137. data/web/locales/ja.yml +69 -0
  138. data/web/locales/ko.yml +68 -0
  139. data/web/locales/nl.yml +68 -0
  140. data/web/locales/no.yml +69 -0
  141. data/web/locales/pl.yml +59 -0
  142. data/web/locales/pt-br.yml +68 -0
  143. data/web/locales/pt.yml +67 -0
  144. data/web/locales/ru.yml +75 -0
  145. data/web/locales/sv.yml +68 -0
  146. data/web/locales/ta.yml +75 -0
  147. data/web/locales/zh-cn.yml +68 -0
  148. data/web/locales/zh-tw.yml +68 -0
  149. data/web/views/_footer.erb +22 -0
  150. data/web/views/_job_info.erb +84 -0
  151. data/web/views/_nav.erb +66 -0
  152. data/web/views/_paging.erb +23 -0
  153. data/web/views/_poll_js.erb +5 -0
  154. data/web/views/_poll_link.erb +7 -0
  155. data/web/views/_status.erb +4 -0
  156. data/web/views/_summary.erb +40 -0
  157. data/web/views/busy.erb +90 -0
  158. data/web/views/dashboard.erb +75 -0
  159. data/web/views/dead.erb +34 -0
  160. data/web/views/layout.erb +31 -0
  161. data/web/views/morgue.erb +71 -0
  162. data/web/views/queue.erb +45 -0
  163. data/web/views/queues.erb +27 -0
  164. data/web/views/retries.erb +74 -0
  165. data/web/views/retry.erb +34 -0
  166. data/web/views/scheduled.erb +54 -0
  167. data/web/views/scheduled_job_info.erb +8 -0
  168. metadata +404 -0
data/test/test_cli.rb ADDED
@@ -0,0 +1,389 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse/cli'
3
+ require 'tempfile'
4
+
5
+ class Roundhouse::CLI
6
+ def die(code)
7
+ @code = code
8
+ end
9
+
10
+ def valid?
11
+ !@code
12
+ end
13
+ end
14
+
15
+ class TestCli < Roundhouse::Test
16
+ describe 'CLI#parse' do
17
+
18
+ before do
19
+ @cli = Roundhouse::CLI.new
20
+ @opts = Roundhouse.options.dup
21
+ end
22
+
23
+ after do
24
+ Roundhouse.options = @opts
25
+ end
26
+
27
+ it 'does not require the specified Ruby code' do
28
+ @cli.parse(['roundhouse', '-r', './test/fake_env.rb'])
29
+ refute($LOADED_FEATURES.any? { |x| x =~ /fake_env/ })
30
+ assert @cli.valid?
31
+ end
32
+
33
+ it 'does not boot rails' do
34
+ refute defined?(::Rails::Application)
35
+ @cli.parse(['roundhouse', '-r', './myapp'])
36
+ refute defined?(::Rails::Application)
37
+ end
38
+
39
+ it 'changes concurrency' do
40
+ @cli.parse(['roundhouse', '-c', '60', '-r', './test/fake_env.rb'])
41
+ assert_equal 60, Roundhouse.options[:concurrency]
42
+ end
43
+
44
+ it 'changes queues' do
45
+ @cli.parse(['roundhouse', '-q', 'foo', '-r', './test/fake_env.rb'])
46
+ assert_equal ['foo'], Roundhouse.options[:queues]
47
+ end
48
+
49
+ it 'accepts a process index' do
50
+ @cli.parse(['roundhouse', '-i', '7', '-r', './test/fake_env.rb'])
51
+ assert_equal 7, Roundhouse.options[:index]
52
+ end
53
+
54
+ it 'accepts a stringy process index' do
55
+ @cli.parse(['roundhouse', '-i', 'worker.7', '-r', './test/fake_env.rb'])
56
+ assert_equal 7, Roundhouse.options[:index]
57
+ end
58
+
59
+ it 'sets strictly ordered queues if weights are not present' do
60
+ @cli.parse(['roundhouse', '-q', 'foo', '-q', 'bar', '-r', './test/fake_env.rb'])
61
+ assert_equal true, !!Roundhouse.options[:strict]
62
+ end
63
+
64
+ it 'does not set strictly ordered queues if weights are present' do
65
+ @cli.parse(['roundhouse', '-q', 'foo,3', '-r', './test/fake_env.rb'])
66
+ assert_equal false, !!Roundhouse.options[:strict]
67
+ end
68
+
69
+ it 'does not set strictly ordered queues if weights are present with multiple queues' do
70
+ @cli.parse(['roundhouse', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
71
+ assert_equal false, !!Roundhouse.options[:strict]
72
+ end
73
+
74
+ it 'changes timeout' do
75
+ @cli.parse(['roundhouse', '-t', '30', '-r', './test/fake_env.rb'])
76
+ assert_equal 30, Roundhouse.options[:timeout]
77
+ end
78
+
79
+ it 'handles multiple queues with weights' do
80
+ @cli.parse(['roundhouse', '-q', 'foo,3', '-q', 'bar', '-r', './test/fake_env.rb'])
81
+ assert_equal %w(foo foo foo bar), Roundhouse.options[:queues]
82
+ end
83
+
84
+ it 'handles queues with multi-word names' do
85
+ @cli.parse(['roundhouse', '-q', 'queue_one', '-q', 'queue-two', '-r', './test/fake_env.rb'])
86
+ assert_equal %w(queue_one queue-two), Roundhouse.options[:queues]
87
+ end
88
+
89
+ it 'handles queues with dots in the name' do
90
+ @cli.parse(['roundhouse', '-q', 'foo.bar', '-r', './test/fake_env.rb'])
91
+ assert_equal ['foo.bar'], Roundhouse.options[:queues]
92
+ end
93
+
94
+ it 'sets verbose' do
95
+ old = Roundhouse.logger.level
96
+ @cli.parse(['roundhouse', '-v', '-r', './test/fake_env.rb'])
97
+ assert_equal Logger::DEBUG, Roundhouse.logger.level
98
+ # If we leave the logger at DEBUG it'll add a lot of noise to the test output
99
+ Roundhouse.options.delete(:verbose)
100
+ Roundhouse.logger.level = old
101
+ end
102
+
103
+ describe 'with logfile' do
104
+ before do
105
+ @old_logger = Roundhouse.logger
106
+ @tmp_log_path = '/tmp/roundhouse.log'
107
+ end
108
+
109
+ after do
110
+ Roundhouse.logger = @old_logger
111
+ Roundhouse.options.delete(:logfile)
112
+ File.unlink @tmp_log_path if File.exist?(@tmp_log_path)
113
+ end
114
+
115
+ it 'sets the logfile path' do
116
+ @cli.parse(['roundhouse', '-L', @tmp_log_path, '-r', './test/fake_env.rb'])
117
+
118
+ assert_equal @tmp_log_path, Roundhouse.options[:logfile]
119
+ end
120
+
121
+ it 'creates and writes to a logfile' do
122
+ @cli.parse(['roundhouse', '-L', @tmp_log_path, '-r', './test/fake_env.rb'])
123
+
124
+ Roundhouse.logger.info('test message')
125
+
126
+ assert_match(/test message/, File.read(@tmp_log_path), "didn't include the log message")
127
+ end
128
+
129
+ it 'appends messages to a logfile' do
130
+ File.open(@tmp_log_path, 'w') do |f|
131
+ f.puts 'already existent log message'
132
+ end
133
+
134
+ @cli.parse(['roundhouse', '-L', @tmp_log_path, '-r', './test/fake_env.rb'])
135
+
136
+ Roundhouse.logger.info('test message')
137
+
138
+ log_file_content = File.read(@tmp_log_path)
139
+ assert_match(/already existent/, log_file_content, "didn't include the old message")
140
+ assert_match(/test message/, log_file_content, "didn't include the new message")
141
+ end
142
+ end
143
+
144
+ describe 'with pidfile' do
145
+ before do
146
+ @tmp_file = Tempfile.new('roundhouse-test')
147
+ @tmp_path = @tmp_file.path
148
+ @tmp_file.close!
149
+
150
+ @cli.parse(['roundhouse', '-P', @tmp_path, '-r', './test/fake_env.rb'])
151
+ end
152
+
153
+ after do
154
+ File.unlink @tmp_path if File.exist? @tmp_path
155
+ end
156
+
157
+ it 'sets pidfile path' do
158
+ assert_equal @tmp_path, Roundhouse.options[:pidfile]
159
+ end
160
+
161
+ it 'writes pidfile' do
162
+ assert_equal File.read(@tmp_path).strip.to_i, Process.pid
163
+ end
164
+ end
165
+
166
+ describe 'with config file' do
167
+ before do
168
+ @cli.parse(['roundhouse', '-C', './test/config.yml'])
169
+ end
170
+
171
+ it 'takes a path' do
172
+ assert_equal './test/config.yml', Roundhouse.options[:config_file]
173
+ end
174
+
175
+ it 'sets verbose' do
176
+ refute Roundhouse.options[:verbose]
177
+ end
178
+
179
+ it 'sets require file' do
180
+ assert_equal './test/fake_env.rb', Roundhouse.options[:require]
181
+ end
182
+
183
+ it 'does not set environment' do
184
+ assert_equal nil, Roundhouse.options[:environment]
185
+ end
186
+
187
+ it 'sets concurrency' do
188
+ assert_equal 50, Roundhouse.options[:concurrency]
189
+ end
190
+
191
+ it 'sets pid file' do
192
+ assert_equal '/tmp/roundhouse-config-test.pid', Roundhouse.options[:pidfile]
193
+ end
194
+
195
+ it 'sets logfile' do
196
+ assert_equal '/tmp/roundhouse.log', Roundhouse.options[:logfile]
197
+ end
198
+
199
+ it 'sets queues' do
200
+ assert_equal 2, Roundhouse.options[:queues].count { |q| q == 'very_often' }
201
+ assert_equal 1, Roundhouse.options[:queues].count { |q| q == 'seldom' }
202
+ end
203
+ end
204
+
205
+ describe 'with env based config file' do
206
+ before do
207
+ @cli.parse(['roundhouse', '-e', 'staging', '-C', './test/env_based_config.yml'])
208
+ end
209
+
210
+ it 'takes a path' do
211
+ assert_equal './test/env_based_config.yml', Roundhouse.options[:config_file]
212
+ end
213
+
214
+ it 'sets verbose' do
215
+ refute Roundhouse.options[:verbose]
216
+ end
217
+
218
+ it 'sets require file' do
219
+ assert_equal './test/fake_env.rb', Roundhouse.options[:require]
220
+ end
221
+
222
+ it 'sets environment' do
223
+ assert_equal 'staging', Roundhouse.options[:environment]
224
+ end
225
+
226
+ it 'sets concurrency' do
227
+ assert_equal 5, Roundhouse.options[:concurrency]
228
+ end
229
+
230
+ it 'sets pid file' do
231
+ assert_equal '/tmp/roundhouse-config-test.pid', Roundhouse.options[:pidfile]
232
+ end
233
+
234
+ it 'sets logfile' do
235
+ assert_equal '/tmp/roundhouse.log', Roundhouse.options[:logfile]
236
+ end
237
+
238
+ it 'sets queues' do
239
+ assert_equal 2, Roundhouse.options[:queues].count { |q| q == 'very_often' }
240
+ assert_equal 1, Roundhouse.options[:queues].count { |q| q == 'seldom' }
241
+ end
242
+ end
243
+
244
+ describe 'with an empty config file' do
245
+ before do
246
+ @tmp_file = Tempfile.new('roundhouse-test')
247
+ @tmp_path = @tmp_file.path
248
+ @tmp_file.close!
249
+ end
250
+
251
+ after do
252
+ File.unlink @tmp_path if File.exist? @tmp_path
253
+ end
254
+
255
+ it 'takes a path' do
256
+ @cli.parse(['roundhouse', '-C', @tmp_path])
257
+ assert_equal @tmp_path, Roundhouse.options[:config_file]
258
+ end
259
+
260
+ it 'should have an identical options hash, except for config_file' do
261
+ @cli.parse(['roundhouse'])
262
+ old_options = Roundhouse.options.clone
263
+
264
+ @cli.parse(['roundhouse', '-C', @tmp_path])
265
+ new_options = Roundhouse.options.clone
266
+ refute_equal old_options, new_options
267
+
268
+ new_options.delete(:config_file)
269
+ assert_equal old_options, new_options
270
+ end
271
+ end
272
+
273
+ describe 'with config file and flags' do
274
+ before do
275
+ # We need an actual file here.
276
+ @tmp_lib_path = '/tmp/require-me.rb'
277
+ File.open(@tmp_lib_path, 'w') do |f|
278
+ f.puts "# do work"
279
+ end
280
+
281
+ @tmp_file = Tempfile.new('roundhouser')
282
+ @tmp_path = @tmp_file.path
283
+ @tmp_file.close!
284
+
285
+ @cli.parse(['roundhouse',
286
+ '-C', './test/config.yml',
287
+ '-e', 'snoop',
288
+ '-c', '100',
289
+ '-r', @tmp_lib_path,
290
+ '-P', @tmp_path,
291
+ '-q', 'often,7',
292
+ '-q', 'seldom,3'])
293
+ end
294
+
295
+ after do
296
+ File.unlink @tmp_lib_path if File.exist? @tmp_lib_path
297
+ File.unlink @tmp_path if File.exist? @tmp_path
298
+ end
299
+
300
+ it 'uses concurrency flag' do
301
+ assert_equal 100, Roundhouse.options[:concurrency]
302
+ end
303
+
304
+ it 'uses require file flag' do
305
+ assert_equal @tmp_lib_path, Roundhouse.options[:require]
306
+ end
307
+
308
+ it 'uses environment flag' do
309
+ assert_equal 'snoop', Roundhouse.options[:environment]
310
+ end
311
+
312
+ it 'uses pidfile flag' do
313
+ assert_equal @tmp_path, Roundhouse.options[:pidfile]
314
+ end
315
+
316
+ it 'sets queues' do
317
+ assert_equal 7, Roundhouse.options[:queues].count { |q| q == 'often' }
318
+ assert_equal 3, Roundhouse.options[:queues].count { |q| q == 'seldom' }
319
+ end
320
+ end
321
+
322
+ describe 'Roundhouse::CLI#parse_queues' do
323
+ describe 'when weight is present' do
324
+ it 'concatenates queues by factor of weight and sets strict to false' do
325
+ opts = { strict: true }
326
+ @cli.__send__ :parse_queues, opts, [['often', 7], ['repeatedly', 3]]
327
+ @cli.__send__ :parse_queues, opts, [['once']]
328
+ assert_equal (%w[often] * 7 + %w[repeatedly] * 3 + %w[once]), opts[:queues]
329
+ assert !opts[:strict]
330
+ end
331
+ end
332
+
333
+ describe 'when weight is not present' do
334
+ it 'returns queues and sets strict' do
335
+ opts = { strict: true }
336
+ @cli.__send__ :parse_queues, opts, [['once'], ['one_time']]
337
+ @cli.__send__ :parse_queues, opts, [['einmal']]
338
+ assert_equal %w[once one_time einmal], opts[:queues]
339
+ assert opts[:strict]
340
+ end
341
+ end
342
+ end
343
+
344
+ describe 'Roundhouse::CLI#parse_queue' do
345
+ describe 'when weight is present' do
346
+ it 'concatenates queue to opts[:queues] weight number of times and sets strict to false' do
347
+ opts = { strict: true }
348
+ @cli.__send__ :parse_queue, opts, 'often', 7
349
+ assert_equal %w[often] * 7, opts[:queues]
350
+ assert !opts[:strict]
351
+ end
352
+ end
353
+
354
+ describe 'when weight is not present' do
355
+ it 'concatenates queue to opts[:queues] once and leaves strict true' do
356
+ opts = { strict: true }
357
+ @cli.__send__ :parse_queue, opts, 'once', nil
358
+ assert_equal %w[once], opts[:queues]
359
+ assert opts[:strict]
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ describe 'misc' do
366
+ it 'handles interrupts' do
367
+ cli = Roundhouse::CLI.new
368
+ assert_raises Interrupt do
369
+ cli.handle_signal('INT')
370
+ end
371
+ assert_raises Interrupt do
372
+ cli.handle_signal('TERM')
373
+ end
374
+ cli.handle_signal('USR2')
375
+ cli.handle_signal('TTIN')
376
+ end
377
+
378
+ it 'can fire events' do
379
+ count = 0
380
+ Roundhouse.options[:lifecycle_events][:startup] = [proc {
381
+ count += 1
382
+ }]
383
+ cli = Roundhouse::CLI.new
384
+ cli.fire_event(:startup)
385
+ assert_equal 1, count
386
+ end
387
+ end
388
+
389
+ end
@@ -0,0 +1,294 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse/client'
3
+ require 'roundhouse/worker'
4
+ require 'roundhouse/api'
5
+
6
+ require 'active_support/core_ext/numeric/time'
7
+
8
+ class TestClient < Roundhouse::Test
9
+ describe 'with mock redis' do
10
+ before do
11
+ @redis = Minitest::Mock.new
12
+ def @redis.multi; [yield] * 2 if block_given?; end
13
+ def @redis.set(*); true; end
14
+ def @redis.sadd(*); true; end
15
+ def @redis.srem(*); true; end
16
+ def @redis.get(*); nil; end
17
+ def @redis.del(*); nil; end
18
+ def @redis.incrby(*); nil; end
19
+ def @redis.setex(*); true; end
20
+ def @redis.expire(*); true; end
21
+ def @redis.watch(*); true; end
22
+ def @redis.with_connection; yield self; end
23
+ def @redis.with; yield self; end
24
+ def @redis.exec; true; end
25
+ def @redis.hset(*); true; end
26
+ def @redis.hget(*); true; end
27
+ Roundhouse.instance_variable_set(:@redis, @redis)
28
+ Roundhouse::Client.instance_variable_set(:@default, nil)
29
+ end
30
+
31
+ after do
32
+ Roundhouse.redis = REDIS
33
+ Roundhouse::Client.instance_variable_set(:@default, nil)
34
+ end
35
+
36
+ it 'raises ArgumentError with invalid params' do
37
+ assert_raises ArgumentError do
38
+ Roundhouse::Client.push('foo', 1)
39
+ end
40
+
41
+ assert_raises ArgumentError do
42
+ Roundhouse::Client.push('foo', :class => 'Foo', :noargs => [1, 2])
43
+ end
44
+
45
+ assert_raises ArgumentError do
46
+ Roundhouse::Client.push('queue_id' => 'wrong', 'class' => MyWorker, 'noargs' => [1, 2])
47
+ end
48
+
49
+ assert_raises ArgumentError do
50
+ Roundhouse::Client.push('queue_id' => 1, 'class' => MyWorker, 'noargs' => [1, 2])
51
+ end
52
+
53
+ assert_raises ArgumentError do
54
+ Roundhouse::Client.push('queue_id' => 1, 'class' => 42, 'args' => [1, 2])
55
+ end
56
+
57
+ assert_raises ArgumentError do
58
+ Roundhouse::Client.push('queue_id' => 1, 'class' => MyWorker, 'args' => 1)
59
+ end
60
+ end
61
+
62
+ describe 'as instance' do
63
+ it 'can push' do
64
+ @redis.expect :lpush, 1, ['queue:1', Array]
65
+ client = Roundhouse::Client.new
66
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
67
+ jid = client.push('class' => 'Blah', 'args' => [1,2,3], 'queue_id' => 1)
68
+ assert_equal 24, jid.size
69
+ end
70
+ end
71
+
72
+ it 'allows local middleware modification' do
73
+ @redis.expect :lpush, 1, ['queue:1', Array]
74
+ $called = false
75
+ mware = Class.new { def call(worker_klass,msg,q,r); $called = true; msg;end }
76
+ client = Roundhouse::Client.new
77
+ client.middleware do |chain|
78
+ chain.add mware
79
+ end
80
+
81
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
82
+ client.push('class' => 'Blah', 'args' => [1,2,3], 'queue_id' => 1)
83
+ end
84
+
85
+ assert $called
86
+ assert client.middleware.exists?(mware)
87
+ refute Roundhouse.client_middleware.exists?(mware)
88
+ end
89
+ end
90
+
91
+ it 'pushes messages to redis' do
92
+ @redis.expect :lpush, 1, ['queue:1', Array]
93
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
94
+ pushed = Roundhouse::Client.push('queue_id' => 1, 'class' => MyWorker, 'args' => [1, 2])
95
+ assert pushed
96
+ assert_equal 24, pushed.size
97
+ end
98
+ @redis.verify
99
+ end
100
+
101
+ it 'pushes messages to redis using a String class' do
102
+ @redis.expect :lpush, 1, ['queue:1', Array]
103
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
104
+ pushed = Roundhouse::Client.push('queue_id' => 1, 'class' => 'MyWorker', 'args' => [1, 2])
105
+ assert pushed
106
+ assert_equal 24, pushed.size
107
+ end
108
+ @redis.verify
109
+ end
110
+
111
+ class MyWorker
112
+ include Roundhouse::Worker
113
+ end
114
+
115
+ it 'has default options' do
116
+ assert_equal Roundhouse.default_worker_options, MyWorker.get_roundhouse_options
117
+ end
118
+
119
+ it 'handles perform_async' do
120
+ @redis.expect :lpush, 1, ['queue:100', Array]
121
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
122
+ pushed = MyWorker.perform_async(100, 1, 2)
123
+ assert pushed
124
+ end
125
+ @redis.verify
126
+ end
127
+
128
+ it 'enqueues messages to redis' do
129
+ @redis.expect :lpush, 1, ['queue:100', Array]
130
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
131
+ pushed = Roundhouse::Client.enqueue_to(100, MyWorker, 1, 2)
132
+ assert pushed
133
+ end
134
+ @redis.verify
135
+ end
136
+
137
+ it 'enqueues messages to redis (delayed)' do
138
+ @redis.expect :zadd, 1, ['schedule', Array]
139
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
140
+ pushed = Roundhouse::Client.enqueue_to_in(100, 3.minutes, MyWorker, 1, 2)
141
+ assert pushed
142
+ end
143
+ @redis.verify
144
+ end
145
+
146
+ it 'enqueues messages to redis (delayed into past)' do
147
+ @redis.expect :lpush, 1, ['queue:100', Array]
148
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
149
+ pushed = Roundhouse::Client.enqueue_to_in(100, -3.minutes, MyWorker, 1, 2)
150
+ assert pushed
151
+ end
152
+ @redis.verify
153
+ end
154
+
155
+ class QueuedWorker
156
+ include Roundhouse::Worker
157
+ roundhouse_options :queue => :flimflam
158
+ end
159
+
160
+ it 'enqueues to the named queue' do
161
+ @redis.expect :lpush, 1, ['queue:100', Array]
162
+ Roundhouse::Monitor.stub :maybe_add_to_rotation, true do
163
+ pushed = QueuedWorker.perform_async(100, 1, 2)
164
+ assert pushed
165
+ end
166
+ @redis.verify
167
+ end
168
+
169
+ it 'retrieves queues' do
170
+ @redis.expect :smembers, ['0'], [Roundhouse::Monitor::BUCKETS]
171
+ @redis.expect :hkeys, ['100'], [Roundhouse::Monitor.status_bucket(100)]
172
+ assert_equal [100], Roundhouse::Queue.all.map(&:queue_id)
173
+ end
174
+ end
175
+
176
+ describe 'bulk' do
177
+ after do
178
+ Roundhouse::Queue.new(100).clear
179
+ end
180
+ it 'can push a large set of jobs at once' do
181
+ jids = Roundhouse::Client.push_bulk('class' => QueuedWorker, 'queue_id' => 100, 'args' => (1..1_000).to_a.map { |x| Array(x) })
182
+ assert_equal 1_000, jids.size
183
+ end
184
+
185
+ it 'can push a large set of jobs at once using a String class' do
186
+ jids = Roundhouse::Client.push_bulk('class' => 'QueuedWorker', 'queue_id' => 100, 'args' => (1..1_000).to_a.map { |x| Array(x) })
187
+ assert_equal 1_000, jids.size
188
+ end
189
+ it 'returns the jids for the jobs' do
190
+ Roundhouse::Client.push_bulk('class' => 'QueuedWorker', 'queue_id' => 100, 'args' => (1..2).to_a.map { |x| Array(x) }).each do |jid|
191
+ assert_match(/[0-9a-f]{12}/, jid)
192
+ end
193
+ end
194
+ end
195
+
196
+ class BaseWorker
197
+ include Roundhouse::Worker
198
+ roundhouse_options 'retry' => 'base'
199
+ end
200
+ class AWorker < BaseWorker
201
+ end
202
+ class BWorker < BaseWorker
203
+ roundhouse_options 'retry' => 'b'
204
+ end
205
+ class CWorker < BaseWorker
206
+ roundhouse_options 'retry' => 2
207
+ end
208
+
209
+ describe 'client middleware' do
210
+
211
+ class Stopper
212
+ def call(worker_class, message, queue, r)
213
+ raise ArgumentError unless r
214
+ yield if message['args'].first.odd?
215
+ end
216
+ end
217
+
218
+ it 'can stop some of the jobs from pushing' do
219
+ Roundhouse.client_middleware.add Stopper
220
+ begin
221
+ assert_equal nil, Roundhouse::Client.push('queue_id' => 100, 'class' => MyWorker, 'args' => [0])
222
+ assert_match(/[0-9a-f]{12}/, Roundhouse::Client.push('queue_id' => 100, 'class' => MyWorker, 'args' => [1]))
223
+ Roundhouse::Client.push_bulk('queue_id' => 100, 'class' => MyWorker, 'args' => [[0], [1]]).each do |jid|
224
+ assert_match(/[0-9a-f]{12}/, jid)
225
+ end
226
+ ensure
227
+ Roundhouse.client_middleware.remove Stopper
228
+ end
229
+ end
230
+ end
231
+
232
+ describe 'inheritance' do
233
+ it 'inherits roundhouse options' do
234
+ assert_equal 'base', AWorker.get_roundhouse_options['retry']
235
+ assert_equal 'b', BWorker.get_roundhouse_options['retry']
236
+ end
237
+ end
238
+
239
+ describe 'item normalization' do
240
+ it 'defaults retry to true' do
241
+ assert_equal true, Roundhouse::Client.new.__send__(:normalize_item, 'queue_id' => 100, 'class' => QueuedWorker, 'args' => [])['retry']
242
+ end
243
+
244
+ it "does not normalize numeric retry's" do
245
+ assert_equal 2, Roundhouse::Client.new.__send__(:normalize_item, 'queue_id' => 100, 'class' => CWorker, 'args' => [])['retry']
246
+ end
247
+ end
248
+
249
+ describe 'sharding' do
250
+ class DWorker < BaseWorker
251
+ end
252
+
253
+ it 'allows roundhouse_options to point to different Redi' do
254
+ skip 'sharding not needed right now'
255
+
256
+ conn = MiniTest::Mock.new
257
+ conn.expect(:multi, [0, 1])
258
+ DWorker.roundhouse_options('pool' => ConnectionPool.new(size: 1) { conn })
259
+ DWorker.perform_async(1,2,3)
260
+ conn.verify
261
+ end
262
+
263
+ it 'allows #via to point to different Redi' do
264
+ skip '#via not needed right now'
265
+
266
+ conn = MiniTest::Mock.new
267
+ conn.expect(:multi, [0, 1])
268
+ default = Roundhouse::Client.new.redis_pool
269
+ sharded_pool = ConnectionPool.new(size: 1) { conn }
270
+ Roundhouse::Client.via(sharded_pool) do
271
+ CWorker.perform_async(100, 1,2,3)
272
+ assert_equal sharded_pool, Roundhouse::Client.new.redis_pool
273
+ assert_raises RuntimeError do
274
+ Roundhouse::Client.via(default) do
275
+ # nothing
276
+ end
277
+ end
278
+ end
279
+ assert_equal default, Roundhouse::Client.new.redis_pool
280
+ conn.verify
281
+ end
282
+
283
+ it 'allows Resque helpers to point to different Redi' do
284
+ skip 'sharding not needed right now'
285
+
286
+ conn = MiniTest::Mock.new
287
+ conn.expect(:multi, []) { |*args, &block| block.call }
288
+ conn.expect(:zadd, 1, [String, Array])
289
+ DWorker.roundhouse_options('pool' => ConnectionPool.new(size: 1) { conn })
290
+ Roundhouse::Client.enqueue_to_in(100, 10, DWorker, 3)
291
+ conn.verify
292
+ end
293
+ end
294
+ end