spec/punchblock/translator/asterisk/call_spec.rb in punchblock-2.6.0 vs spec/punchblock/translator/asterisk/call_spec.rb in punchblock-2.7.0

- old
+ new

@@ -514,62 +514,103 @@ context 'with an event for a known AGI command component' do let(:mock_component_node) { Punchblock::Component::Asterisk::AGI::Command.new name: 'EXEC ANSWER', params: [] } let :component do Component::Asterisk::AGICommand.new mock_component_node, subject end - - let(:ami_event) do - 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 - it 'should send the event to the component' do - expect(component).to receive(:handle_ami_event).once.with ami_event - subject.process_ami_event ami_event + context 'with Asterisk 11 AsyncAGI SubEvent' do + let(:ami_event) do + RubyAMI::Event.new "AsyncAGI", + "SubEvent" => "End", + "Channel" => "SIP/1234-00000000", + "CommandID" => component.id, + "Command" => "EXEC ANSWER", + "Result" => "200%20result=123%20(timeout)%0A" + end + + it 'should send the event to the component' do + expect(component).to receive(:handle_ami_event).once.with ami_event + subject.process_ami_event ami_event + end + + it 'should not send an answered event' do + expect(translator).to receive(:handle_pb_event).with(kind_of(Punchblock::Event::Answered)).never + subject.process_ami_event ami_event + end end - it 'should not send an answered event' do - expect(translator).to receive(:handle_pb_event).with(kind_of(Punchblock::Event::Answered)).never - subject.process_ami_event ami_event + context 'with Asterisk 13 AsyncAGIEnd and CommandId with a lowercase d' do + let(:ami_event) do + RubyAMI::Event.new "AsyncAGIEnd", + "Channel" => "SIP/1234-00000000", + "CommandId" => component.id, + "Command" => "EXEC ANSWER", + "Result" => "200%20result=123%20(timeout)%0A" + end + + it 'should send the event to the component' do + expect(component).to receive(:handle_ami_event).once.with ami_event + subject.process_ami_event ami_event + end + + it 'should not send an answered event' do + expect(translator).to receive(:handle_pb_event).with(kind_of(Punchblock::Event::Answered)).never + subject.process_ami_event ami_event + end end end + def should_send_answered_event + expected_answered = Punchblock::Event::Answered.new + expected_answered.target_call_id = subject.id + expect(translator).to receive(:handle_pb_event).with expected_answered + subject.process_ami_event ami_event + end + + def answered_should_be_true + subject.process_ami_event ami_event + expect(subject.answered?).to be_true + end + + def should_only_send_one_answered_event + expected_answered = Punchblock::Event::Answered.new + expected_answered.target_call_id = subject.id + expect(translator).to receive(:handle_pb_event).with(expected_answered).once + subject.process_ami_event ami_event + subject.process_ami_event ami_event + end + + def should_use_ami_timestamp_for_rayo_event + expected_answered = Punchblock::Event::Answered.new target_call_id: subject.id, + timestamp: DateTime.new(2014, 2, 25, 22, 46, 20) + expect(translator).to receive(:handle_pb_event).with expected_answered + + subject.process_ami_event ami_event + end + context 'with an AsyncAGI Start event' do let(:ami_event) do RubyAMI::Event.new "AsyncAGI", "SubEvent" => "Start", "Channel" => "SIP/1234-00000000", "Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A" end it 'should send an answered event' do - expected_answered = Punchblock::Event::Answered.new - expected_answered.target_call_id = subject.id - expect(translator).to receive(:handle_pb_event).with expected_answered - subject.process_ami_event ami_event + should_send_answered_event end it '#answered? should be true' do - subject.process_ami_event ami_event - expect(subject.answered?).to be_true + answered_should_be_true end context "for a second time" do it 'should only send one answered event' do - expected_answered = Punchblock::Event::Answered.new - expected_answered.target_call_id = subject.id - expect(translator).to receive(:handle_pb_event).with(expected_answered).once - subject.process_ami_event ami_event - subject.process_ami_event ami_event + should_only_send_one_answered_event end end context "when the AMI event has a timestamp" do let :ami_event do @@ -579,17 +620,48 @@ "Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A", 'Timestamp' => '1393368380.572575' end it "should use the AMI timestamp for the Rayo event" do - expected_answered = Punchblock::Event::Answered.new target_call_id: subject.id, - timestamp: DateTime.new(2014, 2, 25, 22, 46, 20) - expect(translator).to receive(:handle_pb_event).with expected_answered + should_use_ami_timestamp_for_rayo_event + end + end + end - subject.process_ami_event ami_event + context 'with an Asterisk 13 AsyncAGIStart event' do + let(:ami_event) do + RubyAMI::Event.new "AsyncAGIStart", + "Channel" => "SIP/1234-00000000", + "Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A" + end + + it 'should send an answered event' do + should_send_answered_event + end + + it '#answered? should be true' do + answered_should_be_true + end + + context "for a second time" do + it 'should only send one answered event' do + should_only_send_one_answered_event end end + + context "when the AMI event has a timestamp" do + let :ami_event do + RubyAMI::Event.new "AsyncAGIStart", + "Channel" => "SIP/1234-00000000", + "Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A", + 'Timestamp' => '1393368380.572575' + end + + it "should use the AMI timestamp for the Rayo event" do + should_use_ami_timestamp_for_rayo_event + end + end end context 'with a Newstate event' do let :ami_event do RubyAMI::Event.new 'Newstate', @@ -734,10 +806,128 @@ end subject.process_ami_event ami_event end end + context 'with a BridgeEnter event' do + let(:bridge_uniqueid) { "1234-5678" } + let(:call_channel) { "SIP/foo" } + let :ami_event do + RubyAMI::Event.new 'BridgeEnter', + 'Privilege' => "call,all", + 'BridgeUniqueid' => bridge_uniqueid, + 'Channel' => call_channel + end + + context 'when the event is received the first time' do + it 'sets an entry in translator.bridges' do + subject.process_ami_event ami_event + expect(translator.bridges[bridge_uniqueid]).to eq call_channel + end + end + + context 'when the event is received a second time for the same BridgeUniqueid' do + let(:other_channel) { 'SIP/5678-00000000' } + let :other_call do + Call.new other_channel, translator, ami_client, connection + end + let(:other_call_id) { other_call.id } + + let :command do + Punchblock::Command::Join.new call_uri: other_call_id + end + + let :ami_event do + RubyAMI::Event.new 'BridgeEnter', + 'Privilege' => "call,all", + 'BridgeUniqueid' => bridge_uniqueid, + 'Channel' => call_channel + end + + let :expected_joined do + Punchblock::Event::Joined.new target_call_id: subject.id, + call_uri: other_call_id + end + + let :expected_joined_other do + Punchblock::Event::Joined.new target_call_id: other_call_id, + call_uri: subject.id + end + + before do + translator.bridges[bridge_uniqueid] = other_channel + translator.register_call other_call + + other_call.pending_joins[channel] = command + command.request! + expect(subject).to receive(:execute_agi_command).and_return code: 200 + subject.execute_command command + end + + it 'sends the correct Joined events' do + expect(translator).to receive(:handle_pb_event).with expected_joined + expect(translator).to receive(:handle_pb_event).with expected_joined_other + subject.process_ami_event ami_event + expect(command.response(0.5)).to eq(true) + end + end + end + + context 'with a BridgeLeave event' do + let(:bridge_uniqueid) { "1234-5678" } + let(:call_channel) { "SIP/foo-1234" } + let :ami_event do + RubyAMI::Event.new 'BridgeLeave', + 'Privilege' => "call,all", + 'BridgeUniqueid' => bridge_uniqueid, + 'Channel' => call_channel + end + + context 'when the event is received the first time' do + it 'sets an entry in translator.bridges' do + subject.process_ami_event ami_event + expect(translator.bridges[bridge_uniqueid + '_leave']).to eq call_channel + end + end + + context 'when the event is received a second time for the same BridgeUniqueid' do + let(:other_channel) { 'SIP/5678-00000000' } + let :other_call do + Call.new other_channel, translator, ami_client, connection + end + let(:other_call_id) { other_call.id } + + let :ami_event do + RubyAMI::Event.new 'BridgeLeave', + 'Privilege' => "call,all", + 'BridgeUniqueid' => bridge_uniqueid, + 'Channel' => call_channel + end + + let :expected_unjoined do + Punchblock::Event::Unjoined.new target_call_id: subject.id, + call_uri: other_call_id + end + + let :expected_unjoined_other do + Punchblock::Event::Unjoined.new target_call_id: other_call_id, + call_uri: subject.id + end + + before do + translator.bridges[bridge_uniqueid + '_leave'] = other_channel + translator.register_call other_call + end + + it 'sends the correct Unjoined events' do + expect(translator).to receive(:handle_pb_event).with expected_unjoined + expect(translator).to receive(:handle_pb_event).with expected_unjoined_other + subject.process_ami_event ami_event + end + end + end + context 'with a BridgeExec event' do let :ami_event do RubyAMI::Event.new 'BridgeExec', 'Privilege' => "call,all", 'Response' => "Success", @@ -792,14 +982,14 @@ end end 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, ami_client, connection end + let(:other_call_id) { other_call.id } let :ami_event do RubyAMI::Event.new 'Bridge', 'Privilege' => "call,all", 'Bridgestate' => state, @@ -826,11 +1016,10 @@ end before do translator.register_call other_call expect(translator).to receive(:call_for_channel).with(other_channel).and_return(other_call) - expect(other_call).to receive(:id).and_return other_call_id end context "of state 'Link'" do let(:state) { 'Link' } @@ -1377,10 +1566,19 @@ it "executes the proper dialplan Bridge application" do expect(subject).to receive(:execute_agi_command).with('EXEC Bridge', "#{other_channel},F(#{REDIRECT_CONTEXT},#{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY})").and_return code: 200 subject.execute_command command end + context "when the other call doesn't exist" do + let(:other_call) { nil } + + it "returns an error" do + subject.execute_command command + expect(command.response(0.5)).to eq(ProtocolError.new.setup(:service_unavailable, "Could not find join party with address #{other_call_id}", subject.id)) + end + end + context "when the AMI command raises an error" do let(:message) { 'Some error' } let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } } before { expect(subject).to receive(:execute_agi_command).and_raise error } @@ -1756,10 +1954,11 @@ expect { subject.execute_agi_command 'EXEC ANSWER' }.to raise_error(ChannelGoneError, message) end end end + describe 'when receiving an AsyncAGI event' do context 'of type Exec' do let(:ami_event) do RubyAMI::Event.new 'AsyncAGI', "SubEvent" => "Exec", @@ -1769,14 +1968,30 @@ "Result" => "200%20result=123%20(timeout)%0A" end it 'should return the result' do fut = Celluloid::Future.new { subject.execute_agi_command 'EXEC ANSWER' } - sleep 0.25 - subject.process_ami_event ami_event + expect(fut.value).to eq({code: 200, result: 123, data: 'timeout'}) + end + end + end + describe 'when receiving an Asterisk 13 AsyncAGIExec event' do + context 'without a subtype' do + let(:ami_event) do + RubyAMI::Event.new 'AsyncAGIExec', + "Channel" => channel, + "CommandId" => Punchblock.new_uuid, + "Command" => "EXEC ANSWER", + "Result" => "200%20result=123%20(timeout)%0A" + end + + it 'should return the result' do + fut = Celluloid::Future.new { subject.execute_agi_command 'EXEC ANSWER' } + sleep 0.25 + subject.process_ami_event ami_event expect(fut.value).to eq({code: 200, result: 123, data: 'timeout'}) end end end end