stealth 1.1.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +18 -8
  3. data/CHANGELOG.md +100 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +49 -43
  6. data/LICENSE +4 -17
  7. data/README.md +9 -17
  8. data/VERSION +1 -1
  9. data/lib/stealth/base.rb +62 -13
  10. data/lib/stealth/cli.rb +1 -2
  11. data/lib/stealth/commands/console.rb +1 -1
  12. data/lib/stealth/configuration.rb +0 -3
  13. data/lib/stealth/controller/callbacks.rb +1 -1
  14. data/lib/stealth/controller/catch_all.rb +27 -4
  15. data/lib/stealth/controller/controller.rb +168 -49
  16. data/lib/stealth/controller/dev_jumps.rb +41 -0
  17. data/lib/stealth/controller/dynamic_delay.rb +4 -6
  18. data/lib/stealth/controller/interrupt_detect.rb +100 -0
  19. data/lib/stealth/controller/messages.rb +283 -0
  20. data/lib/stealth/controller/nlp.rb +50 -0
  21. data/lib/stealth/controller/replies.rb +179 -41
  22. data/lib/stealth/controller/unrecognized_message.rb +62 -0
  23. data/lib/stealth/core_ext.rb +5 -0
  24. data/lib/stealth/{flow/core_ext.rb → core_ext/numeric.rb} +0 -1
  25. data/lib/stealth/core_ext/string.rb +18 -0
  26. data/lib/stealth/dispatcher.rb +21 -0
  27. data/lib/stealth/errors.rb +12 -0
  28. data/lib/stealth/flow/base.rb +1 -2
  29. data/lib/stealth/flow/specification.rb +3 -2
  30. data/lib/stealth/flow/state.rb +3 -3
  31. data/lib/stealth/generators/builder/Gemfile +4 -3
  32. data/lib/stealth/generators/builder/bot/controllers/bot_controller.rb +42 -0
  33. data/lib/stealth/generators/builder/bot/controllers/catch_alls_controller.rb +2 -0
  34. data/lib/stealth/generators/builder/bot/controllers/goodbyes_controller.rb +2 -0
  35. data/lib/stealth/generators/builder/bot/controllers/hellos_controller.rb +2 -0
  36. data/lib/stealth/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  37. data/lib/stealth/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  38. data/lib/stealth/generators/builder/config/flow_map.rb +8 -0
  39. data/lib/stealth/generators/builder/config/initializers/autoload.rb +8 -0
  40. data/lib/stealth/generators/builder/config/initializers/inflections.rb +16 -0
  41. data/lib/stealth/generators/builder/config/puma.rb +15 -0
  42. data/lib/stealth/helpers/redis.rb +40 -0
  43. data/lib/stealth/lock.rb +83 -0
  44. data/lib/stealth/logger.rb +27 -18
  45. data/lib/stealth/nlp/client.rb +22 -0
  46. data/lib/stealth/nlp/result.rb +57 -0
  47. data/lib/stealth/reloader.rb +90 -0
  48. data/lib/stealth/reply.rb +17 -0
  49. data/lib/stealth/scheduled_reply.rb +3 -3
  50. data/lib/stealth/server.rb +4 -4
  51. data/lib/stealth/service_message.rb +3 -2
  52. data/lib/stealth/service_reply.rb +5 -1
  53. data/lib/stealth/services/base_reply_handler.rb +2 -2
  54. data/lib/stealth/session.rb +106 -53
  55. data/spec/configuration_spec.rb +9 -2
  56. data/spec/controller/callbacks_spec.rb +23 -28
  57. data/spec/controller/catch_all_spec.rb +81 -29
  58. data/spec/controller/controller_spec.rb +444 -43
  59. data/spec/controller/dynamic_delay_spec.rb +16 -18
  60. data/spec/controller/helpers_spec.rb +1 -2
  61. data/spec/controller/interrupt_detect_spec.rb +171 -0
  62. data/spec/controller/messages_spec.rb +744 -0
  63. data/spec/controller/nlp_spec.rb +93 -0
  64. data/spec/controller/replies_spec.rb +446 -11
  65. data/spec/controller/unrecognized_message_spec.rb +168 -0
  66. data/spec/dispatcher_spec.rb +79 -0
  67. data/spec/flow/flow_spec.rb +1 -2
  68. data/spec/flow/state_spec.rb +14 -3
  69. data/spec/helpers/redis_spec.rb +77 -0
  70. data/spec/lock_spec.rb +100 -0
  71. data/spec/nlp/client_spec.rb +23 -0
  72. data/spec/nlp/result_spec.rb +57 -0
  73. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  74. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  75. data/spec/replies/messages/say_randomize_text.yml +10 -0
  76. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  77. data/spec/reply_spec.rb +61 -0
  78. data/spec/scheduled_reply_spec.rb +23 -0
  79. data/spec/service_reply_spec.rb +1 -2
  80. data/spec/session_spec.rb +251 -12
  81. data/spec/spec_helper.rb +21 -0
  82. data/spec/support/controllers/vaders_controller.rb +24 -0
  83. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  84. data/spec/support/nlp_clients/luis.rb +9 -0
  85. data/spec/support/nlp_results/luis_result.rb +163 -0
  86. data/spec/version_spec.rb +1 -2
  87. data/stealth.gemspec +6 -6
  88. metadata +83 -39
  89. data/docs/00-introduction.md +0 -37
  90. data/docs/01-getting-started.md +0 -21
  91. data/docs/02-local-development.md +0 -40
  92. data/docs/03-basics.md +0 -171
  93. data/docs/04-sessions.md +0 -29
  94. data/docs/05-controllers.md +0 -179
  95. data/docs/06-models.md +0 -39
  96. data/docs/07-replies.md +0 -114
  97. data/docs/08-catchalls.md +0 -49
  98. data/docs/09-messaging-integrations.md +0 -80
  99. data/docs/10-nlp-integrations.md +0 -13
  100. data/docs/11-analytics.md +0 -13
  101. data/docs/12-commands.md +0 -62
  102. data/docs/13-deployment.md +0 -50
  103. data/lib/stealth/generators/builder/config/initializers/.keep +0 -0
