stealth 1.1.2 → 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Stealth::Nlp::Client" do
6
+
7
+ describe 'blank client' do
8
+ let(:nlp_client) { Stealth::Nlp::Client.new }
9
+
10
+ it 'should return nil for client' do
11
+ expect(nlp_client.client).to be_nil
12
+ end
13
+
14
+ it 'should return nil for the understand call' do
15
+ expect(nlp_client.understand(query: 'hello world!')).to be_nil
16
+ end
17
+
18
+ it 'should return nil for the understand_speec call' do
19
+ expect(nlp_client.understand_speech(audio_file: 'https://path.to/audio.mp3')).to be_nil
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Stealth::Nlp::Result" do
6
+
7
+ it 'should return the built-in entity types' do
8
+ expect(Stealth::Nlp::Result::ENTITY_TYPES).to eq %i(number currency email percentage phone age
9
+ url ordinal geo dimension temp datetime duration
10
+ key_phrase name)
11
+ end
12
+
13
+ describe 'blank result' do
14
+ let(:stealth_result) { Stealth::Nlp::Result.new(result: 1234) }
15
+
16
+ it 'should initialize @result to the value provided during instantiation' do
17
+ expect(stealth_result.result).to eq 1234
18
+ end
19
+
20
+ it 'should return nil for parsed_result' do
21
+ expect(stealth_result.parsed_result).to be_nil
22
+ end
23
+
24
+ it 'should return nil for intent_id' do
25
+ expect(stealth_result.intent_id).to be_nil
26
+ end
27
+
28
+ it 'should return nil for intent' do
29
+ expect(stealth_result.intent).to be_nil
30
+ end
31
+
32
+ it 'should return nil for intent_score' do
33
+ expect(stealth_result.intent_score).to be_nil
34
+ end
35
+
36
+ it 'should return {} for raw_entities' do
37
+ expect(stealth_result.raw_entities).to eq({})
38
+ end
39
+
40
+ it 'should return {} for entities' do
41
+ expect(stealth_result.entities).to eq({})
42
+ end
43
+
44
+ it 'should return nil for sentiment' do
45
+ expect(stealth_result.sentiment).to be_nil
46
+ end
47
+
48
+ it 'should return nil for sentiment_score' do
49
+ expect(stealth_result.sentiment_score).to be_nil
50
+ end
51
+
52
+ it 'should return false for present?' do
53
+ expect(stealth_result.present?).to be false
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,4 @@
1
+ - reply_type: text
2
+ text: "Hi, Morty. Welcome to Stealth bot..."
3
+ - reply_type: text
4
+ text: "We offer users an awesome Ruby framework for building chat bots."
@@ -0,0 +1,10 @@
1
+ - reply_type: speech
2
+ text:
3
+ - Build amazing chatbots with tools you know and love.
4
+ - Stealth is an open source Ruby framework for voice and text chatbots.
5
+ - From our MVC architecture to our convention over configuration philosophy, you'll feel right at home with Stealth.
6
+ - Every service integration in Stealth is a Ruby gem.
7
+ - From web servers to continuous integration testing, Stealth is built to take advantage of all the great work done by the web development community.
8
+ - Stealth is a Rack application.
9
+ - Stealth already powers bots for large, well-known brands.
10
+ - Stealth is MIT licensed to ensure you own your bot.
@@ -0,0 +1,10 @@
1
+ - reply_type: text
2
+ text:
3
+ - Build amazing chatbots with tools you know and love.
4
+ - Stealth is an open source Ruby framework for voice and text chatbots.
5
+ - From our MVC architecture to our convention over configuration philosophy, you'll feel right at home with Stealth.
6
+ - Every service integration in Stealth is a Ruby gem.
7
+ - From web servers to continuous integration testing, Stealth is built to take advantage of all the great work done by the web development community.
8
+ - Stealth is a Rack application.
9
+ - Stealth already powers bots for large, well-known brands.
10
+ - Stealth is MIT licensed to ensure you own your bot.
@@ -0,0 +1,10 @@
1
+ - reply_type: text
2
+ text: "Hi, Morty. Welcome to Stealth bot..."
3
+ - reply_type: delay
4
+ duration: 2
5
+ - reply_type: text
6
+ text: "We offer users an awesome Ruby framework for building chat bots."
7
+ - reply_type: delay
8
+ duration: 2
9
+ - reply_type: text
10
+ text: "This reply was nested."
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Stealth::Reply" do
6
+
7
+ let!(:unstructured_text) {
8
+ { 'reply_type' => 'text', 'text' => 'Hello World!' }
9
+ }
10
+ let!(:unstructured_delay) {
11
+ { 'reply_type' => 'delay', 'duration' => 'dynamic' }
12
+ }
13
+ let(:text_reply) { Stealth::Reply.new(unstructured_reply: unstructured_text) }
14
+ let(:delay_reply) { Stealth::Reply.new(unstructured_reply: unstructured_delay) }
15
+
16
+ describe 'hash-like [] getter' do
17
+ it 'should return the values' do
18
+ expect(text_reply['text']).to eq 'Hello World!'
19
+ expect(delay_reply['duration']).to eq 'dynamic'
20
+ end
21
+ end
22
+
23
+ describe 'hash-like []= setter' do
24
+ it 'should return the values' do
25
+ text_reply['woot'] = 'root'
26
+ delay_reply['duration'] = 4.3
27
+ expect(text_reply['woot']).to eq 'root'
28
+ expect(delay_reply['duration']).to eq 4.3
29
+ end
30
+ end
31
+
32
+ describe 'reply_type' do
33
+ it 'should act as a getter method for reply_type' do
34
+ expect(text_reply.reply_type).to eq 'text'
35
+ expect(delay_reply.reply_type).to eq 'delay'
36
+ end
37
+ end
38
+
39
+ describe 'delay?' do
40
+ it 'should return false for a text reply' do
41
+ expect(text_reply.delay?).to be false
42
+ end
43
+
44
+ it 'should return true for a delay reply' do
45
+ expect(delay_reply.delay?).to be true
46
+ end
47
+ end
48
+
49
+ describe 'self.dynamic_delay' do
50
+ it 'should return a new Stealth::Reply' do
51
+ expect(Stealth::Reply.dynamic_delay).to be_a(Stealth::Reply)
52
+ end
53
+
54
+ it 'should be a dynamic delay' do
55
+ expect(Stealth::Reply.dynamic_delay.delay?).to be true
56
+ expect(Stealth::Reply.dynamic_delay.reply_type).to eq 'delay'
57
+ expect(Stealth::Reply.dynamic_delay['duration']).to eq 'dynamic'
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Stealth::ScheduledReplyJob" do
6
+
7
+ let(:scheduled_reply_job) { Stealth::ScheduledReplyJob.new }
8
+
9
+ it "should instantiate BotController with service_message, set flow and state, and route" do
10
+ service_msg_double = double('service_message')
11
+ expect(Stealth::ServiceMessage).to receive(:new).with(service: 'twilio').and_return(service_msg_double)
12
+ expect(service_msg_double).to receive(:sender_id=).with('+18885551212')
13
+ expect(service_msg_double).to receive(:target_id=).with('33322')
14
+
15
+ bot_controller_double = double('bot_controller')
16
+ expect(BotController).to receive(:new).with(service_message: service_msg_double).and_return(bot_controller_double)
17
+ expect(bot_controller_double).to receive(:step_to).with(flow: 'my_flow', state: 'say_hi')
18
+
19
+ scheduled_reply_job = Stealth::ScheduledReplyJob.new
20
+ scheduled_reply_job.perform('twilio', '+18885551212', 'my_flow', 'say_hi', '33322')
21
+ end
22
+
23
+ end
@@ -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::ServiceReply" do
7
6
 
