state_machines_rspec 0.3.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 +19 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +8 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +6 -0
- data/lib/matchers/events/handle_event.rb +35 -0
- data/lib/matchers/events/matcher.rb +71 -0
- data/lib/matchers/events/reject_event.rb +35 -0
- data/lib/matchers/states/have_state.rb +46 -0
- data/lib/matchers/states/matcher.rb +56 -0
- data/lib/matchers/states/reject_state.rb +34 -0
- data/lib/state_machines_rspec.rb +11 -0
- data/lib/state_machines_rspec/state_machines_introspector.rb +70 -0
- data/lib/state_machines_rspec/version.rb +3 -0
- data/spec/integration/integration_spec.rb +327 -0
- data/spec/integration/models/vehicle.rb +119 -0
- data/spec/matchers/events/handle_event_spec.rb +133 -0
- data/spec/matchers/events/reject_event_spec.rb +133 -0
- data/spec/matchers/states/have_state_spec.rb +136 -0
- data/spec/matchers/states/reject_state_spec.rb +97 -0
- data/spec/spec_helper.rb +12 -0
- data/state_machines_rspec.gemspec +33 -0
- metadata +233 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachinesRspec::Matchers::HandleEventMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
context 'when :when state is specified' do
|
6
|
+
context 'but the state doesn\'t exist' do
|
7
|
+
before do
|
8
|
+
matcher_class = Class.new do
|
9
|
+
state_machine :state, initial: :mathy
|
10
|
+
end
|
11
|
+
@matcher_subject = matcher_class.new
|
12
|
+
@matcher = described_class.new([when: :artsy])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises' do
|
16
|
+
expect { @matcher.matches? @matcher_subject }.
|
17
|
+
to raise_error StateMachinesIntrospectorError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and that state exists' do
|
22
|
+
before do
|
23
|
+
matcher_class = Class.new do
|
24
|
+
state_machine :state, initial: :mathy do
|
25
|
+
state :artsy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@matcher_subject = matcher_class.new
|
29
|
+
@matcher = described_class.new([when: :artsy])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the state' do
|
33
|
+
@matcher.matches? @matcher_subject
|
34
|
+
expect(@matcher_subject.state).to eq 'artsy'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when subject can perform events' do
|
40
|
+
before do
|
41
|
+
matcher_class = Class.new do
|
42
|
+
state_machine :mathiness, initial: :mathy do
|
43
|
+
event(:mathematize) { transition any => same }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@matcher_subject = matcher_class.new
|
47
|
+
@matcher = described_class.new([:mathematize, on: :mathiness])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not set a failure message' do
|
51
|
+
@matcher.matches? @matcher_subject
|
52
|
+
expect(@matcher.failure_message).to be_nil
|
53
|
+
end
|
54
|
+
it 'returns true' do
|
55
|
+
expect(@matcher.matches?(@matcher_subject)).to be_truthy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when subject cannot perform events' do
|
60
|
+
before do
|
61
|
+
matcher_class = Class.new do
|
62
|
+
state_machine :state, initial: :mathy do
|
63
|
+
state :polynomial
|
64
|
+
|
65
|
+
event(:mathematize) { transition any => same }
|
66
|
+
event(:algebraify) { transition :polynomial => same }
|
67
|
+
event(:trigonomalize) { transition :trigonomalize => same }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@matcher_subject = matcher_class.new
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'because it cannot perform the transition' do
|
74
|
+
before do
|
75
|
+
@matcher = described_class.new([:mathematize, :algebraify, :trigonomalize])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets a failure message' do
|
79
|
+
@matcher.matches? @matcher_subject
|
80
|
+
expect(@matcher.failure_message).to eq('Expected to be able to handle events: algebraify, trigonomalize ' +
|
81
|
+
'in state: mathy')
|
82
|
+
end
|
83
|
+
it 'returns false' do
|
84
|
+
expect(@matcher.matches?(@matcher_subject)).to be_falsey
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'because no such events exist' do
|
89
|
+
before do
|
90
|
+
@matcher = described_class.new([:polynomialize, :eulerasterize])
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'does not raise' do
|
94
|
+
expect { @matcher.matches?(@matcher_subject) }.not_to raise_error
|
95
|
+
end
|
96
|
+
it 'sets a failure message' do
|
97
|
+
@matcher.matches? @matcher_subject
|
98
|
+
expect(@matcher.failure_message).to eq('state_machine: state does not ' +
|
99
|
+
'define events: polynomialize, eulerasterize')
|
100
|
+
end
|
101
|
+
it 'returns false' do
|
102
|
+
expect(@matcher.matches?(@matcher_subject)).to be_falsey
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#description' do
|
109
|
+
context 'with no options' do
|
110
|
+
let(:matcher) { described_class.new([:placate, :mollify]) }
|
111
|
+
|
112
|
+
it 'returns a string description' do
|
113
|
+
expect(matcher.description).to eq('handle :placate, :mollify')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when :when state is specified' do
|
118
|
+
let(:matcher) { described_class.new([:destroy_food, when: :hangry]) }
|
119
|
+
|
120
|
+
it 'mentions the requisite state' do
|
121
|
+
expect(matcher.description).to eq('handle :destroy_food when :hangry')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'when :on is specified' do
|
126
|
+
let(:matcher) { described_class.new([:ensmarmify, on: :tired_investors]) }
|
127
|
+
|
128
|
+
it 'mentions the state machines variable' do
|
129
|
+
expect(matcher.description).to eq('handle :ensmarmify on :tired_investors')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachinesRspec::Matchers::RejectEventMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
context 'when :when state is specified' do
|
6
|
+
context 'but that state doesn\'t exist' do
|
7
|
+
before do
|
8
|
+
matcher_class = Class.new do
|
9
|
+
state_machine :state, initial: :sleazy
|
10
|
+
end
|
11
|
+
@matcher_subject = matcher_class.new
|
12
|
+
@matcher = described_class.new([when: :sneezy])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises' do
|
16
|
+
expect { @matcher.matches? @matcher_subject }.
|
17
|
+
to raise_error StateMachinesIntrospectorError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and that state exists' do
|
22
|
+
before do
|
23
|
+
matcher_class = Class.new do
|
24
|
+
state_machine :state, initial: :sleazy do
|
25
|
+
state :sneezy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@matcher_subject = matcher_class.new
|
29
|
+
@matcher = described_class.new([when: :sneezy])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the state' do
|
33
|
+
@matcher.matches? @matcher_subject
|
34
|
+
expect(@matcher_subject.state).to eq('sneezy')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when an expectation is made on an event that is undefined' do
|
40
|
+
before do
|
41
|
+
matcher_class = Class.new do
|
42
|
+
state_machine :state, initial: :snarky do
|
43
|
+
event(:primmadonnalize) { transition any => same }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@matcher_subject = matcher_class.new
|
47
|
+
@matcher = described_class.new([:primmadonnalize, :martinilunchitize])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not raise' do
|
51
|
+
expect { @matcher.matches?(@matcher_subject) }.not_to raise_error
|
52
|
+
end
|
53
|
+
it 'sets a failure message' do
|
54
|
+
@matcher.matches? @matcher_subject
|
55
|
+
expect(@matcher.failure_message).to eq('state_machine: state does not ' +
|
56
|
+
'define events: martinilunchitize')
|
57
|
+
end
|
58
|
+
it 'returns false' do
|
59
|
+
expect(@matcher.matches?(@matcher_subject)).to be_falsey
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when subject cannot perform any of the specified events' do
|
64
|
+
before do
|
65
|
+
matcher_class = Class.new do
|
66
|
+
state_machine :state, initial: :snarky do
|
67
|
+
state :haughty
|
68
|
+
event(:primmadonnalize) { transition :haughty => same }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@matcher_subject = matcher_class.new
|
72
|
+
@matcher = described_class.new([:primmadonnalize])
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'does not set a failure message' do
|
76
|
+
@matcher.matches? @matcher_subject
|
77
|
+
expect(@matcher.failure_message).to be_nil
|
78
|
+
end
|
79
|
+
it 'returns true' do
|
80
|
+
expect(@matcher.matches?(@matcher_subject)).to be_truthy
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when subject can perform any one of the specified events' do
|
85
|
+
before do
|
86
|
+
matcher_class = Class.new do
|
87
|
+
state_machine :state, initial: :snarky do
|
88
|
+
state :haughty
|
89
|
+
event(:primmadonnalize) { transition :haughty => same }
|
90
|
+
event(:defer_to_management) { transition any => same }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
@matcher_subject = matcher_class.new
|
94
|
+
@matcher = described_class.new([:primmadonnalize, :defer_to_management])
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets a failure message' do
|
98
|
+
@matcher.matches? @matcher_subject
|
99
|
+
expect(@matcher.failure_message).to eq('Did not expect to be able to handle events: defer_to_management ' +
|
100
|
+
'in state: snarky')
|
101
|
+
end
|
102
|
+
it 'returns false' do
|
103
|
+
expect(@matcher.matches?(@matcher_subject)).to be_falsey
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#description' do
|
109
|
+
context 'with no options' do
|
110
|
+
let(:matcher) { described_class.new([:makeadealify, :hustlinate]) }
|
111
|
+
|
112
|
+
it 'returns a string description' do
|
113
|
+
expect(matcher.description).to eq('reject :makeadealify, :hustlinate')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when :when state is specified' do
|
118
|
+
let(:matcher) { described_class.new([:begargle, when: :sleep_encrusted]) }
|
119
|
+
|
120
|
+
it 'mentions the requisite state' do
|
121
|
+
expect(matcher.description).to eq('reject :begargle when :sleep_encrusted')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'when :on is specified' do
|
126
|
+
let(:matcher) { described_class.new([:harrangue, on: :suspicious_crowd]) }
|
127
|
+
|
128
|
+
it 'mentions the state machines variable' do
|
129
|
+
expect(matcher.description).to eq('reject :harrangue on :suspicious_crowd')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachinesRspec::Matchers::HaveStateMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
before { @matcher = described_class.new([:rad, :not_so_rad, { on: :radical_state }]) }
|
6
|
+
|
7
|
+
context 'when values are asserted on multiple states' do
|
8
|
+
before do
|
9
|
+
@matcher = described_class.new([:rad, :not_so_rad, { value: 'rad' }])
|
10
|
+
end
|
11
|
+
it 'raises an ArgumentError' do
|
12
|
+
expect { @matcher.matches? nil }.to raise_error ArgumentError,
|
13
|
+
'cannot make value assertions on multiple states at once'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when class does not have a matching state attribute' do
|
18
|
+
before do
|
19
|
+
@class = Class.new do
|
20
|
+
state_machine :bodacious_state, initial: :super_bodacious
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'raises' do
|
25
|
+
expect { @matcher.matches? @class.new }.
|
26
|
+
to raise_error StateMachinesIntrospectorError,
|
27
|
+
/.+? does not have a state machine defined on radical_state/
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when class has a matching state attribute' do
|
32
|
+
context 'but is missing some of the specified states' do
|
33
|
+
before do
|
34
|
+
@class = Class.new do
|
35
|
+
state_machine :radical_state do
|
36
|
+
state :not_so_rad
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'sets a failure message indicating a state is missing' do
|
42
|
+
@matcher.matches? @class.new
|
43
|
+
expect(@matcher.failure_message).to eq 'Expected radical_state to allow states: rad'
|
44
|
+
end
|
45
|
+
it 'returns false' do
|
46
|
+
expect(@matcher.matches?(@class.new)).to be_falsey
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'and has all states specified' do
|
51
|
+
before do
|
52
|
+
@class = Class.new do
|
53
|
+
state_machine :radical_state do
|
54
|
+
state :rad, value: 'totes rad'
|
55
|
+
state :not_so_rad, value: 'meh'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'state values not specified' do
|
61
|
+
it 'does not set a failure message' do
|
62
|
+
@matcher.matches? @class.new
|
63
|
+
expect(@matcher.failure_message).to be_nil
|
64
|
+
end
|
65
|
+
it 'returns true' do
|
66
|
+
expect(@matcher.matches?(@class.new)).to be_truthy
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'state value matches specified value' do
|
71
|
+
before do
|
72
|
+
@matcher = described_class.new([:rad, { on: :radical_state, value: 'uber-rad' }])
|
73
|
+
@class = Class.new do
|
74
|
+
state_machine :radical_state do
|
75
|
+
state :rad, value: 'uber-rad'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'does not set a failure message' do
|
81
|
+
@matcher.matches? @class.new
|
82
|
+
expect(@matcher.failure_message).to be_nil
|
83
|
+
end
|
84
|
+
it 'returns true' do
|
85
|
+
expect(@matcher.matches?(@class.new)).to be_truthy
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'state value does not match specified value' do
|
90
|
+
before do
|
91
|
+
@matcher = described_class.new([:rad, { on: :radical_state, value: 'uber-rad' }])
|
92
|
+
@class = Class.new do
|
93
|
+
state_machine :radical_state do
|
94
|
+
state :rad, value: 'kinda rad'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'does not set a failure message' do
|
100
|
+
@matcher.matches? @class.new
|
101
|
+
expect(@matcher.failure_message).to eq 'Expected rad to have value uber-rad'
|
102
|
+
end
|
103
|
+
it 'returns true' do
|
104
|
+
expect(@matcher.matches?(@class.new)).to be_falsey
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#description' do
|
112
|
+
context 'with no options' do
|
113
|
+
let(:matcher) { described_class.new([:fancy_shirt, :cracked_toenail]) }
|
114
|
+
|
115
|
+
it 'returns a string description' do
|
116
|
+
expect(matcher.description).to eq('have :fancy_shirt, :cracked_toenail')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when :value is specified' do
|
121
|
+
let(:matcher) { described_class.new([:mustache, value: :really_shady]) }
|
122
|
+
|
123
|
+
it 'mentions the requisite state' do
|
124
|
+
expect(matcher.description).to eq('have :mustache == :really_shady')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when :on state machines is specified' do
|
129
|
+
let(:matcher) { described_class.new([:lunch, on: :tuesday]) }
|
130
|
+
|
131
|
+
it 'mentions the state machines variable' do
|
132
|
+
expect(matcher.description).to eq('have :lunch on :tuesday')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachinesRspec::Matchers::RejectStateMatcher do
|
4
|
+
describe '#matches?' do
|
5
|
+
context 'when :on state machines attribute is specified' do
|
6
|
+
before { @matcher = described_class.new([:supportive, on: :environment]) }
|
7
|
+
context 'but that state machines doesn\'t exist' do
|
8
|
+
before { @class = Class.new }
|
9
|
+
it 'raises' do
|
10
|
+
expect { @matcher.matches? @class.new }.to raise_error
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'and that state machines exists' do
|
15
|
+
context 'but it defines states which match one of the specified states' do
|
16
|
+
before do
|
17
|
+
@class = Class.new do
|
18
|
+
state_machine :environment, initial: :supportive
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets a failure message' do
|
23
|
+
@matcher.matches? @class.new
|
24
|
+
expect(@matcher.failure_message).to eq('Did not expect environment to allow states: supportive')
|
25
|
+
end
|
26
|
+
it 'returns false' do
|
27
|
+
expect(@matcher.matches?(@class.new)).to be_falsey
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'and it does not define any of the states specified' do
|
32
|
+
before do
|
33
|
+
@class = Class.new do
|
34
|
+
state_machine :environment, initial: :conducive
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not set a failure message' do
|
39
|
+
@matcher.matches? @class.new
|
40
|
+
expect(@matcher.failure_message).to be_nil
|
41
|
+
end
|
42
|
+
it 'returns true' do
|
43
|
+
expect(@matcher.matches?(@class.new)).to be_truthy
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when :on state machines is not specified' do
|
50
|
+
before { @matcher = described_class.new([:ever_changing]) }
|
51
|
+
context 'but the default state machines defines states which match one of the specified states' do
|
52
|
+
before do
|
53
|
+
@class = Class.new do
|
54
|
+
state_machine initial: :ever_changing
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'sets a failure message' do
|
59
|
+
@matcher.matches? @class.new
|
60
|
+
expect(@matcher.failure_message).to eq('Did not expect state to allow states: ever_changing')
|
61
|
+
end
|
62
|
+
it 'returns false' do
|
63
|
+
expect(@matcher.matches?(@class.new)).to be_falsey
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'and the default state machines does not define any of the states specified' do
|
68
|
+
before { @class = Class.new }
|
69
|
+
it 'does not set a failure message' do
|
70
|
+
@matcher.matches? @class.new
|
71
|
+
expect(@matcher.failure_message).to be_nil
|
72
|
+
end
|
73
|
+
it 'returns true' do
|
74
|
+
expect(@matcher.matches?(@class.new)).to be_truthy
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#description' do
|
81
|
+
context 'with no options' do
|
82
|
+
let(:matcher) { described_class.new([:mustard, :tomatoes]) }
|
83
|
+
|
84
|
+
it 'returns a string description' do
|
85
|
+
expect(matcher.description).to eq('not have :mustard, :tomatoes')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when :on state machines is specified' do
|
90
|
+
let(:matcher) { described_class.new([:peanut_butter, on: :toast]) }
|
91
|
+
|
92
|
+
it 'mentions the state machines variable' do
|
93
|
+
expect(matcher.description).to eq('not have :peanut_butter on :toast')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|