punchblock 1.8.2 → 1.9.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +8 -0
  4. data/lib/punchblock/component/asterisk/agi/command.rb +0 -11
  5. data/lib/punchblock/connection/asterisk.rb +3 -3
  6. data/lib/punchblock/translator/asterisk/agi_command.rb +40 -0
  7. data/lib/punchblock/translator/asterisk/call.rb +56 -47
  8. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +13 -37
  9. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +24 -41
  10. data/lib/punchblock/translator/asterisk/component/input.rb +2 -1
  11. data/lib/punchblock/translator/asterisk/component/output.rb +16 -21
  12. data/lib/punchblock/translator/asterisk/component/record.rb +11 -19
  13. data/lib/punchblock/translator/asterisk/component.rb +12 -9
  14. data/lib/punchblock/translator/asterisk.rb +16 -22
  15. data/lib/punchblock/translator/dtmf_recognizer.rb +4 -4
  16. data/lib/punchblock/translator/freeswitch/component/input.rb +2 -1
  17. data/lib/punchblock/translator/input_component.rb +2 -2
  18. data/lib/punchblock/version.rb +1 -1
  19. data/punchblock.gemspec +1 -1
  20. data/spec/punchblock/connection/asterisk_spec.rb +8 -7
  21. data/spec/punchblock/translator/asterisk/call_spec.rb +262 -229
  22. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +57 -29
  23. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +40 -46
  24. data/spec/punchblock/translator/asterisk/component/input_spec.rb +7 -7
  25. data/spec/punchblock/translator/asterisk/component/output_spec.rb +84 -53
  26. data/spec/punchblock/translator/asterisk/component/record_spec.rb +55 -83
  27. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +5 -1
  28. data/spec/punchblock/translator/asterisk/component_spec.rb +2 -10
  29. data/spec/punchblock/translator/asterisk_spec.rb +73 -100
  30. metadata +5 -10
@@ -7,7 +7,9 @@ module Punchblock
7
7
  class Asterisk
8
8
  describe Call do
9
9
  let(:channel) { 'SIP/foo' }
10
- let(:translator) { Asterisk.new stub('AMI Client').as_null_object, stub('connection').as_null_object }
10
+ let(:ami_client) { stub('AMI Client').as_null_object }
11
+ let(:connection) { stub('connection').as_null_object }
12
+ let(:translator) { Asterisk.new ami_client, connection }
11
13
  let(:agi_env) do
12
14
  {
13
15
  :agi_request => 'async',
@@ -58,7 +60,7 @@ module Punchblock
58
60
  }
59
61
  end
60
62
 
61
- subject { Call.new channel, translator, agi_env }
63
+ subject { Call.new channel, translator, ami_client, connection, agi_env }
62
64
 
63
65
  its(:id) { should be_a String }
64
66
  its(:channel) { should be == channel }
@@ -84,6 +86,22 @@ module Punchblock
84
86
  end
85
87
  end
86
88
 
89
+ describe "getting channel vars" do
90
+ it "should do a GetVar when we don't have a cached value" do
91
+ response = RubyAMI::Response.new 'Value' => 'thevalue'
92
+ ami_client.should_receive(:send_action).once.with('GetVar', 'Channel' => channel, 'Variable' => 'somevariable').and_return response
93
+ subject.channel_var('somevariable').should == 'thevalue'
94
+ end
95
+
96
+ context "when the value comes back from GetVar as '(null)'" do
97
+ it "should return nil" do
98
+ response = RubyAMI::Response.new 'Value' => '(null)'
99
+ ami_client.should_receive(:send_action).once.with('GetVar', 'Channel' => channel, 'Variable' => 'somevariable').and_return response
100
+ subject.channel_var('somevariable').should be_nil
101
+ end
102
+ end
103
+ end
104
+
87
105
  describe '#send_offer' do
88
106
  it 'sends an offer to the translator' do
89
107
  expected_offer = Punchblock::Event::Offer.new :target_call_id => subject.id,
@@ -103,11 +121,10 @@ module Punchblock
103
121
  end
104
122
 
105
123
  describe '#send_progress' do
106
-
107
124
  context "with a call that is already answered" do
108
125
  it 'should not send the EXEC Progress command' do
109
126
  subject.wrapped_object.should_receive(:'answered?').and_return true
110
- subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").never
127
+ subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").never
111
128
  subject.send_progress
