spec/punchblock/translator/asterisk/call_spec.rb in punchblock-1.8.0 vs spec/punchblock/translator/asterisk/call_spec.rb in punchblock-1.8.1

- old
+ new

@@ -5,11 +5,11 @@ module Punchblock module Translator class Asterisk describe Call do let(:channel) { 'SIP/foo' } - let(:translator) { stub('Translator::Asterisk').as_null_object } + let(:translator) { Asterisk.new stub('AMI Client').as_null_object, stub('connection').as_null_object } let(:agi_env) do { :agi_request => 'async', :agi_channel => 'SIP/1234-00000000', :agi_language => 'en', @@ -63,10 +63,12 @@ its(:id) { should be_a String } its(:channel) { should be == channel } its(:translator) { should be translator } its(:agi_env) { should be == agi_env } + before { translator.stub :handle_pb_event } + describe '#shutdown' do it 'should terminate the actor' do subject.shutdown sleep 0.5 subject.should_not be_alive @@ -168,11 +170,11 @@ :channel => 'SIP/1234', :callerid => 'sip:foo@bar.com', :variable => "punchblock_call_id=#{subject.id}" }).tap { |a| a.request! } - translator.should_receive(:execute_global_command!).once.with expected_action + translator.async.should_receive(:execute_global_command).once.with expected_action subject.dial dial_command end context 'with a name and channel in the to field' do let(:to) { 'Jane Smith <SIP/5678>' } @@ -186,11 +188,11 @@ :channel => 'SIP/5678', :callerid => 'sip:foo@bar.com', :variable => "punchblock_call_id=#{subject.id}" }).tap { |a| a.request! } - translator.should_receive(:execute_global_command!).once.with expected_action + translator.async.should_receive(:execute_global_command).once.with expected_action subject.dial dial_command end end context 'with a timeout specified' do @@ -208,11 +210,11 @@ :callerid => 'sip:foo@bar.com', :variable => "punchblock_call_id=#{subject.id}", :timeout => 10000 }).tap { |a| a.request! } - translator.should_receive(:execute_global_command!).once.with expected_action + translator.async.should_receive(:execute_global_command).once.with expected_action subject.dial dial_command end end context 'with headers specified' do @@ -229,11 +231,11 @@ :channel => 'SIP/1234', :callerid => 'sip:foo@bar.com', :variable => "punchblock_call_id=#{subject.id},SIPADDHEADER51=\"X-foo: bar\",SIPADDHEADER52=\"X-doo: dah\"" }).tap { |a| a.request! } - translator.should_receive(:execute_global_command!).once.with expected_action + translator.async.should_receive(:execute_global_command).once.with expected_action subject.dial dial_command end end it 'sends the call ID as a response to the Dial' do @@ -300,10 +302,24 @@ translator.should_receive(:handle_pb_event).with(expected_complete_event).once.ordered 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 + 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 + + subject.async.process_ami_event ami_event + + comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf + comp_command.request! + subject.execute_command comp_command + comp_command.response(0.1).should == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id) + end + context "with an undefined cause" do let(:cause) { '0' } let(:cause_txt) { 'Undefined' } it 'should send an end (hangup) event to the translator' do @@ -427,11 +443,11 @@ end context 'with an event for a known AGI command component' do let(:mock_component_node) { mock 'Punchblock::Component::Asterisk::AGI::Command', :name => 'EXEC ANSWER', :params_array => [] } let :component do - Component::Asterisk::AGICommand.new mock_component_node, subject.translator + Component::Asterisk::AGICommand.new mock_component_node, subject end let(:ami_event) do RubyAMI::Event.new("AsyncAGI").tap do |e| e["SubEvent"] = "End" @@ -577,28 +593,58 @@ let :ami_event do RubyAMI::Event.new('BridgeExec').tap do |e| e['Privilege'] = "call,all" e['Response'] = "Success" e['Channel1'] = "SIP/foo" - e['Channel2'] = "SIP/5678-00000000" + e['Channel2'] = other_channel end end let(:other_channel) { 'SIP/5678-00000000' } - let(:other_call_id) { 'def567' } - let :command do - Punchblock::Command::Join.new :call_id => other_call_id - end - before do - subject.pending_joins[other_channel] = command - command.request! + context "when a join has been executed against another call" do + let :other_call do + Call.new other_channel, translator + 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.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 + 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 + end end - it 'retrieves and sets success on the correct Join' do - subject.process_ami_event ami_event - command.response(0.5).should be == true + context "with no matching join command" do + it "should do nothing" do + expect { subject.process_ami_event ami_event }.not_to raise_error + end end end context 'with a Bridge event' do let(:other_channel) { 'SIP/5678-00000000' } @@ -857,61 +903,61 @@ context 'with an AGI command component' do let :command do Punchblock::Component::Asterisk::AGI::Command.new :name => 'Answer' end - let(:mock_action) { mock 'Component::Asterisk::AGI::Command', :id => 'foo' } + 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.should_receive(:execute!).once + mock_action.async.should_receive(:execute).once subject.execute_command command end end context 'with an Output component' do let :command do Punchblock::Component::Output.new end - let(:mock_action) { mock 'Component::Asterisk::Output', :id => 'foo' } + 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.should_receive(:execute!).once + mock_action.async.should_receive(:execute).once subject.execute_command command end end context 'with an Input component' do let :command do Punchblock::Component::Input.new end - let(:mock_action) { mock 'Component::Asterisk::Input', :id => 'foo' } + 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.should_receive(:execute!).once + mock_action.async.should_receive(:execute).once subject.execute_command command end end context 'with a Record component' do let :command do Punchblock::Component::Record.new end - let(:mock_action) { mock 'Component::Asterisk::Record', :id => 'foo' } + 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.should_receive(:execute!).once + mock_action.async.should_receive(:execute).once subject.execute_command command end end context 'with a component command' do @@ -972,10 +1018,22 @@ subsequent_command.request! subject.execute_command subsequent_command subsequent_command.response.should be == ProtocolError.new.setup(:item_not_found, "Could not find a component with ID #{comp_id} for call #{subject.id}", subject.id, comp_id) end + + context "when we dispatch the command to it" do + it 'sends an error in response to the command' do + component = subject.component_with_id comp_id + + component.should_receive(:execute_command).and_raise(Celluloid::DeadActorError) + + subsequent_command.request! + subject.execute_command subsequent_command + subsequent_command.response.should be == ProtocolError.new.setup(:item_not_found, "Could not find a component with ID #{comp_id} for call #{subject.id}", subject.id, comp_id) + end + end end context "for an unknown component ID" do it 'sends an error in response to the command' do subject.execute_command command @@ -1012,15 +1070,9 @@ 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 - - it "adds the join to the @pending_joins hash" do - translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call) - subject.execute_command command - subject.pending_joins[other_channel].should be command end end context "with an unjoin command" do let(:other_call_id) { "abc123" }