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_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