dynflow 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dynflow/action.rb +13 -0
- data/lib/dynflow/executors/parallel/worker.rb +1 -0
- data/lib/dynflow/testing/assertions.rb +10 -0
- data/lib/dynflow/testing/dummy_execution_plan.rb +22 -7
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/web_console.rb +7 -3
- data/test/action_test.rb +21 -0
- data/test/testing_test.rb +18 -7
- data/web/views/flow_step.erb +12 -2
- data/web/views/show.erb +10 -0
- metadata +2 -2
data/lib/dynflow/action.rb
CHANGED
@@ -395,6 +395,8 @@ module Dynflow
|
|
395
395
|
end
|
396
396
|
end
|
397
397
|
end
|
398
|
+
|
399
|
+
check_serializable :input
|
398
400
|
end
|
399
401
|
end
|
400
402
|
|
@@ -422,6 +424,8 @@ module Dynflow
|
|
422
424
|
if result == SUSPEND
|
423
425
|
self.state = :suspended
|
424
426
|
end
|
427
|
+
|
428
|
+
check_serializable :output
|
425
429
|
end
|
426
430
|
|
427
431
|
else
|
@@ -440,5 +444,14 @@ module Dynflow
|
|
440
444
|
end
|
441
445
|
end
|
442
446
|
end
|
447
|
+
|
448
|
+
def check_serializable(what)
|
449
|
+
Match! what, :input, :output
|
450
|
+
value = send what
|
451
|
+
recursive_to_hash value # it raises when not serializable
|
452
|
+
rescue => e
|
453
|
+
value.replace not_serializable: true
|
454
|
+
raise e
|
455
|
+
end
|
443
456
|
end
|
444
457
|
end
|
@@ -30,6 +30,16 @@ module Dynflow
|
|
30
30
|
found
|
31
31
|
end
|
32
32
|
|
33
|
+
def refute_action_planed(action, planned_action_class)
|
34
|
+
Match! action.phase, Action::Plan
|
35
|
+
Match! action.state, :success
|
36
|
+
found = action.execution_plan.planned_plan_steps.
|
37
|
+
select { |a| a.is_a?(planned_action_class) }
|
38
|
+
|
39
|
+
assert(found.empty?, "Action #{planned_action_class} was planned")
|
40
|
+
found
|
41
|
+
end
|
42
|
+
|
33
43
|
# assert that +action+ has run-phase planned
|
34
44
|
def assert_run_phase(action, input = nil, &block)
|
35
45
|
Match! action.phase, Action::Plan
|
@@ -7,16 +7,28 @@ module Dynflow
|
|
7
7
|
attr_reader :id, :planned_plan_steps, :planned_run_steps, :planned_finalize_steps
|
8
8
|
|
9
9
|
def initialize
|
10
|
-
@id
|
11
|
-
@planned_plan_steps
|
12
|
-
@planned_run_steps
|
13
|
-
@planned_finalize_steps
|
10
|
+
@id = Testing.get_id.to_s
|
11
|
+
@planned_plan_steps = []
|
12
|
+
@planned_run_steps = []
|
13
|
+
@planned_finalize_steps = []
|
14
|
+
@planned_action_stubbers = {}
|
14
15
|
end
|
15
16
|
|
16
17
|
def world
|
17
18
|
@world ||= DummyWorld.new
|
18
19
|
end
|
19
20
|
|
21
|
+
# Allows modify the DummyPlannedAction returned by plan_action
|
22
|
+
def stub_planned_action(klass, &block)
|
23
|
+
@planned_action_stubbers[klass] = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_plan_step(klass, _)
|
27
|
+
dummy_planned_action(klass).tap do |action|
|
28
|
+
@planned_plan_steps << action
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
20
32
|
def add_run_step(action)
|
21
33
|
@planned_run_steps << action
|
22
34
|
action
|
@@ -27,9 +39,12 @@ module Dynflow
|
|
27
39
|
action
|
28
40
|
end
|
29
41
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
42
|
+
def dummy_planned_action(klass)
|
43
|
+
DummyPlannedAction.new(klass).tap do |action|
|
44
|
+
if planned_action_stubber = @planned_action_stubbers[klass]
|
45
|
+
planned_action_stubber.call(action)
|
46
|
+
end
|
47
|
+
end
|
33
48
|
end
|
34
49
|
|
35
50
|
def switch_flow(*args, &block)
|
data/lib/dynflow/version.rb
CHANGED
data/lib/dynflow/web_console.rb
CHANGED
@@ -56,6 +56,10 @@ module Dynflow
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
def duration_to_s(duration)
|
60
|
+
h("%0.2fs" % duration)
|
61
|
+
end
|
62
|
+
|
59
63
|
def load_action(step)
|
60
64
|
world.persistence.load_action(step)
|
61
65
|
end
|
@@ -74,7 +78,7 @@ module Dynflow
|
|
74
78
|
if !value_html.empty?
|
75
79
|
<<-HTML
|
76
80
|
<p>
|
77
|
-
|
81
|
+
<b>#{h(label)}</b>
|
78
82
|
#{value_html}
|
79
83
|
</p>
|
80
84
|
HTML
|
@@ -139,11 +143,11 @@ module Dynflow
|
|
139
143
|
end
|
140
144
|
|
141
145
|
def updated_url(new_params)
|
142
|
-
url("?" + Rack::Utils.
|
146
|
+
url("?" + Rack::Utils.build_nested_query(params.merge(new_params.stringify_keys)))
|
143
147
|
end
|
144
148
|
|
145
149
|
def paginated_url(delta)
|
146
|
-
h(updated_url(page: [0, page + delta].max))
|
150
|
+
h(updated_url(page: [0, page + delta].max.to_s))
|
147
151
|
end
|
148
152
|
|
149
153
|
def pagination_options
|
data/test/action_test.rb
CHANGED
@@ -70,5 +70,26 @@ module Dynflow
|
|
70
70
|
presenter.summary.must_equal(assignees: ["John Doe"])
|
71
71
|
end
|
72
72
|
end
|
73
|
+
|
74
|
+
describe 'serialization' do
|
75
|
+
|
76
|
+
include Testing
|
77
|
+
|
78
|
+
it 'fails when input is not serializable' do
|
79
|
+
klass = Class.new(Dynflow::Action)
|
80
|
+
-> { create_and_plan_action klass, key: Object.new }.must_raise NoMethodError
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'fails when output is not serializable' do
|
84
|
+
klass = Class.new(Dynflow::Action) do
|
85
|
+
def run
|
86
|
+
output.update key: Object.new
|
87
|
+
end
|
88
|
+
end
|
89
|
+
action = create_and_plan_action klass, {}
|
90
|
+
-> { run_action action }.must_raise NoMethodError
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
73
94
|
end
|
74
95
|
end
|
data/test/testing_test.rb
CHANGED
@@ -9,7 +9,7 @@ module Dynflow
|
|
9
9
|
|
10
10
|
describe 'testing' do
|
11
11
|
|
12
|
-
|
12
|
+
specify '#plan_action' do
|
13
13
|
input = { 'input' => 'input' }
|
14
14
|
action = create_and_plan_action CWE::DummyHeavyProgress, input
|
15
15
|
|
@@ -21,9 +21,20 @@ module Dynflow
|
|
21
21
|
assert_run_phase action
|
22
22
|
assert_finalize_phase action
|
23
23
|
assert_action_planed action, CWE::DummySuspended
|
24
|
+
refute_action_planed action, CWE::DummyAnotherTrigger
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
|
+
specify 'stub_plan_action' do
|
28
|
+
action = create_action CWE::DummyHeavyProgress
|
29
|
+
action.execution_plan.stub_planned_action(CWE::DummySuspended) do |sub_action|
|
30
|
+
sub_action.define_singleton_method(:test) { "test" }
|
31
|
+
end
|
32
|
+
plan_action(action, {})
|
33
|
+
stubbed_action = action.execution_plan.planned_plan_steps.first
|
34
|
+
stubbed_action.test.must_equal "test"
|
35
|
+
end
|
36
|
+
|
37
|
+
specify '#run_action without suspend' do
|
27
38
|
input = { 'input' => 'input' }
|
28
39
|
plan = create_and_plan_action CWE::DummyHeavyProgress, input
|
29
40
|
action = run_action plan
|
@@ -37,7 +48,7 @@ module Dynflow
|
|
37
48
|
action.progress_done.must_equal 1
|
38
49
|
end
|
39
50
|
|
40
|
-
|
51
|
+
specify '#run_action with suspend' do
|
41
52
|
input = { 'input' => 'input' }
|
42
53
|
plan = create_and_plan_action CWE::DummySuspended, input
|
43
54
|
action = run_action plan
|
@@ -59,7 +70,7 @@ module Dynflow
|
|
59
70
|
action.progress_done.must_equal 1
|
60
71
|
end
|
61
72
|
|
62
|
-
|
73
|
+
specify '#finalize_action' do
|
63
74
|
input = { 'input' => 'input' }
|
64
75
|
plan = create_and_plan_action CWE::DummyHeavyProgress, input
|
65
76
|
run = run_action plan
|
@@ -122,14 +133,14 @@ module Dynflow
|
|
122
133
|
let(:planned_action) { create_and_plan_action CWE::Merge, plan_input }
|
123
134
|
let(:runned_action) { run_action planned_action }
|
124
135
|
|
125
|
-
it '
|
136
|
+
it 'plans' do
|
126
137
|
assert_run_phase planned_action
|
127
138
|
refute_finalize_phase planned_action
|
128
139
|
|
129
140
|
planned_action.execution_plan.planned_plan_steps.must_be_empty
|
130
141
|
end
|
131
142
|
|
132
|
-
it '
|
143
|
+
it 'runs' do
|
133
144
|
runned_action.output.fetch(:passed).must_equal true
|
134
145
|
end
|
135
146
|
|
@@ -138,7 +149,7 @@ module Dynflow
|
|
138
149
|
super.update review_results: [true, false]
|
139
150
|
end
|
140
151
|
|
141
|
-
it '
|
152
|
+
it 'runs' do
|
142
153
|
runned_action.output.fetch(:passed).must_equal false
|
143
154
|
end
|
144
155
|
end
|
data/web/views/flow_step.erb
CHANGED
@@ -5,17 +5,27 @@
|
|
5
5
|
<% end %>
|
6
6
|
|
7
7
|
<span class="step-label">
|
8
|
-
<%= h(step.id) %>: <%= h(step.action_class.name) %>
|
8
|
+
<%= h(step.id) %>: <%= h(step.action_class.name) %>
|
9
|
+
(<%= h(step.state) %>)
|
10
|
+
<% unless step.state == :pending %>
|
11
|
+
[ <%= duration_to_s(step.real_time) %> / <%= duration_to_s(step.execution_time) %> ]
|
12
|
+
<% end %>
|
9
13
|
</span>
|
10
14
|
<% if @plan.state == :paused && step.state == :error %>
|
11
15
|
<a href="<%= url("/#{@plan.id}/skip/#{step.id}") %>" class="postlink">Skip</a>
|
12
16
|
<% end %>
|
13
17
|
<div class="action">
|
18
|
+
<% unless @plan.state == :pending %>
|
19
|
+
<p><b>Started at:</b> <%= h(step.started_at) %></p>
|
20
|
+
<p><b>Ended at:</b> <%= h(step.ended_at) %></p>
|
21
|
+
<p><b>Real time:</b> <%= duration_to_s(step.real_time) %></p>
|
22
|
+
<p><b>Execution time (excluding suspended state):</b> <%= duration_to_s(step.execution_time) %></p>
|
23
|
+
<% end %>
|
14
24
|
<%= show_action_data("Input:", action.input) %>
|
15
25
|
<%= show_action_data("Output:", action.output) %>
|
16
26
|
<% if step.error %>
|
17
27
|
<p>
|
18
|
-
Error
|
28
|
+
<b>Error:</b>
|
19
29
|
</p>
|
20
30
|
<p>
|
21
31
|
<%= h(step.error.exception_class) %>
|
data/web/views/show.erb
CHANGED
@@ -14,6 +14,16 @@
|
|
14
14
|
<%= h(@plan.result) %>
|
15
15
|
</p>
|
16
16
|
|
17
|
+
<p>
|
18
|
+
<b>Started at:</b>
|
19
|
+
<%= h(@plan.started_at) %>
|
20
|
+
</p>
|
21
|
+
|
22
|
+
<p>
|
23
|
+
<b>Ended at:</b>
|
24
|
+
<%= h(@plan.ended_at) %>
|
25
|
+
</p>
|
26
|
+
|
17
27
|
<ul class="phases nav nav-tabs" id="myTab">
|
18
28
|
<li><a href="#plan">Plan</a></li>
|
19
29
|
<li class="active"><a href="#run">Run</a></li>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-03-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|