adhearsion 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +29 -0
  4. data/Gemfile +0 -2
  5. data/Guardfile +2 -2
  6. data/README.markdown +3 -6
  7. data/Rakefile +1 -1
  8. data/adhearsion.gemspec +7 -2
  9. data/features/cli_create.feature +85 -7
  10. data/features/plugin_generator.feature +4 -0
  11. data/features/step_definitions/app_generator_steps.rb +8 -1
  12. data/lib/adhearsion.rb +6 -3
  13. data/lib/adhearsion/call.rb +101 -30
  14. data/lib/adhearsion/call_controller.rb +40 -12
  15. data/lib/adhearsion/call_controller/dial.rb +119 -36
  16. data/lib/adhearsion/call_controller/input.rb +11 -5
  17. data/lib/adhearsion/call_controller/output.rb +47 -33
  18. data/lib/adhearsion/call_controller/output/async_player.rb +3 -2
  19. data/lib/adhearsion/call_controller/output/formatter.rb +7 -2
  20. data/lib/adhearsion/call_controller/output/player.rb +2 -2
  21. data/lib/adhearsion/call_controller/record.rb +16 -13
  22. data/lib/adhearsion/call_controller/utility.rb +3 -3
  23. data/lib/adhearsion/calls.rb +21 -8
  24. data/lib/adhearsion/cli_commands/ahn_command.rb +1 -0
  25. data/lib/adhearsion/configuration.rb +2 -2
  26. data/lib/adhearsion/console.rb +3 -2
  27. data/lib/adhearsion/generators.rb +7 -9
  28. data/lib/adhearsion/generators/app/app_generator.rb +12 -2
  29. data/lib/adhearsion/generators/app/templates/Gemfile.erb +7 -9
  30. data/lib/adhearsion/generators/app/templates/README.md +0 -19
  31. data/lib/adhearsion/generators/app/templates/adhearsion.erb +37 -0
  32. data/lib/adhearsion/generators/app/templates/config/environment.rb +6 -1
  33. data/lib/adhearsion/generators/app/templates/events.erb +18 -0
  34. data/lib/adhearsion/generators/app/templates/routes.erb +19 -0
  35. data/lib/adhearsion/generators/app/templates/{lib/simon_game.rb → simon_game.rb} +0 -0
  36. data/lib/adhearsion/generators/app/templates/{spec/call_controllers/simon_game_spec.rb → simon_game_spec.rb} +0 -0
  37. data/lib/adhearsion/generators/controller/controller_generator.rb +2 -2
  38. data/lib/adhearsion/generators/controller/templates/lib/{controller.rb → controller.rb.erb} +0 -0
  39. data/lib/adhearsion/generators/controller/templates/spec/{controller_spec.rb → controller_spec.rb.erb} +0 -0
  40. data/lib/adhearsion/generators/plugin/plugin_generator.rb +16 -15
  41. data/lib/adhearsion/generators/plugin/templates/gitignore +17 -0
  42. data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +1 -1
  43. data/lib/adhearsion/initializer.rb +14 -2
  44. data/lib/adhearsion/logging.rb +1 -0
  45. data/lib/adhearsion/outbound_call.rb +3 -7
  46. data/lib/adhearsion/punchblock_plugin/initializer.rb +3 -2
  47. data/lib/adhearsion/router/openended_route.rb +1 -1
  48. data/lib/adhearsion/router/route.rb +4 -3
  49. data/lib/adhearsion/version.rb +1 -1
  50. data/spec/adhearsion/call_controller/dial_spec.rb +811 -79
  51. data/spec/adhearsion/call_controller/output/formatter_spec.rb +13 -1
  52. data/spec/adhearsion/call_controller/output_spec.rb +35 -1
  53. data/spec/adhearsion/call_controller_spec.rb +174 -18
  54. data/spec/adhearsion/call_spec.rb +423 -39
  55. data/spec/adhearsion/calls_spec.rb +19 -3
  56. data/spec/adhearsion/outbound_call_spec.rb +88 -45
  57. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +3 -3
  58. data/spec/adhearsion/router/route_spec.rb +2 -2
  59. data/spec/spec_helper.rb +2 -0
  60. metadata +92 -77
  61. data/features/app_generator.feature +0 -49
  62. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +0 -71
  63. data/lib/adhearsion/generators/plugin/templates/.gitignore +0 -9
@@ -19,6 +19,8 @@ module Adhearsion
19
19
 
20
20
  let(:latch) { CountDownLatch.new 1 }
21
21
 
22
+ let(:join_options) { options[:join_options] || {} }
23
+
22
24
  before do
23
25
  other_mock_call.wrapped_object.stub id: other_call_id, write_command: true
24
26
  second_other_mock_call.wrapped_object.stub id: second_other_call_id, write_command: true
@@ -96,7 +98,7 @@ module Adhearsion
96
98
 
97
99
  it "joins the new call to the existing one on answer" do
98
100
  call.should_receive(:answer).once
99
- other_mock_call.should_receive(:join).once.with(call)
101
+ other_mock_call.should_receive(:join).once.with(call, {})
100
102
 
101
103
  dial_in_thread
102
104
 
@@ -108,9 +110,110 @@ module Adhearsion
108
110
  latch.wait(1).should be_true
109
111
  end
110
112
 
