xip 0.0.1 → 2.0.0.beta2
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 +4 -4
- data/.circleci/config.yml +116 -0
- data/.gitignore +12 -0
- data/CHANGELOG.md +135 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +65 -15
- data/LICENSE +6 -4
- data/README.md +51 -1
- data/VERSION +1 -0
- data/bin/xip +3 -11
- data/lib/xip.rb +1 -3
- data/lib/xip/base.rb +189 -0
- data/lib/xip/cli.rb +273 -0
- data/lib/xip/cli_base.rb +24 -0
- data/lib/xip/commands/command.rb +13 -0
- data/lib/xip/commands/console.rb +74 -0
- data/lib/xip/commands/server.rb +63 -0
- data/lib/xip/configuration.rb +56 -0
- data/lib/xip/controller/callbacks.rb +63 -0
- data/lib/xip/controller/catch_all.rb +84 -0
- data/lib/xip/controller/controller.rb +274 -0
- data/lib/xip/controller/dev_jumps.rb +40 -0
- data/lib/xip/controller/dynamic_delay.rb +61 -0
- data/lib/xip/controller/helpers.rb +128 -0
- data/lib/xip/controller/interrupt_detect.rb +99 -0
- data/lib/xip/controller/messages.rb +283 -0
- data/lib/xip/controller/nlp.rb +49 -0
- data/lib/xip/controller/replies.rb +281 -0
- data/lib/xip/controller/unrecognized_message.rb +61 -0
- data/lib/xip/core_ext.rb +5 -0
- data/lib/xip/core_ext/numeric.rb +10 -0
- data/lib/xip/core_ext/string.rb +18 -0
- data/lib/xip/dispatcher.rb +68 -0
- data/lib/xip/errors.rb +55 -0
- data/lib/xip/flow/base.rb +69 -0
- data/lib/xip/flow/specification.rb +56 -0
- data/lib/xip/flow/state.rb +82 -0
- data/lib/xip/generators/builder.rb +41 -0
- data/lib/xip/generators/builder/.gitignore +30 -0
- data/lib/xip/generators/builder/Gemfile +19 -0
- data/lib/xip/generators/builder/Procfile.dev +2 -0
- data/lib/xip/generators/builder/README.md +9 -0
- data/lib/xip/generators/builder/Rakefile +2 -0
- data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
- data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
- data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
- data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
- data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
- data/lib/xip/generators/builder/config.ru +4 -0
- data/lib/xip/generators/builder/config/boot.rb +6 -0
- data/lib/xip/generators/builder/config/database.yml +25 -0
- data/lib/xip/generators/builder/config/environment.rb +2 -0
- data/lib/xip/generators/builder/config/flow_map.rb +25 -0
- data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
- data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
- data/lib/xip/generators/builder/config/puma.rb +25 -0
- data/lib/xip/generators/builder/config/services.yml +35 -0
- data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
- data/lib/xip/generators/builder/db/seeds.rb +7 -0
- data/lib/xip/generators/generate.rb +39 -0
- data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
- data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
- data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
- data/lib/xip/helpers/redis.rb +40 -0
- data/lib/xip/jobs.rb +9 -0
- data/lib/xip/lock.rb +82 -0
- data/lib/xip/logger.rb +9 -3
- data/lib/xip/migrations/configurator.rb +73 -0
- data/lib/xip/migrations/generators.rb +16 -0
- data/lib/xip/migrations/railtie_config.rb +14 -0
- data/lib/xip/migrations/tasks.rb +43 -0
- data/lib/xip/nlp/client.rb +21 -0
- data/lib/xip/nlp/result.rb +56 -0
- data/lib/xip/reloader.rb +89 -0
- data/lib/xip/reply.rb +36 -0
- data/lib/xip/scheduled_reply.rb +18 -0
- data/lib/xip/server.rb +63 -0
- data/lib/xip/service_message.rb +17 -0
- data/lib/xip/service_reply.rb +44 -0
- data/lib/xip/services/base_client.rb +24 -0
- data/lib/xip/services/base_message_handler.rb +27 -0
- data/lib/xip/services/base_reply_handler.rb +72 -0
- data/lib/xip/services/jobs/handle_message_job.rb +21 -0
- data/lib/xip/session.rb +203 -0
- data/lib/xip/version.rb +7 -1
- data/logo.svg +17 -0
- data/spec/configuration_spec.rb +93 -0
- data/spec/controller/callbacks_spec.rb +217 -0
- data/spec/controller/catch_all_spec.rb +154 -0
- data/spec/controller/controller_spec.rb +889 -0
- data/spec/controller/dynamic_delay_spec.rb +70 -0
- data/spec/controller/helpers_spec.rb +119 -0
- data/spec/controller/interrupt_detect_spec.rb +171 -0
- data/spec/controller/messages_spec.rb +744 -0
- data/spec/controller/nlp_spec.rb +93 -0
- data/spec/controller/replies_spec.rb +694 -0
- data/spec/controller/unrecognized_message_spec.rb +168 -0
- data/spec/dispatcher_spec.rb +79 -0
- data/spec/flow/flow_spec.rb +82 -0
- data/spec/flow/state_spec.rb +109 -0
- data/spec/helpers/redis_spec.rb +77 -0
- data/spec/lock_spec.rb +100 -0
- data/spec/nlp/client_spec.rb +23 -0
- data/spec/nlp/result_spec.rb +57 -0
- data/spec/replies/hello.yml.erb +15 -0
- data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
- data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
- data/spec/replies/messages/say_hola.yml.erb +6 -0
- data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
- data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
- data/spec/replies/messages/say_offer.yml +6 -0
- data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
- data/spec/replies/messages/say_oi.yml.erb +15 -0
- data/spec/replies/messages/say_randomize_speech.yml +10 -0
- data/spec/replies/messages/say_randomize_text.yml +10 -0
- data/spec/replies/messages/say_yo.yml +6 -0
- data/spec/replies/messages/say_yo.yml+twitter +6 -0
- data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
- data/spec/reply_spec.rb +61 -0
- data/spec/scheduled_reply_spec.rb +23 -0
- data/spec/service_reply_spec.rb +92 -0
- data/spec/session_spec.rb +366 -0
- data/spec/spec_helper.rb +22 -66
- data/spec/support/alternate_helpers/foo_helper.rb +5 -0
- data/spec/support/controllers/vaders_controller.rb +24 -0
- data/spec/support/helpers/fun/games_helper.rb +7 -0
- data/spec/support/helpers/fun/pdf_helper.rb +7 -0
- data/spec/support/helpers/standalone_helper.rb +5 -0
- data/spec/support/helpers_typo/users_helper.rb +2 -0
- data/spec/support/nlp_clients/dialogflow.rb +9 -0
- data/spec/support/nlp_clients/luis.rb +9 -0
- data/spec/support/nlp_results/luis_result.rb +163 -0
- data/spec/support/sample_messages.rb +66 -0
- data/spec/support/services.yml +31 -0
- data/spec/support/services_with_erb.yml +31 -0
- data/spec/version_spec.rb +16 -0
- data/xip.gemspec +25 -14
- metadata +320 -18
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
$history = []
|
|
6
|
+
|
|
7
|
+
class BotController < Xip::Controller
|
|
8
|
+
before_action :fetch_user_name
|
|
9
|
+
|
|
10
|
+
def some_action
|
|
11
|
+
step_to flow: 'flow_tester', state: 'my_action'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def other_action
|
|
15
|
+
step_to flow: 'other_flow_tester', state: 'other_action'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def halted_action
|
|
19
|
+
step_to flow: 'flow_tester', state: 'my_action2'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def filtered_action
|
|
23
|
+
step_to flow: 'flow_tester', state: 'my_action3'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def some_other_action2
|
|
27
|
+
step_to flow: 'other_flow_tester', state: 'other_action2'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def some_other_action3
|
|
31
|
+
step_to flow: 'other_flow_tester', state: 'other_action3'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def some_other_action4
|
|
35
|
+
step_to flow: 'other_flow_tester', state: 'other_action4'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def some_other_action5
|
|
39
|
+
step_to flow: 'other_flow_tester', state: 'other_action5'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def fetch_user_name
|
|
45
|
+
$history << "fetched user name"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class FlowTestersController < BotController
|
|
50
|
+
before_action :test_before_halting, only: :my_action2
|
|
51
|
+
before_action :test_action
|
|
52
|
+
before_action :test_filtering, except: [:my_action, :my_action2]
|
|
53
|
+
|
|
54
|
+
attr_reader :action_ran
|
|
55
|
+
|
|
56
|
+
def my_action
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def my_action2
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def my_action3
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
protected
|
|
69
|
+
|
|
70
|
+
def test_action
|
|
71
|
+
$history << "tested action"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_before_halting
|
|
75
|
+
throw(:abort)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_filtering
|
|
79
|
+
$history << "filtered"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_after_halting
|
|
83
|
+
$history << "after action ran"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class OtherFlowTestersController < BotController
|
|
88
|
+
after_action :after_action1, only: :other_action2
|
|
89
|
+
after_action :after_action2, only: :other_action2
|
|
90
|
+
|
|
91
|
+
before_action :run_halt, only: [:other_action3, :other_action5]
|
|
92
|
+
after_action :after_action3, only: :other_action3
|
|
93
|
+
|
|
94
|
+
around_action :run_around_filter, only: [:other_action4, :other_action5]
|
|
95
|
+
|
|
96
|
+
def other_action
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def other_action2
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def other_action3
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def other_action4
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def other_action5
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def after_action1
|
|
119
|
+
$history << "after action 1"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def after_action2
|
|
123
|
+
$history << "after action 2"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def run_halt
|
|
127
|
+
throw(:abort)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def run_around_filter
|
|
131
|
+
$history << "around before"
|
|
132
|
+
yield
|
|
133
|
+
$history << "around after"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class FlowMap
|
|
138
|
+
include Xip::Flow
|
|
139
|
+
|
|
140
|
+
flow :flow_tester do
|
|
141
|
+
state :my_action
|
|
142
|
+
state :my_action2
|
|
143
|
+
state :my_action3
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
flow :other_flow_tester do
|
|
147
|
+
state :other_action
|
|
148
|
+
state :other_action2
|
|
149
|
+
state :other_action3
|
|
150
|
+
state :other_action4
|
|
151
|
+
state :other_action5
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe "Xip::Controller callbacks" do
|
|
156
|
+
|
|
157
|
+
let(:facebook_message) { SampleMessage.new(service: 'facebook') }
|
|
158
|
+
|
|
159
|
+
before(:each) do
|
|
160
|
+
$history = []
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe "before_action" do
|
|
164
|
+
it "should fire the callback on the parent class" do
|
|
165
|
+
controller = BotController.new(service_message: facebook_message.message_with_text)
|
|
166
|
+
controller.other_action
|
|
167
|
+
expect($history).to eq ["fetched user name"]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should fire the callback on a child class" do
|
|
171
|
+
controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
172
|
+
controller.some_action
|
|
173
|
+
expect($history).to eq ["fetched user name", "tested action"]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should halt the callback chain when :abort is thrown" do
|
|
177
|
+
controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
178
|
+
controller.halted_action
|
|
179
|
+
expect($history).to eq ["fetched user name"]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "should respect 'unless' filter" do
|
|
183
|
+
controller = FlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
184
|
+
controller.filtered_action
|
|
185
|
+
expect($history).to eq ["fetched user name", "tested action", "filtered"]
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
describe "after_action" do
|
|
190
|
+
it "should fire the after callbacks in reverse order" do
|
|
191
|
+
controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
192
|
+
controller.some_other_action2
|
|
193
|
+
expect($history).to eq ["fetched user name", "after action 2", "after action 1"]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "should not fire after callbacks if a before callback throws an :abort" do
|
|
197
|
+
controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
198
|
+
controller.some_other_action3
|
|
199
|
+
expect($history).to eq ["fetched user name"]
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
describe "around_action" do
|
|
204
|
+
it "should fire the around callback before and after" do
|
|
205
|
+
controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
206
|
+
controller.some_other_action4
|
|
207
|
+
expect($history).to eq ["fetched user name", "around before", "around after"]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it "should not fire the around callback if a before callback throws abort" do
|
|
211
|
+
controller = OtherFlowTestersController.new(service_message: facebook_message.message_with_text)
|
|
212
|
+
controller.some_other_action5
|
|
213
|
+
expect($history).to eq ["fetched user name"]
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe "Xip::Controller::CatchAll" do
|
|
6
|
+
$msg = nil
|
|
7
|
+
|
|
8
|
+
class StubbedCatchAllsController < Xip::Controller
|
|
9
|
+
def level1
|
|
10
|
+
$msg = current_message
|
|
11
|
+
do_nothing
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def level2
|
|
15
|
+
do_nothing
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def level3
|
|
19
|
+
do_nothing
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class FlowMap
|
|
24
|
+
include Xip::Flow
|
|
25
|
+
|
|
26
|
+
flow :vader do
|
|
27
|
+
state :my_action
|
|
28
|
+
state :my_action2
|
|
29
|
+
state :my_action3
|
|
30
|
+
state :action_with_unrecognized_msg
|
|
31
|
+
state :action_with_unrecognized_match
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
flow :catch_all do
|
|
35
|
+
state :level1
|
|
36
|
+
state :level2
|
|
37
|
+
state :level3
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
let(:facebook_message) { SampleMessage.new(service: 'facebook') }
|
|
42
|
+
let(:controller) { VadersController.new(service_message: facebook_message.message_with_text) }
|
|
43
|
+
|
|
44
|
+
describe "when a CatchAll flow is defined" do
|
|
45
|
+
before(:each) do
|
|
46
|
+
stub_const("CatchAllsController", StubbedCatchAllsController)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
after(:each) do
|
|
50
|
+
$redis.flushdb
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should step_to catch_all->level1 when a StandardError is raised" do
|
|
54
|
+
controller.current_session.session = Xip::Session.canonical_session_slug(flow: 'vader', state: 'my_action')
|
|
55
|
+
controller.action(action: :my_action)
|
|
56
|
+
expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level1')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should step_to catch_all->level1 when an action doesn't progress the flow" do
|
|
60
|
+
controller.current_session.session = Xip::Session.canonical_session_slug(flow: 'vader', state: 'my_action2')
|
|
61
|
+
controller.action(action: :my_action2)
|
|
62
|
+
expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level1')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should step_to catch_all->level2 when an action raises back to back" do
|
|
66
|
+
controller.step_to flow: :vader, state: :my_action
|
|
67
|
+
controller.step_to flow: :vader, state: :my_action
|
|
68
|
+
expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level2')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should step_to catch_all->level3 when an action raises back to back to back" do
|
|
72
|
+
controller.step_to flow: :vader, state: :my_action
|
|
73
|
+
controller.step_to flow: :vader, state: :my_action
|
|
74
|
+
controller.step_to flow: :vader, state: :my_action
|
|
75
|
+
expect($redis.get(controller.current_session.session_key)).to eq('catch_all->level3')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should just stop after the maximum number of catch_all levels have been reached" do
|
|
79
|
+
controller.step_to flow: :vader, state: :my_action
|
|
80
|
+
controller.step_to flow: :vader, state: :my_action
|
|
81
|
+
controller.step_to flow: :vader, state: :my_action
|
|
82
|
+
controller.step_to flow: :vader, state: :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
|
+
'/xip/lib/xip/controller/controller.rb',
|
|
99
|
+
'/xip/lib/xip/controller/catch_all.rb',
|
|
100
|
+
]
|
|
101
|
+
e
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
before(:each) do
|
|
105
|
+
controller.current_session.session = Xip::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(Xip::Logger).to receive(:l).with(topic: 'catch_all', message: "[Level 1] for user #{facebook_message.sender_id} OpenStruct\noops\n/xip/lib/xip/controller/controller.rb\n/xip/lib/xip/controller/catch_all.rb")
|
|
119
|
+
expect(Xip::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 = Xip::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(Xip::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(Xip::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
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe "Xip::Controller" do
|
|
6
|
+
|
|
7
|
+
class MrRobotsController < Xip::Controller
|
|
8
|
+
def my_action
|
|
9
|
+
[:success, :my_action]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def my_action2
|
|
13
|
+
[:success, :my_action2]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def my_action3
|
|
17
|
+
[:success, :my_action3]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class MrTronsController < Xip::Controller
|
|
22
|
+
def other_action
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def other_action2
|
|
27
|
+
step_to state: :other_action4
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def other_action3
|
|
31
|
+
|
|
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
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class FlowMap
|
|
48
|
+
include Xip::Flow
|
|
49
|
+
|
|
50
|
+
flow :mr_robot do
|
|
51
|
+
state :my_action
|
|
52
|
+
state :my_action2
|
|
53
|
+
state :my_action3
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
flow :mr_tron do
|
|
57
|
+
state :other_action
|
|
58
|
+
state :other_action2
|
|
59
|
+
state :other_action3
|
|
60
|
+
state :other_action4
|
|
61
|
+
state :broken_action
|
|
62
|
+
state :part_unknown
|
|
63
|
+
state :deprecated_action, redirects_to: :other_action
|
|
64
|
+
state :deprecated_action2, redirects_to: 'mr_robot->my_action'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
let(:facebook_message) { SampleMessage.new(service: 'facebook') }
|
|
69
|
+
let(:controller) {
|
|
70
|
+
MrTronsController.new(service_message: facebook_message.message_with_text)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe "convenience methods" do
|
|
74
|
+
it "should make the session ID accessible via current_session_id" do
|
|
75
|
+
controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
|
76
|
+
|
|
77
|
+
expect(controller.current_session_id).to eq(facebook_message.sender_id)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should make the message available in current_message.message" do
|
|
81
|
+
expect(controller.current_message.message).to eq(facebook_message.message)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should make the payload available in current_message.payload" do
|
|
85
|
+
message_with_payload = facebook_message.message_with_payload
|
|
86
|
+
expect(controller.current_message.payload).to eq(message_with_payload.payload)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "current_service" do
|
|
90
|
+
let(:twilio_message) { SampleMessage.new(service: 'twilio') }
|
|
91
|
+
let(:controller_with_twilio_message) { MrTronsController.new(service_message: twilio_message.message_with_text) }
|
|
92
|
+
|
|
93
|
+
it "should detect a Facebook message" do
|
|
94
|
+
expect(controller.current_service).to eq('facebook')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should detect a Twilio message" do
|
|
98
|
+
expect(controller_with_twilio_message.current_service).to eq('twilio')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe "messages with location" do
|
|
103
|
+
let(:message_with_location) { facebook_message.message_with_location }
|
|
104
|
+
let(:controller_with_location) { MrTronsController.new(service_message: message_with_location) }
|
|
105
|
+
|
|
106
|
+
it "should make the location available in current_message.location" do
|
|
107
|
+
expect(controller_with_location.current_message.location).to eq(message_with_location.location)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should return true for current_message.has_location?" do
|
|
111
|
+
expect(controller_with_location.has_location?).to be true
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "messages with attachments" do
|
|
116
|
+
let(:message_with_attachments) { facebook_message.message_with_attachments }
|
|
117
|
+
let(:controller_with_attachment) { MrTronsController.new(service_message: message_with_attachments) }
|
|
118
|
+
|
|
119
|
+
it "should make the attachments available in current_message.attachments" do
|
|
120
|
+
expect(controller_with_attachment.current_message.attachments).to eq(message_with_attachments.attachments)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should return true for current_message.has_attachments?" do
|
|
124
|
+
expect(controller_with_attachment.has_attachments?).to be true
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe "states with redirect_to specified" do
|
|
130
|
+
it "should step_to the specified redirect state when only a state is specified" do
|
|
131
|
+
controller.current_session.session = Xip::Session.canonical_session_slug(flow: 'mr_tron', state: 'deprecated_action')
|
|
132
|
+
expect(MrTronsController).to receive(:new).and_return(controller)
|
|
133
|
+
expect(controller).to receive(:other_action)
|
|
134
|
+
controller.action(action: :deprecated_action)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should step_to the specified redirect flow and state when a session is specified" do
|
|
138
|
+
controller.current_session.session = Xip::Session.canonical_session_slug(flow: 'mr_tron', state: 'deprecated_action2')
|
|
139
|
+
mr_robot_controller = MrRobotsController.new(service_message: facebook_message.message_with_text)
|
|
140
|
+
|
|
141
|
+
allow(MrRobotsController).to receive(:new).and_return(mr_robot_controller)
|
|
142
|
+
expect(mr_robot_controller).to receive(:my_action)
|
|
143
|
+
controller.action(action: :deprecated_action2)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should NOT call the redirected controller action method" do
|
|
147
|
+
controller.current_session.session = Xip::Session.canonical_session_slug(flow: 'mr_tron', state: 'deprecated_action')
|
|
148
|
+
expect(MrTronsController).to receive(:new).and_return(controller)
|
|
149
|
+
expect(controller).to_not receive(:deprecated_action)
|
|
150
|
+
controller.action(action: :deprecated_action)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "step_to" do
|
|
155
|
+
it "should raise an ArgumentError if a session, flow, or state is not specified" do
|
|
156
|
+
expect {
|
|
157
|
+
controller.step_to
|
|
158
|
+
}.to raise_error(ArgumentError)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should call the flow's first state's controller action when only a flow is provided" do
|
|
162
|
+
expect_any_instance_of(MrRobotsController).to receive(:my_action)
|
|
163
|
+
controller.step_to flow: "mr_robot"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should call a controller's corresponding action when only a state is provided" do
|
|
167
|
+
expect_any_instance_of(MrTronsController).to receive(:other_action3)
|
|
168
|
+
|
|
169
|
+
controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
|
170
|
+
|
|
171
|
+
controller.step_to state: "other_action3"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "should call a controller's corresponding action when a state and flow is provided" do
|
|
175
|
+
expect_any_instance_of(MrRobotsController).to receive(:my_action3)
|
|
176
|
+
controller.step_to flow: "mr_robot", state: "my_action3"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it "should call a controller's corresponding action when a session is provided" do
|
|
180
|
+
expect_any_instance_of(MrRobotsController).to receive(:my_action3)
|
|
181
|
+
|
|
182
|
+
allow(controller.current_session).to receive(:flow_string).and_return("mr_robot")
|
|
183
|
+
allow(controller.current_session).to receive(:state_string).and_return("my_action3")
|
|
184
|
+
|
|
185
|
+
controller.step_to session: controller.current_session
|
|
186
|
+
end
|
|
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
|
+
|
|
199
|
+
it "should accept flow and string specified as symbols" do
|
|
200
|
+
expect_any_instance_of(MrRobotsController).to receive(:my_action3)
|
|
201
|
+
controller.step_to flow: :mr_robot, state: :my_action3
|
|
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
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
describe "update_session_to" do
|
|
229
|
+
it "should raise an ArgumentError if a session, flow, or state is not specified" do
|
|
230
|
+
expect {
|
|
231
|
+
controller.update_session_to
|
|
232
|
+
}.to raise_error(ArgumentError)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "should update session to flow's first state's controller action when only a flow is provided" do
|
|
236
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
237
|
+
|
|
238
|
+
controller.update_session_to flow: "mr_robot"
|
|
239
|
+
expect(controller.current_session.flow_string).to eq('mr_robot')
|
|
240
|
+
expect(controller.current_session.state_string).to eq('my_action')
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it "should update session to controller's corresponding action when only a state is provided" do
|
|
244
|
+
expect_any_instance_of(MrTronsController).to_not receive(:other_action3)
|
|
245
|
+
|
|
246
|
+
controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
|
247
|
+
|
|
248
|
+
controller.update_session_to state: "other_action3"
|
|
249
|
+
expect(controller.current_session.flow_string).to eq('mr_tron')
|
|
250
|
+
expect(controller.current_session.state_string).to eq('other_action3')
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "should update session to controller's corresponding action when a state and flow is provided" do
|
|
254
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
|
|
255
|
+
|
|
256
|
+
controller.update_session_to flow: "mr_robot", state: "my_action3"
|
|
257
|
+
expect(controller.current_session.flow_string).to eq('mr_robot')
|
|
258
|
+
expect(controller.current_session.state_string).to eq('my_action3')
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should update session to controller's corresponding action when a session is provided" do
|
|
262
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
|
|
263
|
+
|
|
264
|
+
session = Xip::Session.new(id: controller.current_session_id)
|
|
265
|
+
session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
|
|
266
|
+
|
|
267
|
+
controller.update_session_to session: session
|
|
268
|
+
expect(controller.current_session.flow_string).to eq('mr_robot')
|
|
269
|
+
expect(controller.current_session.state_string).to eq('my_action3')
|
|
270
|
+
end
|
|
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
|
+
|
|
280
|
+
it "should accept flow and string specified as symbols" do
|
|
281
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action3)
|
|
282
|
+
|
|
283
|
+
controller.update_session_to flow: :mr_robot, state: :my_action3
|
|
284
|
+
expect(controller.current_session.flow_string).to eq('mr_robot')
|
|
285
|
+
expect(controller.current_session.state_string).to eq('my_action3')
|
|
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
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
describe "step_to_in" do
|
|
301
|
+
it "should raise an ArgumentError if a session, flow, or state is not specified" do
|
|
302
|
+
expect {
|
|
303
|
+
controller.step_to_in
|
|
304
|
+
}.to raise_error(ArgumentError)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "should raise an ArgumentError if delay is not specifed as an ActiveSupport::Duration" do
|
|
308
|
+
expect {
|
|
309
|
+
controller.step_to_in DateTime.now, flow: 'mr_robot'
|
|
310
|
+
}.to raise_error(ArgumentError)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it "should schedule a transition to flow's first state's controller action when only a flow is provided" do
|
|
314
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
315
|
+
|
|
316
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in).with(
|
|
317
|
+
100.seconds,
|
|
318
|
+
controller.current_service,
|
|
319
|
+
controller.current_session_id,
|
|
320
|
+
'mr_robot',
|
|
321
|
+
'my_action',
|
|
322
|
+
nil
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
expect {
|
|
326
|
+
controller.step_to_in 100.seconds, flow: "mr_robot"
|
|
327
|
+
}.to_not change(controller.current_session, :get_session)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
it "should schedule a transition to controller's corresponding action when only a state is provided" do
|
|
331
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
332
|
+
|
|
333
|
+
controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
|
334
|
+
|
|
335
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in).with(
|
|
336
|
+
100.seconds,
|
|
337
|
+
controller.current_service,
|
|
338
|
+
controller.current_session_id,
|
|
339
|
+
'mr_tron',
|
|
340
|
+
'other_action3',
|
|
341
|
+
nil
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
expect {
|
|
345
|
+
controller.step_to_in 100.seconds, state: "other_action3"
|
|
346
|
+
}.to_not change(controller.current_session, :get_session)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
it "should update session to controller's corresponding action when a state and flow is provided" do
|
|
350
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
351
|
+
|
|
352
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in).with(
|
|
353
|
+
100.seconds,
|
|
354
|
+
controller.current_service,
|
|
355
|
+
controller.current_session_id,
|
|
356
|
+
'mr_robot',
|
|
357
|
+
'my_action3',
|
|
358
|
+
nil
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
expect {
|
|
362
|
+
controller.step_to_in 100.seconds, flow: 'mr_robot', state: "my_action3"
|
|
363
|
+
}.to_not change(controller.current_session, :get_session)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
it "should update session to controller's corresponding action when a session is provided" do
|
|
367
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
368
|
+
|
|
369
|
+
session = Xip::Session.new(id: controller.current_session_id)
|
|
370
|
+
session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
|
|
371
|
+
|
|
372
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in).with(
|
|
373
|
+
100.seconds,
|
|
374
|
+
controller.current_service,
|
|
375
|
+
controller.current_session_id,
|
|
376
|
+
'mr_robot',
|
|
377
|
+
'my_action3',
|
|
378
|
+
nil
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
expect {
|
|
382
|
+
controller.step_to_in 100.seconds, session: session
|
|
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(Xip::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)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "should accept flow and string specified as symbols" do
|
|
404
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
405
|
+
|
|
406
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in).with(
|
|
407
|
+
100.seconds,
|
|
408
|
+
controller.current_service,
|
|
409
|
+
controller.current_session_id,
|
|
410
|
+
'mr_robot',
|
|
411
|
+
'my_action3',
|
|
412
|
+
nil
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
expect {
|
|
416
|
+
controller.step_to_in 100.seconds, flow: :mr_robot, state: :my_action3
|
|
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(Xip::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
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
describe "step_to_at" do
|
|
447
|
+
let(:future_timestamp) { DateTime.now + 10.hours }
|
|
448
|
+
|
|
449
|
+
it "should raise an ArgumentError if a session, flow, or state is not specified" do
|
|
450
|
+
expect {
|
|
451
|
+
controller.step_to_at
|
|
452
|
+
}.to raise_error(ArgumentError)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it "should raise an ArgumentError if delay is not specifed as a DateTime" do
|
|
456
|
+
expect {
|
|
457
|
+
controller.step_to_at 100.seconds, flow: 'mr_robot'
|
|
458
|
+
}.to raise_error(ArgumentError)
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
it "should schedule a transition to flow's first state's controller action when only a flow is provided" do
|
|
462
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
463
|
+
|
|
464
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at).with(
|
|
465
|
+
future_timestamp,
|
|
466
|
+
controller.current_service,
|
|
467
|
+
controller.current_session_id,
|
|
468
|
+
'mr_robot',
|
|
469
|
+
'my_action',
|
|
470
|
+
nil
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
expect {
|
|
474
|
+
controller.step_to_at future_timestamp, flow: "mr_robot"
|
|
475
|
+
}.to_not change(controller.current_session, :get_session)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
it "should schedule a transition to controller's corresponding action when only a state is provided" do
|
|
479
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
480
|
+
|
|
481
|
+
controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
|
482
|
+
|
|
483
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at).with(
|
|
484
|
+
future_timestamp,
|
|
485
|
+
controller.current_service,
|
|
486
|
+
controller.current_session_id,
|
|
487
|
+
'mr_tron',
|
|
488
|
+
'other_action3',
|
|
489
|
+
nil
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
expect {
|
|
493
|
+
controller.step_to_at future_timestamp, state: "other_action3"
|
|
494
|
+
}.to_not change(controller.current_session, :get_session)
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
it "should update session to controller's corresponding action when a state and flow is provided" do
|
|
498
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
499
|
+
|
|
500
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at).with(
|
|
501
|
+
future_timestamp,
|
|
502
|
+
controller.current_service,
|
|
503
|
+
controller.current_session_id,
|
|
504
|
+
'mr_robot',
|
|
505
|
+
'my_action3',
|
|
506
|
+
nil
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
expect {
|
|
510
|
+
controller.step_to_at future_timestamp, flow: 'mr_robot', state: "my_action3"
|
|
511
|
+
}.to_not change(controller.current_session, :get_session)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it "should update session to controller's corresponding action when a session is provided" do
|
|
515
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
516
|
+
|
|
517
|
+
session = Xip::Session.new(id: controller.current_session_id)
|
|
518
|
+
session.set_session(new_flow: 'mr_robot', new_state: 'my_action3')
|
|
519
|
+
|
|
520
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at).with(
|
|
521
|
+
future_timestamp,
|
|
522
|
+
controller.current_service,
|
|
523
|
+
controller.current_session_id,
|
|
524
|
+
'mr_robot',
|
|
525
|
+
'my_action3',
|
|
526
|
+
nil
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
expect {
|
|
530
|
+
controller.step_to_at future_timestamp, session: session
|
|
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(Xip::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)
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
it "should accept flow and string specified as symbols" do
|
|
552
|
+
expect_any_instance_of(MrRobotsController).to_not receive(:my_action)
|
|
553
|
+
|
|
554
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at).with(
|
|
555
|
+
future_timestamp,
|
|
556
|
+
controller.current_service,
|
|
557
|
+
controller.current_session_id,
|
|
558
|
+
'mr_robot',
|
|
559
|
+
'my_action3',
|
|
560
|
+
nil
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
expect {
|
|
564
|
+
controller.step_to_at future_timestamp, flow: :mr_robot, state: :my_action3
|
|
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(Xip::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 Xip::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(Xip::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 = Xip::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(Xip::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
|
|
701
|
+
end
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
describe "progressed?" do
|
|
705
|
+
it "should be truthy if an action calls step_to" do
|
|
706
|
+
expect(controller.progressed?).to be_falsey
|
|
707
|
+
controller.step_to flow: "mr_robot"
|
|
708
|
+
expect(controller.progressed?).to be_truthy
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
it "should be falsey if an action only calls step_to_at" do
|
|
712
|
+
expect(controller.progressed?).to be_falsey
|
|
713
|
+
|
|
714
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_at)
|
|
715
|
+
controller.step_to_at (DateTime.now + 10.hours), flow: 'mr_robot'
|
|
716
|
+
|
|
717
|
+
expect(controller.progressed?).to be_falsey
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it "should be falsey if an action only calls step_to_in" do
|
|
721
|
+
expect(controller.progressed?).to be_falsey
|
|
722
|
+
|
|
723
|
+
expect(Xip::ScheduledReplyJob).to receive(:perform_in)
|
|
724
|
+
controller.step_to_in 100.seconds, flow: 'mr_robot'
|
|
725
|
+
|
|
726
|
+
expect(controller.progressed?).to be_falsey
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
it "should be truthy if an action calls update_session_to" do
|
|
730
|
+
expect(controller.progressed?).to be_falsey
|
|
731
|
+
controller.update_session_to flow: "mr_robot"
|
|
732
|
+
expect(controller.progressed?).to be_truthy
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
it "should be truthy if an action sends replies" do
|
|
736
|
+
expect(controller.progressed?).to be_falsey
|
|
737
|
+
|
|
738
|
+
# Stub out a service reply -- we just want send_replies to succeed here
|
|
739
|
+
stubbed_service_reply = double("service_reply")
|
|
740
|
+
allow(controller).to receive(:action_replies).and_return([], :erb)
|
|
741
|
+
allow(stubbed_service_reply).to receive(:replies).and_return([])
|
|
742
|
+
allow(Xip::ServiceReply).to receive(:new).and_return(stubbed_service_reply)
|
|
743
|
+
|
|
744
|
+
controller.send_replies
|
|
745
|
+
expect(controller.progressed?).to be_truthy
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
it "should be falsey otherwise" do
|
|
749
|
+
allow(controller).to receive(:flow_controller).and_return(controller)
|
|
750
|
+
expect(controller.progressed?).to be_falsey
|
|
751
|
+
controller.action(action: :other_action)
|
|
752
|
+
expect(controller.progressed?).to be_falsey
|
|
753
|
+
end
|
|
754
|
+
end
|
|
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(Xip::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(Xip.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(Xip).to receive(:env).and_return(dev_env)
|
|
798
|
+
controller.current_message.message = 'hello world'
|
|
799
|
+
expect(Xip.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(Xip).to receive(:env).and_return(dev_env)
|
|
805
|
+
controller.current_message.message = '1/23/84'
|
|
806
|
+
expect(Xip.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(Xip).to receive(:env).and_return(dev_env)
|
|
812
|
+
controller.current_message.message = '01/23/1984'
|
|
813
|
+
expect(Xip.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(Xip).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
|
+
|
|
889
|
+
end
|