spec/punchblock/translator/asterisk/call_spec.rb in punchblock-1.8.2 vs spec/punchblock/translator/asterisk/call_spec.rb in punchblock-1.9.0

- old
+ new

@@ -5,11 +5,13 @@ module Punchblock module Translator class Asterisk describe Call do let(:channel) { 'SIP/foo' } - let(:translator) { Asterisk.new stub('AMI Client').as_null_object, stub('connection').as_null_object } + let(:ami_client) { stub('AMI Client').as_null_object } + let(:connection) { stub('connection').as_null_object } + let(:translator) { Asterisk.new ami_client, connection } let(:agi_env) do { :agi_request => 'async', :agi_channel => 'SIP/1234-00000000', :agi_language => 'en', @@ -56,11 +58,11 @@ :x_agi_accountcode => '', :x_agi_threadid => '4366221312' } end - subject { Call.new channel, translator, agi_env } + subject { Call.new channel, translator, ami_client, connection, agi_env } its(:id) { should be_a String } its(:channel) { should be == channel } its(:translator) { should be translator } its(:agi_env) { should be == agi_env } @@ -82,10 +84,26 @@ subject.register_component component subject.component_with_id(component_id).should be component end end + describe "getting channel vars" do + it "should do a GetVar when we don't have a cached value" do + response = RubyAMI::Response.new 'Value' => 'thevalue' + ami_client.should_receive(:send_action).once.with('GetVar', 'Channel' => channel, 'Variable' => 'somevariable').and_return response + subject.channel_var('somevariable').should == 'thevalue' + end + + context "when the value comes back from GetVar as '(null)'" do + it "should return nil" do + response = RubyAMI::Response.new 'Value' => '(null)' + ami_client.should_receive(:send_action).once.with('GetVar', 'Channel' => channel, 'Variable' => 'somevariable').and_return response + subject.channel_var('somevariable').should be_nil + end + end + end + describe '#send_offer' do it 'sends an offer to the translator' do expected_offer = Punchblock::Event::Offer.new :target_call_id => subject.id, :to => '1000', :from => 'Jane Smith <SIP/5678>', @@ -101,15 +119,14 @@ subject.outbound?.should be false end end describe '#send_progress' do - context "with a call that is already answered" do it 'should not send the EXEC Progress command' do subject.wrapped_object.should_receive(:'answered?').and_return true - subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").never + subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").never subject.send_progress end end context "with an unanswered call" do @@ -124,27 +141,27 @@ dial_command.request! subject.dial dial_command end it 'should not send the EXEC Progress command' do - subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").never + subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").never subject.send_progress end end context "with a call that is inbound" do before do subject.send_offer end it 'should send the EXEC Progress command to a call that is inbound and not answered' do - subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress") + subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").and_return code: 200, result: 0 subject.send_progress end it 'should send the EXEC Progress command only once if called twice' do - subject.wrapped_object.should_receive(:send_agi_action).with("EXEC Progress").once + subject.wrapped_object.should_receive(:execute_agi_command).with("EXEC Progress").once.and_return code: 200, result: 0 subject.send_progress subject.send_progress end end end @@ -253,27 +270,26 @@ it 'causes accepting the call to be a null operation' do subject.dial dial_command accept_command = Command::Accept.new accept_command.request! - subject.wrapped_object.should_receive(:send_agi_action).never + subject.wrapped_object.should_receive(:execute_agi_command).never subject.execute_command accept_command accept_command.response(0.5).should be true end end describe '#process_ami_event' do context 'with a Hangup event' do let :ami_event do - RubyAMI::Event.new('Hangup').tap do |e| - e['Uniqueid'] = "1320842458.8" - e['Calleridnum'] = "5678" - e['Calleridname'] = "Jane Smith" - e['Cause'] = cause - e['Cause-txt'] = cause_txt - e['Channel'] = "SIP/1234-00000000" - end + RubyAMI::Event.new 'Hangup', + 'Uniqueid' => "1320842458.8", + 'Calleridnum' => "5678", + 'Calleridname' => "Jane Smith", + 'Cause' => cause, + 'Cause-txt' => cause_txt, + 'Channel' => "SIP/1234-00000000" end let(:cause) { '16' } let(:cause_txt) { 'Normal Clearing' } @@ -289,10 +305,11 @@ translator.should_receive(:deregister_call).once.with(subject) subject.process_ami_event ami_event end it "should cause all components to send complete events before sending end event" do + subject.stub :send_progress comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf comp_command.request! component = subject.execute_command comp_command comp_command.response(0.1).should be_a Ref expected_complete_event = Punchblock::Event::Complete.new :target_call_id => subject.id, :component_id => component.id @@ -303,10 +320,11 @@ translator.should_receive(:handle_pb_event).with(expected_end_event).once.ordered subject.process_ami_event ami_event end it "should not allow commands to be executed while components are shutting down" do + subject.stub :send_progress comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf comp_command.request! component = subject.execute_command comp_command comp_command.response(0.1).should be_a Ref @@ -447,17 +465,16 @@ let :component do Component::Asterisk::AGICommand.new mock_component_node, subject end let(:ami_event) do - RubyAMI::Event.new("AsyncAGI").tap do |e| - e["SubEvent"] = "End" - e["Channel"] = "SIP/1234-00000000" - e["CommandID"] = component.id - e["Command"] = "EXEC ANSWER" - e["Result"] = "200%20result=123%20(timeout)%0A" - end + RubyAMI::Event.new "AsyncAGI", + "SubEvent" => "End", + "Channel" => "SIP/1234-00000000", + "CommandID" => component.id, + "Command" => "EXEC ANSWER", + "Result" => "200%20result=123%20(timeout)%0A" end before do subject.register_component component end @@ -468,21 +485,20 @@ end end context 'with a Newstate event' do let :ami_event do - RubyAMI::Event.new('Newstate').tap do |e| - e['Privilege'] = 'call,all' - e['Channel'] = 'SIP/1234-00000000' - e['ChannelState'] = channel_state - e['ChannelStateDesc'] = channel_state_desc - e['CallerIDNum'] = '' - e['CallerIDName'] = '' - e['ConnectedLineNum'] = '' - e['ConnectedLineName'] = '' - e['Uniqueid'] = '1326194671.0' - end + RubyAMI::Event.new 'Newstate', + 'Privilege' => 'call,all', + 'Channel' => 'SIP/1234-00000000', + 'ChannelState' => channel_state, + 'ChannelStateDesc' => channel_state_desc, + 'CallerIDNum' => '', + 'CallerIDName' => '', + 'ConnectedLineNum' => '', + 'ConnectedLineName' => '', + 'Uniqueid' => '1326194671.0' end context 'ringing' do let(:channel_state) { '5' } let(:channel_state_desc) { 'Ringing' } @@ -518,22 +534,21 @@ end end context 'with an OriginateResponse event' do let :ami_event do - RubyAMI::Event.new('OriginateResponse').tap do |e| - e['Privilege'] = 'call,all' - e['ActionID'] = '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909' - e['Response'] = response - e['Channel'] = 'SIP/15557654321' - e['Context'] = '' - e['Exten'] = '' - e['Reason'] = '0' - e['Uniqueid'] = uniqueid - e['CallerIDNum'] = 'sip:5551234567' - e['CallerIDName'] = 'Bryan 100' - end + RubyAMI::Event.new 'OriginateResponse', + 'Privilege' => 'call,all', + 'ActionID' => '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909', + 'Response' => response, + 'Channel' => 'SIP/15557654321', + 'Context' => '', + 'Exten' => '', + 'Reason' => '0', + 'Uniqueid' => uniqueid, + 'CallerIDNum' => 'sip:5551234567', + 'CallerIDName' => 'Bryan 100' end context 'sucessful' do let(:response) { 'Success' } let(:uniqueid) { '<null>' } @@ -567,17 +582,16 @@ end end context 'with a handler registered for a matching event' do let :ami_event do - RubyAMI::Event.new('DTMF').tap do |e| - e['Digit'] = '4' - e['Start'] = 'Yes' - e['End'] = 'No' - e['Uniqueid'] = "1320842458.8" - e['Channel'] = "SIP/1234-00000000" - end + RubyAMI::Event.new 'DTMF', + 'Digit' => '4', + 'Start' => 'Yes', + 'End' => 'No', + 'Uniqueid' => "1320842458.8", + 'Channel' => "SIP/1234-00000000" end let(:response) { mock 'Response' } it 'should execute the handler' do @@ -589,49 +603,48 @@ end end context 'with a BridgeExec event' do let :ami_event do - RubyAMI::Event.new('BridgeExec').tap do |e| - e['Privilege'] = "call,all" - e['Response'] = "Success" - e['Channel1'] = "SIP/foo" - e['Channel2'] = other_channel - end + RubyAMI::Event.new 'BridgeExec', + 'Privilege' => "call,all", + 'Response' => "Success", + 'Channel1' => "SIP/foo", + 'Channel2' => other_channel end let(:other_channel) { 'SIP/5678-00000000' } context "when a join has been executed against another call" do let :other_call do - Call.new other_channel, translator + Call.new other_channel, translator, ami_client, connection end let(:other_call_id) { other_call.id } let :command do Punchblock::Command::Join.new :call_id => other_call_id end before do translator.register_call other_call command.request! + subject.wrapped_object.should_receive(:execute_agi_command).and_return code: 200 subject.execute_command command end it 'retrieves and sets success on the correct Join' do subject.process_ami_event ami_event command.response(0.5).should be == true end context "with the channel names reversed" do let :ami_event do - RubyAMI::Event.new('BridgeExec').tap do |e| - e['Privilege'] = "call,all" - e['Response'] = "Success" - e['Channel1'] = other_channel - e['Channel2'] = "SIP/foo" - end + RubyAMI::Event.new 'BridgeExec', + 'Privilege' => "call,all", + 'Response' => "Success", + 'Channel1' => other_channel, + 'Channel2' => "SIP/foo" end it 'retrieves and sets success on the correct Join' do subject.process_ami_event ami_event command.response(0.5).should be == true @@ -648,39 +661,37 @@ context 'with a Bridge event' do let(:other_channel) { 'SIP/5678-00000000' } let(:other_call_id) { 'def567' } let :other_call do - Call.new other_channel, translator + Call.new other_channel, translator, ami_client, connection end let :ami_event do - RubyAMI::Event.new('Bridge').tap do |e| - e['Privilege'] = "call,all" - e['Bridgestate'] = state - e['Bridgetype'] = "core" - e['Channel1'] = channel - e['Channel2'] = other_channel - e['Uniqueid1'] = "1319717537.11" - e['Uniqueid2'] = "1319717537.10" - e['CallerID1'] = "1234" - e['CallerID2'] = "5678" - end + RubyAMI::Event.new 'Bridge', + 'Privilege' => "call,all", + 'Bridgestate' => state, + 'Bridgetype' => "core", + 'Channel1' => channel, + 'Channel2' => other_channel, + 'Uniqueid1' => "1319717537.11", + 'Uniqueid2' => "1319717537.10", + 'CallerID1' => "1234", + 'CallerID2' => "5678" end let :switched_ami_event do - RubyAMI::Event.new('Bridge').tap do |e| - e['Privilege'] = "call,all" - e['Bridgestate'] = state - e['Bridgetype'] = "core" - e['Channel1'] = other_channel - e['Channel2'] = channel - e['Uniqueid1'] = "1319717537.11" - e['Uniqueid2'] = "1319717537.10" - e['CallerID1'] = "1234" - e['CallerID2'] = "5678" - end + RubyAMI::Event.new 'Bridge', + 'Privilege' => "call,all", + 'Bridgestate' => state, + 'Bridgetype' => "core", + 'Channel1' => other_channel, + 'Channel2' => channel, + 'Uniqueid1' => "1319717537.11", + 'Uniqueid2' => "1319717537.10", + 'CallerID1' => "1234", + 'CallerID2' => "5678" end before do translator.register_call other_call translator.should_receive(:call_for_channel).with(other_channel).and_return(other_call) @@ -732,35 +743,33 @@ context 'with an Unlink event' do let(:other_channel) { 'SIP/5678-00000000' } let(:other_call_id) { 'def567' } let :other_call do - Call.new other_channel, translator + Call.new other_channel, translator, ami_client, connection end let :ami_event do - RubyAMI::Event.new('Unlink').tap do |e| - e['Privilege'] = "call,all" - e['Channel1'] = channel - e['Channel2'] = other_channel - e['Uniqueid1'] = "1319717537.11" - e['Uniqueid2'] = "1319717537.10" - e['CallerID1'] = "1234" - e['CallerID2'] = "5678" - end + RubyAMI::Event.new 'Unlink', + 'Privilege' => "call,all", + 'Channel1' => channel, + 'Channel2' => other_channel, + 'Uniqueid1' => "1319717537.11", + 'Uniqueid2' => "1319717537.10", + 'CallerID1' => "1234", + 'CallerID2' => "5678" end let :switched_ami_event do - RubyAMI::Event.new('Unlink').tap do |e| - e['Privilege'] = "call,all" - e['Channel1'] = other_channel - e['Channel2'] = channel - e['Uniqueid1'] = "1319717537.11" - e['Uniqueid2'] = "1319717537.10" - e['CallerID1'] = "1234" - e['CallerID2'] = "5678" - end + RubyAMI::Event.new 'Unlink', + 'Privilege' => "call,all", + 'Channel1' => other_channel, + 'Channel2' => channel, + 'Uniqueid1' => "1319717537.11", + 'Uniqueid2' => "1319717537.10", + 'CallerID1' => "1234", + 'CallerID2' => "5678" end before do translator.register_call other_call translator.should_receive(:call_for_channel).with(other_channel).and_return(other_call) @@ -783,21 +792,36 @@ translator.should_receive(:handle_pb_event).with expected_unjoined subject.process_ami_event switched_ami_event end end - let :ami_event do - RubyAMI::Event.new('Foo').tap do |e| - e['Uniqueid'] = "1320842458.8" - e['Calleridnum'] = "5678" - e['Calleridname'] = "Jane Smith" - e['Cause'] = "0" - e['Cause-txt'] = "Unknown" - e['Channel'] = channel + context 'with a VarSet event' do + let :ami_event do + RubyAMI::Event.new 'VarSet', + "Privilege" => "dialplan,all", + "Channel" => "SIP/1234-00000000", + "Variable" => "foobar", + "Value" => 'abc123', + "Uniqueid" => "1326210224.0" end + + it 'makes the variable accessible on the call' do + subject.process_ami_event ami_event + subject.channel_var('foobar').should == 'abc123' + end end + let :ami_event do + RubyAMI::Event.new 'Foo', + 'Uniqueid' => "1320842458.8", + 'Calleridnum' => "5678", + 'Calleridname' => "Jane Smith", + 'Cause' => "0", + 'Cause-txt' => "Unknown", + 'Channel' => channel + end + let :expected_pb_event do Event::Asterisk::AMI::Event.new :name => 'Foo', :attributes => { :channel => channel, :uniqueid => "1320842458.8", :calleridnum => "5678", @@ -813,91 +837,65 @@ end end describe '#execute_command' do - let :expected_agi_complete_event do - Punchblock::Event::Complete.new.tap do |c| - c.reason = Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => 200, - :result => 'Success', - :data => 'FOO' - end - end - before do command.request! end context 'with an accept command' do let(:command) { Command::Accept.new } it "should send an EXEC RINGING AGI command and set the command's response" do - component = subject.execute_command command - component.internal.should be_true - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "EXEC RINGING" - agi_command.add_event expected_agi_complete_event + subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC RINGING').and_return code: 200 + subject.execute_command command command.response(0.5).should be true end end context 'with a reject command' do let(:command) { Command::Reject.new } it "with a :busy reason should send an EXEC Busy AGI command and set the command's response" do command.reason = :busy - component = subject.execute_command command - component.internal.should be_true - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "EXEC Busy" - agi_command.add_event expected_agi_complete_event + subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200 + subject.execute_command command command.response(0.5).should be true end it "with a :decline reason should send an EXEC Busy AGI command and set the command's response" do command.reason = :decline - component = subject.execute_command command - component.internal.should be_true - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "EXEC Busy" - agi_command.add_event expected_agi_complete_event + subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200 + subject.execute_command command command.response(0.5).should be true end it "with an :error reason should send an EXEC Congestion AGI command and set the command's response" do command.reason = :error - component = subject.execute_command command - component.internal.should be_true - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "EXEC Congestion" - agi_command.add_event expected_agi_complete_event + subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Congestion').and_return code: 200 + subject.execute_command command command.response(0.5).should be true end end context 'with an answer command' do let(:command) { Command::Answer.new } it "should send an ANSWER AGI command and set the command's response" do - component = subject.execute_command command - component.internal.should be_true - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "ANSWER" - agi_command.add_event expected_agi_complete_event + subject.wrapped_object.should_receive(:execute_agi_command).with('ANSWER').and_return code: 200 + subject.execute_command command command.response(0.5).should be true end end context 'with a hangup command' do let(:command) { Command::Hangup.new } it "should send a Hangup AMI command and set the command's response" do + ami_client.should_receive(:send_action).once.with('Hangup', 'Channel' => channel, 'Cause' => 16).and_return RubyAMI::Response.new subject.execute_command command - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "hangup" - ami_action.headers['Cause'].should be == 16 - ami_action << RubyAMI::Response.new command.response(0.5).should be true end end context 'with an AGI command component' do @@ -906,11 +904,10 @@ end let(:mock_action) { Translator::Asterisk::Component::Asterisk::AGICommand.new(command, subject) } it 'should create an AGI command component actor and execute it asynchronously' do - mock_action.should_receive(:internal=).never Component::Asterisk::AGICommand.should_receive(:new_link).once.with(command, subject).and_return mock_action mock_action.async.should_receive(:execute).once subject.execute_command command end end @@ -922,11 +919,10 @@ let(:mock_action) { Translator::Asterisk::Component::Output.new(command, subject) } it 'should create an Output component and execute it asynchronously' do Component::Output.should_receive(:new_link).once.with(command, subject).and_return mock_action - mock_action.should_receive(:internal=).never mock_action.async.should_receive(:execute).once subject.execute_command command end end @@ -937,11 +933,10 @@ let(:mock_action) { Translator::Asterisk::Component::Input.new(command, subject) } it 'should create an Input component and execute it asynchronously' do Component::Input.should_receive(:new_link).once.with(command, subject).and_return mock_action - mock_action.should_receive(:internal=).never mock_action.async.should_receive(:execute).once subject.execute_command command end end @@ -952,11 +947,10 @@ let(:mock_action) { Translator::Asterisk::Component::Record.new(command, subject) } it 'should create a Record component and execute it asynchronously' do Component::Record.should_receive(:new_link).once.with(command, subject).and_return mock_action - mock_action.should_receive(:internal=).never mock_action.async.should_receive(:execute).once subject.execute_command command end end @@ -1057,133 +1051,172 @@ let(:other_call_id) { "abc123" } let(:other_channel) { 'SIP/bar' } let(:other_translator) { stub('Translator::Asterisk').as_null_object } let :other_call do - Call.new other_channel, other_translator + Call.new other_channel, other_translator, ami_client, connection end let :command do Punchblock::Command::Join.new :call_id => other_call_id end it "executes the proper dialplan Bridge application" do + subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Bridge', other_channel).and_return code: 200 translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call) subject.execute_command command - agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command') - agi_command.name.should be == "EXEC Bridge" - agi_command.params_array.should be == [other_channel] end end context "with an unjoin command" do let(:other_call_id) { "abc123" } let(:other_channel) { 'SIP/bar' } let :other_call do - Call.new other_channel, translator + Call.new other_channel, translator, ami_client, connection end let :command do Punchblock::Command::Unjoin.new :call_id => other_call_id end it "executes the unjoin through redirection" do translator.should_receive(:call_with_id).with(other_call_id).and_return(nil) + + ami_client.should_receive(:send_action).once.with("Redirect", + 'Channel' => channel, + 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT, + ).and_return RubyAMI::Response.new + subject.execute_command command - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "redirect" - ami_action.headers['Channel'].should be == channel - ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT - ami_action << RubyAMI::Response.new command.response(1).should be_true end it "executes the unjoin through redirection, on the subject call and the other call" do translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call) - subject.execute_command command - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "redirect" - ami_action.headers['Channel'].should be == channel - ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT - ami_action.headers['ExtraChannel'].should be == other_channel - ami_action.headers['ExtraExten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['ExtraPriority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['ExtraContext'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT + ami_client.should_receive(:send_action).once.with("Redirect", + 'Channel' => channel, + 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT, + 'ExtraChannel' => other_channel, + 'ExtraExten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'ExtraPriority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'ExtraContext' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT + ).and_return RubyAMI::Response.new + + subject.execute_command command end it "handles redirect errors" do translator.should_receive(:call_with_id).with(other_call_id).and_return(nil) - subject.execute_command command - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "redirect" - ami_action.headers['Channel'].should be == channel - ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT - ami_action << RubyAMI::Error.new.tap { |e| e.message = 'FooBar' } + error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' } + + ami_client.should_receive(:send_action).once.with("Redirect", + 'Channel' => channel, + 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT, + ).and_raise error + + subject.execute_command command response = command.response(1) response.should be_a ProtocolError response.text.should == 'FooBar' end end end#execute_command - describe '#send_agi_action' do + describe '#execute_agi_command' do + before { stub_uuids Punchblock.new_uuid } + + let :response do + RubyAMI::Response.new 'ActionID' => "552a9d9f-46d7-45d8-a257-06fe95f48d99", + 'Message' => 'Added AGI original_command to queue' + end + it 'should send an appropriate AsyncAGI AMI action' do - pending - subject.wrapped_object.should_receive(:send_ami_action).once.with('AGI', 'Command' => 'FOO', 'Channel' => subject.channel) - subject.send_agi_action 'FOO' + Celluloid::Condition.any_instance.should_receive(:wait).and_return nil + ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'EXEC ANSWER', 'CommandID' => Punchblock.new_uuid).and_return(response) + subject.execute_agi_command 'EXEC ANSWER' end - end - describe '#send_ami_action' do - let(:component_id) { Punchblock.new_uuid } - before { stub_uuids component_id } + context 'with some parameters' do + let(:params) { [1000, 'foo'] } - it 'should send the action to the AMI client' do - action = RubyAMI::Action.new 'foo', :foo => :bar - translator.should_receive(:send_ami_action).once.with action - subject.send_ami_action 'foo', :foo => :bar + it 'should send the appropriate action' do + Celluloid::Condition.any_instance.should_receive(:wait).and_return nil + ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'WAIT FOR DIGIT "1000" "foo"', 'CommandID' => Punchblock.new_uuid).and_return(response) + subject.execute_agi_command 'WAIT FOR DIGIT', *params + end end - end - describe '#redirect_back' do - let(:other_channel) { 'SIP/bar' } - let :other_call do - Call.new other_channel, translator + context 'with an error' do + let :error do + RubyAMI::Error.new.tap { |e| e.message = 'Action failed' } end - it "executes the proper AMI action with only the subject call" do - subject.redirect_back - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "redirect" - ami_action.headers['Channel'].should be == channel - ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT + it 'should raise the error' do + ami_client.should_receive(:send_action).once.and_raise error + expect { subject.execute_agi_command 'EXEC ANSWER' }.to raise_error(RubyAMI::Error, 'Action failed') end + end - it "executes the proper AMI action with another call specified" do - subject.redirect_back other_call - ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action') - ami_action.name.should be == "redirect" - ami_action.headers['Channel'].should be == channel - ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT - ami_action.headers['ExtraChannel'].should be == other_channel - ami_action.headers['ExtraExten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION - ami_action.headers['ExtraPriority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY - ami_action.headers['ExtraContext'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT + describe 'when receiving an AsyncAGI event' do + context 'of type Exec' do + let(:ami_event) do + RubyAMI::Event.new 'AsyncAGI', + "SubEvent" => "Exec", + "Channel" => channel, + "CommandID" => Punchblock.new_uuid, + "Command" => "EXEC ANSWER", + "Result" => "200%20result=123%20(timeout)%0A" + end + + it 'should return the result' do + fut = subject.future.execute_agi_command 'EXEC ANSWER' + + subject.process_ami_event ami_event + + fut.value.should == {code: 200, result: 123, data: 'timeout'} + end end + end + end + + describe '#redirect_back' do + let(:other_channel) { 'SIP/bar' } + + let :other_call do + Call.new other_channel, translator, ami_client, connection + end + + it "executes the proper AMI action with only the subject call" do + ami_client.should_receive(:send_action).once.with 'Redirect', + 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT, + 'Channel' => channel + subject.redirect_back + end + + it "executes the proper AMI action with another call specified" do + ami_client.should_receive(:send_action).once.with 'Redirect', + 'Channel' => channel, + 'Exten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'Priority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'Context' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT, + 'ExtraChannel' => other_channel, + 'ExtraExten' => Punchblock::Translator::Asterisk::REDIRECT_EXTENSION, + 'ExtraPriority' => Punchblock::Translator::Asterisk::REDIRECT_PRIORITY, + 'ExtraContext' => Punchblock::Translator::Asterisk::REDIRECT_CONTEXT + subject.redirect_back other_call + end end end end end end