beaneater 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.
data/test/pool_test.rb ADDED
@@ -0,0 +1,154 @@
1
+ # test/connection_test.rb
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe Beaneater::Pool do
6
+
7
+ before do
8
+ @hosts = ['localhost', 'localhost']
9
+ @bp = Beaneater::Pool.new(@hosts)
10
+ end
11
+
12
+ describe 'for #new' do
13
+
14
+ describe "for multiple connection" do
15
+ before do
16
+ @host_string_port = 'localhost:11301'
17
+ @host_num_port = '127.0.0.1:11302'
18
+ @host_string = 'host.local'
19
+ @host_num = '1.1.1.1:11303'
20
+ @hosts = [@host_string_port, @host_num_port, @host_string, @host_num]
21
+
22
+ Net::Telnet.expects(:new).with('Host' => 'localhost', "Port" => 11301, "Prompt" => /\n/).once
23
+ Net::Telnet.expects(:new).with('Host' => '127.0.0.1', "Port" => 11302, "Prompt" => /\n/).once
24
+ Net::Telnet.expects(:new).with('Host' => 'host.local', "Port" => 11300, "Prompt" => /\n/).once
25
+ Net::Telnet.expects(:new).with('Host' => '1.1.1.1', "Port" => 11303, "Prompt" => /\n/).once
26
+
27
+ @bp = Beaneater::Pool.new(@hosts)
28
+ end
29
+
30
+ it "should init 4 connections" do
31
+ assert_equal 4, @bp.connections.size
32
+ end
33
+ end
34
+
35
+ describe "for invalid connection in setup" do
36
+ it "should raise NotConnected" do
37
+ assert_raises(Beaneater::NotConnected) { Beaneater::Pool.new('localhost:5679') }
38
+ end
39
+ end
40
+
41
+ describe "for clearing watch list" do
42
+ it "should clear connections of tube watches" do
43
+ @bp.tubes.watch!('foo', 'bar')
44
+ assert_equal ['foo', 'bar'].sort, @bp.tubes.watched.map(&:name).sort
45
+ @bp2 = Beaneater::Pool.new(@hosts)
46
+ assert_equal ['default'], @bp2.tubes.watched.map(&:name)
47
+ end
48
+ end
49
+
50
+ describe "with ENV variable set" do
51
+ before do
52
+ ENV['BEANSTALKD_URL'] = '0.0.0.0:11300,127.0.0.1:11300'
53
+ end
54
+
55
+ it "should create 1 connection" do
56
+ bp = Beaneater::Pool.new
57
+ bc = bp.connections.first
58
+ bc2 = bp.connections.last
59
+
60
+ assert_equal 2, bp.connections.size
61
+ assert_equal '0.0.0.0', bc.host
62
+ assert_equal 11300, bc.port
63
+
64
+ assert_equal '127.0.0.1', bc2.host
65
+ assert_equal 11300, bc2.port
66
+ end
67
+ end
68
+ end # new
69
+
70
+ describe 'for #transmit_to_all' do
71
+ it "should return yaml loaded response" do
72
+ res = @bp.transmit_to_all 'stats'
73
+ assert_equal 2, res.size
74
+ refute_nil res.first[:body]['current-connections']
75
+ end
76
+ end # transmit_to_all
77
+
78
+ describe 'for #transmit_to_rand' do
79
+ it "should return yaml loaded response" do
80
+ res = @bp.transmit_to_rand 'stats'
81
+ refute_nil res[:body]['current-connections']
82
+ assert_equal 'OK', res[:status]
83
+ end
84
+
85
+ it "should return id" do
86
+ Net::Telnet.any_instance.expects(:cmd).with(has_entries('String' => 'foo')).returns('INSERTED 254')
87
+ res = @bp.transmit_to_rand 'foo'
88
+ assert_equal '254', res[:id]
89
+ assert_equal 'INSERTED', res[:status]
90
+ end
91
+ end # transmit_to_rand
92
+
93
+ describe 'for #transmit_until_res' do
94
+ before do
95
+ Beaneater::Connection.any_instance.expects(:transmit).with('foo', {}).twice.
96
+ returns({:status => "FAILED", :body => 'x'}).then.
97
+ returns({:status => "OK", :body => 'y'}).then.returns({:status => "OK", :body => 'z'})
98
+ end
99
+
100
+ it "should returns first matching status" do
101
+ assert_equal 'y', @bp.transmit_until_res('foo', :status => 'OK')[:body]
102
+ end
103
+ end # transmit_until_res
104
+
105
+ describe 'for #stats' do
106
+ it("should return stats object"){ assert_kind_of Beaneater::Stats, @bp.stats }
107
+ end # stats
108
+
109
+ describe 'for #tubes' do
110
+ it("should return Tubes object"){ assert_kind_of Beaneater::Tubes, @bp.tubes }
111
+ end # tubes
112
+
113
+ describe "for #safe_transmit" do
114
+ it "should retry 3 times for temporary failed connection" do
115
+ Net::Telnet.any_instance.expects(:cmd).raises(EOFError).then.
116
+ raises(Errno::ECONNRESET).then.returns('INSERTED 254').times(3)
117
+ res = @bp.transmit_to_rand 'foo'
118
+ assert_equal '254', res[:id]
119
+ assert_equal 'INSERTED', res[:status]
120
+ end
121
+
122
+ it "should retry on fail 3 times for dead connection" do
123
+ Net::Telnet.any_instance.expects(:cmd).raises(EOFError).times(3)
124
+ assert_raises(Beaneater::NotConnected) { @bp.transmit_to_rand 'foo' }
125
+ end
126
+
127
+ it 'should raise proper exception for invalid status NOT_FOUND' do
128
+ Net::Telnet.any_instance.expects(:cmd).returns('NOT_FOUND')
129
+ assert_raises(Beaneater::NotFoundError) { @bp.transmit_to_rand 'foo' }
130
+ end
131
+
132
+ it 'should raise proper exception for invalid status BAD_FORMAT' do
133
+ Net::Telnet.any_instance.expects(:cmd).returns('BAD_FORMAT')
134
+ assert_raises(Beaneater::BadFormatError) { @bp.transmit_to_rand 'foo' }
135
+ end
136
+
137
+ it 'should raise proper exception for invalid status DEADLINE_SOON' do
138
+ Net::Telnet.any_instance.expects(:cmd).returns('DEADLINE_SOON')
139
+ assert_raises(Beaneater::DeadlineSoonError) { @bp.transmit_to_rand 'foo' }
140
+ end
141
+ end
142
+
143
+ describe "for #close" do
144
+ it "should support closing the pool" do
145
+ connection = @bp.connections.first
146
+ assert_equal 2, @bp.connections.size
147
+ assert_kind_of Beaneater::Connection, connection
148
+ assert_kind_of Net::Telnet, connection.telnet_connection
149
+ @bp.close
150
+ assert_equal 0, @bp.connections.size
151
+ assert_nil connection.telnet_connection
152
+ end
153
+ end # close
154
+ end # Beaneater::Pool
@@ -0,0 +1,41 @@
1
+ # test/connection_test.rb
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe Beaneater::StatStruct do
6
+ before do
7
+ @hash = { :foo => "bar", :bar => "baz", :baz => "foo", :"under-score" => "demo" }
8
+ @struct = Beaneater::StatStruct.from_hash(@hash)
9
+ end
10
+
11
+ describe "for #from_hash" do
12
+ it "should have 4 keys" do
13
+ assert_equal 'bar', @struct.foo
14
+ assert_equal 'baz', @struct.bar
15
+ assert_equal 'foo', @struct.baz
16
+ assert_equal 'demo', @struct.under_score
17
+ end
18
+ end # from_hash
19
+
20
+ describe "for [] access" do
21
+ it "should have hash lookup" do
22
+ assert_equal 'bar', @struct['foo']
23
+ assert_equal 'baz', @struct['bar']
24
+ end
25
+
26
+ it "should convert keys to string" do
27
+ assert_equal 'foo', @struct[:baz]
28
+ assert_equal 'demo', @struct[:"under_score"]
29
+ end
30
+ end # []
31
+
32
+ describe "for #keys" do
33
+ it "should return 4 keys" do
34
+ assert_equal 4, @struct.keys.size
35
+ end
36
+
37
+ it "should return expected keys" do
38
+ assert_equal ['foo', 'bar', 'baz', 'under_score'], @struct.keys
39
+ end
40
+ end # keys
41
+ end
@@ -0,0 +1,42 @@
1
+ # test/connection_test.rb
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe Beaneater::Stats do
6
+ before do
7
+ @pool = stub(:transmit_to_all => [{ :body => { 'uptime' => 1, 'cmd-use' => 2 }}, {:body => { 'uptime' => 3,'cmd-use' => 4 }}])
8
+ @stats = Beaneater::Stats.new(@pool)
9
+ end
10
+
11
+ describe 'for #[]' do
12
+ it "should return stats by key" do
13
+ assert_equal 4, @stats[:uptime]
14
+ end
15
+
16
+ it "should return stats by underscore key" do
17
+ assert_equal 6, @stats[:'cmd_use']
18
+ end
19
+ end # []
20
+
21
+ describe 'for #keys' do
22
+ it "should return list of keys" do
23
+ assert_equal 2, @stats.keys.size
24
+ assert @stats.keys.include?('uptime'), "Expected keys to include 'uptime'"
25
+ assert @stats.keys.include?('cmd_use'), "Expected keys to include 'cmd-use'"
26
+ end
27
+ end # keys
28
+
29
+ describe 'for #method_missing' do
30
+ it "should return stats by key" do
31
+ assert_equal 4, @stats.uptime
32
+ end
33
+
34
+ it "should return stats by underscore key" do
35
+ assert_equal 6, @stats.cmd_use
36
+ end
37
+
38
+ it "should raise NoMethodError" do
39
+ assert_raises(NoMethodError) { @stats.cmd }
40
+ end
41
+ end # method_missing
42
+ end # Beaneater::Stats
@@ -0,0 +1,21 @@
1
+ ENV["TEST"] = 'true'
2
+ require 'rubygems'
3
+ require 'minitest/autorun'
4
+ $:.unshift File.expand_path("../../lib")
5
+ require 'beaneater'
6
+ require 'fakeweb'
7
+ require 'mocha'
8
+
9
+ FakeWeb.allow_net_connect = false
10
+
11
+ class MiniTest::Unit::TestCase
12
+
13
+ # Cleans up all jobs from tubes
14
+ # cleanup_tubes!(['foo'], @bp)
15
+ def cleanup_tubes!(tubes, bp=nil)
16
+ bp ||= @pool
17
+ tubes.each do |name|
18
+ bp.tubes.find(name).clear
19
+ end
20
+ end
21
+ end
data/test/tube_test.rb ADDED
@@ -0,0 +1,164 @@
1
+ # test/connection_test.rb
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe Beaneater::Tube do
6
+ before do
7
+ @pool = Beaneater::Pool.new(['localhost'])
8
+ @tube = Beaneater::Tube.new(@pool, 'baz')
9
+ end
10
+
11
+ describe "for #put" do
12
+ before do
13
+ @time = Time.now.to_i
14
+ end
15
+
16
+ it "should insert a job" do
17
+ @tube.put "bar put #{@time}"
18
+ assert_equal "bar put #{@time}", @tube.peek(:ready).body
19
+ end
20
+
21
+ it "should insert a delayed job" do
22
+ @tube.put "delayed put #{@time}", :delay => 1
23
+ assert_equal "delayed put #{@time}", @tube.peek(:delayed).body
24
+ end
25
+
26
+ it "should try to put 2 times before put successfully" do
27
+ Beaneater::Connection.any_instance.expects(:transmit).once.with(includes('use baz'), {})
28
+ Beaneater::Connection.any_instance.expects(:transmit).times(2).with(includes("bar put #{@time}"), {}).
29
+ raises(Beaneater::DrainingError.new(nil, nil)).then.returns('foo')
30
+ assert_equal 'foo', @tube.put("bar put #{@time}")
31
+ end
32
+
33
+ it "should try to put 3 times before to raise" do
34
+ Beaneater::Connection.any_instance.expects(:transmit).once.with(includes('use baz'), {})
35
+ Beaneater::Connection.any_instance.expects(:transmit).with(includes("bar put #{@time}"), {}).
36
+ times(3).raises(Beaneater::DrainingError.new(nil, nil))
37
+ assert_raises(Beaneater::DrainingError) { @tube.put "bar put #{@time}" }
38
+ end
39
+
40
+ it "supports JSON" do
41
+ json = "{ 'foo' : 'bar' }"
42
+ @tube.put(json)
43
+ assert_equal 'bar', @tube.peek(:ready).body['foo']
44
+ end
45
+
46
+ after do
47
+ Beaneater::Connection.any_instance.unstub(:transmit)
48
+ end
49
+ end # put
50
+
51
+ describe "for #peek" do
52
+ before do
53
+ @time = Time.now.to_i
54
+ end
55
+
56
+ it "should peek delayed" do
57
+ @tube.put "foo delay #{@time}", :delay => 1
58
+ assert_equal "foo delay #{@time}", @tube.peek(:delayed).body
59
+ end
60
+
61
+ it "should peek ready" do
62
+ @tube.put "foo ready #{@time}", :delay => 0
63
+ assert_equal "foo ready #{@time}", @tube.peek(:ready).body
64
+ end
65
+
66
+ it "should peek buried" do
67
+ @tube.put "foo buried #{@time}"
68
+ @tube.reserve.bury
69
+
70
+ assert_equal "foo buried #{@time}", @tube.peek(:buried).body
71
+ end
72
+
73
+ it "should return nil for empty peek" do
74
+ assert_nil @tube.peek(:buried)
75
+ end
76
+ end # peek
77
+
78
+ describe "for #reserve" do
79
+ before do
80
+ @time = Time.now.to_i
81
+ @tube.put "foo reserve #{@time}"
82
+ end
83
+
84
+ it "should reserve job" do
85
+ assert_equal "foo reserve #{@time}", @tube.reserve.body
86
+ end
87
+
88
+ it "should reserve job with block" do
89
+ job = nil
90
+ @tube.reserve { |j| job = j; job.delete }
91
+ assert_equal "foo reserve #{@time}", job.body
92
+ end
93
+ end # reserve
94
+
95
+ describe "for #pause" do
96
+ before do
97
+ @time = Time.now.to_i
98
+ @tube = Beaneater::Tube.new(@pool, 'bam')
99
+ @tube.put "foo pause #{@time}"
100
+ end
101
+
102
+ it "should allow tube pause" do
103
+ assert_equal 0, @tube.stats.pause
104
+ @tube.pause(1)
105
+ assert_equal 1, @tube.stats.pause
106
+ end
107
+ end # pause
108
+
109
+ describe "for #stats" do
110
+ before do
111
+ @time = Time.now.to_i
112
+ @tube.put "foo stats #{@time}"
113
+ @stats = @tube.stats
114
+ end
115
+
116
+ it "should return total number of jobs in tube" do
117
+ assert_equal 1, @stats['current_jobs_ready']
118
+ assert_equal 0, @stats.current_jobs_delayed
119
+ end
120
+
121
+ it "should raise error for empty tube" do
122
+ assert_raises(Beaneater::NotFoundError) { @pool.tubes.find('fake_tube').stats }
123
+ end
124
+ end # stats
125
+
126
+ describe "for #kick" do
127
+ before do
128
+ @time, @time2 = 2.times.map { Time.now.to_i }
129
+ @tube.put "kick #{@time}"
130
+ @tube.put "kick #{@time2}"
131
+
132
+ 2.times.map { @tube.reserve.bury }
133
+ end
134
+
135
+ it "should kick 2 buried jobs" do
136
+ assert_equal 2, @tube.stats.current_jobs_buried
137
+ @tube.kick(2)
138
+ assert_equal 0, @tube.stats.current_jobs_buried
139
+ assert_equal 2, @tube.stats.current_jobs_ready
140
+ end
141
+ end # kick
142
+
143
+ describe "for #clear" do
144
+ @time = Time.now.to_i
145
+ before do
146
+ 2.times { |i| @tube.put "to clear success #{i} #{@time}" }
147
+ 2.times { |i| @tube.put "to clear delayed #{i} #{@time}", :delay => 5 }
148
+ 2.times { |i| @tube.put "to clear bury #{i} #{@time}", :pri => 1 }
149
+ @tube.reserve.bury while @tube.peek(:ready).stats['pri'] == 1
150
+ end
151
+
152
+ it "should clear all jobs in tube" do
153
+ tube_counts = lambda { %w(ready buried delayed).map { |s| @tube.stats["current_jobs_#{s}"] } }
154
+ assert_equal [2, 2, 2], tube_counts.call
155
+ @tube.clear
156
+ stats = @tube.stats
157
+ assert_equal [0, 0, 0], tube_counts.call
158
+ end
159
+ end # clear
160
+
161
+ after do
162
+ cleanup_tubes!(['baz'])
163
+ end
164
+ end # Beaneater::Tubes
@@ -0,0 +1,153 @@
1
+ # test/connection_test.rb
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe Beaneater::Tubes do
6
+ describe "for #find" do
7
+ before do
8
+ @pool = stub
9
+ @tubes = Beaneater::Tubes.new(@pool)
10
+ end
11
+
12
+ it("should return Tube obj") { assert_kind_of Beaneater::Tube, @tubes.find(:foo) }
13
+ it("should return Tube name") { assert_equal "foo", @tubes.find(:foo).name }
14
+ it("should support hash syntax") { assert_equal "bar", @tubes["bar"].name }
15
+ end # find
16
+
17
+ describe "for #use" do
18
+ before do
19
+ @pool = Beaneater::Pool.new(['localhost'])
20
+ end
21
+
22
+ it "should switch to used tube for valid name" do
23
+ tube = Beaneater::Tube.new(@pool, 'some_name')
24
+ @pool.tubes.use('some_name')
25
+ assert_equal 'some_name', @pool.tubes.used.name
26
+ end
27
+
28
+ it "should raise for invalid tube name" do
29
+ assert_raises(Beaneater::InvalidTubeName) { @pool.tubes.use('; ') }
30
+ end
31
+ end # use
32
+
33
+ describe "for #watch & #watched" do
34
+ before do
35
+ @pool = Beaneater::Pool.new(['localhost'])
36
+ end
37
+
38
+ it 'should watch specified tubes' do
39
+ @pool.tubes.watch('foo')
40
+ @pool.tubes.watch('bar')
41
+ assert_equal ['default', 'foo', 'bar'].sort, @pool.tubes.watched.map(&:name).sort
42
+ end
43
+
44
+ it 'should raise invalid name for bad tube' do
45
+ assert_raises(Beaneater::InvalidTubeName) { @pool.tubes.watch('; ') }
46
+ end
47
+ end # watch! & watched
48
+
49
+ describe "for #all" do
50
+ before do
51
+ @pool = Beaneater::Pool.new(['localhost'])
52
+ @pool.tubes.find('foo').put 'bar'
53
+ @pool.tubes.find('bar').put 'foo'
54
+ end
55
+
56
+ it 'should retrieve all tubes' do
57
+ ['default', 'foo', 'bar'].each do |t|
58
+ assert @pool.tubes.all.map(&:name).include?(t)
59
+ end
60
+ end
61
+ end # all
62
+
63
+ describe "for #used" do
64
+ before do
65
+ @pool = Beaneater::Pool.new(['localhost'])
66
+ @pool.tubes.find('foo').put 'bar'
67
+ @pool.tubes.find('bar').put 'foo'
68
+ end
69
+
70
+ it 'should retrieve used tube' do
71
+ assert_equal'bar', @pool.tubes.used.name
72
+ end
73
+ end # used
74
+
75
+ describe "for #watch!" do
76
+ before do
77
+ @pool = Beaneater::Pool.new(['localhost'])
78
+ end
79
+
80
+ it 'should watch specified tubes' do
81
+ @pool.tubes.watch!(:foo)
82
+ @pool.tubes.watch!('bar')
83
+ assert_equal ['bar'].sort, @pool.tubes.watched.map(&:name).sort
84
+ end
85
+ end # watch!
86
+
87
+ describe "for #ignore" do
88
+ before do
89
+ @pool = Beaneater::Pool.new(['localhost'])
90
+ end
91
+
92
+ it 'should ignore specified tubes' do
93
+ @pool.tubes.watch('foo')
94
+ @pool.tubes.watch('bar')
95
+ @pool.tubes.ignore('foo')
96
+ assert_equal ['default', 'bar'].sort, @pool.tubes.watched.map(&:name).sort
97
+ end
98
+ end # ignore
99
+
100
+ describe "for #reserve" do
101
+ before do
102
+ @pool = Beaneater::Pool.new(['localhost'])
103
+ @tube = @pool.tubes.find 'tube'
104
+ @time = Time.now.to_i
105
+ @tube.put "foo reserve #{@time}"
106
+ end
107
+
108
+ it("should reserve job") do
109
+ @pool.tubes.watch 'tube'
110
+ job = @pool.tubes.reserve
111
+ assert_equal "foo reserve #{@time}", job.body
112
+ job.delete
113
+ end
114
+
115
+ it("should reserve job with block") do
116
+ @pool.tubes.watch 'tube'
117
+ job = nil
118
+ @pool.tubes.reserve { |j| job = j; job.delete }
119
+ assert_equal "foo reserve #{@time}", job.body
120
+ end
121
+
122
+ it("should reserve job with block and timeout") do
123
+ @pool.tubes.watch 'tube'
124
+ job = nil
125
+ res = @pool.tubes.reserve(0) { |j| job = j; job.delete }
126
+ assert_equal "foo reserve #{@time}", job.body
127
+ end
128
+
129
+ it "should raise TimedOutError with timeout" do
130
+ @pool.tubes.watch 'tube'
131
+ @pool.tubes.reserve(0) { |j| job = j; job.delete }
132
+ assert_raises(Beaneater::TimedOutError) { @pool.tubes.reserve(0) }
133
+ end
134
+
135
+ it "should raise TimedOutError no delete, with timeout" do
136
+ @pool.tubes.watch 'tube'
137
+ @pool.tubes.reserve(0)
138
+ assert_raises(Beaneater::TimedOutError) { @pool.tubes.reserve(0) }
139
+ end
140
+
141
+ it "should raise DeadlineSoonError with ttr 1" do
142
+ @tube.reserve.delete
143
+ @tube.put "foo reserve #{@time}", :ttr => 1
144
+ @pool.tubes.watch 'tube'
145
+ @pool.tubes.reserve
146
+ assert_raises(Beaneater::DeadlineSoonError) { @pool.tubes.reserve(0) }
147
+ end
148
+
149
+ after do
150
+ cleanup_tubes!(['foo', 'tube'])
151
+ end
152
+ end # reserve
153
+ end # Beaneater::Tubes