@@ -1,7 +1,6 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+ require 'spec_helper'
5
4
 
6
5
  describe "Stealth::Configuration" do
7
6
 
@@ -29,6 +28,14 @@ describe "Stealth::Configuration" do
29
28
  it "should handle multiple keys at the root level" do
30
29
  expect(config.twilio_sms.account_sid).to eq parsed_config['twilio_sms']['account_sid']
31
30
  end
31
+
32
+ it "should return nil if the key is not present at the node" do
33
+ expect(config.twilio_sms.api_key).to be nil
34
+ end
35
+
36
+ it "should raise a NoMethodError when accessing multi-levels of missing nodes" do
37
+ expect { config.slack.api_key }.to raise_error(NoMethodError)
38
+ end
32
39
  end
33
40
 
34
41
  describe "config files with ERB" do
@@ -1,57 +1,48 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
3
+ require 'spec_helper'
4
+
5
+ $history = []
5
6
 
6
7
  class BotController < Stealth::Controller
7
8
  before_action :fetch_user_name
8
9
 
9
- attr_accessor :record
10
-
11
10
  def some_action
12
- @record = []
13
11
  step_to flow: 'flow_tester', state: 'my_action'
14
12
  end
15
13
 
16
14
  def other_action
17
- @record = []
18
15
  step_to flow: 'other_flow_tester', state: 'other_action'
19
16
  end
20
17
 
21
18
  def halted_action
22
- @record = []
23
19
  step_to flow: 'flow_tester', state: 'my_action2'
24
20
  end
25
21
 
26
22
  def filtered_action
27
- @record = []
28
23
  step_to flow: 'flow_tester', state: 'my_action3'
29
24
  end
30
25
 
31
26
  def some_other_action2
32
- @record = []
33
27
  step_to flow: 'other_flow_tester', state: 'other_action2'
34
28
  end
35
29
 
36
30
  def some_other_action3
37
- @record = []
38
31
  step_to flow: 'other_flow_tester', state: 'other_action3'
39
32
  end
40
33
 
41
34
  def some_other_action4
42
- @record = []
43
35
  step_to flow: 'other_flow_tester', state: 'other_action4'
44
36
  end
45
37
 
46
38
  def some_other_action5
47
- @record = []
48
39
  step_to flow: 'other_flow_tester', state: 'other_action5'
49
40
  end
50
41
 
51
42
  private
52
43
 
53
44
  def fetch_user_name
54
- @record << "fetched user name"
45
+ $history << "fetched user name"
55
46
  end
56
47
  end
57
48
 
@@ -77,7 +68,7 @@ class FlowTestersController < BotController
77
68
  protected
78
69
 
79
70
  def test_action
80
- @record << "tested action"
71
+ $history << "tested action"
81
72
  end
82
73
 
83
74
  def test_before_halting
@@ -85,11 +76,11 @@ class FlowTestersController < BotController
85
76
  end
86
77
 
87
78
  def test_filtering
88
- @record << "filtered"
79
+ $history << "filtered"
89
80
  end
90
81
 
91
82
  def test_after_halting
92
- @record << "after action ran"
83
+ $history << "after action ran"
93
84
  end
94
85
  end
95
86
 
@@ -125,11 +116,11 @@ class OtherFlowTestersController < BotController
125
116
  private
126
117
 
127
118
  def after_action1
128
- @record << "after action 1"
119
+ $history << "after action 1"
129
120
  end
130
121
 
131
122
  def after_action2
132
- @record << "after action 2"
123
+ $history << "after action 2"
133
124
  end
134
125
 
135
126
  def run_halt
@@ -137,9 +128,9 @@ class OtherFlowTestersController < BotController
137
128
  end
138
129
 
139
130
  def run_around_filter
140
- @record << "around before"
131
+ $history << "around before"
141
132
  yield
142
- @record << "around after"
133
+ $history << "around after"
143
134
  end
144
135
  end
145
136
 
@@ -165,29 +156,33 @@ describe "Stealth::Controller callbacks" do
165
156
 
166
157
  let(:facebook_message) { SampleMessage.new(service: 'facebook') }
167
158
 
159
+ before(:each) do
160
+ $history = []
161
+ end
162
+
168
163
  describe "before_action" do
169
164
  it "should fire the callback on the parent class" do
170
165
  controller = BotController.new(service_message: facebook_message.message_with_text)
171
166
  controller.other_action
172
- expect(controller.record).to eq ["fetched user name"]
167
+ expect($history).to eq ["fetched user name"]
173
168
  end
174
169
 
175
170
  it "should fire the callback on a child class" do
176
171
  controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
177
172
  controller.some_action
178
- expect(controller.record).to eq ["fetched user name", "tested action"]
173
+ expect($history).to eq ["fetched user name", "tested action"]
179
174
  end
180
175
 
181
176
  it "should halt the callback chain when :abort is thrown" do
182
177
  controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
183
178
  controller.halted_action
184
- expect(controller.record).to eq ["fetched user name"]
179
+ expect($history).to eq ["fetched user name"]
185
180
  end
186
181
 
187
182
  it "should respect 'unless' filter" do
188
183
  controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
189
184
  controller.filtered_action
190
- expect(controller.record).to eq ["fetched user name", "tested action", "filtered"]
185
+ expect($history).to eq ["fetched user name", "tested action", "filtered"]
191
186
  end
192
187
  end
193
188
 
@@ -195,13 +190,13 @@ describe "Stealth::Controller callbacks" do
195
190
  it "should fire the after callbacks in reverse order" do
196
191
  controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
197
192
  controller.some_other_action2
198
- expect(controller.record).to eq ["fetched user name", "after action 2", "after action 1"]
193
+ expect($history).to eq ["fetched user name", "after action 2", "after action 1"]
199
194
  end
200
195
 
201
196
  it "should not fire after callbacks if a before callback throws an :abort" do
