tuber 0.0.1 → 0.6.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/job_test.rb ADDED
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/job_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Tuber::Job do
8
+ before do
9
+ @beanstalk = Tuber.new(tuber_address)
10
+ @tube = @beanstalk.tubes.find 'tube'
11
+ end
12
+
13
+ describe "for #bury" do
14
+ before do
15
+ @tube.clear
16
+ @time = Time.now.to_i
17
+ @tube.put "foo bury #{@time}", :pri => 5
18
+ end
19
+
20
+ it("should be buried with same pri") do
21
+ job = @tube.reserve
22
+ assert_equal "foo bury #{@time}", job.body
23
+ assert_equal 'reserved', job.stats.state
24
+ job.bury
25
+ assert_equal 'buried', job.stats.state
26
+ assert_equal 5, job.stats.pri
27
+ assert_equal "foo bury #{@time}", @tube.peek(:buried).body
28
+ end
29
+
30
+ it("should be released with new pri") do
31
+ job = @tube.reserve
32
+ assert_equal "foo bury #{@time}", job.body
33
+ assert_equal 'reserved', job.stats.state
34
+ job.bury(:pri => 10)
35
+ assert_equal 'buried', job.stats.state
36
+ assert_equal 10, job.stats.pri
37
+ assert_equal "foo bury #{@time}", @tube.peek(:buried).body
38
+ end
39
+
40
+ it "should not bury if not reserved" do
41
+ job = @tube.peek(:ready)
42
+ assert_raises(Tuber::JobNotReserved) { job.bury }
43
+ end
44
+
45
+ it "should not bury if reserved and deleted" do
46
+ job = @tube.reserve
47
+ job.delete
48
+ assert_equal false, job.reserved
49
+ assert_raises(Tuber::NotFoundError) { job.bury }
50
+ end
51
+ end # bury
52
+
53
+ describe "for #release" do
54
+ before do
55
+ @time = Time.now.to_i
56
+ @tube.put "foo release #{@time}", :pri => 5
57
+ end
58
+
59
+ it("should be released with same pri") do
60
+ job = @tube.reserve
61
+ assert_equal "foo release #{@time}", job.body
62
+ assert_equal 'reserved', job.stats.state
63
+ job.release
64
+ assert_equal 'ready', job.stats.state
65
+ assert_equal 5, job.stats.pri
66
+ assert_equal 0, job.stats.delay
67
+ end
68
+
69
+ it("should be released with new pri") do
70
+ job = @tube.reserve
71
+ assert_equal "foo release #{@time}", job.body
72
+ assert_equal 'reserved', job.stats.state
73
+ job.release :pri => 10, :delay => 2
74
+ assert_equal 'delayed', job.stats.state
75
+ assert_equal 10, job.stats.pri
76
+ assert_equal 2, job.stats.delay
77
+ end
78
+
79
+ it "should not released if not reserved" do
80
+ job = @tube.peek(:ready)
81
+ assert_raises(Tuber::JobNotReserved) { job.release }
82
+ end
83
+
84
+ it "should not release if not reserved and buried" do
85
+ job = @tube.reserve
86
+ job.bury
87
+ assert_raises(Tuber::JobNotReserved) { job.release }
88
+ end
89
+ end # release
90
+
91
+ describe "for #delete" do
92
+ before do
93
+ @tube.put 'foo'
94
+ end
95
+
96
+ it("should deletable") do
97
+ job = @tube.peek(:ready)
98
+ assert_equal 'foo', job.body
99
+ job.delete
100
+ assert_nil @tube.peek(:ready)
101
+ end
102
+ end # delete
103
+
104
+ describe "for #touch" do
105
+ before do
106
+ @tube.put 'foo touch', :ttr => 1
107
+ end
108
+
109
+ it("should be toucheable") do
110
+ job = @tube.reserve
111
+ assert_equal 'foo touch', job.body
112
+ job.touch
113
+ assert_equal 1, job.stats.reserves
114
+ job.delete
115
+ end
116
+
117
+ it "should not touch if not reserved" do
118
+ job = @tube.peek(:ready)
119
+ assert_raises(Tuber::JobNotReserved) { job.touch }
120
+ end
121
+
122
+ it "should not touch if not reserved and released" do
123
+ job = @tube.reserve
124
+ job.release
125
+ assert_raises(Tuber::JobNotReserved) { job.touch }
126
+ end
127
+
128
+ it "should not touch if reserved and deleted" do
129
+ job = @tube.reserve
130
+ job.delete
131
+ assert_raises(Tuber::NotFoundError) { job.touch }
132
+ end
133
+ end # touch
134
+
135
+ describe "for #kick" do
136
+ before do
137
+ @tube.put 'foo touch', :ttr => 1
138
+ end
139
+
140
+ it("should be toucheable") do
141
+ job = @tube.reserve
142
+ assert_equal 'foo touch', job.body
143
+ job.bury
144
+ assert_equal 1, @tube.stats.current_jobs_buried
145
+ if @beanstalk.stats.version.to_f > 1.7
146
+ job.kick
147
+ assert_equal 0, @tube.stats.current_jobs_buried
148
+ assert_equal 1, @tube.stats.current_jobs_ready
149
+ end
150
+ end
151
+ end # kick
152
+
153
+ describe "for #stats" do
154
+ before do
155
+ @tube.put 'foo'
156
+ @job = @tube.peek(:ready)
157
+ end
158
+
159
+ it("should have stats") do
160
+ assert_equal 'tube', @job.stats['tube']
161
+ assert_equal 'ready', @job.stats.state
162
+ end
163
+
164
+ it "should return nil for deleted job with no stats" do
165
+ @job.delete
166
+ assert_raises(Tuber::NotFoundError) { @job.stats }
167
+ end
168
+ end # stats
169
+
170
+ describe "for #reserved?" do
171
+ before do
172
+ @tube.put 'foo'
173
+ @job = @tube.peek(:ready)
174
+ end
175
+
176
+ it("should have stats") do
177
+ assert_equal false, @job.reserved?
178
+ job = @tube.reserve
179
+ assert_equal job.id, @job.id
180
+ assert_equal true, @job.reserved?
181
+ @job.delete
182
+ assert_raises(Tuber::NotFoundError) { @job.reserved? }
183
+ end
184
+ end # reserved?
185
+
186
+ describe "for #exists?" do
187
+ before do
188
+ @tube.put 'foo'
189
+ @job = @tube.peek(:ready)
190
+ end
191
+
192
+ it("should exists") { assert @job.exists? }
193
+
194
+ it "should not exist" do
195
+ @job.delete
196
+ assert !@job.exists?
197
+ end
198
+ end # exists?
199
+
200
+ describe "for #tube" do
201
+ before do
202
+ @tube.put 'bar'
203
+ @job = @tube.peek(:ready)
204
+ end
205
+
206
+ it("should have stats") do
207
+ job = @tube.reserve
208
+ assert_equal @tube.name, job.tube
209
+ job.release
210
+ end
211
+ end # tube
212
+
213
+ describe "for #pri" do
214
+ before do
215
+ @tube.put 'bar', :pri => 1
216
+ @job = @tube.peek(:ready)
217
+ end
218
+
219
+ it("should return pri") do
220
+ job = @tube.reserve
221
+ assert_equal 1, job.pri
222
+ job.release
223
+ end
224
+ end # pri
225
+
226
+
227
+ describe "for #ttr" do
228
+ before do
229
+ @tube.put 'bar', :ttr => 5
230
+ @job = @tube.peek(:ready)
231
+ end
232
+
233
+ it("should return ttr") do
234
+ job = @tube.reserve
235
+ assert_equal 5, job.ttr
236
+ job.release
237
+ end
238
+ end # ttr
239
+
240
+ describe "for #delay" do
241
+ before do
242
+ @tube.put 'bar', :delay => 5
243
+ @job = @tube.peek(:delayed)
244
+ end
245
+
246
+ it("should return delay") do
247
+ assert_equal 5, @job.delay
248
+ end
249
+ end # delay
250
+ end # Tuber::Job
data/test/jobs_test.rb ADDED
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/jobs_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Tuber::Jobs do
8
+ before do
9
+ @beanstalk = Tuber.new(tuber_address)
10
+ @jobs = Tuber::Jobs.new(@beanstalk)
11
+ @tube = @beanstalk.tubes.find('baz')
12
+ end
13
+
14
+ describe "for #find" do
15
+ before do
16
+ @time = Time.now.to_i
17
+ @tube.put("foo find #{@time}")
18
+ @job = @tube.peek(:ready)
19
+ end
20
+
21
+ it "should return job from id" do
22
+ assert_equal "foo find #{@time}", @jobs.find(@job.id).body
23
+ end
24
+
25
+ it "should return job using peek" do
26
+ assert_equal "foo find #{@time}", @jobs.find(@job.id).body
27
+ end
28
+
29
+ it "should return job using hash syntax" do
30
+ assert_equal "foo find #{@time}", @jobs.find(@job.id).body
31
+ end
32
+
33
+ it "should return nil for invalid negative id" do
34
+ assert_nil @jobs.find(10000)
35
+ end
36
+
37
+ it "should return nil for invalid negative id" do
38
+ assert_raises(Tuber::BadFormatError) { @jobs.find(-1) }
39
+ end
40
+ end # find
41
+
42
+ describe "for #touch_all" do
43
+ before do
44
+ require_tuber!
45
+ @touch_tube = @beanstalk.tubes.find('touch_all_tube')
46
+ @beanstalk.tubes.watch! 'touch_all_tube'
47
+ end
48
+
49
+ after do
50
+ cleanup_tubes!(['touch_all_tube'], @beanstalk)
51
+ end
52
+
53
+ it "should return 0 when no jobs are held" do
54
+ assert_equal 0, @beanstalk.jobs.touch_all
55
+ end
56
+
57
+ it "should count every job held by the connection" do
58
+ 3.times { |i| @touch_tube.put "touch all #{i}", :ttr => 5 }
59
+ jobs = @beanstalk.tubes.reserve_batch(3)
60
+ assert_equal 3, jobs.size
61
+ assert_equal 3, @beanstalk.jobs.touch_all
62
+ jobs.each(&:delete)
63
+ end
64
+
65
+ it "should exclude jobs the worker no longer holds" do
66
+ 2.times { |i| @touch_tube.put "touch all drop #{i}", :ttr => 5 }
67
+ jobs = @beanstalk.tubes.reserve_batch(2)
68
+ jobs.first.delete
69
+ assert_equal 1, @beanstalk.jobs.touch_all
70
+ jobs.last.delete
71
+ assert_equal 0, @beanstalk.jobs.touch_all
72
+ end
73
+
74
+ it "should extend the ttr of held jobs" do
75
+ @touch_tube.put "touch all ttr", :ttr => 3
76
+ job = @beanstalk.tubes.reserve
77
+ sleep 2
78
+ assert_equal 1, @beanstalk.jobs.touch_all
79
+ sleep 2
80
+ assert_equal 'reserved', job.stats.state
81
+ job.delete
82
+ end
83
+ end # touch_all
84
+
85
+ describe "for #register!" do
86
+ before do
87
+ $foo = 0
88
+ @jobs.register('tube', :retry_on => [Timeout::Error]) do |job|
89
+ $foo += 1
90
+ end
91
+ end
92
+
93
+ it "should store processor" do
94
+ assert_equal 'tube', @jobs.processors.keys.first
95
+ assert_equal [Timeout::Error], @jobs.processors.values.first[:retry_on]
96
+ end
97
+
98
+ it "should store block for 'tube'" do
99
+ @jobs.processors['tube'][:block].call nil
100
+ assert_equal 1, $foo
101
+ end
102
+ end # register!
103
+
104
+ describe "for process!" do
105
+ before do
106
+ $foo = []
107
+
108
+ @jobs.register('tube_success', :retry_on => [Timeout::Error]) do |job|
109
+ # p job.body
110
+ $foo << job.body
111
+ raise Tuber::AbortProcessingError if job.body =~ /abort/
112
+ end
113
+
114
+ @jobs.register('tube_release', :retry_on => [Timeout::Error], :max_retries => 2) do |job|
115
+ $foo << job.body
116
+ raise Timeout::Error
117
+ end
118
+
119
+ @jobs.register('tube_buried') do |job|
120
+ $foo << job.body
121
+ raise RuntimeError
122
+ end
123
+
124
+ cleanup_tubes!(['tube_success', 'tube_release', 'tube_buried'])
125
+
126
+ @beanstalk.tubes.find('tube_success').put("success abort", :pri => 2**31 + 1)
127
+ @beanstalk.tubes.find('tube_success').put("success 2", :pri => 1)
128
+ @beanstalk.tubes.find('tube_release').put("released")
129
+ @beanstalk.tubes.find('tube_buried').put("buried")
130
+
131
+ @jobs.process!(:release_delay => 0)
132
+ end
133
+
134
+ it "should process all jobs" do
135
+ assert_equal ['success 2', 'released', 'released', 'released', 'buried', 'success abort'], $foo
136
+ end
137
+
138
+ it "should clear successful_jobs" do
139
+ assert_equal 0, @beanstalk.tubes.find('tube_success').stats.current_jobs_ready
140
+ assert_equal 1, @beanstalk.tubes.find('tube_success').stats.current_jobs_buried
141
+ assert_equal 0, @beanstalk.tubes.find('tube_success').stats.current_jobs_reserved
142
+ end
143
+
144
+ it "should retry release jobs 2 times" do
145
+ assert_equal 2, @beanstalk.tubes.find('tube_release').peek(:buried).stats.releases
146
+ end
147
+
148
+ it "should bury unexpected exception" do
149
+ assert_equal 1, @beanstalk.tubes.find('tube_buried').stats.current_jobs_buried
150
+ end
151
+ end # for_process!
152
+ end # Tuber::Jobs
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/prompt_regexp_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+ require 'socket'
7
+
8
+ describe "Reading from socket client" do
9
+ before do
10
+ @fake_port = 11301
11
+ @tube_name = 'tube.to.test'
12
+
13
+ @fake_server = Thread.start do
14
+ server = TCPServer.new(@fake_port)
15
+ loop do
16
+ IO.select([server])
17
+ client = server.accept_nonblock
18
+ while line = client.gets
19
+ case line
20
+ when /list-tubes-watched/i
21
+ client.print "OK #{7+@tube_name.size}\r\n---\n- #{@tube_name}\n\r\n"
22
+ when /watch #{@tube_name}/i
23
+ client.print "WATCHING 1\r\n"
24
+ when /reserve/i
25
+ client.print "RESERVED 17 25\r\n"
26
+ client.print "[first part]"
27
+ # Emulate network delay
28
+ sleep 0.5
29
+ client.print "[second part]\r\n"
30
+ else
31
+ client.print "ERROR\r\n"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ slept = 0
38
+ while @beanstalk.nil?
39
+ begin
40
+ @beanstalk = Tuber.new("localhost:#{@fake_port}")
41
+ rescue Tuber::NotConnected
42
+ raise 'Could not connect to fake beanstalkd server' if slept > 1
43
+ sleep 0.1
44
+ slept += 0.1
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ it 'should reserve job with full body' do
51
+ job = @beanstalk.tubes[@tube_name].reserve
52
+ assert_equal '[first part][second part]', job.body
53
+ end
54
+
55
+ after do
56
+ @beanstalk.close
57
+ @fake_server.kill
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/stat_struct_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Tuber::StatStruct do
8
+ before do
9
+ @hash = { :foo => "bar", :bar => "baz", :baz => "foo", :"under-score" => "demo" }
10
+ @struct = Tuber::StatStruct.from_hash(@hash)
11
+ end
12
+
13
+ describe "for #from_hash" do
14
+ it "should have 4 keys" do
15
+ assert_equal 'bar', @struct.foo
16
+ assert_equal 'baz', @struct.bar
17
+ assert_equal 'foo', @struct.baz
18
+ assert_equal 'demo', @struct.under_score
19
+ end
20
+ end # from_hash
21
+
22
+ describe "for [] access" do
23
+ it "should have hash lookup" do
24
+ assert_equal 'bar', @struct['foo']
25
+ assert_equal 'baz', @struct['bar']
26
+ end
27
+
28
+ it "should convert keys to string" do
29
+ assert_equal 'foo', @struct[:baz]
30
+ assert_equal 'demo', @struct[:"under_score"]
31
+ end
32
+
33
+ it "should look up hyphenated beanstalkd keys" do
34
+ assert_equal 'demo', @struct['under-score']
35
+ assert_equal 'demo', @struct[:"under-score"]
36
+ end
37
+ end # []
38
+
39
+ describe "for #keys" do
40
+ it "should return 4 keys" do
41
+ assert_equal 4, @struct.keys.size
42
+ end
43
+
44
+ it "should return expected keys" do
45
+ assert_equal ['foo', 'bar', 'baz', 'under_score'].sort, @struct.keys.sort
46
+ end
47
+ end # keys
48
+
49
+ describe "for #to_h" do
50
+ it "should return 4 keys / values" do
51
+ assert_equal 4, @struct.to_h.size
52
+ end
53
+
54
+ it "should return expect hash" do
55
+ assert_equal [['foo', 'bar'], ['bar', 'baz'], ['baz', 'foo'], ['under_score', 'demo']].sort, @struct.to_h.sort
56
+ end
57
+ end # to_h
58
+ end # Tuber::StatStruct
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/stats_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Tuber::Stats do
8
+ before do
9
+ connection = stub(:transmit => { :body => { 'uptime' => 1, 'cmd-use' => 2 }, :status => "OK"})
10
+ @beanstalk = stub(:connection => connection)
11
+ @stats = Tuber::Stats.new(@beanstalk)
12
+ end
13
+
14
+ describe 'for #[]' do
15
+ it "should return stats by key" do
16
+ assert_equal 1, @stats[:uptime]
17
+ end
18
+
19
+ it "should return stats by underscore key" do
20
+ assert_equal 2, @stats[:'cmd_use']
21
+ end
22
+ end # []
23
+
24
+ describe 'for #keys' do
25
+ it "should return list of keys" do
26
+ assert_equal 2, @stats.keys.size
27
+ assert @stats.keys.include?('uptime'), "Expected keys to include 'uptime'"
28
+ assert @stats.keys.include?('cmd_use'), "Expected keys to include 'cmd-use'"
29
+ end
30
+ end # keys
31
+
32
+ describe 'for #method_missing' do
33
+ it "should return stats by key" do
34
+ assert_equal 1, @stats.uptime
35
+ end
36
+
37
+ it "should return stats by underscore key" do
38
+ assert_equal 2, @stats.cmd_use
39
+ end
40
+
41
+ it "should raise NoMethodError" do
42
+ assert_raises(NoMethodError) { @stats.cmd }
43
+ end
44
+ end # method_missing
45
+ end # Tuber::Stats
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["TEST"] = 'true'
4
+ require 'rubygems'
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+ require 'minitest/autorun'
8
+ $:.unshift File.expand_path("../../lib")
9
+ require 'tuber'
10
+ require 'timeout'
11
+ begin
12
+ require 'mocha/minitest'
13
+ rescue LoadError
14
+ require 'mocha'
15
+ end
16
+ require 'json'
17
+
18
+ class MiniTest::Unit::TestCase
19
+
20
+ # Address of the server the integration-style tests talk to. Override with
21
+ # TUBER_TEST_ADDRESS when localhost:11300 isn't the server you mean
22
+ # (e.g. TUBER_TEST_ADDRESS=127.0.0.1 when another queue holds the IPv6 port).
23
+ def tuber_address
24
+ ENV.fetch('TUBER_TEST_ADDRESS', 'localhost')
25
+ end
26
+
27
+ # True when the server under test is Tuber rather than stock beanstalkd.
28
+ def tuber_server?
29
+ if $tuber_server.nil?
30
+ client = Tuber.new(tuber_address)
31
+ begin
32
+ $tuber_server = client.connection.transmit('stats')[:body]['version'].to_s.include?('tuber')
33
+ ensure
34
+ client.close
35
+ end
36
+ end
37
+ $tuber_server
38
+ end
39
+
40
+ # Skips the current test unless the server speaks Tuber's extended protocol
41
+ # (reserve-batch, touch-all, flush-buried, ...).
42
+ def require_tuber!
43
+ skip "requires a Tuber server, not stock beanstalkd" unless tuber_server?
44
+ end
45
+
46
+ # Cleans up all jobs from specific tubes
47
+ #
48
+ # @example
49
+ # cleanup_tubes!(['foo'], @beanstalk)
50
+ #
51
+ def cleanup_tubes!(tubes, client=nil)
52
+ client ||= @beanstalk
53
+ tubes.each do |name|
54
+ client.tubes.find(name).clear
55
+ end
56
+ end
57
+
58
+ # Cleans up all jobs from all tubes known to the connection
59
+ def flush_all(client=nil)
60
+ client ||= @beanstalk
61
+
62
+ # Do not continue if it is a mock or the connection has been closed
63
+ return if !client.is_a?(Tuber) || !client.connection.connection
64
+
65
+ client.tubes.all.each do |tube|
66
+ tube.clear
67
+ end
68
+ end
69
+
70
+ # Run clean up after each test to ensure clean state in all tests
71
+ def teardown
72
+ flush_all
73
+ end
74
+ end