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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +116 -0
  3. data/.gitignore +12 -0
  4. data/CHANGELOG.md +135 -0
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +65 -15
  7. data/LICENSE +6 -4
  8. data/README.md +51 -1
  9. data/VERSION +1 -0
  10. data/bin/xip +3 -11
  11. data/lib/xip.rb +1 -3
  12. data/lib/xip/base.rb +189 -0
  13. data/lib/xip/cli.rb +273 -0
  14. data/lib/xip/cli_base.rb +24 -0
  15. data/lib/xip/commands/command.rb +13 -0
  16. data/lib/xip/commands/console.rb +74 -0
  17. data/lib/xip/commands/server.rb +63 -0
  18. data/lib/xip/configuration.rb +56 -0
  19. data/lib/xip/controller/callbacks.rb +63 -0
  20. data/lib/xip/controller/catch_all.rb +84 -0
  21. data/lib/xip/controller/controller.rb +274 -0
  22. data/lib/xip/controller/dev_jumps.rb +40 -0
  23. data/lib/xip/controller/dynamic_delay.rb +61 -0
  24. data/lib/xip/controller/helpers.rb +128 -0
  25. data/lib/xip/controller/interrupt_detect.rb +99 -0
  26. data/lib/xip/controller/messages.rb +283 -0
  27. data/lib/xip/controller/nlp.rb +49 -0
  28. data/lib/xip/controller/replies.rb +281 -0
  29. data/lib/xip/controller/unrecognized_message.rb +61 -0
  30. data/lib/xip/core_ext.rb +5 -0
  31. data/lib/xip/core_ext/numeric.rb +10 -0
  32. data/lib/xip/core_ext/string.rb +18 -0
  33. data/lib/xip/dispatcher.rb +68 -0
  34. data/lib/xip/errors.rb +55 -0
  35. data/lib/xip/flow/base.rb +69 -0
  36. data/lib/xip/flow/specification.rb +56 -0
  37. data/lib/xip/flow/state.rb +82 -0
  38. data/lib/xip/generators/builder.rb +41 -0
  39. data/lib/xip/generators/builder/.gitignore +30 -0
  40. data/lib/xip/generators/builder/Gemfile +19 -0
  41. data/lib/xip/generators/builder/Procfile.dev +2 -0
  42. data/lib/xip/generators/builder/README.md +9 -0
  43. data/lib/xip/generators/builder/Rakefile +2 -0
  44. data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
  45. data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
  46. data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
  47. data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
  48. data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
  49. data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  50. data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  51. data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
  52. data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
  53. data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
  54. data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
  55. data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
  56. data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
  57. data/lib/xip/generators/builder/config.ru +4 -0
  58. data/lib/xip/generators/builder/config/boot.rb +6 -0
  59. data/lib/xip/generators/builder/config/database.yml +25 -0
  60. data/lib/xip/generators/builder/config/environment.rb +2 -0
  61. data/lib/xip/generators/builder/config/flow_map.rb +25 -0
  62. data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
  63. data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
  64. data/lib/xip/generators/builder/config/puma.rb +25 -0
  65. data/lib/xip/generators/builder/config/services.yml +35 -0
  66. data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
  67. data/lib/xip/generators/builder/db/seeds.rb +7 -0
  68. data/lib/xip/generators/generate.rb +39 -0
  69. data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
  70. data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
  71. data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
  72. data/lib/xip/helpers/redis.rb +40 -0
  73. data/lib/xip/jobs.rb +9 -0
  74. data/lib/xip/lock.rb +82 -0
  75. data/lib/xip/logger.rb +9 -3
  76. data/lib/xip/migrations/configurator.rb +73 -0
  77. data/lib/xip/migrations/generators.rb +16 -0
  78. data/lib/xip/migrations/railtie_config.rb +14 -0
  79. data/lib/xip/migrations/tasks.rb +43 -0
  80. data/lib/xip/nlp/client.rb +21 -0
  81. data/lib/xip/nlp/result.rb +56 -0
  82. data/lib/xip/reloader.rb +89 -0
  83. data/lib/xip/reply.rb +36 -0
  84. data/lib/xip/scheduled_reply.rb +18 -0
  85. data/lib/xip/server.rb +63 -0
  86. data/lib/xip/service_message.rb +17 -0
  87. data/lib/xip/service_reply.rb +44 -0
  88. data/lib/xip/services/base_client.rb +24 -0
  89. data/lib/xip/services/base_message_handler.rb +27 -0
  90. data/lib/xip/services/base_reply_handler.rb +72 -0
  91. data/lib/xip/services/jobs/handle_message_job.rb +21 -0
  92. data/lib/xip/session.rb +203 -0
  93. data/lib/xip/version.rb +7 -1
  94. data/logo.svg +17 -0
  95. data/spec/configuration_spec.rb +93 -0
  96. data/spec/controller/callbacks_spec.rb +217 -0
  97. data/spec/controller/catch_all_spec.rb +154 -0
  98. data/spec/controller/controller_spec.rb +889 -0
  99. data/spec/controller/dynamic_delay_spec.rb +70 -0
  100. data/spec/controller/helpers_spec.rb +119 -0
  101. data/spec/controller/interrupt_detect_spec.rb +171 -0
  102. data/spec/controller/messages_spec.rb +744 -0
  103. data/spec/controller/nlp_spec.rb +93 -0
  104. data/spec/controller/replies_spec.rb +694 -0
  105. data/spec/controller/unrecognized_message_spec.rb +168 -0
  106. data/spec/dispatcher_spec.rb +79 -0
  107. data/spec/flow/flow_spec.rb +82 -0
  108. data/spec/flow/state_spec.rb +109 -0
  109. data/spec/helpers/redis_spec.rb +77 -0
  110. data/spec/lock_spec.rb +100 -0
  111. data/spec/nlp/client_spec.rb +23 -0
  112. data/spec/nlp/result_spec.rb +57 -0
  113. data/spec/replies/hello.yml.erb +15 -0
  114. data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
  115. data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
  116. data/spec/replies/messages/say_hola.yml.erb +6 -0
  117. data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
  118. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  119. data/spec/replies/messages/say_offer.yml +6 -0
  120. data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
  121. data/spec/replies/messages/say_oi.yml.erb +15 -0
  122. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  123. data/spec/replies/messages/say_randomize_text.yml +10 -0
  124. data/spec/replies/messages/say_yo.yml +6 -0
  125. data/spec/replies/messages/say_yo.yml+twitter +6 -0
  126. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  127. data/spec/reply_spec.rb +61 -0
  128. data/spec/scheduled_reply_spec.rb +23 -0
  129. data/spec/service_reply_spec.rb +92 -0
  130. data/spec/session_spec.rb +366 -0
  131. data/spec/spec_helper.rb +22 -66
  132. data/spec/support/alternate_helpers/foo_helper.rb +5 -0
  133. data/spec/support/controllers/vaders_controller.rb +24 -0
  134. data/spec/support/helpers/fun/games_helper.rb +7 -0
  135. data/spec/support/helpers/fun/pdf_helper.rb +7 -0
  136. data/spec/support/helpers/standalone_helper.rb +5 -0
  137. data/spec/support/helpers_typo/users_helper.rb +2 -0
  138. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  139. data/spec/support/nlp_clients/luis.rb +9 -0
  140. data/spec/support/nlp_results/luis_result.rb +163 -0
  141. data/spec/support/sample_messages.rb +66 -0
  142. data/spec/support/services.yml +31 -0
  143. data/spec/support/services_with_erb.yml +31 -0
  144. data/spec/version_spec.rb +16 -0
  145. data/xip.gemspec +25 -14
  146. metadata +320 -18
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Xip::ScheduledReplyJob" do
6
+
7
+ let(:scheduled_reply_job) { Xip::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(Xip::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 = Xip::ScheduledReplyJob.new
20
+ scheduled_reply_job.perform('twilio', '+18885551212', 'my_flow', 'say_hi', '33322')
21
+ end
22
+
23
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Xip::ServiceReply" do
6
+
7
+ let(:recipient_id) { "8b3e0a3c-62f1-401e-8b0f-615c9d256b1f" }
8
+ let(:yaml_reply) { File.read(File.join(File.dirname(__FILE__), 'replies', 'hello.yml.erb')) }
9
+
10
+ describe "nested reply with ERB" do
11
+ it "should load all the replies" do
12
+ first_name = "Presley"
13
+
14
+ service_reply = Xip::ServiceReply.new(
15
+ recipient_id: recipient_id,
16
+ yaml_reply: yaml_reply,
17
+ context: binding,
18
+ preprocessor: :erb
19
+ )
20
+
21
+ expect(service_reply.replies.size).to eq 5
22
+ end
23
+
24
+ it "should load all replies as Xip::Reply objects" do
25
+ first_name = "Presley"
26
+
27
+ service_reply = Xip::ServiceReply.new(
28
+ recipient_id: recipient_id,
29
+ yaml_reply: yaml_reply,
30
+ context: binding,
31
+ preprocessor: :erb
32
+ )
33
+
34
+ expect(service_reply.replies).to all(be_an(Xip::Reply))
35
+ end
36
+
37
+ it "should replace the ERB tag" do
38
+ first_name = "Presley"
39
+
40
+ service_reply = Xip::ServiceReply.new(
41
+ recipient_id: recipient_id,
42
+ yaml_reply: yaml_reply,
43
+ context: binding,
44
+ preprocessor: :erb
45
+ )
46
+
47
+ phrase_in_reply = service_reply.replies.first['text']
48
+ expect(phrase_in_reply).to eq "Hi, Presley. Welcome to Xip bot..."
49
+ end
50
+
51
+ it "should raise Xip::Errors::UndefinedVariable when local variable is not available" do
52
+ expect {
53
+ service_reply = Xip::ServiceReply.new(
54
+ recipient_id: recipient_id,
55
+ yaml_reply: yaml_reply,
56
+ context: binding,
57
+ preprocessor: :erb
58
+ )
59
+ }.to raise_error(Xip::Errors::UndefinedVariable)
60
+ end
61
+ end
62
+
63
+ describe "processing a reply without a preprocessor specified" do
64
+ it "should not replace the ERB tag when no preprocessor is specified" do
65
+ first_name = "Gisele"
66
+
67
+ service_reply = Xip::ServiceReply.new(
68
+ recipient_id: recipient_id,
69
+ yaml_reply: yaml_reply,
70
+ context: binding
71
+ )
72
+
73
+ phrase_in_reply = service_reply.replies.first['text']
74
+ expect(phrase_in_reply).to eq "Hi, <%= first_name %>. Welcome to Xip bot..."
75
+ end
76
+
77
+ it "should not replace the ERB tag when :none is specified as the preprocessor" do
78
+ first_name = "Gisele"
79
+
80
+ service_reply = Xip::ServiceReply.new(
81
+ recipient_id: recipient_id,
82
+ yaml_reply: yaml_reply,
83
+ context: binding,
84
+ preprocessor: :none
85
+ )
86
+
87
+ phrase_in_reply = service_reply.replies.first['text']
88
+ expect(phrase_in_reply).to eq "Hi, <%= first_name %>. Welcome to Xip bot..."
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,366 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ class FlowMap
6
+ include Xip::Flow
7
+
8
+ flow :new_todo do
9
+ state :new
10
+ state :get_due_date
11
+ state :created, fails_to: :new
12
+ state :error
13
+ end
14
+
15
+ flow :marco do
16
+ state :polo
17
+ end
18
+ end
19
+
20
+ describe "Xip::Session" do
21
+ let(:id) { '0xDEADBEEF' }
22
+
23
+ it "should raise an error if $redis is not set" do
24
+ $redis = nil
25
+
26
+ expect {
27
+ Xip::Session.new(id: id)
28
+ }.to raise_error(Xip::Errors::RedisNotConfigured)
29
+
30
+ $redis = MockRedis.new
31
+ end
32
+
33
+ describe "without a session" do
34
+ let(:session) { Xip::Session.new(id: id) }
35
+
36
+ it "should have nil flow and state" do
37
+ expect(session.flow).to be_nil
38
+ expect(session.state).to be_nil
39
+ end
40
+
41
+ it "should have nil flow_string and state_string" do
42
+ expect(session.flow_string).to be_nil
43
+ expect(session.state_string).to be_nil
44
+ end
45
+
46
+ it "should respond to present? and blank?" do
47
+ expect(session.present?).to be false
48
+ expect(session.blank?).to be true
49
+ end
50
+ end
51
+
52
+ describe "with a session" do
53
+ let(:session) do
54
+ session = Xip::Session.new(id: id)
55
+ session.set_session(new_flow: 'marco', new_state: 'polo')
56
+ session
57
+ end
58
+
59
+ it "should return the FlowMap" do
60
+ expect(session.flow).to be_a(FlowMap)
61
+ end
62
+
63
+ it "should return the state" do
64
+ expect(session.state).to be_a(Xip::Flow::State)
65
+ expect(session.state).to eq :polo
66
+ end
67
+
68
+ it "should return the flow_string" do
69
+ expect(session.flow_string).to eq "marco"
70
+ end
71
+
72
+ it "should return the state_string" do
73
+ expect(session.state_string).to eq "polo"
74
+ end
75
+
76
+ it "should respond to present? and blank?" do
77
+ expect(session.present?).to be true
78
+ expect(session.blank?).to be false
79
+ end
80
+ end
81
+
82
+ describe "incrementing and decrementing" do
83
+ let(:session) { Xip::Session.new(id: id) }
84
+
85
+ it "should increment the state" do
86
+ session.set_session(new_flow: 'new_todo', new_state: 'get_due_date')
87
+ new_session = session + 1.state
88
+ expect(new_session.state_string).to eq('created')
89
+ end
90
+
91
+ it "should decrement the state" do
92
+ session.set_session(new_flow: 'new_todo', new_state: 'error')
93
+ new_session = session - 2.states
94
+ expect(new_session.state_string).to eq('get_due_date')
95
+ end
96
+
97
+ it "should return the first state if the decrement is out of bounds" do
98
+ session.set_session(new_flow: 'new_todo', new_state: 'get_due_date')
99
+ new_session = session - 5.states
100
+ expect(new_session.state_string).to eq('new')
101
+ end
102
+
103
+ it "should return the last state if the increment is out of bounds" do
104
+ session.set_session(new_flow: 'new_todo', new_state: 'created')
105
+ new_session = session + 5.states
106
+ expect(new_session.state_string).to eq('error')
107
+ end
108
+ end
109
+
110
+ describe "==" do
111
+ let(:session) {
112
+ _session = Xip::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 = Xip::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 = Xip::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 = Xip::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 = Xip::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 = Xip::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
+
148
+ describe "self.is_a_session_string?" do
149
+ it "should return false for state strings" do
150
+ session_string = 'say_hello'
151
+ expect(Xip::Session.is_a_session_string?(session_string)).to be false
152
+ end
153
+
154
+ it "should return false for an incomplete session string" do
155
+ session_string = 'hello->'
156
+ expect(Xip::Session.is_a_session_string?(session_string)).to be false
157
+ end
158
+
159
+ it "should return true for a complete session string" do
160
+ session_string = 'hello->say_hello'
161
+ expect(Xip::Session.is_a_session_string?(session_string)).to be true
162
+ end
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
+ Xip::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
+ Xip::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
+ Xip::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
+ Xip::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) { Xip::Session.new(id: id) }
197
+ let(:previous_session) { Xip::Session.new(id: id, type: :previous) }
198
+ let(:back_to_session) { Xip::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
+ Xip.config.session_ttl = 500
227
+ session.set_session(new_flow: 'marco', new_state: 'polo')
228
+ expect($redis.ttl(id)).to be > 0
229
+ Xip.config.session_ttl = 0
230
+ end
231
+
232
+ it "should set an expiration for previous_session if session_ttl is specified" do
233
+ Xip.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
+ Xip.config.session_ttl = 0
238
+ end
239
+
240
+ it "should NOT set an expiration if session_ttl is not specified" do
241
+ Xip.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
+ Xip.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
+ Xip.config.session_ttl = 0
256
+ end
257
+ end
258
+
259
+ describe "getting sessions" do
260
+ let(:session) { Xip::Session.new(id: id) }
261
+ let(:previous_session) { Xip::Session.new(id: id, type: :previous) }
262
+ let(:back_to_session) { Xip::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
+ Xip.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
+ Xip.config.session_ttl = 500
287
+ session.session = nil # reset memoization
288
+ session.get_session
289
+ expect($redis.ttl(id)).to be > 100
290
+
291
+ Xip.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
+ Xip.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
+ Xip.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
+ Xip.config.session_ttl = 0
310
+ end
311
+ end
312
+
313
+ describe "clearing sessions" do
314
+ let(:session) { Xip::Session.new(id: id) }
315
+ let(:previous_session) { Xip::Session.new(id: id, type: :previous) }
316
+ let(:back_to_session) { Xip::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(Xip::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
+ Xip::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
+ Xip::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
+ Xip::Session.slugify(flow: nil, state: nil)
363
+ }.to raise_error(ArgumentError)
364
+ end
365
+ end
366
+ end