service_skeleton 0.0.0.1.ENOTAG → 0.0.0.2.g46c1e0e

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.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.rubocop.yml +114 -9
  4. data/.travis.yml +11 -0
  5. data/README.md +153 -279
  6. data/lib/service_skeleton/background_worker.rb +80 -0
  7. data/lib/service_skeleton/config.rb +18 -78
  8. data/lib/service_skeleton/config_variable.rb +8 -29
  9. data/lib/service_skeleton/config_variables.rb +68 -54
  10. data/lib/service_skeleton/error.rb +3 -5
  11. data/lib/service_skeleton/filtering_logger.rb +0 -2
  12. data/lib/service_skeleton/logging_helpers.rb +3 -10
  13. data/lib/service_skeleton/metrics_methods.rb +13 -28
  14. data/lib/service_skeleton/signal_handler.rb +183 -0
  15. data/lib/service_skeleton.rb +145 -22
  16. data/service_skeleton.gemspec +9 -10
  17. metadata +19 -102
  18. data/.editorconfig +0 -7
  19. data/.git-blame-ignore-revs +0 -2
  20. data/.github/workflows/ci.yml +0 -50
  21. data/lib/service_skeleton/config_class.rb +0 -16
  22. data/lib/service_skeleton/config_variable/boolean.rb +0 -21
  23. data/lib/service_skeleton/config_variable/enum.rb +0 -27
  24. data/lib/service_skeleton/config_variable/float.rb +0 -25
  25. data/lib/service_skeleton/config_variable/integer.rb +0 -25
  26. data/lib/service_skeleton/config_variable/kv_list.rb +0 -26
  27. data/lib/service_skeleton/config_variable/path_list.rb +0 -13
  28. data/lib/service_skeleton/config_variable/string.rb +0 -18
  29. data/lib/service_skeleton/config_variable/url.rb +0 -36
  30. data/lib/service_skeleton/config_variable/yaml_file.rb +0 -42
  31. data/lib/service_skeleton/generator.rb +0 -165
  32. data/lib/service_skeleton/metric_method_name.rb +0 -9
  33. data/lib/service_skeleton/runner.rb +0 -46
  34. data/lib/service_skeleton/service_name.rb +0 -20
  35. data/lib/service_skeleton/signal_manager.rb +0 -202
  36. data/lib/service_skeleton/signals_methods.rb +0 -15
  37. data/lib/service_skeleton/ultravisor_children.rb +0 -20
  38. data/lib/service_skeleton/ultravisor_loggerstash.rb +0 -11
  39. data/ultravisor/.yardopts +0 -1
  40. data/ultravisor/Guardfile +0 -9
  41. data/ultravisor/README.md +0 -404
  42. data/ultravisor/lib/ultravisor/child/call.rb +0 -21
  43. data/ultravisor/lib/ultravisor/child/call_receiver.rb +0 -14
  44. data/ultravisor/lib/ultravisor/child/cast.rb +0 -16
  45. data/ultravisor/lib/ultravisor/child/cast_receiver.rb +0 -11
  46. data/ultravisor/lib/ultravisor/child/process_cast_call.rb +0 -39
  47. data/ultravisor/lib/ultravisor/child.rb +0 -481
  48. data/ultravisor/lib/ultravisor/error.rb +0 -25
  49. data/ultravisor/lib/ultravisor/logging_helpers.rb +0 -32
  50. data/ultravisor/lib/ultravisor.rb +0 -216
  51. data/ultravisor/spec/example_group_methods.rb +0 -19
  52. data/ultravisor/spec/example_methods.rb +0 -8
  53. data/ultravisor/spec/spec_helper.rb +0 -52
  54. data/ultravisor/spec/ultravisor/add_child_spec.rb +0 -79
  55. data/ultravisor/spec/ultravisor/child/call_spec.rb +0 -121
  56. data/ultravisor/spec/ultravisor/child/cast_spec.rb +0 -111
  57. data/ultravisor/spec/ultravisor/child/id_spec.rb +0 -21
  58. data/ultravisor/spec/ultravisor/child/new_spec.rb +0 -152
  59. data/ultravisor/spec/ultravisor/child/restart_delay_spec.rb +0 -40
  60. data/ultravisor/spec/ultravisor/child/restart_spec.rb +0 -70
  61. data/ultravisor/spec/ultravisor/child/run_spec.rb +0 -95
  62. data/ultravisor/spec/ultravisor/child/shutdown_spec.rb +0 -124
  63. data/ultravisor/spec/ultravisor/child/spawn_spec.rb +0 -107
  64. data/ultravisor/spec/ultravisor/child/unsafe_instance_spec.rb +0 -55
  65. data/ultravisor/spec/ultravisor/child/wait_spec.rb +0 -32
  66. data/ultravisor/spec/ultravisor/new_spec.rb +0 -71
  67. data/ultravisor/spec/ultravisor/remove_child_spec.rb +0 -49
  68. data/ultravisor/spec/ultravisor/run_spec.rb +0 -334
  69. data/ultravisor/spec/ultravisor/shutdown_spec.rb +0 -106
