adhearsion 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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