bean_counter 0.0.1
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.
- data/.gitignore +17 -0
- data/.rdoc_options +9 -0
- data/.rspec +1 -0
- data/.travis.yml +21 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/README.md +306 -0
- data/Rakefile +38 -0
- data/bean_counter.gemspec +26 -0
- data/lib/bean_counter.rb +8 -0
- data/lib/bean_counter/core.rb +115 -0
- data/lib/bean_counter/enqueued_expectation.rb +130 -0
- data/lib/bean_counter/mini_test.rb +3 -0
- data/lib/bean_counter/spec.rb +5 -0
- data/lib/bean_counter/spec_matchers.rb +25 -0
- data/lib/bean_counter/strategies/stalk_climber_strategy.rb +150 -0
- data/lib/bean_counter/strategy.rb +250 -0
- data/lib/bean_counter/test_assertions.rb +227 -0
- data/lib/bean_counter/test_unit.rb +3 -0
- data/lib/bean_counter/tube_expectation.rb +66 -0
- data/lib/bean_counter/version.rb +4 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/spec_spec.rb +224 -0
- data/test/test_helper.rb +52 -0
- data/test/unit/core_test.rb +156 -0
- data/test/unit/enqueued_expectation_test.rb +223 -0
- data/test/unit/strategies/stalk_climber_strategy_test.rb +501 -0
- data/test/unit/strategy_test.rb +98 -0
- data/test/unit/test_assertions_test.rb +270 -0
- data/test/unit/tube_expectation_test.rb +93 -0
- metadata +149 -0
    
        data/test/test_helper.rb
    ADDED
    
    | @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            require 'coveralls'
         | 
| 2 | 
            +
            Coveralls.wear_merged!
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            lib = File.expand_path('../../lib', __FILE__)
         | 
| 5 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require 'test/unit'
         | 
| 8 | 
            +
            require 'mocha/setup'
         | 
| 9 | 
            +
            require 'minitest/autorun'
         | 
| 10 | 
            +
            require 'minitest/should'
         | 
| 11 | 
            +
            require 'bean_counter'
         | 
| 12 | 
            +
            require 'bean_counter/mini_test'
         | 
| 13 | 
            +
            require 'securerandom'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            # Open TCP connection with beanstalkd server prevents
         | 
| 16 | 
            +
            # JRuby timeout from killing thread. Has some weird
         | 
| 17 | 
            +
            # side effects, but allows Timeout#timeout to work
         | 
| 18 | 
            +
            if RUBY_PLATFORM == 'java'
         | 
| 19 | 
            +
              module Timeout
         | 
| 20 | 
            +
                def timeout(sec, klass=nil)
         | 
| 21 | 
            +
                  return yield(sec) if sec == nil or sec.zero?
         | 
| 22 | 
            +
                  thread = Thread.new { yield(sec) }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  if thread.join(sec).nil?
         | 
| 25 | 
            +
                    java_thread = JRuby.reference(thread)
         | 
| 26 | 
            +
                    thread.kill
         | 
| 27 | 
            +
                    java_thread.native_thread.interrupt
         | 
| 28 | 
            +
                    thread.join(0.15)
         | 
| 29 | 
            +
                    raise (klass || Error), 'execution expired'
         | 
| 30 | 
            +
                  else
         | 
| 31 | 
            +
                    thread.value
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            BeanCounter.beanstalkd_url = 'beanstalk://localhost'
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            class BeanCounter::TestCase < MiniTest::Should::TestCase
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              include Timeout
         | 
| 42 | 
            +
             | 
| 43 | 
            +
             | 
| 44 | 
            +
              def client
         | 
| 45 | 
            +
                return @client ||= Beaneater::Connection.new('localhost:11300')
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
             | 
| 51 | 
            +
            class BeanCounter::KnownStrategy < BeanCounter::Strategy
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,156 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class CoreTest < BeanCounter::TestCase
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              context '::beanstalkd_url' do
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                should 'check BeanCounter, then ENV, then Beaneater for beanstalkd_url and raise an error if no url found' do
         | 
| 8 | 
            +
                  original_url_bean_counter = BeanCounter.beanstalkd_url
         | 
| 9 | 
            +
                  original_url_env = ENV['BEANSTALKD_URL']
         | 
| 10 | 
            +
                  original_url_beaneater = Beaneater.configuration.beanstalkd_url
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  BeanCounter.beanstalkd_url = nil
         | 
| 13 | 
            +
                  Beaneater.configuration.beanstalkd_url = nil
         | 
| 14 | 
            +
                  ENV['BEANSTALKD_URL'] = nil
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  assert_raises(RuntimeError) do
         | 
| 17 | 
            +
                    BeanCounter.beanstalkd_url
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  new_url = 'beanstalk://beaneater'
         | 
| 21 | 
            +
                  Beaneater.configuration.beanstalkd_url = new_url
         | 
| 22 | 
            +
                  assert_equal new_url, BeanCounter.beanstalkd_url
         | 
| 23 | 
            +
                  Beaneater.configuration.beanstalkd_url = original_url_beaneater
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
                  new_url = 'beanstalk://bean_counter'
         | 
| 27 | 
            +
                  BeanCounter.beanstalkd_url = new_url
         | 
| 28 | 
            +
                  assert_equal new_url, BeanCounter.beanstalkd_url
         | 
| 29 | 
            +
                  BeanCounter.beanstalkd_url = original_url_bean_counter
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  # Env beanstalkd_url is always turned into an array
         | 
| 32 | 
            +
                  new_url = 'beanstalk://env'
         | 
| 33 | 
            +
                  ENV['BEANSTALKD_URL'] = new_url
         | 
| 34 | 
            +
                  assert_equal [new_url], BeanCounter.beanstalkd_url
         | 
| 35 | 
            +
                  ENV['BEANSTALKD_URL'] = original_url_env
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
              context '::default_strategy' do
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                should 'return materialized version of DEFAULT_STRATEGY' do
         | 
| 44 | 
            +
                  assert_equal(
         | 
| 45 | 
            +
                    BeanCounter::Strategy.materialize_strategy(BeanCounter::DEFAULT_STRATEGY),
         | 
| 46 | 
            +
                    BeanCounter.default_strategy
         | 
| 47 | 
            +
                  )
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
             | 
| 53 | 
            +
              context '::reset!' do
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                setup do
         | 
| 56 | 
            +
                  @tube_name = SecureRandom.uuid
         | 
| 57 | 
            +
                  client.transmit("use #{@tube_name}")
         | 
| 58 | 
            +
                  @message = SecureRandom.uuid
         | 
