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" }