rworkflow 0.6.1

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 (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 }