rworkflow 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +117 -0
- data/lib/rworkflow/flow.rb +450 -0
- data/lib/rworkflow/flow_registry.rb +69 -0
- data/lib/rworkflow/lifecycle.rb +102 -0
- data/lib/rworkflow/minitest/test.rb +53 -0
- data/lib/rworkflow/minitest/worker.rb +17 -0
- data/lib/rworkflow/minitest.rb +8 -0
- data/lib/rworkflow/sidekiq_flow.rb +186 -0
- data/lib/rworkflow/sidekiq_helper.rb +84 -0
- data/lib/rworkflow/sidekiq_lifecycle.rb +8 -0
- data/lib/rworkflow/sidekiq_state.rb +42 -0
- data/lib/rworkflow/state.rb +104 -0
- data/lib/rworkflow/state_error.rb +13 -0
- data/lib/rworkflow/transition_error.rb +9 -0
- data/lib/rworkflow/version.rb +3 -0
- data/lib/rworkflow/worker.rb +62 -0
- data/lib/rworkflow.rb +15 -0
- data/lib/tasks/rworkflow_tasks.rake +4 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +15 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +83 -0
- data/test/dummy/config/environments/test.rb +41 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +516 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/flow_test.rb +112 -0
- data/test/lifecycle_test.rb +81 -0
- data/test/rworkflow_test.rb +7 -0
- data/test/sidekiq_flow_test.rb +173 -0
- data/test/state_test.rb +99 -0
- data/test/test_helper.rb +32 -0
- metadata +199 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body>
|
58
|
+
<!-- This file lives in public/500.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>We're sorry, but something went wrong.</h1>
|
62
|
+
</div>
|
63
|
+
<p>If you are the application owner check the logs for more information.</p>
|
64
|
+
</div>
|
65
|
+
</body>
|
66
|
+
</html>
|
File without changes
|
data/test/flow_test.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Rworkflow
|
4
|
+
class FlowTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
RedisRds::Object.flushdb
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_workflow
|
11
|
+
lifecycle = Lifecycle.new do |lc|
|
12
|
+
lc.state("State1", {cardinality: 2}) do |state|
|
13
|
+
state.transition :pushed, Flow::STATE_SUCCESSFUL
|
14
|
+
state.transition :failed, Flow::STATE_FAILED
|
15
|
+
end
|
16
|
+
|
17
|
+
lc.initial = "State1"
|
18
|
+
end
|
19
|
+
initial_objects = [1,2,3]
|
20
|
+
workflow = Flow.create(lifecycle, 'myWorkflow')
|
21
|
+
workflow_id = workflow.id
|
22
|
+
|
23
|
+
assert Flow.registered?(workflow)
|
24
|
+
|
25
|
+
workflow.start(initial_objects)
|
26
|
+
workflow = Flow.new(workflow_id)
|
27
|
+
assert !workflow.finished?
|
28
|
+
|
29
|
+
workflow.fetch(1, 'State1') do |objects|
|
30
|
+
assert_equal 2, objects.size
|
31
|
+
assert_equal (objects & initial_objects), objects
|
32
|
+
assert !workflow.finished?
|
33
|
+
workflow.transition('State1', :pushed, objects.first)
|
34
|
+
workflow.transition('State1', :failed, objects.second)
|
35
|
+
end
|
36
|
+
|
37
|
+
assert !workflow.finished?
|
38
|
+
|
39
|
+
workflow.fetch(2, 'State1') do |last|
|
40
|
+
assert_equal 1, last.size
|
41
|
+
assert_equal (last & initial_objects), last
|
42
|
+
assert !workflow.finished?
|
43
|
+
workflow.transition('State1', :pushed, last.first)
|
44
|
+
end
|
45
|
+
|
46
|
+
assert workflow.finished?
|
47
|
+
counters = workflow.get_counters
|
48
|
+
assert_equal 2, counters[Flow::STATE_SUCCESSFUL]
|
49
|
+
assert_equal 1, counters[Flow::STATE_FAILED]
|
50
|
+
|
51
|
+
assert_equal [1,3], workflow.list_objects(Flow::STATE_SUCCESSFUL)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_flow_cardinality_all_started
|
55
|
+
lifecycle = Lifecycle.new do |lc|
|
56
|
+
lc.state("State1", {cardinality: Lifecycle::CARDINALITY_ALL_STARTED}) do |state|
|
57
|
+
state.transition :pushed, Flow::STATE_SUCCESSFUL
|
58
|
+
state.transition :failed, Flow::STATE_FAILED
|
59
|
+
end
|
60
|
+
|
61
|
+
lc.initial = "State1"
|
62
|
+
end
|
63
|
+
|
64
|
+
initial_objects = (1..6).to_a
|
65
|
+
workflow = Flow.create(lifecycle, 'myWorkflow')
|
66
|
+
workflow.start(initial_objects)
|
67
|
+
workflow.fetch(1, 'State1') do |objects|
|
68
|
+
assert_equal initial_objects.size, objects.size, 'The flow should fetch the number of objects given at the start'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_flow_state_policy_wait
|
73
|
+
initial_objects = [1,2,3,4]
|
74
|
+
lifecycle = Lifecycle.new do |lc|
|
75
|
+
lc.state("InitState", {cardinality: 1}) do |state|
|
76
|
+
state.transition :pushed, "WaitState"
|
77
|
+
end
|
78
|
+
|
79
|
+
lc.state("WaitState", {cardinality: initial_objects.size, priority: State::DEFAULT_PRIORITY, policy: State::STATE_POLICY_WAIT}) do |state|
|
80
|
+
state.transition :collected, Flow::STATE_SUCCESSFUL
|
81
|
+
end
|
82
|
+
|
83
|
+
lc.initial = "InitState"
|
84
|
+
end
|
85
|
+
|
86
|
+
workflow = Flow.create(lifecycle, 'myWorkflow')
|
87
|
+
workflow.start(initial_objects)
|
88
|
+
|
89
|
+
(initial_objects.size - 1).times do
|
90
|
+
workflow.fetch(1, 'InitState') do |objects|
|
91
|
+
assert_equal 1, objects.size, 'The flow should fetch the number of objects corresponding to the state cardinality'
|
92
|
+
workflow.transition('InitState', :pushed, objects)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
workflow.fetch(1, 'WaitState') do |objects|
|
97
|
+
# This block should not be executed
|
98
|
+
assert false, 'The collector state should not be executed until there is enough waiting objects (>= cardinality)'
|
99
|
+
end
|
100
|
+
|
101
|
+
# Lat object push in the initial state
|
102
|
+
workflow.fetch(1, 'InitState') do |objects|
|
103
|
+
assert_equal 1, objects.size, 'The flow should fetch the number of objects corresponding to the state cardinality'
|
104
|
+
workflow.transition('InitState', :pushed, objects)
|
105
|
+
end
|
106
|
+
|
107
|
+
workflow.fetch(1, 'WaitState') do |objects|
|
108
|
+
assert_equal initial_objects.size, objects.size, 'The flow should fetch the number of objects given at the start'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Rworkflow
|
4
|
+
class LifecycleTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
RedisRds::Object.flushdb
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_definition
|
11
|
+
lifecycle = Lifecycle.new do |lc|
|
12
|
+
lc.state("State1") do |state|
|
13
|
+
state.transition :pushed, :successful
|
14
|
+
end
|
15
|
+
|
16
|
+
lc.initial = 'State1'
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal 'State1', lifecycle.initial
|
20
|
+
assert_equal :successful, lifecycle.transition("State1", :pushed)
|
21
|
+
assert_raises(Rworkflow::StateError) { lifecycle.transition("UnexistingState", :pushed) }
|
22
|
+
assert_raises(Rworkflow::TransitionError) { lifecycle.transition("State1", :non_existing_transition) }
|
23
|
+
|
24
|
+
lifecycle.default = Rworkflow::Flow::STATE_FAILED
|
25
|
+
assert_equal Rworkflow::Flow::STATE_FAILED, lifecycle.default
|
26
|
+
assert_equal Rworkflow::Flow::STATE_FAILED, lifecycle.transition("State1", :non_existing_transition)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_serialization
|
30
|
+
lifecycle = Lifecycle.new do |lc|
|
31
|
+
lc.state("State1") do |state|
|
32
|
+
state.transition :pushed, :successful
|
33
|
+
end
|
34
|
+
|
35
|
+
lc.initial = "State1"
|
36
|
+
end
|
37
|
+
|
38
|
+
serialized = lifecycle.serialize
|
39
|
+
|
40
|
+
unserialized = Lifecycle.unserialize(serialized)
|
41
|
+
|
42
|
+
assert_equal lifecycle.initial, unserialized.initial
|
43
|
+
assert_equal Set.new(lifecycle.states.keys), Set.new(unserialized.states.keys)
|
44
|
+
assert lifecycle.states.all? {|name, state| unserialized.states[name].instance_eval{@transitions} == state.instance_eval{@transitions} }
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_concat
|
48
|
+
lifecycle_one = LCFactory.simple_lifecycle("1", :next)
|
49
|
+
lifecycle_two = LCFactory.simple_lifecycle("2", :finish)
|
50
|
+
|
51
|
+
lifecycle_one.concat!("1", :next, lifecycle_two)
|
52
|
+
|
53
|
+
assert_equal '1', lifecycle_one.initial
|
54
|
+
assert_equal '2', lifecycle_one.transition("1", :next)
|
55
|
+
assert_equal SidekiqFlow::STATE_SUCCESSFUL, lifecycle_one.transition("2", :finish)
|
56
|
+
|
57
|
+
lifecycle_three = LCFactory.simple_lifecycle("3", :finish)
|
58
|
+
lifecycle_three.state('1') do |s|
|
59
|
+
s.transition(:next, '3')
|
60
|
+
s.transition(:prev, '2')
|
61
|
+
end
|
62
|
+
|
63
|
+
lifecycle_one.concat!('2', :finish, lifecycle_three)
|
64
|
+
assert_equal '3', lifecycle_one.transition('1', :next)
|
65
|
+
assert_equal '2', lifecycle_one.transition('1', :prev)
|
66
|
+
assert_equal '3', lifecycle_one.transition('2', :finish)
|
67
|
+
end
|
68
|
+
|
69
|
+
class LCFactory
|
70
|
+
def self.simple_lifecycle(state_name, transition, cardinality = 1, priority = nil)
|
71
|
+
return Rworkflow::Lifecycle.new do |cycle|
|
72
|
+
cycle.state(state_name, cardinality: cardinality, priority: priority) do |state|
|
73
|
+
state.transition transition, Rworkflow::SidekiqFlow::STATE_SUCCESSFUL
|
74
|
+
state.transition :failed, Rworkflow::SidekiqFlow::STATE_FAILED
|
75
|
+
end
|
76
|
+
cycle.initial = state_name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Rworkflow
|
4
|
+
class SidekiqFlowTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
RedisRds::Object.flushdb
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_lethal_workflow
|
11
|
+
lifecycle = Lifecycle.new do |lc|
|
12
|
+
lc.state("Rworkflow::SidekiqFlowTest::Floating", {CARDINALITY_ALL_STARTEDality: 10}) do |state|
|
13
|
+
state.transition :rescued, 'Rworkflow::SidekiqFlowTest::Lifeboat'
|
14
|
+
state.transition :drowned, Rworkflow::Flow::STATE_FAILED
|
15
|
+
end
|
16
|
+
lc.state("Rworkflow::SidekiqFlowTest::Lifeboat", {cardinality: 2}) do |state|
|
17
|
+
state.transition :landed, 'Rworkflow::SidekiqFlowTest::Land'
|
18
|
+
state.transition :starved, Rworkflow::Flow::STATE_FAILED
|
19
|
+
end
|
20
|
+
lc.state("Rworkflow::SidekiqFlowTest::Land") do |state|
|
21
|
+
state.transition :rescued, Rworkflow::Flow::STATE_SUCCESSFUL
|
22
|
+
state.transition :died, Rworkflow::Flow::STATE_FAILED
|
23
|
+
end
|
24
|
+
|
25
|
+
lc.initial = "Rworkflow::SidekiqFlowTest::Floating"
|
26
|
+
end
|
27
|
+
|
28
|
+
initial_objects = (0...20).to_a
|
29
|
+
workflow = SidekiqFlow.create(lifecycle, 'Lethal Rworkflow 2: Lethaler', {})
|
30
|
+
workflow.start(initial_objects)
|
31
|
+
|
32
|
+
assert workflow.finished?
|
33
|
+
counters = workflow.get_counters
|
34
|
+
assert_equal 19, counters[Rworkflow::Flow::STATE_FAILED]
|
35
|
+
assert_equal 1, counters[Rworkflow::Flow::STATE_SUCCESSFUL]
|
36
|
+
|
37
|
+
assert 2, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Floating").get.to_i
|
38
|
+
assert 6, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Lifeboat").get.to_i
|
39
|
+
assert 2, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Land").get.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_pause_continue
|
43
|
+
lifecycle = Lifecycle.new do |lc|
|
44
|
+
lc.state("Rworkflow::SidekiqFlowTest::Floating", {cardinality: 10}) do |state|
|
45
|
+
state.transition :rescued, 'Rworkflow::SidekiqFlowTest::Lifeboat'
|
46
|
+
state.transition :drowned, Rworkflow::Flow::STATE_FAILED
|
47
|
+
end
|
48
|
+
lc.state("Rworkflow::SidekiqFlowTest::Lifeboat", {cardinality: 2}) do |state|
|
49
|
+
state.transition :landed, Rworkflow::Flow::STATE_SUCCESSFUL
|
50
|
+
state.transition :starved, Rworkflow::Flow::STATE_FAILED
|
51
|
+
end
|
52
|
+
|
53
|
+
lc.initial = "Rworkflow::SidekiqFlowTest::Floating"
|
54
|
+
end
|
55
|
+
|
56
|
+
initial_objects = (0...20).to_a
|
57
|
+
workflow = SidekiqFlow.create(lifecycle, 'Lethal Workflow 4: Lethalerest', {})
|
58
|
+
|
59
|
+
workflow.pause
|
60
|
+
workflow.start(initial_objects)
|
61
|
+
assert !workflow.finished?
|
62
|
+
workflow.pause
|
63
|
+
workflow.continue
|
64
|
+
assert !workflow.finished?
|
65
|
+
workflow.continue
|
66
|
+
assert workflow.finished?
|
67
|
+
|
68
|
+
counters = workflow.get_counters
|
69
|
+
assert_equal 18, counters[Rworkflow::Flow::STATE_FAILED]
|
70
|
+
assert_equal 2, counters[Rworkflow::Flow::STATE_SUCCESSFUL]
|
71
|
+
|
72
|
+
assert 2, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Floating").get.to_i
|
73
|
+
assert 6, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Lifeboat").get.to_i
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_collector_state_workflow
|
77
|
+
lifecycle = Lifecycle.new do |lc|
|
78
|
+
lc.state("Rworkflow::SidekiqFlowTest::PostcardSend", {cardinality: 1}) do |state|
|
79
|
+
state.transition :sent, 'Rworkflow::SidekiqFlowTest::PostcardCollector'
|
80
|
+
end
|
81
|
+
|
82
|
+
lc.state("Rworkflow::SidekiqFlowTest::PostcardCollector", {cardinality: Lifecycle::CARDINALITY_ALL_STARTED, priority: State::DEFAULT_PRIORITY, policy: State::STATE_POLICY_WAIT}) do |state|
|
83
|
+
state.transition :received, Rworkflow::Flow::STATE_SUCCESSFUL
|
84
|
+
end
|
85
|
+
|
86
|
+
lc.initial = "Rworkflow::SidekiqFlowTest::PostcardSend"
|
87
|
+
end
|
88
|
+
|
89
|
+
initial_objects = (0...20).to_a
|
90
|
+
workflow = SidekiqFlow.create(lifecycle, 'CollectorWorkflow', {})
|
91
|
+
workflow.start(initial_objects)
|
92
|
+
|
93
|
+
assert workflow.finished?, 'Rworkflow finish successfully'
|
94
|
+
assert_equal 20, RedisRds::String.new("Rworkflow::SidekiqFlowTest::PostcardSend").get.to_i, 'All initial objects should be processed by the first state one by one'
|
95
|
+
assert_equal 1, RedisRds::String.new("Rworkflow::SidekiqFlowTest::PostcardSend_card").get.to_i, 'All initial objects should be processed by the first state one by one'
|
96
|
+
assert_equal 1, RedisRds::String.new("Rworkflow::SidekiqFlowTest::PostcardCollector").get.to_i, 'All initial objects should be processed by the collector state all at once'
|
97
|
+
assert_equal initial_objects.size, RedisRds::String.new("Rworkflow::SidekiqFlowTest::PostcardCollector_card").get.to_i, 'All initial objects should be processed by the collector state all at once'
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_gated
|
101
|
+
lifecycle = Lifecycle.new do |lc|
|
102
|
+
lc.state("Rworkflow::SidekiqFlowTest::Floating", {cardinality: 10}) do |state|
|
103
|
+
state.transition :rescued, 'Rworkflow::SidekiqFlowTest::Lifeboat'
|
104
|
+
state.transition :drowned, Rworkflow::Flow::STATE_FAILED
|
105
|
+
end
|
106
|
+
lc.state("Rworkflow::SidekiqFlowTest::Lifeboat", {cardinality: 2, policy: SidekiqFlow::STATE_POLICY_GATED}) do |state|
|
107
|
+
state.transition :landed, Rworkflow::Flow::STATE_SUCCESSFUL
|
108
|
+
state.transition :starved, Rworkflow::Flow::STATE_FAILED
|
109
|
+
end
|
110
|
+
|
111
|
+
lc.initial = "Rworkflow::SidekiqFlowTest::Floating"
|
112
|
+
end
|
113
|
+
|
114
|
+
initial_objects = (0...20).to_a
|
115
|
+
workflow = SidekiqFlow.create(lifecycle, 'Lethal Workflow 4: Lethalerest', {})
|
116
|
+
|
117
|
+
workflow.start(initial_objects)
|
118
|
+
assert !workflow.finished?
|
119
|
+
workflow.open_gate('Rworkflow::SidekiqFlowTest::Lifeboat')
|
120
|
+
assert workflow.finished?
|
121
|
+
|
122
|
+
counters = workflow.get_counters
|
123
|
+
assert_equal 18, counters[Rworkflow::Flow::STATE_FAILED]
|
124
|
+
assert_equal 2, counters[Rworkflow::Flow::STATE_SUCCESSFUL]
|
125
|
+
|
126
|
+
assert 2, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Floating").get.to_i
|
127
|
+
assert 6, RedisRds::String.new("Rworkflow::SidekiqFlowTest::Lifeboat").get.to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
class Floating < Worker
|
131
|
+
def process(objects)
|
132
|
+
rescued, drowned = objects.partition { |object| object.even? }
|
133
|
+
transition(:rescued, rescued)
|
134
|
+
transition(:drowned, drowned)
|
135
|
+
RedisRds::String.new(self.class.name).incr
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Lifeboat < Worker
|
140
|
+
def process(objects)
|
141
|
+
landed, starved = objects.partition { |object| object < 4 }
|
142
|
+
transition(:landed, landed)
|
143
|
+
transition(:starved, starved)
|
144
|
+
RedisRds::String.new(self.class.name).incr
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Land < Worker
|
149
|
+
def process(objects)
|
150
|
+
rescued, died = objects.partition { |object| object == 0 }
|
151
|
+
transition(:rescued, rescued)
|
152
|
+
transition(:died, died)
|
153
|
+
RedisRds::String.new(self.class.name).incr
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class PostcardSend < Worker
|
158
|
+
def process(objects)
|
159
|
+
transition(:sent, objects)
|
160
|
+
RedisRds::String.new(self.class.name).incr
|
161
|
+
RedisRds::String.new("#{self.class.name}_card").set(objects.size)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class PostcardCollector < Worker
|
166
|
+
def process(objects)
|
167
|
+
transition(:received, objects)
|
168
|
+
RedisRds::String.new(self.class.name).incr
|
169
|
+
RedisRds::String.new("#{self.class.name}_card").set(objects.size)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/test/state_test.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Rworkflow
|
4
|
+
class StateTest < ActiveSupport::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@state = State.new
|
8
|
+
RedisRds::Object.flushdb
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_transition
|
16
|
+
@state.transition('a', 'b')
|
17
|
+
@state.transition('b', 'c')
|
18
|
+
|
19
|
+
assert_equal 'b', @state.transitions['a'], 'Transition A->B missing.'
|
20
|
+
assert_equal 'c', @state.transitions['b'], 'Transition B->C missing.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_perform
|
24
|
+
@state.transition('a', 'b')
|
25
|
+
@state.transition('b', 'c')
|
26
|
+
|
27
|
+
assert_equal 'b', @state.perform('a'), 'Transition A->B missing.'
|
28
|
+
assert_equal 'c', @state.perform('b'), 'Transition B->C missing.'
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_equality
|
32
|
+
other_state = State.new
|
33
|
+
|
34
|
+
assert_equal @state, other_state, 'State A and B should be equal.'
|
35
|
+
|
36
|
+
other_state.policy = State::STATE_POLICY_WAIT
|
37
|
+
assert_not_equal @state, other_state, 'State A != B: different policies!'
|
38
|
+
|
39
|
+
other_state = State.new
|
40
|
+
other_state.priority = :high
|
41
|
+
assert_not_equal @state, other_state, 'State A != B: different priorities!'
|
42
|
+
|
43
|
+
other_state = State.new
|
44
|
+
other_state.cardinality = 32
|
45
|
+
assert_not_equal @state, other_state, 'State A != B: different cardinalities!'
|
46
|
+
|
47
|
+
other_state = State.new
|
48
|
+
other_state.transition('a', 'b')
|
49
|
+
other_state.transition('b', 'c')
|
50
|
+
@state.transition('a', 'b')
|
51
|
+
@state.transition('b', 'c')
|
52
|
+
assert_equal @state, other_state, 'State A === B: same transitions!'
|
53
|
+
|
54
|
+
other_state.transition('c', 'd')
|
55
|
+
assert_not_equal @state, other_state, 'State A != B: different transitions!'
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_serialization
|
59
|
+
other_state = State.new
|
60
|
+
other_state.transition('a', 'b')
|
61
|
+
other_state.transition('b', 'c')
|
62
|
+
assert_not_equal @state.serialize, other_state.serialize, 'State A should not equal B: serialization failed'
|
63
|
+
|
64
|
+
@state.transition('a', 'b')
|
65
|
+
@state.transition('b', 'c')
|
66
|
+
assert_equal @state.serialize, other_state.serialize, 'State A should equal B: serialization failed'
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_clone
|
70
|
+
cloned = @state.clone
|
71
|
+
assert_equal @state, cloned, 'Original and cloned states should be equal'
|
72
|
+
assert !@state.equal?(cloned), 'Original and cloned states should not be the same object'
|
73
|
+
|
74
|
+
@state.transition('a', 'b')
|
75
|
+
@state.policy = State::STATE_POLICY_WAIT
|
76
|
+
@state.cardinality = 2
|
77
|
+
@state.priority = :high
|
78
|
+
cloned = @state.clone
|
79
|
+
assert_equal @state, cloned, 'Original and cloned states should be equal'
|
80
|
+
assert !@state.equal?(cloned), 'Original and cloned states should not be the same object'
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_merge
|
84
|
+
other_state = State.new(cardinality: 2, priority: :high, policy: State::STATE_POLICY_WAIT)
|
85
|
+
merged = @state.merge(other_state)
|
86
|
+
assert_equal merged, other_state, 'Merged state should be equal to state B'
|
87
|
+
|
88
|
+
other_state.transition('a', 'b')
|
89
|
+
other_state.transition('b', 'c')
|
90
|
+
@state.transition('a', 'c')
|
91
|
+
@state.transition('c', 'd')
|
92
|
+
|
93
|
+
expected_state = other_state.clone
|
94
|
+
expected_state.transition('c', 'd')
|
95
|
+
merged = @state.merge(other_state)
|
96
|
+
assert_equal merged, expected_state, 'Merged state should have same properties as B plus additional transition C->D'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Configure Rails Environment
|
2
|
+
ENV['RAILS_ENV'] = 'test'
|
3
|
+
|
4
|
+
require 'simplecov'
|
5
|
+
require 'coveralls'
|
6
|
+
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
Coveralls::SimpleCov::Formatter
|
10
|
+
]
|
11
|
+
SimpleCov.start
|
12
|
+
|
13
|
+
require File.expand_path('../dummy/config/environment.rb', __FILE__)
|
14
|
+
require 'rails/test_help'
|
15
|
+
|
16
|
+
Rails.backtrace_cleaner.remove_silencers!
|
17
|
+
|
18
|
+
connection = Redis.new(
|
19
|
+
host: 'localhost',
|
20
|
+
db: 1,
|
21
|
+
port: 6379,
|
22
|
+
timeout: 30,
|
23
|
+
thread_safe: true
|
24
|
+
)
|
25
|
+
|
26
|
+
RedisRds.configure(
|
27
|
+
connection: connection,
|
28
|
+
namespace: 'testns'
|
29
|
+
)
|
30
|
+
|
31
|
+
# Load support files
|
32
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|