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.
@@ -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 Sta`kClimber::Job
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
@@ -1,4 +1,4 @@
1
1
  module BeanCounter
2
2
  # BeanCounter version number
3
- VERSION = '0.0.4'
3
+ VERSION = '0.1.0'
4
4
  end
@@ -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