@@ -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
  class FlowMap
7
6
  include Stealth::Flow
@@ -19,20 +18,20 @@ class FlowMap
19
18
  end
20
19
 
21
20
  describe "Stealth::Session" do
22
- let(:user_id) { '0xDEADBEEF' }
21
+ let(:id) { '0xDEADBEEF' }
23
22
 
24
23
  it "should raise an error if $redis is not set" do
25
24
  $redis = nil
26
25
 
27
26
  expect {
28
- Stealth::Session.new(user_id: user_id)
27
+ Stealth::Session.new(id: id)
29
28
  }.to raise_error(Stealth::Errors::RedisNotConfigured)
30
29
 
31
30
  $redis = MockRedis.new
32
31
  end
33
32
 
34
33
  describe "without a session" do
35
- let(:session) { Stealth::Session.new(user_id: user_id) }
34
+ let(:session) { Stealth::Session.new(id: id) }
36
35
 
37
36
  it "should have nil flow and state" do
38
37
  expect(session.flow).to be_nil
@@ -52,8 +51,8 @@ describe "Stealth::Session" do
52
51
 
53
52
  describe "with a session" do
54
53
  let(:session) do
55
- session = Stealth::Session.new(user_id: user_id)
56
- session.set(flow: 'marco', state: 'polo')
54
+ session = Stealth::Session.new(id: id)
55
+ session.set_session(new_flow: 'marco', new_state: 'polo')
57
56
  session