202
197
  controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
203
198
  controller.some_other_action3
204
- expect(controller.record).to eq ["fetched user name"]
199
+ expect($history).to eq ["fetched user name"]
205
200
  end
206
201
  end
207
202
 
@@ -209,13 +204,13 @@ describe "Stealth::Controller callbacks" do
209
204
  it "should fire the around callback before and after" do
210
205
  controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
211
206
  controller.some_other_action4
212
- expect(controller.record).to eq ["fetched user name", "around before", "around after"]
207
+ expect($history).to eq ["fetched user name", "around before", "around after"]
213
208
  end
214
209
 
215
210
  it "should not fire the around callback if a before callback throws abort" do
216
211
  controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
217
212
  controller.some_other_action5
218
- expect(controller.record).to eq ["fetched user name"]
213
+ expect($history).to eq ["fetched user name"]
219
214
  end
220
215
  end
221
216
 
@@ -1,35 +1,22 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
3
+ require 'spec_helper'
5
4
 
6
5
  describe "Stealth::Controller::CatchAll" do
7
-
8
- class VadersController < Stealth::Controller
9
- def my_action
10
- raise "oops"
11
- end
12
-
13
- def my_action2
14
-
15
- end
16
-
17
- def my_action3
18
-
19
- end
20
- end
6
+ $msg = nil
21
7
 
22
8
  class StubbedCatchAllsController < Stealth::Controller
23
9
  def level1
24
-
10
+ $msg = current_message
11
+ do_nothing
25
12
  end
26
13
 
27
14
  def level2
28
-
15
+ do_nothing
29
16
  end
30
17
 
31
18
  def level3
32
-
19
+ do_nothing
33
20
  end
34
21
  end
35
22
 
@@ -40,6 +27,8 @@ describe "Stealth::Controller::CatchAll" do
40
27
  state :my_action
41
28
  state :my_action2
42
29
  state :my_action3
30
+ state :action_with_unrecognized_msg
31
+ state :action_with_unrecognized_match
43
32
  end
44
33
 
45
34
  flow :catch_all do
@@ -64,30 +53,26 @@ describe "Stealth::Controller::CatchAll" do
64
53
  it "should step_to catch_all->level1 when a StandardError is raised" do
65
54
  controller.current_session.session = Stealth::Session.canonical_session_slug(flow: 'vader', state: 'my_action')
66
55
  controller.action(action: :my_action)
67
- expect(controller.current_session.flow_string).to eq("catch_all")
68
- expect(controller.current_session.state_string).to eq("level1")
56
+ expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level1')
69
57
  end
70
58
 
71
59
  it "should step_to catch_all->level1 when an action doesn't progress the flow" do
72
60
  controller.current_session.session = Stealth::Session.canonical_session_slug(flow: 'vader', state: 'my_action2')
73
61
  controller.action(action: :my_action2)
74
- expect(controller.current_session.flow_string).to eq("catch_all")
75
- expect(controller.current_session.state_string).to eq("level1")
62
+ expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level1')
76
63
  end
77
64
 
78
65
  it "should step_to catch_all->level2 when an action raises back to back" do
79
66
  controller.step_to flow: :vader, state: :my_action
80
67
  controller.step_to flow: :vader, state: :my_action
81
- expect(controller.current_session.flow_string).to eq("catch_all")
82
- expect(controller.current_session.state_string).to eq("level2")
68
+ expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level2')
83
69
  end
84
70
 
85
71
  it "should step_to catch_all->level3 when an action raises back to back to back" do
86
72
  controller.step_to flow: :vader, state: :my_action
87
73
  controller.step_to flow: :vader, state: :my_action
88
74
  controller.step_to flow: :vader, state: :my_action
89
- expect(controller.current_session.flow_string).to eq("catch_all")
90
- expect(controller.current_session.state_string).to eq("level3")
75
+ expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level3')
91
76
  end
92
77
 
93
78
  it "should just stop after the maximum number of catch_all levels have been reached" do
@@ -95,8 +80,75 @@ describe "Stealth::Controller::CatchAll" do
95
80
  controller.step_to flow: :vader, state: :my_action
96
81
  controller.step_to flow: :vader, state: :my_action
97
82
  controller.step_to flow: :vader, state: :my_action