112
129
  end
113
130
  end
@@ -126,7 +143,7 @@ module Punchblock
126
143
  end
127
144
 
128
145
  it 'should not send the EXEC Progress command' do
129
- subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").never
146
+ subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").never
130
147
  subject.send_progress
131
148
  end
132
149
  end
@@ -137,12 +154,12 @@ module Punchblock
137
154
  end
138
155
 
139
156
  it 'should send the EXEC Progress command to a call that is inbound and not answered' do
140
- subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress")
157
+ subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").and_return code: 200, result: 0
141
158
  subject.send_progress
142
159
  end
143
160
 
144
161
  it 'should send the EXEC Progress command only once if called twice' do
145
- subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").once
162
+ subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").once.and_return code: 200, result: 0
146
163
  subject.send_progress
147
164
  subject.send_progress
148
165
  end
@@ -255,7 +272,7 @@ module Punchblock
255
272
  subject.dial dial_command
256
273
  accept_command = Command::Accept.new
257
274
  accept_command.request!
258
- subject.wrapped_object.should_receive(:send_agi_action).never
275
+ subject.wrapped_object.should_receive(:execute_agi_command).never
259
276
  subject.execute_command accept_command
260
277
  accept_command.response(0.5).should be true
261
278
  end
@@ -264,14 +281,13 @@ module Punchblock
264
281
  describe '#process_ami_event' do
265
282
  context 'with a Hangup event' do
266
283
  let :ami_event do
267
- RubyAMI::Event.new('Hangup').tap do |e|
268
- e['Uniqueid'] = "1320842458.8"
269
- e['Calleridnum'] = "5678"
270
- e['Calleridname'] = "Jane Smith"
271
- e['Cause'] = cause
272
- e['Cause-txt'] = cause_txt
273
- e['Channel'] = "SIP/1234-00000000"
274
- end
284
+ RubyAMI::Event.new 'Hangup',
285
+ 'Uniqueid' => "1320842458.8",
286
+ 'Calleridnum' => "5678",
287
+ 'Calleridname' => "Jane Smith",
288
+ 'Cause' => cause,
289
+ 'Cause-txt' => cause_txt,
290
+ 'Channel' => "SIP/1234-00000000"
275
291
  end
276
292
 
277
293
  let(:cause) { '16' }
@@ -291,6 +307,7 @@ module Punchblock
291
307
  end
292
308
 
293
309
  it "should cause all components to send complete events before sending end event" do
310
+ subject.stub :send_progress
294
311
  comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf
295
312
  comp_command.request!
296
313
  component = subject.execute_command comp_command
@@ -305,6 +322,7 @@ module Punchblock
305
322
  end
306
323
 
307
324
  it "should not allow commands to be executed while components are shutting down" do
325
+ subject.stub :send_progress
308
326
  comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf
309
327
  comp_command.request!
310
328
  component = subject.execute_command comp_command
@@ -449,13 +467,12 @@ module Punchblock
449
467
  end
450
468
 
451
469
  let(:ami_event) do
452
- RubyAMI::Event.new("AsyncAGI").tap do |e|
453
- e["SubEvent"] = "End"
454
- e["Channel"] = "SIP/1234-00000000"
455
- e["CommandID"] = component.id
456
- e["Command"] = "EXEC ANSWER"
457
- e["Result"] = "200%20result=123%20(timeout)%0A"
458
- end
470
+ RubyAMI::Event.new "AsyncAGI",
471
+ "SubEvent" => "End",
472
+ "Channel" => "SIP/1234-00000000",
473
+ "CommandID" => component.id,
474
+ "Command" => "EXEC ANSWER",
475
+ "Result" => "200%20result=123%20(timeout)%0A"
459
476
  end
460
477
 
461
478
  before do
@@ -470,17 +487,16 @@ module Punchblock
470
487
 
471
488
  context 'with a Newstate event' do
472
489
  let :ami_event do
