roundhouse-x 0.1.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.
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