| 59 | 
            +
                  client.transmit("watch #{@tube_name}")
         | 
| 60 | 
            +
                  client.transmit('ignore default')
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                should 'remove all jobs from all tubes when not given a tube name' do
         | 
| 64 | 
            +
                  jobs = []
         | 
| 65 | 
            +
                  jobs << client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")[:id]
         | 
| 66 | 
            +
                  timeout(1) do
         | 
| 67 | 
            +
                    job_id = client.transmit('reserve')[:id]
         | 
| 68 | 
            +
                    client.transmit("bury #{job_id} 0")
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                  5.times do
         | 
| 71 | 
            +
                    client.transmit("use #{SecureRandom.uuid}")
         | 
| 72 | 
            +
                    jobs << client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")[:id]
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                  client.transmit("use #{SecureRandom.uuid}")
         | 
| 75 | 
            +
                  jobs << client.transmit("put 0 1024 120 #{@message.bytesize}\r\n#{@message}")[:id]
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  BeanCounter.reset!
         | 
| 78 | 
            +
                  jobs.each do |job_id|
         | 
| 79 | 
            +
                    assert_raises(Beaneater::NotFoundError) do
         | 
| 80 | 
            +
                      client.transmit("stats-job #{job_id}")
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
             | 
| 86 | 
            +
                should 'only remove jobs from the specified tube when given a tube name' do
         | 
| 87 | 
            +
                  jobs = []
         | 
| 88 | 
            +
                  jobs << client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")[:id]
         | 
| 89 | 
            +
                  timeout(1) do
         | 
| 90 | 
            +
                    job_id = client.transmit('reserve')[:id]
         | 
| 91 | 
            +
                    client.transmit("bury #{job_id} 0")
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                  jobs << client.transmit("put 0 1024 120 #{@message.bytesize}\r\n#{@message}")[:id]
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  client.transmit("use #{SecureRandom.uuid}")
         | 
| 96 | 
            +
                  other_job_id = client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")[:id].to_i
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  BeanCounter.reset!(@tube_name)
         | 
| 99 | 
            +
                  jobs.each do |job_id|
         | 
| 100 | 
            +
                    assert_raises(Beaneater::NotFoundError) do
         | 
| 101 | 
            +
                      client.transmit("stats-job #{job_id}")
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
                  assert_equal other_job_id, client.transmit("stats-job #{other_job_id}")[:body]['id']
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
             | 
| 110 | 
            +
              context '::strategies' do
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                should 'return strategies from BeanCounter::Strategy' do
         | 
| 113 | 
            +
                  BeanCounter::Strategy.expects(:strategies).once.returns(:return_value)
         | 
| 114 | 
            +
                  assert_equal :return_value, BeanCounter.strategies
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
             | 
| 120 | 
            +
              context '::strategy=' do
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                should 'set instance variable when given valid strategy' do
         | 
| 123 | 
            +
                  original_strategy = BeanCounter.strategy.class
         | 
| 124 | 
            +
                  [
         | 
| 125 | 
            +
                    'BeanCounter::KnownStrategy',
         | 
| 126 | 
            +
                    :'BeanCounter::KnownStrategy',
         | 
| 127 | 
            +
                    BeanCounter::KnownStrategy
         | 
| 128 | 
            +
                  ].each do |good_strategy|
         | 
| 129 | 
            +
                    BeanCounter.strategy = BeanCounter::KnownStrategy
         | 
| 130 | 
            +
                    assert_kind_of BeanCounter::KnownStrategy, BeanCounter.strategy
         | 
| 131 | 
            +
                    BeanCounter.strategy = BeanCounter::Strategy
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                  BeanCounter.strategy = original_strategy
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
             | 
| 137 | 
            +
                should 'accept nil for strategy' do
         | 
| 138 | 
            +
                  original_strategy = BeanCounter.strategy.class
         | 
| 139 | 
            +
                  BeanCounter.strategy = nil
         | 
| 140 | 
            +
                  assert_nil BeanCounter.instance_variable_get(:@strategy)
         | 
| 141 | 
            +
                  BeanCounter.strategy = original_strategy
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
             | 
| 147 | 
            +
              context '::strategy' do
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                should 'default to BeanCounter.default_strategy' do
         | 
| 150 | 
            +
                  BeanCounter.strategy = nil
         | 
| 151 | 
            +
                  assert_kind_of BeanCounter.default_strategy, BeanCounter.strategy
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            end
         | 
| @@ -0,0 +1,223 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class EnqueuedExpectationTest < BeanCounter::TestCase
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              EnqueuedExpectation = BeanCounter::EnqueuedExpectation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context '#collection_matcher' do
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                should 'traverse collection with detect and return true if no count given' do
         | 
| 10 | 
            +
                  expectation = EnqueuedExpectation.new({})
         | 
| 11 | 
            +
                  collection = [:job]
         | 
| 12 | 
            +
                  collection.expects(:detect).once.returns(:job)
         | 
| 13 | 
            +
                  expectation.strategy.expects(:jobs).returns(collection)
         | 
| 14 | 
            +
                  assert expectation.matches?
         | 
| 15 | 
            +
                  assert_equal collection, expectation.found
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                should 'traverse collection with select and return true if count matches' do
         | 
| 20 | 
            +
                  expectation = EnqueuedExpectation.new({:count => 1})
         | 
| 21 | 
            +
                  collection = [:job]
         | 
| 22 | 
            +
                  collection.expects(:select).once.returns(collection)
         | 
| 23 | 
            +
                  expectation.strategy.expects(:jobs).returns(collection)
         | 
| 24 | 
            +
                  assert expectation.matches?
         | 
| 25 | 
            +
                  assert_equal collection, expectation.found
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
             | 
| 29 | 
            +
                should 'return false and set found if strategy does not include match' do
         | 
| 30 | 
            +
                  expectation = EnqueuedExpectation.new({})
         | 
| 31 | 
            +
                  expectation.strategy.expects(:jobs).returns([:wrong_job])
         | 
| 32 | 
            +
                  expectation.strategy.expects(:job_matches?).returns(false)
         | 
| 33 | 
            +
                  refute expectation.matches?
         | 
| 34 | 
            +
                  assert_nil expectation.found
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
             | 
| 38 | 
            +
                should 'return false and set found if strategy does not include expected number of matches' do
         | 
| 39 | 
            +
                  collection = [:job, :job, :job]
         | 
| 40 | 
            +
                  [{:count => 0}, {:count => 1}, {:count => 2}, {:count => 1..2}].each do |expected|
         | 
| 41 | 
            +
                    expectation = EnqueuedExpectation.new(expected)
         | 