473
- RubyAMI::Event.new('Newstate').tap do |e|
474
- e['Privilege'] = 'call,all'
475
- e['Channel'] = 'SIP/1234-00000000'
476
- e['ChannelState'] = channel_state
477
- e['ChannelStateDesc'] = channel_state_desc
478
- e['CallerIDNum'] = ''
479
- e['CallerIDName'] = ''
480
- e['ConnectedLineNum'] = ''
481
- e['ConnectedLineName'] = ''
482
- e['Uniqueid'] = '1326194671.0'
483
- end
490
+ RubyAMI::Event.new 'Newstate',
491
+ 'Privilege' => 'call,all',
492
+ 'Channel' => 'SIP/1234-00000000',
493
+ 'ChannelState' => channel_state,
494
+ 'ChannelStateDesc' => channel_state_desc,
495
+ 'CallerIDNum' => '',
496
+ 'CallerIDName' => '',
497
+ 'ConnectedLineNum' => '',
498
+ 'ConnectedLineName' => '',
499
+ 'Uniqueid' => '1326194671.0'
484
500
  end
485
501
 
486
502
  context 'ringing' do
@@ -520,18 +536,17 @@ module Punchblock
520
536
 
521
537
  context 'with an OriginateResponse event' do
522
538
  let :ami_event do
523
- RubyAMI::Event.new('OriginateResponse').tap do |e|
524
- e['Privilege'] = 'call,all'
525
- e['ActionID'] = '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909'
526
- e['Response'] = response
527
- e['Channel'] = 'SIP/15557654321'
528
- e['Context'] = ''
529
- e['Exten'] = ''
530
- e['Reason'] = '0'
531
- e['Uniqueid'] = uniqueid
532
- e['CallerIDNum'] = 'sip:5551234567'
533
- e['CallerIDName'] = 'Bryan 100'
534
- end
539
+ RubyAMI::Event.new 'OriginateResponse',
540
+ 'Privilege' => 'call,all',
541
+ 'ActionID' => '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909',
542
+ 'Response' => response,
543
+ 'Channel' => 'SIP/15557654321',
544
+ 'Context' => '',
545
+ 'Exten' => '',
546
+ 'Reason' => '0',
547
+ 'Uniqueid' => uniqueid,
548
+ 'CallerIDNum' => 'sip:5551234567',
549
+ 'CallerIDName' => 'Bryan 100'
535
550
  end
536
551
 
537
552
  context 'sucessful' do
@@ -569,13 +584,12 @@ module Punchblock
569
584
 
570
585
  context 'with a handler registered for a matching event' do
571
586
  let :ami_event do
572
- RubyAMI::Event.new('DTMF').tap do |e|
573
- e['Digit'] = '4'
574
- e['Start'] = 'Yes'
575
- e['End'] = 'No'
576
- e['Uniqueid'] = "1320842458.8"
577
- e['Channel'] = "SIP/1234-00000000"
578
- end
587
+ RubyAMI::Event.new 'DTMF',
588
+ 'Digit' => '4',
589
+ 'Start' => 'Yes',
590
+ 'End' => 'No',
591
+ 'Uniqueid' => "1320842458.8",
592
+ 'Channel' => "SIP/1234-00000000"
579
593
  end
580
594
 
581
595
  let(:response) { mock 'Response' }
@@ -591,19 +605,18 @@ module Punchblock
591
605
 
592
606
  context 'with a BridgeExec event' do
593
607
  let :ami_event do
594
- RubyAMI::Event.new('BridgeExec').tap do |e|
595
- e['Privilege'] = "call,all"
596
- e['Response'] = "Success"
597
- e['Channel1'] = "SIP/foo"
598
- e['Channel2'] = other_channel
599
- end
608
+ RubyAMI::Event.new 'BridgeExec',
609
+ 'Privilege' => "call,all",
610
+ 'Response' => "Success",
611
+ 'Channel1' => "SIP/foo",
612
+ 'Channel2' => other_channel
600
613
  end
601
614
 
602
615
  let(:other_channel) { 'SIP/5678-00000000' }
603
616
 
604
617
  context "when a join has been executed against another call" do
605
618
  let :other_call do
606
- Call.new other_channel, translator
619
+ Call.new other_channel, translator, ami_client, connection
607
620
  end
608
621
 
609
622
  let(:other_call_id) { other_call.id }
@@ -614,6 +627,7 @@ module Punchblock
614
627
  before do
615
628
  translator.register_call other_call
616
629
  command.request!
630
+ subject.wrapped_object.should_receive(:execute_agi_command).and_return code: 200
617
631
  subject.execute_command command
618
632
  end
619
633
 
@@ -624,12 +638,11 @@ module Punchblock
624
638
 
625
639
  context "with the channel names reversed" do
626
640
  let :ami_event do
