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
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
|
data/test/stats_test.rb
ADDED
|
@@ -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
|
data/test/test_helper.rb
ADDED
|
@@ -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
|
data/test/tube_test.rb
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# test/tube_test.rb
|
|
4
|
+
|
|
5
|
+
require File.expand_path('../test_helper', __FILE__)
|
|
6
|
+
|
|
7
|
+
describe Tuber::Tube do
|
|
8
|
+
before do
|
|
9
|
+
@beanstalk = Tuber.new(tuber_address)
|
|
10
|
+
@tube = Tuber::Tube.new(@beanstalk, 'baz')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "for #put" do
|
|
14
|
+
before do
|
|
15
|
+
@time = Time.now.to_i
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should insert a job" do
|
|
19
|
+
@tube.put "bar put #{@time}"
|
|
20
|
+
assert_equal "bar put #{@time}", @tube.peek(:ready).body
|
|
21
|
+
assert_equal 120, @tube.peek(:ready).ttr
|
|
22
|
+
assert_equal 65536, @tube.peek(:ready).pri
|
|
23
|
+
assert_equal 0, @tube.peek(:ready).delay
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should insert a delayed job" do
|
|
27
|
+
@tube.put "delayed put #{@time}", :delay => 1
|
|
28
|
+
assert_equal "delayed put #{@time}", @tube.peek(:delayed).body
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should support custom serializer" do
|
|
32
|
+
Tuber.configure.job_serializer = lambda { |b| JSON.dump(b) }
|
|
33
|
+
@tube.put({ foo: "bar"})
|
|
34
|
+
assert_equal '{"foo":"bar"}', @tube.peek(:ready).body
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
after do
|
|
38
|
+
Tuber::Connection.any_instance.unstub(:transmit)
|
|
39
|
+
Tuber.configure.job_serializer = lambda { |b| b }
|
|
40
|
+
end
|
|
41
|
+
end # put
|
|
42
|
+
|
|
43
|
+
describe "for #peek" do
|
|
44
|
+
before do
|
|
45
|
+
@time = Time.now.to_i
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should peek delayed" do
|
|
49
|
+
@tube.put "foo delay #{@time}", :delay => 1
|
|
50
|
+
assert_equal "foo delay #{@time}", @tube.peek(:delayed).body
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should peek ready" do
|
|
54
|
+
@tube.put "foo ready #{@time}", :delay => 0
|
|
55
|
+
assert_equal "foo ready #{@time}", @tube.peek(:ready).body
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should peek buried" do
|
|
59
|
+
@tube.put "foo buried #{@time}"
|
|
60
|
+
@tube.reserve.bury
|
|
61
|
+
|
|
62
|
+
assert_equal "foo buried #{@time}", @tube.peek(:buried).body
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should return nil for empty peek" do
|
|
66
|
+
assert_nil @tube.peek(:buried)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "supports valid JSON" do
|
|
70
|
+
json = '{ "foo" : "bar" }'
|
|
71
|
+
@tube.put(json)
|
|
72
|
+
assert_equal 'bar', JSON.parse(@tube.peek(:ready).body)['foo']
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "supports non valid JSON" do
|
|
76
|
+
json = '{"message":"hi"}'
|
|
77
|
+
@tube.put(json)
|
|
78
|
+
assert_equal 'hi', JSON.parse(@tube.peek(:ready).body)['message']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "supports passing crlf through" do
|
|
82
|
+
@tube.put("\r\n")
|
|
83
|
+
assert_equal "\r\n", @tube.peek(:ready).body
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "supports passing any byte value through" do
|
|
87
|
+
bytes = (0..255).to_a.pack("c*")
|
|
88
|
+
@tube.put(bytes)
|
|
89
|
+
assert_equal bytes, @tube.peek(:ready).body
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should support custom parser" do
|
|
93
|
+
Tuber.configure.job_parser = lambda { |b| JSON.parse(b) }
|
|
94
|
+
json = '{"message": "hi: there! this: is a long message"}'
|
|
95
|
+
@tube.put(json)
|
|
96
|
+
assert_equal 'hi: there! this: is a long message', @tube.peek(:ready).body['message']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
after do
|
|
100
|
+
Tuber.configure.job_parser = lambda { |b| b }
|
|
101
|
+
end
|
|
102
|
+
end # peek
|
|
103
|
+
|
|
104
|
+
describe "for #reserve" do
|
|
105
|
+
before do
|
|
106
|
+
@time = Time.now.to_i
|
|
107
|
+
@json = %Q&{ "foo" : "#{@time} bar" }&
|
|
108
|
+
@tube.put @json
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should reserve job" do
|
|
112
|
+
@job = @tube.reserve
|
|
113
|
+
assert_equal "#{@time} bar", JSON.parse(@job.body)['foo']
|
|
114
|
+
@job.delete
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should reserve job with block" do
|
|
118
|
+
job = nil
|
|
119
|
+
@tube.reserve { |j| job = j; job.delete }
|
|
120
|
+
assert_equal "#{@time} bar", JSON.parse(job.body)['foo']
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should support custom parser" do
|
|
124
|
+
Tuber.configure.job_parser = lambda { |b| JSON.parse(b) }
|
|
125
|
+
@job = @tube.reserve
|
|
126
|
+
assert_equal "#{@time} bar", @job.body['foo']
|
|
127
|
+
@job.delete
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
after do
|
|
131
|
+
Tuber.configure.job_parser = lambda { |b| b }
|
|
132
|
+
end
|
|
133
|
+
end # reserve
|
|
134
|
+
|
|
135
|
+
describe "for #put with tuber tags" do
|
|
136
|
+
before do
|
|
137
|
+
@beanstalk = stub
|
|
138
|
+
@connection = stub(tube_used: 'baz')
|
|
139
|
+
@beanstalk.stubs(:connection).returns(@connection)
|
|
140
|
+
@tubes = stub
|
|
141
|
+
@tubes.stubs(:use).with('baz').returns('baz')
|
|
142
|
+
@beanstalk.stubs(:tubes).returns(@tubes)
|
|
143
|
+
@tube = Tuber::Tube.new(@beanstalk, 'baz')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should append idp tag as string" do
|
|
147
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:report\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
148
|
+
@tube.put "data", idp: "report"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should append idp tag with ttl as array" do
|
|
152
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:report:300\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
153
|
+
@tube.put "data", idp: ["report", 300]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should append con tag as string" do
|
|
157
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 con:db\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
158
|
+
@tube.put "data", con: "db"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should append con tag with limit as array" do
|
|
162
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 con:db:3\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
163
|
+
@tube.put "data", con: ["db", 3]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should append grp tag" do
|
|
167
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 grp:batch-1\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
168
|
+
@tube.put "data", grp: "batch-1"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should append aft tag" do
|
|
172
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 aft:batch-0\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
173
|
+
@tube.put "data", aft: "batch-0"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should append all four tags" do
|
|
177
|
+
@connection.expects(:transmit).with("put 65536 0 120 4 idp:key:60 con:db:2 grp:g1 aft:g0\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
178
|
+
@tube.put "data", idp: ["key", 60], con: ["db", 2], grp: "g1", aft: "g0"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should send plain put without tags" do
|
|
182
|
+
@connection.expects(:transmit).with("put 65536 0 120 4\r\ndata").returns({status: "INSERTED", id: "1"})
|
|
183
|
+
@tube.put "data"
|
|
184
|
+
end
|
|
185
|
+
end # put with tuber tags
|
|
186
|
+
|
|
187
|
+
describe "for #pause" do
|
|
188
|
+
before do
|
|
189
|
+
@time = Time.now.to_i
|
|
190
|
+
@tube = Tuber::Tube.new(@beanstalk, 'bam')
|
|
191
|
+
@tube.put "foo pause #{@time}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should allow tube pause" do
|
|
195
|
+
assert_equal 0, @tube.stats.pause
|
|
196
|
+
@tube.pause(1)
|
|
197
|
+
assert_equal 1, @tube.stats.pause
|
|
198
|
+
end
|
|
199
|
+
end # pause
|
|
200
|
+
|
|
201
|
+
describe "for #stats" do
|
|
202
|
+
before do
|
|
203
|
+
@time = Time.now.to_i
|
|
204
|
+
@tube.put "foo stats #{@time}"
|
|
205
|
+
@stats = @tube.stats
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "should return total number of jobs in tube" do
|
|
209
|
+
assert_equal 1, @stats['current_jobs_ready']
|
|
210
|
+
assert_equal 0, @stats.current_jobs_delayed
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "should raise error for empty tube" do
|
|
214
|
+
assert_raises(Tuber::NotFoundError) { @beanstalk.tubes.find('fake_tube').stats }
|
|
215
|
+
end
|
|
216
|
+
end # stats
|
|
217
|
+
|
|
218
|
+
describe "for #kick" do
|
|
219
|
+
before do
|
|
220
|
+
@time, @time2 = 2.times.map { Time.now.to_i }
|
|
221
|
+
@tube.put "kick #{@time}"
|
|
222
|
+
@tube.put "kick #{@time2}"
|
|
223
|
+
|
|
224
|
+
2.times.map { @tube.reserve.bury }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "should kick 2 buried jobs" do
|
|
228
|
+
assert_equal 2, @tube.stats.current_jobs_buried
|
|
229
|
+
@tube.kick(2)
|
|
230
|
+
assert_equal 0, @tube.stats.current_jobs_buried
|
|
231
|
+
assert_equal 2, @tube.stats.current_jobs_ready
|
|
232
|
+
end
|
|
233
|
+
end # kick
|
|
234
|
+
|
|
235
|
+
describe "for #clear" do
|
|
236
|
+
@time = Time.now.to_i
|
|
237
|
+
before do
|
|
238
|
+
2.times { |i| @tube.put "to clear success #{i} #{@time}" }
|
|
239
|
+
2.times { |i| @tube.put "to clear delayed #{i} #{@time}", :delay => 5 }
|
|
240
|
+
2.times { |i| @tube.put "to clear bury #{i} #{@time}", :pri => 1 }
|
|
241
|
+
@tube.reserve.bury while @tube.peek(:ready).stats['pri'] == 1
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "should clear all jobs in tube" do
|
|
245
|
+
tube_counts = lambda { %w(ready buried delayed).map { |s| @tube.stats["current_jobs_#{s}"] } }
|
|
246
|
+
assert_equal [2, 2, 2], tube_counts.call
|
|
247
|
+
@tube.clear
|
|
248
|
+
@tube.stats
|
|
249
|
+
assert_equal [0, 0, 0], tube_counts.call
|
|
250
|
+
end
|
|
251
|
+
end # clear
|
|
252
|
+
describe "for #flush" do
|
|
253
|
+
before do
|
|
254
|
+
require_tuber!
|
|
255
|
+
@tube = Tuber::Tube.new(@beanstalk, 'flush_test')
|
|
256
|
+
3.times { |i| @tube.put "flush job #{i}" }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should flush all jobs and return count" do
|
|
260
|
+
count = @tube.flush
|
|
261
|
+
assert_equal 3, count
|
|
262
|
+
assert_nil @tube.peek(:ready)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should return 0 for empty tube" do
|
|
266
|
+
@tube.flush
|
|
267
|
+
count = @tube.flush
|
|
268
|
+
assert_equal 0, count
|
|
269
|
+
end
|
|
270
|
+
end # flush
|
|
271
|
+
|
|
272
|
+
describe "for #flush_buried" do
|
|
273
|
+
before do
|
|
274
|
+
require_tuber!
|
|
275
|
+
@tube = Tuber::Tube.new(@beanstalk, 'flush_buried_test')
|
|
276
|
+
@beanstalk.tubes.watch! 'flush_buried_test'
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
after do
|
|
280
|
+
@tube.clear
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "should flush only buried jobs and return count" do
|
|
284
|
+
2.times { |i| @tube.put "buried job #{i}" }
|
|
285
|
+
2.times { @beanstalk.tubes.reserve.bury }
|
|
286
|
+
@tube.put "ready job"
|
|
287
|
+
|
|
288
|
+
count = @tube.flush_buried
|
|
289
|
+
assert_equal 2, count
|
|
290
|
+
assert_nil @tube.peek(:buried)
|
|
291
|
+
refute_nil @tube.peek(:ready)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it "should return 0 when there are no buried jobs" do
|
|
295
|
+
@tube.put "ready only"
|
|
296
|
+
assert_equal 0, @tube.flush_buried
|
|
297
|
+
end
|
|
298
|
+
end # flush_buried
|
|
299
|
+
end # Tuber::Tube
|