58
57
  end
59
58
 
@@ -81,33 +80,71 @@ describe "Stealth::Session" do
81
80
  end
82
81
 
83
82
  describe "incrementing and decrementing" do
84
- let(:session) { Stealth::Session.new(user_id: user_id) }
83
+ let(:session) { Stealth::Session.new(id: id) }
85
84
 
86
85
  it "should increment the state" do
87
- session.set(flow: 'new_todo', state: 'get_due_date')
86
+ session.set_session(new_flow: 'new_todo', new_state: 'get_due_date')
88
87
  new_session = session + 1.state
89
88
  expect(new_session.state_string).to eq('created')
90
89
  end
91
90
 
92
91
  it "should decrement the state" do
93
- session.set(flow: 'new_todo', state: 'error')
92
+ session.set_session(new_flow: 'new_todo', new_state: 'error')
94
93
  new_session = session - 2.states
95
94
  expect(new_session.state_string).to eq('get_due_date')
96
95
  end
97
96
 
98
97
  it "should return the first state if the decrement is out of bounds" do
99
- session.set(flow: 'new_todo', state: 'get_due_date')
98
+ session.set_session(new_flow: 'new_todo', new_state: 'get_due_date')
100
99
  new_session = session - 5.states
101
100
  expect(new_session.state_string).to eq('new')
102
101
  end
103
102
 
104
103
  it "should return the last state if the increment is out of bounds" do
105
- session.set(flow: 'new_todo', state: 'created')
104
+ session.set_session(new_flow: 'new_todo', new_state: 'created')
106
105
  new_session = session + 5.states
107
106
  expect(new_session.state_string).to eq('error')
108
107
  end
109
108
  end
110
109
 
110
+ describe "==" do
111
+ let(:session) {
112
+ _session = Stealth::Session.new(id: id, type: :primary)
113
+ _session.set_session(new_flow: 'hello', new_state: 'say_hola')
114
+ _session
115
+ }
116
+
117
+ it "should return false if the session ids do not" do
118
+ other_session = Stealth::Session.new(id: 'xyz123', type: :primary)
119
+ other_session.set_session(new_flow: 'hello', new_state: 'say_hola')
120
+ expect(session == other_session).to be false
121
+ end
122
+
123
+ it "should return false if the states do not" do
124
+ other_session = Stealth::Session.new(id: id, type: :primary)
125
+ other_session.set_session(new_flow: 'hello', new_state: 'say_hola2')
126
+ expect(session == other_session).to be false
127
+ end
128
+
129
+ it "should return false if flows do not match" do
130
+ other_session = Stealth::Session.new(id: id, type: :primary)
131
+ other_session.set_session(new_flow: 'hello2', new_state: 'say_hola')
132
+ expect(session == other_session).to be false
133
+ end
134
+
135
+ it 'should return false if the session type does not match' do
136
+ other_session = Stealth::Session.new(id: id, type: :back_to)
137
+ other_session.set_session(new_flow: 'hello', new_state: 'say_hola')
138
+ expect(session == other_session).to be false
139
+ end
140
+
141
+ it 'should return true if the session id, flow, state, and session types match' do
142
+ other_session = Stealth::Session.new(id: id, type: :primary)
143
+ other_session.set_session(new_flow: 'hello', new_state: 'say_hola')
144
+ expect(session == other_session).to be true
145
+ end
146
+ end
147
+
111
148
  describe "self.is_a_session_string?" do
112
149
  it "should return false for state strings" do
113
150
  session_string = 'say_hello'
@@ -124,4 +161,206 @@ describe "Stealth::Session" do
124
161
  expect(Stealth::Session.is_a_session_string?(session_string)).to be true
125
162
  end
126
163
  end
