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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +16 -0
- data/3.0-Upgrade.md +70 -0
- data/Changes.md +1127 -0
- data/Gemfile +27 -0
- data/LICENSE +7 -0
- data/README.md +52 -0
- data/Rakefile +9 -0
- data/bin/roundhouse +19 -0
- data/bin/roundhousectl +93 -0
- data/lib/generators/roundhouse/templates/worker.rb.erb +9 -0
- data/lib/generators/roundhouse/templates/worker_spec.rb.erb +6 -0
- data/lib/generators/roundhouse/templates/worker_test.rb.erb +8 -0
- data/lib/generators/roundhouse/worker_generator.rb +49 -0
- data/lib/roundhouse/actor.rb +39 -0
- data/lib/roundhouse/api.rb +859 -0
- data/lib/roundhouse/cli.rb +396 -0
- data/lib/roundhouse/client.rb +210 -0
- data/lib/roundhouse/core_ext.rb +105 -0
- data/lib/roundhouse/exception_handler.rb +30 -0
- data/lib/roundhouse/fetch.rb +154 -0
- data/lib/roundhouse/launcher.rb +98 -0
- data/lib/roundhouse/logging.rb +104 -0
- data/lib/roundhouse/manager.rb +236 -0
- data/lib/roundhouse/middleware/chain.rb +149 -0
- data/lib/roundhouse/middleware/i18n.rb +41 -0
- data/lib/roundhouse/middleware/server/active_record.rb +13 -0
- data/lib/roundhouse/middleware/server/logging.rb +40 -0
- data/lib/roundhouse/middleware/server/retry_jobs.rb +206 -0
- data/lib/roundhouse/monitor.rb +124 -0
- data/lib/roundhouse/paginator.rb +42 -0
- data/lib/roundhouse/processor.rb +159 -0
- data/lib/roundhouse/rails.rb +24 -0
- data/lib/roundhouse/redis_connection.rb +77 -0
- data/lib/roundhouse/scheduled.rb +115 -0
- data/lib/roundhouse/testing/inline.rb +28 -0
- data/lib/roundhouse/testing.rb +193 -0
- data/lib/roundhouse/util.rb +68 -0
- data/lib/roundhouse/version.rb +3 -0
- data/lib/roundhouse/web.rb +264 -0
- data/lib/roundhouse/web_helpers.rb +249 -0
- data/lib/roundhouse/worker.rb +90 -0
- data/lib/roundhouse.rb +177 -0
- data/roundhouse.gemspec +27 -0
- data/test/config.yml +9 -0
- data/test/env_based_config.yml +11 -0
- data/test/fake_env.rb +0 -0
- data/test/fixtures/en.yml +2 -0
- data/test/helper.rb +49 -0
- data/test/test_api.rb +521 -0
- data/test/test_cli.rb +389 -0
- data/test/test_client.rb +294 -0
- data/test/test_exception_handler.rb +55 -0
- data/test/test_fetch.rb +206 -0
- data/test/test_logging.rb +34 -0
- data/test/test_manager.rb +169 -0
- data/test/test_middleware.rb +160 -0
- data/test/test_monitor.rb +258 -0
- data/test/test_processor.rb +176 -0
- data/test/test_rails.rb +23 -0
- data/test/test_redis_connection.rb +127 -0
- data/test/test_retry.rb +390 -0
- data/test/test_roundhouse.rb +87 -0
- data/test/test_scheduled.rb +120 -0
- data/test/test_scheduling.rb +75 -0
- data/test/test_testing.rb +78 -0
- data/test/test_testing_fake.rb +240 -0
- data/test/test_testing_inline.rb +65 -0
- data/test/test_util.rb +18 -0
- data/test/test_web.rb +605 -0
- data/test/test_web_helpers.rb +52 -0
- data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
- data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status/active.png +0 -0
- data/web/assets/images/status/idle.png +0 -0
- data/web/assets/images/status-sd8051fd480.png +0 -0
- data/web/assets/javascripts/application.js +83 -0
- data/web/assets/javascripts/dashboard.js +300 -0
- data/web/assets/javascripts/locales/README.md +27 -0
- data/web/assets/javascripts/locales/jquery.timeago.ar.js +96 -0
- data/web/assets/javascripts/locales/jquery.timeago.bg.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.bs.js +49 -0
- data/web/assets/javascripts/locales/jquery.timeago.ca.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.cs.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.cy.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.da.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.de.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.el.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.en-short.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.en.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.es.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.et.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.fa.js +22 -0
- data/web/assets/javascripts/locales/jquery.timeago.fi.js +28 -0
- data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +16 -0
- data/web/assets/javascripts/locales/jquery.timeago.fr.js +17 -0
- data/web/assets/javascripts/locales/jquery.timeago.he.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.hr.js +49 -0
- data/web/assets/javascripts/locales/jquery.timeago.hu.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.hy.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.id.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.it.js +16 -0
- data/web/assets/javascripts/locales/jquery.timeago.ja.js +19 -0
- data/web/assets/javascripts/locales/jquery.timeago.ko.js +17 -0
- data/web/assets/javascripts/locales/jquery.timeago.lt.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.mk.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.nl.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.no.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.pl.js +31 -0
- data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +16 -0
- data/web/assets/javascripts/locales/jquery.timeago.pt.js +16 -0
- data/web/assets/javascripts/locales/jquery.timeago.ro.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.rs.js +49 -0
- data/web/assets/javascripts/locales/jquery.timeago.ru.js +34 -0
- data/web/assets/javascripts/locales/jquery.timeago.sk.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.sl.js +44 -0
- data/web/assets/javascripts/locales/jquery.timeago.sv.js +18 -0
- data/web/assets/javascripts/locales/jquery.timeago.th.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.tr.js +16 -0
- data/web/assets/javascripts/locales/jquery.timeago.uk.js +34 -0
- data/web/assets/javascripts/locales/jquery.timeago.uz.js +19 -0
- data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +20 -0
- data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +20 -0
- data/web/assets/stylesheets/application.css +746 -0
- data/web/assets/stylesheets/bootstrap.css +9 -0
- data/web/locales/cs.yml +68 -0
- data/web/locales/da.yml +68 -0
- data/web/locales/de.yml +69 -0
- data/web/locales/el.yml +68 -0
- data/web/locales/en.yml +77 -0
- data/web/locales/es.yml +69 -0
- data/web/locales/fr.yml +69 -0
- data/web/locales/hi.yml +75 -0
- data/web/locales/it.yml +69 -0
- data/web/locales/ja.yml +69 -0
- data/web/locales/ko.yml +68 -0
- data/web/locales/nl.yml +68 -0
- data/web/locales/no.yml +69 -0
- data/web/locales/pl.yml +59 -0
- data/web/locales/pt-br.yml +68 -0
- data/web/locales/pt.yml +67 -0
- data/web/locales/ru.yml +75 -0
- data/web/locales/sv.yml +68 -0
- data/web/locales/ta.yml +75 -0
- data/web/locales/zh-cn.yml +68 -0
- data/web/locales/zh-tw.yml +68 -0
- data/web/views/_footer.erb +22 -0
- data/web/views/_job_info.erb +84 -0
- data/web/views/_nav.erb +66 -0
- data/web/views/_paging.erb +23 -0
- data/web/views/_poll_js.erb +5 -0
- data/web/views/_poll_link.erb +7 -0
- data/web/views/_status.erb +4 -0
- data/web/views/_summary.erb +40 -0
- data/web/views/busy.erb +90 -0
- data/web/views/dashboard.erb +75 -0
- data/web/views/dead.erb +34 -0
- data/web/views/layout.erb +31 -0
- data/web/views/morgue.erb +71 -0
- data/web/views/queue.erb +45 -0
- data/web/views/queues.erb +27 -0
- data/web/views/retries.erb +74 -0
- data/web/views/retry.erb +34 -0
- data/web/views/scheduled.erb +54 -0
- data/web/views/scheduled_job_info.erb +8 -0
- 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: <a>hello</a>" )
|
|
313
|
+
assert !last_response.body.include?( "fail message: <a>hello</a>" )
|
|
314
|
+
|
|
315
|
+
assert last_response.body.include?( "args\">"<a>hello</a>"<" )
|
|
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?( "<a>hello</a>" )
|
|
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?( "<a>hello</a>" )
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|