spec/punchblock/translator/asterisk/call_spec.rb in punchblock-1.9.4 vs spec/punchblock/translator/asterisk/call_spec.rb in punchblock-2.0.0.beta1
- old
+ new
@@ -5,12 +5,12 @@
module Punchblock
module Translator
class Asterisk
describe Call do
let(:channel) { 'SIP/foo' }
- let(:ami_client) { stub('AMI Client').as_null_object }
- let(:connection) { stub('connection').as_null_object }
+ let(:ami_client) { double('AMI Client').as_null_object }
+ let(:connection) { double('connection').as_null_object }
let(:translator) { Asterisk.new ami_client, connection }
let(:agi_env) do
{
:agi_request => 'async',
:agi_channel => 'SIP/1234-00000000',
@@ -35,30 +35,30 @@
}
end
let :sip_headers do
{
- :x_agi_request => 'async',
- :x_agi_channel => 'SIP/1234-00000000',
- :x_agi_language => 'en',
- :x_agi_type => 'SIP',
- :x_agi_uniqueid => '1320835995.0',
- :x_agi_version => '1.8.4.1',
- :x_agi_callerid => '5678',
- :x_agi_calleridname => 'Jane Smith',
- :x_agi_callingpres => '0',
- :x_agi_callingani2 => '0',
- :x_agi_callington => '0',
- :x_agi_callingtns => '0',
- :x_agi_dnid => 'unknown',
- :x_agi_rdnis => 'unknown',
- :x_agi_context => 'default',
- :x_agi_extension => '1000',
- :x_agi_priority => '1',
- :x_agi_enhanced => '0.0',
- :x_agi_accountcode => '',
- :x_agi_threadid => '4366221312'
+ 'X-agi_request' => 'async',
+ 'X-agi_channel' => 'SIP/1234-00000000',
+ 'X-agi_language' => 'en',
+ 'X-agi_type' => 'SIP',
+ 'X-agi_uniqueid' => '1320835995.0',
+ 'X-agi_version' => '1.8.4.1',
+ 'X-agi_callerid' => '5678',
+ 'X-agi_calleridname' => 'Jane Smith',
+ 'X-agi_callingpres' => '0',
+ 'X-agi_callingani2' => '0',
+ 'X-agi_callington' => '0',
+ 'X-agi_callingtns' => '0',
+ 'X-agi_dnid' => 'unknown',
+ 'X-agi_rdnis' => 'unknown',
+ 'X-agi_context' => 'default',
+ 'X-agi_extension' => '1000',
+ 'X-agi_priority' => '1',
+ 'X-agi_enhanced' => '0.0',
+ 'X-agi_accountcode' => '',
+ 'X-agi_threadid' => '4366221312'
}
end
subject { Call.new channel, translator, ami_client, connection, agi_env }
@@ -78,11 +78,11 @@
end
describe '#register_component' do
it 'should make the component accessible by ID' do
component_id = 'abc123'
- component = mock 'Translator::Asterisk::Component', :id => component_id
+ component = double 'Translator::Asterisk::Component', :id => component_id
subject.register_component component
subject.component_with_id(component_id).should be component
end
end
@@ -294,56 +294,76 @@
let(:cause_txt) { 'Normal Clearing' }
it "should cause the actor to be terminated" do
translator.should_receive(:handle_pb_event).twice
subject.process_ami_event ami_event
- sleep 5.5
subject.should_not be_alive
end
it "de-registers the call from the translator" do
translator.stub :handle_pb_event
- translator.should_receive(:deregister_call).once.with(subject)
+ translator.should_receive(:deregister_call).once.with(subject.id, subject.channel)
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 = Punchblock::Component::Input.new :grammar => {:value => RubySpeech::GRXML.draw(root: 'foo') { rule id: 'foo' }}, :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
expected_complete_event.reason = Punchblock::Event::Complete::Hangup.new
- expected_end_event = Punchblock::Event::End.new :reason => :hangup, :target_call_id => subject.id
+ expected_end_event = Punchblock::Event::End.new :reason => :hangup, platform_code: cause, :target_call_id => subject.id
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
+ call_id = subject.id
+
subject.stub :send_progress
- comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf
+ comp_command = Punchblock::Component::Input.new :grammar => {:value => RubySpeech::GRXML.draw(root: 'foo') { rule id: 'foo' }}, :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 = Punchblock::Component::Input.new :grammar => {:value => '<grammar root="foo"><rule id="foo"/></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)
+ comp_command.response(0.1).should == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{call_id}", call_id)
end
+ context "after processing a hangup command" do
+ let(:command) { Command::Hangup.new }
+
+ before do
+ command.request!
+ subject.execute_command command
+ end
+
+ it 'should send an end (hangup_command) event to the translator' do
+ expected_end_event = Punchblock::Event::End.new :reason => :hangup_command,
+ platform_code: cause,
+ :target_call_id => subject.id
+ translator.should_receive(:handle_pb_event).with expected_end_event
+
+ subject.process_ami_event ami_event
+ end
+ 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
expected_end_event = Punchblock::Event::End.new :reason => :hangup,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
@@ -352,10 +372,11 @@
let(:cause) { '16' }
let(:cause_txt) { 'Normal Clearing' }
it 'should send an end (hangup) event to the translator' do
expected_end_event = Punchblock::Event::End.new :reason => :hangup,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
@@ -364,10 +385,11 @@
let(:cause) { '17' }
let(:cause_txt) { 'User Busy' }
it 'should send an end (busy) event to the translator' do
expected_end_event = Punchblock::Event::End.new :reason => :busy,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
@@ -380,10 +402,11 @@
let(:cause) { cause.to_s }
let(:cause_txt) { cause_txt }
it 'should send an end (timeout) event to the translator' do
expected_end_event = Punchblock::Event::End.new :reason => :timeout,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
@@ -398,10 +421,11 @@
let(:cause) { cause.to_s }
let(:cause_txt) { cause_txt }
it 'should send an end (reject) event to the translator' do
expected_end_event = Punchblock::Event::End.new :reason => :reject,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
@@ -450,20 +474,21 @@
let(:cause) { cause.to_s }
let(:cause_txt) { cause_txt }
it 'should send an end (error) event to the translator' do
expected_end_event = Punchblock::Event::End.new :reason => :error,
+ platform_code: cause,
:target_call_id => subject.id
translator.should_receive(:handle_pb_event).with expected_end_event
subject.process_ami_event ami_event
end
end
end
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(: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
@@ -590,11 +615,11 @@
'End' => 'No',
'Uniqueid' => "1320842458.8",
'Channel' => "SIP/1234-00000000"
end
- let(:response) { mock 'Response' }
+ let(:response) { double 'Response' }
it 'should execute the handler' do
response.should_receive(:call).once.with ami_event
subject.register_handler :ami, :name => 'DTMF' do |event|
response.call event
@@ -619,11 +644,11 @@
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
+ Punchblock::Command::Join.new call_uri: other_call_id
end
before do
translator.register_call other_call
command.request!
@@ -700,14 +725,12 @@
context "of state 'Link'" do
let(:state) { 'Link' }
let :expected_joined do
- Punchblock::Event::Joined.new.tap do |joined|
- joined.target_call_id = subject.id
- joined.call_id = other_call_id
- end
+ Punchblock::Event::Joined.new target_call_id: subject.id,
+ call_uri: other_call_id
end
it 'sends the Joined event when the call is the first channel' do
translator.should_receive(:handle_pb_event).with expected_joined
subject.process_ami_event ami_event
@@ -721,14 +744,12 @@
context "of state 'Unlink'" do
let(:state) { 'Unlink' }
let :expected_unjoined do
- Punchblock::Event::Unjoined.new.tap do |joined|
- joined.target_call_id = subject.id
- joined.call_id = other_call_id
- end
+ Punchblock::Event::Unjoined.new target_call_id: subject.id,
+ call_uri: other_call_id
end
it 'sends the Unjoined event when the call is the first channel' do
translator.should_receive(:handle_pb_event).with expected_unjoined
subject.process_ami_event ami_event
@@ -775,14 +796,12 @@
translator.should_receive(:call_for_channel).with(other_channel).and_return(other_call)
other_call.should_receive(:id).and_return other_call_id
end
let :expected_unjoined do
- Punchblock::Event::Unjoined.new.tap do |joined|
- joined.target_call_id = subject.id
- joined.call_id = other_call_id
- end
+ Punchblock::Event::Unjoined.new target_call_id: subject.id,
+ call_uri: other_call_id
end
it 'sends the Unjoined event when the call is the first channel' do
translator.should_receive(:handle_pb_event).with expected_unjoined
subject.process_ami_event ami_event
@@ -819,18 +838,18 @@
'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",
- :calleridname => "Jane Smith",
- :cause => "0",
- :'cause-txt' => "Unknown"},
- :target_call_id => subject.id
+ Event::Asterisk::AMI::Event.new name: 'Foo',
+ headers: { 'Channel' => channel,
+ 'Uniqueid' => "1320842458.8",
+ 'Calleridnum' => "5678",
+ 'Calleridname' => "Jane Smith",
+ 'Cause' => "0",
+ 'Cause-txt' => "Unknown"},
+ target_call_id: subject.id
end
it 'sends the AMI event to the connection as a PB event' do
translator.should_receive(:handle_pb_event).with expected_pb_event
subject.process_ami_event ami_event
@@ -861,12 +880,12 @@
it "should return an error with the message" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
end
- context "with message 'No such channel'" do
- let(:message) { 'No such channel' }
+ context "because the channel is gone" do
+ let(:error) { ChannelGoneError }
it "should return an :item_not_found event for the call" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
end
@@ -882,13 +901,13 @@
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
+ it "with a :decline reason should send a Hangup AMI command (cause 21) and set the command's response" do
command.reason = :decline
- subject.wrapped_object.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200
+ ami_client.should_receive(:send_action).once.with('Hangup', 'Channel' => channel, 'Cause' => 21).and_return RubyAMI::Response.new
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
@@ -907,12 +926,12 @@
it "should return an error with the message" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
end
- context "with message 'No such channel'" do
- let(:message) { 'No such channel' }
+ context "because the channel is gone" do
+ let(:error) { ChannelGoneError }
it "should return an :item_not_found event for the call" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
end
@@ -933,11 +952,11 @@
subject.wrapped_object.should_receive(:execute_agi_command)
subject.execute_command command
subject.should be_answered
end
- context "when the AMI commannd raises an error" do
+ context "when the AMI command raises an error" do
let(:message) { 'Some error' }
let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
before { subject.wrapped_object.should_receive(:execute_agi_command).and_raise error }
@@ -949,12 +968,12 @@
it "should not be answered" do
subject.execute_command command
subject.should_not be_answered
end
- context "with message 'No such channel'" do
- let(:message) { 'No such channel' }
+ context "because the channel is gone" do
+ let(:error) { ChannelGoneError }
it "should return an :item_not_found event for the call" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
end
@@ -980,21 +999,155 @@
it "should return an error with the message" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
end
- context "with message 'No such channel'" do
+ context "which is 'No such channel'" do
let(:message) { 'No such channel' }
it "should return an :item_not_found event for the call" do
subject.execute_command command
command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
end
end
+
+ context "which is 'Channel SIP/nosuchchannel does not exist.'" do
+ let(:message) { 'Channel SIP/nosuchchannel does not exist.' }
+
+ it "should return an :item_not_found event for the call" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
+ end
+ end
end
end
+ context "with a join command" do
+ let(:other_call_id) { "abc123" }
+ let(:other_channel) { 'SIP/bar' }
+ let(:other_translator) { double('Translator::Asterisk').as_null_object }
+
+ let :other_call do
+ Call.new other_channel, other_translator, ami_client, connection
+ end
+
+ let :command do
+ Punchblock::Command::Join.new call_uri: other_call_id
+ end
+
+ before { translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call) }
+
+ 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
+ subject.execute_command command
+ 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 { subject.wrapped_object.should_receive(:execute_agi_command).and_raise error }
+
+ it "should return an error with the message" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
+ end
+
+ it "should not be answered" do
+ subject.execute_command command
+ subject.should_not be_answered
+ end
+
+ context "because the channel is gone" do
+ let(:error) { ChannelGoneError }
+
+ it "should return an :item_not_found event for the call" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
+ end
+ end
+ 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, ami_client, connection
+ end
+
+ let :command do
+ Punchblock::Command::Unjoin.new call_uri: 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
+
+ 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)
+
+ 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
+
+ context "when the AMI commannd raises an error" do
+ let(:message) { 'Some error' }
+ let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
+
+ before do
+ translator.should_receive(:call_with_id).with(other_call_id).and_return(nil)
+ ami_client.should_receive(:send_action).and_raise error
+ end
+
+ it "should return an error with the message" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
+ end
+
+ context "which is 'No such channel'" do
+ let(:message) { 'No such channel' }
+
+ it "should return an :item_not_found event for the call" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
+ end
+ end
+
+ context "which is 'Channel SIP/nosuchchannel does not exist.'" do
+ let(:message) { 'Channel SIP/nosuchchannel does not exist.' }
+
+ it "should return an :item_not_found event for the call" do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
+ end
+ end
+ end
+ end
+
context 'with an AGI command component' do
let :command do
Punchblock::Component::Asterisk::AGI::Command.new :name => 'Answer'
end
@@ -1033,10 +1186,85 @@
mock_action.async.should_receive(:execute).once
subject.execute_command command
end
end
+ context 'with a Prompt component' do
+ def grxml_doc(mode = :dtmf)
+ RubySpeech::GRXML.draw :mode => mode.to_s, :root => 'digits' do
+ rule id: 'digits' do
+ one_of do
+ 0.upto(1) { |d| item { d.to_s } }
+ end
+ end
+ end
+ end
+
+ let :command do
+ Punchblock::Component::Prompt.new(
+ {
+ render_document: {
+ content_type: 'text/uri-list',
+ value: ['http://example.com/hello.mp3']
+ },
+ renderer: renderer
+ },
+ {
+ grammar: {
+ value: grxml_doc,
+ content_type: 'application/srgs+xml'
+ },
+ recognizer: recognizer
+ })
+ end
+
+ let(:mock_action) { Translator::Asterisk::Component::MRCPPrompt.new(command, subject) }
+
+ context "when the recognizer is unimrcp and the renderer is unimrcp" do
+ let(:recognizer) { :unimrcp }
+ let(:renderer) { :unimrcp }
+
+ it 'should create an MRCPPrompt component and execute it asynchronously' do
+ Component::MRCPPrompt.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
+
+ context "when the recognizer is unimrcp and the renderer is asterisk" do
+ let(:recognizer) { :unimrcp }
+ let(:renderer) { :asterisk }
+
+ it 'should create an MRCPPrompt component and execute it asynchronously' do
+ Component::MRCPNativePrompt.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
+
+ context "when the recognizer is unimrcp and the renderer is something we can't compose with unimrcp" do
+ let(:recognizer) { :unimrcp }
+ let(:renderer) { :swift }
+
+ it 'should return an error' do
+ subject.execute_command command
+ command.response(0.5).should be == ProtocolError.new.setup(:invalid_command, "Invalid recognizer/renderer combination", subject.id)
+ end
+ end
+
+ context "when the recognizer is something other than unimrcp" do
+ let(:recognizer) { :asterisk }
+ let(:renderer) { :unimrcp }
+
+ it 'should create a ComposedPrompt component and execute it asynchronously' do
+ Component::ComposedPrompt.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
+ end
+
context 'with a Record component' do
let :command do
Punchblock::Component::Record.new
end
@@ -1055,11 +1283,11 @@
let :command do
Punchblock::Component::Stop.new :component_id => component_id
end
let :mock_component do
- mock 'Component', :id => component_id
+ double 'Component', :id => component_id
end
context "for a known component ID" do
before { subject.register_component mock_component }
@@ -1072,20 +1300,18 @@
context "for a component which began executing but crashed" do
let :component_command do
Punchblock::Component::Asterisk::AGI::Command.new :name => 'Wait'
end
- let(:comp_id) { component_command.response.id }
+ let(:comp_id) { component_command.response.component_id }
let(:subsequent_command) { Punchblock::Component::Stop.new :component_id => comp_id }
let :expected_event do
- Punchblock::Event::Complete.new.tap do |e|
- e.target_call_id = subject.id
- e.component_id = comp_id
- e.reason = Punchblock::Event::Complete::Error.new
- end
+ Punchblock::Event::Complete.new target_call_id: subject.id,
+ component_id: comp_id,
+ reason: Punchblock::Event::Complete::Error.new
end
before do
component_command.request!
subject.execute_command component_command
@@ -1139,93 +1365,10 @@
it 'sends an error in response to the command' do
subject.execute_command command
command.response.should be == ProtocolError.new.setup('command-not-acceptable', "Did not understand command for call #{subject.id}", subject.id)
end
end
-
- context "with a join command" do
- 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, 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
- 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, 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
-
- 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)
-
- 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)
-
- 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 '#execute_agi_command' do
before { stub_uuids Punchblock.new_uuid }
@@ -1249,16 +1392,36 @@
subject.execute_agi_command 'WAIT FOR DIGIT', *params
end
end
context 'with an error' do
+ let(:message) { 'Action failed' }
+
let :error do
- RubyAMI::Error.new.tap { |e| e.message = 'Action failed' }
+ RubyAMI::Error.new.tap { |e| e.message = message }
end
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
+
+ context "which is 'No such channel'" do
+ let(:message) { 'No such channel' }
+
+ it 'should raise ChannelGoneError' do
+ ami_client.should_receive(:send_action).once.and_raise error
+ expect { subject.execute_agi_command 'EXEC ANSWER' }.to raise_error(ChannelGoneError, message)
+ end
+ end
+
+ context "which is 'Channel SIP/nosuchchannel does not exist.'" do
+ let(:message) { 'Channel SIP/nosuchchannel does not exist.' }
+
+ it 'should raise ChannelGoneError' do
+ ami_client.should_receive(:send_action).once.and_raise error
+ 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