stalk_climber 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rdoc_options +7 -0
- data/.travis.yml +8 -10
- data/Gemfile +1 -0
- data/lib/stalk_climber.rb +5 -0
- data/lib/stalk_climber/climber.rb +42 -54
- data/lib/stalk_climber/climber_enumerable.rb +110 -0
- data/lib/stalk_climber/climber_enumerables.rb +7 -0
- data/lib/stalk_climber/connection.rb +49 -37
- data/lib/stalk_climber/connection_pool.rb +37 -7
- data/lib/stalk_climber/job.rb +270 -67
- data/lib/stalk_climber/lazy_enumerable.rb +1 -0
- data/lib/stalk_climber/tube.rb +182 -0
- data/lib/stalk_climber/tubes.rb +37 -0
- data/lib/stalk_climber/version.rb +2 -1
- data/test/test_helper.rb +4 -2
- data/test/unit/beaneater_job_test.rb +260 -0
- data/test/unit/climber_enumerable.rb +18 -0
- data/test/unit/climber_test.rb +31 -100
- data/test/unit/connection_pool_test.rb +48 -33
- data/test/unit/connection_test.rb +200 -149
- data/test/unit/job_test.rb +240 -147
- data/test/unit/jobs_test.rb +106 -0
- data/test/unit/tube_test.rb +89 -0
- data/test/unit/tubes_test.rb +52 -0
- metadata +17 -2
data/test/unit/job_test.rb
CHANGED
@@ -1,196 +1,289 @@
|
|
1
1
|
require 'test_helper'
|
2
|
-
require 'json'
|
3
2
|
|
4
|
-
class Job <
|
3
|
+
class Job < StalkClimber::TestCase
|
5
4
|
|
6
|
-
|
5
|
+
setup do
|
7
6
|
@connection = StalkClimber::Connection.new('localhost:11300')
|
8
7
|
@job = seed_jobs(1).first
|
9
8
|
end
|
10
9
|
|
11
10
|
|
12
|
-
|
13
|
-
body = {'test' => true}.to_json
|
11
|
+
context 'beaneater integration' do
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
assert_equal body, @job.body
|
13
|
+
should 'derive from Beaneater::Job' do
|
14
|
+
assert_kind_of Beaneater::Job, @job
|
15
|
+
end
|
19
16
|
|
20
|
-
@job.connection.expects(:transmit).never
|
21
|
-
assert_equal body, @job.body
|
22
17
|
end
|
23
18
|
|
24
19
|
|
25
|
-
|
26
|
-
assert @job.instance_variable_get(:@connection)
|
27
|
-
assert @job.connection
|
28
|
-
end
|
20
|
+
context '#body' do
|
29
21
|
|
22
|
+
should 'perform peek and set/return body' do
|
23
|
+
body = "test #{Time.now.to_i}"
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
@job.connection.expects(:transmit).returns({
|
26
|
+
:body => body,
|
27
|
+
})
|
28
|
+
assert_equal body, @job.body
|
29
|
+
|
30
|
+
@job.connection.expects(:transmit).never
|
31
|
+
assert_equal body, @job.body
|
35
32
|
end
|
36
|
-
|
37
|
-
refute @job.instance_variable_get(:@stats)
|
38
|
-
assert_equal 'DELETED', @job.instance_variable_get(:@status)
|
33
|
+
|
39
34
|
end
|
40
35
|
|
41
36
|
|
42
|
-
|
43
|
-
|
37
|
+
context '#connection' do
|
38
|
+
|
39
|
+
should 'set connection instance variable to connection' do
|
40
|
+
assert @job.instance_variable_get(:@connection)
|
41
|
+
assert @job.connection
|
42
|
+
end
|
44
43
|
|
45
|
-
@job.delete
|
46
|
-
refute @job.exists?
|
47
44
|
end
|
48
45
|
|
49
46
|
|
50
|
-
|
51
|
-
job = StalkClimber::Job.new(@connection.transmit("peek #{@job.id}"))
|
52
|
-
@connection.expects(:transmit).never
|
53
|
-
assert_equal @connection, job.connection
|
54
|
-
assert_equal @job.id, job.id
|
55
|
-
assert_equal 'FOUND', job.instance_variable_get(:@status)
|
56
|
-
assert_equal('{}', job.body)
|
57
|
-
refute job.instance_variable_get(:@stats)
|
58
|
-
end
|
47
|
+
context '#delete' do
|
59
48
|
|
49
|
+
should 'delete the job' do
|
50
|
+
assert_equal 'DELETED', @job.delete[:status]
|
51
|
+
assert_raises Beaneater::NotFoundError do
|
52
|
+
@connection.transmit("peek #{@job.id}")
|
53
|
+
end
|
54
|
+
refute @job.instance_variable_get(:@body)
|
55
|
+
refute @job.instance_variable_get(:@stats)
|
56
|
+
assert_equal 'DELETED', @job.instance_variable_get(:@status)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
should 'not clear job instance variables if delete fails' do
|
61
|
+
client = Beaneater::Connection.new('localhost:11300')
|
62
|
+
tube_name = SecureRandom.uuid
|
63
|
+
client.transmit("use #{tube_name}")
|
64
|
+
client.transmit("watch #{tube_name}")
|
65
|
+
client.transmit('ignore default')
|
66
|
+
job = client.transmit(StalkClimber::Connection::PROBE_TRANSMISSION)
|
67
|
+
@job = StalkClimber::Job.new(@connection.transmit("stats-job #{job[:id]}"))
|
68
|
+
client.transmit('reserve')
|
69
|
+
assert_raises Beaneater::NotFoundError do
|
70
|
+
@job.delete
|
71
|
+
end
|
72
|
+
assert @job.instance_variable_get(:@stats), 'Expected stats instance variable to still exist after failed deletion'
|
73
|
+
client.transmit("delete #{job[:id]}")
|
74
|
+
end
|
60
75
|
|
61
|
-
def test_initialize_with_put_response
|
62
|
-
@connection.expects(:transmit).never
|
63
|
-
assert @job.id
|
64
|
-
assert_equal @connection, @job.connection
|
65
|
-
assert_equal 'INSERTED', @job.instance_variable_get(:@status)
|
66
|
-
refute @job.instance_variable_get(:@body)
|
67
|
-
refute @job.instance_variable_get(:@stats)
|
68
76
|
end
|
69
77
|
|
70
78
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
refute job.instance_variable_get(:@body)
|
80
|
-
StalkClimber::Job::STATS_ATTRIBUTES.each do |method_name|
|
81
|
-
assert job.send(method_name)
|
79
|
+
context '#exists?' do
|
80
|
+
|
81
|
+
|
82
|
+
should 'verify the job exists' do
|
83
|
+
assert @job.exists?
|
84
|
+
|
85
|
+
@job.delete
|
86
|
+
refute @job.exists?
|
82
87
|
end
|
88
|
+
|
83
89
|
end
|
84
90
|
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
'
|
94
|
-
'
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
92
|
+
context '#initialize' do
|
93
|
+
|
94
|
+
should 'support initialization with peek response' do
|
95
|
+
job = StalkClimber::Job.new(@connection.transmit("peek #{@job.id}"))
|
96
|
+
@connection.expects(:transmit).never
|
97
|
+
assert_equal @connection, job.connection
|
98
|
+
assert_equal @job.id, job.id
|
99
|
+
assert_equal 'FOUND', job.instance_variable_get(:@status)
|
100
|
+
assert_equal('{}', job.body)
|
101
|
+
refute job.instance_variable_get(:@stats)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
should 'support initialization with put response' do
|
106
|
+
@connection.expects(:transmit).never
|
107
|
+
assert @job.id
|
108
|
+
assert_equal @connection, @job.connection
|
109
|
+
assert_equal 'INSERTED', @job.instance_variable_get(:@status)
|
110
|
+
refute @job.instance_variable_get(:@body)
|
111
|
+
refute @job.instance_variable_get(:@stats)
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
should 'support initialization with stats response' do
|
116
|
+
job = StalkClimber::Job.new(@connection.transmit("stats-job #{@job.id}"))
|
117
|
+
@connection.expects(:transmit).never
|
118
|
+
assert_equal @job.id, job.id
|
119
|
+
assert_equal @connection, job.connection
|
120
|
+
assert_equal 'OK', job.instance_variable_get(:@status)
|
121
|
+
assert job.stats(false)
|
122
|
+
assert job.instance_variable_get(:@stats)
|
123
|
+
refute job.instance_variable_get(:@body)
|
124
|
+
StalkClimber::Job::CACHED_ATTRIBUTES.each do |method_name|
|
125
|
+
assert job.send(method_name)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
should 'support initialization with reserve response' do
|
131
|
+
job = StalkClimber::Job.new(@connection.transmit('reserve'))
|
132
|
+
@connection.expects(:transmit).never
|
133
|
+
assert_equal @connection, job.connection
|
134
|
+
assert job.id
|
135
|
+
assert_equal 'RESERVED', job.instance_variable_get(:@status)
|
136
|
+
assert_equal('{}', job.body)
|
137
|
+
refute job.instance_variable_get(:@stats)
|
111
138
|
end
|
139
|
+
|
140
|
+
|
141
|
+
should 'raise error if initialized with unknown response type' do
|
142
|
+
assert_raises RuntimeError do
|
143
|
+
StalkClimber::Job.new({:status => 'DELETED'})
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
112
147
|
end
|
113
148
|
|
114
149
|
|
115
|
-
|
116
|
-
|
117
|
-
|
150
|
+
context 'stats methods' do
|
151
|
+
|
152
|
+
should 'initialize stats attributes and attribute methods should return correct values' do
|
153
|
+
stats_body = {
|
154
|
+
'age' => 3,
|
155
|
+
'buries' => 0,
|
156
|
+
'delay' => 0,
|
157
|
+
'id' => 4412,
|
158
|
+
'kicks' => 0,
|
159
|
+
'pri' => 4294967295,
|
160
|
+
'releases' => 0,
|
161
|
+
'reserves' => 0,
|
162
|
+
'state' => 'ready',
|
163
|
+
'time-left' => 0,
|
164
|
+
'timeouts' => 0,
|
165
|
+
'ttr' => 300,
|
166
|
+
'tube' => 'default',
|
167
|
+
}
|
168
|
+
stats_response = {
|
169
|
+
:body => stats_body,
|
170
|
+
:connection => @connection,
|
171
|
+
:id => 149,
|
172
|
+
:status => 'OK',
|
173
|
+
}
|
174
|
+
job = StalkClimber::Job.new(stats_response)
|
175
|
+
StalkClimber::Job::CACHED_ATTRIBUTES.each do |method_name|
|
176
|
+
assert_equal stats_body[method_name], job.send(method_name), "Expected #{stats_body[method_name]} for #{method_name}, got #{job.send(method_name)}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
should 'be able to force refresh of stats' do
|
182
|
+
initial_value = @job.age
|
183
|
+
@connection.expects(:transmit).returns({:body => {'age' => initial_value + 100}})
|
184
|
+
assert_equal initial_value + 100, @job.age(true)
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
should 'allow for not refreshing stats' do
|
189
|
+
initial_value = @job.age
|
190
|
+
@connection.expects(:transmit).never
|
191
|
+
assert_equal initial_value, @job.age(false)
|
118
192
|
end
|
193
|
+
|
119
194
|
end
|
120
195
|
|
121
196
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
197
|
+
context '#stats' do
|
198
|
+
|
199
|
+
should 'be able to force refresh' do
|
200
|
+
body = {
|
201
|
+
'age'=>3,
|
202
|
+
'buries'=>0,
|
203
|
+
'delay'=>0,
|
204
|
+
'kicks'=>0,
|
205
|
+
'id' => 4412,
|
206
|
+
'pri'=>4294967295,
|
207
|
+
'releases'=>0,
|
208
|
+
'reserves'=>0,
|
209
|
+
'state'=>'ready',
|
210
|
+
'time-left'=>0,
|
211
|
+
'timeouts'=>0,
|
212
|
+
'ttr'=>300,
|
213
|
+
'tube'=>'default',
|
214
|
+
}
|
215
|
+
stats_1 = {
|
216
|
+
:body => {},
|
217
|
+
:connection => @connection,
|
218
|
+
:id => 149,
|
219
|
+
:status => 'OK',
|
220
|
+
}
|
221
|
+
stats_2 = {
|
222
|
+
:body => body,
|
223
|
+
:connection => @connection,
|
224
|
+
:id => 149,
|
225
|
+
:status => 'OK',
|
226
|
+
}
|
227
|
+
@connection.expects(:transmit).twice.returns(stats_1, stats_2)
|
228
|
+
job = StalkClimber::Job.new(@connection.transmit("stats-job #{@job.id}"))
|
229
|
+
job.stats
|
230
|
+
StalkClimber::Job::CACHED_ATTRIBUTES.each do |method_name|
|
231
|
+
assert_equal body[method_name], job.send(method_name), "Expected #{body[method_name.to_sym]} for #{method_name}, got #{job.send(method_name)}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
should 'allow for not refreshing stats' do
|
237
|
+
stat = @job.stats
|
238
|
+
@job.connection.expects(:transmit).never
|
239
|
+
assert_equal stat, @job.stats(false)
|
240
|
+
end
|
241
|
+
|
126
242
|
end
|
127
243
|
|
128
244
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
StalkClimber::Job::STATS_ATTRIBUTES.each do |method_name|
|
161
|
-
assert_equal body[method_name], job.send(method_name), "Expected #{body[method_name.to_sym]} for #{method_name}, got #{job.send(method_name)}"
|
245
|
+
context '#to_h' do
|
246
|
+
|
247
|
+
should 'return the expected hash' do
|
248
|
+
job_body = "test #{Time.now.to_i}"
|
249
|
+
stats_body = {
|
250
|
+
'age' => 3,
|
251
|
+
'body' => job_body, # Will be ignored during job init
|
252
|
+
'buries' => 0,
|
253
|
+
'connection' => @connection, # Will be ignored during job init
|
254
|
+
'delay' => 0,
|
255
|
+
'id' => 4412,
|
256
|
+
'kicks' => 0,
|
257
|
+
'pri' => 4294967295,
|
258
|
+
'releases' => 0,
|
259
|
+
'reserves' => 0,
|
260
|
+
'state' => 'ready',
|
261
|
+
'time-left' => 0,
|
262
|
+
'timeouts' => 0,
|
263
|
+
'ttr' => 300,
|
264
|
+
'tube' => 'default',
|
265
|
+
}
|
266
|
+
stats_response = {
|
267
|
+
:body => stats_body,
|
268
|
+
:connection => @connection,
|
269
|
+
:id => 149,
|
270
|
+
:status => 'OK',
|
271
|
+
}
|
272
|
+
job = StalkClimber::Job.new(stats_response)
|
273
|
+
job.instance_variable_set(:@body, job_body)
|
274
|
+
job.connection.expects(:transmit).once.returns(stats_response)
|
275
|
+
assert_equal stats_body, job.to_h
|
162
276
|
end
|
277
|
+
|
163
278
|
end
|
164
279
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
'delay' => 0,
|
173
|
-
'id' => 4412,
|
174
|
-
'kicks' => 0,
|
175
|
-
'pri' => 4294967295,
|
176
|
-
'releases' => 0,
|
177
|
-
'reserves' => 0,
|
178
|
-
'state' => 'ready',
|
179
|
-
'time-left' => 0,
|
180
|
-
'timeouts' => 0,
|
181
|
-
'ttr' => 300,
|
182
|
-
'tube' => 'default',
|
183
|
-
}
|
184
|
-
stats_response = {
|
185
|
-
:body => stats_body,
|
186
|
-
:connection => @connection,
|
187
|
-
:id => 149,
|
188
|
-
:status => 'OK',
|
189
|
-
}
|
190
|
-
job = StalkClimber::Job.new(stats_response)
|
191
|
-
job.instance_variable_set(:@body, job_body)
|
192
|
-
expected_hash = Hash[stats_body.map { |k, v| [k.to_sym, v] }]
|
193
|
-
assert_equal expected_hash, job.to_h
|
280
|
+
|
281
|
+
context '#to_s' do
|
282
|
+
|
283
|
+
should 'return expected string representation of job' do
|
284
|
+
assert_equal "#<StalkClimber::Job id=#{@job.id} body=#{@job.body.inspect}>", @job.to_s
|
285
|
+
end
|
286
|
+
|
194
287
|
end
|
195
288
|
|
196
289
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class JobsTest < StalkClimber::TestCase
|
4
|
+
|
5
|
+
context '#each' do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@climber = StalkClimber::Climber.new(BEANSTALK_ADDRESSES)
|
9
|
+
|
10
|
+
@test_jobs = {}
|
11
|
+
@climber.connection_pool.connections.each do |connection|
|
12
|
+
@test_jobs[connection.address] = []
|
13
|
+
5.times.to_a.map! do
|
14
|
+
@test_jobs[connection.address] << StalkClimber::Job.new(connection.transmit(StalkClimber::Connection::PROBE_TRANSMISSION))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
should 'cache jobs for later use' do
|
21
|
+
jobs = {}
|
22
|
+
@climber.jobs.each do |job|
|
23
|
+
jobs[job.connection.address] ||= {}
|
24
|
+
jobs[job.connection.address][job.id] = job
|
25
|
+
end
|
26
|
+
|
27
|
+
@climber.expects(:with_job).never
|
28
|
+
@climber.jobs.each do |job|
|
29
|
+
assert_equal jobs[job.connection.address][job.id], job
|
30
|
+
end
|
31
|
+
|
32
|
+
@climber.connection_pool.connections.each do |connection|
|
33
|
+
@test_jobs[connection.address].map(&:delete)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
should 'allow breaking from enumeration' do
|
39
|
+
begin
|
40
|
+
count = 0
|
41
|
+
@climber.jobs.each do |job|
|
42
|
+
break if 2 == count += 1
|
43
|
+
assert(false, "Jobs#each did not break when expected") if count >= 3
|
44
|
+
end
|
45
|
+
rescue => e
|
46
|
+
assert(false, "Breaking from Jobs#each raised #{e.inspect}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
context '#each_threaded' do
|
54
|
+
|
55
|
+
should 'work correctly in non-break situations' do
|
56
|
+
climber = StalkClimber::Climber.new(BEANSTALK_ADDRESSES)
|
57
|
+
test_jobs = {}
|
58
|
+
climber.connection_pool.connections.each do |connection|
|
59
|
+
test_jobs[connection.address] = []
|
60
|
+
5.times.to_a.map! do
|
61
|
+
test_jobs[connection.address] << StalkClimber::Job.new(connection.transmit(StalkClimber::Connection::PROBE_TRANSMISSION))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
climber.jobs.each_threaded do |job|
|
66
|
+
job
|
67
|
+
end
|
68
|
+
|
69
|
+
climber.connection_pool.connections.each do |connection|
|
70
|
+
test_jobs[connection.address].map(&:delete)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
context 'enumberable contract' do
|
78
|
+
|
79
|
+
should 'function correctly as an enumerable' do
|
80
|
+
climber = StalkClimber::Climber.new(BEANSTALK_ADDRESSES)
|
81
|
+
test_jobs = {}
|
82
|
+
climber.connection_pool.connections.each do |connection|
|
83
|
+
test_jobs[connection.address] = []
|
84
|
+
5.times.to_a.map! do
|
85
|
+
test_jobs[connection.address] << StalkClimber::Job.new(connection.transmit(StalkClimber::Connection::PROBE_TRANSMISSION))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# verify enumeration can be short circuited
|
90
|
+
climber.jobs.any? do |job|
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
# test normal enumeration
|
95
|
+
climber.jobs.all? do |job|
|
96
|
+
job
|
97
|
+
end
|
98
|
+
|
99
|
+
climber.connection_pool.connections.each do |connection|
|
100
|
+
test_jobs[connection.address].map(&:delete)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|