beaneater 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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