627
- RubyAMI::Event.new('BridgeExec').tap do |e|
628
- e['Privilege'] = "call,all"
629
- e['Response'] = "Success"
630
- e['Channel1'] = other_channel
631
- e['Channel2'] = "SIP/foo"
632
- end
641
+ RubyAMI::Event.new 'BridgeExec',
642
+ 'Privilege' => "call,all",
643
+ 'Response' => "Success",
644
+ 'Channel1' => other_channel,
645
+ 'Channel2' => "SIP/foo"
633
646
  end
634
647
 
635
648
  it 'retrieves and sets success on the correct Join' do
@@ -650,35 +663,33 @@ module Punchblock
650
663
  let(:other_channel) { 'SIP/5678-00000000' }
651
664
  let(:other_call_id) { 'def567' }
652
665
  let :other_call do
653
- Call.new other_channel, translator
666
+ Call.new other_channel, translator, ami_client, connection
654
667
  end
655
668
 
656
669
  let :ami_event do
657
- RubyAMI::Event.new('Bridge').tap do |e|
658
- e['Privilege'] = "call,all"
659
- e['Bridgestate'] = state
660
- e['Bridgetype'] = "core"
661
- e['Channel1'] = channel
662
- e['Channel2'] = other_channel
663
- e['Uniqueid1'] = "1319717537.11"
664
- e['Uniqueid2'] = "1319717537.10"
665
- e['CallerID1'] = "1234"
666
- e['CallerID2'] = "5678"
667
- end
670
+ RubyAMI::Event.new 'Bridge',
671
+ 'Privilege' => "call,all",
672
+ 'Bridgestate' => state,
673
+ 'Bridgetype' => "core",
674
+ 'Channel1' => channel,
675
+ 'Channel2' => other_channel,
676
+ 'Uniqueid1' => "1319717537.11",
677
+ 'Uniqueid2' => "1319717537.10",
678
+ 'CallerID1' => "1234",
679
+ 'CallerID2' => "5678"
668
680
  end
669
681
 
670
682
  let :switched_ami_event do
671
- RubyAMI::Event.new('Bridge').tap do |e|
672
- e['Privilege'] = "call,all"
673
- e['Bridgestate'] = state
674
- e['Bridgetype'] = "core"
675
- e['Channel1'] = other_channel
676
- e['Channel2'] = channel
677
- e['Uniqueid1'] = "1319717537.11"
678
- e['Uniqueid2'] = "1319717537.10"
679
- e['CallerID1'] = "1234"
680
- e['CallerID2'] = "5678"
681
- end
683
+ RubyAMI::Event.new 'Bridge',
684
+ 'Privilege' => "call,all",
685
+ 'Bridgestate' => state,
686
+ 'Bridgetype' => "core",
687
+ 'Channel1' => other_channel,
688
+ 'Channel2' => channel,
689
+ 'Uniqueid1' => "1319717537.11",
690
+ 'Uniqueid2' => "1319717537.10",
691
+ 'CallerID1' => "1234",
692
+ 'CallerID2' => "5678"
682
693
  end
683
694
 
684
695
  before do
@@ -734,31 +745,29 @@ module Punchblock
734
745
  let(:other_channel) { 'SIP/5678-00000000' }
735
746
  let(:other_call_id) { 'def567' }
736
747
  let :other_call do
737
- Call.new other_channel, translator
748
+ Call.new other_channel, translator, ami_client, connection
738
749
  end
739
750
 
740
751
  let :ami_event do
741
- RubyAMI::Event.new('Unlink').tap do |e|
742
- e['Privilege'] = "call,all"
743
- e['Channel1'] = channel
744
- e['Channel2'] = other_channel
745
- e['Uniqueid1'] = "1319717537.11"
746
- e['Uniqueid2'] = "1319717537.10"
747
- e['CallerID1'] = "1234"
748
- e['CallerID2'] = "5678"
749
- end
752
+ RubyAMI::Event.new 'Unlink',
753
+ 'Privilege' => "call,all",
754
+ 'Channel1' => channel,
755
+ 'Channel2' => other_channel,
756
+ 'Uniqueid1' => "1319717537.11",
757
+ 'Uniqueid2' => "1319717537.10",
758
+ 'CallerID1' => "1234",
759
+ 'CallerID2' => "5678"
750
760
  end
751
761
 
752
762
  let :switched_ami_event do
