stealth 1.1.6 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +15 -54
  3. data/CHANGELOG.md +72 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +49 -44
  6. data/LICENSE +4 -17
  7. data/README.md +9 -17
  8. data/VERSION +1 -1
  9. data/lib/stealth/base.rb +62 -15
  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 +178 -40
  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 +3 -3
  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 -38
  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,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Stealth::Controller::Nlp 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 'nlp_client_klass' do
11
+ it 'should return the correct class for LUIS' do
12
+ config_dbl = double('Stealth Config', nlp_integration: :luis).as_null_object
13
+ allow(Stealth).to receive(:config).and_return(config_dbl)
14
+ expect(controller.send(:nlp_client_klass)).to eq Stealth::Nlp::Luis::Client
15
+ end
16
+
17
+ it 'should return the correct class for Dialogflow' do
18
+ config_dbl = double('Stealth Config', nlp_integration: :dialogflow).as_null_object
19
+ allow(Stealth).to receive(:config).and_return(config_dbl)
20
+ expect(controller.send(:nlp_client_klass)).to eq Stealth::Nlp::Dialogflow::Client
21
+ end
22
+
23
+ it 'should raise an error if it cannot locate a class' do
24
+ config_dbl = double('Stealth Config', nlp_integration: :unknown).as_null_object
25
+ allow(Stealth).to receive(:config).and_return(config_dbl)
26
+ expect {
27
+ controller.send(:nlp_client_klass)
28
+ }.to raise_error(NameError)
29
+ end
30
+ end
31
+
32
+ describe 'perform_nlp!' do
33
+
34
+ describe 'NLP has not yet been configured' do
35
+ it 'should raise Stealth::Errors::ConfigurationError' do
36
+ config_dbl = double('Stealth Config', nlp_integration: nil).as_null_object
37
+ allow(Stealth).to receive(:config).and_return(config_dbl)
38
+
39
+ expect {
40
+ controller.perform_nlp!
41
+ }.to raise_error(Stealth::Errors::ConfigurationError)
42
+ end
43
+ end
44
+
45
+ describe 'NLP has been configured' do
46
+ before(:each) do
47
+ config_dbl = double('Stealth Config', nlp_integration: :luis).as_null_object
48
+ @luis_client_dbl = double('LUIS Client')
49
+ allow(Stealth).to receive(:config).and_return(config_dbl)
50
+ allow(Stealth::Nlp::Luis::Client).to receive(:new).and_return(@luis_client_dbl)
51
+ end
52
+
53
+ let(:nlp_result) { Stealth::Nlp::Result.new(result: {}) }
54
+
55
+ it 'should call understand on the NLP client' do
56
+ expect(@luis_client_dbl).to receive(:understand).with(query: 'Hello World!').and_return(nlp_result)
57
+ controller.perform_nlp!
58
+ end
59
+
60
+ it 'should return an Nlp::Result object' do
61
+ expect(@luis_client_dbl).to receive(:understand).with(query: 'Hello World!').and_return(nlp_result)
62
+ expect(controller.perform_nlp!).to eq nlp_result
63
+ end
64
+
65
+ it 'should memoize the understand call' do
66
+ expect(@luis_client_dbl).to receive(:understand).once.with(query: 'Hello World!').and_return(nlp_result)
67
+ controller.perform_nlp!
68
+ controller.perform_nlp!
69
+ controller.perform_nlp!
70
+ end
71
+
72
+ it 'should store the nlp_result for the current controller' do
73
+ expect(@luis_client_dbl).to receive(:understand).once.with(query: 'Hello World!').and_return(nlp_result)
74
+ controller.perform_nlp!
75
+ expect(controller.nlp_result).to eq nlp_result
76
+ end
77
+
78
+ it 'should store the nlp_result for the current service_message' do
79
+ expect(@luis_client_dbl).to receive(:understand).once.with(query: 'Hello World!').and_return(nlp_result)
80
+ controller.perform_nlp!
81
+ expect(controller.current_message.nlp_result).to eq nlp_result
82
+ end
83
+
84
+ it 'should perform the NLP query if it has been cleared out' do
85
+ expect(@luis_client_dbl).to receive(:understand).exactly(2).times.with(query: 'Hello World!').and_return(nlp_result)
86
+ controller.perform_nlp!
87
+ controller.nlp_result = nil
88
+ controller.perform_nlp!
89
+ end
90
+ end
91
+ end
92
+
93
+ end
@@ -1,7 +1,6 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
3
+ require 'spec_helper'
5
4
 