113
+ context "with a join target specified" do
114
+ let(:options) { { join_target: {mixer_name: 'foobar'} } }
115
+
116
+ it "joins the calls to the specified target on answer" do
117
+ call.should_receive(:answer).once
118
+ call.should_receive(:join).once.with({mixer_name: 'foobar'}, {})
119
+ other_mock_call.should_receive(:join).once.with({mixer_name: 'foobar'}, {})
120
+
121
+ dial_in_thread
122
+
123
+ latch.wait(1).should be_false
124
+
125
+ other_mock_call << mock_answered
126
+ other_mock_call << mock_end
127
+
128
+ latch.wait(1).should be_true
129
+ end
130
+ end
131
+
132
+ context "with a pre-join callback specified" do
133
+ let(:foo) { double }
134
+ let(:options) { { pre_join: ->(call) { foo.bar call } } }
135
+
136
+ it "executes the callback prior to joining" do
137
+ foo.should_receive(:bar).once.with(other_mock_call).ordered
138
+ call.should_receive(:answer).once.ordered
139
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
140
+
141
+ dial_in_thread
142
+
143
+ latch.wait(1).should be_false
144
+
145
+ other_mock_call << mock_answered
146
+ other_mock_call << mock_end
147
+
148
+ latch.wait(1).should be_true
149
+ end
150
+ end
151
+
152
+ context "with ringback specified" do
153
+ let(:component) { Punchblock::Component::Output.new }
154
+ let(:options) { { ringback: ['file://tt-monkeys'] } }
155
+
156
+ before do
157
+ component.request!
158
+ component.execute!
159
+ end
160
+
161
+ it "plays the ringback asynchronously, terminating prior to joining" do
162
+ subject.should_receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
163
+ component.should_receive(:stop!).twice
164
+ call.should_receive(:answer).once.ordered
165
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
166
+
167
+ dial_in_thread
168
+
169
+ latch.wait(1).should be_false
170
+
171
+ other_mock_call << mock_answered
172
+ other_mock_call << mock_end
173
+
174
+ latch.wait(1).should be_true
175
+ end
176
+
177
+ context "as a callback" do
178
+ let(:foo) { double }
179
+ let(:options) { { ringback: -> { foo.bar; component } } }
180
+
181
+ it "calls the callback to start, and uses the return value of the callback to stop the ringback" do
182
+ foo.should_receive(:bar).once.ordered
183
+ component.should_receive(:stop!).twice
184
+ call.should_receive(:answer).once.ordered
185
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
186
+
187
+ dial_in_thread
188
+
189
+ latch.wait(1).should be_false
190
+
191
+ other_mock_call << mock_answered
192
+ other_mock_call << mock_end
193
+
194
+ latch.wait(1).should be_true
195
+ end
196
+ end
197
+
198
+ context "when the call is rejected" do
199
+ it "terminates the ringback before returning" do
200
+ subject.should_receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
201
+ component.should_receive(:stop!).once
202
+
203
+ t = dial_in_thread
204
+
205
+ latch.wait(1).should be_false
206
+
207
+ other_mock_call << mock_end(:reject)
208
+
209
+ latch.wait(1).should be_true
210
+ end
211
+ end
212
+ end
213
+
111
214
  it "hangs up the new call when the root call ends" do
112
215
  call.should_receive(:answer).once
113
- other_mock_call.should_receive(:join).once.with(call)
216
+ other_mock_call.should_receive(:join).once.with(call, {})
114
217
  other_mock_call.should_receive(:hangup).once
115
218
 
116
219
  dial_in_thread
@@ -162,7 +265,7 @@ module Adhearsion
162
265
  context "when the call is answered and joined" do
163
266
  it "has an overall dial status of :answer" do
164
267
  call.should_receive(:answer).once
165
- other_mock_call.should_receive(:join).once.with(call)
268
+ other_mock_call.should_receive(:join).once.with(call, {})
166
269
 
167
270
  t = dial_in_thread
168
271
 
@@ -183,7 +286,7 @@ module Adhearsion
183
286
 
184
287
  it "records the duration of the join" do
185
288
  call.should_receive(:answer).once
186
- other_mock_call.should_receive(:join).once.with(call)
289
+ other_mock_call.should_receive(:join).once.with(call, {})
187
290
  other_mock_call.stub hangup: true
188
291
 
189
292
  t = dial_in_thread
@@ -208,26 +311,51 @@ module Adhearsion
208
311
  joined_status = status.joins[status.calls.first]
209
312
  joined_status.duration.should == 37.0
210
313
  end
314
+
315
+ context "when join options are specified" do
316
+ let(:options) { { join_options: {media: :direct} } }
317
+
318
+ it "joins the calls with those options" do
319
+ call.should_receive(:answer).once
320
+ other_mock_call.should_receive(:join).once.with(call, media: :direct)
321
+ other_mock_call.stub hangup: true
322
+
323
+ t = dial_in_thread
324
+
325
+ sleep 0.5
326
+
327
+ other_mock_call << mock_answered
328
+
329
+ other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
330
+ other_mock_call << mock_end
331
+
332
+ latch.wait(1).should be_true
333
+
334
+ t.join
335
+ end
336
+ end
211
337
  end
212
338
 
213
339
  context "when a dial is split" do
340
+ let(:join_target) { call }
341
+
214
342
  before do
215
343
  call.should_receive(:answer).once
216
- other_mock_call.should_receive(:join).once.with(call)
217
- call.stub(:unjoin).and_return do
344
+ other_mock_call.should_receive(:join).once.with(join_target, join_options)
345
+ other_mock_call.stub(:unjoin).and_return do
218
346
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
219
347
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
220
348
  end
221
349
  end
222
350
 
223
351
  it "should unjoin the calls" do
224
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
352
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
225
353
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
226
354
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
227
355
  end
228
356
 
229
357
  dial = Dial::Dial.new to, options, call
230
- dial.run
358
+ dial.run subject
231
359
 
232
360
  waiter_thread = Thread.new do
233
361
  dial.await_completion
@@ -249,7 +377,7 @@ module Adhearsion
249
377
 
250
378
  it "should not unblock immediately" do
251
379
  dial = Dial::Dial.new to, options, call
252
- dial.run
380
+ dial.run subject
253
381
 
254
382
  waiter_thread = Thread.new do
255
383
  dial.await_completion
@@ -274,7 +402,7 @@ module Adhearsion
274
402
 
275
403
  it "should set end time" do
276
404
  dial = Dial::Dial.new to, options, call
277
- dial.run
405
+ dial.run subject
278
406
 
279
407
  waiter_thread = Thread.new do
280
408
  dial.await_completion
@@ -326,7 +454,7 @@ module Adhearsion
326
454
 
327
455
  it "should execute the :main controller on the originating call and :others on the outbound calls" do
328
456
  dial = Dial::Dial.new to, options, call
329
- dial.run
457
+ dial.run subject
330
458
 
331
459
  waiter_thread = Thread.new do
332
460
  dial.await_completion
@@ -362,13 +490,13 @@ module Adhearsion
362
490
 
363
491
  context "when rejoining" do
364
492
  it "should rejoin the calls" do
365
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
493
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
366
494
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
367
495
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
368
496
  end
369
497
 
370
498
  dial = Dial::Dial.new to, options, call
371
- dial.run
499
+ dial.run subject
372
500
 
373
501
  waiter_thread = Thread.new do
374
502
  dial.await_completion
@@ -381,7 +509,7 @@ module Adhearsion
381
509
 
382
510
  dial.split
383
511
 
384
- other_mock_call.should_receive(:join).once.ordered.with(call)
512
+ other_mock_call.should_receive(:join).once.ordered.with(call, {})
385
513
  dial.rejoin
