tuber 0.0.1 → 0.5.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +68 -0
- data/.gitignore +17 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +112 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +27 -0
- data/README.md +379 -8
- data/Rakefile +35 -0
- data/TODO +1 -0
- data/examples/demo.rb +97 -0
- data/lib/tuber/configuration.rb +31 -0
- data/lib/tuber/connection.rb +366 -0
- data/lib/tuber/errors.rb +78 -0
- data/lib/tuber/job/collection.rb +160 -0
- data/lib/tuber/job/record.rb +226 -0
- data/lib/tuber/job.rb +4 -0
- data/lib/tuber/stats/fast_struct.rb +98 -0
- data/lib/tuber/stats/stat_struct.rb +52 -0
- data/lib/tuber/stats.rb +81 -0
- data/lib/tuber/tube/collection.rb +253 -0
- data/lib/tuber/tube/record.rb +231 -0
- data/lib/tuber/tube.rb +4 -0
- data/lib/tuber/version.rb +6 -0
- data/lib/tuber.rb +104 -2
- data/test/connection_test.rb +291 -0
- data/test/errors_test.rb +35 -0
- data/test/job_test.rb +250 -0
- data/test/jobs_test.rb +152 -0
- data/test/prompt_regexp_test.rb +59 -0
- data/test/stat_struct_test.rb +58 -0
- data/test/stats_test.rb +45 -0
- data/test/test_helper.rb +74 -0
- data/test/tube_test.rb +299 -0
- data/test/tuber_test.rb +112 -0
- data/test/tubes_test.rb +329 -0
- data/tuber.gemspec +33 -0
- metadata +112 -6
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# test/connection_test.rb
|
|
4
|
+
|
|
5
|
+
require File.expand_path('../test_helper', __FILE__)
|
|
6
|
+
|
|
7
|
+
describe Tuber::Connection do
|
|
8
|
+
|
|
9
|
+
describe 'for #new' do
|
|
10
|
+
before do
|
|
11
|
+
@host = 'localhost'
|
|
12
|
+
@bc = Tuber::Connection.new(@host)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should store address, host and port" do
|
|
16
|
+
assert_equal 'localhost', @bc.address
|
|
17
|
+
assert_equal 'localhost', @bc.host
|
|
18
|
+
assert_equal 11300, @bc.port
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should init connection" do
|
|
22
|
+
assert_kind_of TCPSocket, @bc.connection
|
|
23
|
+
if @bc.connection.peeraddr[0] == 'AF_INET'
|
|
24
|
+
assert_equal '127.0.0.1', @bc.connection.peeraddr[3]
|
|
25
|
+
else
|
|
26
|
+
assert_equal 'AF_INET6', @bc.connection.peeraddr[0]
|
|
27
|
+
assert_equal '::1', @bc.connection.peeraddr[3]
|
|
28
|
+
end
|
|
29
|
+
assert_equal 11300, @bc.connection.peeraddr[1]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should raise on invalid connection" do
|
|
33
|
+
assert_raises(Tuber::NotConnected) { Tuber::Connection.new("localhost:8544") }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should support array connection to single connection" do
|
|
37
|
+
@bc2 = Tuber::Connection.new([@host])
|
|
38
|
+
assert_equal 'localhost', @bc.address
|
|
39
|
+
assert_equal 'localhost', @bc.host
|
|
40
|
+
assert_equal 11300, @bc.port
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should use TUBER_URL over BEANSTALKD_URL from env" do
|
|
44
|
+
ENV['TUBER_URL'] = tuber_address.to_s
|
|
45
|
+
ENV['BEANSTALKD_URL'] = 'unreachable.invalid:99999'
|
|
46
|
+
bc = Tuber::Connection.new(nil)
|
|
47
|
+
assert_equal tuber_address.to_s, bc.address
|
|
48
|
+
ensure
|
|
49
|
+
ENV.delete('TUBER_URL')
|
|
50
|
+
ENV.delete('BEANSTALKD_URL')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should fall back to BEANSTALKD_URL from env" do
|
|
54
|
+
ENV['BEANSTALKD_URL'] = tuber_address.to_s
|
|
55
|
+
bc = Tuber::Connection.new(nil)
|
|
56
|
+
assert_equal tuber_address.to_s, bc.address
|
|
57
|
+
ensure
|
|
58
|
+
ENV.delete('BEANSTALKD_URL')
|
|
59
|
+
end
|
|
60
|
+
end # new
|
|
61
|
+
|
|
62
|
+
describe 'for timeout configuration' do
|
|
63
|
+
after do
|
|
64
|
+
Tuber.configure do |config|
|
|
65
|
+
config.connect_timeout = nil
|
|
66
|
+
config.resolv_timeout = nil
|
|
67
|
+
config.read_timeout = nil
|
|
68
|
+
config.write_timeout = nil
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should pass connect_timeout and resolv_timeout to TCPSocket.new on Ruby >= 3.0" do
|
|
73
|
+
skip("connect_timeout/resolv_timeout kwargs require Ruby >= 3.0") if RUBY_VERSION < "3.0"
|
|
74
|
+
|
|
75
|
+
Tuber.configure do |config|
|
|
76
|
+
config.connect_timeout = 2
|
|
77
|
+
config.resolv_timeout = 2
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
real_socket = TCPSocket.new('localhost', 11300)
|
|
81
|
+
TCPSocket.expects(:new).with('localhost', 11300, connect_timeout: 2, resolv_timeout: 2).returns(real_socket)
|
|
82
|
+
|
|
83
|
+
bc = Tuber::Connection.new('localhost')
|
|
84
|
+
assert_kind_of TCPSocket, bc.connection
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should ignore connect_timeout and resolv_timeout on Ruby < 3.0" do
|
|
88
|
+
skip("connect_timeout/resolv_timeout kwargs are supported on Ruby >= 3.0") if RUBY_VERSION >= "3.0"
|
|
89
|
+
|
|
90
|
+
Tuber.configure do |config|
|
|
91
|
+
config.connect_timeout = 2
|
|
92
|
+
config.resolv_timeout = 4
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
real_socket = TCPSocket.new('localhost', 11300)
|
|
96
|
+
TCPSocket.expects(:new).with('localhost', 11300).returns(real_socket)
|
|
97
|
+
|
|
98
|
+
bc = Tuber::Connection.new('localhost')
|
|
99
|
+
assert_same real_socket, bc.connection
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should set SO_RCVTIMEO and SO_SNDTIMEO when read/write timeouts are configured" do
|
|
103
|
+
Tuber.configure do |config|
|
|
104
|
+
config.read_timeout = 3
|
|
105
|
+
config.write_timeout = 5
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
bc = Tuber::Connection.new('localhost')
|
|
109
|
+
rcv = bc.connection.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).unpack('l_l_')[0]
|
|
110
|
+
snd = bc.connection.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO).unpack('l_l_')[0]
|
|
111
|
+
assert_equal 3, rcv
|
|
112
|
+
assert_equal 5, snd
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end # timeout configuration
|
|
116
|
+
|
|
117
|
+
describe 'for #transmit' do
|
|
118
|
+
before do
|
|
119
|
+
@host = 'localhost'
|
|
120
|
+
@bc = Tuber::Connection.new(@host)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should return yaml loaded response" do
|
|
124
|
+
res = @bc.transmit 'stats'
|
|
125
|
+
refute_nil res[:body]['current-connections']
|
|
126
|
+
assert_equal 'OK', res[:status]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should return id" do
|
|
130
|
+
res = @bc.transmit "put 0 0 100 1\r\nX"
|
|
131
|
+
assert res[:id]
|
|
132
|
+
assert_equal 'INSERTED', res[:status]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should support dashes in response" do
|
|
136
|
+
res = @bc.transmit "use foo-bar\r\n"
|
|
137
|
+
assert_equal 'USING', res[:status]
|
|
138
|
+
assert_equal 'foo-bar', res[:id]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "should pass crlf through without changing its length" do
|
|
142
|
+
res = @bc.transmit "put 0 0 100 2\r\n\r\n"
|
|
143
|
+
assert_equal 'INSERTED', res[:status]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should handle *any* byte value without changing length" do
|
|
147
|
+
res = @bc.transmit "put 0 0 100 256\r\n"+(0..255).to_a.pack("c*")
|
|
148
|
+
assert_equal 'INSERTED', res[:status]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should retry idempotent command with success after one connection failure" do
|
|
152
|
+
TCPSocket.any_instance.expects(:readline).times(2).
|
|
153
|
+
raises(EOFError.new).then.
|
|
154
|
+
returns("USING foo\n")
|
|
155
|
+
|
|
156
|
+
res = @bc.transmit "use foo\r\n"
|
|
157
|
+
assert_equal 'USING', res[:status]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should not retransmit delete after a connection failure" do
|
|
161
|
+
TCPSocket.any_instance.expects(:readline).times(1).raises(EOFError.new)
|
|
162
|
+
|
|
163
|
+
assert_raises(EOFError) { @bc.transmit "delete 56\r\n" }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should not retransmit put after a connection failure" do
|
|
167
|
+
TCPSocket.any_instance.expects(:readline).times(1).raises(Errno::ECONNRESET.new)
|
|
168
|
+
|
|
169
|
+
assert_raises(Errno::ECONNRESET) { @bc.transmit "put 0 0 100 4\r\ndata" }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "heals the connection after a non-retransmitted failure" do
|
|
173
|
+
$eof_once = false
|
|
174
|
+
TCPSocket.prepend(Module.new do
|
|
175
|
+
def readline
|
|
176
|
+
if $eof_once == false
|
|
177
|
+
$eof_once = true
|
|
178
|
+
raise EOFError
|
|
179
|
+
end
|
|
180
|
+
super
|
|
181
|
+
end
|
|
182
|
+
end)
|
|
183
|
+
|
|
184
|
+
assert_raises(EOFError) { @bc.transmit "delete 56\r\n" }
|
|
185
|
+
|
|
186
|
+
# The reconnect still happened, so the next command works.
|
|
187
|
+
res = @bc.transmit "use foo"
|
|
188
|
+
assert_equal 'USING', res[:status]
|
|
189
|
+
ensure
|
|
190
|
+
$eof_once = true
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "should fail after exceeding retries with DrainingError" do
|
|
194
|
+
TCPSocket.any_instance.expects(:readline).times(3).
|
|
195
|
+
raises(Tuber::UnexpectedResponse.from_status("DRAINING", "delete 56"))
|
|
196
|
+
|
|
197
|
+
assert_raises(Tuber::DrainingError) { @bc.transmit "delete 56\r\n" }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it "should fail after exceeding reconnect max retries" do
|
|
201
|
+
# next connection attempts should fail
|
|
202
|
+
TCPSocket.stubs(:new).times(3).raises(Errno::ECONNREFUSED.new)
|
|
203
|
+
TCPSocket.any_instance.stubs(:readline).times(1).raises(EOFError.new)
|
|
204
|
+
|
|
205
|
+
assert_raises(Tuber::NotConnected) { @bc.transmit "delete 56\r\n" }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "tubes_watched are restored after reconnect" do
|
|
209
|
+
client = Tuber.new('127.0.0.1:11300')
|
|
210
|
+
client.tubes.watch! "another"
|
|
211
|
+
|
|
212
|
+
$called = false
|
|
213
|
+
TCPSocket.prepend Module.new {
|
|
214
|
+
def readline
|
|
215
|
+
if !$called
|
|
216
|
+
$called = true
|
|
217
|
+
raise EOFError
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
super
|
|
221
|
+
end
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
assert_equal %w[another], client.tubes.watched.map(&:name)
|
|
225
|
+
ensure
|
|
226
|
+
# Leave the prepended module inert: with $called reset to nil it would
|
|
227
|
+
# fire one more EOFError at an arbitrary later readline, which a
|
|
228
|
+
# non-retransmitted command (delete/put) now surfaces as a failure.
|
|
229
|
+
$called = true
|
|
230
|
+
end
|
|
231
|
+
end # transmit
|
|
232
|
+
|
|
233
|
+
describe 'for idempotent insert response' do
|
|
234
|
+
before do
|
|
235
|
+
require_tuber!
|
|
236
|
+
@host = tuber_address
|
|
237
|
+
@bc = Tuber::Connection.new(@host)
|
|
238
|
+
@idp_key = "testkey_#{Time.now.to_f}"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "should return state for dedup hit" do
|
|
242
|
+
@bc.transmit "use idp_test"
|
|
243
|
+
res1 = @bc.transmit "put 0 0 100 4 idp:#{@idp_key}\r\ndata"
|
|
244
|
+
assert_equal 'INSERTED', res1[:status]
|
|
245
|
+
assert_nil res1[:state]
|
|
246
|
+
|
|
247
|
+
res2 = @bc.transmit "put 0 0 100 4 idp:#{@idp_key}\r\ndata"
|
|
248
|
+
assert_equal 'INSERTED', res2[:status]
|
|
249
|
+
assert_equal res1[:id], res2[:id]
|
|
250
|
+
assert_equal 'READY', res2[:state]
|
|
251
|
+
end
|
|
252
|
+
end # idempotent insert response
|
|
253
|
+
|
|
254
|
+
describe 'for #close' do
|
|
255
|
+
before do
|
|
256
|
+
@host = 'localhost'
|
|
257
|
+
@bc = Tuber::Connection.new(@host)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "should clear connection" do
|
|
261
|
+
assert_kind_of TCPSocket, @bc.connection
|
|
262
|
+
@bc.close
|
|
263
|
+
assert_nil @bc.connection
|
|
264
|
+
assert_raises(Tuber::NotConnected) { @bc.transmit 'stats' }
|
|
265
|
+
end
|
|
266
|
+
end # close
|
|
267
|
+
describe 'for drain and undrain commands' do
|
|
268
|
+
it "should return DRAINING status for drain command" do
|
|
269
|
+
@bc = Tuber::Connection.new('localhost')
|
|
270
|
+
TCPSocket.any_instance.stubs(:write)
|
|
271
|
+
TCPSocket.any_instance.expects(:readline).returns("DRAINING\r\n")
|
|
272
|
+
res = @bc.transmit("drain")
|
|
273
|
+
assert_equal 'DRAINING', res[:status]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "should return NOT_DRAINING status for undrain command" do
|
|
277
|
+
@bc = Tuber::Connection.new('localhost')
|
|
278
|
+
TCPSocket.any_instance.stubs(:write)
|
|
279
|
+
TCPSocket.any_instance.expects(:readline).returns("NOT_DRAINING\r\n")
|
|
280
|
+
res = @bc.transmit("undrain")
|
|
281
|
+
assert_equal 'NOT_DRAINING', res[:status]
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it "should still raise DrainingError for non-drain commands" do
|
|
285
|
+
@bc = Tuber::Connection.new('localhost')
|
|
286
|
+
TCPSocket.any_instance.stubs(:write)
|
|
287
|
+
TCPSocket.any_instance.expects(:readline).times(3).returns("DRAINING\r\n")
|
|
288
|
+
assert_raises(Tuber::DrainingError) { @bc.transmit("put 0 0 100 4\r\ntest") }
|
|
289
|
+
end
|
|
290
|
+
end # drain and undrain
|
|
291
|
+
end # Tuber::Connection
|
data/test/errors_test.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# test/errors_test.rb
|
|
4
|
+
|
|
5
|
+
require File.expand_path('../test_helper', __FILE__)
|
|
6
|
+
|
|
7
|
+
describe "Tuber::Errors" do
|
|
8
|
+
it 'should raise proper exception for invalid status NOT_FOUND' do
|
|
9
|
+
@klazz = Tuber::UnexpectedResponse.from_status("NOT_FOUND", "job-stats -1")
|
|
10
|
+
assert_kind_of(Tuber::NotFoundError, @klazz)
|
|
11
|
+
assert_equal 'job-stats -1', @klazz.cmd
|
|
12
|
+
assert_equal 'NOT_FOUND', @klazz.status
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should raise proper exception for invalid status BAD_FORMAT' do
|
|
16
|
+
@klazz = Tuber::UnexpectedResponse.from_status("BAD_FORMAT", "FAKE")
|
|
17
|
+
assert_kind_of(Tuber::BadFormatError, @klazz)
|
|
18
|
+
assert_equal 'FAKE', @klazz.cmd
|
|
19
|
+
assert_equal 'BAD_FORMAT', @klazz.status
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should raise proper exception for invalid status DEADLINE_SOON' do
|
|
23
|
+
@klazz = Tuber::UnexpectedResponse.from_status("DEADLINE_SOON", "reserve 0")
|
|
24
|
+
assert_kind_of(Tuber::DeadlineSoonError, @klazz)
|
|
25
|
+
assert_equal 'reserve 0', @klazz.cmd
|
|
26
|
+
assert_equal 'DEADLINE_SOON', @klazz.status
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should raise proper exception for invalid status EXPECTED_CRLF' do
|
|
30
|
+
@klazz = Tuber::UnexpectedResponse.from_status("EXPECTED_CRLF", "reserve 0")
|
|
31
|
+
assert_kind_of(Tuber::ExpectedCrlfError, @klazz)
|
|
32
|
+
assert_equal 'reserve 0', @klazz.cmd
|
|
33
|
+
assert_equal 'EXPECTED_CRLF', @klazz.status
|
|
34
|
+
end
|
|
35
|
+
end
|
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
|