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,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Stealth::Controller::UnrecognizedMessage" do
|
6
|
+
|
7
|
+
let(:fb_message) { SampleMessage.new(service: 'facebook') }
|
8
|
+
let(:controller) { VadersController.new(service_message: fb_message.message_with_text) }
|
9
|
+
|
10
|
+
describe 'run_unrecognized_message' do
|
11
|
+
let(:e) {
|
12
|
+
e = OpenStruct.new
|
13
|
+
e.class = RuntimeError
|
14
|
+
e.message = 'oops'
|
15
|
+
e.backtrace = [
|
16
|
+
'/stealth/lib/stealth/controller/controller.rb',
|
17
|
+
'/stealth/lib/stealth/controller/catch_all.rb',
|
18
|
+
]
|
19
|
+
e
|
20
|
+
}
|
21
|
+
|
22
|
+
describe 'when UnrecognizedMessagesController is not defined' do
|
23
|
+
before(:each) do
|
24
|
+
Object.send(:remove_const, :UnrecognizedMessagesController)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should log and run catch_all" do
|
28
|
+
expect(Stealth::Logger).to receive(:l).with(
|
29
|
+
topic: 'unrecognized_message',
|
30
|
+
message: "The message \"Hello World!\" was not recognized in the original context."
|
31
|
+
).ordered
|
32
|
+
|
33
|
+
expect(Stealth::Logger).to receive(:l).with(
|
34
|
+
topic: 'unrecognized_message',
|
35
|
+
message: 'Running catch_all; UnrecognizedMessagesController not defined.'
|
36
|
+
).ordered
|
37
|
+
|
38
|
+
expect(controller).to receive(:run_catch_all).with(err: e)
|
39
|
+
controller.run_unrecognized_message(err: e)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should call handle_unrecognized_message on the UnrecognizedMessagesController" do
|
44
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
45
|
+
def handle_unrecognized_message
|
46
|
+
do_nothing
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
expect(Stealth::Logger).to receive(:l).with(
|
51
|
+
topic: 'unrecognized_message',
|
52
|
+
message: "The message \"Hello World!\" was not recognized in the original context."
|
53
|
+
).ordered
|
54
|
+
|
55
|
+
expect(Stealth::Logger).to receive(:l).with(
|
56
|
+
topic: 'unrecognized_message',
|
57
|
+
message: 'A match was detected. Skipping catch-all.'
|
58
|
+
).ordered
|
59
|
+
|
60
|
+
controller.run_unrecognized_message(err: e)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should log if the UnrecognizedMessagesController#handle_unrecognized_message does not progress the session" do
|
64
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
65
|
+
def handle_unrecognized_message
|
66
|
+
# Oops
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
expect(Stealth::Logger).to receive(:l).with(
|
71
|
+
topic: 'unrecognized_message',
|
72
|
+
message: "The message \"Hello World!\" was not recognized in the original context."
|
73
|
+
).ordered
|
74
|
+
|
75
|
+
expect(Stealth::Logger).to receive(:l).with(
|
76
|
+
topic: 'unrecognized_message',
|
77
|
+
message: 'Did not send replies, update session, or step'
|
78
|
+
).ordered
|
79
|
+
|
80
|
+
expect(controller).to_not receive(:run_catch_all)
|
81
|
+
|
82
|
+
controller.run_unrecognized_message(err: e)
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'handoff to catch_all' do
|
86
|
+
before(:each) do
|
87
|
+
@session = Stealth::Session.new(id: controller.current_session_id)
|
88
|
+
@session.set_session(new_flow: 'vader', new_state: 'action_with_unrecognized_msg')
|
89
|
+
|
90
|
+
@error_slug = [
|
91
|
+
'error',
|
92
|
+
controller.current_session_id,
|
93
|
+
'vader',
|
94
|
+
'action_with_unrecognized_msg'
|
95
|
+
].join('-')
|
96
|
+
|
97
|
+
$redis.del(@error_slug)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should catch StandardError within UnrecognizedMessagesController and run catch_all" do
|
101
|
+
$err = Stealth::Errors::ReplyNotFound.new('oops')
|
102
|
+
|
103
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
104
|
+
def handle_unrecognized_message
|
105
|
+
raise $err
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
expect(Stealth::Logger).to receive(:l).with(
|
110
|
+
topic: 'unrecognized_message',
|
111
|
+
message: "The message \"Hello World!\" was not recognized in the original context."
|
112
|
+
).ordered
|
113
|
+
|
114
|
+
expect(controller).to receive(:run_catch_all).with(err: $err)
|
115
|
+
|
116
|
+
controller.run_unrecognized_message(err: e)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should track the catch_all level against the original session during exceptions" do
|
120
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
121
|
+
def handle_unrecognized_message
|
122
|
+
raise 'oops'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
expect($redis.get(@error_slug)).to be_nil
|
127
|
+
controller.run_unrecognized_message(err: e)
|
128
|
+
expect($redis.get(@error_slug)).to eq '1'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should track the catch_all level against the original session for UnrecognizedMessage errors" do
|
132
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
133
|
+
def handle_unrecognized_message
|
134
|
+
handle_message(
|
135
|
+
'x' => proc { do_nothing },
|
136
|
+
'y' => proc { do_nothing }
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
expect($redis.get(@error_slug)).to be_nil
|
142
|
+
controller.action(action: :action_with_unrecognized_msg)
|
143
|
+
expect($redis.get(@error_slug)).to eq '1'
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should NOT run catch_all if UnrecognizedMessagesController handles the message" do
|
147
|
+
$x = 0
|
148
|
+
class UnrecognizedMessagesController < Stealth::Controller
|
149
|
+
def handle_unrecognized_message
|
150
|
+
handle_message(
|
151
|
+
'Hello World!' => proc {
|
152
|
+
$x = 1
|
153
|
+
do_nothing
|
154
|
+
},
|
155
|
+
'y' => proc { do_nothing }
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
expect($redis.get(@error_slug)).to be_nil
|
161
|
+
controller.action(action: :action_with_unrecognized_msg)
|
162
|
+
expect($redis.get(@error_slug)).to be_nil
|
163
|
+
expect($x).to eq 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Stealth::Dispatcher" do
|
6
|
+
|
7
|
+
class Stealth::Services::Facebook::MessageHandler
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'coordinate' do
|
12
|
+
let(:dispatcher) {
|
13
|
+
Stealth::Dispatcher.new(
|
14
|
+
service: 'facebook',
|
15
|
+
params: {},
|
16
|
+
headers: {}
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
it 'should call coordinate on the message handler' do
|
21
|
+
message_handler = double
|
22
|
+
expect(Stealth::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
|
23
|
+
expect(message_handler).to receive(:coordinate)
|
24
|
+
|
25
|
+
dispatcher.coordinate
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'process' do
|
30
|
+
class StubbedBotController < Stealth::Controller
|
31
|
+
def route
|
32
|
+
true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:dispatcher) {
|
37
|
+
Stealth::Dispatcher.new(
|
38
|
+
service: 'facebook',
|
39
|
+
params: {},
|
40
|
+
headers: {}
|
41
|
+
)
|
42
|
+
}
|
43
|
+
let(:fb_message) { SampleMessage.new(service: 'facebook') }
|
44
|
+
let(:stubbed_controller) {
|
45
|
+
StubbedBotController.new(service_message: fb_message.message_with_text)
|
46
|
+
}
|
47
|
+
|
48
|
+
it 'should call process on the message handler' do
|
49
|
+
message_handler = double
|
50
|
+
|
51
|
+
# Stub out the message handler to return a service_message
|
52
|
+
expect(Stealth::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
|
53
|
+
expect(message_handler).to receive(:process).and_return(fb_message.message_with_text)
|
54
|
+
|
55
|
+
# Stub out BotController and set session
|
56
|
+
expect(BotController).to receive(:new).and_return(stubbed_controller)
|
57
|
+
stubbed_controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
58
|
+
|
59
|
+
dispatcher.process
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should log the incoming message if transcript_logging is enabled' do
|
63
|
+
message_handler = double
|
64
|
+
|
65
|
+
# Stub out the message handler to return a service_message
|
66
|
+
expect(Stealth::Services::Facebook::MessageHandler).to receive(:new).and_return(message_handler)
|
67
|
+
expect(message_handler).to receive(:process).and_return(fb_message.message_with_text)
|
68
|
+
|
69
|
+
# Stub out BotController and set session
|
70
|
+
expect(BotController).to receive(:new).and_return(stubbed_controller)
|
71
|
+
stubbed_controller.current_session.set_session(new_flow: 'mr_tron', new_state: 'other_action')
|
72
|
+
|
73
|
+
Stealth.config.transcript_logging = true
|
74
|
+
expect(dispatcher).to receive(:log_incoming_message).with(fb_message.message_with_text)
|
75
|
+
dispatcher.process
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/spec/flow/flow_spec.rb
CHANGED
data/spec/flow/state_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
|
describe Stealth::Flow::State do
|
7
6
|
|
@@ -15,6 +14,7 @@ describe Stealth::Flow::State do
|
|
15
14
|
state :created2, fails_to: 'new_todo->new'
|
16
15
|
state :deprecated, redirects_to: 'new'
|
17
16
|
state :deprecated2, redirects_to: 'other_flow->say_hi'
|
17
|
+
state :new_opts, opt1: 'hello', opt2: 1
|
18
18
|
state :error
|
19
19
|
end
|
20
20
|
end
|
@@ -69,6 +69,17 @@ describe Stealth::Flow::State do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
describe "opts" do
|
73
|
+
it "should return {} for a state that has not specified any opts" do
|
74
|
+
expect(flow_map.current_state.opts).to eq({})
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return the opts if they were specified" do
|
78
|
+
flow_map.init(flow: :new_todo, state: :new_opts)
|
79
|
+
expect(flow_map.current_state.opts).to eq({ opt1: 'hello', opt2: 1 })
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
72
83
|
describe "state incrementing and decrementing" do
|
73
84
|
it "should increment the state" do
|
74
85
|
flow_map.init(flow: :new_todo, state: :get_due_date)
|
@@ -78,7 +89,7 @@ describe Stealth::Flow::State do
|
|
78
89
|
|
79
90
|
it "should decrement the state" do
|
80
91
|
flow_map.init(flow: :new_todo, state: :error)
|
81
|
-
new_state = flow_map.current_state -
|
92
|
+
new_state = flow_map.current_state - 6.states
|
82
93
|
expect(new_state).to eq(:get_due_date)
|
83
94
|
end
|
84
95
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Stealth::Redis" do
|
6
|
+
|
7
|
+
class RedisTester
|
8
|
+
include Stealth::Redis
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:redis_tester) { RedisTester.new }
|
12
|
+
let(:key) { 'xyz' }
|
13
|
+
|
14
|
+
describe "get_key" do
|
15
|
+
it "should return the key from Redis if an expiration is not set" do
|
16
|
+
$redis.set(key, 'abc')
|
17
|
+
expect(redis_tester.send(:get_key, key)).to eq 'abc'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should call getex if an expiration is set" do
|
21
|
+
expect(redis_tester).to receive(:getex).with(key, 30)
|
22
|
+
redis_tester.send(:get_key, key, expiration: 30)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "delete_key" do
|
27
|
+
it 'should delete the key from Redis' do
|
28
|
+
$redis.set(key, 'abc')
|
29
|
+
expect(redis_tester.send(:get_key, key)).to eq 'abc'
|
30
|
+
redis_tester.send(:delete_key, key)
|
31
|
+
expect(redis_tester.send(:get_key, key)).to be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "getex" do
|
36
|
+
it "should return the key from Redis" do
|
37
|
+
Stealth.config.session_ttl = 50
|
38
|
+
$redis.set(key, 'abc')
|
39
|
+
expect(redis_tester.send(:getex, key)).to eq 'abc'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should set the expiration of a key in Redis" do
|
43
|
+
Stealth.config.session_ttl = 50
|
44
|
+
$redis.set(key, 'abc')
|
45
|
+
redis_tester.send(:getex, key)
|
46
|
+
expect($redis.ttl(key)).to be_between(0, 50).inclusive
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should update the expiration of a key in Redis" do
|
50
|
+
Stealth.config.session_ttl = 500
|
51
|
+
$redis.setex(key, 50, 'abc')
|
52
|
+
redis_tester.send(:getex, key)
|
53
|
+
expect($redis.ttl(key)).to be_between(400, 500).inclusive
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "persist_key" do
|
58
|
+
it "should set the key in Redis" do
|
59
|
+
Stealth.config.session_ttl = 50
|
60
|
+
redis_tester.send(:persist_key, key: key, value: 'zzz')
|
61
|
+
expect($redis.get(key)).to eq 'zzz'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should set the expiration to session_ttl if none specified" do
|
65
|
+
Stealth.config.session_ttl = 50
|
66
|
+
redis_tester.send(:persist_key, key: key, value: 'zzz')
|
67
|
+
expect($redis.ttl(key)).to be_between(0, 50).inclusive
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should set the expiration to the specified value when provided" do
|
71
|
+
Stealth.config.session_ttl = 50
|
72
|
+
redis_tester.send(:persist_key, key: key, value: 'zzz', expiration: 500)
|
73
|
+
expect($redis.ttl(key)).to be_between(400, 500).inclusive
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/spec/lock_spec.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Stealth::Lock" do
|
6
|
+
let(:session_id) { SecureRandom.hex(14) }
|
7
|
+
let(:session_slug) { 'hello->say_hello' }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
Stealth.config.lock_autorelease = 30
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "create" do
|
14
|
+
it "should raise an ArgumentError if the session_slug was not provided" do
|
15
|
+
lock = Stealth::Lock.new(session_id: session_id)
|
16
|
+
expect {
|
17
|
+
lock.create
|
18
|
+
}.to raise_error(ArgumentError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should save the lock using a canonical key and value" do
|
22
|
+
lock = Stealth::Lock.new(session_id: session_id, session_slug: session_slug)
|
23
|
+
canonical_key = "#{session_id}-lock"
|
24
|
+
expected_value = "#{lock.tid}##{session_slug}"
|
25
|
+
lock.create
|
26
|
+
expect($redis.get(canonical_key)).to eq expected_value
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should include the reply file position in the lock" do
|
30
|
+
lock = Stealth::Lock.new(
|
31
|
+
session_id: session_id,
|
32
|
+
session_slug: session_slug,
|
33
|
+
position: 3
|
34
|
+
)
|
35
|
+
canonical_key = "#{session_id}-lock"
|
36
|
+
expected_value = "#{lock.tid}##{session_slug}:3"
|
37
|
+
lock.create
|
38
|
+
expect($redis.get(canonical_key)).to eq expected_value
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should set the lock expiration to lock_autorelease" do
|
42
|
+
lock = Stealth::Lock.new(session_id: session_id, session_slug: session_slug)
|
43
|
+
canonical_key = "#{session_id}-lock"
|
44
|
+
expected_value = "#{lock.tid}##{session_slug}"
|
45
|
+
lock.create
|
46
|
+
expect($redis.ttl(canonical_key)).to be_between(1, 30).inclusive
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "release" do
|
51
|
+
it "should delete the key in Redis" do
|
52
|
+
lock = Stealth::Lock.new(session_id: session_id, session_slug: session_slug)
|
53
|
+
canonical_key = "#{session_id}-lock"
|
54
|
+
lock.create
|
55
|
+
expect($redis.get(canonical_key)).to_not be_nil
|
56
|
+
lock.release
|
57
|
+
expect($redis.get(canonical_key)).to be_nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "slug" do
|
62
|
+
it "should return the lock slug from Redis" do
|
63
|
+
lock = Stealth::Lock.new(session_id: session_id, session_slug: session_slug)
|
64
|
+
lock.create
|
65
|
+
canonical_key = "#{session_id}-lock"
|
66
|
+
expect(lock.slug).to eq "#{lock.tid}##{session_slug}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "flow_and_state" do
|
71
|
+
it "should return a hash containing the flow and state" do
|
72
|
+
lock = Stealth::Lock.new(session_id: session_id, session_slug: session_slug)
|
73
|
+
expect(lock.flow_and_state[:flow]).to eq 'hello'
|
74
|
+
expect(lock.flow_and_state[:state]).to eq 'say_hello'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "self.find_lock" do
|
79
|
+
it "should load the lock from Redis" do
|
80
|
+
lock_key = "#{session_id}-lock"
|
81
|
+
example_tid = 'ovefhgJvx'
|
82
|
+
example_session = 'goodbye->say_goodbye'
|
83
|
+
example_position = 2
|
84
|
+
example_lock = "#{example_tid}##{example_session}:#{example_position}"
|
85
|
+
$redis.set(lock_key, example_lock)
|
86
|
+
|
87
|
+
lock = Stealth::Lock.find_lock(session_id: session_id)
|
88
|
+
expect(lock.tid).to eq example_tid
|
89
|
+
expect(lock.session_slug).to eq example_session
|
90
|
+
expect(lock.position).to eq example_position
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return nil if the lock is not found" do
|
94
|
+
lock_key = "#{session_id}-lock"
|
95
|
+
lock = Stealth::Lock.find_lock(session_id: session_id)
|
96
|
+
expect($redis.get(lock_key)).to be_nil
|
97
|
+
expect(lock).to be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|