98
- expect(controller.current_session.flow_string).to eq("vader")
99
- expect(controller.current_session.state_string).to eq("my_action")
83
+ expect($redis.get(controller.current_session.session_key)).to eq('vader->my_action')
84
+ end
85
+
86
+ it "should NOT run the catch_all if do_nothing is called" do
87
+ controller.current_session.set_session(new_flow: 'vader', new_state: 'my_action3')
88
+ controller.action(action: :my_action3)
89
+ expect($redis.get(controller.current_session.session_key)).to eq('vader->my_action3')
90
+ end
91
+
92
+ describe "catch_alls from within catch_all flow" do
93
+ let(:e) {
94
+ e = OpenStruct.new
95
+ e.class = RuntimeError
96
+ e.message = 'oops'
97
+ e.backtrace = [
98
+ '/stealth/lib/stealth/controller/controller.rb',
99
+ '/stealth/lib/stealth/controller/catch_all.rb',
100
+ ]
101
+ e
102
+ }
103
+
104
+ before(:each) do
105
+ controller.current_session.session = Stealth::Session.canonical_session_slug(flow: 'catch_all', state: 'level1')
106
+ end
107
+
108
+ it "should not step_to to catch_all" do
109
+ expect(controller).to_not receive(:step_to)
110
+ controller.run_catch_all(err: e)
111
+ end
112
+
113
+ it "should return false" do
114
+ expect(controller.run_catch_all(err: e)).to be false
115
+ end
116
+
117
+ it "should log the error message" do
118
+ expect(Stealth::Logger).to receive(:l).with(topic: 'catch_all', message: "[Level 1] for user #{facebook_message.sender_id} OpenStruct\noops\n/stealth/lib/stealth/controller/controller.rb\n/stealth/lib/stealth/controller/catch_all.rb")
119
+ expect(Stealth::Logger).to receive(:l).with(topic: 'catch_all', message: "CatchAll triggered for user #{facebook_message.sender_id} from within CatchAll; ignoring.")
120
+ controller.run_catch_all(err: e)
121
+ end
122
+ end
123
+
124
+ describe "catch_all_reason" do
125
+ before(:each) do
126
+ @session = Stealth::Session.new(id: controller.current_session_id)
127
+ @session.set_session(new_flow: 'vader', new_state: 'my_action2')
128
+ end
129
+
130
+ after(:each) do
131
+ $msg = nil
132
+ end
133
+
134
+ it 'should have access to the error raised in current_message.catch_all_reason' do
135
+ controller.action(action: :my_action)
136
+ expect($msg.catch_all_reason).to be_a(Hash)
137
+ expect($msg.catch_all_reason[:err]).to eq(RuntimeError)
138
+ expect($msg.catch_all_reason[:err_msg]).to eq('oops')
139
+ end
140
+
141
+ it 'should have the correct error when handle_message fails to recognize a message' do
142
+ controller.action(action: :action_with_unrecognized_msg)
143
+ expect($msg.catch_all_reason[:err]).to eq(Stealth::Errors::UnrecognizedMessage)
144
+ expect($msg.catch_all_reason[:err_msg]).to eq("The reply '#{facebook_message.message_with_text.message}' was not recognized.")
145
+ end
146
+
147
+ it 'should have the correct error when get_match fails to recognize a message' do
148
+ controller.action(action: :action_with_unrecognized_match)
149
+ expect($msg.catch_all_reason[:err]).to eq(Stealth::Errors::UnrecognizedMessage)
150
+ expect($msg.catch_all_reason[:err_msg]).to eq("The reply '#{facebook_message.message_with_text.message}' was not recognized.")
151
+ end
100
152
  end
101
153
  end
102
154
  end
@@ -1,7 +1,6 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
3
+ require 'spec_helper'
5
4
 
6
5
  describe "Stealth::Controller" do
7
6
 
@@ -25,12 +24,24 @@ describe "Stealth::Controller" do
25
24
  end
26
25
 
27
26
  def other_action2
28
-
27
+ step_to state: :other_action4
29
28
  end
30
29
 
31
30
  def other_action3
32
31
 
33
32
  end
33
+
34
+ def other_action4
35
+ do_nothing
36
+ end
37
+
38
+ def broken_action
39
+ raise StandardError
40
+ end
41
+
42
+ def parts_unknown
43
+ step_to flow: :parts, state: :unknown
44
+ end
34
45
  end
35
46
 
36
47
  class FlowMap
@@ -46,27 +57,26 @@ describe "Stealth::Controller" do
46
57
  state :other_action
47
58
  state :other_action2
48
59
  state :other_action3
60
+ state :other_action4
61
+ state :broken_action
62
+ state :part_unknown
49
63
  state :deprecated_action, redirects_to: :other_action
50
64
  state :deprecated_action2, redirects_to: 'mr_robot->my_action'
51
65
  end
52
66
  end
53
67
 
54
68
  let(:facebook_message) { SampleMessage.new(service: 'facebook') }
55
- let(:controller) { MrTronsController.new(service_message: facebook_message.message_with_text) }
69
+ let(:controller) {
70
+ MrTronsController.new(service_message: facebook_message.message_with_text)
71
+ }
56
72
 
57
73
  describe "convenience methods" do
58
74
  it "should make the session ID accessible via current_session_id" do
59
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
75
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
60
76
 
61
77
  expect(controller.current_session_id).to eq(facebook_message.sender_id)
62
78
  end
63
79
 
64
- it "should make the session ID accessible via current_user_id" do
65
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
66
-
67
- expect(controller.current_user_id).to eq(facebook_message.sender_id)
68
- end
69
-
70
80
  it "should make the message available in current_message.message" do
71
81
  expect(controller.current_message.message).to eq(facebook_message.message)
72
82
  end
@@ -126,9 +136,9 @@ describe "Stealth::Controller" do
126
136
 
127
137
  it "should step_to the specified redirect flow and state when a session is specified" do
128
138
  controller.current_session.session = Stealth::Session.canonical_session_slug(flow: 'mr_tron', state: 'deprecated_action2')
129
- mr_robot_controller = MrTronsController.new(service_message: facebook_message.message_with_text)
139
+ mr_robot_controller = MrRobotsController.new(service_message: facebook_message.message_with_text)
130
140
 
131
- expect(MrRobotsController).to receive(:new).and_return(mr_robot_controller)
141
+ allow(MrRobotsController).to receive(:new).and_return(mr_robot_controller)
132
142
  expect(mr_robot_controller).to receive(:my_action)
133
143
  controller.action(action: :deprecated_action2)
134
144
  end
@@ -156,7 +166,7 @@ describe "Stealth::Controller" do
156
166
  it "should call a controller's corresponding action when only a state is provided" do
157
167
  expect_any_instance_of(MrTronsController).to receive(:other_action3)
158
168
 
159
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
169
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
160
170
 
161
171
  controller.step_to state: "other_action3"
162
172
  end
@@ -175,10 +185,44 @@ describe "Stealth::Controller" do
175
185
  controller.step_to session: controller.current_session
176
186
  end
177
187
 
188
+ it "should call a controller's corresponding action when a session slug is provided" do
189
+ expect_any_instance_of(MrRobotsController).to receive(:my_action3)
190
+ controller.step_to slug: 'mr_robot->my_action3'
191
+ end
192
+
193
+ it "should pass along the service_message" do
194
+ robot_controller_dbl = double('MrRobotsController').as_null_object
195
+ expect(MrRobotsController).to receive(:new).with(service_message: controller.current_message, pos: nil).and_return(robot_controller_dbl)
196
+ controller.step_to flow: :mr_robot, state: :my_action3
197
+ end
198
+
178
199
  it "should accept flow and string specified as symbols" do