386
514
 
387
515
  other_mock_call << mock_end
@@ -392,17 +520,122 @@ module Adhearsion
392
520
  dial.status.result.should be == :answer
393
521
  end
394
522
 
523
+ context "when join options were set originally" do
524
+ let(:options) { { join_options: {media: :direct} } }
525
+
526
+ it "should rejoin with the same parameters" do
527
+ other_mock_call.stub(:unjoin)
528
+
529
+ dial = Dial::Dial.new to, options, call
530
+ dial.run subject
531
+
532
+ other_mock_call << mock_answered
533
+
534
+ dial.split
535
+
536
+ other_mock_call.should_receive(:join).once.ordered.with(call, media: :direct)
537
+ dial.rejoin
538
+ end
539
+ end
540
+
541
+ context "when join options are passed to rejoin" do
542
+ it "should rejoin with those parameters" do
543
+ other_mock_call.stub(:unjoin)
544
+
545
+ dial = Dial::Dial.new to, options, call
546
+ dial.run subject
547
+
548
+ other_mock_call << mock_answered
549
+
550
+ dial.split
551
+
552
+ other_mock_call.should_receive(:join).once.ordered.with(call, media: :direct)
553
+ dial.rejoin nil, media: :direct
554
+ end
555
+ end
556
+
557
+ context "when a join target was originally specified" do
558
+ let(:join_target) { {mixer_name: 'foobar'} }
559
+ let(:options) { { join_target: join_target } }
560
+
561
+ it "joins the calls to the specified target on answer" do
562
+ call.should_receive(:join).once.with(join_target, {})
563
+ other_mock_call.should_receive(:unjoin).once.ordered.with(join_target)
564
+ call.should_receive(:unjoin).once.ordered.with(join_target).and_return do
565
+ call << Punchblock::Event::Unjoined.new(join_target)
566
+ other_mock_call << Punchblock::Event::Unjoined.new(join_target)
567
+ end
568
+
569
+ dial = Dial::Dial.new to, options, call
570
+ dial.run subject
571
+
572
+ waiter_thread = Thread.new do
573
+ dial.await_completion
574
+ latch.countdown!
575
+ end
576
+
577
+ sleep 0.5
578
+
579
+ other_mock_call << mock_answered
580
+
581
+ dial.split
582
+
583
+ call.should_receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
584
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
585
+ dial.rejoin
586
+
587
+ other_mock_call << mock_end
588
+
589
+ latch.wait(1).should be_true
590
+
591
+ waiter_thread.join
592
+ dial.status.result.should be == :answer
593
+ end
594
+ end
595
+
395
596
  context "to a specified mixer" do
396
597
  let(:mixer) { SecureRandom.uuid }
397
598
 
398
599
  it "should join all calls to the mixer" do
399
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
600
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
601
+ call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
602
+ other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
603
+ end
604
+
605
+ dial = Dial::Dial.new to, options, call
606
+ dial.run subject
607
+
608
+ waiter_thread = Thread.new do
609
+ dial.await_completion
610
+ latch.countdown!
611
+ end
612
+
613
+ sleep 0.5
614
+
615
+ other_mock_call << mock_answered
616
+
617
+ dial.split
618
+
619
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
620
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
621
+ dial.rejoin mixer_name: mixer
622
+
623
+ other_mock_call << mock_end
624
+
625
+ latch.wait(1).should be_true
626
+
627
+ waiter_thread.join
628
+ dial.status.result.should be == :answer
629
+ end
630
+
631
+ it "#split should then unjoin calls from the mixer" do
632
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
400
633
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
401
634
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
402
635
  end
403
636
 
404
637
  dial = Dial::Dial.new to, options, call
405
- dial.run
638
+ dial.run subject
406
639
 
407
640
  waiter_thread = Thread.new do
408
641
  dial.await_completion
@@ -415,10 +648,18 @@ module Adhearsion
415
648
 
416
649
  dial.split
417
650
 
418
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
419
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
651
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
652
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
420
653
  dial.rejoin mixer_name: mixer
421
654
 
655
+ other_mock_call.should_receive(:unjoin).once.ordered.with(mixer_name: mixer).and_return do
656
+ other_mock_call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
657
+ end
658
+ call.should_receive(:unjoin).once.ordered.with(mixer_name: mixer).and_return do
659
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
660
+ end
661
+ dial.split
662
+
422
663
  other_mock_call << mock_end
423
664
 
424
665
  latch.wait(1).should be_true
@@ -440,34 +681,34 @@ module Adhearsion
440
681
  before do
441
682
  second_root_call.stub write_command: true, id: second_root_call_id
442
683
  OutboundCall.should_receive(:new).and_return second_other_mock_call
443
- second_other_mock_call.should_receive(:join).once.with(second_root_call)
684
+ second_other_mock_call.should_receive(:join).once.with(second_root_call, {})
444
685
  second_other_mock_call.should_receive(:dial).once.with(second_to, options)
445
686
  second_root_call.should_receive(:answer).once
446
687
 
447
688
  SecureRandom.stub uuid: mixer
448
689
 
449
- dial.run
450
- other_dial.run
690
+ dial.run subject
691
+ other_dial.run subject
451
692
 
452
693
  other_mock_call << mock_answered
453
694
  second_other_mock_call << mock_answered
454
695
  end
455
696
 
456
697
  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
698
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
458
699
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
459
700
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
460
701
  end
461
- second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_return do
702
+ second_other_mock_call.should_receive(:unjoin).once.ordered.with(second_root_call).and_return do
462
703
  second_root_call << Punchblock::Event::Unjoined.new(call_uri: second_other_mock_call.id)
463
704
  second_other_mock_call << Punchblock::Event::Unjoined.new(call_uri: second_root_call.id)
464
705
  end
465
706
 
466
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
467
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
707
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
708
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
468
709
 
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)
710
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
711
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
471
712
 
472
713
  dial.merge other_dial
473
714
 
@@ -488,6 +729,23 @@ module Adhearsion
488
729
  dial.status.result.should be == :answer
489
730
  end
490
731
 
732
+ context "when join options were specified originally" do
733
+ let(:options) { { join_options: {media: :direct} } }
734
+
735
+ it "should rejoin with default options" do
736
+ other_mock_call.stub(:unjoin)
737
+ second_other_mock_call.stub(:unjoin)
738
+
739
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
740
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
741
+
742
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
743
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
744
+
745
+ dial.merge other_dial
746
+ end
747
+ end
748
+
491
749
  it "should add the merged calls to the returned status" do
