ntl-orchestra 0.9.0
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 +7 -0
- data/.gitignore +16 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +539 -0
- data/Rakefile +21 -0
- data/bin/rake +16 -0
- data/lib/orchestra/conductor.rb +119 -0
- data/lib/orchestra/configuration.rb +12 -0
- data/lib/orchestra/dsl/nodes.rb +72 -0
- data/lib/orchestra/dsl/object_adapter.rb +134 -0
- data/lib/orchestra/dsl/operations.rb +108 -0
- data/lib/orchestra/errors.rb +44 -0
- data/lib/orchestra/node/output.rb +61 -0
- data/lib/orchestra/node.rb +130 -0
- data/lib/orchestra/operation.rb +49 -0
- data/lib/orchestra/performance.rb +137 -0
- data/lib/orchestra/recording.rb +83 -0
- data/lib/orchestra/run_list.rb +171 -0
- data/lib/orchestra/thread_pool.rb +163 -0
- data/lib/orchestra/util.rb +98 -0
- data/lib/orchestra/version.rb +3 -0
- data/lib/orchestra.rb +35 -0
- data/orchestra.gemspec +26 -0
- data/test/examples/fizz_buzz.rb +32 -0
- data/test/examples/invitation_service.rb +118 -0
- data/test/integration/multithreading_test.rb +38 -0
- data/test/integration/recording_telemetry_test.rb +86 -0
- data/test/integration/replayable_operation_test.rb +53 -0
- data/test/lib/console.rb +103 -0
- data/test/lib/test_runner.rb +19 -0
- data/test/support/telemetry_recorder.rb +49 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/conductor_test.rb +25 -0
- data/test/unit/dsl_test.rb +122 -0
- data/test/unit/node_test.rb +122 -0
- data/test/unit/object_adapter_test.rb +100 -0
- data/test/unit/operation_test.rb +224 -0
- data/test/unit/run_list_test.rb +131 -0
- data/test/unit/thread_pool_test.rb +105 -0
- data/test/unit/util_test.rb +20 -0
- data/tmp/.keep +0 -0
- metadata +159 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
class DSLTest < Minitest::Test
|
2
|
+
def test_failing_to_supply_perform_block
|
3
|
+
error = assert_raises ArgumentError do
|
4
|
+
Orchestra::Node::InlineNode.build do
|
5
|
+
provides :foo
|
6
|
+
depends_on :bar
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_equal "expected inline node to define a perform block", error.message
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_two_nodes_one_name
|
14
|
+
error = assert_raises ArgumentError do
|
15
|
+
Orchestra.define do
|
16
|
+
node :foo do
|
17
|
+
depends_on :bar
|
18
|
+
perform do bar + bar end
|
19
|
+
end
|
20
|
+
node :foo do
|
21
|
+
depends_on :qux
|
22
|
+
perform do qux * qux end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
assert_equal "There are duplicate nodes named :foo", error.message
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_result_node
|
31
|
+
operation = Orchestra.define do
|
32
|
+
result :foo do perform do 'foo' end end
|
33
|
+
end
|
34
|
+
assert_equal :foo, operation.result
|
35
|
+
|
36
|
+
error = assert_raises ArgumentError do
|
37
|
+
operation = Orchestra.define do
|
38
|
+
result do perform do 'foo' end end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
assert_equal "Could not infer name for node from a provision", error.message
|
42
|
+
|
43
|
+
operation = Orchestra.define do
|
44
|
+
result do
|
45
|
+
provides :foo
|
46
|
+
perform do 'foo' end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
assert_equal :foo, operation.result
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_command_operations_using_finally
|
53
|
+
operation = Orchestra.define do
|
54
|
+
node :unnecessary do
|
55
|
+
provides :baz
|
56
|
+
perform do raise "Can't get here" end
|
57
|
+
end
|
58
|
+
|
59
|
+
node :necessary do
|
60
|
+
depends_on :baz
|
61
|
+
provides :bar
|
62
|
+
perform do baz + 1 end
|
63
|
+
end
|
64
|
+
|
65
|
+
finally do
|
66
|
+
depends_on :bar
|
67
|
+
perform do bar * 2 end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
test_observer = Module.new do
|
72
|
+
extend self
|
73
|
+
attr :result
|
74
|
+
def update event, *args
|
75
|
+
return unless event == :operation_exited
|
76
|
+
_, @result = args
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
conductor = Orchestra::Conductor.new
|
81
|
+
conductor.add_observer test_observer
|
82
|
+
|
83
|
+
assert_equal nil, conductor.perform(operation, :baz => 3)
|
84
|
+
assert_equal 8, test_observer.result
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_modifies
|
88
|
+
operation = Orchestra.define do
|
89
|
+
result do
|
90
|
+
modifies :list
|
91
|
+
perform do list << :foo end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
ary = []
|
96
|
+
Orchestra.perform operation, :list => ary
|
97
|
+
|
98
|
+
assert_equal [:foo], ary
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_must_supply_result
|
102
|
+
error = assert_raises ArgumentError do
|
103
|
+
Orchestra.define do
|
104
|
+
node :foo do
|
105
|
+
perform do 'foo' end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
assert_equal "Must supply a result", error.message
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_must_contain_at_least_one_node
|
114
|
+
error = assert_raises ArgumentError do
|
115
|
+
Orchestra.define do
|
116
|
+
self.result = :foo
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
assert_equal "Must supply at least one node", error.message
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
class NodeTest < Minitest::Test
|
2
|
+
def test_performing_a_node
|
3
|
+
node = build_simple_node
|
4
|
+
|
5
|
+
assert_equal(
|
6
|
+
{ :bar => 4 },
|
7
|
+
node.perform(:foo => 2, :bar => 2),
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_providing_a_single_hash
|
12
|
+
node = Orchestra::Node::InlineNode.new(
|
13
|
+
:dependencies => [:foo],
|
14
|
+
:provides => [:bar],
|
15
|
+
:perform_block => lambda { { :bar => (foo * 2) } },
|
16
|
+
)
|
17
|
+
|
18
|
+
assert_equal(
|
19
|
+
{ :bar => 4 },
|
20
|
+
node.perform(:foo => 2),
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_providing_a_single_hash_that_is_not_the_output
|
25
|
+
node = Orchestra::Node::InlineNode.new(
|
26
|
+
:dependencies => [:foo],
|
27
|
+
:provides => [:bar],
|
28
|
+
:perform_block => lambda { { :baz => (foo * 2) } },
|
29
|
+
)
|
30
|
+
|
31
|
+
assert_equal(
|
32
|
+
{ :bar => { :baz => 4 } },
|
33
|
+
node.perform(:foo => 2),
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_performing_a_collection_node
|
38
|
+
node = Orchestra::Node::InlineNode.new(
|
39
|
+
:dependencies => [:foo],
|
40
|
+
:provides => [:bar],
|
41
|
+
:perform_block => lambda { |e| e * 2 },
|
42
|
+
:collection => :foo,
|
43
|
+
)
|
44
|
+
|
45
|
+
assert_equal(
|
46
|
+
{ :bar => [2, 4, 6, 8] },
|
47
|
+
node.perform(:foo => [1, 2, 3, 4]),
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_defaulting
|
52
|
+
node = build_simple_node
|
53
|
+
|
54
|
+
assert_equal(
|
55
|
+
{ :bar => 8 },
|
56
|
+
node.perform(:foo => 2),
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_introspecting_dependencies
|
61
|
+
node = build_simple_node
|
62
|
+
|
63
|
+
assert_equal [:foo, :bar], node.dependencies
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_introspecting_mandatory_dependencies
|
67
|
+
node = build_simple_node
|
68
|
+
|
69
|
+
assert_equal [:foo], node.required_dependencies
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_node_fails_to_supply_provisions
|
73
|
+
node = Orchestra::Node::InlineNode.new(
|
74
|
+
:provides => [:foo, :bar, :baz],
|
75
|
+
:perform_block => lambda { nil },
|
76
|
+
)
|
77
|
+
|
78
|
+
error = assert_raises Orchestra::MissingProvisionError do node.perform end
|
79
|
+
|
80
|
+
assert_equal(
|
81
|
+
"failed to supply output: :foo, :bar and :baz",
|
82
|
+
error.message,
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_cannot_return_nil
|
87
|
+
node = Orchestra::Node::InlineNode.new(
|
88
|
+
:provides => [:foo],
|
89
|
+
:perform_block => lambda do nil end
|
90
|
+
)
|
91
|
+
|
92
|
+
error = assert_raises Orchestra::MissingProvisionError do node.perform end
|
93
|
+
|
94
|
+
assert_equal(
|
95
|
+
"failed to supply output: :foo",
|
96
|
+
error.message,
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_node_provides_extra_provisions
|
101
|
+
node = Orchestra::Node::InlineNode.new(
|
102
|
+
:provides => [:foo],
|
103
|
+
:perform_block => lambda do { :foo => :bar, :baz => :qux } end,
|
104
|
+
)
|
105
|
+
|
106
|
+
assert_equal(
|
107
|
+
{ :foo => :bar },
|
108
|
+
node.perform,
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def build_simple_node
|
115
|
+
Orchestra::Node::InlineNode.new(
|
116
|
+
:defaults => { :bar => lambda { 4 } },
|
117
|
+
:dependencies => [:foo, :bar],
|
118
|
+
:provides => [:bar],
|
119
|
+
:perform_block => lambda { foo * bar },
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class ObjectAdapterTest < Minitest::Test
|
2
|
+
def setup
|
3
|
+
@builder = Orchestra::DSL::Operations::Builder.new
|
4
|
+
end
|
5
|
+
|
6
|
+
def test_method_does_not_exist_on_singleton
|
7
|
+
error = assert_raises NotImplementedError do
|
8
|
+
@builder.add_node Splitter, :provides => :words, :method => :foo
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_equal "ObjectAdapterTest::Splitter does not implement method `foo'", error.message
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_method_does_not_exist_on_object
|
15
|
+
error = assert_raises NotImplementedError do
|
16
|
+
@builder.add_node Upcaser, :provides => :words
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal "ObjectAdapterTest::Upcaser does not implement instance method `perform'", error.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_dependencies_inferred_from_method_defaults
|
23
|
+
node = @builder.add_node Upcaser, :iterates_over => :words, :provides => :upcased_words, :method => :call
|
24
|
+
|
25
|
+
assert_equal [:words, :transform], node.dependencies
|
26
|
+
assert_equal [:words], node.required_dependencies
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_performing_an_operation_with_integrated_objects
|
30
|
+
operation = Orchestra.define do
|
31
|
+
node Splitter, :provides => :words
|
32
|
+
node Upcaser, :iterates_over => :words, :provides => :upcased_words, :method => :call
|
33
|
+
node Bolder, :iterates_over => :upcased_words, :provides => :bolded_words, :method => :call
|
34
|
+
node Joiner, :method => :join
|
35
|
+
self.result = :joiner
|
36
|
+
end
|
37
|
+
|
38
|
+
result = Orchestra.perform(
|
39
|
+
operation,
|
40
|
+
:sentence => "the quick brown fox jumps over the lazy dog",
|
41
|
+
:bold_text => "*",
|
42
|
+
)
|
43
|
+
|
44
|
+
assert_equal(
|
45
|
+
%(*THE* *QUICK* *BROWN* *FOX* *JUMPS* *OVER* *THE* *LAZY* *DOG*),
|
46
|
+
result,
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_provent_singleton_objects_from_handling_collections
|
51
|
+
error = assert_raises ArgumentError do
|
52
|
+
Orchestra.define do
|
53
|
+
node Splitter, :iterates_over => :sentence
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_equal(
|
58
|
+
"ObjectAdapterTest::Splitter is a singleton; cannot iterate over collection :sentence",
|
59
|
+
error.message
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
module Splitter
|
64
|
+
def self.perform sentence
|
65
|
+
sentence.split %r{[[:space:]]+}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Upcaser
|
70
|
+
def initialize transform = :upcase
|
71
|
+
@transform = transform
|
72
|
+
end
|
73
|
+
|
74
|
+
def call element
|
75
|
+
element.public_send @transform
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Bolder
|
80
|
+
attr :bold_text
|
81
|
+
|
82
|
+
def initialize bold_text = "**"
|
83
|
+
@bold_text = bold_text
|
84
|
+
end
|
85
|
+
|
86
|
+
def call word
|
87
|
+
"#{bold_text}#{word}#{bold_text}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Joiner
|
92
|
+
def initialize bolded_words
|
93
|
+
@bolded_words = bolded_words
|
94
|
+
end
|
95
|
+
|
96
|
+
def join
|
97
|
+
@bolded_words.join ' '
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
module OperationTest
|
2
|
+
class PerformTest < Minitest::Test
|
3
|
+
def test_simple_operation
|
4
|
+
operation = build_simple_operation
|
5
|
+
|
6
|
+
assert_equal(
|
7
|
+
%(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG),
|
8
|
+
operation.perform(:sentence => %(the quick brown fox jumps over the lazy dog)),
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_performing_operation_without_inputs
|
13
|
+
operation = build_simple_operation
|
14
|
+
|
15
|
+
error = assert_raises Orchestra::MissingInputError do
|
16
|
+
operation.perform
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal %(Missing input :sentence), error.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_mutating_inputs
|
23
|
+
operation = build_mutator
|
24
|
+
|
25
|
+
shopping_list = [%(1 clove garlic)]
|
26
|
+
operation.perform :shopping_list => shopping_list
|
27
|
+
|
28
|
+
assert_equal(
|
29
|
+
[%(1 clove garlic), %(2 bunches of carrots), %(1 stalk of celery), %(3 yellow onions)],
|
30
|
+
shopping_list,
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_skipping_unnecessary_steps
|
35
|
+
operation = build_simple_operation
|
36
|
+
|
37
|
+
assert_equal(
|
38
|
+
%(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG),
|
39
|
+
operation.perform(:upcased_word_list => %w(THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG)),
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_passing_conductor_into_nodes
|
44
|
+
conductor = Orchestra::Conductor.new
|
45
|
+
|
46
|
+
node = Orchestra::Node::InlineNode.build do
|
47
|
+
depends_on :conductor
|
48
|
+
provides :conductor_id
|
49
|
+
perform do conductor.object_id end
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_equal conductor.object_id, node.perform[:conductor_id]
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_missing_input_errors
|
56
|
+
operation = Orchestra.define do
|
57
|
+
node :foo do
|
58
|
+
depends_on :bar
|
59
|
+
perform do bar + bar end
|
60
|
+
end
|
61
|
+
node :baz do
|
62
|
+
depends_on :qux
|
63
|
+
perform do qux * qux end
|
64
|
+
end
|
65
|
+
node :result do
|
66
|
+
depends_on :foo, :baz
|
67
|
+
perform do baz - foo end
|
68
|
+
end
|
69
|
+
self.result = :result
|
70
|
+
end
|
71
|
+
|
72
|
+
error = assert_raises Orchestra::MissingInputError do
|
73
|
+
Orchestra.perform operation, :bar => nil
|
74
|
+
end
|
75
|
+
|
76
|
+
assert_equal "Missing inputs :bar and :qux", error.message
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def build_simple_operation
|
82
|
+
Orchestra.define do
|
83
|
+
node :split do
|
84
|
+
depends_on :sentence
|
85
|
+
provides :word_list
|
86
|
+
perform do sentence.split %r{[[:space:]]+} end
|
87
|
+
end
|
88
|
+
|
89
|
+
node :upcase do
|
90
|
+
depends_on :word_list
|
91
|
+
provides :upcased_word_list
|
92
|
+
perform do word_list.map &:upcase end
|
93
|
+
end
|
94
|
+
|
95
|
+
node :join do
|
96
|
+
depends_on :upcased_word_list
|
97
|
+
perform do upcased_word_list.join ' ' end
|
98
|
+
end
|
99
|
+
|
100
|
+
self.result = :join
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_mutator
|
105
|
+
Orchestra.define do
|
106
|
+
node :carrots do
|
107
|
+
depends_on :shopping_list
|
108
|
+
provides :shopping_list
|
109
|
+
perform do shopping_list << "2 bunches of carrots" end
|
110
|
+
end
|
111
|
+
|
112
|
+
node :celery do
|
113
|
+
depends_on :shopping_list
|
114
|
+
provides :shopping_list
|
115
|
+
perform do shopping_list << "1 stalk of celery" end
|
116
|
+
end
|
117
|
+
|
118
|
+
node :onions do
|
119
|
+
depends_on :shopping_list
|
120
|
+
provides :shopping_list
|
121
|
+
perform do shopping_list << "3 yellow onions" end
|
122
|
+
end
|
123
|
+
|
124
|
+
self.result = :shopping_list
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
class IntrospectionTest < Minitest::Test
|
131
|
+
def test_introspecting_dependencies
|
132
|
+
node = Orchestra::Node::InlineNode.build do
|
133
|
+
depends_on :foo, :bar => :baz
|
134
|
+
provides :baz
|
135
|
+
perform do :noop end
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_equal [:foo, :bar], node.dependencies
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_introspecting_optional_dependencies
|
142
|
+
node = Orchestra::Node::InlineNode.build do
|
143
|
+
depends_on :foo, :bar => :baz
|
144
|
+
provides :qux
|
145
|
+
perform do :noop end
|
146
|
+
end
|
147
|
+
|
148
|
+
assert_equal [:bar], node.optional_dependencies
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_introspecting_mandatory_dependencies
|
152
|
+
node = Orchestra::Node::InlineNode.build do
|
153
|
+
depends_on :foo, :bar => :baz
|
154
|
+
provides :baz
|
155
|
+
perform do :noop end
|
156
|
+
end
|
157
|
+
|
158
|
+
assert_equal [:foo], node.required_dependencies
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
class EmbeddingOperationsTest < Minitest::Test
|
164
|
+
def test_embedding_operations
|
165
|
+
inner = Orchestra.define do
|
166
|
+
node :double do
|
167
|
+
depends_on :number
|
168
|
+
provides :doubled
|
169
|
+
perform do number * 2 end
|
170
|
+
end
|
171
|
+
|
172
|
+
result :plus_one do
|
173
|
+
depends_on :doubled
|
174
|
+
perform do doubled + 1 end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
outer = Orchestra.define do
|
179
|
+
node inner
|
180
|
+
|
181
|
+
result :squared do
|
182
|
+
depends_on :plus_one
|
183
|
+
perform do plus_one ** 2 end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
telemetry = {}
|
188
|
+
|
189
|
+
conductor = Orchestra::Conductor.new
|
190
|
+
conductor.add_observer TelemetryRecorder.new telemetry
|
191
|
+
|
192
|
+
result = conductor.perform outer, :number => 4
|
193
|
+
|
194
|
+
assert_equal 81, result
|
195
|
+
|
196
|
+
assert_equal expected_telemetry, telemetry
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def expected_telemetry
|
202
|
+
{
|
203
|
+
:input => { :number => 4 },
|
204
|
+
:movements => {
|
205
|
+
:double => {
|
206
|
+
:input => { :number => 4 },
|
207
|
+
:output => { :doubled => 8 },
|
208
|
+
},
|
209
|
+
:plus_one => {
|
210
|
+
:input => { :doubled => 8 },
|
211
|
+
:output => { :plus_one => 9 },
|
212
|
+
},
|
213
|
+
:squared => {
|
214
|
+
:input => { :plus_one => 9 },
|
215
|
+
:output => { :squared => 81 },
|
216
|
+
},
|
217
|
+
},
|
218
|
+
:output => 81,
|
219
|
+
:performance_name => nil,
|
220
|
+
:service_calls => [],
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
class RunListTest < Minitest::Test
|
2
|
+
def test_all_are_required
|
3
|
+
builder.input_names << :foo
|
4
|
+
|
5
|
+
run_list = builder.build
|
6
|
+
|
7
|
+
assert_equal %w(foo⇒bar bar⇒baz baz⇒qux qux⇒res), run_list.node_names
|
8
|
+
assert_includes run_list.dependencies, :foo
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_discards_unnecessary_nodes
|
12
|
+
builder['aba⇒cab'] = OpenStruct.new :required_dependencies => [:aba], :optional_dependencies => [], :provisions => [:cab]
|
13
|
+
|
14
|
+
run_list = builder.build
|
15
|
+
|
16
|
+
assert_equal %w(foo⇒bar bar⇒baz baz⇒qux qux⇒res), run_list.node_names
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_supplying_dependencies
|
20
|
+
builder.input_names << :baz
|
21
|
+
|
22
|
+
run_list = builder.build
|
23
|
+
|
24
|
+
assert_equal %w(baz⇒qux qux⇒res), run_list.node_names
|
25
|
+
refute_includes run_list.dependencies, :foo
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_nodes_that_modify
|
29
|
+
assemble_builder modifying_nodes
|
30
|
+
|
31
|
+
run_list = builder.build
|
32
|
+
|
33
|
+
assert_equal %w(foo bar baz), run_list.node_names
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_reorders_optional_deps_before_mandatory_deps_when_possible
|
37
|
+
assemble_builder order_changes_because_of_optional_deps
|
38
|
+
|
39
|
+
run_list = builder.build
|
40
|
+
|
41
|
+
assert_equal %w(baz+foo bar+baz foo+bar final), run_list.node_names
|
42
|
+
assert_equal [], run_list.required_dependencies
|
43
|
+
assert_equal [:bar, :baz, :foo], run_list.optional_dependencies
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_wrap_tsort_cycle_errors
|
47
|
+
assemble_builder circular_dependency_tree
|
48
|
+
|
49
|
+
error = assert_raises Orchestra::CircularDependencyError do
|
50
|
+
builder.build
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_equal(
|
54
|
+
"Circular dependency detected! Check your dependencies/provides",
|
55
|
+
error.message
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def assemble_builder nodes = default_nodes
|
62
|
+
@builder ||= begin
|
63
|
+
builder = Orchestra::RunList::Builder.new :res
|
64
|
+
builder.merge! nodes
|
65
|
+
builder
|
66
|
+
end
|
67
|
+
end
|
68
|
+
alias_method :builder, :assemble_builder
|
69
|
+
|
70
|
+
def default_nodes
|
71
|
+
{
|
72
|
+
'foo⇒bar' => OpenStruct.new(:required_dependencies => [:foo], :provisions => [:bar], optional_dependencies: []),
|
73
|
+
'bar⇒baz' => OpenStruct.new(:required_dependencies => [:bar], :provisions => [:baz], optional_dependencies: []),
|
74
|
+
'baz⇒qux' => OpenStruct.new(:required_dependencies => [:baz], :provisions => [:qux], optional_dependencies: []),
|
75
|
+
'qux⇒res' => OpenStruct.new(:required_dependencies => [:qux], :provisions => [:res], optional_dependencies: []),
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def modifying_nodes
|
80
|
+
{
|
81
|
+
'foo' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared], optional_dependencies: []),
|
82
|
+
'bar' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared], optional_dependencies: []),
|
83
|
+
'baz' => OpenStruct.new(:required_dependencies => [:shared], :provisions => [:shared, :res], optional_dependencies: []),
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def circular_dependency_tree
|
88
|
+
{
|
89
|
+
'foo+bar' => OpenStruct.new(
|
90
|
+
:optional_dependencies => [:bar],
|
91
|
+
:required_dependencies => [:foo],
|
92
|
+
:provisions => [:aba]
|
93
|
+
),
|
94
|
+
'bar+baz' => OpenStruct.new(
|
95
|
+
:optional_dependencies => [:foo],
|
96
|
+
:required_dependencies => [:bar],
|
97
|
+
:provisions => [:cab],
|
98
|
+
),
|
99
|
+
'final' => OpenStruct.new(
|
100
|
+
:optional_dependencies => [],
|
101
|
+
:required_dependencies => [:aba, :cab],
|
102
|
+
:provisions => [:res],
|
103
|
+
)
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def order_changes_because_of_optional_deps
|
108
|
+
{
|
109
|
+
'foo+bar' => OpenStruct.new(
|
110
|
+
:optional_dependencies => [],
|
111
|
+
:required_dependencies => [:foo, :bar],
|
112
|
+
:provisions => [:aba]
|
113
|
+
),
|
114
|
+
'bar+baz' => OpenStruct.new(
|
115
|
+
:optional_dependencies => [:bar],
|
116
|
+
:required_dependencies => [:baz],
|
117
|
+
:provisions => [:cab],
|
118
|
+
),
|
119
|
+
'baz+foo' => OpenStruct.new(
|
120
|
+
:optional_dependencies => [:baz, :foo],
|
121
|
+
:required_dependencies => [],
|
122
|
+
:provisions => [:bra],
|
123
|
+
),
|
124
|
+
'final' => OpenStruct.new(
|
125
|
+
:optional_dependencies => [],
|
126
|
+
:required_dependencies => [:aba, :cab, :bra],
|
127
|
+
:provisions => [:res],
|
128
|
+
),
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|