stealth 1.1.2 → 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +18 -8
  3. data/CHANGELOG.md +100 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +49 -43
  6. data/LICENSE +4 -17
  7. data/README.md +9 -17
  8. data/VERSION +1 -1
  9. data/lib/stealth/base.rb +62 -13
  10. data/lib/stealth/cli.rb +1 -2
  11. data/lib/stealth/commands/console.rb +1 -1
  12. data/lib/stealth/configuration.rb +0 -3
  13. data/lib/stealth/controller/callbacks.rb +1 -1
  14. data/lib/stealth/controller/catch_all.rb +27 -4
  15. data/lib/stealth/controller/controller.rb +168 -49
  16. data/lib/stealth/controller/dev_jumps.rb +41 -0
  17. data/lib/stealth/controller/dynamic_delay.rb +4 -6
  18. data/lib/stealth/controller/interrupt_detect.rb +100 -0
  19. data/lib/stealth/controller/messages.rb +283 -0
  20. data/lib/stealth/controller/nlp.rb +50 -0
  21. data/lib/stealth/controller/replies.rb +179 -41
  22. data/lib/stealth/controller/unrecognized_message.rb +62 -0
  23. data/lib/stealth/core_ext.rb +5 -0
  24. data/lib/stealth/{flow/core_ext.rb → core_ext/numeric.rb} +0 -1
  25. data/lib/stealth/core_ext/string.rb +18 -0
  26. data/lib/stealth/dispatcher.rb +21 -0
  27. data/lib/stealth/errors.rb +12 -0
  28. data/lib/stealth/flow/base.rb +1 -2
  29. data/lib/stealth/flow/specification.rb +3 -2
  30. data/lib/stealth/flow/state.rb +3 -3
  31. data/lib/stealth/generators/builder/Gemfile +4 -3
  32. data/lib/stealth/generators/builder/bot/controllers/bot_controller.rb +42 -0
  33. data/lib/stealth/generators/builder/bot/controllers/catch_alls_controller.rb +2 -0
  34. data/lib/stealth/generators/builder/bot/controllers/goodbyes_controller.rb +2 -0
  35. data/lib/stealth/generators/builder/bot/controllers/hellos_controller.rb +2 -0
  36. data/lib/stealth/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  37. data/lib/stealth/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  38. data/lib/stealth/generators/builder/config/flow_map.rb +8 -0
  39. data/lib/stealth/generators/builder/config/initializers/autoload.rb +8 -0
  40. data/lib/stealth/generators/builder/config/initializers/inflections.rb +16 -0
  41. data/lib/stealth/generators/builder/config/puma.rb +15 -0
  42. data/lib/stealth/helpers/redis.rb +40 -0
  43. data/lib/stealth/lock.rb +83 -0
  44. data/lib/stealth/logger.rb +27 -18
  45. data/lib/stealth/nlp/client.rb +22 -0
  46. data/lib/stealth/nlp/result.rb +57 -0
  47. data/lib/stealth/reloader.rb +90 -0
  48. data/lib/stealth/reply.rb +17 -0
  49. data/lib/stealth/scheduled_reply.rb +3 -3
  50. data/lib/stealth/server.rb +4 -4
  51. data/lib/stealth/service_message.rb +3 -2
  52. data/lib/stealth/service_reply.rb +5 -1
  53. data/lib/stealth/services/base_reply_handler.rb +2 -2
  54. data/lib/stealth/session.rb +106 -53
  55. data/spec/configuration_spec.rb +9 -2
  56. data/spec/controller/callbacks_spec.rb +23 -28
  57. data/spec/controller/catch_all_spec.rb +81 -29
  58. data/spec/controller/controller_spec.rb +444 -43
  59. data/spec/controller/dynamic_delay_spec.rb +16 -18
  60. data/spec/controller/helpers_spec.rb +1 -2
  61. data/spec/controller/interrupt_detect_spec.rb +171 -0
  62. data/spec/controller/messages_spec.rb +744 -0
  63. data/spec/controller/nlp_spec.rb +93 -0
  64. data/spec/controller/replies_spec.rb +446 -11
  65. data/spec/controller/unrecognized_message_spec.rb +168 -0
  66. data/spec/dispatcher_spec.rb +79 -0
  67. data/spec/flow/flow_spec.rb +1 -2
  68. data/spec/flow/state_spec.rb +14 -3
  69. data/spec/helpers/redis_spec.rb +77 -0
  70. data/spec/lock_spec.rb +100 -0
  71. data/spec/nlp/client_spec.rb +23 -0
  72. data/spec/nlp/result_spec.rb +57 -0
  73. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  74. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  75. data/spec/replies/messages/say_randomize_text.yml +10 -0
  76. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  77. data/spec/reply_spec.rb +61 -0
  78. data/spec/scheduled_reply_spec.rb +23 -0
  79. data/spec/service_reply_spec.rb +1 -2
  80. data/spec/session_spec.rb +251 -12
  81. data/spec/spec_helper.rb +21 -0
  82. data/spec/support/controllers/vaders_controller.rb +24 -0
  83. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  84. data/spec/support/nlp_clients/luis.rb +9 -0
  85. data/spec/support/nlp_results/luis_result.rb +163 -0
  86. data/spec/version_spec.rb +1 -2
  87. data/stealth.gemspec +6 -6
  88. metadata +83 -39
  89. data/docs/00-introduction.md +0 -37
  90. data/docs/01-getting-started.md +0 -21
  91. data/docs/02-local-development.md +0 -40
  92. data/docs/03-basics.md +0 -171
  93. data/docs/04-sessions.md +0 -29
  94. data/docs/05-controllers.md +0 -179
  95. data/docs/06-models.md +0 -39
  96. data/docs/07-replies.md +0 -114
  97. data/docs/08-catchalls.md +0 -49
  98. data/docs/09-messaging-integrations.md +0 -80
  99. data/docs/10-nlp-integrations.md +0 -13
  100. data/docs/11-analytics.md +0 -13
  101. data/docs/12-commands.md +0 -62
  102. data/docs/13-deployment.md +0 -50
  103. data/lib/stealth/generators/builder/config/initializers/.keep +0 -0
@@ -0,0 +1,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