6
5
  describe "Stealth::Controller replies" do
7
6
 
@@ -39,9 +38,43 @@ describe "Stealth::Controller replies" do
39
38
  send_replies
40
39
  end
41
40
 
41
+ def say_msgs_without_breaks
42
+ send_replies
43
+ end
44
+
42
45
  def say_uh_oh
43
46
  send_replies
44
47
  end
48
+
49
+ def say_randomize_text
50
+ send_replies
51
+ end
52
+
53
+ def say_randomize_speech
54
+ send_replies
55
+ end
56
+
57
+ def say_custom_reply
58
+ send_replies custom_reply: 'messages/say_offer'
59
+ end
60
+
61
+ def say_howdy_with_dynamic
62
+ send_replies
63
+ end
64
+
65
+ def say_nested_custom_reply
66
+ send_replies custom_reply: 'messages/sub1/sub2/say_nested'
67
+ end
68
+
69
+ def say_inline_reply
70
+ reply = [
71
+ { 'reply_type' => 'text', 'text' => 'Hi, Morty. Welcome to Stealth bot...' },
72
+ { 'reply_type' => 'delay', 'duration' => 2 },
73
+ { 'reply_type' => 'text', 'text' => 'We offer users an awesome Ruby framework for building chat bots.' }
74
+ ]
75
+
76
+ send_replies inline: reply
77
+ end
45
78
  end
46
79
 
47
80
  describe "missing reply" do
@@ -100,6 +133,11 @@ describe "Stealth::Controller replies" do
100
133
  allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
101
134
  allow(controller.current_session).to receive(:flow_string).and_return("message")
102
135
  allow(controller.current_session).to receive(:state_string).and_return("say_oi")
136
+ Stealth.config.auto_insert_delays = false
137
+ end
138
+
139
+ after(:each) do
140
+ Stealth.config.auto_insert_delays = true
103
141
  end
104
142
 
105
143
  it "should translate each reply_type in the reply" do
@@ -139,6 +177,11 @@ describe "Stealth::Controller replies" do
139
177
  allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
140
178
  allow(controller.current_session).to receive(:flow_string).and_return("message")
141
179
  allow(controller.current_session).to receive(:state_string).and_return("say_offer")
180
+ Stealth.config.auto_insert_delays = false
181
+ end
182
+
183
+ after(:each) do
184
+ Stealth.config.auto_insert_delays = true
142
185
  end
143
186
 
144
187
  it "should translate each reply_type in the reply" do
@@ -169,6 +212,243 @@ describe "Stealth::Controller replies" do
169
212
  end
170
213
  end
171
214
 