492
750
  [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
493
751
  dial.merge other_dial
@@ -560,16 +818,164 @@ module Adhearsion
560
818
  dial.status.result.should be == :answer
561
819
  end
562
820
 
821
+ it "should subsequently rejoin to a mixer" do
822
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
823
+
824
+ dial.merge other_dial
825
+
826
+ waiter_thread = Thread.new do
827
+ dial.await_completion
828
+ latch.countdown!
829
+ end
830
+
831
+ sleep 0.5
832
+
833
+ other_mock_call << mock_end
834
+ latch.wait(1).should be_false
835
+
836
+ [call, second_root_call, second_other_mock_call].each do |call|
837
+ call.should_receive(:unjoin).once.with(mixer_name: mixer).and_return do
838
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
839
+ end
840
+ end
841
+
842
+ dial.split
843
+
844
+ [call, second_root_call, second_other_mock_call].each do |call|
845
+ call.should_receive(:join).once.with({mixer_name: mixer}, {}).and_return do
846
+ call << Punchblock::Event::Joined.new(mixer_name: mixer)
847
+ end
848
+ end
849
+
850
+ dial.rejoin
851
+ end
852
+
853
+ describe "if splitting fails" do
854
+ it "should not add the merged calls to the returned status" do
855
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
856
+ other_dial.should_receive(:split).and_raise StandardError
857
+ expect { dial.merge other_dial }.to raise_error(StandardError)
858
+
859
+ waiter_thread = Thread.new do
860
+ dial.await_completion
861
+ latch.countdown!
862
+ end
863
+
864
+ sleep 0.5
865
+
866
+ other_mock_call.async << mock_end
867
+ second_root_call.async << mock_end
868
+ second_other_mock_call.async << mock_end
869
+
870
+ latch.wait(1).should be_true
871
+
872
+ waiter_thread.join
873
+ dial.status.result.should be == :answer
874
+ dial.status.calls.should_not include(second_root_call, second_other_mock_call)
875
+ end
876
+
877
+ it "should unblock before all joined calls end" do
878
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
879
+
880
+ other_dial.should_receive(:split).and_raise StandardError
881
+ expect { dial.merge other_dial }.to raise_error(StandardError)
882
+
883
+ waiter_thread = Thread.new do
884
+ dial.await_completion
885
+ latch.countdown!
886
+ end
887
+
888
+ sleep 0.5
889
+
890
+ other_mock_call << mock_end
891
+ latch.wait(1).should be_true
892
+
893
+ second_other_mock_call << mock_end
894
+ latch.wait(1).should be_true
895
+
896
+ second_root_call << mock_end
897
+ latch.wait(1).should be_true
898
+
899
+ waiter_thread.join
900
+ dial.status.result.should be == :answer
901
+ end
902
+
903
+ it "should not cleanup merged calls when the root call ends" do
904
+ [call, other_mock_call, second_root_call, second_other_mock_call].each do |c|
905
+ c.stub join: true, unjoin: true
906
+ end
907
+ other_mock_call.should_receive(:hangup).once
908
+ [second_root_call, second_other_mock_call].each do |c|
909
+ c.should_receive(:hangup).never
910
+ end
911
+
912
+ other_dial.should_receive(:split).and_raise StandardError
913
+ expect { dial.merge other_dial }.to raise_error(StandardError)
914
+
915
+ waiter_thread = Thread.new do
916
+ dial.await_completion
917
+ dial.cleanup_calls
918
+ latch.countdown!
919
+ end
920
+
921
+ sleep 0.5
922
+
923
+ call << mock_end
924
+ latch.wait(1).should be_true
925
+
926
+ waiter_thread.join
927
+ dial.status.result.should be == :answer
928
+ end
929
+ end
930
+
931
+ context "if a call hangs up" do
932
+ it "should still allow splitting and rejoining" do
933
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
934
+
935
+ dial.merge other_dial
936
+
937
+ waiter_thread = Thread.new do
938
+ dial.await_completion
939
+ latch.countdown!
940
+ end
941
+
942
+ sleep 0.5
943
+
944
+ [call, second_root_call, second_other_mock_call].each do |call|
945
+ call.should_receive(:unjoin).once.with(mixer_name: mixer).and_return do
946
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
947
+ end
948
+ end
949
+
950
+ other_mock_call.should_receive(:unjoin).and_raise Adhearsion::Call::Hangup
951
+
952
+ dial.split
953
+
954
+ other_mock_call << mock_end
955
+ latch.wait(1).should be_false
956
+
957
+ [call, second_root_call, second_other_mock_call].each do |call|
958
+ call.should_receive(:join).once.with({mixer_name: mixer}, {}).and_return do
959
+ call << Punchblock::Event::Joined.new(mixer_name: mixer)
960
+ end
961
+ end
962
+
963
+ other_mock_call.should_receive(:join).and_raise Adhearsion::Call::ExpiredError
964
+
965
+ dial.rejoin
966
+ end
967
+ end
968
+
563
969
  context "if the calls were not joined" do
564
970
  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)
971
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
972
+ second_other_mock_call.should_receive(:unjoin).once.ordered.with(second_root_call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
567
973
 
568
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
569
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
974
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
975
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
570
976
 
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)
977
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
978
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
573
979
 
574
980
  dial.merge other_dial
575
981
 
@@ -633,7 +1039,7 @@ module Adhearsion
633
1039
 
634
1040
  it "dials all parties and joins the first one to answer, hanging up the rest" do
635
1041
  call.should_receive(:answer).once
636
- other_mock_call.should_receive(:join).once.with(call)
1042
+ other_mock_call.should_receive(:join).once.with(call, {})
637
1043
  second_other_mock_call.should_receive(:hangup).once.and_return do
638
1044
  second_other_mock_call << mock_end
639
1045
  end
@@ -656,7 +1062,7 @@ module Adhearsion
656
1062
 
657
1063
  it "unblocks when the joined call unjoins, allowing it to proceed further" do
658
1064
  call.should_receive(:answer).once
659
- other_mock_call.should_receive(:join).once.with(call)
1065
+ other_mock_call.should_receive(:join).once.with(call, {})
660
1066
  other_mock_call.should_receive(:hangup).once
661
1067
  second_other_mock_call.should_receive(:hangup).once.and_return do
662
1068
  second_other_mock_call << mock_end