| 42 | 
            +
                    expectation.strategy.expects(:jobs).returns(collection)
         | 
| 43 | 
            +
                    expectation.strategy.expects(:job_matches?).times(3).returns(true)
         | 
| 44 | 
            +
                    refute expectation.matches?
         | 
| 45 | 
            +
                    assert_equal collection, expectation.found
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
             | 
| 52 | 
            +
              context '#expected_count?' do
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                should 'return true if a certain number of matches expected' do
         | 
| 55 | 
            +
                  expectation = EnqueuedExpectation.new(:count => 5)
         | 
| 56 | 
            +
                  assert expectation.expected_count?
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
             | 
| 60 | 
            +
                should 'return false if any number of matches expected' do
         | 
| 61 | 
            +
                  expectation = EnqueuedExpectation.new({})
         | 
| 62 | 
            +
                  refute expectation.expected_count?
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
             | 
| 68 | 
            +
              context '#failure message' do
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                should 'return expected message if nothing found and no count' do
         | 
| 71 | 
            +
                  expected = {}
         | 
| 72 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 73 | 
            +
                  expected.expects(:to_s).returns('expected')
         | 
| 74 | 
            +
                  expected = 'expected any number of jobs matching expected, found none'
         | 
| 75 | 
            +
                  assert_equal expected, expectation.failure_message
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
             | 
| 79 | 
            +
                should 'return expected message if nothing found and count given' do
         | 
| 80 | 
            +
                  expected = {:count => 3}
         | 
| 81 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 82 | 
            +
                  expected.expects(:to_s).returns('expected')
         | 
| 83 | 
            +
                  expected = 'expected 3 jobs matching expected, found none'
         | 
| 84 | 
            +
                  assert_equal expected, expectation.failure_message
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
             | 
| 88 | 
            +
                should 'return expected message if number found does not match count given' do
         | 
| 89 | 
            +
                  expected = {:count => 3}
         | 
| 90 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 91 | 
            +
                  expectation.strategy.jobs.expects(:select).returns([:job, :job])
         | 
| 92 | 
            +
                  expectation.matches?
         | 
| 93 | 
            +
                  expectation.strategy.expects(:pretty_print_job).twice.returns('job')
         | 
| 94 | 
            +
                  expected.expects(:to_s).returns('expected')
         | 
| 95 | 
            +
                  expected = "expected 3 jobs matching expected, found 2: job\njob"
         | 
| 96 | 
            +
                  assert_equal expected, expectation.failure_message
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
             | 
| 102 | 
            +
              context '#new' do
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                should 'set expected correctly' do
         | 
| 105 | 
            +
                  expected = {}
         | 
| 106 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 107 | 
            +
                  assert_equal expectation.expected, expected
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
             | 
| 111 | 
            +
                should 'pull out count and set expected_count correctly' do
         | 
| 112 | 
            +
                  expected = {:count => 1}
         | 
| 113 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 114 | 
            +
                  assert_equal({}, expectation.expected)
         | 
| 115 | 
            +
                  assert_equal 1, expectation.expected_count
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  expected = {'count' => 2}
         | 
| 118 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 119 | 
            +
                  assert_equal({}, expectation.expected)
         | 
| 120 | 
            +
                  assert_equal 2, expectation.expected_count
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  expected = {:count => 3, 'count' => 4}
         | 
| 123 | 
            +
                  expectation = EnqueuedExpectation.new(expected)
         | 
| 124 | 
            +
                  assert_equal({}, expectation.expected)
         | 
| 125 | 
            +
                  assert_equal 3, expectation.expected_count
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
             | 
| 131 | 
            +
              context '#matches?' do
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                setup do
         | 
| 134 | 
            +
                  @expectation = EnqueuedExpectation.new({})
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
             | 
| 138 | 
            +
                should 'call #proc_matcher when given Proc' do
         | 
| 139 | 
            +
                  lamb = lambda {}
         | 
| 140 | 
            +
                  prok = proc {}
         | 
| 141 | 
            +
                  proc_new = Proc.new {}
         | 
| 142 | 
            +
                  @expectation.expects(:proc_matcher).times(3)
         | 
| 143 | 
            +
                  @expectation.matches?(lamb)
         | 
| 144 | 
            +
                  @expectation.matches?(prok)
         | 
| 145 | 
            +
                  @expectation.matches?(proc_new)
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
             | 
| 149 | 
            +
                should 'call #collection_macther with strategy.jobs when not given proc' do
         | 
| 150 | 
            +
                  @expectation.expects(:collection_matcher)
         | 
| 151 | 
            +
                  BeanCounter.strategy.expects(:jobs).returns([])
         | 
| 152 | 
            +
                  @expectation.matches?
         | 
| 153 | 
            +
                end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
             | 
| 158 | 
            +
              context '#negative_failure_message' do
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                should 'return empty string if nothing found' do
         | 
| 161 | 
            +
                  expectation = EnqueuedExpectation.new({})
         | 
| 162 | 
            +
                  expectation.strategy.expects(:jobs).returns([])
         | 
| 163 | 
            +
                  refute expectation.matches?
         | 
| 164 | 
            +
                  assert_equal '', expectation.negative_failure_message
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
             | 
| 168 | 
            +
                should 'return expected message if matching job found and no count given' do
         | 
| 169 | 
            +
                  expectation = EnqueuedExpectation.new({})
         | 
| 170 | 
            +
                  expectation.strategy.jobs.expects(:detect).returns(:job)
         | 
| 171 | 
            +
                  expectation.strategy.expects(:pretty_print_job).returns('job')
         | 
| 172 | 
            +
                  expectation.expected.expects(:to_s).returns('expected')
         | 
| 173 | 
            +
                  assert expectation.matches?
         | 
| 174 | 
            +
                  expected = 'did not expect any jobs matching expected, found 1: job'
         | 
| 175 | 
            +
                  assert_equal expected, expectation.negative_failure_message
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
             | 
| 179 | 
            +
                should 'return expected message if matching job found and count of 1 given' do
         | 
| 180 | 
            +
                  expectation = EnqueuedExpectation.new({:count => 1})
         | 
| 181 | 
            +
                  expectation.strategy.jobs.expects(:select).returns([:job])
         | 
| 182 | 
            +
                  expectation.strategy.expects(:pretty_print_job).returns('job')
         | 
| 183 | 
            +
                  expectation.expected.expects(:to_s).returns('expected')
         | 
| 184 | 
            +
                  assert expectation.matches?
         | 
| 185 | 
            +
                  expected = 'did not expect 1 job matching expected, found 1: job'
         | 
