adhearsion 2.4.0.beta1 → 2.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -3
- data/README.markdown +1 -1
- data/lib/adhearsion/call.rb +33 -8
- data/lib/adhearsion/call_controller.rb +9 -0
- data/lib/adhearsion/call_controller/dial.rb +145 -32
- data/lib/adhearsion/console.rb +1 -1
- data/lib/adhearsion/generators/app/templates/README.md +20 -1
- data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +4 -2
- data/lib/adhearsion/initializer.rb +2 -0
- data/lib/adhearsion/process.rb +2 -0
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +808 -6
- data/spec/adhearsion/call_controller_spec.rb +7 -0
- data/spec/adhearsion/process_spec.rb +14 -0
- metadata +2 -2
@@ -22,7 +22,7 @@ Adhearsion.config do |config|
|
|
22
22
|
end
|
23
23
|
|
24
24
|
##
|
25
|
-
# Use with Rayo (eg Voxeo PRISM)
|
25
|
+
# Use with Rayo (eg Voxeo PRISM or FreeSWITCH mod_rayo)
|
26
26
|
#
|
27
27
|
# config.punchblock.username = "" # Your XMPP JID for use with Rayo
|
28
28
|
# config.punchblock.password = "" # Your XMPP password
|
@@ -36,7 +36,9 @@ Adhearsion.config do |config|
|
|
36
36
|
# config.punchblock.host = "127.0.0.1" # Your AMI host
|
37
37
|
|
38
38
|
##
|
39
|
-
# Use with FreeSWITCH
|
39
|
+
# Use with FreeSWITCH via EventSocket
|
40
|
+
#
|
41
|
+
# This configuration is no longer recommended and mod_rayo is preferred
|
40
42
|
#
|
41
43
|
# config.punchblock.platform = :freeswitch # Use FreeSWITCH
|
42
44
|
# config.punchblock.password = "" # Your Inbound EventSocket password
|
@@ -180,6 +180,8 @@ module Adhearsion
|
|
180
180
|
lib_folder = [Adhearsion.config.platform.root, Adhearsion.config.platform.lib].join '/'
|
181
181
|
return false unless File.directory? lib_folder
|
182
182
|
|
183
|
+
$LOAD_PATH.unshift lib_folder
|
184
|
+
|
183
185
|
Dir.chdir lib_folder do
|
184
186
|
rbfiles = File.join "**", "*.rb"
|
185
187
|
Dir.glob(rbfiles).each do |file|
|
data/lib/adhearsion/process.rb
CHANGED
data/lib/adhearsion/version.rb
CHANGED
@@ -108,7 +108,7 @@ module Adhearsion
|
|
108
108
|
latch.wait(1).should be_true
|
109
109
|
end
|
110
110
|
|
111
|
-
it "hangs up the new call when the
|
111
|
+
it "hangs up the new call when the root call ends" do
|
112
112
|
call.should_receive(:answer).once
|
113
113
|
other_mock_call.should_receive(:join).once.with(call)
|
114
114
|
other_mock_call.should_receive(:hangup).once
|
@@ -176,7 +176,6 @@ module Adhearsion
|
|
176
176
|
t.join
|
177
177
|
status = t.value
|
178
178
|
status.result.should be == :answer
|
179
|
-
status.joined_call.should eq(other_mock_call)
|
180
179
|
|
181
180
|
joined_status = status.joins[status.calls.first]
|
182
181
|
joined_status.result.should == :joined
|
@@ -206,11 +205,393 @@ module Adhearsion
|
|
206
205
|
t.join
|
207
206
|
status = t.value
|
208
207
|
status.result.should be == :answer
|
209
|
-
status.joined_call.should eq(other_mock_call)
|
210
208
|
joined_status = status.joins[status.calls.first]
|
211
209
|
joined_status.duration.should == 37.0
|
212
210
|
end
|
213
211
|
end
|
212
|
+
|
213
|
+
context "when a dial is split" do
|
214
|
+
before do
|
215
|
+
call.should_receive(:answer).once
|
216
|
+
other_mock_call.should_receive(:join).once.with(call)
|
217
|
+
call.stub(:unjoin).and_return do
|
218
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
219
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should unjoin the calls" do
|
224
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
225
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
226
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
227
|
+
end
|
228
|
+
|
229
|
+
dial = Dial::Dial.new to, options, call
|
230
|
+
dial.run
|
231
|
+
|
232
|
+
waiter_thread = Thread.new do
|
233
|
+
dial.await_completion
|
234
|
+
latch.countdown!
|
235
|
+
end
|
236
|
+
|
237
|
+
sleep 0.5
|
238
|
+
|
239
|
+
other_mock_call << mock_answered
|
240
|
+
|
241
|
+
dial.split
|
242
|
+
other_mock_call << mock_end
|
243
|
+
|
244
|
+
latch.wait(1).should be_true
|
245
|
+
|
246
|
+
waiter_thread.join
|
247
|
+
dial.status.result.should be == :answer
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should not unblock immediately" do
|
251
|
+
dial = Dial::Dial.new to, options, call
|
252
|
+
dial.run
|
253
|
+
|
254
|
+
waiter_thread = Thread.new do
|
255
|
+
dial.await_completion
|
256
|
+
latch.countdown!
|
257
|
+
end
|
258
|
+
|
259
|
+
sleep 0.5
|
260
|
+
|
261
|
+
other_mock_call << mock_answered
|
262
|
+
|
263
|
+
dial.split
|
264
|
+
|
265
|
+
latch.wait(1).should be_false
|
266
|
+
|
267
|
+
other_mock_call << mock_end
|
268
|
+
|
269
|
+
latch.wait(1).should be_true
|
270
|
+
|
271
|
+
waiter_thread.join
|
272
|
+
dial.status.result.should be == :answer
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should set end time" do
|
276
|
+
dial = Dial::Dial.new to, options, call
|
277
|
+
dial.run
|
278
|
+
|
279
|
+
waiter_thread = Thread.new do
|
280
|
+
dial.await_completion
|
281
|
+
latch.countdown!
|
282
|
+
end
|
283
|
+
|
284
|
+
sleep 0.5
|
285
|
+
|
286
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
287
|
+
Timecop.freeze base_time
|
288
|
+
|
289
|
+
other_mock_call << mock_answered
|
290
|
+
|
291
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
292
|
+
Timecop.freeze base_time
|
293
|
+
dial.split
|
294
|
+
|
295
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 54)
|
296
|
+
Timecop.freeze base_time
|
297
|
+
other_mock_call << mock_end
|
298
|
+
|
299
|
+
latch.wait(1).should be_true
|
300
|
+
|
301
|
+
waiter_thread.join
|
302
|
+
status = dial.status
|
303
|
+
status.result.should be == :answer
|
304
|
+
joined_status = status.joins[status.calls.first]
|
305
|
+
joined_status.duration.should == 37.0
|
306
|
+
end
|
307
|
+
|
308
|
+
context "with new controllers specified" do
|
309
|
+
let(:split_latch) { CountDownLatch.new 2 }
|
310
|
+
|
311
|
+
let(:split_controller) do
|
312
|
+
latch = split_latch
|
313
|
+
Class.new(Adhearsion::CallController) do
|
314
|
+
@@split_latch = latch
|
315
|
+
|
316
|
+
def run
|
317
|
+
call['hit_split_controller'] = self.class
|
318
|
+
call['split_controller_metadata'] = metadata
|
319
|
+
@@split_latch.countdown!
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
let(:main_split_controller) { Class.new(split_controller) }
|
325
|
+
let(:others_split_controller) { Class.new(split_controller) }
|
326
|
+
|
327
|
+
it "should execute the :main controller on the originating call and :others on the outbound calls" do
|
328
|
+
dial = Dial::Dial.new to, options, call
|
329
|
+
dial.run
|
330
|
+
|
331
|
+
waiter_thread = Thread.new do
|
332
|
+
dial.await_completion
|
333
|
+
latch.countdown!
|
334
|
+
end
|
335
|
+
|
336
|
+
sleep 0.5
|
337
|
+
|
338
|
+
other_mock_call << mock_answered
|
339
|
+
|
340
|
+
should_receive(:callback).once.with(call)
|
341
|
+
should_receive(:callback).once.with(other_mock_call)
|
342
|
+
|
343
|
+
dial.split main: main_split_controller, others: others_split_controller, main_callback: ->(call) { self.callback(call) }, others_callback: ->(call) { self.callback(call) }
|
344
|
+
|
345
|
+
latch.wait(1).should be_false
|
346
|
+
split_latch.wait(1).should be_true
|
347
|
+
|
348
|
+
call['hit_split_controller'].should == main_split_controller
|
349
|
+
call['split_controller_metadata']['current_dial'].should be dial
|
350
|
+
|
351
|
+
other_mock_call['hit_split_controller'].should == others_split_controller
|
352
|
+
other_mock_call['split_controller_metadata']['current_dial'].should be dial
|
353
|
+
|
354
|
+
other_mock_call << mock_end
|
355
|
+
|
356
|
+
latch.wait(1).should be_true
|
357
|
+
|
358
|
+
waiter_thread.join
|
359
|
+
dial.status.result.should be == :answer
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "when rejoining" do
|
364
|
+
it "should rejoin the calls" do
|
365
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
366
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
367
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
368
|
+
end
|
369
|
+
|
370
|
+
dial = Dial::Dial.new to, options, call
|
371
|
+
dial.run
|
372
|
+
|
373
|
+
waiter_thread = Thread.new do
|
374
|
+
dial.await_completion
|
375
|
+
latch.countdown!
|
376
|
+
end
|
377
|
+
|
378
|
+
sleep 0.5
|
379
|
+
|
380
|
+
other_mock_call << mock_answered
|
381
|
+
|
382
|
+
dial.split
|
383
|
+
|
384
|
+
other_mock_call.should_receive(:join).once.ordered.with(call)
|
385
|
+
dial.rejoin
|
386
|
+
|
387
|
+
other_mock_call << mock_end
|
388
|
+
|
389
|
+
latch.wait(1).should be_true
|
390
|
+
|
391
|
+
waiter_thread.join
|
392
|
+
dial.status.result.should be == :answer
|
393
|
+
end
|
394
|
+
|
395
|
+
context "to a specified mixer" do
|
396
|
+
let(:mixer) { SecureRandom.uuid }
|
397
|
+
|
398
|
+
it "should join all calls to the mixer" do
|
399
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
400
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
401
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
402
|
+
end
|
403
|
+
|
404
|
+
dial = Dial::Dial.new to, options, call
|
405
|
+
dial.run
|
406
|
+
|
407
|
+
waiter_thread = Thread.new do
|
408
|
+
dial.await_completion
|
409
|
+
latch.countdown!
|
410
|
+
end
|
411
|
+
|
412
|
+
sleep 0.5
|
413
|
+
|
414
|
+
other_mock_call << mock_answered
|
415
|
+
|
416
|
+
dial.split
|
417
|
+
|
418
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
419
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
420
|
+
dial.rejoin mixer_name: mixer
|
421
|
+
|
422
|
+
other_mock_call << mock_end
|
423
|
+
|
424
|
+
latch.wait(1).should be_true
|
425
|
+
|
426
|
+
waiter_thread.join
|
427
|
+
dial.status.result.should be == :answer
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context "when another dial is merged in" do
|
433
|
+
let(:second_root_call_id) { new_uuid }
|
434
|
+
let(:second_root_call) { Adhearsion::Call.new }
|
435
|
+
let(:mixer) { SecureRandom.uuid }
|
436
|
+
|
437
|
+
let(:dial) { Dial::Dial.new to, options, call }
|
438
|
+
let(:other_dial) { Dial::Dial.new second_to, options, second_root_call }
|
439
|
+
|
440
|
+
before do
|
441
|
+
second_root_call.stub write_command: true, id: second_root_call_id
|
442
|
+
OutboundCall.should_receive(:new).and_return second_other_mock_call
|
443
|
+
second_other_mock_call.should_receive(:join).once.with(second_root_call)
|
444
|
+
second_other_mock_call.should_receive(:dial).once.with(second_to, options)
|
445
|
+
second_root_call.should_receive(:answer).once
|
446
|
+
|
447
|
+
SecureRandom.stub uuid: mixer
|
448
|
+
|
449
|
+
dial.run
|
450
|
+
other_dial.run
|
451
|
+
|
452
|
+
other_mock_call << mock_answered
|
453
|
+
second_other_mock_call << mock_answered
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should split calls, rejoin to a mixer, and rejoin other calls to mixer" do
|
457
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
458
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
459
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
460
|
+
end
|
461
|
+
second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_return do
|
462
|
+
second_root_call << Punchblock::Event::Unjoined.new(call_uri: second_other_mock_call.id)
|
463
|
+
second_other_mock_call << Punchblock::Event::Unjoined.new(call_uri: second_root_call.id)
|
464
|
+
end
|
465
|
+
|
466
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
467
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
468
|
+
|
469
|
+
second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
470
|
+
second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
471
|
+
|
472
|
+
dial.merge other_dial
|
473
|
+
|
474
|
+
waiter_thread = Thread.new do
|
475
|
+
dial.await_completion
|
476
|
+
latch.countdown!
|
477
|
+
end
|
478
|
+
|
479
|
+
sleep 0.5
|
480
|
+
|
481
|
+
other_mock_call << mock_end
|
482
|
+
second_root_call << mock_end
|
483
|
+
second_other_mock_call << mock_end
|
484
|
+
|
485
|
+
latch.wait(1).should be_true
|
486
|
+
|
487
|
+
waiter_thread.join
|
488
|
+
dial.status.result.should be == :answer
|
489
|
+
end
|
490
|
+
|
491
|
+
it "should add the merged calls to the returned status" do
|
492
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
|
493
|
+
dial.merge other_dial
|
494
|
+
|
495
|
+
waiter_thread = Thread.new do
|
496
|
+
dial.await_completion
|
497
|
+
latch.countdown!
|
498
|
+
end
|
499
|
+
|
500
|
+
sleep 0.5
|
501
|
+
|
502
|
+
other_mock_call << mock_end
|
503
|
+
second_root_call << mock_end
|
504
|
+
second_other_mock_call << mock_end
|
505
|
+
|
506
|
+
latch.wait(1).should be_true
|
507
|
+
|
508
|
+
waiter_thread.join
|
509
|
+
dial.status.result.should be == :answer
|
510
|
+
dial.status.calls.should include(second_root_call, second_other_mock_call)
|
511
|
+
end
|
512
|
+
|
513
|
+
it "should not unblock until all joined calls end" do
|
514
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
|
515
|
+
|
516
|
+
dial.merge other_dial
|
517
|
+
|
518
|
+
waiter_thread = Thread.new do
|
519
|
+
dial.await_completion
|
520
|
+
latch.countdown!
|
521
|
+
end
|
522
|
+
|
523
|
+
sleep 0.5
|
524
|
+
|
525
|
+
other_mock_call << mock_end
|
526
|
+
latch.wait(1).should be_false
|
527
|
+
|
528
|
+
second_other_mock_call << mock_end
|
529
|
+
latch.wait(1).should be_false
|
530
|
+
|
531
|
+
second_root_call << mock_end
|
532
|
+
latch.wait(1).should be_true
|
533
|
+
|
534
|
+
waiter_thread.join
|
535
|
+
dial.status.result.should be == :answer
|
536
|
+
end
|
537
|
+
|
538
|
+
it "should cleanup merged calls when the root call ends" do
|
539
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
540
|
+
c.stub join: true, unjoin: true
|
541
|
+
end
|
542
|
+
[other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
543
|
+
c.should_receive(:hangup).once
|
544
|
+
end
|
545
|
+
|
546
|
+
dial.merge other_dial
|
547
|
+
|
548
|
+
waiter_thread = Thread.new do
|
549
|
+
dial.await_completion
|
550
|
+
dial.cleanup_calls
|
551
|
+
latch.countdown!
|
552
|
+
end
|
553
|
+
|
554
|
+
sleep 0.5
|
555
|
+
|
556
|
+
call << mock_end
|
557
|
+
latch.wait(1).should be_true
|
558
|
+
|
559
|
+
waiter_thread.join
|
560
|
+
dial.status.result.should be == :answer
|
561
|
+
end
|
562
|
+
|
563
|
+
context "if the calls were not joined" do
|
564
|
+
it "should still join to mixer" do
|
565
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
566
|
+
second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
567
|
+
|
568
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
569
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
570
|
+
|
571
|
+
second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
572
|
+
second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
573
|
+
|
574
|
+
dial.merge other_dial
|
575
|
+
|
576
|
+
waiter_thread = Thread.new do
|
577
|
+
dial.await_completion
|
578
|
+
latch.countdown!
|
579
|
+
end
|
580
|
+
|
581
|
+
sleep 0.5
|
582
|
+
|
583
|
+
other_mock_call << mock_end
|
584
|
+
second_root_call << mock_end
|
585
|
+
second_other_mock_call << mock_end
|
586
|
+
|
587
|
+
latch.wait(1).should be_true
|
588
|
+
|
589
|
+
waiter_thread.join
|
590
|
+
dial.status.result.should be == :answer
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
214
595
|
end
|
215
596
|
|
216
597
|
describe "when the caller has already hung up" do
|
@@ -725,7 +1106,7 @@ module Adhearsion
|
|
725
1106
|
latch.wait(1).should be_true
|
726
1107
|
end
|
727
1108
|
|
728
|
-
it "hangs up the new call when the
|
1109
|
+
it "hangs up the new call when the root call ends" do
|
729
1110
|
other_mock_call.should_receive(:hangup).once
|
730
1111
|
call.should_receive(:answer).once
|
731
1112
|
other_mock_call.should_receive(:join).once.with(call)
|
@@ -793,7 +1174,6 @@ module Adhearsion
|
|
793
1174
|
t.join
|
794
1175
|
status = t.value
|
795
1176
|
status.result.should be == :answer
|
796
|
-
status.joined_call.should eq(other_mock_call)
|
797
1177
|
|
798
1178
|
joined_status = status.joins[status.calls.first]
|
799
1179
|
joined_status.result.should == :joined
|
@@ -823,11 +1203,393 @@ module Adhearsion
|
|
823
1203
|
t.join
|
824
1204
|
status = t.value
|
825
1205
|
status.result.should be == :answer
|
826
|
-
status.joined_call.should eq(other_mock_call)
|
827
1206
|
joined_status = status.joins[status.calls.first]
|
828
1207
|
joined_status.duration.should == 37.0
|
829
1208
|
end
|
830
1209
|
end
|
1210
|
+
|
1211
|
+
context "when a dial is split" do
|
1212
|
+
before do
|
1213
|
+
call.should_receive(:answer).once
|
1214
|
+
other_mock_call.should_receive(:join).once.with(call)
|
1215
|
+
call.stub(:unjoin).and_return do
|
1216
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
1217
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
it "should unjoin the calls" do
|
1222
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
1223
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
1224
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1228
|
+
dial.run
|
1229
|
+
|
1230
|
+
waiter_thread = Thread.new do
|
1231
|
+
dial.await_completion
|
1232
|
+
latch.countdown!
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
sleep 0.5
|
1236
|
+
|
1237
|
+
other_mock_call << mock_answered
|
1238
|
+
|
1239
|
+
dial.split
|
1240
|
+
other_mock_call << mock_end
|
1241
|
+
|
1242
|
+
latch.wait(1).should be_true
|
1243
|
+
|
1244
|
+
waiter_thread.join
|
1245
|
+
dial.status.result.should be == :answer
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
it "should not unblock immediately" do
|
1249
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1250
|
+
dial.run
|
1251
|
+
|
1252
|
+
waiter_thread = Thread.new do
|
1253
|
+
dial.await_completion
|
1254
|
+
latch.countdown!
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
sleep 0.5
|
1258
|
+
|
1259
|
+
other_mock_call << mock_answered
|
1260
|
+
|
1261
|
+
dial.split
|
1262
|
+
|
1263
|
+
latch.wait(1).should be_false
|
1264
|
+
|
1265
|
+
other_mock_call << mock_end
|
1266
|
+
|
1267
|
+
latch.wait(1).should be_true
|
1268
|
+
|
1269
|
+
waiter_thread.join
|
1270
|
+
dial.status.result.should be == :answer
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
it "should set end time" do
|
1274
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1275
|
+
dial.run
|
1276
|
+
|
1277
|
+
waiter_thread = Thread.new do
|
1278
|
+
dial.await_completion
|
1279
|
+
latch.countdown!
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
sleep 0.5
|
1283
|
+
|
1284
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
1285
|
+
Timecop.freeze base_time
|
1286
|
+
|
1287
|
+
other_mock_call << mock_answered
|
1288
|
+
|
1289
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
1290
|
+
Timecop.freeze base_time
|
1291
|
+
dial.split
|
1292
|
+
|
1293
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 54)
|
1294
|
+
Timecop.freeze base_time
|
1295
|
+
other_mock_call << mock_end
|
1296
|
+
|
1297
|
+
latch.wait(1).should be_true
|
1298
|
+
|
1299
|
+
waiter_thread.join
|
1300
|
+
status = dial.status
|
1301
|
+
status.result.should be == :answer
|
1302
|
+
joined_status = status.joins[status.calls.first]
|
1303
|
+
joined_status.duration.should == 37.0
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
context "with new controllers specified" do
|
1307
|
+
let(:split_latch) { CountDownLatch.new 2 }
|
1308
|
+
|
1309
|
+
let(:split_controller) do
|
1310
|
+
latch = split_latch
|
1311
|
+
Class.new(Adhearsion::CallController) do
|
1312
|
+
@@split_latch = latch
|
1313
|
+
|
1314
|
+
def run
|
1315
|
+
call['hit_split_controller'] = self.class
|
1316
|
+
call['split_controller_metadata'] = metadata
|
1317
|
+
@@split_latch.countdown!
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
let(:main_split_controller) { Class.new(split_controller) }
|
1323
|
+
let(:others_split_controller) { Class.new(split_controller) }
|
1324
|
+
|
1325
|
+
it "should execute the :main controller on the originating call and :others on the outbound calls" do
|
1326
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1327
|
+
dial.run
|
1328
|
+
|
1329
|
+
waiter_thread = Thread.new do
|
1330
|
+
dial.await_completion
|
1331
|
+
latch.countdown!
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
sleep 0.5
|
1335
|
+
|
1336
|
+
other_mock_call << mock_answered
|
1337
|
+
|
1338
|
+
should_receive(:callback).once.with(call)
|
1339
|
+
should_receive(:callback).once.with(other_mock_call)
|
1340
|
+
|
1341
|
+
dial.split main: main_split_controller, others: others_split_controller, main_callback: ->(call) { self.callback(call) }, others_callback: ->(call) { self.callback(call) }
|
1342
|
+
|
1343
|
+
latch.wait(1).should be_false
|
1344
|
+
split_latch.wait(1).should be_true
|
1345
|
+
|
1346
|
+
call['hit_split_controller'].should == main_split_controller
|
1347
|
+
call['split_controller_metadata']['current_dial'].should be dial
|
1348
|
+
|
1349
|
+
other_mock_call['hit_split_controller'].should == others_split_controller
|
1350
|
+
other_mock_call['split_controller_metadata']['current_dial'].should be dial
|
1351
|
+
|
1352
|
+
other_mock_call << mock_end
|
1353
|
+
|
1354
|
+
latch.wait(1).should be_true
|
1355
|
+
|
1356
|
+
waiter_thread.join
|
1357
|
+
dial.status.result.should be == :answer
|
1358
|
+
end
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
context "when rejoining" do
|
1362
|
+
it "should rejoin the calls" do
|
1363
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
1364
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
1365
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1369
|
+
dial.run
|
1370
|
+
|
1371
|
+
waiter_thread = Thread.new do
|
1372
|
+
dial.await_completion
|
1373
|
+
latch.countdown!
|
1374
|
+
end
|
1375
|
+
|
1376
|
+
sleep 0.5
|
1377
|
+
|
1378
|
+
other_mock_call << mock_answered
|
1379
|
+
|
1380
|
+
dial.split
|
1381
|
+
|
1382
|
+
other_mock_call.should_receive(:join).once.ordered.with(call)
|
1383
|
+
dial.rejoin
|
1384
|
+
|
1385
|
+
other_mock_call << mock_end
|
1386
|
+
|
1387
|
+
latch.wait(1).should be_true
|
1388
|
+
|
1389
|
+
waiter_thread.join
|
1390
|
+
dial.status.result.should be == :answer
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
context "to a specified mixer" do
|
1394
|
+
let(:mixer) { SecureRandom.uuid }
|
1395
|
+
|
1396
|
+
it "should join all calls to the mixer" do
|
1397
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
1398
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
1399
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
dial = Dial::ParallelConfirmationDial.new to, options, call
|
1403
|
+
dial.run
|
1404
|
+
|
1405
|
+
waiter_thread = Thread.new do
|
1406
|
+
dial.await_completion
|
1407
|
+
latch.countdown!
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
sleep 0.5
|
1411
|
+
|
1412
|
+
other_mock_call << mock_answered
|
1413
|
+
|
1414
|
+
dial.split
|
1415
|
+
|
1416
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1417
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1418
|
+
dial.rejoin mixer_name: mixer
|
1419
|
+
|
1420
|
+
other_mock_call << mock_end
|
1421
|
+
|
1422
|
+
latch.wait(1).should be_true
|
1423
|
+
|
1424
|
+
waiter_thread.join
|
1425
|
+
dial.status.result.should be == :answer
|
1426
|
+
end
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
context "when another dial is merged in" do
|
1431
|
+
let(:second_root_call_id) { new_uuid }
|
1432
|
+
let(:second_root_call) { Adhearsion::Call.new }
|
1433
|
+
let(:mixer) { SecureRandom.uuid }
|
1434
|
+
|
1435
|
+
let(:dial) { Dial::ParallelConfirmationDial.new to, options, call }
|
1436
|
+
let(:other_dial) { Dial::ParallelConfirmationDial.new second_to, options, second_root_call }
|
1437
|
+
|
1438
|
+
before do
|
1439
|
+
second_root_call.stub write_command: true, id: second_root_call_id
|
1440
|
+
OutboundCall.should_receive(:new).and_return second_other_mock_call
|
1441
|
+
second_other_mock_call.should_receive(:join).once.with(second_root_call)
|
1442
|
+
second_other_mock_call.should_receive(:dial).once.with(second_to, options)
|
1443
|
+
second_root_call.should_receive(:answer).once
|
1444
|
+
|
1445
|
+
SecureRandom.stub uuid: mixer
|
1446
|
+
|
1447
|
+
dial.run
|
1448
|
+
other_dial.run
|
1449
|
+
|
1450
|
+
other_mock_call << mock_answered
|
1451
|
+
second_other_mock_call << mock_answered
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
it "should split calls, rejoin to a mixer, and rejoin other calls to mixer" do
|
1455
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
|
1456
|
+
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
1457
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1458
|
+
end
|
1459
|
+
second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_return do
|
1460
|
+
second_root_call << Punchblock::Event::Unjoined.new(call_uri: second_other_mock_call.id)
|
1461
|
+
second_other_mock_call << Punchblock::Event::Unjoined.new(call_uri: second_root_call.id)
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1465
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1466
|
+
|
1467
|
+
second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1468
|
+
second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1469
|
+
|
1470
|
+
dial.merge other_dial
|
1471
|
+
|
1472
|
+
waiter_thread = Thread.new do
|
1473
|
+
dial.await_completion
|
1474
|
+
latch.countdown!
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
sleep 0.5
|
1478
|
+
|
1479
|
+
other_mock_call << mock_end
|
1480
|
+
second_root_call << mock_end
|
1481
|
+
second_other_mock_call << mock_end
|
1482
|
+
|
1483
|
+
latch.wait(1).should be_true
|
1484
|
+
|
1485
|
+
waiter_thread.join
|
1486
|
+
dial.status.result.should be == :answer
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
it "should add the merged calls to the returned status" do
|
1490
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
|
1491
|
+
dial.merge other_dial
|
1492
|
+
|
1493
|
+
waiter_thread = Thread.new do
|
1494
|
+
dial.await_completion
|
1495
|
+
latch.countdown!
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
sleep 0.5
|
1499
|
+
|
1500
|
+
other_mock_call << mock_end
|
1501
|
+
second_root_call << mock_end
|
1502
|
+
second_other_mock_call << mock_end
|
1503
|
+
|
1504
|
+
latch.wait(1).should be_true
|
1505
|
+
|
1506
|
+
waiter_thread.join
|
1507
|
+
dial.status.result.should be == :answer
|
1508
|
+
dial.status.calls.should include(second_root_call, second_other_mock_call)
|
1509
|
+
end
|
1510
|
+
|
1511
|
+
it "should not unblock until all joined calls end" do
|
1512
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
|
1513
|
+
|
1514
|
+
dial.merge other_dial
|
1515
|
+
|
1516
|
+
waiter_thread = Thread.new do
|
1517
|
+
dial.await_completion
|
1518
|
+
latch.countdown!
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
sleep 0.5
|
1522
|
+
|
1523
|
+
other_mock_call << mock_end
|
1524
|
+
latch.wait(1).should be_false
|
1525
|
+
|
1526
|
+
second_other_mock_call << mock_end
|
1527
|
+
latch.wait(1).should be_false
|
1528
|
+
|
1529
|
+
second_root_call << mock_end
|
1530
|
+
latch.wait(1).should be_true
|
1531
|
+
|
1532
|
+
waiter_thread.join
|
1533
|
+
dial.status.result.should be == :answer
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
it "should cleanup merged calls when the root call ends" do
|
1537
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
1538
|
+
c.stub join: true, unjoin: true
|
1539
|
+
end
|
1540
|
+
[other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
1541
|
+
c.should_receive(:hangup).once
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
dial.merge other_dial
|
1545
|
+
|
1546
|
+
waiter_thread = Thread.new do
|
1547
|
+
dial.await_completion
|
1548
|
+
dial.cleanup_calls
|
1549
|
+
latch.countdown!
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
sleep 0.5
|
1553
|
+
|
1554
|
+
call << mock_end
|
1555
|
+
latch.wait(1).should be_true
|
1556
|
+
|
1557
|
+
waiter_thread.join
|
1558
|
+
dial.status.result.should be == :answer
|
1559
|
+
end
|
1560
|
+
|
1561
|
+
context "if the calls were not joined" do
|
1562
|
+
it "should still join to mixer" do
|
1563
|
+
call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
1564
|
+
second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
1565
|
+
|
1566
|
+
call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1567
|
+
other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1568
|
+
|
1569
|
+
second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1570
|
+
second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
|
1571
|
+
|
1572
|
+
dial.merge other_dial
|
1573
|
+
|
1574
|
+
waiter_thread = Thread.new do
|
1575
|
+
dial.await_completion
|
1576
|
+
latch.countdown!
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
sleep 0.5
|
1580
|
+
|
1581
|
+
other_mock_call << mock_end
|
1582
|
+
second_root_call << mock_end
|
1583
|
+
second_other_mock_call << mock_end
|
1584
|
+
|
1585
|
+
latch.wait(1).should be_true
|
1586
|
+
|
1587
|
+
waiter_thread.join
|
1588
|
+
dial.status.result.should be == :answer
|
1589
|
+
end
|
1590
|
+
end
|
1591
|
+
end
|
1592
|
+
end
|
831
1593
|
end
|
832
1594
|
|
833
1595
|
describe "when the caller has already hung up" do
|
@@ -1289,6 +2051,46 @@ module Adhearsion
|
|
1289
2051
|
end
|
1290
2052
|
end
|
1291
2053
|
end
|
2054
|
+
|
2055
|
+
describe Dial::Dial do
|
2056
|
+
subject { Dial::Dial.new to, {}, call }
|
2057
|
+
|
2058
|
+
describe "#prep_calls" do
|
2059
|
+
it "yields all calls to the passed block" do
|
2060
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
2061
|
+
|
2062
|
+
gathered_calls = []
|
2063
|
+
subject.prep_calls { |call| gathered_calls << call }
|
2064
|
+
|
2065
|
+
expect(gathered_calls).to include(other_mock_call)
|
2066
|
+
end
|
2067
|
+
end
|
2068
|
+
|
2069
|
+
context "#skip_cleanup" do
|
2070
|
+
it "allows the new call to continue after the root call ends" do
|
2071
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
2072
|
+
|
2073
|
+
call.stub answer: true
|
2074
|
+
other_mock_call.stub dial: true, join: true
|
2075
|
+
other_mock_call.should_receive(:hangup).never
|
2076
|
+
|
2077
|
+
subject.run
|
2078
|
+
|
2079
|
+
subject.skip_cleanup
|
2080
|
+
|
2081
|
+
Thread.new do
|
2082
|
+
subject.await_completion
|
2083
|
+
subject.cleanup_calls
|
2084
|
+
latch.countdown!
|
2085
|
+
end
|
2086
|
+
|
2087
|
+
other_mock_call << mock_answered
|
2088
|
+
call << mock_end
|
2089
|
+
|
2090
|
+
latch.wait(1).should be_true
|
2091
|
+
end
|
2092
|
+
end
|
2093
|
+
end
|
1292
2094
|
end
|
1293
2095
|
end
|
1294
2096
|
end
|