@@ -768,7 +1174,7 @@ module Adhearsion
768
1174
  context "when a call is answered and joined, and the other ends with an error" do
769
1175
  it "has an overall dial status of :answer" do
770
1176
  call.should_receive(:answer).once
771
- other_mock_call.should_receive(:join).once.with(call)
1177
+ other_mock_call.should_receive(:join).once.with(call, {})
772
1178
  second_other_mock_call.should_receive(:hangup).once.and_return do
773
1179
  second_other_mock_call << mock_end(:error)
774
1180
  end
@@ -817,7 +1223,7 @@ module Adhearsion
817
1223
  it "should not abort until the far end hangs up" do
818
1224
  other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout))
819
1225
  call.should_receive(:answer).once
820
- other_mock_call.should_receive(:join).once.with(call)
1226
+ other_mock_call.should_receive(:join).once.with(call, {})
821
1227
  OutboundCall.should_receive(:new).and_return other_mock_call
822
1228
 
823
1229
  time = Time.now
@@ -920,7 +1326,7 @@ module Adhearsion
920
1326
  other_mock_call.should_receive(:hangup).once
921
1327
  other_mock_call['confirm'] = true
922
1328
  call.should_receive(:answer).once
923
- other_mock_call.should_receive(:join).once.with(call)
1329
+ other_mock_call.should_receive(:join).once.with(call, {})
924
1330
 
925
1331
  t = dial_in_thread
926
1332
 
@@ -992,7 +1398,7 @@ module Adhearsion
992
1398
  call.should_receive(:answer).once
993
1399
 
994
1400
  other_mock_call.should_receive(:dial).once.with(to, from: nil)
995
- other_mock_call.should_receive(:join).once.with(call)
1401
+ other_mock_call.should_receive(:join).once.with(call, {})
996
1402
  other_mock_call.should_receive(:hangup).once.and_return do
997
1403
  other_mock_call << mock_end
998
1404
  end
@@ -1095,7 +1501,7 @@ module Adhearsion
1095
1501
 
1096
1502
  it "joins the new call to the existing one on answer" do
1097
1503
  call.should_receive(:answer).once
1098
- other_mock_call.should_receive(:join).once.with(call)
1504
+ other_mock_call.should_receive(:join).once.with(call, {})
1099
1505
 
1100
1506
  dial_in_thread
1101
1507
 
@@ -1107,10 +1513,111 @@ module Adhearsion
1107
1513
  latch.wait(1).should be_true
1108
1514
  end
1109
1515
 
1516
+ context "with a join target specified" do
1517
+ let(:options) { { join_target: {mixer_name: 'foobar'} } }
1518
+
1519
+ it "joins the calls to the specified target on answer" do
1520
+ call.should_receive(:answer).once
1521
+ call.should_receive(:join).once.with({mixer_name: 'foobar'}, {})
1522
+ other_mock_call.should_receive(:join).once.with({mixer_name: 'foobar'}, {})
1523
+
1524
+ dial_in_thread
1525
+
1526
+ latch.wait(1).should be_false
1527
+
1528
+ other_mock_call << mock_answered
1529
+ other_mock_call << mock_end
1530
+
1531
+ latch.wait(1).should be_true
1532
+ end
1533
+ end
1534
+
1535
+ context "with a pre-join callback specified" do
1536
+ let(:foo) { double }
1537
+ let(:options) { { pre_join: ->(call) { foo.bar call } } }
1538
+
1539
+ it "executes the callback prior to joining" do
1540
+ foo.should_receive(:bar).once.with(other_mock_call).ordered
1541
+ call.should_receive(:answer).once.ordered
1542
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
1543
+
1544
+ dial_in_thread
1545
+
1546
+ latch.wait(1).should be_false
1547
+
1548
+ other_mock_call << mock_answered
1549
+ other_mock_call << mock_end
1550
+
1551
+ latch.wait(1).should be_true
1552
+ end
1553
+ end
1554
+
1555
+ context "with ringback specified" do
1556
+ let(:component) { Punchblock::Component::Output.new }
1557
+ let(:options) { { ringback: ['file://tt-monkeys'] } }
1558
+
1559
+ before do
1560
+ component.request!
1561
+ component.execute!
1562
+ end
1563
+
1564
+ it "plays the ringback asynchronously, terminating prior to joining" do
1565
+ subject.should_receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
1566
+ component.should_receive(:stop!).twice
1567
+ call.should_receive(:answer).once.ordered
1568
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
1569
+
1570
+ dial_in_thread
1571
+
1572
+ latch.wait(1).should be_false
1573
+
1574
+ other_mock_call << mock_answered
1575
+ other_mock_call << mock_end
1576
+
1577
+ latch.wait(1).should be_true
1578
+ end
1579
+
1580
+ context "as a callback" do
1581
+ let(:foo) { double }
1582
+ let(:options) { { ringback: -> { foo.bar; component } } }
1583
+
1584
+ it "calls the callback to start, and uses the return value of the callback to stop the ringback" do
1585
+ foo.should_receive(:bar).once.ordered
1586
+ component.should_receive(:stop!).twice
1587
+ call.should_receive(:answer).once.ordered
1588
+ other_mock_call.should_receive(:join).once.with(call, {}).ordered
1589
+
1590
+ dial_in_thread
1591
+
1592
+ latch.wait(1).should be_false
1593
+
1594
+ other_mock_call << mock_answered
1595
+ other_mock_call << mock_end
1596
+
1597
+ latch.wait(1).should be_true
1598
+ end
1599
+ end
1600
+
1601
+ context "when the call is rejected" do
1602
+ it "terminates the ringback before returning" do
1603
+ subject.should_receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
1604
+ component.should_receive(:stop!).once
1605
+
1606
+ t = dial_in_thread
1607
+
1608
+ latch.wait(1).should be_false
1609
+
1610
+ other_mock_call << mock_end(:reject)
1611
+
1612
+ latch.wait(1).should be_true
1613
+ end
1614
+ end
1615
+ end
1616
+
1110
1617
  it "hangs up the new call when the root call ends" do
1111
1618
  other_mock_call.should_receive(:hangup).once
1112
1619
  call.should_receive(:answer).once
1113
- other_mock_call.should_receive(:join).once.with(call)
1620
+ other_mock_call.should_receive(:join).once.with(call, {})
1114
1621
 
1115
1622
  dial_in_thread
1116
1623
 
@@ -1161,7 +1668,7 @@ module Adhearsion
1161
1668
  context "when the call is answered and joined" do
