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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +18 -8
- data/CHANGELOG.md +100 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +49 -43
- data/LICENSE +4 -17
- data/README.md +9 -17
- data/VERSION +1 -1
- data/lib/stealth/base.rb +62 -13
- data/lib/stealth/cli.rb +1 -2
- data/lib/stealth/commands/console.rb +1 -1
- data/lib/stealth/configuration.rb +0 -3
- data/lib/stealth/controller/callbacks.rb +1 -1
- data/lib/stealth/controller/catch_all.rb +27 -4
- data/lib/stealth/controller/controller.rb +168 -49
- data/lib/stealth/controller/dev_jumps.rb +41 -0
- data/lib/stealth/controller/dynamic_delay.rb +4 -6
- data/lib/stealth/controller/interrupt_detect.rb +100 -0
- data/lib/stealth/controller/messages.rb +283 -0
- data/lib/stealth/controller/nlp.rb +50 -0
- data/lib/stealth/controller/replies.rb +179 -41
- data/lib/stealth/controller/unrecognized_message.rb +62 -0
- data/lib/stealth/core_ext.rb +5 -0
- data/lib/stealth/{flow/core_ext.rb → core_ext/numeric.rb} +0 -1
- data/lib/stealth/core_ext/string.rb +18 -0
- data/lib/stealth/dispatcher.rb +21 -0
- data/lib/stealth/errors.rb +12 -0
- data/lib/stealth/flow/base.rb +1 -2
- data/lib/stealth/flow/specification.rb +3 -2
- data/lib/stealth/flow/state.rb +3 -3
- data/lib/stealth/generators/builder/Gemfile +4 -3
- data/lib/stealth/generators/builder/bot/controllers/bot_controller.rb +42 -0
- data/lib/stealth/generators/builder/bot/controllers/catch_alls_controller.rb +2 -0
- data/lib/stealth/generators/builder/bot/controllers/goodbyes_controller.rb +2 -0
- data/lib/stealth/generators/builder/bot/controllers/hellos_controller.rb +2 -0
- data/lib/stealth/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
- data/lib/stealth/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
- data/lib/stealth/generators/builder/config/flow_map.rb +8 -0
- data/lib/stealth/generators/builder/config/initializers/autoload.rb +8 -0
- data/lib/stealth/generators/builder/config/initializers/inflections.rb +16 -0
- data/lib/stealth/generators/builder/config/puma.rb +15 -0
- data/lib/stealth/helpers/redis.rb +40 -0
- data/lib/stealth/lock.rb +83 -0
- data/lib/stealth/logger.rb +27 -18
- data/lib/stealth/nlp/client.rb +22 -0
- data/lib/stealth/nlp/result.rb +57 -0
- data/lib/stealth/reloader.rb +90 -0
- data/lib/stealth/reply.rb +17 -0
- data/lib/stealth/scheduled_reply.rb +3 -3
- data/lib/stealth/server.rb +4 -4
- data/lib/stealth/service_message.rb +3 -2
- data/lib/stealth/service_reply.rb +5 -1
- data/lib/stealth/services/base_reply_handler.rb +2 -2
- data/lib/stealth/session.rb +106 -53
- data/spec/configuration_spec.rb +9 -2
- data/spec/controller/callbacks_spec.rb +23 -28
- data/spec/controller/catch_all_spec.rb +81 -29
- data/spec/controller/controller_spec.rb +444 -43
- data/spec/controller/dynamic_delay_spec.rb +16 -18
- data/spec/controller/helpers_spec.rb +1 -2
- 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 +446 -11
- data/spec/controller/unrecognized_message_spec.rb +168 -0
- data/spec/dispatcher_spec.rb +79 -0
- data/spec/flow/flow_spec.rb +1 -2
- data/spec/flow/state_spec.rb +14 -3
- 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/messages/say_msgs_without_breaks.yml +4 -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/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 +1 -2
- data/spec/session_spec.rb +251 -12
- data/spec/spec_helper.rb +21 -0
- data/spec/support/controllers/vaders_controller.rb +24 -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/version_spec.rb +1 -2
- data/stealth.gemspec +6 -6
- metadata +83 -39
- data/docs/00-introduction.md +0 -37
- data/docs/01-getting-started.md +0 -21
- data/docs/02-local-development.md +0 -40
- data/docs/03-basics.md +0 -171
- data/docs/04-sessions.md +0 -29
- data/docs/05-controllers.md +0 -179
- data/docs/06-models.md +0 -39
- data/docs/07-replies.md +0 -114
- data/docs/08-catchalls.md +0 -49
- data/docs/09-messaging-integrations.md +0 -80
- data/docs/10-nlp-integrations.md +0 -13
- data/docs/11-analytics.md +0 -13
- data/docs/12-commands.md +0 -62
- data/docs/13-deployment.md +0 -50
- 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,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."
|
data/spec/reply_spec.rb
ADDED
@@ -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
|
data/spec/service_reply_spec.rb
CHANGED
data/spec/session_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
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(:
|
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(
|
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(
|
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(
|
56
|
-
session.
|
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(
|
83
|
+
let(:session) { Stealth::Session.new(id: id) }
|
85
84
|
|
86
85
|
it "should increment the state" do
|
87
|
-
session.
|
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.
|
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.
|
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.
|
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
|