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_web.rb ADDED
@@ -0,0 +1,605 @@
1
+ # encoding: utf-8
2
+ require_relative 'helper'
3
+ require 'roundhouse'
4
+ require 'roundhouse/web'
5
+ require 'rack/test'
6
+ require 'tilt/erubis'
7
+
8
+ class TestWeb < Roundhouse::Test
9
+
10
+ describe 'roundhouse web' do
11
+ include Rack::Test::Methods
12
+
13
+ def app
14
+ Roundhouse::Web
15
+ end
16
+
17
+ def job_params(job, score)
18
+ "#{score}-#{job['jid']}"
19
+ end
20
+
21
+ before do
22
+ Roundhouse.redis = REDIS
23
+ Roundhouse.redis {|c| c.flushdb }
24
+ end
25
+
26
+ class WebWorker
27
+ include Roundhouse::Worker
28
+
29
+ def perform(a, b)
30
+ a + b
31
+ end
32
+ end
33
+
34
+ it 'can show text with any locales' do
35
+ rackenv = {'HTTP_ACCEPT_LANGUAGE' => 'ru,en'}
36
+ get '/', {}, rackenv
37
+ assert_match(/Панель управления/, last_response.body)
38
+ rackenv = {'HTTP_ACCEPT_LANGUAGE' => 'es,en'}
39
+ get '/', {}, rackenv
40
+ assert_match(/Panel de Control/, last_response.body)
41
+ rackenv = {'HTTP_ACCEPT_LANGUAGE' => 'en-us'}
42
+ get '/', {}, rackenv
43
+ assert_match(/Dashboard/, last_response.body)
44
+ end
45
+
46
+ describe 'busy' do
47
+
48
+ it 'can display workers' do
49
+ Roundhouse.redis do |conn|
50
+ conn.incr('busy')
51
+ conn.sadd('processes', 'foo:1234')
52
+ conn.hmset('foo:1234', 'info', Roundhouse.dump_json('hostname' => 'foo', 'started_at' => Time.now.to_f, "queues" => []), 'at', Time.now.to_f, 'busy', 4)
53
+ identity = 'foo:1234:workers'
54
+ hash = {:queue => 'critical', :payload => { 'class' => WebWorker.name, 'args' => [1,'abc'] }, :run_at => Time.now.to_i }
55
+ conn.hmset(identity, 1001, Roundhouse.dump_json(hash))
56
+ end
57
+ assert_equal ['1001'], Roundhouse::Workers.new.map { |pid, tid, data| tid }
58
+
59
+ get '/busy'
60
+ assert_equal 200, last_response.status
61
+ assert_match(/status-active/, last_response.body)
62
+ assert_match(/critical/, last_response.body)
63
+ assert_match(/WebWorker/, last_response.body)
64
+ end
65
+
66
+ it 'can quiet a process' do
67
+ identity = 'identity'
68
+ signals_key = "#{identity}-signals"
69
+
70
+ assert_nil Roundhouse.redis { |c| c.lpop signals_key }
71
+ post '/busy', 'quiet' => '1', 'identity' => identity
72
+ assert_equal 302, last_response.status
73
+ assert_equal 'USR1', Roundhouse.redis { |c| c.lpop signals_key }
74
+ end
75
+
76
+ it 'can stop a process' do
77
+ identity = 'identity'
78
+ signals_key = "#{identity}-signals"
79
+
80
+ assert_nil Roundhouse.redis { |c| c.lpop signals_key }
81
+ post '/busy', 'stop' => '1', 'identity' => identity
82
+ assert_equal 302, last_response.status
83
+ assert_equal 'TERM', Roundhouse.redis { |c| c.lpop signals_key }
84
+ end
85
+ end
86
+
87
+ it 'can display queues' do
88
+ assert Roundhouse::Client.push('queue_id' => 100, 'class' => WebWorker, 'args' => [1, 3])
89
+
90
+ get '/queues'
91
+ assert_equal 200, last_response.status
92
+ assert_match(/100/, last_response.body)
93
+ refute_match(/HardWorker/, last_response.body)
94
+ end
95
+
96
+ it 'handles queue view' do
97
+ get '/queues/default'
98
+ assert_equal 200, last_response.status
99
+ end
100
+
101
+ it 'can delete a queue' do
102
+ Roundhouse.redis do |conn|
103
+ conn.rpush('queue:100', '{}')
104
+ Roundhouse::Monitor.activate(conn, 100)
105
+ end
106
+
107
+ get '/queues/100'
108
+ assert_equal 200, last_response.status
109
+
110
+ post '/queues/100'
111
+ assert_equal 302, last_response.status
112
+
113
+ Roundhouse.redis do |conn|
114
+ refute conn.hexists(Roundhouse::Monitor.status_bucket(100), 100)
115
+ refute conn.exists('queue:100')
116
+ end
117
+ end
118
+
119
+ it 'can delete a job' do
120
+ Roundhouse.redis do |conn|
121
+ conn.rpush('queue:foo', "{}")
122
+ conn.rpush('queue:foo', "{\"foo\":\"bar\"}")
123
+ conn.rpush('queue:foo', "{\"foo2\":\"bar2\"}")
124
+ end
125
+
126
+ get '/queues/foo'
127
+ assert_equal 200, last_response.status
128
+
129
+ post '/queues/foo/delete', key_val: "{\"foo\":\"bar\"}"
130
+ assert_equal 302, last_response.status
131
+
132
+ Roundhouse.redis do |conn|
133
+ refute conn.lrange('queue:foo', 0, -1).include?("{\"foo\":\"bar\"}")
134
+ end
135
+ end
136
+
137
+ it 'can display retries' do
138
+ get '/retries'
139
+ assert_equal 200, last_response.status
140
+ assert_match(/found/, last_response.body)
141
+ refute_match(/HardWorker/, last_response.body)
142
+
143
+ add_retry
144
+
145
+ get '/retries'
146
+ assert_equal 200, last_response.status
147
+ refute_match(/found/, last_response.body)
148
+ assert_match(/HardWorker/, last_response.body)
149
+ end
150
+
151
+ it 'can display a single retry' do
152
+ params = add_retry
153
+ get '/retries/0-shouldntexist'
154
+ assert_equal 302, last_response.status
155
+ get "/retries/#{job_params(*params)}"
156
+ assert_equal 200, last_response.status
157
+ assert_match(/HardWorker/, last_response.body)
158
+ end
159
+
160
+ it 'handles missing retry' do
161
+ get "/retries/0-shouldntexist"
162
+ assert_equal 302, last_response.status
163
+ end
164
+
165
+ it 'can delete a single retry' do
166
+ params = add_retry
167
+ post "/retries/#{job_params(*params)}", 'delete' => 'Delete'
168
+ assert_equal 302, last_response.status
169
+ assert_equal 'http://example.org/retries', last_response.header['Location']
170
+
171
+ get "/retries"
172
+ assert_equal 200, last_response.status
173
+ refute_match(/#{params.first['args'][2]}/, last_response.body)
174
+ end
175
+
176
+ it 'can delete all retries' do
177
+ 3.times { add_retry }
178
+
179
+ post "/retries/all/delete", 'delete' => 'Delete'
180
+ assert_equal 0, Roundhouse::RetrySet.new.size
181
+ assert_equal 302, last_response.status
182
+ assert_equal 'http://example.org/retries', last_response.header['Location']
183
+ end
184
+
185
+ it 'can retry a single retry now' do
186
+ params = add_retry
187
+ post "/retries/#{job_params(*params)}", 'retry' => 'Retry'
188
+ assert_equal 302, last_response.status
189
+ assert_equal 'http://example.org/retries', last_response.header['Location']
190
+
191
+ get '/queues/100'
192
+ assert_equal 200, last_response.status
193
+ assert_match(/#{params.first['args'][2]}/, last_response.body)
194
+ end
195
+
196
+ it 'can kill a single retry now' do
197
+ params = add_retry
198
+ post "/retries/#{job_params(*params)}", 'kill' => 'Kill'
199
+ assert_equal 302, last_response.status
200
+ assert_equal 'http://example.org/retries', last_response.header['Location']
201
+
202
+ get '/morgue'
203
+ assert_equal 200, last_response.status
204
+ assert_match(/#{params.first['args'][2]}/, last_response.body)
205
+ end
206
+
207
+ it 'can display scheduled' do
208
+ get '/scheduled'
209
+ assert_equal 200, last_response.status
210
+ assert_match(/found/, last_response.body)
211
+ refute_match(/HardWorker/, last_response.body)
212
+
213
+ add_scheduled
214
+
215
+ get '/scheduled'
216
+ assert_equal 200, last_response.status
217
+ refute_match(/found/, last_response.body)
218
+ assert_match(/HardWorker/, last_response.body)
219
+ end
220
+
221
+ it 'can display a single scheduled job' do
222
+ params = add_scheduled
223
+ get '/scheduled/0-shouldntexist'
224
+ assert_equal 302, last_response.status
225
+ get "/scheduled/#{job_params(*params)}"
226
+ assert_equal 200, last_response.status
227
+ assert_match(/HardWorker/, last_response.body)
228
+ end
229
+
230
+ it 'handles missing scheduled job' do
231
+ get "/scheduled/0-shouldntexist"
232
+ assert_equal 302, last_response.status
233
+ end
234
+
235
+ it 'can add to queue a single scheduled job' do
236
+ params = add_scheduled
237
+ post "/scheduled/#{job_params(*params)}", 'add_to_queue' => true
238
+ assert_equal 302, last_response.status
239
+ assert_equal 'http://example.org/scheduled', last_response.header['Location']
240
+
241
+ get '/queues/100'
242
+ assert_equal 200, last_response.status
243
+ assert_match(/#{params.first['args'][2]}/, last_response.body)
244
+ end
245
+
246
+ it 'can delete a single scheduled job' do
247
+ params = add_scheduled
248
+ post "/scheduled/#{job_params(*params)}", 'delete' => 'Delete'
249
+ assert_equal 302, last_response.status
250
+ assert_equal 'http://example.org/scheduled', last_response.header['Location']
251
+
252
+ get "/scheduled"
253
+ assert_equal 200, last_response.status
254
+ refute_match(/#{params.first['args'][2]}/, last_response.body)
255
+ end
256
+
257
+ it 'can delete scheduled' do
258
+ params = add_scheduled
259
+ Roundhouse.redis do |conn|
260
+ assert_equal 1, conn.zcard('schedule')
261
+ post '/scheduled', 'key' => [job_params(*params)], 'delete' => 'Delete'
262
+ assert_equal 302, last_response.status
263
+ assert_equal 'http://example.org/scheduled', last_response.header['Location']
264
+ assert_equal 0, conn.zcard('schedule')
265
+ end
266
+ end
267
+
268
+ it "can move scheduled to a queue" do
269
+ q = Roundhouse::Queue.new(100)
270
+ params = add_scheduled
271
+ Roundhouse.redis do |conn|
272
+ assert_equal 1, conn.zcard('schedule')
273
+ assert_equal 0, q.size
274
+ post '/scheduled', 'key' => [job_params(*params)], 'add_to_queue' => 'AddToQueue'
275
+ assert_equal 302, last_response.status
276
+ assert_equal 'http://example.org/scheduled', last_response.header['Location']
277
+ assert_equal 0, conn.zcard('schedule')
278
+ assert_equal 1, q.size
279
+ get '/queues/100'
280
+ assert_equal 200, last_response.status
281
+ assert_match(/#{params[0]['args'][2]}/, last_response.body)
282
+ end
283
+ end
284
+
285
+ it 'can retry all retries' do
286
+ msg = add_retry.first
287
+ add_retry
288
+
289
+ post "/retries/all/retry", 'retry' => 'Retry'
290
+ assert_equal 302, last_response.status
291
+ assert_equal 'http://example.org/retries', last_response.header['Location']
292
+ assert_equal 2, Roundhouse::Queue.new(100).size
293
+
294
+ get '/queues/100'
295
+ assert_equal 200, last_response.status
296
+ assert_match(/#{msg['args'][2]}/, last_response.body)
297
+ end
298
+
299
+ it 'calls updatePage() once when polling' do
300
+ get '/busy?poll=true'
301
+ assert_equal 200, last_response.status
302
+ assert_equal 1, last_response.body.scan('updatePage(').count
303
+ end
304
+
305
+ it 'escape job args and error messages' do
306
+ # on /retries page
307
+ params = add_xss_retry
308
+ get '/retries'
309
+ assert_equal 200, last_response.status
310
+ assert_match(/FailWorker/, last_response.body)
311
+
312
+ assert last_response.body.include?( "fail message: &lt;a&gt;hello&lt;&#x2F;a&gt;" )
313
+ assert !last_response.body.include?( "fail message: <a>hello</a>" )
314
+
315
+ assert last_response.body.include?( "args\">&quot;&lt;a&gt;hello&lt;&#x2F;a&gt;&quot;<" )
316
+ assert !last_response.body.include?( "args\"><a>hello</a><" )
317
+
318
+ # on /workers page
319
+ Roundhouse.redis do |conn|
320
+ pro = 'foo:1234'
321
+ conn.sadd('processes', pro)
322
+ conn.hmset(pro, 'info', Roundhouse.dump_json('started_at' => Time.now.to_f, 'labels' => ['frumduz'], 'queues' =>[]), 'busy', 1, 'beat', Time.now.to_f)
323
+ identity = "#{pro}:workers"
324
+ hash = {:queue => 'critical', :payload => { 'class' => "FailWorker", 'args' => ["<a>hello</a>"] }, :run_at => Time.now.to_i }
325
+ conn.hmset(identity, 100001, Roundhouse.dump_json(hash))
326
+ conn.incr('busy')
327
+ end
328
+
329
+ get '/busy'
330
+ assert_equal 200, last_response.status
331
+ assert_match(/FailWorker/, last_response.body)
332
+ assert_match(/frumduz/, last_response.body)
333
+ assert last_response.body.include?( "&lt;a&gt;hello&lt;&#x2F;a&gt;" )
334
+ assert !last_response.body.include?( "<a>hello</a>" )
335
+
336
+
337
+ # on /queues page
338
+ params = add_xss_retry # sorry, don't know how to easily make this show up on queues page otherwise.
339
+ post "/retries/#{job_params(*params)}", 'retry' => 'Retry'
340
+ assert_equal 302, last_response.status
341
+
342
+ get '/queues/100'
343
+ assert_equal 200, last_response.status
344
+ assert last_response.body.include?( "&lt;a&gt;hello&lt;&#x2F;a&gt;" )
345
+ assert !last_response.body.include?( "<a>hello</a>" )
346
+ end
347
+
348
+ it 'can show user defined tab' do
349
+ begin
350
+ Roundhouse::Web.tabs['Custom Tab'] = '/custom'
351
+
352
+ get '/'
353
+ assert_match 'Custom Tab', last_response.body
354
+
355
+ ensure
356
+ Roundhouse::Web.tabs.delete 'Custom Tab'
357
+ end
358
+ end
359
+
360
+ it 'can display home' do
361
+ get '/'
362
+ assert_equal 200, last_response.status
363
+ end
364
+
365
+ Roundhouse::Web.settings.locales << File.join(File.dirname(__FILE__), "fixtures")
366
+ it 'can show user defined tab with custom locales' do
367
+ begin
368
+ Roundhouse::Web.tabs['Custom Tab'] = '/custom'
369
+ Roundhouse::Web.get('/custom') do
370
+ t('translated_text')
371
+ end
372
+
373
+ get '/custom'
374
+ assert_match(/Changed text/, last_response.body)
375
+
376
+ ensure
377
+ Roundhouse::Web.tabs.delete 'Custom Tab'
378
+ end
379
+ end
380
+
381
+ describe 'dashboard/stats' do
382
+ it 'redirects to stats' do
383
+ get '/dashboard/stats'
384
+ assert_equal 302, last_response.status
385
+ assert_equal 'http://example.org/stats', last_response.header['Location']
386
+ end
387
+ end
388
+
389
+ describe 'stats' do
390
+ include Roundhouse::Util
391
+
392
+ before do
393
+ Roundhouse.redis do |conn|
394
+ conn.set("stat:processed", 5)
395
+ conn.set("stat:failed", 2)
396
+ conn.sadd("queues", "default")
397
+ end
398
+ 2.times { add_retry }
399
+ 3.times { add_scheduled }
400
+ 4.times { add_worker }
401
+
402
+ get '/stats'
403
+ @response = Roundhouse.load_json(last_response.body)
404
+ end
405
+
406
+ it 'can refresh dashboard stats' do
407
+ assert_equal 200, last_response.status
408
+ end
409
+
410
+ describe "for roundhouse" do
411
+ it 'are namespaced' do
412
+ assert_includes @response.keys, "roundhouse"
413
+ end
414
+
415
+ it 'reports processed' do
416
+ assert_equal 5, @response["roundhouse"]["processed"]
417
+ end
418
+
419
+ it 'reports failed' do
420
+ assert_equal 2, @response["roundhouse"]["failed"]
421
+ end
422
+
423
+ it 'reports busy' do
424
+ assert_equal 4, @response["roundhouse"]["busy"]
425
+ end
426
+
427
+ it 'reports processes' do
428
+ assert_equal 1, @response["roundhouse"]["processes"]
429
+ end
430
+
431
+ it 'reports retries' do
432
+ assert_equal 2, @response["roundhouse"]["retries"]
433
+ end
434
+
435
+ it 'reports scheduled' do
436
+ assert_equal 3, @response["roundhouse"]["scheduled"]
437
+ end
438
+
439
+ it 'reports latency' do
440
+ skip 'no default latency'
441
+ assert_equal 0, @response["roundhouse"]["default_latency"]
442
+ end
443
+ end
444
+
445
+ describe "for redis" do
446
+ it 'are namespaced' do
447
+ assert_includes @response.keys, "redis"
448
+ end
449
+
450
+ it 'reports version' do
451
+ assert_includes @response["redis"].keys, "redis_version"
452
+ end
453
+
454
+ it 'reports uptime' do
455
+ assert_includes @response["redis"].keys, "uptime_in_days"
456
+ end
457
+
458
+ it 'reports connected clients' do
459
+ assert_includes @response["redis"].keys, "connected_clients"
460
+ end
461
+
462
+ it 'reports user memory' do
463
+ assert_includes @response["redis"].keys, "used_memory_human"
464
+ end
465
+
466
+ it 'reports memory peak' do
467
+ assert_includes @response["redis"].keys, "used_memory_peak_human"
468
+ end
469
+ end
470
+ end
471
+
472
+ describe 'stats/queues' do
473
+ include Roundhouse::Util
474
+
475
+ before do
476
+ Roundhouse.redis do |conn|
477
+ conn.set("stat:processed", 5)
478
+ conn.set("stat:failed", 2)
479
+ conn.sadd("queues", "default")
480
+ conn.sadd("queues", "queue2")
481
+ end
482
+ 2.times { add_retry }
483
+ 3.times { add_scheduled }
484
+ 4.times { add_worker }
485
+
486
+ get '/stats/queues'
487
+ @response = Roundhouse.load_json(last_response.body)
488
+ end
489
+
490
+ it 'reports the queue depth' do
491
+ assert_equal 0, @response["default"]
492
+ assert_equal 0, @response["queue2"]
493
+ end
494
+ end
495
+
496
+ describe 'dead jobs' do
497
+ it 'shows empty index' do
498
+ get 'morgue'
499
+ assert_equal 200, last_response.status
500
+ end
501
+
502
+ it 'shows index with jobs' do
503
+ (_, score) = add_dead
504
+ get 'morgue'
505
+ assert_equal 200, last_response.status
506
+ assert_match(/#{score}/, last_response.body)
507
+ end
508
+
509
+ it 'can delete all dead' do
510
+ 3.times { add_dead }
511
+
512
+ assert_equal 3, Roundhouse::DeadSet.new.size
513
+ post "/morgue/all/delete", 'delete' => 'Delete'
514
+ assert_equal 0, Roundhouse::DeadSet.new.size
515
+ assert_equal 302, last_response.status
516
+ assert_equal 'http://example.org/morgue', last_response.header['Location']
517
+ end
518
+
519
+ it 'can retry a dead job' do
520
+ params = add_dead
521
+ post "/morgue/#{job_params(*params)}", 'retry' => 'Retry'
522
+ assert_equal 302, last_response.status
523
+ assert_equal 'http://example.org/morgue', last_response.header['Location']
524
+
525
+ get '/queues/100'
526
+ assert_equal 200, last_response.status
527
+ assert_match(/#{params.first['args'][2]}/, last_response.body)
528
+ end
529
+ end
530
+
531
+ def add_scheduled
532
+ score = Time.now.to_f
533
+ msg = { 'class' => 'HardWorker',
534
+ 'queue_id' => 100,
535
+ 'args' => ['bob', 1, Time.now.to_f],
536
+ 'jid' => SecureRandom.hex(12) }
537
+ Roundhouse.redis do |conn|
538
+ conn.zadd('schedule', score, Roundhouse.dump_json(msg))
539
+ end
540
+ [msg, score]
541
+ end
542
+
543
+ def add_retry
544
+ msg = { 'class' => 'HardWorker',
545
+ 'args' => ['bob', 1, Time.now.to_f],
546
+ 'queue_id' => 100,
547
+ 'error_message' => 'Some fake message',
548
+ 'error_class' => 'RuntimeError',
549
+ 'retry_count' => 0,
550
+ 'failed_at' => Time.now.to_f,
551
+ 'jid' => SecureRandom.hex(12) }
552
+ score = Time.now.to_f
553
+ Roundhouse.redis do |conn|
554
+ conn.zadd('retry', score, Roundhouse.dump_json(msg))
555
+ end
556
+ [msg, score]
557
+ end
558
+
559
+ def add_dead
560
+ msg = { 'class' => 'HardWorker',
561
+ 'queue_id' => 100,
562
+ 'args' => ['bob', 1, Time.now.to_f],
563
+ 'queue' => 'foo',
564
+ 'error_message' => 'Some fake message',
565
+ 'error_class' => 'RuntimeError',
566
+ 'retry_count' => 0,
567
+ 'failed_at' => Time.now.utc,
568
+ 'jid' => SecureRandom.hex(12) }
569
+ score = Time.now.to_f
570
+ Roundhouse.redis do |conn|
571
+ conn.zadd('dead', score, Roundhouse.dump_json(msg))
572
+ end
573
+ [msg, score]
574
+ end
575
+
576
+ def add_xss_retry
577
+ msg = { 'class' => 'FailWorker',
578
+ 'queue_id' => 100,
579
+ 'args' => ['<a>hello</a>'],
580
+ 'queue' => 'foo',
581
+ 'error_message' => 'fail message: <a>hello</a>',
582
+ 'error_class' => 'RuntimeError',
583
+ 'retry_count' => 0,
584
+ 'failed_at' => Time.now.to_f,
585
+ 'jid' => SecureRandom.hex(12) }
586
+ score = Time.now.to_f
587
+ Roundhouse.redis do |conn|
588
+ conn.zadd('retry', score, Roundhouse.dump_json(msg))
589
+ end
590
+ [msg, score]
591
+ end
592
+
593
+ def add_worker
594
+ key = "#{hostname}:#{$$}"
595
+ msg = "{\"queue\":\"default\",\"payload\":{\"retry\":true,\"queue\":\"default\",\"timeout\":20,\"backtrace\":5,\"class\":\"HardWorker\",\"args\":[\"bob\",10,5],\"jid\":\"2b5ad2b016f5e063a1c62872\"},\"run_at\":1361208995}"
596
+ Roundhouse.redis do |conn|
597
+ conn.multi do
598
+ conn.sadd("processes", key)
599
+ conn.hmset(key, 'info', Roundhouse.dump_json('hostname' => 'foo', 'started_at' => Time.now.to_f, "queues" => []), 'at', Time.now.to_f, 'busy', 4)
600
+ conn.hmset("#{key}:workers", Time.now.to_f, msg)
601
+ end
602
+ end
603
+ end
604
+ end
605
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'helper'
2
+ require 'roundhouse'
3
+ require 'roundhouse/web_helpers'
4
+
5
+ class TestWebHelpers < Roundhouse::Test
6
+
7
+ class Helpers
8
+ include Roundhouse::WebHelpers
9
+
10
+ def initialize(params={})
11
+ @thehash = default.merge(params)
12
+ end
13
+
14
+ def request
15
+ self
16
+ end
17
+
18
+ def settings
19
+ self
20
+ end
21
+
22
+ def locales
23
+ ['web/locales']
24
+ end
25
+
26
+ def env
27
+ @thehash
28
+ end
29
+
30
+ def default
31
+ {
32
+ }
33
+ end
34
+ end
35
+
36
+ def test_locale_determination
37
+ obj = Helpers.new
38
+ assert_equal 'en', obj.locale
39
+
40
+ obj = Helpers.new('HTTP_ACCEPT_LANGUAGE' => 'fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2')
41
+ assert_equal 'fr', obj.locale
42
+
43
+ obj = Helpers.new('HTTP_ACCEPT_LANGUAGE' => 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2')
44
+ assert_equal 'zh-cn', obj.locale
45
+
46
+ obj = Helpers.new('HTTP_ACCEPT_LANGUAGE' => 'en-us; *')
47
+ assert_equal 'en', obj.locale
48
+
49
+ obj = Helpers.new('HTTP_ACCEPT_LANGUAGE' => '*')
50
+ assert_equal 'en', obj.locale
51
+ end
52
+ end
Binary file
Binary file
Binary file