215
+ describe "custom_reply" do
216
+ let(:stubbed_handler) { double("handler") }
217
+ let(:stubbed_client) { double("client") }
218
+
219
+ before(:each) do
220
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
221
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
222
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
223
+ allow(controller.current_session).to receive(:state_string).and_return("say_custom_reply")
224
+ Stealth.config.auto_insert_delays = false
225
+ end
226
+
227
+ after(:each) do
228
+ Stealth.config.auto_insert_delays = true
229
+ end
230
+
231
+ it "should translate each reply_type in the reply" do
232
+ allow(stubbed_client).to receive(:transmit).and_return(true)
233
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
234
+
235
+ expect(stubbed_handler).to receive(:text).exactly(2).times
236
+ expect(stubbed_handler).to receive(:delay).exactly(1).times
237
+ controller.say_custom_reply
238
+ end
239
+
240
+ it "should transmit each reply_type in the reply" do
241
+ allow(stubbed_handler).to receive(:text).exactly(2).times
242
+ allow(stubbed_handler).to receive(:delay).exactly(1).times
243
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
244
+
245
+ expect(stubbed_client).to receive(:transmit).exactly(3).times
246
+ controller.say_custom_reply
247
+ end
248
+
249
+ it "should sleep on delays" do
250
+ allow(stubbed_handler).to receive(:text).exactly(2).times
251
+ allow(stubbed_handler).to receive(:delay).exactly(1).times
252
+ allow(stubbed_client).to receive(:transmit).exactly(3).times
253
+
254
+ expect(controller).to receive(:sleep).exactly(1).times.with(2.0)
255
+ controller.say_custom_reply
256
+ end
257
+
258
+ it "should correctly load from sub-dirs" do
259
+ expect(stubbed_handler).to receive(:text).exactly(3).times
260
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
261
+ expect(stubbed_client).to receive(:transmit).exactly(5).times
262
+
263
+ expect(controller).to receive(:sleep).exactly(2).times.with(2.0)
264
+ controller.say_nested_custom_reply
265
+ end
266
+ end
267
+
268
+ describe "inline replies" do
269
+ let(:stubbed_handler) { double("handler") }
270
+ let(:stubbed_client) { double("client") }
271
+
272
+ before(:each) do
273
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
274
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
275
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
276
+ allow(controller.current_session).to receive(:state_string).and_return("say_inline_reply")
277
+ Stealth.config.auto_insert_delays = false
278
+ end
279
+
280
+ after(:each) do
281
+ Stealth.config.auto_insert_delays = true
282
+ end
283
+
284
+ it "should translate each reply_type in the reply" do
285
+ allow(stubbed_client).to receive(:transmit).and_return(true)
286
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
287
+
288
+ expect(stubbed_handler).to receive(:text).exactly(2).times
289
+ expect(stubbed_handler).to receive(:delay).exactly(1).times
290
+ controller.say_inline_reply
291
+ end
292
+
293
+ it "should transmit each reply_type in the reply" do
294
+ allow(stubbed_handler).to receive(:text).exactly(2).times
295
+ allow(stubbed_handler).to receive(:delay).exactly(1).times
296
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
297
+
298
+ expect(stubbed_client).to receive(:transmit).exactly(3).times
299
+ controller.say_inline_reply
300
+ end
301
+
302
+ it "should sleep on delays" do
303
+ allow(stubbed_handler).to receive(:text).exactly(2).times
304
+ allow(stubbed_handler).to receive(:delay).exactly(1).times
305
+ allow(stubbed_client).to receive(:transmit).exactly(3).times
306
+
307
+ expect(controller).to receive(:sleep).exactly(1).times.with(2.0)
308
+ controller.say_inline_reply
309
+ end
310
+ end
311
+
312
+ describe "auto delays" do
313
+ let(:stubbed_handler) { double("handler") }
314
+ let(:stubbed_client) { double("client") }
315
+
316
+ before(:each) do
317
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
318
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
319
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
320
+ allow(controller.current_session).to receive(:state_string).and_return("say_msgs_without_breaks")
321
+ end
322
+
323
+ it "should add two additional delays to a reply without delays" do
324
+ allow(stubbed_client).to receive(:transmit).and_return(true)
325
+ allow(controller).to receive(:sleep).and_return(true)
326
+
327
+ expect(stubbed_handler).to receive(:text).exactly(2).times
328
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
329
+ controller.say_msgs_without_breaks
330
+ end
331
+
332
+ it "should only add a single delay to a reply that already contains a delay" do
333
+ allow(stubbed_client).to receive(:transmit).and_return(true)
334
+ allow(controller).to receive(:sleep).and_return(true)
335
+
336
+ expect(stubbed_handler).to receive(:text).exactly(2).times
337
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
338
+ controller.say_offer
339
+ end
340
+
341
+ it "should not add delays if auto_insert_delays = false" do
342
+ allow(stubbed_client).to receive(:transmit).and_return(true)
343
+ allow(controller).to receive(:sleep).and_return(true)
344
+
345
+ expect(stubbed_handler).to receive(:text).exactly(2).times
346
+ expect(stubbed_handler).to_not receive(:delay)
347
+
348
+ Stealth.config.auto_insert_delays = false
349
+ controller.say_msgs_without_breaks
350
+ Stealth.config.auto_insert_delays = true
351
+ end
352
+ end
353
+
354
+ describe "session locking" do
355
+ let(:stubbed_handler) { double("handler") }
356
+ let(:stubbed_client) { double("client") }
357
+
358
+ before(:each) do
359
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
360
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
361
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
362
+ allow(controller.current_session).to receive(:state_string).and_return("say_offer")
363
+ Stealth.config.auto_insert_delays = false
364
+ end
365
+
366
+ after(:each) do
367
+ Stealth.config.auto_insert_delays = true
368
+ end
369
+
370
+ it "should update the lock for each reply_type in the reply" do
371
+ allow(stubbed_client).to receive(:transmit).and_return(true)
372
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
373
+
374
+ expect(controller).to receive(:lock_session!).exactly(3).times
375
+ expect(stubbed_handler).to receive(:text).exactly(2).times
376
+ expect(stubbed_handler).to receive(:delay).exactly(1).times
377
+ controller.say_offer
378
+ end
379
+
380
+ it "should update the lock position for each reply_type in the reply" do
381
+ allow(stubbed_client).to receive(:transmit).and_return(true)
382
+ allow(controller).to receive(:sleep).and_return(true).with(2.0)
383
+
384
+ expect(controller).to receive(
385
+ :lock_session!
386
+ ).with(
387
+ session_slug: controller.current_session.get_session,
388
+ position: 0
389
+ ).exactly(1).times
390
+
391
+ expect(controller).to receive(
392
+ :lock_session!
393
+ ).with(
394
+ session_slug: controller.current_session.get_session,
395
+ position: 1
396
+ ).exactly(1).times
397
+
398
+ expect(controller).to receive(
399
+ :lock_session!
400
+ ).with(
401
+ session_slug: controller.current_session.get_session,
402
+ position: 2
403
+ ).exactly(1).times
404
+
405
+ expect(stubbed_handler).to receive(:text).exactly(2).times
406
+ expect(stubbed_handler).to receive(:delay).exactly(1).times
407
+ controller.say_offer
408
+ end
409
+
410
+ it "should update the lock position with an offset for each reply_type in the reply" do
411
+ allow(stubbed_client).to receive(:transmit).and_return(true)
412
+ allow(controller).to receive(:sleep).and_return(true)
413
+
414
+ controller.pos = 17 # set the offset
415
+
416
+ expect(controller).to receive(
417
+ :lock_session!
418
+ ).with(
419
+ session_slug: controller.current_session.get_session,
420
+ position: 17
421
+ ).exactly(1).times
422
+
423
+ expect(controller).to receive(
424
+ :lock_session!
425
+ ).with(
426
+ session_slug: controller.current_session.get_session,
427
+ position: 18
428
+ ).exactly(1).times
429
+
430
+ expect(controller).to receive(
431
+ :lock_session!
432
+ ).with(
433
+ session_slug: controller.current_session.get_session,
434
+ position: 19
435
+ ).exactly(1).times
436
+
437
+ expect(controller).to receive(
438
+ :lock_session!
439
+ ).with(
440
+ session_slug: controller.current_session.get_session,
441
+ position: 20
442
+ ).exactly(1).times
443
+
444
+ expect(stubbed_handler).to receive(:cards).exactly(1).time
445
+ expect(stubbed_handler).to receive(:list).exactly(1).time
446
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
447
+ allow(controller.current_session).to receive(:state_string).and_return("say_howdy_with_dynamic")
448
+ controller.say_howdy_with_dynamic
449
+ end
450
+ end
451
+
172
452
  describe "dynamic delays" do
