geary 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.markdown +76 -0
- data/bin/geary +9 -0
- data/lib/gearman/client.rb +80 -0
- data/lib/gearman/connection.rb +132 -0
- data/lib/gearman/error.rb +7 -0
- data/lib/gearman/packet.rb +9 -0
- data/lib/gearman/packet/repository.rb +35 -0
- data/lib/gearman/packet/sugar.rb +103 -0
- data/lib/gearman/worker.rb +61 -0
- data/lib/geary.rb +8 -0
- data/lib/geary/cli.rb +86 -0
- data/lib/geary/configuration.rb +18 -0
- data/lib/geary/error.rb +7 -0
- data/lib/geary/manager.rb +84 -0
- data/lib/geary/option_parser.rb +42 -0
- data/lib/geary/performer.rb +73 -0
- data/lib/geary/railtie.rb +9 -0
- data/lib/geary/worker.rb +49 -0
- data/spec/gearman/client_spec.rb +36 -0
- data/spec/gearman/connection_spec.rb +67 -0
- data/spec/gearman/packet/sugar_spec.rb +58 -0
- data/spec/gearman/packet_spec.rb +22 -0
- data/spec/gearman/worker_spec.rb +68 -0
- data/spec/geary/cli_spec.rb +40 -0
- data/spec/geary/manager_spec.rb +123 -0
- data/spec/geary/option_parser_spec.rb +17 -0
- data/spec/geary/performer_spec.rb +128 -0
- data/spec/geary/worker_spec.rb +23 -0
- metadata +222 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'gearman/client'
|
2
|
+
require 'support/fake_server'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Gearman
|
6
|
+
describe Client do
|
7
|
+
|
8
|
+
let!(:address) { URI('gearman://127.0.0.1:4730') }
|
9
|
+
let!(:gearmand) { FakeServer.new(address) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
gearmand.async.run
|
13
|
+
gearmand.wait :accept
|
14
|
+
end
|
15
|
+
|
16
|
+
after { gearmand.shutdown }
|
17
|
+
|
18
|
+
it 'submits background jobs' do
|
19
|
+
gearmand.respond_with(Packet::JOB_CREATED.new(['handle']))
|
20
|
+
|
21
|
+
expected_packet = Packet::SUBMIT_JOB_BG.new(
|
22
|
+
function_name: 'super_ability',
|
23
|
+
unique_id: 'UUID',
|
24
|
+
data: 'data'
|
25
|
+
)
|
26
|
+
|
27
|
+
client = Client.new(address)
|
28
|
+
client.generate_unique_id_with -> { 'UUID' }
|
29
|
+
|
30
|
+
client.submit_job_bg('super_ability', 'data')
|
31
|
+
|
32
|
+
expect(gearmand.packets_read.last).to eql(expected_packet)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'gearman/connection'
|
2
|
+
require 'support/fake_server'
|
3
|
+
require 'support/without_logging'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Gearman
|
7
|
+
|
8
|
+
describe Connection do
|
9
|
+
include WithoutLogging
|
10
|
+
|
11
|
+
let!(:address) { URI('gearman://127.0.0.1:4730') }
|
12
|
+
let!(:server) { FakeServer.new(address) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
server.async.run
|
16
|
+
server.wait :accept
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
server.shutdown
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can write packets to a socket' do
|
24
|
+
connection = Connection.new(address)
|
25
|
+
connection.write(Gearman::Packet::WORK_COMPLETE.new(['*', '*']))
|
26
|
+
|
27
|
+
expect(server.packets_read.last).
|
28
|
+
to eql(Gearman::Packet::WORK_COMPLETE.new(['*', '*']))
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'can read packets from a socket' do
|
32
|
+
server.respond_with(Gearman::Packet::NO_JOB.new)
|
33
|
+
|
34
|
+
connection = Connection.new(address)
|
35
|
+
connection.write(Gearman::Packet::GRAB_JOB.new)
|
36
|
+
|
37
|
+
expect(connection.next).to eql(Gearman::Packet::NO_JOB.new)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can specify that it expects only certain types of packets' do
|
41
|
+
without_logging do
|
42
|
+
server.respond_with(Gearman::Packet::JOB_ASSIGN.new([1] * 3))
|
43
|
+
|
44
|
+
connection = Connection.new(address)
|
45
|
+
connection.write(Gearman::Packet::GRAB_JOB.new)
|
46
|
+
|
47
|
+
expect do
|
48
|
+
connection.next(Gearman::Packet::NO_JOB)
|
49
|
+
end.to raise_error(Connection::UnexpectedPacketError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'will raise a ServerError if it reads an error packet' do
|
54
|
+
without_logging do
|
55
|
+
server.respond_with(Gearman::Packet::ERROR.new(["E", "T"]))
|
56
|
+
|
57
|
+
connection = Connection.new(address)
|
58
|
+
connection.write(Gearman::Packet::GRAB_JOB.new)
|
59
|
+
|
60
|
+
expect do
|
61
|
+
connection.next(Gearman::Packet::NO_JOB)
|
62
|
+
end.to raise_error(Connection::ServerError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'gearman/packet/sugar'
|
2
|
+
|
3
|
+
module Gearman
|
4
|
+
module Packet
|
5
|
+
|
6
|
+
describe Sugar do
|
7
|
+
|
8
|
+
it 'allows classes to create initializers which accept options' do
|
9
|
+
class_ = Class.new { extend Sugar ; takes(:foo, :bar) }
|
10
|
+
object = class_.new(foo: 'foo', bar: 'bar')
|
11
|
+
|
12
|
+
expect(object.foo).to eql('foo')
|
13
|
+
expect(object.bar).to eql('bar')
|
14
|
+
expect(object.arguments).to eql(['foo', 'bar'])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'allows classes to create initializers which accept positional arguments' do
|
18
|
+
class_ = Class.new { extend Sugar ; takes(:foo, :bar) }
|
19
|
+
object = class_.new(['foo', 'bar'])
|
20
|
+
|
21
|
+
expect(object.foo).to eql('foo')
|
22
|
+
expect(object.bar).to eql('bar')
|
23
|
+
expect(object.arguments).to eql(['foo', 'bar'])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'raises an ArgumentError if given something other than a Hash or an Array' do
|
27
|
+
class_ = Class.new { extend Sugar ; takes(:foo, :bar) }
|
28
|
+
|
29
|
+
expect do
|
30
|
+
object = class_.new(1)
|
31
|
+
end.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows classes to set "numbers" for their instances' do
|
35
|
+
class_ = Class.new { extend Sugar ; number 1 }
|
36
|
+
object = class_.new
|
37
|
+
|
38
|
+
expect(object.number).to eql(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'can create new packet types with ease' do
|
42
|
+
type = Sugar.type 'CanDo', takes: [:function_name], number: 1
|
43
|
+
|
44
|
+
expect(type.new(['foo']).function_name).to eql('foo')
|
45
|
+
expect(type.new(function_name: 'foo').function_name).to eql('foo')
|
46
|
+
expect(type.new(function_name: 'foo').number).to eql(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'creates packets with value equality' do
|
50
|
+
type = Sugar.type 'CanDo', takes: [:function_name], number: 1
|
51
|
+
|
52
|
+
expect(type.new(['foo'])).to eql(type.new(['foo']))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'gearman/packet'
|
2
|
+
|
3
|
+
module Gearman
|
4
|
+
describe Packet do
|
5
|
+
|
6
|
+
before do
|
7
|
+
Packet::Repository.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'contains a bunch of packet types' do
|
11
|
+
%w(CAN_DO PRE_SLEEP NOOP GRAB_JOB NO_JOB JOB_ASSIGN
|
12
|
+
WORK_COMPLETE WORK_EXCEPTION).each do |type|
|
13
|
+
type = Gearman::Packet.const_get(type)
|
14
|
+
arguments = type.const_get('ARGUMENTS').map { 'foo ' }
|
15
|
+
|
16
|
+
expect do
|
17
|
+
type.new(arguments)
|
18
|
+
end.to_not raise_error
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'gearman/worker'
|
2
|
+
require 'support/actor_double'
|
3
|
+
|
4
|
+
module Gearman
|
5
|
+
|
6
|
+
class FakeConnection
|
7
|
+
include Celluloid
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Worker do
|
11
|
+
include ActorDouble
|
12
|
+
|
13
|
+
let!(:connection) { actor_double }
|
14
|
+
|
15
|
+
it 'expects a NOOP after it sends PRE_SLEEP' do
|
16
|
+
connection.stub(:write)
|
17
|
+
connection.should_receive(:next).with(Packet::NOOP)
|
18
|
+
|
19
|
+
worker = Worker.new('gearman://localhost:4730')
|
20
|
+
worker.configure_connection ->(address) { connection }
|
21
|
+
|
22
|
+
worker.pre_sleep
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'expects either a JOB_ASSIGN or a NO_JOB when it grabs a job' do
|
26
|
+
connection.stub(:write)
|
27
|
+
connection.should_receive(:next).with(Packet::JOB_ASSIGN, Packet::NO_JOB)
|
28
|
+
|
29
|
+
worker = Worker.new('gearman://localhost:4730')
|
30
|
+
worker.configure_connection ->(address) { connection }
|
31
|
+
|
32
|
+
worker.grab_job
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'sends WORK_EXCEPTION' do
|
36
|
+
work_exception = Packet::WORK_EXCEPTION.new(handle: 'h', data: 'd')
|
37
|
+
connection.should_receive(:write).with(work_exception)
|
38
|
+
|
39
|
+
worker = Worker.new('gearman://localhost:4730')
|
40
|
+
worker.configure_connection ->(address) { connection }
|
41
|
+
|
42
|
+
worker.work_exception('h', 'd')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sends WORK_COMPLETE' do
|
46
|
+
work_complete = Packet::WORK_COMPLETE.new(handle: 'h', data: 'd')
|
47
|
+
connection.should_receive(:write).with(work_complete)
|
48
|
+
|
49
|
+
worker = Worker.new('gearman://localhost:4730')
|
50
|
+
worker.configure_connection ->(address) { connection }
|
51
|
+
|
52
|
+
worker.work_complete('h', 'd')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sends CAN_DO' do
|
56
|
+
can_do = Packet::CAN_DO.new(function_name: 'ability')
|
57
|
+
|
58
|
+
connection.should_receive(:write).with(can_do)
|
59
|
+
|
60
|
+
worker = Worker.new('gearman://localhost:4730')
|
61
|
+
worker.configure_connection ->(address) { connection }
|
62
|
+
|
63
|
+
worker.can_do('ability')
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'geary/cli'
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
module Geary
|
7
|
+
describe CLI do
|
8
|
+
|
9
|
+
it 'shuts down when sent TERM' do
|
10
|
+
kernel = double('kernel')
|
11
|
+
kernel.should_receive(:exit)
|
12
|
+
argv = ['-c 0']
|
13
|
+
cli = CLI.new(argv, STDOUT, STDERR, kernel)
|
14
|
+
|
15
|
+
t = Thread.new { cli.execute! }
|
16
|
+
t.abort_on_exception = true
|
17
|
+
|
18
|
+
cli.external_signal_queue.puts('TERM')
|
19
|
+
|
20
|
+
Timeout.timeout(1, StandardError) { t.value } rescue nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'shuts down when sent INT' do
|
24
|
+
kernel = double('kernel')
|
25
|
+
kernel.should_receive(:exit)
|
26
|
+
|
27
|
+
argv = ['-c 0']
|
28
|
+
cli = CLI.new(argv, STDOUT, STDERR, kernel)
|
29
|
+
|
30
|
+
t = Thread.new { cli.execute! }
|
31
|
+
t.abort_on_exception = true
|
32
|
+
|
33
|
+
IO.select([], [cli.external_signal_queue])
|
34
|
+
cli.external_signal_queue.puts('INT')
|
35
|
+
|
36
|
+
Timeout.timeout(1, StandardError) { t.value } rescue nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'gearmand_control'
|
2
|
+
|
3
|
+
require 'geary/configuration'
|
4
|
+
require 'geary/manager'
|
5
|
+
|
6
|
+
require 'support/fake_performer'
|
7
|
+
require 'support/with_tolerance'
|
8
|
+
require 'support/without_logging'
|
9
|
+
|
10
|
+
module Geary
|
11
|
+
|
12
|
+
describe Manager do
|
13
|
+
include WithTolerance
|
14
|
+
include WithoutLogging
|
15
|
+
|
16
|
+
let(:configuration) do
|
17
|
+
configuration = Configuration.new(
|
18
|
+
server_addresses: ['gearman://localhost:4730'],
|
19
|
+
concurrency: 2
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'starting the manager' do
|
24
|
+
|
25
|
+
it 'establishes a link to managed performers' do
|
26
|
+
manager = Manager.new(configuration: configuration,
|
27
|
+
performer_type: FakePerformer)
|
28
|
+
manager.start
|
29
|
+
|
30
|
+
expect(manager.links.count).to eql(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'starts each managed performer' do
|
34
|
+
manager = Manager.new(configuration: configuration,
|
35
|
+
performer_type: FakePerformer)
|
36
|
+
manager.start
|
37
|
+
|
38
|
+
expect(manager.links.all?(&:started?)).to be_true
|
39
|
+
expect(manager.links.map(&:server_address).uniq).
|
40
|
+
to eql(configuration.server_addresses)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'performer supervision' do
|
46
|
+
|
47
|
+
it 'restarts performers when they die' do
|
48
|
+
without_logging do
|
49
|
+
manager = Manager.new(configuration: configuration,
|
50
|
+
performer_type: FakePerformer)
|
51
|
+
manager.start
|
52
|
+
|
53
|
+
imminently_dead_performers = manager.performers
|
54
|
+
imminently_dead_performers.map(&:async).each(&:die)
|
55
|
+
|
56
|
+
with_tolerance do
|
57
|
+
expect(imminently_dead_performers.count(&:alive?)).to eql(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
with_tolerance do
|
61
|
+
expect(manager.links.count).to eql(2)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'forgets performers if they die without a reason' do
|
67
|
+
without_logging do
|
68
|
+
manager = Manager.new(configuration: configuration,
|
69
|
+
performer_type: FakePerformer)
|
70
|
+
manager.start
|
71
|
+
|
72
|
+
forgettable_performers = manager.performers
|
73
|
+
forgettable_performers.map(&:async).each(&:die_quietly)
|
74
|
+
|
75
|
+
with_tolerance do
|
76
|
+
expect(forgettable_performers.count(&:alive?)).to eql(0)
|
77
|
+
end
|
78
|
+
|
79
|
+
with_tolerance do
|
80
|
+
expect(manager.links.count).to eql(0)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'ceasing management' do
|
88
|
+
|
89
|
+
it 'terminates linked performers' do
|
90
|
+
manager = Manager.new(configuration: configuration,
|
91
|
+
performer_type: FakePerformer)
|
92
|
+
manager.start
|
93
|
+
|
94
|
+
performers = manager.performers
|
95
|
+
|
96
|
+
expect do
|
97
|
+
manager.stop
|
98
|
+
end.to change { performers.count(&:alive?) }.from(2).to(0)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'unlinks linked performers' do
|
102
|
+
manager = Manager.new(configuration: configuration,
|
103
|
+
performer_type: FakePerformer)
|
104
|
+
manager.start
|
105
|
+
|
106
|
+
expect do
|
107
|
+
manager.stop
|
108
|
+
end.to change { manager.links.count }.from(2).to(0)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'signals that it has stopped' do
|
112
|
+
manager = Manager.new(configuration: configuration,
|
113
|
+
performer_type: FakePerformer)
|
114
|
+
manager.async.start
|
115
|
+
manager.async.stop
|
116
|
+
|
117
|
+
expect(manager.wait :done).to be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'geary/option_parser'
|
2
|
+
|
3
|
+
module Geary
|
4
|
+
describe OptionParser do
|
5
|
+
|
6
|
+
it 'understands comma-delimited servers to mean multiple servers' do
|
7
|
+
args = ['-s', 'gearman://localhost:4730,gearman://localhost:4731']
|
8
|
+
parser = OptionParser.new
|
9
|
+
|
10
|
+
configuration = parser.parse(args)
|
11
|
+
|
12
|
+
expect(configuration.server_addresses.map(&:to_s)).
|
13
|
+
to eql(['gearman://localhost:4730', 'gearman://localhost:4731'])
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'geary/performer'
|
2
|
+
require 'support/actor_double'
|
3
|
+
require 'support/without_logging'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Geary
|
7
|
+
describe Performer do
|
8
|
+
include ActorDouble
|
9
|
+
include WithoutLogging
|
10
|
+
|
11
|
+
let(:address) do
|
12
|
+
URI('gearman://localhost:4730')
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:gearman) { actor_double }
|
16
|
+
|
17
|
+
it 'registers its ability with Gearman' do
|
18
|
+
gearman.stub(:grab_job)
|
19
|
+
gearman.should_receive(:can_do)
|
20
|
+
|
21
|
+
performer = Performer.new(address)
|
22
|
+
performer.configure_connection ->(address) { gearman }
|
23
|
+
|
24
|
+
performer.start
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'tries to pop a job off the queue' do
|
28
|
+
gearman.stub(:can_do)
|
29
|
+
gearman.should_receive(:grab_job)
|
30
|
+
|
31
|
+
performer = Performer.new(address)
|
32
|
+
performer.configure_connection ->(address) { gearman }
|
33
|
+
|
34
|
+
performer.start
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'sleeps if it gets a NO_JOB' do
|
38
|
+
gearman.stub(:can_do)
|
39
|
+
gearman.stub(:grab_job).and_return(Gearman::Packet::NO_JOB.new, nil)
|
40
|
+
gearman.should_receive(:pre_sleep)
|
41
|
+
|
42
|
+
performer = Performer.new(address)
|
43
|
+
performer.configure_connection ->(address) { gearman }
|
44
|
+
|
45
|
+
performer.start
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'performs the job if it gets a JOB_ASSIGN' do
|
49
|
+
worker_class = Class.new { }
|
50
|
+
worker = worker_class.any_instance
|
51
|
+
|
52
|
+
Object.const_set('A', worker_class)
|
53
|
+
|
54
|
+
job = JSON.dump({
|
55
|
+
class: 'A',
|
56
|
+
args: ['a']
|
57
|
+
})
|
58
|
+
|
59
|
+
job_assign = Gearman::Packet::JOB_ASSIGN.new(['h', 'f', job])
|
60
|
+
|
61
|
+
gearman.stub(:can_do)
|
62
|
+
gearman.stub(:grab_job).and_return(job_assign, nil)
|
63
|
+
gearman.stub(:work_complete)
|
64
|
+
|
65
|
+
worker.should_receive(:perform).with('a')
|
66
|
+
|
67
|
+
performer = Performer.new(address)
|
68
|
+
performer.configure_connection ->(address) { gearman }
|
69
|
+
|
70
|
+
performer.start
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'sends the result of a job to Gearman' do
|
74
|
+
worker_class = Class.new { }
|
75
|
+
worker = worker_class.any_instance
|
76
|
+
worker.stub(:perform) { 'result' }
|
77
|
+
|
78
|
+
Object.const_set('B', worker_class)
|
79
|
+
|
80
|
+
job = JSON.dump({
|
81
|
+
class: 'B',
|
82
|
+
args: ['a']
|
83
|
+
})
|
84
|
+
|
85
|
+
async_proxy = double('gearman.async')
|
86
|
+
job_assign = Gearman::Packet::JOB_ASSIGN.new(['h', 'f', job])
|
87
|
+
|
88
|
+
gearman.stub(:can_do)
|
89
|
+
gearman.stub(:grab_job).and_return(job_assign, nil)
|
90
|
+
gearman.stub(:async) { async_proxy }
|
91
|
+
async_proxy.should_receive(:work_complete).with('h', 'result')
|
92
|
+
|
93
|
+
performer = Performer.new(address)
|
94
|
+
performer.configure_connection ->(address) { gearman }
|
95
|
+
|
96
|
+
performer.start
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'sends a WORK_EXCEPTION to Gearman if the job raises' do
|
100
|
+
worker_class = Class.new { }
|
101
|
+
worker = worker_class.any_instance
|
102
|
+
worker.stub(:perform).and_raise RuntimeError, "ack!"
|
103
|
+
|
104
|
+
Object.const_set('C', worker_class)
|
105
|
+
|
106
|
+
job = JSON.dump({
|
107
|
+
class: 'C',
|
108
|
+
args: ['a']
|
109
|
+
})
|
110
|
+
|
111
|
+
async_proxy = double('gearman.async')
|
112
|
+
job_assign = Gearman::Packet::JOB_ASSIGN.new(['h', 'f', job])
|
113
|
+
|
114
|
+
gearman.stub(:can_do)
|
115
|
+
gearman.stub(:grab_job).and_return(job_assign, nil)
|
116
|
+
gearman.stub(:async) { async_proxy }
|
117
|
+
async_proxy.should_receive(:work_exception).with('h', 'ack!')
|
118
|
+
|
119
|
+
performer = Performer.new(address)
|
120
|
+
performer.configure_connection ->(address) { gearman }
|
121
|
+
|
122
|
+
performer.start
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'can repair its connection to Gearman'
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|