179
200
  expect_any_instance_of(MrRobotsController).to receive(:my_action3)
180
201
  controller.step_to flow: :mr_robot, state: :my_action3
181
202
  end
203
+
204
+ it "should check if an interruption occured" do
205
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
206
+ controller.step_to flow: :mr_robot, state: :my_action3
207
+ end
208
+
209
+ it "should call run_interrupt_action if an interruption occured and return" do
210
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
211
+ expect(controller).to receive(:run_interrupt_action)
212
+ expect(controller.step_to(flow: :mr_robot, state: :my_action3)).to eq :interrupted
213
+ end
214
+
215
+ it "should set @pos if it is specified in the arguments" do
216
+ expect_any_instance_of(MrRobotsController).to receive(:my_action3)
217
+ controller.step_to flow: :mr_robot, state: :my_action3, pos: -1
218
+ expect(controller.pos).to eq -1
219
+ end
220
+
221
+ it "should leave @pos as nil if the pos argument is not specified" do
222
+ expect_any_instance_of(MrRobotsController).to receive(:my_action3)
223
+ controller.step_to flow: :mr_robot, state: :my_action3
224
+ expect(controller.pos).to be_nil
225
+ end
182
226
  end
183
227
 
184
228
  describe "update_session_to" do
@@ -199,7 +243,7 @@ describe "Stealth::Controller" do
199
243
  it "should update session to controller's corresponding action when only a state is provided" do
200
244
  expect_any_instance_of(MrTronsController).to_not receive(:other_action3)
201
245
 
202
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
246
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
203
247
 
204
248
  controller.update_session_to state: "other_action3"
205
249
  expect(controller.current_session.flow_string).to eq('mr_tron')
@@ -217,14 +261,22 @@ describe "Stealth::Controller" do
217
261
  it "should update session to controller's corresponding action when a session is provided" do
218
262
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
219
263
 
220
- session = Stealth::Session.new(user_id: controller.current_session_id)
221
- session.set(flow: 'mr_robot', state: 'my_action3')
264
+ session = Stealth::Session.new(id: controller.current_session_id)
265
+ session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
222
266
 
223
267
  controller.update_session_to session: session
224
268
  expect(controller.current_session.flow_string).to eq('mr_robot')
225
269
  expect(controller.current_session.state_string).to eq('my_action3')
226
270
  end
227
271
 
272
+ it "should update session to controller's corresponding action when a session slug is provided" do
273
+ expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
274
+ expect(controller.current_session.flow_string).to eq('mr_robot')
275
+ expect(controller.current_session.state_string).to eq('my_action3')
276
+
277
+ controller.update_session_to slug: 'mr_robot->my_action3'
278
+ end
279
+
228
280
  it "should accept flow and string specified as symbols" do
229
281
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
230
282
 
@@ -232,6 +284,17 @@ describe "Stealth::Controller" do
232
284
  expect(controller.current_session.flow_string).to eq('mr_robot')
233
285
  expect(controller.current_session.state_string).to eq('my_action3')
234
286
  end
287
+
288
+ it "should check if an interruption occured" do
289
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
290
+ controller.update_session_to flow: :mr_robot, state: :my_action3
291
+ end
292
+
293
+ it "should call run_interrupt_action if an interruption occured and return" do
294
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
295
+ expect(controller).to receive(:run_interrupt_action)
296
+ expect(controller.update_session_to(flow: :mr_robot, state: :my_action3)).to eq :interrupted
297
+ end
235
298
  end
236
299
 
237
300
  describe "step_to_in" do
@@ -255,30 +318,32 @@ describe "Stealth::Controller" do
255
318
  controller.current_service,
256
319
  controller.current_session_id,
257
320
  'mr_robot',
258
- 'my_action'
321
+ 'my_action',
322
+ nil
259
323
  )
260
324
 
261
325
  expect {
262
326
  controller.step_to_in 100.seconds, flow: "mr_robot"
263
- }.to_not change(controller.current_session, :get)
327
+ }.to_not change(controller.current_session, :get_session)
264
328
  end
265
329
 
266
330
  it "should schedule a transition to controller's corresponding action when only a state is provided" do
267
331
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
268
332
 
269
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
333
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
270
334
 
271
335
  expect(Stealth::ScheduledReplyJob).to receive(:perform_in).with(
272
336
  100.seconds,
273
337
  controller.current_service,
274
338
  controller.current_session_id,
275
339
  'mr_tron',
276
- 'other_action3'
340
+ 'other_action3',
341
+ nil
277
342
  )
278
343
 
279
344
  expect {
280
345
  controller.step_to_in 100.seconds, state: "other_action3"
281
- }.to_not change(controller.current_session, :get)
346
+ }.to_not change(controller.current_session, :get_session)
282
347
  end
283
348
 
284
349
  it "should update session to controller's corresponding action when a state and flow is provided" do
@@ -289,31 +354,50 @@ describe "Stealth::Controller" do
289
354
  controller.current_service,
290
355
  controller.current_session_id,
291
356
  'mr_robot',
292
- 'my_action3'
357
+ 'my_action3',
358
+ nil
293
359
  )
294
360
 
295
361
  expect {
296
362
  controller.step_to_in 100.seconds, flow: 'mr_robot', state: "my_action3"
297
- }.to_not change(controller.current_session, :get)
363
+ }.to_not change(controller.current_session, :get_session)
298
364
  end
299
365
 
300
366
  it "should update session to controller's corresponding action when a session is provided" do
301
367
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
302
368
 
303
- session = Stealth::Session.new(user_id: controller.current_session_id)
304
- session.set(flow: 'mr_robot', state: 'my_action3')
369
+ session = Stealth::Session.new(id: controller.current_session_id)
370
+ session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
305
371
 
306
372
  expect(Stealth::ScheduledReplyJob).to receive(:perform_in).with(
307
373
  100.seconds,
308
374
  controller.current_service,
309
375
  controller.current_session_id,
310
376
  'mr_robot',
311
- 'my_action3'
377
+ 'my_action3',
378
+ nil
312
379
  )