| 186 | 
            +
                  assert_equal expected, expectation.negative_failure_message
         | 
| 187 | 
            +
                end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
             | 
| 190 | 
            +
                should 'return expected message if matching jobs found and count not equal 1' do
         | 
| 191 | 
            +
                  expectation = EnqueuedExpectation.new({:count => 2})
         | 
| 192 | 
            +
                  expectation.strategy.jobs.expects(:select).returns([:job, :job])
         | 
| 193 | 
            +
                  expectation.strategy.expects(:pretty_print_job).twice.returns('job')
         | 
| 194 | 
            +
                  expectation.expected.expects(:to_s).returns('expected')
         | 
| 195 | 
            +
                  assert expectation.matches?
         | 
| 196 | 
            +
                  expected = "did not expect 2 jobs matching expected, found 2: job\njob"
         | 
| 197 | 
            +
                  assert_equal expected, expectation.negative_failure_message
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
              end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
              context '#proc_matcher' do
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                setup do
         | 
| 205 | 
            +
                  @prok = proc {}
         | 
| 206 | 
            +
                  @expectation = EnqueuedExpectation.new({})
         | 
| 207 | 
            +
                end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                should 'pass provided block to strategy#collect_new_jobs' do
         | 
| 210 | 
            +
                  @expectation.strategy.expects(:collect_new_jobs).returns([])
         | 
| 211 | 
            +
                  refute @expectation.matches?(@prok)
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
             | 
| 215 | 
            +
                should 'pass collected jobs to #collection_matcher' do
         | 
| 216 | 
            +
                  @expectation.strategy.expects(:collect_new_jobs).returns([:job])
         | 
| 217 | 
            +
                  @expectation.strategy.expects(:job_matches?).returns(true)
         | 
| 218 | 
            +
                  assert @expectation.matches?(@prok)
         | 
| 219 | 
            +
                end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
              end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
            end
         | 
| @@ -0,0 +1,501 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class StalkClimberStrategyTest < BeanCounter::TestCase
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              setup do
         | 
| 6 | 
            +
                @strategy = BeanCounter::Strategy::StalkClimberStrategy.new
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              context '#jobs' do
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                should 'implement #jobs' do
         | 
| 12 | 
            +
                  assert @strategy.respond_to?(:jobs)
         | 
| 13 | 
            +
                  assert_kind_of Enumerable, @strategy.jobs
         | 
| 14 | 
            +
                  begin
         | 
| 15 | 
            +
                    @strategy.jobs.each do
         | 
| 16 | 
            +
                      break
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  rescue NotImplementedError
         | 
| 19 | 
            +
                    raise 'Expected subclass of Strategy, BeanCounter::Strategy::StalkClimberStrategy, to provide #jobs enumerator'
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
              context '#collect_new_jobs' do
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                should 'raise ArgumentError unless block given' do
         | 
| 29 | 
            +
                  assert_raises(ArgumentError) do
         | 
| 30 | 
            +
                    @strategy.collect_new_jobs
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 35 | 
            +
                should 'return empty array if no new jobs enqueued' do
         | 
| 36 | 
            +
                  new_jobs = @strategy.collect_new_jobs {}
         | 
| 37 | 
            +
                  assert_equal [], new_jobs
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
                should 'return only jobs enqueued during block execution' do
         | 
| 42 | 
            +
                  @tube_name = SecureRandom.uuid
         | 
| 43 | 
            +
                  client.transmit("use #{@tube_name}")
         | 
| 44 | 
            +
                  client.transmit("watch #{@tube_name}")
         | 
| 45 | 
            +
                  client.transmit('ignore default')
         | 
| 46 | 
            +
                  message = SecureRandom.uuid
         | 
| 47 | 
            +
                  all_jobs = []
         | 
| 48 | 
            +
                  all_jobs << client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")[:id].to_i
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  jobs = []
         | 
| 51 | 
            +
                  new_jobs = @strategy.collect_new_jobs do
         | 
| 52 | 
            +
                    5.times do
         | 
| 53 | 
            +
                      message = SecureRandom.uuid
         | 
| 54 | 
            +
                      job_id = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")[:id].to_i
         | 
| 55 | 
            +
                      jobs << job_id
         | 
| 56 | 
            +
                      all_jobs << job_id
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  message = SecureRandom.uuid
         | 
| 61 | 
            +
                  all_jobs << client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")[:id].to_i
         | 
| 62 | 
            +
                  assert_equal new_jobs.map(&:id), jobs
         | 
| 63 | 
            +
                  all_jobs.each { |job_id| client.transmit("delete #{job_id}") }
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
             | 
| 69 | 
            +
              context '#delete_job' do
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                should 'delete the provided job and return true' do
         | 
| 72 | 
            +
                  message = SecureRandom.uuid
         | 
| 73 | 
            +
                  job = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
         | 
| 74 | 
            +
                  strategy_job = StalkClimber::Job.new(job)
         | 
| 75 | 
            +
                  assert @strategy.delete_job(strategy_job), 'Failed to delete job'
         | 
| 76 | 
            +
                  refute strategy_job.exists?
         | 
| 77 | 
            +
                  assert_raises(Beaneater::NotFoundError) do
         | 
| 78 | 
            +
                    client.transmit("stats-job #{job[:id]}")
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
                should 'not complain and return true if job already deleted' do
         | 
| 84 | 
            +
                  message = SecureRandom.uuid
         | 
| 85 | 
            +
                  job = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
         | 
| 86 | 
            +
                  strategy_job = StalkClimber::Job.new(job)
         | 
| 87 | 
            +
                  client.transmit("delete #{job[:id]}")
         | 
| 88 | 
            +
                  assert @strategy.delete_job(strategy_job), 'Expected true return value after attempt to delete deleted job'
         | 
| 89 | 
            +
                  refute strategy_job.exists?
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
             | 
| 93 | 
            +
                should 'return false if the job could not be deleted' do
         | 
| 94 | 
            +
                  tube_name = SecureRandom.uuid
         | 
| 95 | 
            +
                  client.transmit("use #{tube_name}")
         | 
| 96 | 
            +
                  client.transmit("watch #{tube_name}")
         | 
| 97 | 
            +
                  client.transmit('ignore default')
         | 
| 98 | 
            +
                  message = SecureRandom.uuid
         | 
| 99 | 
            +
                  job = client.transmit("put 0 0 120 #{message.bytesize}\r\n#{message}")
         | 
| 100 | 
            +
                  strategy_job = @strategy.jobs.detect{|strat_job| strat_job.body == message}
         | 
| 101 | 
            +
                  reserved_job = client.transmit('reserve')
         | 