164
+
165
+ describe "self.canonical_session_slug" do
166
+ it "should generate a canonical session slug given a flow and state as symbols" do
167
+ expect(
168
+ Stealth::Session.canonical_session_slug(flow: :hello, state: :say_hello)
169
+ ).to eq 'hello->say_hello'
170
+ end
171
+
172
+ it "should generate a canonical session slug given a flow and state as strings" do
173
+ expect(
174
+ Stealth::Session.canonical_session_slug(flow: 'hello', state: 'say_hello')
175
+ ).to eq 'hello->say_hello'
176
+ end
177
+ end
178
+
179
+ describe "self.flow_and_state_from_session_slug" do
180
+ it "should return the flow and string as a hash with symbolized keys" do
181
+ slug = 'hello->say_hello'
182
+ expect(
183
+ Stealth::Session.flow_and_state_from_session_slug(slug: slug)
184
+ ).to eq({ flow: 'hello', state: 'say_hello' })
185
+ end
186
+
187
+ it "should not raise if slug is nil" do
188
+ slug = nil
189
+ expect(
190
+ Stealth::Session.flow_and_state_from_session_slug(slug: slug)
191
+ ).to eq({ flow: nil, state: nil })
192
+ end
193
+ end
194
+
195
+ describe "setting sessions" do
196
+ let(:session) { Stealth::Session.new(id: id) }
197
+ let(:previous_session) { Stealth::Session.new(id: id, type: :previous) }
198
+ let(:back_to_session) { Stealth::Session.new(id: id, type: :back_to) }
199
+
200
+ before(:each) do
201
+ $redis.del(id)
202
+ $redis.del([id, 'previous'].join('-'))
203
+ $redis.del([id, 'back_to'].join('-'))
204
+ end
205
+
206
+ it "should store the new session" do
207
+ session.set_session(new_flow: 'marco', new_state: 'polo')
208
+ expect($redis.get(id)).to eq 'marco->polo'
209
+ end
210
+
211
+ it "should store the current_session to previous_session" do
212
+ $redis.set(id, 'new_todo->new')
213
+ $redis.set([id, 'previous'].join('-'), 'new_todo->error')
214
+ session.set_session(new_flow: 'marco', new_state: 'polo')
215
+ expect(previous_session.get_session).to eq 'new_todo->new'
216
+ end
217
+
218
+ it "should not update previous_session if it matches current_session" do
219
+ $redis.set(id, 'marco->polo')
220
+ $redis.set([id, 'previous'].join('-'), 'new_todo->new')
221
+ session.set_session(new_flow: 'marco', new_state: 'polo')
222
+ expect(previous_session.get_session).to eq 'new_todo->new'
223
+ end
224
+
225
+ it "should set an expiration for current_session if session_ttl is specified" do
226
+ Stealth.config.session_ttl = 500
227
+ session.set_session(new_flow: 'marco', new_state: 'polo')
228
+ expect($redis.ttl(id)).to be > 0
229
+ Stealth.config.session_ttl = 0
230
+ end
231
+
232
+ it "should set an expiration for previous_session if session_ttl is specified" do
233
+ Stealth.config.session_ttl = 500
234
+ $redis.set(id, 'new_todo->new')
235
+ session.set_session(new_flow: 'marco', new_state: 'polo')
236
+ expect($redis.ttl([id, 'previous'].join('-'))).to be > 0
237
+ Stealth.config.session_ttl = 0
238
+ end
239
+
240
+ it "should NOT set an expiration if session_ttl is not specified" do
241
+ Stealth.config.session_ttl = 0
242
+ session.set_session(new_flow: 'new_todo', new_state: 'get_due_date')
243
+ expect($redis.ttl(id)).to eq -1 # Does not expire
244
+ end
245
+
246
+ it "should set the session for back_to" do
247
+ back_to_session.set_session(new_flow: 'marco', new_state: 'polo')
248
+ expect($redis.get([id, 'back_to'].join('-'))).to eq 'marco->polo'
249
+ end
250
+
251
+ it "should set an expiration for back_to if session_ttl is specified" do
252
+ Stealth.config.session_ttl = 500
253
+ back_to_session.set_session(new_flow: 'marco', new_state: 'polo')
254
+ expect($redis.ttl([id, 'back_to'].join('-'))).to be > 0
255
+ Stealth.config.session_ttl = 0
256
+ end
257
+ end
258
+
259
+ describe "getting sessions" do
260
+ let(:session) { Stealth::Session.new(id: id) }
261
+ let(:previous_session) { Stealth::Session.new(id: id, type: :previous) }
262
+ let(:back_to_session) { Stealth::Session.new(id: id, type: :back_to) }
263
+
264
+ before(:each) do
265
+ $redis.del(id)
266
+ $redis.del([id, 'previous'].join('-'))
267
+ $redis.del([id, 'back_to'].join('-'))
268
+ end
269
+
270
+ it "should return the stored current_session" do
271
+ session.set_session(new_flow: 'marco', new_state: 'polo')
272
+ expect(session.get_session).to eq 'marco->polo'
273
+ end
274
+
275
+ it "should return the stored previous_session if previous is requested" do
276
+ $redis.set(id, 'new_todo->new')
277
+ session.set_session(new_flow: 'marco', new_state: 'polo')
278
+ expect(previous_session.get_session).to eq 'new_todo->new'
279
+ end
280
+
281
+ it "should update the expiration of current_session if session_ttl is set" do
282
+ Stealth.config.session_ttl = 50
283
+ session.set_session(new_flow: 'marco', new_state: 'polo')
284
+ expect($redis.ttl(id)).to be_between(0, 50).inclusive
285
+
286
+ Stealth.config.session_ttl = 500
287
+ session.session = nil # reset memoization
288
+ session.get_session
289
+ expect($redis.ttl(id)).to be > 100
290
+
291
+ Stealth.config.session_ttl = 0
292
+ end
293
+
294
+ it "should return the stored back_to_session" do
295
+ back_to_session.set_session(new_flow: 'marco', new_state: 'polo')
296
+ expect(back_to_session.get_session).to eq 'marco->polo'
297
+ end
298
+
299
+ it "should update the expiration of back_to_session if session_ttl is set" do
300
+ Stealth.config.session_ttl = 50
301
+ back_to_session.set_session(new_flow: 'marco', new_state: 'polo')
302
+ expect($redis.ttl([id, 'back_to'].join('-'))).to be_between(0, 50).inclusive
303
+
304
+ Stealth.config.session_ttl = 500
305
+ back_to_session.session = nil # reset memoization
306
+ back_to_session.get_session
307
+ expect($redis.ttl([id, 'back_to'].join('-'))).to be > 100
308
+
309
+ Stealth.config.session_ttl = 0
310
+ end
311
+ end
312
+
313
+ describe "clearing sessions" do
314
+ let(:session) { Stealth::Session.new(id: id) }
315
+ let(:previous_session) { Stealth::Session.new(id: id, type: :previous) }
316
+ let(:back_to_session) { Stealth::Session.new(id: id, type: :back_to) }
317
+
318
+ before(:each) do
319
+ session.send(:persist_key, key: session.session_key, value: '1')
320
+ previous_session.send(:persist_key, key: previous_session.session_key, value: '1')
321
+ back_to_session.send(:persist_key, key: back_to_session.session_key, value: '1')
322
+ end
323
+
324
+ it "should remove a default session from Redis" do
325
+ expect($redis.get(session.session_key)).to eq '1'
326
+ session.clear_session
327
+ expect($redis.get(session.session_key)).to be_nil
328
+ end
329
+
330
+ it "should remove a previous session from Redis" do
331
+ expect($redis.get(previous_session.session_key)).to eq '1'
332
+ previous_session.clear_session
333
+ expect($redis.get(previous_session.session_key)).to be_nil
334
+ end
335
+
336
+ it "should remove a back_to session from Redis" do
337
+ expect($redis.get(back_to_session.session_key)).to eq '1'
338
+ back_to_session.clear_session
339
+ expect($redis.get(back_to_session.session_key)).to be_nil
340
+ end
341
+ end
342
+
343
+ describe "self.slugify" do
344
+ it "should return a session slug given a flow and state" do
345
+ expect(Stealth::Session.slugify(flow: 'hello', state: 'world')).to eq 'hello->world'
346
+ end
347
+
348
+ it "should raise an ArgumentError if flow is blank" do
349
+ expect {
350
+ Stealth::Session.slugify(flow: '', state: 'world')
351
+ }.to raise_error(ArgumentError)
352
+ end
353
+
354
+ it "should raise an ArgumentError if state is blank" do
355
+ expect {
356
+ Stealth::Session.slugify(flow: 'hello', state: nil)
357
+ }.to raise_error(ArgumentError)
358
+ end
359
+
360
+ it "should raise an ArgumentError if flow and state are blank" do
361
+ expect {
362
+ Stealth::Session.slugify(flow: nil, state: nil)
363
+ }.to raise_error(ArgumentError)
364
+ end
365
+ end
127
366
  end