753
- RubyAMI::Event.new('Unlink').tap do |e|
754
- e['Privilege'] = "call,all"
755
- e['Channel1'] = other_channel
756
- e['Channel2'] = channel
757
- e['Uniqueid1'] = "1319717537.11"
758
- e['Uniqueid2'] = "1319717537.10"
759
- e['CallerID1'] = "1234"
760
- e['CallerID2'] = "5678"
761
- end
763
+ RubyAMI::Event.new 'Unlink',
764
+ 'Privilege' => "call,all",
765
+ 'Channel1' => other_channel,
766
+ 'Channel2' => channel,
767
+ 'Uniqueid1' => "1319717537.11",
768
+ 'Uniqueid2' => "1319717537.10",
769
+ 'CallerID1' => "1234",
770
+ 'CallerID2' => "5678"
762
771
  end
763
772
 
764
773
  before do
@@ -785,17 +794,32 @@ module Punchblock
785
794
  end
786
795
  end
787
796
 
788
- let :ami_event do
789
- RubyAMI::Event.new('Foo').tap do |e|
790
- e['Uniqueid'] = "1320842458.8"
791
- e['Calleridnum'] = "5678"
792
- e['Calleridname'] = "Jane Smith"
793
- e['Cause'] = "0"
794
- e['Cause-txt'] = "Unknown"
795
- e['Channel'] = channel
797
+ context 'with a VarSet event' do
798
+ let :ami_event do
799
+ RubyAMI::Event.new 'VarSet',
800
+ "Privilege" => "dialplan,all",
801
+ "Channel" => "SIP/1234-00000000",
802
+ "Variable" => "foobar",
803
+ "Value" => 'abc123',
804
+ "Uniqueid" => "1326210224.0"
805
+ end
806
+
807
+ it 'makes the variable accessible on the call' do
808
+ subject.process_ami_event ami_event
809
+ subject.channel_var('foobar').should == 'abc123'
796
810
  end
797
811
  end
798
812
 
813
+ let :ami_event do
814
+ RubyAMI::Event.new 'Foo',
815
+ 'Uniqueid' => "1320842458.8",
816
+ 'Calleridnum' => "5678",
817
+ 'Calleridname' => "Jane Smith",
818
+ 'Cause' => "0",
819
+ 'Cause-txt' => "Unknown",
820
+ 'Channel' => channel
821
+ end
822
+
799
823
  let :expected_pb_event do
800
824
  Event::Asterisk::AMI::Event.new :name => 'Foo',