1162
1669
  it "has an overall dial status of :answer" do
1163
1670
  call.should_receive(:answer).once
1164
- other_mock_call.should_receive(:join).once.with(call)
1671
+ other_mock_call.should_receive(:join).once.with(call, {})
1165
1672
 
1166
1673
  t = dial_in_thread
1167
1674
 
@@ -1182,7 +1689,7 @@ module Adhearsion
1182
1689
 
1183
1690
  it "records the duration of the join" do
1184
1691
  call.should_receive(:answer).once
1185
- other_mock_call.should_receive(:join).once.with(call)
1692
+ other_mock_call.should_receive(:join).once.with(call, {})
1186
1693
  other_mock_call.stub hangup: true
1187
1694
 
1188
1695
  t = dial_in_thread
@@ -1207,26 +1714,51 @@ module Adhearsion
1207
1714
  joined_status = status.joins[status.calls.first]
1208
1715
  joined_status.duration.should == 37.0
1209
1716
  end
1717
+
1718
+ context "when join options are specified" do
1719
+ let(:options) { { join_options: {media: :direct} } }
1720
+
1721
+ it "joins the calls with those options" do
1722
+ call.should_receive(:answer).once
1723
+ other_mock_call.should_receive(:join).once.with(call, media: :direct)
1724
+ other_mock_call.stub hangup: true
1725
+
1726
+ t = dial_in_thread
1727
+
1728
+ sleep 0.5
1729
+
1730
+ other_mock_call << mock_answered
1731
+
1732
+ other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1733
+ other_mock_call << mock_end
1734
+
1735
+ latch.wait(1).should be_true
1736
+
1737
+ t.join
1738
+ end
1739
+ end
1210
1740
  end
1211
1741
 
1212
1742
  context "when a dial is split" do
1743
+ let(:join_target) { call }
1744
+
1213
1745
  before do
1214
1746
  call.should_receive(:answer).once
1215
- other_mock_call.should_receive(:join).once.with(call)
1216
- call.stub(:unjoin).and_return do
1747
+ other_mock_call.should_receive(:join).once.with(join_target, join_options)
1748
+ other_mock_call.stub(:unjoin).and_return do
1217
1749
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
1218
1750
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1219
1751
  end
1220
1752
  end
1221
1753
 
1222
1754
  it "should unjoin the calls" do
1223
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
1755
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
1224
1756
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
1225
1757
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1226
1758
  end
1227
1759
 
1228
1760
  dial = Dial::ParallelConfirmationDial.new to, options, call
1229
- dial.run
1761
+ dial.run subject
1230
1762
 
1231
1763
  waiter_thread = Thread.new do
1232
1764
  dial.await_completion
@@ -1248,7 +1780,7 @@ module Adhearsion
1248
1780
 
1249
1781
  it "should not unblock immediately" do
1250
1782
  dial = Dial::ParallelConfirmationDial.new to, options, call
1251
- dial.run
1783
+ dial.run subject
1252
1784
 
1253
1785
  waiter_thread = Thread.new do
1254
1786
  dial.await_completion
@@ -1273,7 +1805,7 @@ module Adhearsion
1273
1805
 
1274
1806
  it "should set end time" do
1275
1807
  dial = Dial::ParallelConfirmationDial.new to, options, call
1276
- dial.run
1808
+ dial.run subject
1277
1809
 
1278
1810
  waiter_thread = Thread.new do
1279
1811
  dial.await_completion
@@ -1325,7 +1857,7 @@ module Adhearsion
1325
1857
 
1326
1858
  it "should execute the :main controller on the originating call and :others on the outbound calls" do
1327
1859
  dial = Dial::ParallelConfirmationDial.new to, options, call
1328
- dial.run
1860
+ dial.run subject
1329
1861
 
1330
1862
  waiter_thread = Thread.new do
1331
1863
  dial.await_completion
@@ -1361,13 +1893,13 @@ module Adhearsion
1361
1893
 
1362
1894
  context "when rejoining" do
1363
1895
  it "should rejoin the calls" do
1364
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
1896
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
1365
1897
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
1366
1898
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1367
1899
  end
1368
1900
 
1369
1901
  dial = Dial::ParallelConfirmationDial.new to, options, call
1370
- dial.run
1902
+ dial.run subject
1371
1903
 
1372
1904
  waiter_thread = Thread.new do
1373
1905
  dial.await_completion
@@ -1380,7 +1912,7 @@ module Adhearsion
1380
1912
 
1381
1913
  dial.split
1382
1914
 
1383
- other_mock_call.should_receive(:join).once.ordered.with(call)
1915
+ other_mock_call.should_receive(:join).once.ordered.with(call, {})
1384
1916
  dial.rejoin
1385
1917
 
1386
1918
  other_mock_call << mock_end
@@ -1391,17 +1923,90 @@ module Adhearsion
1391
1923
  dial.status.result.should be == :answer
1392
1924
  end
1393
1925
 
1926
+ context "when join options were set originally" do
1927
+ let(:options) { { join_options: {media: :direct} } }
1928
+
1929
+ it "should rejoin with the same parameters" do
1930
+ other_mock_call.stub(:unjoin)
1931
+
1932
+ dial = Dial::ParallelConfirmationDial.new to, options, call
1933
+ dial.run subject
1934
+
1935
+ other_mock_call << mock_answered
1936
+
1937
+ dial.split
1938
+
1939
+ other_mock_call.should_receive(:join).once.ordered.with(call, media: :direct)
1940
+ dial.rejoin
1941
+ end
1942
+ end
1943
+
1944
+ context "when join options are passed to rejoin" do
1945
+ it "should rejoin with those parameters" do
1946
+ other_mock_call.stub(:unjoin)
1947
+
1948
+ dial = Dial::ParallelConfirmationDial.new to, options, call
1949
+ dial.run subject
1950
+
1951
+ other_mock_call << mock_answered
1952
+
1953
+ dial.split
1954
+
1955
+ other_mock_call.should_receive(:join).once.ordered.with(call, media: :direct)
1956
+ dial.rejoin nil, media: :direct
1957
+ end
1958
+ end
1959
+
1960
+ context "when a join target was originally specified" do
1961
+ let(:join_target) { {mixer_name: 'foobar'} }
1962
+ let(:options) { { join_target: join_target } }
1963
+
1964
+ it "joins the calls to the specified target on answer" do
1965
+ call.should_receive(:join).once.with(join_target, {})
1966
+ other_mock_call.should_receive(:unjoin).once.ordered.with(join_target)
1967
+ call.should_receive(:unjoin).once.ordered.with(join_target).and_return do
1968
+ call << Punchblock::Event::Unjoined.new(join_target)
1969
+ other_mock_call << Punchblock::Event::Unjoined.new(join_target)
1970
+ end
1971
+
1972
+ dial = Dial::ParallelConfirmationDial.new to, options, call
1973
+ dial.run subject
1974
+
1975
+ waiter_thread = Thread.new do
1976
+ dial.await_completion
1977
+ latch.countdown!
1978
+ end
1979
+
1980
+ sleep 0.5
1981
+
1982
+ other_mock_call << mock_answered
1983
+
1984
+ dial.split
1985
+
1986
+ call.should_receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
1987
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
1988
+ dial.rejoin
1989
+
1990
+ other_mock_call << mock_end
1991
+
1992
+ latch.wait(1).should be_true
1993
+
1994
+ waiter_thread.join
1995
+ dial.status.result.should be == :answer
1996
+ end
1997
+ end
1998
+
1394
1999
  context "to a specified mixer" do
