adhearsion 2.4.0.beta1 → 2.4.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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|
@@ -103,6 +103,8 @@ module Adhearsion
103
103
 
104
104
  def fqdn
105
105
  Socket.gethostbyname(Socket.gethostname).first
106
+ rescue SocketError
107
+ Socket.gethostname
106
108
  end
107
109
 
108
110
  def self.method_missing(method_name, *args, &block)
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Adhearsion
4
- VERSION = '2.4.0.beta1'
4
+ VERSION = '2.4.0.beta2'
5
5
  end
@@ -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 dial unblocks" do
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 dial unblocks" do
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