801
825
  :attributes => { :channel => channel,
@@ -815,14 +839,6 @@ module Punchblock
815
839
  end
816
840
 
817
841
  describe '#execute_command' do
818
- let :expected_agi_complete_event do
819
- Punchblock::Event::Complete.new.tap do |c|
820
- c.reason = Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => 200,
821
- :result => 'Success',
822
- :data => 'FOO'
823
- end
824
- end
825
-
826
842
  before do
827
843
  command.request!
828
844
  end
@@ -831,11 +847,8 @@ module Punchblock
831
847
  let(:command) { Command::Accept.new }
832
848
 
833
849
  it "should send an EXEC RINGING AGI command and set the command's response" do
834
- component = subject.execute_command command
835
- component.internal.should be_true
836
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
837
- agi_command.name.should be == "EXEC RINGING"
838
- agi_command.add_event expected_agi_complete_event
850
+ subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC RINGING').and_return code: 200
851
+ subject.execute_command command
839
852
  command.response(0.5).should be true
840
853
  end
841
854
  end
@@ -845,31 +858,22 @@ module Punchblock
845
858
 
846
859
  it "with a :busy reason should send an EXEC Busy AGI command and set the command's response" do
847
860
  command.reason = :busy
848
- component = subject.execute_command command
849
- component.internal.should be_true
850
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
851
- agi_command.name.should be == "EXEC Busy"
852
- agi_command.add_event expected_agi_complete_event
861
+ subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200
862
+ subject.execute_command command
853
863
  command.response(0.5).should be true
854
864
  end
855
865
 
856
866
  it "with a :decline reason should send an EXEC Busy AGI command and set the command's response" do
857
867
  command.reason = :decline
858
- component = subject.execute_command command
859
- component.internal.should be_true
860
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
861
- agi_command.name.should be == "EXEC Busy"
862
- agi_command.add_event expected_agi_complete_event
868
+ subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200
869
+ subject.execute_command command
863
870
  command.response(0.5).should be true
864
871
  end
865
872
 
866
873
  it "with an :error reason should send an EXEC Congestion AGI command and set the command's response" do
867
874
  command.reason = :error
868
- component = subject.execute_command command
869
- component.internal.should be_true
870
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
871
- agi_command.name.should be == "EXEC Congestion"
872
- agi_command.add_event expected_agi_complete_event
875
+ subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Congestion').and_return code: 200
876
+ subject.execute_command command
873
877
  command.response(0.5).should be true
874
878
  end
875
879
  end
@@ -878,11 +882,8 @@ module Punchblock
878
882
  let(:command) { Command::Answer.new }
879
883
 
880
884
  it "should send an ANSWER AGI command and set the command's response" do
881
- component = subject.execute_command command
882
- component.internal.should be_true
883
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
884
- agi_command.name.should be == "ANSWER"
885
- agi_command.add_event expected_agi_complete_event
885
+ subject.wrapped_object.should_receive(:execute_agi_command).with('ANSWER').and_return code: 200
886
+ subject.execute_command command
886
887
  command.response(0.5).should be true
887
888
  end
888
889
  end
@@ -891,11 +892,8 @@ module Punchblock
891
892
  let(:command) { Command::Hangup.new }
892
893
 
893
894
  it "should send a Hangup AMI command and set the command's response" do
895
+ ami_client.should_receive(:send_action).once.with('Hangup', 'Channel' => channel, 'Cause' => 16).and_return RubyAMI::Response.new
894
896
  subject.execute_command command
895
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
896
- ami_action.name.should be == "hangup"
897
- ami_action.headers['Cause'].should be == 16
898
- ami_action << RubyAMI::Response.new
899
897
  command.response(0.5).should be true
900
898
  end
901
899
  end
@@ -908,7 +906,6 @@ module Punchblock
908
906
  let(:mock_action) { Translator::Asterisk::Component::Asterisk::AGICommand.new(command, subject) }
909
907
 
910
908
  it 'should create an AGI command component actor and execute it asynchronously' do
911
- mock_action.should_receive(:internal=).never
912
909
  Component::Asterisk::AGICommand.should_receive(:new_link).once.with(command, subject).and_return mock_action
913
910
  mock_action.async.should_receive(:execute).once
914
911
  subject.execute_command command
@@ -924,7 +921,6 @@ module Punchblock
924
921
 
925
922
  it 'should create an Output component and execute it asynchronously' do
926
923
  Component::Output.should_receive(:new_link).once.with(command, subject).and_return mock_action
927
- mock_action.should_receive(:internal=).never
928
924
  mock_action.async.should_receive(:execute).once
929
925
  subject.execute_command command
930
926
  end
@@ -939,7 +935,6 @@ module Punchblock
939
935
 
940
936
  it 'should create an Input component and execute it asynchronously' do
941
937
  Component::Input.should_receive(:new_link).once.with(command, subject).and_return mock_action
942
- mock_action.should_receive(:internal=).never
943
938
  mock_action.async.should_receive(:execute).once
944
939
  subject.execute_command command
945
940
  end
@@ -954,7 +949,6 @@ module Punchblock
954
949
 
955
950
  it 'should create a Record component and execute it asynchronously' do
956
951
  Component::Record.should_receive(:new_link).once.with(command, subject).and_return mock_action
957
- mock_action.should_receive(:internal=).never
958
952
  mock_action.async.should_receive(:execute).once
959
953
  subject.execute_command command
960
954
  end
@@ -1059,7 +1053,7 @@ module Punchblock
1059
1053
  let(:other_translator) { stub('Translator::Asterisk').as_null_object }
1060
1054
 
1061
1055
  let :other_call do
1062
- Call.new other_channel, other_translator
1056
+ Call.new other_channel, other_translator, ami_client, connection
1063
1057
  end
1064
1058
 
1065
1059
  let :command do
@@ -1067,11 +1061,9 @@ module Punchblock
1067
1061
  end
1068
1062
 
1069
1063
  it "executes the proper dialplan Bridge application" do
1064
+ subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Bridge', other_channel).and_return code: 200
1070
1065
  translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call)
1071
1066
  subject.execute_command command
1072
- agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
1073
- agi_command.name.should be == "EXEC Bridge"
1074
- agi_command.params_array.should be == [other_channel]
1075
1067
  end