313
380
 
314
381
  expect {
315
382
  controller.step_to_in 100.seconds, session: session
316
- }.to_not change(controller.current_session, :get)
383
+ }.to_not change(controller.current_session, :get_session)
384
+ end
385
+
386
+ it "should update session to controller's corresponding action when a session slug is provided" do
387
+ expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
388
+
389
+ expect(Stealth::ScheduledReplyJob).to receive(:perform_in).with(
390
+ 100.seconds,
391
+ controller.current_service,
392
+ controller.current_session_id,
393
+ 'mr_robot',
394
+ 'my_action3',
395
+ nil
396
+ )
397
+
398
+ expect {
399
+ controller.step_to_in 100.seconds, slug: 'mr_robot->my_action3'
400
+ }.to_not change(controller.current_session, :get_session)
317
401
  end
318
402
 
319
403
  it "should accept flow and string specified as symbols" do
@@ -324,12 +408,38 @@ describe "Stealth::Controller" do
324
408
  controller.current_service,
325
409
  controller.current_session_id,
326
410
  'mr_robot',
327
- 'my_action3'
411
+ 'my_action3',
412
+ nil
328
413
  )
329
414
 
330
415
  expect {
331
416
  controller.step_to_in 100.seconds, flow: :mr_robot, state: :my_action3
332
- }.to_not change(controller.current_session, :get)
417
+ }.to_not change(controller.current_session, :get_session)
418
+ end
419
+
420
+ it "should pass along the target_id if set on the message" do
421
+ expect(Stealth::ScheduledReplyJob).to receive(:perform_in).with(
422
+ 100.seconds,
423
+ controller.current_service,
424
+ controller.current_session_id,
425
+ 'mr_robot',
426
+ 'my_action3',
427
+ '+18885551212'
428
+ )
429
+
430
+ controller.current_message.target_id = '+18885551212'
431
+ controller.step_to_in 100.seconds, flow: :mr_robot, state: :my_action3
432
+ end
433
+
434
+ it "should check if an interruption occured" do
435
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
436
+ controller.step_to_in 100.seconds, flow: :mr_robot, state: :my_action3
437
+ end
438
+
439
+ it "should call run_interrupt_action if an interruption occured and return" do
440
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
441
+ expect(controller).to receive(:run_interrupt_action)
442
+ expect(controller.step_to_in(100.seconds, flow: :mr_robot, state: :my_action3)).to eq :interrupted
333
443
  end
334
444
  end
335
445
 
@@ -356,30 +466,32 @@ describe "Stealth::Controller" do
356
466
  controller.current_service,
357
467
  controller.current_session_id,
358
468
  'mr_robot',
359
- 'my_action'
469
+ 'my_action',
470
+ nil
360
471
  )
361
472
 
362
473
  expect {
363
474
  controller.step_to_at future_timestamp, flow: "mr_robot"
364
- }.to_not change(controller.current_session, :get)
475
+ }.to_not change(controller.current_session, :get_session)
365
476
  end
366
477
 
367
478
  it "should schedule a transition to controller's corresponding action when only a state is provided" do
368
479
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
369
480
 
370
- controller.current_session.set(flow: 'mr_tron', state: 'other_action')
481
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
371
482
 
372
483
  expect(Stealth::ScheduledReplyJob).to receive(:perform_at).with(
373
484
  future_timestamp,
374
485
  controller.current_service,
375
486
  controller.current_session_id,
376
487
  'mr_tron',
377
- 'other_action3'
488
+ 'other_action3',
489
+ nil
378
490
  )
379
491
 
380
492
  expect {
381
493
  controller.step_to_at future_timestamp, state: "other_action3"
382
- }.to_not change(controller.current_session, :get)
494
+ }.to_not change(controller.current_session, :get_session)
383
495
  end
384
496
 
385
497
  it "should update session to controller's corresponding action when a state and flow is provided" do
@@ -390,31 +502,50 @@ describe "Stealth::Controller" do
390
502
  controller.current_service,
391
503
  controller.current_session_id,
392
504
  'mr_robot',
393
- 'my_action3'
505
+ 'my_action3',
506
+ nil
394
507
  )
395
508
 
396
509
  expect {
397
510
  controller.step_to_at future_timestamp, flow: 'mr_robot', state: "my_action3"
398
- }.to_not change(controller.current_session, :get)
511
+ }.to_not change(controller.current_session, :get_session)
399
512
  end
400
513
 
401
514
  it "should update session to controller's corresponding action when a session is provided" do
402
515
  expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
403
516
 
404
- session = Stealth::Session.new(user_id: controller.current_session_id)
405
- session.set(flow: 'mr_robot', state: 'my_action3')
517
+ session = Stealth::Session.new(id: controller.current_session_id)
518
+ session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
406
519
 
407
520
  expect(Stealth::ScheduledReplyJob).to receive(:perform_at).with(
408
521
  future_timestamp,
409
522
  controller.current_service,
410
523
  controller.current_session_id,
411
524
  'mr_robot',
412
- 'my_action3'
525
+ 'my_action3',
526
+ nil
413
527
  )
414
528
 
415
529
  expect {
416
530
  controller.step_to_at future_timestamp, session: session
417
- }.to_not change(controller.current_session, :get)
531
+ }.to_not change(controller.current_session, :get_session)
532
+ end
533
+
534
+ it "should update session to controller's corresponding action when a session slug is provided" do
535
+ expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
536
+
537
+ expect(Stealth::ScheduledReplyJob).to receive(:perform_at).with(
538
+ future_timestamp,
539
+ controller.current_service,
540
+ controller.current_session_id,
541
+ 'mr_robot',
542
+ 'my_action3',
543
+ nil
544
+ )
545
+
546
+ expect {
547
+ controller.step_to_at future_timestamp, slug: 'mr_robot->my_action3'
548
+ }.to_not change(controller.current_session, :get_session)
418
549
  end
419
550
 
420
551
  it "should accept flow and string specified as symbols" do