@@ -1,216 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "logger"
4
-
5
- require_relative "./ultravisor/child"
6
- require_relative "./ultravisor/error"
7
- require_relative "./ultravisor/logging_helpers"
8
-
9
- # A super-dooOOOoooper supervisor.
10
- #
11
- class Ultravisor
12
- include LoggingHelpers
13
-
14
- def initialize(children: [], strategy: :one_for_one, logger: Logger.new("/dev/null"))
15
- @queue, @logger = Queue.new, logger
16
-
17
- @strategy = strategy
18
- validate_strategy
19
-
20
- @op_m, @op_cv = Mutex.new, ConditionVariable.new
21
- @running_thread = nil
22
-
23
- initialize_children(children)
24
- end
25
-
26
- def run
27
- logger.debug(logloc) { "called" }
28
-
29
- @op_m.synchronize do
30
- if @running_thread
31
- raise AlreadyRunningError,
32
- "This ultravisor is already running"
33
- end
34
-
35
- @queue.clear
36
- @running_thread = Thread.current
37
- Thread.current.name = "Ultravisor"
38
- end
39
-
40
- logger.debug(logloc) { "Going to start children #{@children.map(&:first).inspect}" }
41
- @children.each { |c| c.last.spawn(@queue) }
42
-
43
- process_events
44
-
45
- @op_m.synchronize do
46
- logger.debug(logloc) { "Shutdown time for #{@children.reverse.map(&:first).inspect}" }
47
- @children.reverse.each { |c| c.last.shutdown }
48
-
49
- @running_thread = nil
50
- @op_cv.broadcast
51
- end
52
-
53
- self
54
- end
55
-
56
- def shutdown(wait: true, force: false)
57
- @op_m.synchronize do
58
- return self unless @running_thread
59
- if force
60
- @children.reverse.each { |c| c.last.shutdown(force: true) }
61
- @running_thread.kill
62
- @running_thread = nil
63
- @op_cv.broadcast
64
- else
65
- @queue << :shutdown
66
- if wait
67
- @op_cv.wait(@op_m) while @running_thread
68
- end
69
- end
70
- end
71
- self
72
- end
73
-
74
- def [](id)
75
- @children.assoc(id)&.last
76
- end
77
-
78
- def add_child(**args)
79
- logger.debug(logloc) { "Adding child #{args[:id].inspect}" }
80
- args[:logger] ||= logger
81
-
82
- @op_m.synchronize do
83
- c = Ultravisor::Child.new(**args)
84
-
85
- if @children.assoc(c.id)
86
- raise DuplicateChildError,
87
- "Child with ID #{c.id.inspect} already exists"
88
- end
89
-
90
- @children << [c.id, c]
91
-
92
- if @running_thread
93
- logger.debug(logloc) { "Auto-starting new child #{args[:id].inspect}" }
94
- c.spawn(@queue)
95
- end
96
- end
97
- end
98
-
99
- def remove_child(id)
100
- logger.debug(logloc) { "Removing child #{id.inspect}" }
101
-
102
- @op_m.synchronize do
103
- c = @children.assoc(id)
104
-
105
- return nil if c.nil?
106
-
107
- @children.delete(c)
108
- if @running_thread
109
- logger.debug(logloc) { "Shutting down removed child #{id.inspect}" }
110
- c.last.shutdown
111
- end
112
- end
113
- end
114
-
115
- private
116
-
117
- def validate_strategy
118
- unless %i{one_for_one all_for_one rest_for_one}.include?(@strategy)
119
- raise ArgumentError,
120
- "Invalid strategy #{@strategy.inspect}"
121
- end
122
- end
123
-
124
- def initialize_children(children)
125
- unless children.is_a?(Array)
126
- raise ArgumentError,
127
- "children must be an Array"
128
- end
129
-
130
- @children = []
131
-
132
- children.each do |cfg|
133
- cfg[:logger] ||= logger
134
- c = Ultravisor::Child.new(**cfg)
135
- if @children.assoc(c.id)
136
- raise DuplicateChildError,
137
- "Duplicate child ID: #{c.id.inspect}"
138
- end
139
-
140
- @children << [c.id, c]
141
- end
142
- end
143
-
144
- def process_events
145
- loop do
146
- qe = @queue.pop
147
-
148
- case qe
149
- when Ultravisor::Child
150
- logger.debug(logloc) { "Received Ultravisor::Child queue entry for #{qe.id}" }
151
- @op_m.synchronize { child_exited(qe) }
152
- when :shutdown
153
- logger.debug(logloc) { "Received :shutdown queue entry" }
154
- break
155
- else
156
- logger.error(logloc) { "Unknown queue entry: #{qe.inspect}" }
157
- end
158
- end
159
- end
160
-
161
- def child_exited(child)
162
- if child.termination_exception
163
- log_exception(child.termination_exception, "Ultravisor::Child(#{child.id.inspect})") { "Thread terminated by unhandled exception" }
164
- end
165
-
166
- if @running_thread.nil?
167
- logger.debug(logloc) { "Child termination after shutdown" }
168
- # Child termination processed after we've shut down... nope
169
- return
170
- end
171
-
172
- begin
173
- return unless child.restart?
174
- rescue Ultravisor::BlownRestartPolicyError
175
- # Uh oh...
176
- logger.error(logloc) { "Child #{child.id} has exceeded its restart policy. Shutting down the Ultravisor." }
177
- @queue << :shutdown
178
- return
179
- end
180
-
181
- case @strategy
182
- when :all_for_one
183
- @children.reverse.each do |id, c|
184
- # Don't need to shut down the child that has caused all this mess
185
- next if child.id == id
186
-
187
- c.shutdown
188
- end
189
- when :rest_for_one
190
- @children.reverse.each do |id, c|
191
- # Don't go past the child that caused the problems
192
- break if child.id == id
193
-
194
- c.shutdown
195
- end
196
- end
197
-
198
- sleep child.restart_delay
199
-
200
- case @strategy
201
- when :all_for_one
202
- @children.each do |_, c|
203
- c.spawn(@queue)
204
- end
205
- when :rest_for_one
206
- s = false
207
- @children.each do |id, c|
208
- s = true if child.id == id
209
-
210
- c.spawn(@queue) if s
211
- end
212
- when :one_for_one
213
- child.spawn(@queue)
214
- end
215
- end
216
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger'
4
-
5
- module ExampleGroupMethods
6
- def uses_logger
7
- let(:logger) { instance_double(Logger, 'mock') }
8
-
9
- before(:each) do
10
- allow(Logger).to receive(:new).and_return(logger)
11
- allow(logger).to receive(:debug).with(instance_of(String))
12
- allow(logger).to receive(:info).with(instance_of(String))
13
- allow(logger).to receive(:error) { |p, &m| puts "#{p}: #{m.call}" }
14
- allow(logger).to receive(:level=).with(Logger::INFO)
15
- allow(logger).to receive(:formatter=).with(an_instance_of(Proc))
16
- allow(logger).to receive(:kind_of?).with(Logger).and_return(true)
17
- end
18
- end
19
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
- module ExampleMethods
3
- def tmptrace
4
- require "tracer"
5
- Tracer.add_filter { |event, file, line, id, binding, klass, *rest| klass.to_s =~ /Ultravisor/ }
6
- Tracer.on { yield }
7
- end
8
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'bundler'
3
- Bundler.setup(:default, :development)
4
- require 'rspec/core'
5
- require 'rspec/mocks'
6
-
7
- Thread.report_on_exception = false
8
-
9
- require 'simplecov'
10
- SimpleCov.start do
11
- add_filter('spec')
12
- end
13
-
14
- class ListIncompletelyCoveredFiles
15
- def format(result)
16
- incompletes = result.files.select { |f| f.covered_percent < 100 }
17
-
18
- unless incompletes.empty?
19
- puts
20
- puts "Files with incomplete test coverage:"
21
- incompletes.each do |f|
22
- printf " %2.01f%% %s\n", f.covered_percent, f.filename
23
- end
24
- puts; puts
25
- end
26
- end
27
- end
28
-
29
- SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
30
- SimpleCov::Formatter::HTMLFormatter,
31
- ListIncompletelyCoveredFiles
32
- ])
33
-
34
- require_relative "./example_group_methods"
35
- require_relative "./example_methods"
36
-
37
- RSpec.configure do |config|
38
- config.extend ExampleGroupMethods
39
- config.include ExampleMethods
40
-
41
- config.order = :random
42
- config.fail_fast = !!ENV["RSPEC_CONFIG_FAIL_FAST"]
43
- config.full_backtrace = !!ENV["RSPEC_CONFIG_FULL_BACKTRACE"]
44
-
45
- config.expect_with :rspec do |c|
46
- c.syntax = :expect
47
- end
48
-
49
- config.mock_with :rspec do |mocks|
50
- mocks.verify_partial_doubles = true
51
- end
52
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative "../spec_helper"
3
-
4
- require_relative "../../lib/ultravisor"
5
-
6
- describe Ultravisor do
7
- let(:args) { {} }
8
- let(:ultravisor) { Ultravisor.new(**args) }
9
- let!(:child) { Ultravisor::Child.new(id: :xtra, klass: Class, method: :to_s) }
10
-
11
- describe "#add_child" do
12
- before(:each) do
13
- allow(Ultravisor::Child).to receive(:new).and_return(child)
14
- end
15
-
16
- context "when the ultravisor isn't running" do
17
- it "creates a new Child object" do
18
- expect(Ultravisor::Child).to receive(:new).with(id: :xtra, klass: Class, method: :to_s, logger: instance_of(Logger))
19
-
20
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
21
- end
22
-
23
- it "registers the new child" do
24
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
25
-
26
- expect(ultravisor[:xtra]).to be_a(Ultravisor::Child)
27
- end
28
-
29
- it "explodes if a dupe child ID is used" do
30
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
31
-
32
- expect do
33
- ultravisor.add_child(id: :xtra, klass: Object, method: :to_s)
34
- end.to raise_error(Ultravisor::DuplicateChildError)
35
- end
36
-
37
- it "doesn't spawn a child thread" do
38
- expect(child).to_not receive(:spawn)
39
-
40
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
41
- end
42
- end
43
-
44
- context "while the ultravisor *is* running" do
45
- let(:mock_thread) { instance_double(Thread) }
46
-
47
- before(:each) do
48
- allow(child).to receive(:spawn)
49
- ultravisor.instance_variable_set(:@running_thread, mock_thread)
50
- end
51
-
52
- it "creates a new Child object" do
53
- expect(Ultravisor::Child).to receive(:new).with(id: :xtra, klass: Class, method: :to_s, logger: instance_of(Logger))
54
-
55
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
56
- end
57
-
58
- it "registers the new child" do
59
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
60
-
61
- expect(ultravisor[:xtra]).to be_a(Ultravisor::Child)
62
- end
63
-
64
- it "explodes if a dupe child ID is used" do
65
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
66
-
67
- expect do
68
- ultravisor.add_child(id: :xtra, klass: Object, method: :to_s)
69
- end.to raise_error(Ultravisor::DuplicateChildError)
70
- end
71
-
72
- it "spawns a child thread" do
73
- ultravisor.add_child(id: :xtra, klass: Class, method: :to_s)
74
-
75
- expect(child).to have_received(:spawn)
76
- end
77
- end
78
- end
79
- end
@@ -1,121 +0,0 @@
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
- class FakeError < RuntimeError; end
10
-
11
- def run
12
- end
13
-
14
- def poke_processor
15
- process_castcall
16
- end
17
-
18
- def failing_method
19
- raise FakeError
20
- end
21
- end
22
-
23
- describe Ultravisor::Child do
24
- let(:child) { Ultravisor::Child.new(**args) }
25
- let(:instance) { child.__send__(:new_instance) }
26
-
27
- describe "#call" do
28
- context "without enable_castcall" do
29
- let(:args) do
30
- {
31
- id: :call_child,
32
- klass: CastCallTest,
33
- method: :run,
34
- }
35
- end
36
-
37
- it "does not accept calls to #call" do
38
- expect { child.call }.to raise_error(NoMethodError)
39
- end
40
- end
41
-
42
- context "with enable_castcall" do
43
- let(:args) do
44
- {
45
- id: :call_child,
46
- klass: CastCallTest,
47
- method: :run,
48
- enable_castcall: true,
49
- }
50
- end
51
-
52
- before(:each) do
53
- # Got to have an instance otherwise all hell breaks loose
54
- child.instance_variable_set(:@instance, instance)
55
-
56
- # So we can check if and when it's been called
57
- allow(instance).to receive(:to_s).and_call_original
58
- allow(instance).to receive(:failing_method).and_call_original
59
- end
60
-
61
- it "accepts calls to #call" do
62
- expect { child.call }.to_not raise_error
63
- end
64
-
65
- it "does not accept call calls to methods that do not exist on the worker object" do
66
- expect { child.call.flibbetygibbets }.to raise_error(NoMethodError)
67
- end
68
-
69
- it "does not accept call calls to private methods on the worker object" do
70
- expect { child.call.eval }.to raise_error(NoMethodError)
71
- end
72
-
73
- it "accepts calls to methods that exist on the worker object" do
74
- th = Thread.new { child.call.to_s }
75
- th.join(0.01)
76
- instance.poke_processor
77
- expect { th.value }.to_not raise_error
78
- end
79
-
80
- it "calls the instance method only when process_castcall is called" do
81
- th = Thread.new { child.call.to_s }
82
- th.join(0.001)
83
-
84
- # Thread should be ticking along, not dead
85
- expect(th.status).to eq("sleep")
86
- expect(instance).to_not have_received(:to_s)
87
- instance.poke_processor
88
- expect(instance).to have_received(:to_s)
89
- expect(th.value).to be_a(String)
90
- end
91
-
92
- it "raises a relevant error if the call itself causes an exception is dying" do
93
- th = Thread.new { child.call.failing_method }
94
- th.join(0.001)
95
-
96
- expect(instance).to_not have_received(:failing_method)
97
- expect { instance.poke_processor }.to raise_error(CastCallTest::FakeError)
98
- expect(instance).to have_received(:failing_method)
99
-
100
- expect { th.value }.to raise_error(Ultravisor::ChildRestartedError)
101
- end
102
-
103
- it "raises a relevant error if the instance is dying" do
104
- instance.instance_variable_get(:@ultravisor_child_castcall_queue).close
105
-
106
- expect { child.call.to_s }.to raise_error(Ultravisor::ChildRestartedError)
107
- end
108
-
109
- it "raises an error to all incomplete calls if the instance terminates" do
110
- th = Thread.new { child.call.to_s }
111
-
112
- th.join(0.001) until th.status == "sleep"
113
-
114
- expect(instance).to_not have_received(:to_s)
115
- child.instance_variable_get(:@spawn_m).synchronize { child.__send__(:termination_cleanup) }
116
-
117
- expect { th.value }.to raise_error(Ultravisor::ChildRestartedError)
118
- end
119
- end
120
- end
121
- end
@@ -1,111 +0,0 @@
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
@@ -1,21 +0,0 @@
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