bean_counter 0.0.4 → 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/.travis.yml +1 -0
- data/README.md +30 -4
- data/bean_counter.gemspec +1 -0
- data/lib/bean_counter.rb +1 -0
- data/lib/bean_counter/strategies/gemerald_beanstalk_strategy.rb +3 -0
- data/lib/bean_counter/strategies/gemerald_beanstalk_strategy/job.rb +73 -0
- data/lib/bean_counter/strategies/gemerald_beanstalk_strategy/strategy.rb +251 -0
- data/lib/bean_counter/strategies/gemerald_beanstalk_strategy/tube.rb +56 -0
- data/lib/bean_counter/strategies/stalk_climber_strategy.rb +1 -1
- data/lib/bean_counter/version.rb +1 -1
- data/test/test_helper.rb +23 -0
- data/test/unit/strategies/gemerald_beanstalk_strategy/job_test.rb +86 -0
- data/test/unit/strategies/gemerald_beanstalk_strategy/tube_test.rb +103 -0
- data/test/unit/strategies/gemerald_beanstalk_strategy_test.rb +604 -0
- data/test/unit/strategies/stalk_climber_strategy_test.rb +14 -1
- metadata +44 -32
- data/.rspec +0 -1
@@ -69,7 +69,7 @@ class BeanCounter::Strategy::StalkClimberStrategy < BeanCounter::Strategy
|
|
69
69
|
end
|
70
70
|
|
71
71
|
|
72
|
-
# Returns a Boolean indicating whether or not the provided
|
72
|
+
# Returns a Boolean indicating whether or not the provided StalkClimber::Job
|
73
73
|
# `job` matches the given Hash of `options`.
|
74
74
|
#
|
75
75
|
# See {MATCHABLE_JOB_ATTRIBUTES} for a list of
|
data/lib/bean_counter/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'debugger'
|
1
2
|
require 'coveralls'
|
2
3
|
Coveralls.wear_merged!
|
3
4
|
|
@@ -41,10 +42,32 @@ class BeanCounter::TestCase < MiniTest::Should::TestCase
|
|
41
42
|
include Timeout
|
42
43
|
|
43
44
|
|
45
|
+
def self.create_test_gemerald_beanstalks
|
46
|
+
return if class_variable_defined?(:@@gemerald_strategy)
|
47
|
+
@@gemerald_addrs ||= ['127.0.0.1:11400', '127.0.0.1:11401']
|
48
|
+
previous_urls = BeanCounter.beanstalkd_url
|
49
|
+
BeanCounter.beanstalkd_url = @@gemerald_addrs
|
50
|
+
@@gemerald_strategy = BeanCounter::Strategy::GemeraldBeanstalkStrategy.new
|
51
|
+
@@gemerald_beanstalks = @@gemerald_strategy.send(:beanstalks)
|
52
|
+
clients_by_address = @@gemerald_beanstalks.map do |beanstalk|
|
53
|
+
[beanstalk.address, beanstalk.direct_connection_client]
|
54
|
+
end
|
55
|
+
@@gemerald_clients = Hash[clients_by_address]
|
56
|
+
BeanCounter.beanstalkd_url = previous_urls
|
57
|
+
end
|
58
|
+
|
59
|
+
|
44
60
|
def client
|
45
61
|
return @client ||= Beaneater::Connection.new('localhost:11300')
|
46
62
|
end
|
47
63
|
|
64
|
+
|
65
|
+
def gemerald_client(gemerald_addr = nil)
|
66
|
+
self.class.create_test_gemerald_beanstalks
|
67
|
+
gemerald_addr ||= @@gemerald_addrs.first
|
68
|
+
return @@gemerald_clients[gemerald_addr]
|
69
|
+
end
|
70
|
+
|
48
71
|
end
|
49
72
|
|
50
73
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GemeraldBeanstalkStrategyJobTest < BeanCounter::TestCase
|
4
|
+
|
5
|
+
Job = BeanCounter::Strategy::GemeraldBeanstalkStrategy::Job
|
6
|
+
|
7
|
+
context 'attr methods' do
|
8
|
+
|
9
|
+
should 'respond to stats attr methods and retrieve from #to_hash Hash' do
|
10
|
+
Job::STATS_METHODS.each do |attr|
|
11
|
+
job = Job.new(stub(:stats => {attr => attr}), nil)
|
12
|
+
assert_equal attr, job.send(attr.gsub(/-/, '_'))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
should 'respond to #connection' do
|
18
|
+
assert_equal :connection, Job.new(nil, :connection).connection
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
should 'retrieve body and stats from gemerald_job' do
|
23
|
+
job = Job.new(stub(:stats => :stats, :body => :body), nil)
|
24
|
+
assert_equal :body, job.body
|
25
|
+
assert_equal :stats, job.stats
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
context '#delete' do
|
32
|
+
|
33
|
+
should 'return true if job deleted successfully' do
|
34
|
+
job = Job.new(stub(:stats => {'id' => 1}), stub(:transmit => "DELETED\r\n"))
|
35
|
+
assert job.delete
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
should 'return true if job does not exist' do
|
40
|
+
job = Job.new(stub(:stats => {'id' => 1}), stub(:transmit => "NOT_FOUND\r\n"))
|
41
|
+
assert job.delete
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
should 'return false if job could not be deleted' do
|
46
|
+
mock_connection = mock
|
47
|
+
mock_connection.expects(:transmit).twice.returns("NOT_FOUND\r\n", "FOUND\r\n")
|
48
|
+
job = Job.new(stub(:stats => {'id' => 1, :state => 'ready'}), mock_connection)
|
49
|
+
refute job.delete
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
context '#exists?' do
|
56
|
+
|
57
|
+
should 'return false if the job state is deleted' do
|
58
|
+
job = Job.new(stub(:stats => {'id' => 1, 'state' => 'deleted'}), nil)
|
59
|
+
refute job.exists?
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
should 'return true if job could be found on server' do
|
64
|
+
job = Job.new(stub(:stats => {'id' => 1, 'state' => 'ready'}), stub(:transmit => "FOUND\r\n"))
|
65
|
+
assert job.exists?
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
should 'return false if job could not be found on server' do
|
70
|
+
job = Job.new(stub(:stats => {'id' => 1, 'state' => 'ready'}), stub(:transmit => "NOT_FOUND\r\n"))
|
71
|
+
refute job.exists?
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
context '#to_hash' do
|
78
|
+
|
79
|
+
should 'return job stats less file with body and connection in alpha order' do
|
80
|
+
job = Job.new(stub(:stats => {'file' => :file, 'id' => 1}, :body => :body), :connection)
|
81
|
+
assert_equal({'body' => :body, 'connection' => :connection, 'id' => 1}, job.to_hash)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GemeraldBeanstalkStrategyTubeTest < BeanCounter::TestCase
|
4
|
+
|
5
|
+
Tube = BeanCounter::Strategy::GemeraldBeanstalkStrategy::Tube
|
6
|
+
|
7
|
+
context 'attr methods' do
|
8
|
+
|
9
|
+
should 'respond to stats attr methods and retrieve from #to_hash Hash' do
|
10
|
+
tube = Tube.new('xxx', [])
|
11
|
+
Tube::STATS_METHODS.each do |attr|
|
12
|
+
tube.expects(:to_hash).returns({attr => attr})
|
13
|
+
assert_equal attr, tube.send(attr.gsub(/-/, '_'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
should 'respond to #name' do
|
19
|
+
assert_equal 'xxx', Tube.new('xxx', []).name
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
context 'tests against real servers' do
|
26
|
+
|
27
|
+
setup do
|
28
|
+
create_test_beanstalks
|
29
|
+
@tube_name = SecureRandom.uuid
|
30
|
+
client.transmit("watch #{@tube_name}")
|
31
|
+
@tube = @strategy.send(:strategy_tube, @tube_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
context '#exists' do
|
36
|
+
|
37
|
+
should 'return true if the tube exists, false otherwise' do
|
38
|
+
assert @tube.exists?
|
39
|
+
client.transmit("ignore #{@tube_name}")
|
40
|
+
refute @tube.exists?
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
context '#to_hash' do
|
47
|
+
|
48
|
+
setup do
|
49
|
+
@tube_name = SecureRandom.uuid
|
50
|
+
client.transmit("watch #{@tube_name}")
|
51
|
+
@tube = @strategy.send(:strategy_tube, @tube_name)
|
52
|
+
|
53
|
+
other_client = client(@@gemerald_addrs.last)
|
54
|
+
other_client.transmit("watch #{@tube_name}")
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
should 'retrieve tube stats from all servers and return merged stats' do
|
59
|
+
stats = {
|
60
|
+
'cmd-delete' => 100,
|
61
|
+
'cmd-pause-tube' => 101,
|
62
|
+
'current-jobs-buried' => 102,
|
63
|
+
'current-jobs-delayed' => 103,
|
64
|
+
'current-jobs-ready' => 104,
|
65
|
+
'current-jobs-reserved' => 105,
|
66
|
+
'current-jobs-urgent' => 106,
|
67
|
+
'current-using' => 107,
|
68
|
+
'current-waiting' => 108,
|
69
|
+
'current-watching' => 109,
|
70
|
+
'name' => @tube_name,
|
71
|
+
'pause' => 111,
|
72
|
+
'pause-time-left' => 110,
|
73
|
+
'total-jobs' => 112,
|
74
|
+
}
|
75
|
+
GemeraldBeanstalk::Tube.any_instance.expects(:stats).twice.returns(stats)
|
76
|
+
expected = Hash[stats.map{|k, v| [k, v.is_a?(Numeric) ? v * 2 : v]}]
|
77
|
+
assert_equal expected, @tube.to_hash
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
should 'return empty Hash if the tube does not exist' do
|
82
|
+
assert_equal({}, @strategy.send(:strategy_tube, 'xxxx').to_hash)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def client(addr = nil)
|
91
|
+
self.class.create_test_gemerald_beanstalks
|
92
|
+
addr ||= @@gemerald_addrs.first
|
93
|
+
return @@gemerald_clients[addr]
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def create_test_beanstalks
|
98
|
+
self.class.create_test_gemerald_beanstalks
|
99
|
+
@strategy = @@gemerald_strategy
|
100
|
+
@beanstalks = @@gemerald_beanstalks
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,604 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
class GemeraldBeanstalkStrategyTest < BeanCounter::TestCase
|
5
|
+
|
6
|
+
GemeraldBeanstalkStrategy = BeanCounter::Strategy::GemeraldBeanstalkStrategy
|
7
|
+
|
8
|
+
context 'BeanCounter::Strategy::GemeraldBeanstalkStrategy' do
|
9
|
+
|
10
|
+
should 'be a known_strategy' do
|
11
|
+
BeanCounter::Strategy.known_strategy?(BeanCounter::Strategy::GemeraldBeanstalkStrategy)
|
12
|
+
BeanCounter::Strategy.known_strategy?(GemeraldBeanstalkStrategy)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
%w[jobs tubes].each do |enumerator|
|
19
|
+
context "##{enumerator} mocked behaviors" do
|
20
|
+
|
21
|
+
enumerator_singular = enumerator[0, enumerator.length - 1]
|
22
|
+
|
23
|
+
should "delegate to #{enumerator_singular}_enumerator returning an Enumerator" do
|
24
|
+
BeanCounter.expects(:beanstalkd_url).returns([])
|
25
|
+
@strategy = BeanCounter::Strategy::GemeraldBeanstalkStrategy.new
|
26
|
+
@strategy.expects(enumerator).returns([:foo].to_enum)
|
27
|
+
enum = @strategy.send(enumerator)
|
28
|
+
assert_kind_of Enumerator, enum
|
29
|
+
assert_equal :foo, enum.first
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
context 'gemerald_beanstalk config' do
|
38
|
+
|
39
|
+
should 'load gemerald_beanstalk introspection plugin' do
|
40
|
+
begin
|
41
|
+
included = GemeraldBeanstalk::Beanstalk.included_modules.include?(GemeraldBeanstalk::Plugin::Introspection)
|
42
|
+
rescue NameError => e
|
43
|
+
raise e unless e.message == 'uninitialized constant GemeraldBeanstalk::Plugin::Introspection'
|
44
|
+
included = false
|
45
|
+
end
|
46
|
+
assert included, 'Expected GemeraldBeanstalk::Plugin::Introspection to be loaded'
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
context '#parse_url' do
|
53
|
+
|
54
|
+
setup do
|
55
|
+
BeanCounter.expects(:beanstalkd_url).returns([])
|
56
|
+
@strategy = BeanCounter::Strategy::GemeraldBeanstalkStrategy.new
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
['', 'beanstalk://'].each do |uri_scheme|
|
61
|
+
context uri_scheme == '' ? 'without uri_scheme' : 'with uri scheme' do
|
62
|
+
should 'correctly parse an IP address without a port' do
|
63
|
+
url = uri_scheme + '127.0.0.1'
|
64
|
+
assert_equal ['127.0.0.1'], @strategy.send(:parse_url, url)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
should 'correctly parse an IP address a port' do
|
69
|
+
url = uri_scheme + '127.0.0.1:11300'
|
70
|
+
assert_equal ['127.0.0.1', 11300], @strategy.send(:parse_url, url)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
should 'correctly parse a hostname without a port' do
|
75
|
+
url = uri_scheme + 'localhost'
|
76
|
+
assert_equal ['localhost'], @strategy.send(:parse_url, url)
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
should 'correctly parse a hostname with a port' do
|
81
|
+
url = uri_scheme + 'localhost:11300'
|
82
|
+
assert_equal ['localhost', 11300], @strategy.send(:parse_url, url)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
should 'raise InvalidURIScheme if uri scheme is not beanstalk' do
|
89
|
+
assert_raises(ArgumentError) do
|
90
|
+
@strategy.send(:parse_url, 'http://localhost:11300')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
context 'tests against real servers' do
|
98
|
+
|
99
|
+
setup do
|
100
|
+
create_test_beanstalks
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
context 'enumerators' do
|
105
|
+
|
106
|
+
%w[jobs tubes].each do |enumerator|
|
107
|
+
enumerator_singular = enumerator[0, enumerator.length - 1]
|
108
|
+
|
109
|
+
context "##{enumerator_singular}_enumerator" do
|
110
|
+
|
111
|
+
should 'return an enumerator' do
|
112
|
+
assert_kind_of Enumerator, @strategy.send(enumerator)
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
should "iterate over all #{enumerator} on all beanstalks" do
|
117
|
+
expected_count = @beanstalks.length
|
118
|
+
iterated_count = 0
|
119
|
+
@beanstalks.each_with_index do |beanstalk, index|
|
120
|
+
stub = enumerator == 'jobs' ? [index] : {index => index}
|
121
|
+
beanstalk.stubs(enumerator).returns(stub)
|
122
|
+
end
|
123
|
+
@strategy.expects("strategy_#{enumerator_singular}").
|
124
|
+
times(expected_count).
|
125
|
+
returns(*(expected_count.times.to_a))
|
126
|
+
enums = @strategy.send(enumerator).to_a
|
127
|
+
enums.each_with_index do |enumerable, index|
|
128
|
+
assert_equal index, enumerable
|
129
|
+
iterated_count += 1
|
130
|
+
end
|
131
|
+
|
132
|
+
assert_equal expected_count, iterated_count
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
context '#jobs' do
|
141
|
+
|
142
|
+
should 'get actual jobs when not mocked' do
|
143
|
+
@strategy.jobs.each do |job|
|
144
|
+
assert @strategy.delete_job(job)
|
145
|
+
end
|
146
|
+
@beanstalks.each_with_index do |beanstalk, index|
|
147
|
+
beanstalk.direct_connection_client.transmit("put 0 0 120 1\r\n#{index}")
|
148
|
+
end
|
149
|
+
@strategy.jobs.each_with_index do |job, index|
|
150
|
+
assert_kind_of BeanCounter::Strategy::GemeraldBeanstalkStrategy::Job, job
|
151
|
+
assert_equal index.to_s, job.body
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
context '#tubes' do
|
159
|
+
|
160
|
+
should 'get merged tube stats when not mocked' do
|
161
|
+
shared_tube = SecureRandom.uuid
|
162
|
+
tube_names = @@gemerald_clients.values.map do |client|
|
163
|
+
tube_name = SecureRandom.uuid
|
164
|
+
client.transmit("watch #{tube_name}")
|
165
|
+
client.transmit("watch #{shared_tube}")
|
166
|
+
tube_name
|
167
|
+
end
|
168
|
+
tube_names.concat(['default', shared_tube])
|
169
|
+
@strategy.tubes.each do |tube|
|
170
|
+
tube_names.delete(tube.name)
|
171
|
+
end
|
172
|
+
assert tube_names.empty?, 'Expected all tubes to be enumerated'
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
context '#collect_new_jobs' do
|
181
|
+
|
182
|
+
should 'raise ArgumentError unless block given' do
|
183
|
+
assert_raises(ArgumentError) do
|
184
|
+
@strategy.collect_new_jobs
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
should 'return empty array if no jobs enqueued by block' do
|
190
|
+
new_jobs = @strategy.collect_new_jobs {}
|
191
|
+
assert_equal [], new_jobs
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
should 'only return jobs enqueued during the execution of the given block' do
|
196
|
+
tube_name = message = SecureRandom.uuid
|
197
|
+
other_client = client(@@gemerald_addrs.last)
|
198
|
+
[client, other_client].each do |gb_client|
|
199
|
+
gb_client.transmit("use #{tube_name}")
|
200
|
+
gb_client.transmit("watch #{tube_name}")
|
201
|
+
gb_client.transmit('ignore default')
|
202
|
+
gb_client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
203
|
+
end
|
204
|
+
# Add an extra job to first client to make id lists unequal and help test order
|
205
|
+
message = SecureRandom.uuid
|
206
|
+
client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
207
|
+
|
208
|
+
jobs = []
|
209
|
+
new_jobs = @strategy.collect_new_jobs do
|
210
|
+
[client, other_client].each do |gb_client|
|
211
|
+
5.times do
|
212
|
+
message = SecureRandom.uuid
|
213
|
+
response = gb_client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
214
|
+
jobs << insertion_id(response)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
[client, other_client].each do |gb_client|
|
220
|
+
message = SecureRandom.uuid
|
221
|
+
gb_client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
222
|
+
end
|
223
|
+
assert_equal new_jobs.map{|job| job.id}, jobs
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
context '#delete_job' do
|
230
|
+
|
231
|
+
should 'delete the given job and return true' do
|
232
|
+
message = SecureRandom.uuid
|
233
|
+
response = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
234
|
+
job_id = insertion_id(response)
|
235
|
+
job = strategy_job(client, job_id)
|
236
|
+
assert @strategy.delete_job(job), 'Failed to delete job'
|
237
|
+
assert_equal 'deleted', job.state
|
238
|
+
assert_equal "NOT_FOUND\r\n", client.transmit("stats-job #{job_id}")
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
should 'return true if the job has already been deleted' do
|
243
|
+
message = SecureRandom.uuid
|
244
|
+
response = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
245
|
+
job_id = insertion_id(response)
|
246
|
+
job = strategy_job(client, job_id)
|
247
|
+
assert @strategy.delete_job(job), 'Failed to delete job'
|
248
|
+
assert @strategy.delete_job(job), 'Expected deleting a deleted job to return true'
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
should 'return false if the job could not be deleted' do
|
253
|
+
tube_name = message = SecureRandom.uuid
|
254
|
+
other_client = client.beanstalk.direct_connection_client
|
255
|
+
other_client.transmit("use #{tube_name}")
|
256
|
+
other_client.transmit("watch #{tube_name}")
|
257
|
+
other_client.transmit('ignore defualt')
|
258
|
+
response = other_client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
|
259
|
+
job_id = insertion_id(response)
|
260
|
+
other_client.transmit('reserve')
|
261
|
+
job = strategy_job(client, job_id)
|
262
|
+
assert_equal 'reserved', job.state
|
263
|
+
refute @strategy.delete_job(job), 'Expected deleting job reserved by other client to fail'
|
264
|
+
other_client.transmit("delete #{job_id}")
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
context '#job_matches?' do
|
271
|
+
|
272
|
+
setup do
|
273
|
+
@message = @tube_name = SecureRandom.uuid
|
274
|
+
client.transmit("use #{@tube_name}")
|
275
|
+
response = client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
|
276
|
+
job_id = insertion_id(response)
|
277
|
+
@job = strategy_job(client, job_id)
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
should 'match string fields using string or regex' do
|
282
|
+
{
|
283
|
+
:body => @message,
|
284
|
+
:state => @job.state,
|
285
|
+
:tube => @tube_name,
|
286
|
+
}.each_pair do |key, value|
|
287
|
+
assert @strategy.job_matches?(@job, key => value)
|
288
|
+
assert @strategy.job_matches?(@job, key => %r[#{value[2, 3]}])
|
289
|
+
refute @strategy.job_matches?(@job, key => 'foobar')
|
290
|
+
refute @strategy.job_matches?(@job, key => /foobar/)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
should 'match integer fields using integer or range' do
|
296
|
+
verify_attrs(@job, {
|
297
|
+
:age => @job.age,
|
298
|
+
:buries => @job.buries,
|
299
|
+
:delay => @job.delay,
|
300
|
+
:id => @job.id,
|
301
|
+
:kicks => @job.kicks,
|
302
|
+
:pri => @job.pri,
|
303
|
+
:releases => @job.releases,
|
304
|
+
:reserves => @job.reserves,
|
305
|
+
:'time-left' => @job.time_left,
|
306
|
+
:timeouts => @job.timeouts,
|
307
|
+
:ttr => @job.ttr,
|
308
|
+
})
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
should 'match integer fields using integer or range (with more stubs)' do
|
313
|
+
job_attrs = {
|
314
|
+
'age' => 100,
|
315
|
+
'buries' => 101,
|
316
|
+
'delay' => 102,
|
317
|
+
'kicks' => 103,
|
318
|
+
'pri' => 104,
|
319
|
+
'releases' => 105,
|
320
|
+
'reserves' => 106,
|
321
|
+
'time-left' => 107,
|
322
|
+
'timeouts' => 108,
|
323
|
+
'ttr' => 109,
|
324
|
+
}
|
325
|
+
@job.stubs(:exists?).returns(true)
|
326
|
+
@job.instance_variable_get(:@gemerald_job).stubs(:stats).returns(job_attrs)
|
327
|
+
verify_attrs(@job, job_attrs)
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
should 'be able to match with a proc' do
|
332
|
+
matching_connection_proc = proc do |connection|
|
333
|
+
connection.beanstalk.address == '127.0.0.1:11400'
|
334
|
+
end
|
335
|
+
assert @strategy.job_matches?(@job, :connection => matching_connection_proc)
|
336
|
+
|
337
|
+
failing_connection_proc = proc do |connection|
|
338
|
+
connection.beanstalk.address != '127.0.0.1:11400'
|
339
|
+
end
|
340
|
+
refute @strategy.job_matches?(@job, :connection => failing_connection_proc)
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
should 'not try to match on non-matachable attributes' do
|
345
|
+
# Should never be called
|
346
|
+
@job.expects(:bury).never
|
347
|
+
@job.expects(:delete).never
|
348
|
+
@job.expects(:deadline_approaching).never
|
349
|
+
|
350
|
+
assert @strategy.job_matches?(@job, {
|
351
|
+
:body => @message,
|
352
|
+
:bury => 'baz',
|
353
|
+
:deadline_approaching => 'bar',
|
354
|
+
:delete => 'bar',
|
355
|
+
:stats => 'boom',
|
356
|
+
})
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
should 'not match deleted job' do
|
361
|
+
client.transmit("delete #{@job.id}")
|
362
|
+
refute @strategy.job_matches?(@job, :body => @message)
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
|
368
|
+
context '#pretty_print_job' do
|
369
|
+
|
370
|
+
setup do
|
371
|
+
@message = @tube_name = SecureRandom.uuid
|
372
|
+
client.transmit("use #{@tube_name}")
|
373
|
+
response = client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
|
374
|
+
job_id = insertion_id(response)
|
375
|
+
@job = strategy_job(client, job_id)
|
376
|
+
end
|
377
|
+
|
378
|
+
|
379
|
+
should 'return expected text representation of job' do
|
380
|
+
stats = {
|
381
|
+
'age' => age = 3,
|
382
|
+
'buries' => buries = 0,
|
383
|
+
'delay' => delay = 0,
|
384
|
+
'id' => id = 4412,
|
385
|
+
'kicks' => kicks = 0,
|
386
|
+
'pri' => pri = 4294967295,
|
387
|
+
'releases' => releases = 0,
|
388
|
+
'reserves' => reserves = 0,
|
389
|
+
'state' => state = 'ready',
|
390
|
+
'time-left' => time_left = 0,
|
391
|
+
'timeouts' => timeouts = 0,
|
392
|
+
'ttr' => ttr = 300,
|
393
|
+
'tube' => tube = 'default',
|
394
|
+
}
|
395
|
+
@job.expects(:stats).returns(stats)
|
396
|
+
expected = %W[
|
397
|
+
"age"=>#{age} "body"=>"#{@message}" "buries"=>#{buries}
|
398
|
+
"delay"=>#{delay} "id"=>#{id} "kicks"=>#{kicks} "pri"=>#{pri} "releases"=>#{releases}
|
399
|
+
"reserves"=>#{reserves} "state"=>"#{state}" "time-left"=>#{time_left}
|
400
|
+
"timeouts"=>#{timeouts} "ttr"=>#{ttr} "tube"=>"#{tube}"
|
401
|
+
].join(', ')
|
402
|
+
assert_equal "{#{expected}}", @strategy.pretty_print_job(@job)
|
403
|
+
end
|
404
|
+
|
405
|
+
end
|
406
|
+
|
407
|
+
|
408
|
+
context '#pretty_print_tube' do
|
409
|
+
|
410
|
+
setup do
|
411
|
+
@tube_name = SecureRandom.uuid
|
412
|
+
client.transmit("watch #{@tube_name}")
|
413
|
+
@tube = strategy_tube(@tube_name)
|
414
|
+
|
415
|
+
other_client = client(@@gemerald_addrs.last)
|
416
|
+
other_client.transmit("watch #{@tube_name}")
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
should 'return expected text representation of tube' do
|
421
|
+
stats = {
|
422
|
+
'cmd-delete' => cmd_delete = 100,
|
423
|
+
'cmd-pause-tube' => cmd_pause_tube = 101,
|
424
|
+
'current-jobs-buried' => current_jobs_buried = 102,
|
425
|
+
'current-jobs-delayed' => current_jobs_delayed = 103,
|
426
|
+
'current-jobs-ready' => current_jobs_ready = 104,
|
427
|
+
'current-jobs-reserved' => current_jobs_reserved = 105,
|
428
|
+
'current-jobs-urgent' => current_jobs_urgent = 106,
|
429
|
+
'current-using' => current_using = 107,
|
430
|
+
'current-waiting' => current_waiting = 108,
|
431
|
+
'current-watching' => current_watching = 109,
|
432
|
+
'name' => name = @tube_name,
|
433
|
+
'pause' => pause = 111,
|
434
|
+
'pause-time-left' => pause_time_left = 110,
|
435
|
+
'total-jobs' => total_jobs = 112,
|
436
|
+
}
|
437
|
+
GemeraldBeanstalk::Tube.any_instance.expects(:stats).twice.returns(stats)
|
438
|
+
expected = %W[
|
439
|
+
"cmd-delete"=>#{cmd_delete * 2} "cmd-pause-tube"=>#{cmd_pause_tube * 2}
|
440
|
+
"current-jobs-buried"=>#{current_jobs_buried * 2}
|
441
|
+
"current-jobs-delayed"=>#{current_jobs_delayed * 2}
|
442
|
+
"current-jobs-ready"=>#{current_jobs_ready * 2}
|
443
|
+
"current-jobs-reserved"=>#{current_jobs_reserved * 2}
|
444
|
+
"current-jobs-urgent"=>#{current_jobs_urgent * 2}
|
445
|
+
"current-using"=>#{current_using * 2} "current-waiting"=>#{current_waiting * 2}
|
446
|
+
"current-watching"=>#{current_watching * 2} "name"=>"#{name}"
|
447
|
+
"pause"=>#{pause * 2} "pause-time-left"=>#{pause_time_left * 2}
|
448
|
+
"total-jobs"=>#{total_jobs * 2}
|
449
|
+
].join(', ')
|
450
|
+
assert_equal "{#{expected}}", @strategy.pretty_print_tube(@tube)
|
451
|
+
end
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
context '#tube_matches?' do
|
457
|
+
|
458
|
+
setup do
|
459
|
+
@tube_name = SecureRandom.uuid
|
460
|
+
client.transmit("watch #{@tube_name}")
|
461
|
+
@tube = strategy_tube(@tube_name)
|
462
|
+
end
|
463
|
+
|
464
|
+
|
465
|
+
should 'match name using string or regex' do
|
466
|
+
assert @strategy.tube_matches?(@tube, :name => @tube_name)
|
467
|
+
assert @strategy.tube_matches?(@tube, :name => %r[#{@tube_name[2, 3]}])
|
468
|
+
refute @strategy.tube_matches?(@tube, :name => 'foobar')
|
469
|
+
refute @strategy.tube_matches?(@tube, :name => /foobar/)
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
should 'match integer fields using integer or range' do
|
474
|
+
tube_stats = @tube.to_hash
|
475
|
+
verify_attrs(@tube, {
|
476
|
+
'cmd-delete' => tube_stats['cmd-delete'],
|
477
|
+
'cmd-pause-tube' => tube_stats['cmd-pause-tube'],
|
478
|
+
'current-jobs-buried' => tube_stats['current-jobs-buried'],
|
479
|
+
'current-jobs-delayed' => tube_stats['current-jobs-delayed'],
|
480
|
+
'current-jobs-ready' => tube_stats['current-jobs-ready'],
|
481
|
+
'current-jobs-reserved' => tube_stats['current-jobs-reserved'],
|
482
|
+
'current-jobs-urgent' => tube_stats['current-jobs-urgent'],
|
483
|
+
'current-using' => tube_stats['current-using'],
|
484
|
+
'current-waiting' => tube_stats['current-waiting'],
|
485
|
+
'current-watching' => tube_stats['current-watching'],
|
486
|
+
'pause' => tube_stats['pause'],
|
487
|
+
'pause-time-left' => tube_stats['pause-time-left'],
|
488
|
+
'total-jobs' => tube_stats['total-jobs'],
|
489
|
+
})
|
490
|
+
end
|
491
|
+
|
492
|
+
|
493
|
+
should 'match integer fields using integer or range (with more stubs)' do
|
494
|
+
tube_attrs = {
|
495
|
+
'cmd-delete' => 100,
|
496
|
+
'cmd-pause-tube' => 101,
|
497
|
+
'current-jobs-buried' => 102,
|
498
|
+
'current-jobs-delayed' => 103,
|
499
|
+
'current-jobs-ready' => 104,
|
500
|
+
'current-jobs-reserved' => 105,
|
501
|
+
'current-jobs-urgent' => 106,
|
502
|
+
'current-using' => 107,
|
503
|
+
'current-waiting' => 108,
|
504
|
+
'current-watching' => 109,
|
505
|
+
'pause-time-left' => 110,
|
506
|
+
'pause' => 111,
|
507
|
+
'total-jobs' => 112,
|
508
|
+
}
|
509
|
+
@tube.stubs(:to_hash).returns(tube_attrs)
|
510
|
+
verify_attrs(@tube, tube_attrs)
|
511
|
+
end
|
512
|
+
|
513
|
+
|
514
|
+
should 'be able to match with a proc' do
|
515
|
+
matching_name_proc = proc do |name|
|
516
|
+
name == @tube_name
|
517
|
+
end
|
518
|
+
assert @strategy.tube_matches?(@tube, :name => matching_name_proc)
|
519
|
+
|
520
|
+
failing_name_proc = proc do |name|
|
521
|
+
name != @tube_name
|
522
|
+
end
|
523
|
+
refute @strategy.tube_matches?(@tube, :name => failing_name_proc)
|
524
|
+
end
|
525
|
+
|
526
|
+
|
527
|
+
should 'not try to match on non-matachable attributes' do
|
528
|
+
%w[delete put reserve].each do |method|
|
529
|
+
@tube.expects(method).never
|
530
|
+
end
|
531
|
+
|
532
|
+
assert @strategy.tube_matches?(@tube, {
|
533
|
+
:delete => true,
|
534
|
+
:name => @tube_name,
|
535
|
+
:put => true,
|
536
|
+
:reserve => true,
|
537
|
+
})
|
538
|
+
end
|
539
|
+
|
540
|
+
|
541
|
+
should 'not match tube that no longer exists' do
|
542
|
+
client.transmit("ignore #{@tube_name}")
|
543
|
+
refute @tube.exists?
|
544
|
+
refute @strategy.tube_matches?(@tube)
|
545
|
+
end
|
546
|
+
|
547
|
+
end
|
548
|
+
|
549
|
+
end
|
550
|
+
|
551
|
+
|
552
|
+
def client(addr = nil)
|
553
|
+
return gemerald_client(addr)
|
554
|
+
end
|
555
|
+
|
556
|
+
|
557
|
+
def create_test_beanstalks
|
558
|
+
self.class.create_test_gemerald_beanstalks
|
559
|
+
@strategy = @@gemerald_strategy
|
560
|
+
@beanstalks = @@gemerald_beanstalks
|
561
|
+
end
|
562
|
+
|
563
|
+
|
564
|
+
def insertion_id(response)
|
565
|
+
return response.scan(/INSERTED (\d+)\r\n/).flatten.first.to_i
|
566
|
+
end
|
567
|
+
|
568
|
+
|
569
|
+
def strategy_job(gb_client, job_id)
|
570
|
+
gb_job = gb_client.beanstalk.jobs.reverse.detect{|job| job.id == job_id}
|
571
|
+
return @strategy.send(:strategy_job, gb_job)
|
572
|
+
end
|
573
|
+
|
574
|
+
|
575
|
+
def strategy_tube(tube_name)
|
576
|
+
return @strategy.send(:strategy_tube, tube_name)
|
577
|
+
end
|
578
|
+
|
579
|
+
|
580
|
+
def verify_attrs(strategy_object, attrs)
|
581
|
+
method = strategy_object.is_a?(BeanCounter::Strategy::GemeraldBeanstalkStrategy::Job) ? :job_matches? : :tube_matches?
|
582
|
+
attrs.each_pair do |key, value|
|
583
|
+
obj_hash = strategy_object.to_hash
|
584
|
+
sanitized_key = key.to_s
|
585
|
+
assert(
|
586
|
+
@strategy.send(method, strategy_object, key => value),
|
587
|
+
"Expected #{key} (#{obj_hash[sanitized_key]}) to match #{value}"
|
588
|
+
)
|
589
|
+
assert(
|
590
|
+
@strategy.send(method, strategy_object, key => (value - 5)..(value + 1)),
|
591
|
+
"Expected #{key} (#{obj_hash[sanitized_key]}) to match #{value} +/-5"
|
592
|
+
)
|
593
|
+
refute(
|
594
|
+
@strategy.send(method, strategy_object, key => value - 1),
|
595
|
+
"Expected #{key} (#{obj_hash[sanitized_key]}) to not match #{value}"
|
596
|
+
)
|
597
|
+
refute(
|
598
|
+
@strategy.send(method, strategy_object, key => (value + 100)..(value + 200)),
|
599
|
+
"Expected #{key} (#{obj_hash[sanitized_key]}) to not match #{value + 100}..#{value + 200}"
|
600
|
+
)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
end
|