1076
1068
  end
1077
1069
 
@@ -1080,7 +1072,7 @@ module Punchblock
1080
1072
  let(:other_channel) { 'SIP/bar' }
1081
1073
 
1082
1074
  let :other_call do
1083
- Call.new other_channel, translator
1075
+ Call.new other_channel, translator, ami_client, connection
1084
1076
  end
1085
1077
 
1086
1078
  let :command do
@@ -1089,45 +1081,49 @@ module Punchblock
1089
1081
 
1090
1082
  it "executes the unjoin through redirection" do
1091
1083
  translator.should_receive(:call_with_id).with(other_call_id).and_return(nil)
1084
+
1085
+ ami_client.should_receive(:send_action).once.with("Redirect",
1086
+ 'Channel' => channel,
1087
+ 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1088
+ 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1089
+ 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT,
1090
+ ).and_return RubyAMI::Response.new
1091
+
1092
1092
  subject.execute_command command
1093
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1094
- ami_action.name.should be == "redirect"
1095
- ami_action.headers['Channel'].should be == channel
1096
- ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1097
- ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1098
- ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1099
-
1100
- ami_action << RubyAMI::Response.new
1093
+
1101
1094
  command.response(1).should be_true
1102
1095
  end
1103
1096
 
1104
1097
  it "executes the unjoin through redirection, on the subject call and the other call" do
1105
1098
  translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call)
1106
- subject.execute_command command
1107
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1108
- ami_action.name.should be == "redirect"
1109
- ami_action.headers['Channel'].should be == channel
1110
- ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1111
- ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1112
- ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1113
1099
 