| 102 | 
            +
                  assert_equal strategy_job.id, reserved_job[:id].to_i
         | 
| 103 | 
            +
                  refute @strategy.delete_job(strategy_job), 'Expected false return value after failed attempt to delete reserved job'
         | 
| 104 | 
            +
                  assert strategy_job.exists?
         | 
| 105 | 
            +
                  client.transmit("delete #{job[:id]}")
         | 
| 106 | 
            +
                  refute strategy_job.exists?
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              context '#job_matches?' do
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                setup do
         | 
| 114 | 
            +
                  @tube_name = SecureRandom.uuid
         | 
| 115 | 
            +
                  @message = SecureRandom.uuid
         | 
| 116 | 
            +
                  client.transmit("use #{@tube_name}")
         | 
| 117 | 
            +
                  client.transmit("watch #{@tube_name}")
         | 
| 118 | 
            +
                  client.transmit('ignore default')
         | 
| 119 | 
            +
                  @job = client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
         | 
| 120 | 
            +
                  @strategy_job = StalkClimber::Job.new(@job)
         | 
| 121 | 
            +
                  # Update job state so everything is cached
         | 
| 122 | 
            +
                  @strategy_job.age
         | 
| 123 | 
            +
                  @strategy_job.body
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
             | 
| 127 | 
            +
                teardown do
         | 
| 128 | 
            +
                  begin
         | 
| 129 | 
            +
                    client.transmit("delete #{@job[:id]}")
         | 
| 130 | 
            +
                  rescue Beaneater::NotFoundError
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
             | 
| 135 | 
            +
                should 'match string fields using string or regex' do
         | 
| 136 | 
            +
                  job_stats = client.transmit("stats-job #{@job[:id]}")[:body]
         | 
| 137 | 
            +
                  {
         | 
| 138 | 
            +
                    :body => @message,
         | 
| 139 | 
            +
                    :state => job_stats['state'],
         | 
| 140 | 
            +
                    :tube => @tube_name,
         | 
| 141 | 
            +
                  }.each_pair do |key, value|
         | 
| 142 | 
            +
                    assert @strategy.job_matches?(@strategy_job, key => value)
         | 