173
453
  let(:stubbed_handler) { double("handler") }
174
454
  let(:stubbed_client) { double("client") }
@@ -182,33 +462,33 @@ describe "Stealth::Controller replies" do
182
462
 
183
463
  it "should use the default multiplier if none is set" do
184
464
  allow(stubbed_handler).to receive(:text).exactly(2).times
185
- allow(stubbed_handler).to receive(:delay).exactly(1).times
186
- allow(stubbed_client).to receive(:transmit).exactly(3).times
465
+ allow(stubbed_handler).to receive(:delay).exactly(2).times
466
+ allow(stubbed_client).to receive(:transmit).exactly(4).times
187
467
 
188
468
  delay = Stealth.config.dynamic_delay_muliplier * Stealth::Controller::DynamicDelay::SHORT_DELAY
189
- expect(controller).to receive(:sleep).exactly(1).times.with(delay)
469
+ expect(controller).to receive(:sleep).exactly(2).times.with(delay)
190
470
  controller.say_offer_with_dynamic
191
471
  end
192
472
 
193
473
  it "should slow down SHORT_DELAY if dynamic_delay_muliplier > 1" do
194
474
  allow(stubbed_handler).to receive(:text).exactly(2).times
195
- allow(stubbed_handler).to receive(:delay).exactly(1).times
196
- allow(stubbed_client).to receive(:transmit).exactly(3).times
475
+ allow(stubbed_handler).to receive(:delay).exactly(2).times
476
+ allow(stubbed_client).to receive(:transmit).exactly(4).times
197
477
 