1114
- ami_action.headers['ExtraChannel'].should be == other_channel
1115
- ami_action.headers['ExtraExten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1116
- ami_action.headers['ExtraPriority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1117
- ami_action.headers['ExtraContext'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1100
+ ami_client.should_receive(:send_action).once.with("Redirect",
1101
+ 'Channel' => channel,
1102
+ 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1103
+ 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1104
+ 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT,
1105
+ 'ExtraChannel' => other_channel,
1106
+ 'ExtraExten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1107
+ 'ExtraPriority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1108
+ 'ExtraContext' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1109
+ ).and_return RubyAMI::Response.new
1110
+
1111
+ subject.execute_command command
1118
1112
  end
1119
1113
 
1120
1114
  it "handles redirect errors" do
1121
1115
  translator.should_receive(:call_with_id).with(other_call_id).and_return(nil)
1116
+
1117
+ error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
1118
+
1119
+ ami_client.should_receive(:send_action).once.with("Redirect",
1120
+ 'Channel' => channel,
1121
+ 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1122
+ 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1123
+ 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT,
1124
+ ).and_raise error
1125
+
1122
1126
  subject.execute_command command
1123
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1124
- ami_action.name.should be == "redirect"
1125
- ami_action.headers['Channel'].should be == channel
1126
- ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1127
- ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1128
- ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1129
-
1130
- ami_action << RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
1131
1127
  response = command.response(1)
1132
1128
  response.should be_a ProtocolError
1133
1129
  response.text.should == 'FooBar'
@@ -1135,54 +1131,91 @@ module Punchblock
1135
1131
  end
1136
1132
  end#execute_command
1137
1133
 
1138
- describe '#send_agi_action' do
1134
+ describe '#execute_agi_command' do
1135
+ before { stub_uuids Punchblock.new_uuid }
1136
+
1137
+ let :response do
1138
+ RubyAMI::Response.new 'ActionID' => "552a9d9f-46d7-45d8-a257-06fe95f48d99",
1139
+ 'Message' => 'Added AGI original_command to queue'
1140
+ end
1141
+
1139
1142
  it 'should send an appropriate AsyncAGI AMI action' do
1140
- pending
1141
- subject.wrapped_object.should_receive(:send_ami_action).once.with('AGI', 'Command' => 'FOO', 'Channel' => subject.channel)
1142
- subject.send_agi_action 'FOO'
1143
+ Celluloid::Condition.any_instance.should_receive(:wait).and_return nil
1144
+ ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'EXEC ANSWER', 'CommandID' => Punchblock.new_uuid).and_return(response)
1145
+ subject.execute_agi_command 'EXEC ANSWER'
1143
1146
  end
1144
- end
1145
1147
 
1146
- describe '#send_ami_action' do
1147
- let(:component_id) { Punchblock.new_uuid }
1148
- before { stub_uuids component_id }
1148
+ context 'with some parameters' do
1149
+ let(:params) { [1000, 'foo'] }
1150
+
1151
+ it 'should send the appropriate action' do
1152
+ Celluloid::Condition.any_instance.should_receive(:wait).and_return nil
1153
+ ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'WAIT FOR DIGIT "1000" "foo"', 'CommandID' => Punchblock.new_uuid).and_return(response)
1154
+ subject.execute_agi_command 'WAIT FOR DIGIT', *params
1155
+ end
1156
+ end
1157
+
1158
+ context 'with an error' do
1159
+ let :error do
1160
+ RubyAMI::Error.new.tap { |e| e.message = 'Action failed' }
1161
+ end
1162
+
1163
+ it 'should raise the error' do
1164
+ ami_client.should_receive(:send_action).once.and_raise error
1165
+ expect { subject.execute_agi_command 'EXEC ANSWER' }.to raise_error(RubyAMI::Error, 'Action failed')
1166
+ end
1167
+ end
1168
+
1169
+ describe 'when receiving an AsyncAGI event' do
1170
+ context 'of type Exec' do
1171
+ let(:ami_event) do
1172
+ RubyAMI::Event.new 'AsyncAGI',
1173
+ "SubEvent" => "Exec",
1174
+ "Channel" => channel,
1175
+ "CommandID" => Punchblock.new_uuid,
1176
+ "Command" => "EXEC ANSWER",
1177
+ "Result" => "200%20result=123%20(timeout)%0A"
1178
+ end
1179
+
1180
+ it 'should return the result' do
1181
+ fut = subject.future.execute_agi_command 'EXEC ANSWER'
1149
1182
 
1150
- it 'should send the action to the AMI client' do
1151
- action = RubyAMI::Action.new 'foo', :foo => :bar
1152
- translator.should_receive(:send_ami_action).once.with action
1153
- subject.send_ami_action 'foo', :foo => :bar
1183
+ subject.process_ami_event ami_event
1184
+
1185
+ fut.value.should == {code: 200, result: 123, data: 'timeout'}
1186
+ end
1187
+ end
1154
1188
  end
1155
1189
  end
1156
1190
 
1157
1191
  describe '#redirect_back' do
1158
- let(:other_channel) { 'SIP/bar' }
1159
- let :other_call do
1160
- Call.new other_channel, translator
1161
- end
1162
-
1163
- it "executes the proper AMI action with only the subject call" do
1164
- subject.redirect_back
1165
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1166
- ami_action.name.should be == "redirect"
1167
- ami_action.headers['Channel'].should be == channel
1168
- ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1169
- ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1170
- ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1171
- end
1172
-
1173
- it "executes the proper AMI action with another call specified" do
1174
- subject.redirect_back other_call
1175
- ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1176
- ami_action.name.should be == "redirect"
1177
- ami_action.headers['Channel'].should be == channel
1178
- ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1179
- ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1180
- ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1181
- ami_action.headers['ExtraChannel'].should be == other_channel
1182
- ami_action.headers['ExtraExten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1183
- ami_action.headers['ExtraPriority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1184
- ami_action.headers['ExtraContext'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1185
- end
1192
+ let(:other_channel) { 'SIP/bar' }
1193
+
1194
+ let :other_call do
1195
+ Call.new other_channel, translator, ami_client, connection
1196
+ end
1197
+
1198
+ it "executes the proper AMI action with only the subject call" do
1199
+ ami_client.should_receive(:send_action).once.with 'Redirect',
1200
+ 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1201
+ 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1202
+ 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT,
1203
+ 'Channel' => channel
1204
+ subject.redirect_back
1205
+ end
1206
+
1207
+ it "executes the proper AMI action with another call specified" do
1208
+ ami_client.should_receive(:send_action).once.with 'Redirect',
1209
+ 'Channel' => channel,
1210
+ 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1211
+ 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1212
+ 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT,
1213
+ 'ExtraChannel' => other_channel,
1214
+ 'ExtraExten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION,
1215
+ 'ExtraPriority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY,
1216
+ 'ExtraContext' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1217
+ subject.redirect_back other_call
1218
+ end
1186
1219
  end
1187
1220
  end
1188
1221
  end