1395
2000
  let(:mixer) { SecureRandom.uuid }
1396
2001
 
1397
2002
  it "should join all calls to the mixer" do
1398
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
2003
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
1399
2004
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
1400
2005
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1401
2006
  end
1402
2007
 
1403
2008
  dial = Dial::ParallelConfirmationDial.new to, options, call
1404
- dial.run
2009
+ dial.run subject
1405
2010
 
1406
2011
  waiter_thread = Thread.new do
1407
2012
  dial.await_completion
@@ -1414,8 +2019,8 @@ module Adhearsion
1414
2019
 
1415
2020
  dial.split
1416
2021
 
1417
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
1418
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
2022
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2023
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
1419
2024
  dial.rejoin mixer_name: mixer
1420
2025
 
1421
2026
  other_mock_call << mock_end
@@ -1425,6 +2030,46 @@ module Adhearsion
1425
2030
  waiter_thread.join
1426
2031
  dial.status.result.should be == :answer
1427
2032
  end
2033
+
2034
+ it "#split should then unjoin calls from the mixer" do
2035
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
2036
+ call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
2037
+ other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
2038
+ end
2039
+
2040
+ dial = Dial::ParallelConfirmationDial.new to, options, call
2041
+ dial.run subject
2042
+
2043
+ waiter_thread = Thread.new do
2044
+ dial.await_completion
2045
+ latch.countdown!
2046
+ end
2047
+
2048
+ sleep 0.5
2049
+
2050
+ other_mock_call << mock_answered
2051
+
2052
+ dial.split
2053
+
2054
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2055
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2056
+ dial.rejoin mixer_name: mixer
2057
+
2058
+ other_mock_call.should_receive(:unjoin).once.ordered.with(mixer_name: mixer).and_return do
2059
+ other_mock_call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
2060
+ end
2061
+ call.should_receive(:unjoin).once.ordered.with(mixer_name: mixer).and_return do
2062
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
2063
+ end
2064
+ dial.split
2065
+
2066
+ other_mock_call << mock_end
2067
+
2068
+ latch.wait(1).should be_true
2069
+
2070
+ waiter_thread.join
2071
+ dial.status.result.should be == :answer
2072
+ end
1428
2073
  end
1429
2074
  end
1430
2075
 
@@ -1439,34 +2084,34 @@ module Adhearsion
1439
2084
  before do
1440
2085
  second_root_call.stub write_command: true, id: second_root_call_id
1441
2086
  OutboundCall.should_receive(:new).and_return second_other_mock_call
1442
- second_other_mock_call.should_receive(:join).once.with(second_root_call)
2087
+ second_other_mock_call.should_receive(:join).once.with(second_root_call, {})
1443
2088
  second_other_mock_call.should_receive(:dial).once.with(second_to, options)
1444
2089
  second_root_call.should_receive(:answer).once
1445
2090
 
1446
2091
  SecureRandom.stub uuid: mixer
1447
2092
 
1448
- dial.run
1449
- other_dial.run
2093
+ dial.run subject
2094
+ other_dial.run subject
1450
2095
 
1451
2096
  other_mock_call << mock_answered
1452
2097
  second_other_mock_call << mock_answered
1453
2098
  end
1454
2099
 
1455
2100
  it "should split calls, rejoin to a mixer, and rejoin other calls to mixer" do
1456
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_return do
2101
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_return do
1457
2102
  call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
1458
2103
  other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
1459
2104
  end
1460
- second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_return do
2105
+ second_other_mock_call.should_receive(:unjoin).once.ordered.with(second_root_call).and_return do
1461
2106
  second_root_call << Punchblock::Event::Unjoined.new(call_uri: second_other_mock_call.id)
1462
2107
  second_other_mock_call << Punchblock::Event::Unjoined.new(call_uri: second_root_call.id)
1463
2108
  end
1464
2109
 
1465
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
1466
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
2110
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2111
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
1467
2112
 
1468
- second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
1469
- second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
2113
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2114
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
1470
2115
 
1471
2116
  dial.merge other_dial
1472
2117
 
@@ -1487,6 +2132,23 @@ module Adhearsion
1487
2132
  dial.status.result.should be == :answer
1488
2133
  end
1489
2134
 
2135
+ context "when join options were specified originally" do
2136
+ let(:options) { { join_options: {media: :direct} } }
2137
+
2138
+ it "should rejoin with default options" do
2139
+ other_mock_call.stub(:unjoin)
2140
+ second_other_mock_call.stub(:unjoin)
2141
+
2142
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2143
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2144
+
2145
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2146
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2147
+
2148
+ dial.merge other_dial
2149
+ end
2150
+ end
2151
+
1490
2152
  it "should add the merged calls to the returned status" do