198
478
  Stealth.config.dynamic_delay_muliplier = 5
199
479
  delay = Stealth.config.dynamic_delay_muliplier * Stealth::Controller::DynamicDelay::SHORT_DELAY
200
- expect(controller).to receive(:sleep).exactly(1).times.with(delay)
480
+ expect(controller).to receive(:sleep).exactly(2).times.with(delay)
201
481
  controller.say_offer_with_dynamic
202
482
  end
203
483
 
204
484
  it "should speed up SHORT_DELAY if dynamic_delay_muliplier < 1" do
205
485
  allow(stubbed_handler).to receive(:text).exactly(2).times
206
- allow(stubbed_handler).to receive(:delay).exactly(1).times
207
- allow(stubbed_client).to receive(:transmit).exactly(3).times
486
+ allow(stubbed_handler).to receive(:delay).exactly(2).times
487
+ allow(stubbed_client).to receive(:transmit).exactly(4).times
208
488
 
209
489
  Stealth.config.dynamic_delay_muliplier = 0.1
210
490
  delay = Stealth.config.dynamic_delay_muliplier * Stealth::Controller::DynamicDelay::SHORT_DELAY
211
- expect(controller).to receive(:sleep).exactly(1).times.with(delay)
491
+ expect(controller).to receive(:sleep).exactly(2).times.with(delay)
212
492
  controller.say_offer_with_dynamic
213
493
  end
214
494
  end
@@ -256,4 +536,159 @@ describe "Stealth::Controller replies" do
256
536
  end
257
537
  end
258
538
 