@@ -425,12 +556,148 @@ describe "Stealth::Controller" do
425
556
  controller.current_service,
426
557
  controller.current_session_id,
427
558
  'mr_robot',
428
- 'my_action3'
559
+ 'my_action3',
560
+ nil
429
561
  )
430
562
 
431
563
  expect {
432
564
  controller.step_to_at future_timestamp, flow: :mr_robot, state: :my_action3
433
- }.to_not change(controller.current_session, :get)
565
+ }.to_not change(controller.current_session, :get_session)
566
+ end
567
+
568
+ it "should pass along the target_id if set on the message" do
569
+ expect(Stealth::ScheduledReplyJob).to receive(:perform_at).with(
570
+ future_timestamp,
571
+ controller.current_service,
572
+ controller.current_session_id,
573
+ 'mr_robot',
574
+ 'my_action3',
575
+ '+18885551212'
576
+ )
577
+
578
+ controller.current_message.target_id = '+18885551212'
579
+ controller.step_to_at future_timestamp, flow: :mr_robot, state: :my_action3
580
+ end
581
+
582
+ it "should check if an interruption occured" do
583
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
584
+ controller.step_to_at future_timestamp, flow: :mr_robot, state: :my_action3
585
+ end
586
+
587
+ it "should call run_interrupt_action if an interruption occured and return" do
588
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
589
+ expect(controller).to receive(:run_interrupt_action)
590
+ expect(controller.step_to_at(future_timestamp, flow: :mr_robot, state: :my_action3)).to eq :interrupted
591
+ end
592
+ end
593
+
594
+ describe "set_back_to" do
595
+ it "should raise an ArgumentError if a session, flow, or state is not specified" do
596
+ expect {
597
+ controller.set_back_to
598
+ }.to raise_error(ArgumentError)
599
+ end
600
+
601
+ it "should call the flow's first state's controller action when only a flow is provided" do
602
+ expect {
603
+ controller.set_back_to(flow: :mr_robot)
604
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('mr_robot->my_action')
605
+ end
606
+
607
+ it "should call a controller's corresponding action when only a state is provided" do
608
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
609
+
610
+ expect {
611
+ controller.set_back_to(state: :other_action3)
612
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('mr_tron->other_action3')
613
+ end
614
+
615
+ it "should call a controller's corresponding action when a state and flow is provided" do
616
+ expect {
617
+ controller.set_back_to(flow: 'marco', state: 'polo')
618
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('marco->polo')
619
+ end
620
+
621
+ it "should call a controller's corresponding action when a session is provided" do
622
+ allow(controller.current_session).to receive(:flow_string).and_return("mr_robot")
623
+ allow(controller.current_session).to receive(:state_string).and_return("my_action3")
624
+
625
+ expect {
626
+ controller.set_back_to(session: controller.current_session)
627
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('mr_robot->my_action3')
628
+ end
629
+
630
+ it "should call a controller's corresponding action when a session slug is provided" do
631
+ expect {
632
+ controller.set_back_to(slug: 'marco->polo')
633
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('marco->polo')
634
+ end
635
+
636
+ it "should default to the scoped flow if one is not specified" do
637
+ controller.current_session.set_session(new_flow: :mr_tron, new_state: :other_action)
638
+ expect {
639
+ controller.set_back_to(state: 'polo')
640
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.to('mr_tron->polo')
641
+ end
642
+
643
+ it "should overwrite the existing back_to_session if one is already present" do
644
+ $redis.set([controller.current_session_id, 'back_to'].join('-'), 'marco->polo')
645
+ controller.current_session.set_session(new_flow: :mr_tron, new_state: :other_action)
646
+ expect {
647
+ controller.set_back_to(state: 'other_action')
648
+ }.to change{ $redis.get([controller.current_session_id, 'back_to'].join('-')) }.from('marco->polo').to('mr_tron->other_action')
649
+ end
650
+
651
+ it "should check if an interruption occured" do
652
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
653
+ controller.set_back_to flow: :mr_robot, state: :my_action3
654
+ end
655
+
656
+ it "should call run_interrupt_action if an interruption occured and return" do
657
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
658
+ expect(controller).to receive(:run_interrupt_action)
659
+ expect(controller.set_back_to(flow: :mr_robot, state: :my_action3)).to eq :interrupted
660
+ end
661
+ end
662
+
663
+ describe "step_back" do
664
+ let(:back_to_slug) { [controller.current_session_id, 'back_to'].join('-') }
665
+
666
+ it "should raise Stealth::Errors::InvalidStateTransition if back_to_session is not set" do
667
+ $redis.del(back_to_slug)
668
+ expect {
669
+ controller.step_back
670
+ }.to raise_error(Stealth::Errors::InvalidStateTransition)
671
+ end
672
+
673
+ it "should step_to the stored back_to_session" do
674
+ controller.set_back_to(flow: 'marco', state: 'polo')
675
+ back_to_session = Stealth::Session.new(
676
+ id: controller.current_session_id,
677
+ type: :back_to
678
+ )
679
+
680
+ # We need to control the returned session object so the IDs match
681
+ expect(Stealth::Session).to receive(:new).with(
682
+ id: controller.current_session_id,
683
+ type: :back_to
684
+ ).and_return(back_to_session)
685
+ expect(controller).to receive(:step_to).with(session: back_to_session)
686
+
687
+ controller.step_back
688
+ end
689
+
690
+ it "should check if an interruption occured" do
691
+ controller.set_back_to(flow: :mr_robot, state: :my_action3)
692
+ expect(controller).to receive(:interrupt_detected?).and_return(false)
693
+ controller.step_back
694
+ end
695
+
696
+ it "should call run_interrupt_action if an interruption occured and return" do
697
+ controller.set_back_to(flow: :mr_robot, state: :my_action3)
698
+ expect(controller).to receive(:interrupt_detected?).and_return(true)
699
+ expect(controller).to receive(:run_interrupt_action)
700
+ expect(controller.step_back).to eq :interrupted
434
701
  end
435
702
  end
436
703
 
@@ -479,10 +746,144 @@ describe "Stealth::Controller" do
479
746
  end
480
747
 
481
748
  it "should be falsey otherwise" do
749
+ allow(controller).to receive(:flow_controller).and_return(controller)
482
750
  expect(controller.progressed?).to be_falsey
483
751
  controller.action(action: :other_action)
484
752
  expect(controller.progressed?).to be_falsey
485
753
  end
486
754
  end
487
755
 
756
+ describe "do_nothing" do
757
+ it "should set progressed to truthy when called" do
758
+ allow(controller).to receive(:flow_controller).and_return(controller)
759
+ expect(controller.progressed?).to be_falsey
760
+ controller.action(action: :other_action4)
761
+ expect(controller.progressed?).to be_truthy
762
+ end
763
+ end
764
+
765
+ describe "update_session" do
766
+ before(:each) do
767
+ controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
768
+ end
769
+
770
+ it "should set progressed to :updated_session" do
771
+ controller.send(:update_session, flow: :mr_tron, state: :other_action)
772
+ expect(controller.progressed?).to eq :updated_session
773
+ end
774
+
775
+ it "call set_session on the current_session with the new flow and state" do
776
+ controller.send(:update_session, flow: :mr_robot, state: :my_action)
777
+ expect(controller.current_session.flow_string).to eq 'mr_robot'
778
+ expect(controller.current_session.state_string).to eq 'my_action'
779
+ end
780
+
781
+ it "should not call set_session on current_session if the flow and state match" do
782
+ expect_any_instance_of(Stealth::Session).to_not receive(:set_session)
783
+ controller.send(:update_session, flow: :mr_tron, state: :other_action)
784
+ end
785
+ end
786
+
787
+ describe "dev jumps" do
788
+ let!(:dev_env) { ActiveSupport::StringInquirer.new('development') }
789
+
790
+ describe "dev_jump_detected?" do
791
+ it "should return false if the enviornment is not 'development'" do
792
+ expect(Stealth.env).to eq 'test'
793
+ expect(controller.send(:dev_jump_detected?)).to be false
794
+ end
795
+
796
+ it "should return false if the message does not match the jump format" do
797
+ allow(Stealth).to receive(:env).and_return(dev_env)
798
+ controller.current_message.message = 'hello world'
799
+ expect(Stealth.env.development?).to be true
800
+ expect(controller.send(:dev_jump_detected?)).to be false
801
+ end
802
+
803
+ it "should return false if the message looks like an American date" do
804
+ allow(Stealth).to receive(:env).and_return(dev_env)
805
+ controller.current_message.message = '1/23/84'
806
+ expect(Stealth.env.development?).to be true
807
+ expect(controller.send(:dev_jump_detected?)).to be false
808
+ end
809
+
810
+ it "should return false if the message looks like an American date that is zero padded" do
811
+ allow(Stealth).to receive(:env).and_return(dev_env)
812
+ controller.current_message.message = '01/23/1984'
813
+ expect(Stealth.env.development?).to be true
814
+ expect(controller.send(:dev_jump_detected?)).to be false
815
+ end
816
+
817
+ describe "with a dev jump message" do
818
+ before(:each) do
819
+ expect(controller).to receive(:handle_dev_jump).and_return(true)
820
+ expect(Stealth).to receive(:env).and_return(dev_env)
821
+ end
822
+
823
+ it "should return true if the message is in the format /flow/state" do
824
+ controller.current_message.message = '/mr_robot/my_action'
825
+ expect(controller.send(:dev_jump_detected?)).to be true
826
+ end
827
+
828
+ it "should return true if the message is in the format /flow" do
829
+ controller.current_message.message = '/mr_robot'
830
+ expect(controller.send(:dev_jump_detected?)).to be true
831
+ end
832
+
833
+ it "should return true if the message is in the format //state" do
834
+ controller.current_message.message = '//my_action'
835
+ expect(controller.send(:dev_jump_detected?)).to be true
836
+ end
837
+ end
838
+ end
839
+
840
+ describe "handle_dev_jump" do
841
+ it "should handle messages in the format /flow/state" do
842
+ controller.current_message.message = '/mr_robot/my_action'
843
+ expect(controller).to receive(:step_to).with(flow: 'mr_robot', state: 'my_action')
844
+ controller.send(:handle_dev_jump)
845
+ end
846
+
847
+ it "should handle messages in the format /flow" do
848
+ controller.current_message.message = '/mr_robot'
849
+ expect(controller).to receive(:step_to).with(flow: 'mr_robot', state: nil)
850
+ controller.send(:handle_dev_jump)
851
+ end
852
+
853
+ it "should handle messages in the format //state" do
854
+ controller.current_message.message = '//my_action'
855
+ expect(controller).to receive(:step_to).with(flow: nil, state: 'my_action')
856
+ controller.send(:handle_dev_jump)
857
+ end
858
+ end
859
+
860
+ describe "session locking" do
861
+ before(:each) do
862
+ allow(MrTronsController).to receive(:new).and_return(controller)
863
+ end
864
+
865
+ it "should lock and then unlock a session when a do_nothing action is called" do
866
+ expect(controller).to receive(:lock_session!).once
867
+ expect(controller).to receive(:release_lock!).once
868
+ controller.action(action: :other_action4)
869
+ end
870
+
871
+ it "should lock and then unlock a session twice when an action steps to another" do
872
+ expect(controller).to receive(:lock_session!).twice
873
+ expect(controller).to receive(:release_lock!).twice
874
+ controller.action(action: :other_action2)
875
+ end
876
+
877
+ it 'should still release the lock even if an action raises' do
878
+ expect(controller).to receive(:release_lock!).once
879
+ controller.action(action: :broken_action)
880
+ end
881
+
882
+ it 'should still release the lock if an action steps to an unknown flow->state' do
883
+ expect(controller).to receive(:release_lock!).once
884
+ controller.action(action: :parts_unknown)
885
+ end
886
+ end
887
+ end
888
+
488
889
  end