1491
2153
  [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
1492
2154
  dial.merge other_dial
@@ -1559,16 +2221,86 @@ module Adhearsion
1559
2221
  dial.status.result.should be == :answer
1560
2222
  end
1561
2223
 
2224
+ it "should subsequently rejoin to a mixer" do
2225
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
2226
+
2227
+ dial.merge other_dial
2228
+
2229
+ waiter_thread = Thread.new do
2230
+ dial.await_completion
2231
+ latch.countdown!
2232
+ end
2233
+
2234
+ sleep 0.5
2235
+
2236
+ other_mock_call << mock_end
2237
+ latch.wait(1).should be_false
2238
+
2239
+ [call, second_root_call, second_other_mock_call].each do |call|
2240
+ call.should_receive(:unjoin).once.with(mixer_name: mixer).and_return do
2241
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
2242
+ end
2243
+ end
2244
+
2245
+ dial.split
2246
+
2247
+ [call, other_mock_call, second_root_call, second_other_mock_call].each do |call|
2248
+ call.should_receive(:join).once.with({mixer_name: mixer}, {}).and_return do
2249
+ call << Punchblock::Event::Joined.new(mixer_name: mixer)
2250
+ end
2251
+ end
2252
+
2253
+ dial.rejoin
2254
+ end
2255
+
2256
+ context "if a call hangs up" do
2257
+ it "should still allow splitting and rejoining" do
2258
+ [call, other_mock_call, second_root_call, second_other_mock_call].each { |c| c.stub join: true, unjoin: true }
2259
+
2260
+ dial.merge other_dial
2261
+
2262
+ waiter_thread = Thread.new do
2263
+ dial.await_completion
2264
+ latch.countdown!
2265
+ end
2266
+
2267
+ sleep 0.5
2268
+
2269
+ [call, second_root_call, second_other_mock_call].each do |call|
2270
+ call.should_receive(:unjoin).once.with(mixer_name: mixer).and_return do
2271
+ call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
2272
+ end
2273
+ end
2274
+
2275
+ other_mock_call.should_receive(:unjoin).and_raise Adhearsion::Call::Hangup
2276
+
2277
+ dial.split
2278
+
2279
+ other_mock_call << mock_end
2280
+ latch.wait(1).should be_false
2281
+
2282
+ [call, second_root_call, second_other_mock_call].each do |call|
2283
+ call.should_receive(:join).once.with({mixer_name: mixer}, {}).and_return do
2284
+ call << Punchblock::Event::Joined.new(mixer_name: mixer)
2285
+ end
2286
+ end
2287
+
2288
+ other_mock_call.should_receive(:join).and_raise Adhearsion::Call::ExpiredError
2289
+
2290
+ dial.rejoin
2291
+ end
2292
+ end
2293
+
1562
2294
  context "if the calls were not joined" do
1563
2295
  it "should still join to mixer" do
1564
- call.should_receive(:unjoin).once.ordered.with(other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
1565
- second_root_call.should_receive(:unjoin).once.ordered.with(second_other_mock_call.id).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
2296
+ other_mock_call.should_receive(:unjoin).once.ordered.with(call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
2297
+ second_other_mock_call.should_receive(:unjoin).once.ordered.with(second_root_call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
1566
2298
 
1567
- call.should_receive(:join).once.ordered.with(mixer_name: mixer)
1568
- other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
2299
+ call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2300
+ other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
1569
2301
 
1570
- second_root_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
1571
- second_other_mock_call.should_receive(:join).once.ordered.with(mixer_name: mixer)
2302
+ second_root_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
2303
+ second_other_mock_call.should_receive(:join).once.ordered.with({mixer_name: mixer}, {})
1572
2304
 
1573
2305
  dial.merge other_dial
1574
2306
 
@@ -1632,7 +2364,7 @@ module Adhearsion
1632
2364
 
1633
2365
  it "dials all parties and joins the first one to answer, hanging up the rest" do
1634
2366
  call.should_receive(:answer).once
1635
- other_mock_call.should_receive(:join).once.with(call)
2367
+ other_mock_call.should_receive(:join).once.with(call, {})
1636
2368
  second_other_mock_call.should_receive(:hangup).once.and_return do
1637
2369
  second_other_mock_call << mock_end
1638
2370
  end
@@ -1655,7 +2387,7 @@ module Adhearsion
1655
2387
 
1656
2388
  it "unblocks when the joined call unjoins, allowing it to proceed further" do
1657
2389
  call.should_receive(:answer).once
1658
- other_mock_call.should_receive(:join).once.with(call)
2390
+ other_mock_call.should_receive(:join).once.with(call, {})
1659
2391
  other_mock_call.should_receive(:hangup).once
1660
2392
  second_other_mock_call.should_receive(:hangup).once.and_return do
1661
2393
  second_other_mock_call << mock_end
@@ -1767,7 +2499,7 @@ module Adhearsion
1767
2499
  context "when a call is answered and joined, and the other ends with an error" do
1768
2500
  it "has an overall dial status of :answer" do
1769
2501
  call.should_receive(:answer).once
1770
- other_mock_call.should_receive(:join).once.with(call)
2502
+ other_mock_call.should_receive(:join).once.with(call, {})
1771
2503
  second_other_mock_call.should_receive(:hangup).once.and_return do
1772
2504
  second_other_mock_call << mock_end(:error)
1773
2505
  end
@@ -1816,7 +2548,7 @@ module Adhearsion
1816
2548
  it "should not abort until the far end hangs up" do
1817
2549
  other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout))
1818
2550
  call.should_receive(:answer).once
1819
- other_mock_call.should_receive(:join).once.with(call)
2551
+ other_mock_call.should_receive(:join).once.with(call, {})
1820
2552
  OutboundCall.should_receive(:new).and_return other_mock_call
1821
2553
 
1822
2554
  time = Time.now
@@ -1924,7 +2656,7 @@ module Adhearsion
1924
2656
  end
1925
2657
  other_mock_call['confirm'] = true
1926
2658
  call.should_receive(:answer).once
1927
- other_mock_call.should_receive(:join).once.with(call)
2659
+ other_mock_call.should_receive(:join).once.with(call, {})
1928
2660
 
1929
2661
  t = dial_in_thread
1930
2662
 
@@ -2011,7 +2743,7 @@ module Adhearsion
2011
2743
  call.should_receive(:answer).once
2012
2744
 
2013
2745
  other_mock_call.should_receive(:dial).once.with(to, from: nil)
2014
- other_mock_call.should_receive(:join).once.with(call)
2746
+ other_mock_call.should_receive(:join).once.with(call, {})
2015
2747
  other_mock_call.should_receive(:hangup).once.and_return do
2016
2748
  other_mock_call.async.deliver_message mock_end
2017
2749
  end
@@ -2075,7 +2807,7 @@ module Adhearsion
2075
2807
  other_mock_call.stub dial: true, join: true
2076
2808
  other_mock_call.should_receive(:hangup).never
2077
2809
 
2078
- subject.run
2810
+ subject.run double('controller')
2079
2811
 
2080
2812
  subject.skip_cleanup
2081
2813