rworkflow 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +117 -0
  4. data/lib/rworkflow/flow.rb +450 -0
  5. data/lib/rworkflow/flow_registry.rb +69 -0
  6. data/lib/rworkflow/lifecycle.rb +102 -0
  7. data/lib/rworkflow/minitest/test.rb +53 -0
  8. data/lib/rworkflow/minitest/worker.rb +17 -0
  9. data/lib/rworkflow/minitest.rb +8 -0
  10. data/lib/rworkflow/sidekiq_flow.rb +186 -0
  11. data/lib/rworkflow/sidekiq_helper.rb +84 -0
  12. data/lib/rworkflow/sidekiq_lifecycle.rb +8 -0
  13. data/lib/rworkflow/sidekiq_state.rb +42 -0
  14. data/lib/rworkflow/state.rb +104 -0
  15. data/lib/rworkflow/state_error.rb +13 -0
  16. data/lib/rworkflow/transition_error.rb +9 -0
  17. data/lib/rworkflow/version.rb +3 -0
  18. data/lib/rworkflow/worker.rb +62 -0
  19. data/lib/rworkflow.rb +15 -0
  20. data/lib/tasks/rworkflow_tasks.rake +4 -0
  21. data/test/dummy/README.rdoc +28 -0
  22. data/test/dummy/Rakefile +6 -0
  23. data/test/dummy/app/assets/javascripts/application.js +13 -0
  24. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  25. data/test/dummy/app/controllers/application_controller.rb +5 -0
  26. data/test/dummy/app/helpers/application_helper.rb +2 -0
  27. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  28. data/test/dummy/bin/bundle +3 -0
  29. data/test/dummy/bin/rails +4 -0
  30. data/test/dummy/bin/rake +4 -0
  31. data/test/dummy/config/application.rb +15 -0
  32. data/test/dummy/config/boot.rb +5 -0
  33. data/test/dummy/config/database.yml +25 -0
  34. data/test/dummy/config/environment.rb +5 -0
  35. data/test/dummy/config/environments/development.rb +37 -0
  36. data/test/dummy/config/environments/production.rb +83 -0
  37. data/test/dummy/config/environments/test.rb +41 -0
  38. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  39. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  40. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  41. data/test/dummy/config/initializers/inflections.rb +16 -0
  42. data/test/dummy/config/initializers/mime_types.rb +4 -0
  43. data/test/dummy/config/initializers/session_store.rb +3 -0
  44. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/test/dummy/config/locales/en.yml +23 -0
  46. data/test/dummy/config/routes.rb +56 -0
  47. data/test/dummy/config/secrets.yml +22 -0
  48. data/test/dummy/config.ru +4 -0
  49. data/test/dummy/db/test.sqlite3 +0 -0
  50. data/test/dummy/log/test.log +516 -0
  51. data/test/dummy/public/404.html +67 -0
  52. data/test/dummy/public/422.html +67 -0
  53. data/test/dummy/public/500.html +66 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/flow_test.rb +112 -0
  56. data/test/lifecycle_test.rb +81 -0
  57. data/test/rworkflow_test.rb +7 -0
  58. data/test/sidekiq_flow_test.rb +173 -0
  59. data/test/state_test.rb +99 -0
  60. data/test/test_helper.rb +32 -0
  61. 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,7 @@
1
+ require 'test_helper'
2
+
3
+ class RworkflowTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, Rworkflow
6
+ end
7
+ 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
@@ -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
@@ -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 }