539
+ describe "randomized replies" do
540
+ let(:stubbed_handler) { double("handler") }
541
+ let(:stubbed_client) { double("client") }
542
+
543
+ before(:each) do
544
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
545
+ end
546
+
547
+ describe "text replies" do
548
+ before(:each) do
549
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
550
+ allow(controller.current_session).to receive(:state_string).and_return("say_randomize_text")
551
+ Stealth.config.auto_insert_delays = false
552
+ end
553
+
554
+ after(:each) do
555
+ Stealth.config.auto_insert_delays = true
556
+ end
557
+
558
+ it "should receive a single text string" do
559
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new) do |*args|
560
+ expect(args.first[:reply]['text']).to be_a(String)
561
+ stubbed_handler
562
+ end
563
+ allow(stubbed_handler).to receive(:text).exactly(1).time
564
+ expect(stubbed_client).to receive(:transmit).exactly(1).time
565
+ controller.say_randomize_text
566
+ end
567
+ end
568
+ end
569
+
570
+ describe "sub-state replies" do
571
+ let(:stubbed_handler) { double("handler") }
572
+ let(:stubbed_client) { double("client") }
573
+
574
+ before(:each) do
575
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
576
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
577
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
578
+ allow(controller.current_session).to receive(:state_string).and_return("say_offer")
579
+ allow(stubbed_client).to receive(:transmit).and_return(true)
580
+ allow(controller).to receive(:sleep).and_return(true)
581
+ end
582
+
583
+ it "should transmit only the last reply in the file when @pos = -1" do
584
+ expect(stubbed_handler).to receive(:text).exactly(1).time
585
+ expect(stubbed_handler).to receive(:delay).exactly(1).time # auto-delay
586
+ controller.pos = -1
587
+ controller.say_offer
588
+ end
589
+
590
+ it "should transmit the last two replies in the file when @pos = -2" do
591
+ expect(stubbed_handler).to receive(:text).exactly(1).time
592
+ expect(stubbed_handler).to receive(:delay).exactly(1).time
593
+ controller.pos = -2
594
+ controller.say_offer
595
+ end
596
+
597
+ it "should transmit all the replies in the file when @pos = 0" do
598
+ expect(stubbed_handler).to receive(:text).exactly(2).times
599
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
600
+ controller.pos = 0
601
+ controller.say_offer
602
+ end
603
+
604
+ it "should transmit all the replies in the file when @pos = nil" do
605
+ expect(stubbed_handler).to receive(:text).exactly(2).times
606
+ expect(stubbed_handler).to receive(:delay).exactly(2).times
607
+ expect(controller.pos).to be_nil
608
+ controller.say_offer
609
+ end
610
+ end
611
+
612
+ describe "client errors" do
613
+ let(:stubbed_handler) { double("handler") }
614
+ let(:stubbed_client) { double("client") }
615
+
616
+ before(:each) do
617
+ allow(Stealth::Services::Facebook::ReplyHandler).to receive(:new).and_return(stubbed_handler)
618
+ allow(Stealth::Services::Facebook::Client).to receive(:new).and_return(stubbed_client)
619
+ allow(controller.current_session).to receive(:flow_string).and_return("message")
620
+ allow(controller.current_session).to receive(:state_string).and_return("say_offer")
621
+ allow(stubbed_handler).to receive(:delay).exactly(1).time
622
+ allow(stubbed_handler).to receive(:text).exactly(1).time
623
+ end
624
+
625
+ describe "Stealth::Errors::UserOptOut" do
626
+ before(:each) do
627
+ expect(stubbed_client).to receive(:transmit).and_raise(
628
+ Stealth::Errors::UserOptOut.new('boom')
629
+ ).once # Retuns early; doesn't send the remaining replies in the file
630
+ end
631
+
632
+ it "should log the unhandled exception if the controller does not have a handle_opt_out method" do
633
+ expect(Stealth::Logger).to receive(:l).with(
634
+ topic: :err,
635
+ message: "User #{facebook_message.sender_id} unhandled exception due to opt-out."
636
+ )
637
+ expect(controller).to receive(:do_nothing)
638
+ controller.say_offer
639
+ end
640
+
641
+ it "should call handle_opt_out method" do
642
+ expect(controller).to receive(:handle_opt_out)
643
+ expect(Stealth::Logger).to receive(:l).with(
644
+ topic: 'facebook',
645
+ message: "User #{facebook_message.sender_id} opted out. [boom]"
646
+ )
647
+ expect(controller).to receive(:do_nothing)
648
+ controller.say_offer
649
+ end
650
+ end
651
+
652
+ describe "Stealth::Errors::InvalidSessionID" do
653
+ before(:each) do
654
+ expect(stubbed_client).to receive(:transmit).and_raise(
655
+ Stealth::Errors::InvalidSessionID.new('boom')
656
+ ).once # Retuns early; doesn't send the remaining replies in the file
657
+ end
658
+
659
+ it "should log the unhandled exception if the controller does not have a handle_invalid_session_id method" do
660
+ expect(Stealth::Logger).to receive(:l).with(
661
+ topic: :err,
662
+ message: "User #{facebook_message.sender_id} unhandled exception due to invalid session_id."
663
+ )
664
+ expect(controller).to receive(:do_nothing)
665
+ controller.say_offer
666
+ end
667
+
668
+ it "should call handle_invalid_session_id method" do
669
+ expect(controller).to receive(:handle_invalid_session_id)
670
+ expect(Stealth::Logger).to receive(:l).with(
671
+ topic: 'facebook',
672
+ message: "User #{facebook_message.sender_id} has an invalid session_id. [boom]"
673
+ )
674
+ expect(controller).to receive(:do_nothing)
675
+ controller.say_offer
676
+ end
677
+ end
678
+
679
+ describe 'an unknown client error' do
680
+ before(:each) do
681
+ allow(stubbed_client).to receive(:transmit).and_raise(
682
+ StandardError
683
+ )
684
+ end
685
+
686
+ it 'should raise the error' do
687
+ expect {
688
+ controller.say_offer
689
+ }.to raise_error(StandardError)
690
+ end
691
+ end
692
+ end
693
+
259
694
  end