service_skeleton 1.0.1 → 2.0.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/.git-blame-ignore-revs +2 -0
- data/.github/workflows/ci.yml +50 -0
- data/.gitignore +0 -7
- data/.rubocop.yml +11 -1
- data/README.md +1 -53
- data/lib/service_skeleton/config.rb +20 -13
- data/lib/service_skeleton/generator.rb +4 -4
- data/lib/service_skeleton/runner.rb +3 -3
- data/lib/service_skeleton/ultravisor_children.rb +2 -1
- data/lib/service_skeleton/ultravisor_loggerstash.rb +9 -1
- data/service_skeleton.gemspec +4 -14
- data/ultravisor/.yardopts +1 -0
- data/ultravisor/Guardfile +9 -0
- data/ultravisor/README.md +404 -0
- data/ultravisor/lib/ultravisor.rb +216 -0
- data/ultravisor/lib/ultravisor/child.rb +485 -0
- data/ultravisor/lib/ultravisor/child/call.rb +21 -0
- data/ultravisor/lib/ultravisor/child/call_receiver.rb +14 -0
- data/ultravisor/lib/ultravisor/child/cast.rb +16 -0
- data/ultravisor/lib/ultravisor/child/cast_receiver.rb +11 -0
- data/ultravisor/lib/ultravisor/child/process_cast_call.rb +39 -0
- data/ultravisor/lib/ultravisor/error.rb +25 -0
- data/ultravisor/lib/ultravisor/logging_helpers.rb +32 -0
- data/ultravisor/spec/example_group_methods.rb +19 -0
- data/ultravisor/spec/example_methods.rb +8 -0
- data/ultravisor/spec/spec_helper.rb +56 -0
- data/ultravisor/spec/ultravisor/add_child_spec.rb +79 -0
- data/ultravisor/spec/ultravisor/child/call_spec.rb +121 -0
- data/ultravisor/spec/ultravisor/child/cast_spec.rb +111 -0
- data/ultravisor/spec/ultravisor/child/id_spec.rb +21 -0
- data/ultravisor/spec/ultravisor/child/new_spec.rb +152 -0
- data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +40 -0
- data/ultravisor/spec/ultravisor/child/restart_spec.rb +70 -0
- data/ultravisor/spec/ultravisor/child/run_spec.rb +95 -0
- data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +124 -0
- data/ultravisor/spec/ultravisor/child/spawn_spec.rb +216 -0
- data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +55 -0
- data/ultravisor/spec/ultravisor/child/wait_spec.rb +32 -0
- data/ultravisor/spec/ultravisor/new_spec.rb +71 -0
- data/ultravisor/spec/ultravisor/remove_child_spec.rb +49 -0
- data/ultravisor/spec/ultravisor/run_spec.rb +334 -0
- data/ultravisor/spec/ultravisor/shutdown_spec.rb +106 -0
- metadata +48 -64
- data/.travis.yml +0 -11
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
require_relative "../../../lib/ultravisor/error"
|
7
|
+
|
8
|
+
class CastCallTest
|
9
|
+
def run
|
10
|
+
end
|
11
|
+
|
12
|
+
def poke_processor
|
13
|
+
process_castcall
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Ultravisor::Child do
|
18
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
19
|
+
let(:instance) { child.__send__(:new_instance) }
|
20
|
+
|
21
|
+
describe "#cast" do
|
22
|
+
context "without enable_castcall" do
|
23
|
+
let(:args) do
|
24
|
+
{
|
25
|
+
id: :cast_child,
|
26
|
+
klass: CastCallTest,
|
27
|
+
method: :run,
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "does not accept calls to #cast" do
|
32
|
+
expect { child.cast }.to raise_error(NoMethodError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with enable_castcall" do
|
37
|
+
let(:args) do
|
38
|
+
{
|
39
|
+
id: :cast_child,
|
40
|
+
klass: CastCallTest,
|
41
|
+
method: :run,
|
42
|
+
enable_castcall: true,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
# Got to have an instance otherwise all hell breaks loose
|
48
|
+
child.instance_variable_set(:@instance, instance)
|
49
|
+
|
50
|
+
# So we can check if and when it's been called
|
51
|
+
allow(instance).to receive(:to_s).and_call_original
|
52
|
+
end
|
53
|
+
|
54
|
+
it "accepts calls to #cast" do
|
55
|
+
expect { child.cast }.to_not raise_error
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does not accept cast calls to methods that do not exist on the worker object" do
|
59
|
+
expect { child.cast.flibbetygibbets }.to raise_error(NoMethodError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "does not accept cast calls to private methods on the worker object" do
|
63
|
+
expect { child.cast.eval }.to raise_error(NoMethodError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "accepts calls to methods that exist on the worker object" do
|
67
|
+
expect { child.cast.to_s }.to_not raise_error
|
68
|
+
end
|
69
|
+
|
70
|
+
it "calls the instance method only when process_castcall is called" do
|
71
|
+
child.cast.to_s
|
72
|
+
expect(instance).to_not have_received(:to_s)
|
73
|
+
instance.poke_processor
|
74
|
+
expect(instance).to have_received(:to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "processes all the queued method calls" do
|
78
|
+
child.cast.to_s
|
79
|
+
child.cast.to_s
|
80
|
+
child.cast.to_s
|
81
|
+
child.cast.to_s
|
82
|
+
child.cast.to_s
|
83
|
+
expect(instance).to_not have_received(:to_s)
|
84
|
+
instance.poke_processor
|
85
|
+
expect(instance).to have_received(:to_s).exactly(5).times
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:cc_fd) { instance.__send__(:castcall_fd) }
|
89
|
+
it "marks the castcall_fd as readable only after cast is called" do
|
90
|
+
expect(IO.select([cc_fd], nil, nil, 0)).to eq(nil)
|
91
|
+
|
92
|
+
child.cast.to_s
|
93
|
+
|
94
|
+
expect(IO.select([cc_fd], nil, nil, 0)).to eq([[cc_fd], [], []])
|
95
|
+
end
|
96
|
+
|
97
|
+
it "does not have a readable castcall_fd after process_castcall" do
|
98
|
+
child.cast.to_s
|
99
|
+
expect(IO.select([cc_fd], nil, nil, 0)).to eq([[cc_fd], [], []])
|
100
|
+
instance.poke_processor
|
101
|
+
expect(IO.select([cc_fd], nil, nil, 0)).to eq(nil)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "does not explode if the instance is dying" do
|
105
|
+
instance.instance_variable_get(:@ultravisor_child_castcall_queue).close
|
106
|
+
|
107
|
+
expect { child.cast.to_s }.to_not raise_error
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
require_relative "../../../lib/ultravisor/error"
|
7
|
+
|
8
|
+
describe Ultravisor::Child do
|
9
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
10
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
|
11
|
+
|
12
|
+
describe "#id" do
|
13
|
+
context "with minimal arguments" do
|
14
|
+
let(:args) { { id: :bob, klass: mock_class, method: :run } }
|
15
|
+
|
16
|
+
it "returns the child's ID" do
|
17
|
+
expect(child.id).to eq(:bob)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
require_relative "../../../lib/ultravisor/error"
|
7
|
+
|
8
|
+
describe Ultravisor::Child do
|
9
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
10
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end; def sigterm; end } } }
|
11
|
+
|
12
|
+
describe ".new" do
|
13
|
+
context "with minimal arguments" do
|
14
|
+
let(:args) { { id: :bob, klass: mock_class, method: :run } }
|
15
|
+
|
16
|
+
it "does not explode" do
|
17
|
+
expect { Ultravisor::Child.new(**args) }.to_not raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "does not instantiate an instance of klass" do
|
21
|
+
expect(mock_class).to_not receive(:new)
|
22
|
+
|
23
|
+
Ultravisor::Child.new(**args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with defined args" do
|
28
|
+
let(:args) { { id: :bob, klass: mock_class, args: [foo: "bar"], method: :run } }
|
29
|
+
|
30
|
+
it "explodes" do
|
31
|
+
expect { Ultravisor::Child.new(**args) }.to raise_error(Ultravisor::InvalidKAMError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with a class that takes args" do
|
36
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def initialize(*x); end } } }
|
37
|
+
let(:args) { { id: :testy, klass: mock_class, args: [1, 2, 3], method: :to_s } }
|
38
|
+
|
39
|
+
it "doesn't explode" do
|
40
|
+
expect { Ultravisor::Child.new(**args) }.to_not raise_error
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with a non-existent method" do
|
45
|
+
let(:args) { { id: :testy, klass: mock_class, method: :gogogo } }
|
46
|
+
|
47
|
+
it "explodes" do
|
48
|
+
expect { Ultravisor::Child.new(**args) }.to raise_error(Ultravisor::InvalidKAMError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with a method that takes args" do
|
53
|
+
let(:args) { { id: :testy, klass: mock_class, method: :public_send } }
|
54
|
+
|
55
|
+
it "explodes" do
|
56
|
+
expect { Ultravisor::Child.new(**args) }.to raise_error(Ultravisor::InvalidKAMError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with a valid restart value" do
|
61
|
+
it "is fine" do
|
62
|
+
%i{always on_failure never}.each do |v|
|
63
|
+
expect { Ultravisor::Child.new(id: :x, klass: Object, method: :to_s, restart: v) }.to_not raise_error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with an invalid restart value" do
|
69
|
+
it "explodes" do
|
70
|
+
[:sometimes, "always", 42, { max: 4 }].each do |v|
|
71
|
+
expect { Ultravisor::Child.new(id: :x, klass: Object, method: :to_s, restart: v) }.to raise_error(ArgumentError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "with a valid restart_policy" do
|
77
|
+
it "is happy" do
|
78
|
+
expect do
|
79
|
+
Ultravisor::Child.new(id: :rp, klass: Object, method: :to_s, restart_policy: { period: 5, max: 2, delay: 1 })
|
80
|
+
end.to_not raise_error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
[
|
85
|
+
{ when: :never },
|
86
|
+
{ period: -1 },
|
87
|
+
{ period: "never" },
|
88
|
+
{ period: :sometimes },
|
89
|
+
{ period: (0..10) },
|
90
|
+
{ max: -1 },
|
91
|
+
{ max: "powers" },
|
92
|
+
{ max: (1..5) },
|
93
|
+
{ delay: -1 },
|
94
|
+
{ delay: "buses" },
|
95
|
+
{ delay: (-1..3) },
|
96
|
+
{ delay: (3..1) },
|
97
|
+
"whenever you're ready",
|
98
|
+
].each do |p|
|
99
|
+
it "explodes with invalid restart_policy #{p.inspect}" do
|
100
|
+
expect do
|
101
|
+
Ultravisor::Child.new(id: :boom, klass: Object, method: :to_s, restart_policy: p)
|
102
|
+
end.to raise_error(ArgumentError)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "with a valid shutdown spec" do
|
107
|
+
it "is happy" do
|
108
|
+
expect do
|
109
|
+
Ultravisor::Child.new(id: :rp, klass: mock_class, method: :to_s, shutdown: { method: :sigterm, timeout: 2 })
|
110
|
+
end.to_not raise_error
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
[
|
115
|
+
{ method: "man" },
|
116
|
+
{ method: :send },
|
117
|
+
{ method: :nonexistent_method },
|
118
|
+
{ timeout: -4 },
|
119
|
+
{ timeout: (3..5) },
|
120
|
+
{ timeout: "MC Hammer" },
|
121
|
+
{ woogiewoogie: "boo!" },
|
122
|
+
"freddie!",
|
123
|
+
].each do |s|
|
124
|
+
it "explodes with invalid shutdown spec #{s.inspect}" do
|
125
|
+
expect do
|
126
|
+
Ultravisor::Child.new(id: :boom, klass: Object, method: :to_s, shutdown: s)
|
127
|
+
end.to raise_error(ArgumentError)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "with castcall enabled" do
|
132
|
+
it "is happy" do
|
133
|
+
expect do
|
134
|
+
Ultravisor::Child.new(id: :castcall, klass: Object, method: :to_s, enable_castcall: true)
|
135
|
+
end.to_not raise_error
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
[
|
140
|
+
:bob,
|
141
|
+
42,
|
142
|
+
"unsafe",
|
143
|
+
{ safe: :un },
|
144
|
+
].each do |a|
|
145
|
+
it "explodes with invalid access spec #{a.inspect}" do
|
146
|
+
expect do
|
147
|
+
Ultravisor::Child.new(id: :boom, klass: Object, method: :to_s, access: a)
|
148
|
+
end.to raise_error(ArgumentError)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
|
7
|
+
describe Ultravisor::Child do
|
8
|
+
let(:base_args) { { id: :bob, klass: mock_class, method: :run } }
|
9
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
10
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
|
11
|
+
|
12
|
+
describe "#restart_delay" do
|
13
|
+
context "by default" do
|
14
|
+
let(:args) { base_args }
|
15
|
+
|
16
|
+
it "returns the default delay" do
|
17
|
+
expect(child.restart_delay).to eq(1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with a specified numeric delay" do
|
22
|
+
let(:args) { base_args.merge(restart_policy: { delay: 3.14159 }) }
|
23
|
+
|
24
|
+
it "returns the specified delay" do
|
25
|
+
expect(child.restart_delay).to be_within(0.00001).of(3.14159)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with a delay range" do
|
30
|
+
let(:args) { base_args.merge(restart_policy: { delay: 2..5 }) }
|
31
|
+
|
32
|
+
it "returns a delay in the given range" do
|
33
|
+
delays = 10.times.map { child.restart_delay }
|
34
|
+
|
35
|
+
expect(delays.all? { |d| (2..5).include?(d) }).to be(true)
|
36
|
+
expect(delays.uniq.length).to eq(10)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
require_relative "../../../lib/ultravisor/error"
|
7
|
+
|
8
|
+
describe Ultravisor::Child do
|
9
|
+
let(:base_args) { { id: :bob, klass: Object, method: :to_s } }
|
10
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
11
|
+
|
12
|
+
describe "#restart?" do
|
13
|
+
context "when restart: :always" do
|
14
|
+
# Yes, this is the default, but I do like to be explicit
|
15
|
+
let(:args) { base_args.merge(restart: :always) }
|
16
|
+
|
17
|
+
it "is true always" do
|
18
|
+
expect(child.restart?).to be(true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when restart: :never" do
|
23
|
+
let(:args) { base_args.merge(restart: :never) }
|
24
|
+
|
25
|
+
it "is false always" do
|
26
|
+
expect(child.restart?).to be(false)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when restart: :on_failure" do
|
31
|
+
let(:args) { base_args.merge(restart: :on_failure) }
|
32
|
+
|
33
|
+
it "is true if the child terminated with an exception" do
|
34
|
+
expect(child).to receive(:termination_exception).and_return(Exception.new("boom"))
|
35
|
+
|
36
|
+
expect(child.restart?).to be(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "is false if the child didn't terminate with an exception" do
|
40
|
+
expect(child).to receive(:termination_exception).and_return(nil)
|
41
|
+
|
42
|
+
expect(child.restart?).to be(false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with a restart history that isn't blown" do
|
47
|
+
let(:args) { base_args.merge(restart: :always, restart_policy: { period: 10, max: 3 }) }
|
48
|
+
|
49
|
+
before(:each) do
|
50
|
+
child.instance_variable_set(:@runtime_history, [4.99, 4.99, 4.99])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "still returns true" do
|
54
|
+
expect(child.restart?).to be(true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with a restart history that is blown" do
|
59
|
+
let(:args) { base_args.merge(restart: :always, restart_policy: { period: 10, max: 2 }) }
|
60
|
+
|
61
|
+
before(:each) do
|
62
|
+
child.instance_variable_set(:@runtime_history, [4.99, 4.99, 4.99])
|
63
|
+
end
|
64
|
+
|
65
|
+
it "explodes" do
|
66
|
+
expect { child.restart? }.to raise_error(Ultravisor::BlownRestartPolicyError)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
|
5
|
+
require_relative "../../../lib/ultravisor/child"
|
6
|
+
require_relative "../../../lib/ultravisor/error"
|
7
|
+
|
8
|
+
describe Ultravisor::Child do
|
9
|
+
let(:child) { Ultravisor::Child.new(**args) }
|
10
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def run; end } } }
|
11
|
+
let(:mock_instance) { double(mock_class) } # rubocop:disable RSpec/VerifiedDoubles
|
12
|
+
let(:term_queue) { instance_double(Queue) }
|
13
|
+
|
14
|
+
describe "#run" do
|
15
|
+
before(:each) do
|
16
|
+
allow(term_queue).to receive(:<<)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with minimal arguments" do
|
20
|
+
let(:args) { { id: :bob, klass: mock_class, method: :run } }
|
21
|
+
|
22
|
+
before(:each) do
|
23
|
+
allow(mock_class).to receive(:new).and_return(mock_instance)
|
24
|
+
allow(mock_instance).to receive(:run)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "instantiates the class" do
|
28
|
+
expect(mock_class).to receive(:new).with(no_args)
|
29
|
+
|
30
|
+
child.spawn(term_queue).wait
|
31
|
+
end
|
32
|
+
|
33
|
+
it "calls the run method on the class instance" do
|
34
|
+
expect(mock_instance).to receive(:run).with(no_args)
|
35
|
+
|
36
|
+
child.spawn(term_queue).wait
|
37
|
+
end
|
38
|
+
|
39
|
+
it "registers the thread it is running in" do
|
40
|
+
expect(mock_instance).to receive(:run) do
|
41
|
+
expect(child.instance_variable_get(:@thread)).to eq(Thread.current)
|
42
|
+
end
|
43
|
+
|
44
|
+
child.spawn(term_queue).wait
|
45
|
+
end
|
46
|
+
|
47
|
+
it "notes the start time" do
|
48
|
+
expect(mock_instance).to receive(:run) do
|
49
|
+
# Can only check @start_time while the child is running, as the
|
50
|
+
# variable gets nil'd after the run completes
|
51
|
+
expect(child.instance_variable_get(:@start_time).to_f).to be_within(0.01).of(Time.now.to_f)
|
52
|
+
end
|
53
|
+
|
54
|
+
child.spawn(term_queue).wait
|
55
|
+
end
|
56
|
+
|
57
|
+
it "notes the termination value" do
|
58
|
+
expect(mock_instance).to receive(:run).with(no_args).and_return(42)
|
59
|
+
|
60
|
+
child.spawn(term_queue)
|
61
|
+
|
62
|
+
expect(child.termination_value).to eq(42)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "tells the ultravisor it terminated" do
|
66
|
+
expect(term_queue).to receive(:<<).with(child)
|
67
|
+
|
68
|
+
child.spawn(term_queue).wait
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when the worker object's run method raises an exception" do
|
72
|
+
before(:each) do
|
73
|
+
allow(mock_instance).to receive(:run).and_raise(RuntimeError.new("FWACKOOM"))
|
74
|
+
end
|
75
|
+
|
76
|
+
it "makes a note of the exception" do
|
77
|
+
child.spawn(term_queue)
|
78
|
+
|
79
|
+
expect(child.termination_exception).to be_a(RuntimeError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "with a worker class that takes args" do
|
85
|
+
let(:args) { { id: :testy, klass: mock_class, args: ["foo", "bar", baz: "wombat"], method: :run } }
|
86
|
+
let(:mock_class) { Class.new.tap { |k| k.class_eval { def initialize(*x); end; def run; end } } }
|
87
|
+
|
88
|
+
it "creates the class instance with args" do
|
89
|
+
expect(mock_class).to receive(:new).with("foo", "bar", baz: "wombat").and_call_original
|
90
|
+
|
91
|
+
child.spawn(term_queue).wait
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|