| 143 | 
            +
                    assert @strategy.job_matches?(@strategy_job, key => %r[#{value[2, 3]}])
         | 
| 144 | 
            +
                    refute @strategy.job_matches?(@strategy_job, key => 'foobar')
         | 
| 145 | 
            +
                    refute @strategy.job_matches?(@strategy_job, key => /foobar/)
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
             | 
| 150 | 
            +
                should 'match integer fields using integer or range' do
         | 
| 151 | 
            +
                  job_stats = client.transmit("stats-job #{@job[:id]}")[:body]
         | 
| 152 | 
            +
                  verify_attrs(@strategy_job, {
         | 
| 153 | 
            +
                    :age => job_stats['age'],
         | 
| 154 | 
            +
                    :buries => job_stats['buries'],
         | 
| 155 | 
            +
                    :delay => job_stats['delay'],
         | 
| 156 | 
            +
                    :id => @job[:id].to_i,
         | 
| 157 | 
            +
                    :kicks => job_stats['kicks'],
         | 
| 158 | 
            +
                    :pri => job_stats['pri'],
         | 
| 159 | 
            +
                    :releases => job_stats['releases'],
         | 
| 160 | 
            +
                    :reserves => job_stats['reserves'],
         | 
| 161 | 
            +
                    :'time-left' => job_stats['time-left'],
         | 
| 162 | 
            +
                    :timeouts => job_stats['timeouts'],
         | 
| 163 | 
            +
                    :ttr => job_stats['ttr'],
         | 
| 164 | 
            +
                  })
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
             | 
| 168 | 
            +
                should 'match integer fields using integer or range (with more stubs)' do
         | 
| 169 | 
            +
                  job_attrs = {
         | 
| 170 | 
            +
                    :age => 100,
         | 
| 171 | 
            +
                    :buries => 101,
         | 
| 172 | 
            +
                    :delay => 102,
         | 
| 173 | 
            +
                    :id => 12345,
         | 
| 174 | 
            +
                    :kicks => 103,
         | 
| 175 | 
            +
                    :pri => 104,
         | 
| 176 | 
            +
                    :releases => 105,
         | 
| 177 | 
            +
                    :reserves => 106,
         | 
| 178 | 
            +
                    :'time-left' => 107,
         | 
| 179 | 
            +
                    :timeouts => 108,
         | 
| 180 | 
            +
                    :ttr => 109,
         | 
| 181 | 
            +
                  }
         | 
| 182 | 
            +
                  job_attrs.each_pair do |key, value|
         | 
| 183 | 
            +
                    sanitized_key = @strategy.send(:stats_method_name, key)
         | 
| 184 | 
            +
                    @strategy_job.expects(sanitized_key).times(8).returns(value)
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
                  @strategy_job.expects(:exists?).times(job_attrs.length * 4).returns(true)
         | 
| 187 | 
            +
                  verify_attrs(@strategy_job, job_attrs)
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
             | 
| 191 | 
            +
                should 'be able to match with a proc' do
         | 
| 192 | 
            +
                  matching_connection_proc = proc do |connection|
         | 
| 193 | 
            +
                    connection == client
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
                  assert @strategy.job_matches?(@strategy_job, :connection => matching_connection_proc)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  failing_connection_proc = proc do |connection|
         | 
| 198 | 
            +
                    connection != client
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
                  refute @strategy.job_matches?(@strategy_job, :connection => failing_connection_proc)
         | 
| 201 | 
            +
                end
         | 
| 202 | 
            +
             | 
| 203 | 
            +
             | 
| 204 | 
            +
                should 'not try to match on non-matachable attributes' do
         | 
| 205 | 
            +
                  # Called once to refresh job state before checking match
         | 
| 206 | 
            +
                  @strategy_job.expects(:exists?).once.returns(true)
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                  # Would be called but skipped because of expectation on exists?
         | 
| 209 | 
            +
                  @strategy_job.expects(:stats).never
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                  # Should never be called
         | 
| 212 | 
            +
                  @strategy_job.expects(:delete).never
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  assert @strategy.job_matches?(@strategy_job, {
         | 
| 215 | 
            +
                    :body => @message,
         | 
| 216 | 
            +
                    :delete => 'bar',
         | 
| 217 | 
            +
                    :exists? => 'baz',
         | 
| 218 | 
            +
                    :stats => 'boom',
         | 
| 219 | 
            +
                  })
         | 
| 220 | 
            +
                end
         | 
| 221 | 
            +
             | 
| 222 | 
            +
             | 
| 223 | 
            +
                should 'not match job deleted after it was cached' do
         | 
| 224 | 
            +
                  client.transmit("delete #{@job[:id]}")
         | 
| 225 | 
            +
                  refute @strategy.job_matches?(@strategy_job, :body => @message)
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
             | 
| 228 | 
            +
             | 
| 229 | 
            +
                should 'match against updated job stats' do
         | 
| 230 | 
            +
                  pri = 1024
         | 
| 231 | 
            +
                  delay = 2048
         | 
| 232 | 
            +
                  job = nil
         | 
| 233 | 
            +
                  timeout(1) do
         | 
| 234 | 
            +
                    job = client.transmit('reserve')
         | 
| 235 | 
            +
                  end
         | 
| 236 | 
            +
                  client.transmit("release #{job[:id]} #{pri} #{delay}")
         | 
| 237 | 
            +
                  assert @strategy.job_matches?(@strategy_job, {
         | 
| 238 | 
            +
                    :body => @message,
         | 
| 239 | 
            +
                    :delay => (delay - 2)..delay,
         | 
| 240 | 
            +
                    :pri => pri,
         | 
| 241 | 
            +
                    :state => 'delayed',
         | 
| 242 | 
            +
                  })
         | 
| 243 | 
            +
                  refute @strategy.job_matches?(@strategy_job, {
         | 
| 244 | 
            +
                    :body => @message,
         | 
| 245 | 
            +
                    :delay => 0,
         | 
| 246 | 
            +
                    :pri => 0,
         | 
| 247 | 
            +
                  })
         | 
| 248 | 
            +
                end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
              end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
             | 
| 253 | 
            +
              context '#pretty_print_job' do
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                should 'return expected text representation of job' do
         | 
| 256 | 
            +
                  job_body = SecureRandom.uuid
         | 
| 257 | 
            +
                  stats_body = {
         | 
| 258 | 
            +
                    'age' => age = 3,
         | 
| 259 | 
            +
                    'body' => job_body, # Will be ignored during job init
         | 
| 260 | 
            +
                    'buries' => buries = 0,
         | 
| 261 | 
            +
                    'delay' => delay = 0,
         | 
| 262 | 
            +
                    'id' => id = 4412,
         | 
| 263 | 
            +
                    'kicks' => kicks = 0,
         | 
| 264 | 
            +
                    'pri' => pri = 4294967295,
         | 
| 265 | 
            +
                    'releases' => releases = 0,
         | 
| 266 | 
            +
                    'reserves' => reserves = 0,
         | 
| 267 | 
            +
                    'state' => state = 'ready',
         | 
| 268 | 
            +
                    'time-left' => time_left = 0,
         | 
| 269 | 
            +
                    'timeouts' => timeouts = 0,
         | 
| 270 | 
            +
                    'ttr'  => ttr = 300,
         | 
| 271 | 
            +
                    'tube' => tube = 'default',
         | 
| 272 | 
            +
                  }
         | 
| 273 | 
            +
                  stats_response = {
         | 
| 274 | 
            +
                    :body => stats_body,
         | 
| 275 | 
            +
                    :connection => client,
         | 
| 276 | 
            +
                    :id => 149,
         | 
| 277 | 
            +
                    :status => 'OK',
         | 
| 278 | 
            +
                  }
         | 
| 279 | 
            +
                  job = StalkClimber::Job.new(stats_response)
         | 
| 280 | 
            +
                  job.instance_variable_set(:@body, job_body)
         | 
| 281 | 
            +
                  job.connection.expects(:transmit).once.returns(stats_response)
         | 
| 282 | 
            +
                  expected = %W[
         | 
| 283 | 
            +
                    "age"=>#{age} "body"=>"#{job_body}" "buries"=>#{buries} "connection"=>#{client.to_s}
         | 
| 284 | 
            +
                    "delay"=>#{delay} "id"=>#{id} "kicks"=>#{kicks} "pri"=>#{pri} "releases"=>#{releases}
         | 
| 285 | 
            +
                    "reserves"=>#{reserves} "state"=>"#{state}" "time-left"=>#{time_left}
         | 
| 286 | 
            +
                    "timeouts"=>#{timeouts} "ttr"=>#{ttr} "tube"=>"#{tube}"
         | 
| 287 | 
            +
                  ].join(', ')
         | 
| 288 | 
            +
                  assert_equal "{#{expected}}", @strategy.pretty_print_job(job)
         | 
| 289 | 
            +
                end
         | 
| 290 | 
            +
             | 
| 291 | 
            +
              end
         | 
| 292 | 
            +
             | 
| 293 | 
            +
             | 
| 294 | 
            +
              context '#pretty_print_tube' do
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                should 'return expected text representation of tube' do
         | 
| 297 | 
            +
                  tube_name = SecureRandom.uuid
         | 
| 298 | 
            +
                  stats_struct = Beaneater::StatStruct.from_hash({
         | 
| 299 | 
            +
                    'cmd-delete' => cmd_delete = 100,
         | 
| 300 | 
            +
                    'cmd-pause-tube' => cmd_pause_tube = 101,
         | 
| 301 | 
            +
                    'current-jobs-buried' => current_jobs_buried = 102,
         | 
| 302 | 
            +
                    'current-jobs-delayed' => current_jobs_delayed = 103,
         | 
| 303 | 
            +
                    'current-jobs-ready' => current_jobs_ready = 104,
         | 
| 304 | 
            +
                    'current-jobs-reserved' => current_jobs_reserved = 105,
         | 
| 305 | 
            +
                    'current-jobs-urgent' => current_jobs_urgent = 106,
         | 
| 306 | 
            +
                    'current-using' => current_using = 107,
         | 
| 307 | 
            +
                    'current-waiting' => current_waiting = 108,
         | 
| 308 | 
            +
                    'current-watching' => current_watching = 109,
         | 
| 309 | 
            +
                    'name' => name = tube_name,
         | 
| 310 | 
            +
                    'pause-time-left' => pause_time_left = 110,
         | 
| 311 | 
            +
                    'pause' => pause = 111,
         | 
| 312 | 
            +
                    'total-jobs' => total_jobs = 112,
         | 
| 313 | 
            +
                  })
         | 
| 314 | 
            +
                  connection_pool = @strategy.send(:climber).connection_pool
         | 
| 315 | 
            +
                  tube = StalkClimber::Tube.new(connection_pool, tube_name)
         | 
| 316 | 
            +
                  tube.stubs(:stats).returns(stats_struct)
         | 
| 317 | 
            +
                  expected = %W[
         | 
| 318 | 
            +
                    "cmd-delete"=>#{cmd_delete} "cmd-pause-tube"=>#{cmd_pause_tube}
         | 
| 319 | 
            +
                    "current-jobs-buried"=>#{current_jobs_buried}
         | 
| 320 | 
            +
                    "current-jobs-delayed"=>#{current_jobs_delayed}
         | 
| 321 | 
            +
                    "current-jobs-ready"=>#{current_jobs_ready}
         | 
| 322 | 
            +
                    "current-jobs-reserved"=>#{current_jobs_reserved}
         | 
| 323 | 
            +
                    "current-jobs-urgent"=>#{current_jobs_urgent}
         | 
| 324 | 
            +
                    "current-using"=>#{current_using} "current-waiting"=>#{current_waiting}
         | 
| 325 | 
            +
                    "current-watching"=>#{current_watching} "name"=>"#{name}"
         | 
| 326 | 
            +
                    "pause"=>#{pause} "pause-time-left"=>#{pause_time_left}
         | 
| 327 | 
            +
                    "total-jobs"=>#{total_jobs}
         | 
| 328 | 
            +
                  ].join(', ')
         | 
| 329 | 
            +
                  assert_equal "{#{expected}}", @strategy.pretty_print_tube(tube)
         | 
| 330 | 
            +
                end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
              end
         | 
| 333 | 
            +
             | 
| 334 | 
            +
             | 
| 335 | 
            +
              context 'tube_matches?' do
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                setup do
         | 
| 338 | 
            +
                  @tube_name = SecureRandom.uuid
         | 
| 339 | 
            +
                  @message = SecureRandom.uuid
         | 
| 340 | 
            +
                  client.transmit("watch #{@tube_name}")
         | 
| 341 | 
            +
                  @tube = StalkClimber::Tube.new(@strategy.send(:climber).connection_pool, @tube_name)
         | 
| 342 | 
            +
                end
         | 
| 343 | 
            +
             | 
| 344 | 
            +
             | 
| 345 | 
            +
                should 'match name using string or regex' do
         | 
| 346 | 
            +
                  assert @strategy.tube_matches?(@tube, :name => @tube_name)
         | 
| 347 | 
            +
                  assert @strategy.tube_matches?(@tube, :name => %r[#{@tube_name[2, 3]}])
         | 
| 348 | 
            +
                  refute @strategy.tube_matches?(@tube, :name => 'foobar')
         | 
| 349 | 
            +
                  refute @strategy.tube_matches?(@tube, :name => /foobar/)
         | 
| 350 | 
            +
                end
         | 
| 351 | 
            +
             | 
| 352 | 
            +
             | 
| 353 | 
            +
                should 'match integer fields using integer or range' do
         | 
| 354 | 
            +
                  tube_stats = client.transmit("stats-tube #{@tube_name}")[:body]
         | 
| 355 | 
            +
                  verify_attrs(@tube, {
         | 
| 356 | 
            +
                    'cmd-delete' => tube_stats['cmd-delete'],
         | 
| 357 | 
            +
                    'cmd-pause-tube' => tube_stats['cmd-pause-tube'],
         | 
| 358 | 
            +
                    'current-jobs-buried' => tube_stats['current-jobs-buried'],
         | 
| 359 | 
            +
                    'current-jobs-delayed' => tube_stats['current-jobs-delayed'],
         | 
| 360 | 
            +
                    'current-jobs-ready' => tube_stats['current-jobs-ready'],
         | 
| 361 | 
            +
                    'current-jobs-reserved' => tube_stats['current-jobs-reserved'],
         | 
| 362 | 
            +
                    'current-jobs-urgent' => tube_stats['current-jobs-urgent'],
         | 
| 363 | 
            +
                    'current-using' => tube_stats['current-using'],
         | 
| 364 | 
            +
                    'current-waiting' => tube_stats['current-waiting'],
         | 
| 365 | 
            +
                    'current-watching' => tube_stats['current-watching'],
         | 
| 366 | 
            +
                    'pause' => tube_stats['pause'],
         | 
| 367 | 
            +
                    'pause-time-left' => tube_stats['pause-time-left'],
         | 
| 368 | 
            +
                    'total-jobs' => tube_stats['total-jobs'],
         | 
| 369 | 
            +
                  })
         | 
| 370 | 
            +
                end
         | 
| 371 | 
            +
             | 
| 372 | 
            +
             | 
| 373 | 
            +
                should 'match integer fields using integer or range (with more stubs)' do
         | 
| 374 | 
            +
                  tube_attrs = {
         | 
| 375 | 
            +
                    'cmd-delete' => 100,
         | 
| 376 | 
            +
                    'cmd-pause-tube' => 101,
         | 
| 377 | 
            +
                    'current-jobs-buried' => 102,
         | 
| 378 | 
            +
                    'current-jobs-delayed' => 103,
         | 
| 379 | 
            +
                    'current-jobs-ready' => 104,
         | 
| 380 | 
            +
                    'current-jobs-reserved' => 105,
         | 
| 381 | 
            +
                    'current-jobs-urgent' => 106,
         | 
| 382 | 
            +
                    'current-using' => 107,
         | 
| 383 | 
            +
                    'current-waiting' => 108,
         | 
| 384 | 
            +
                    'current-watching' => 109,
         | 
| 385 | 
            +
                    'pause-time-left' => 110,
         | 
| 386 | 
            +
                    'pause' => 111,
         | 
| 387 | 
            +
                    'total-jobs' => 112,
         | 
| 388 | 
            +
                  }
         | 
| 389 | 
            +
                  tube_attrs.each_pair do |key, value|
         | 
| 390 | 
            +
                    sanitized_key = @strategy.send(:stats_method_name, key)
         | 
| 391 | 
            +
                    @tube.expects(sanitized_key).times(8).returns(value)
         | 
| 392 | 
            +
                  end
         | 
| 393 | 
            +
                  @tube.expects(:exists?).times(tube_attrs.length * 4).returns(true)
         | 
| 394 | 
            +
                  verify_attrs(@tube, tube_attrs)
         | 
| 395 | 
            +
                end
         | 
| 396 | 
            +
             | 
| 397 | 
            +
             | 
| 398 | 
            +
                should 'not try to match on non-matachable attributes' do
         | 
| 399 | 
            +
                  # Called once to refresh state before checking match
         | 
| 400 | 
            +
                  @tube.expects(:exists?).once.returns(true)
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                  # Attribute matcher on pause calls pause_time
         | 
| 403 | 
            +
                  @tube.expects(:pause_time).once.returns(5)
         | 
| 404 | 
            +
             | 
| 405 | 
            +
                  # Would be called but skipped because of expectation on exists?
         | 
| 406 | 
            +
                  @tube.expects(:connection).never
         | 
| 407 | 
            +
                  @tube.expects(:stats).never
         | 
| 408 | 
            +
             | 
| 409 | 
            +
                  # Should never be called
         | 
| 410 | 
            +
                  %w[clear delete kick pause peek put reserve].each do |method|
         | 
| 411 | 
            +
                    @tube.expects(method).never
         | 
| 412 | 
            +
                  end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                  assert @strategy.tube_matches?(@tube, {
         | 
| 415 | 
            +
                    :clear => true,
         | 
| 416 | 
            +
                    :connection => 'foo',
         | 
| 417 | 
            +
                    :delete => 'bar',
         | 
| 418 | 
            +
                    :exists? => 'baz',
         | 
| 419 | 
            +
                    :kick => true,
         | 
| 420 | 
            +
                    :name => @tube_name,
         | 
| 421 | 
            +
                    :pause => 5,
         | 
| 422 | 
            +
                    :peek => true,
         | 
| 423 | 
            +
                    :put => true,
         | 
| 424 | 
            +
                    :reserve => true,
         | 
| 425 | 
            +
                    :stats => 'boom',
         | 
| 426 | 
            +
                  })
         | 
| 427 | 
            +
                end
         | 
| 428 | 
            +
             | 
| 429 | 
            +
             | 
| 430 | 
            +
                should 'not match tube that no longer exists' do
         | 
| 431 | 
            +
                  client.transmit("ignore #{@tube_name}")
         | 
| 432 | 
            +
                  refute @tube.exists?
         | 
| 433 | 
            +
                  refute @strategy.tube_matches?(@tube)
         | 
| 434 | 
            +
                end
         | 
| 435 | 
            +
             | 
| 436 | 
            +
             | 
| 437 | 
            +
                should 'match against updated tube stats' do
         | 
| 438 | 
            +
                  assert @strategy.tube_matches?(@tube, 'current-using' => 0)
         | 
| 439 | 
            +
                  client.transmit("use #{@tube_name}")
         | 
| 440 | 
            +
                  refute @strategy.tube_matches?(@tube, 'current-using' => 0)
         | 
| 441 | 
            +
                  assert @strategy.tube_matches?(@tube, 'current-using' => 1)
         | 
| 442 | 
            +
                end
         | 
| 443 | 
            +
             | 
| 444 | 
            +
             | 
| 445 | 
            +
              end
         | 
| 446 | 
            +
             | 
| 447 | 
            +
             | 
| 448 | 
            +
              context 'tubes' do
         | 
| 449 | 
            +
             | 
| 450 | 
            +
                should 'implement #tubes' do
         | 
| 451 | 
            +
                  assert @strategy.respond_to?(:tubes)
         | 
| 452 | 
            +
                  assert_kind_of Enumerable, @strategy.tubes
         | 
| 453 | 
            +
                  begin
         | 
| 454 | 
            +
                    @strategy.tubes.each do
         | 
| 455 | 
            +
                      break
         | 
| 456 | 
            +
                    end
         | 
| 457 | 
            +
                  rescue NotImplementedError
         | 
| 458 | 
            +
                    raise 'Expected subclass of Strategy, BeanCounter::Strategy::StalkClimberStrategy, to provide #tubes enumerator'
         | 
| 459 | 
            +
                  end
         | 
| 460 | 
            +
                end
         | 
| 461 | 
            +
             | 
| 462 | 
            +
              end
         | 
| 463 | 
            +
             | 
| 464 | 
            +
             | 
| 465 | 
            +
              context '#test_tube' do
         | 
| 466 | 
            +
             | 
| 467 | 
            +
                should 'return default test tube unless set otherwise' do
         | 
| 468 | 
            +
                  assert_equal BeanCounter::Strategy::StalkClimberStrategy::TEST_TUBE, @strategy.send(:test_tube)
         | 
| 469 | 
            +
                  @strategy.test_tube = new_tube = 'bean_counter_stalk_climber_test_new'
         | 
| 470 | 
            +
                  assert_equal new_tube, @strategy.send(:test_tube)
         | 
| 471 | 
            +
                  @strategy.test_tube = nil
         | 
| 472 | 
            +
                  assert_equal BeanCounter::Strategy::StalkClimberStrategy::TEST_TUBE, @strategy.send(:test_tube)
         | 
| 473 | 
            +
                end
         | 
| 474 | 
            +
             | 
| 475 | 
            +
              end
         | 
| 476 | 
            +
             | 
| 477 | 
            +
             | 
| 478 | 
            +
              def verify_attrs(strategy_object, attrs)
         | 
| 479 | 
            +
                method = strategy_object.is_a?(StalkClimber::Job) ? :job_matches? : :tube_matches?
         | 
| 480 | 
            +
                attrs.each_pair do |key, value|
         | 
| 481 | 
            +
                  sanitized_key = @strategy.send(:stats_method_name, key)
         | 
| 482 | 
            +
                  assert(
         | 
| 483 | 
            +
                    @strategy.send(method, strategy_object, key => value),
         | 
| 484 | 
            +
                    "Expected #{key} (#{strategy_object.send(sanitized_key)}) to match #{value}"
         | 
| 485 | 
            +
                  )
         | 
| 486 | 
            +
                  assert(
         | 
| 487 | 
            +
                    @strategy.send(method, strategy_object, key => (value - 5)..(value + 1)),
         | 
| 488 | 
            +
                    "Expected #{key} (#{strategy_object.send(sanitized_key)}) to match #{value} +/-5"
         | 
| 489 | 
            +
                  )
         | 
| 490 | 
            +
                  refute(
         | 
| 491 | 
            +
                    @strategy.send(method, strategy_object, key => value - 1),
         | 
| 492 | 
            +
                    "Expected #{key} (#{strategy_object.send(sanitized_key)}) to not match #{value}"
         | 
| 493 | 
            +
                  )
         | 
| 494 | 
            +
                  refute(
         | 
| 495 | 
            +
                    @strategy.send(method, strategy_object, key => (value + 100)..(value + 200)),
         | 
| 496 | 
            +
                    "Expected #{key} (#{strategy_object.send(sanitized_key)}) to not match #{value + 100}..#{value + 200}"
         | 
| 497 | 
            +
                  )
         | 
| 498 | 
            +
                end
         | 
| 499 | 
            +
              end
         | 
| 500 | 
            +
             | 
| 501 | 
            +
            end
         |