spec/punchblock/translator/freeswitch/call_spec.rb in punchblock-1.6.1 vs spec/punchblock/translator/freeswitch/call_spec.rb in punchblock-1.7.0

- old
+ new

@@ -5,14 +5,14 @@ module Punchblock module Translator class Freeswitch describe Call do let(:id) { Punchblock.new_uuid } - let(:stream) { stub_everything 'RubyFS::Stream' } - let(:media_engine) { :freeswitch } + let(:stream) { stub('RubyFS::Stream').as_null_object } + let(:media_engine) { 'freeswitch' } let(:default_voice) { :hal } - let(:translator) { Freeswitch.new stub_everything('Connection::Freeswitch') } + let(:translator) { Freeswitch.new stub('Connection::Freeswitch').as_null_object } let(:es_env) do { :variable_direction => "inbound", :variable_uuid => "3f0e1e18-c056-11e1-b099-fffeda3ce54f", :variable_session_id => "1", @@ -186,11 +186,11 @@ it 'sends an offer to the translator' do expected_offer = Punchblock::Event::Offer.new :target_call_id => subject.id, :to => "10@127.0.0.1", :from => "Extension 1000 <1000@127.0.0.1>", :headers => headers - translator.expects(:handle_pb_event).with expected_offer + translator.should_receive(:handle_pb_event).with expected_offer subject.send_offer end it 'should make the call identify as inbound' do subject.send_offer @@ -200,25 +200,25 @@ end end describe "#application" do it "should execute a FS application on the current call" do - stream.expects(:application).once.with(id, 'appname', 'options') + stream.should_receive(:application).once.with(id, 'appname', 'options') subject.application 'appname', 'options' end end describe "#sendmsg" do it "should execute a FS sendmsg on the current call" do - stream.expects(:sendmsg).once.with(id, 'msg', :foo => 'bar') + stream.should_receive(:sendmsg).once.with(id, 'msg', :foo => 'bar') subject.sendmsg 'msg', :foo => 'bar' end end describe "#uuid_foo" do it "should execute a FS uuid_* on the current call using bgapi" do - stream.expects(:bgapi).once.with("uuid_record #{id} blah.mp3") + stream.should_receive(:bgapi).once.with("uuid_record #{id} blah.mp3") subject.uuid_foo 'record', 'blah.mp3' end end describe '#dial' do @@ -232,73 +232,73 @@ end before { dial_command.request! } it 'sends an originate bgapi command' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}'}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}'}#{to} &park()" subject.dial dial_command end context 'with a name and channel in the from field' do let(:from_name) { 'Jane Smith' } let(:from_number) { '1001' } let(:from) { "#{from_name} <#{from_number}>" } it 'sends an originate bgapi command with the cid fields set correctly' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from_number}',origination_caller_id_name='#{from_name}'}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from_number}',origination_caller_id_name='#{from_name}'}#{to} &park()" subject.dial dial_command end end context 'with a name and empty channel in the from field' do let(:from_name) { 'Jane Smith' } let(:from_number) { '' } let(:from) { "#{from_name} <#{from_number}>" } it 'sends an originate bgapi command with the cid fields set correctly' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_name='#{from_name}'}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_name='#{from_name}'}#{to} &park()" subject.dial dial_command end end context 'with a number in the from field with angled brackets' do let(:from_number) { '1001' } let(:from) { "<#{from_number}>" } it 'sends an originate bgapi command with the cid fields set correctly' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from_number}'}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from_number}'}#{to} &park()" subject.dial dial_command end end context 'with an empty from attribute' do let(:from) { '' } it 'sends an originate bgapi command with the cid fields set correctly' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id}}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id}}#{to} &park()" subject.dial dial_command end end context 'with a timeout specified' do let :dial_command_options do { :timeout => 10000 } end it 'includes the timeout in the originate command' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}',originate_timeout=10}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}',originate_timeout=10}#{to} &park()" subject.dial dial_command end end context 'with headers specified' do let :dial_command_options do { :headers => {'X-foo' => 'bar', 'X-doo' => 'dah'} } end it 'includes the headers in the originate command' do - stream.expects(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}',sip_h_X-foo='bar',sip_h_X-doo='dah'}#{to} &park()" + stream.should_receive(:bgapi).once.with "originate {return_ring_ready=true,origination_uuid=#{subject.id},origination_caller_id_number='#{from}',sip_h_X-foo='bar',sip_h_X-doo='dah'}#{to} &park()" subject.dial dial_command end end it 'sends the call ID as a response to the Dial' do @@ -331,19 +331,19 @@ end let(:cause) { 'ORIGINATOR_CANCEL' } it "should cause the actor to be terminated" do - translator.expects(:handle_pb_event).once + translator.should_receive(:handle_pb_event).once subject.handle_es_event es_event sleep 5.5 subject.should_not be_alive end it "de-registers the call from the translator" do - translator.stubs :handle_pb_event - translator.expects(:deregister_call).once.with(subject) + translator.stub :handle_pb_event + translator.should_receive(:deregister_call).once.with(subject) subject.handle_es_event es_event end it "should cause all components to send complete events before sending end event" do ssml_doc = RubySpeech::SSML.draw { audio { 'foo.wav' } } @@ -354,13 +354,12 @@ 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 - end_sequence = sequence 'end events' - translator.expects(:handle_pb_event).with(expected_complete_event).once.in_sequence(end_sequence) - translator.expects(:handle_pb_event).with(expected_end_event).once.in_sequence(end_sequence) + 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.handle_es_event es_event end [ 'NORMAL_CLEARING', @@ -376,11 +375,11 @@ let(:cause) { cause } it 'should send an end (hangup) event to the translator' do expected_end_event = Punchblock::Event::End.new :reason => :hangup, :target_call_id => subject.id - translator.expects(:handle_pb_event).with expected_end_event + translator.should_receive(:handle_pb_event).with expected_end_event subject.handle_es_event es_event end end end @@ -388,11 +387,11 @@ let(:cause) { 'USER_BUSY' } it 'should send an end (busy) event to the translator' do expected_end_event = Punchblock::Event::End.new :reason => :busy, :target_call_id => subject.id - translator.expects(:handle_pb_event).with expected_end_event + translator.should_receive(:handle_pb_event).with expected_end_event subject.handle_es_event es_event end end [ @@ -407,11 +406,11 @@ let(:cause) { cause } it 'should send an end (timeout) event to the translator' do expected_end_event = Punchblock::Event::End.new :reason => :timeout, :target_call_id => subject.id - translator.expects(:handle_pb_event).with expected_end_event + translator.should_receive(:handle_pb_event).with expected_end_event subject.handle_es_event es_event end end end @@ -438,11 +437,11 @@ let(:cause) { cause } it 'should send an end (reject) event to the translator' do expected_end_event = Punchblock::Event::End.new :reason => :reject, :target_call_id => subject.id - translator.expects(:handle_pb_event).with expected_end_event + translator.should_receive(:handle_pb_event).with expected_end_event subject.handle_es_event es_event end end end @@ -482,11 +481,11 @@ let(:cause) { cause } it 'should send an end (error) event to the translator' do expected_end_event = Punchblock::Event::End.new :reason => :error, :target_call_id => subject.id - translator.expects(:handle_pb_event).with expected_end_event + translator.should_receive(:handle_pb_event).with expected_end_event subject.handle_es_event es_event end end end end @@ -504,11 +503,11 @@ before do subject.register_component component end it 'should send the event to the component' do - component.expects(:handle_es_event).once.with es_event + component.should_receive(:handle_es_event).once.with es_event subject.handle_es_event es_event end end context 'with a CHANNEL_STATE event' do @@ -523,11 +522,11 @@ let(:channel_call_state) { 'RINGING' } it 'should send a ringing event' do expected_ringing = Punchblock::Event::Ringing.new expected_ringing.target_call_id = subject.id - translator.expects(:handle_pb_event).with expected_ringing + translator.should_receive(:handle_pb_event).with expected_ringing subject.handle_es_event es_event end it '#answered? should return false' do subject.handle_es_event es_event @@ -537,11 +536,11 @@ context 'something else' do let(:channel_call_state) { 'FOO' } it 'should not send a ringing event' do - translator.expects(:handle_pb_event).never + translator.should_receive(:handle_pb_event).never subject.handle_es_event es_event end it '#answered? should return false' do subject.handle_es_event es_event @@ -556,11 +555,11 @@ end it 'should send an answered event' do expected_answered = Punchblock::Event::Answered.new expected_answered.target_call_id = subject.id - translator.expects(:handle_pb_event).with expected_answered + translator.should_receive(:handle_pb_event).with expected_answered subject.handle_es_event es_event end it '#answered? should be true' do subject.handle_es_event es_event @@ -574,11 +573,11 @@ end let(:response) { mock 'Response' } it 'should execute the handler' do - response.expects(:call).once.with es_event + response.should_receive(:call).once.with es_event subject.register_handler :es, :event_name => 'DTMF' do |event| response.call event end subject.handle_es_event es_event end @@ -602,11 +601,11 @@ :other_leg_unique_id => other_call_id } end it "should send a joined event with the correct call ID" do - translator.expects(:handle_pb_event).with expected_joined + translator.should_receive(:handle_pb_event).with expected_joined subject.handle_es_event bridge_event end end context "where this is the joined call" do @@ -617,11 +616,11 @@ :other_leg_unique_id => id } end it "should send a joined event with the correct call ID" do - translator.expects(:handle_pb_event).with expected_joined + translator.should_receive(:handle_pb_event).with expected_joined subject.handle_es_event bridge_event end end end @@ -643,11 +642,11 @@ :other_leg_unique_id => other_call_id } end it "should send a unjoined event with the correct call ID" do - translator.expects(:handle_pb_event).with expected_unjoined + translator.should_receive(:handle_pb_event).with expected_unjoined subject.handle_es_event unbridge_event end end context "where this is the joined call" do @@ -658,11 +657,11 @@ :other_leg_unique_id => id } end it "should send a unjoined event with the correct call ID" do - translator.expects(:handle_pb_event).with expected_unjoined + translator.should_receive(:handle_pb_event).with expected_unjoined subject.handle_es_event unbridge_event end end end end @@ -674,33 +673,45 @@ context 'with an accept command' do let(:command) { Command::Accept.new } it "should send a respond 180 command and set the command's response" do - subject.wrapped_object.expects(:application).once.with('respond', '180 Ringing').yields(true) + subject.wrapped_object.should_receive(:application).once.with('respond', '180 Ringing') 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 execute the answer application and set the command's response" do subject - Punchblock.expects(:new_uuid).once.returns 'abc123' - subject.wrapped_object.expects(:application).once.with('answer', "%[punchblock_command_id=abc123]") + Punchblock.should_receive(:new_uuid).once.and_return 'abc123' + subject.wrapped_object.should_receive(:application).once.with('answer', "%[punchblock_command_id=abc123]") subject.should_not be_answered subject.execute_command command subject.handle_es_event RubyFS::Event.new(nil, :event_name => 'CHANNEL_ANSWER', :scope_variable_punchblock_command_id => 'abc123') command.response(0.5).should be true subject.should be_answered end + + it "should not execute the answer application twice if already answered" do + subject + Punchblock.should_receive(:new_uuid).once.and_return 'abc123' + subject.wrapped_object.should_receive(:application).once.with('answer', "%[punchblock_command_id=abc123]") + subject.should_not be_answered + subject.execute_command command + subject.handle_es_event RubyFS::Event.new(nil, :event_name => 'CHANNEL_ANSWER', :scope_variable_punchblock_command_id => 'abc123') + command.response(0.5).should be true + subject.should be_answered + subject.execute_command command + end end def expect_hangup_with_reason(reason) - subject.wrapped_object.expects(:sendmsg).once.with(:call_command => 'hangup', :hangup_cause => reason).yields(true) + subject.wrapped_object.should_receive(:sendmsg).once.with(:call_command => 'hangup', :hangup_cause => reason) end context 'with a hangup command' do let(:command) { Command::Hangup.new } @@ -742,60 +753,75 @@ end let(:mock_component) { mock 'Freeswitch::Component::Output', :id => 'foo' } it 'should create an Output component and execute it asynchronously' do - Component::Output.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once + Component::Output.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once subject.execute_command command subject.component_with_id('foo').should be mock_component end context 'with the media engine of :flite' do let(:media_engine) { :flite } it 'should create a FliteOutput component and execute it asynchronously using flite and the calls default voice' do - Component::FliteOutput.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once.with(media_engine, default_voice) + Component::FliteOutput.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once.with(media_engine, default_voice) subject.execute_command command subject.component_with_id('foo').should be mock_component end end context 'with the media engine of :cepstral' do let(:media_engine) { :cepstral } it 'should create a TTSOutput component and execute it asynchronously using cepstral and the calls default voice' do - Component::TTSOutput.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once.with(media_engine, default_voice) + Component::TTSOutput.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once.with(media_engine, default_voice) subject.execute_command command subject.component_with_id('foo').should be mock_component end end context 'with the media engine of :unimrcp' do let(:media_engine) { :unimrcp } it 'should create a TTSOutput component and execute it asynchronously using unimrcp and the calls default voice' do - Component::TTSOutput.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once.with(media_engine, default_voice) + Component::TTSOutput.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once.with(media_engine, default_voice) subject.execute_command command subject.component_with_id('foo').should be mock_component end end + + context "with a media renderer set on the component" do + let(:media_engine) { :cepstral } + let(:media_renderer) { :native } + let :command_with_renderer do + Punchblock::Component::Output.new :renderer => media_renderer + end + + it "should use the component media engine and not the platform one if it is set" do + Component::Output.should_receive(:new_link).once.with(command_with_renderer, subject).and_return mock_component + mock_component.should_receive(:execute!).once + subject.execute_command command_with_renderer + subject.component_with_id('foo').should be mock_component + end + end end context 'with an Input component' do let :command do Punchblock::Component::Input.new end let(:mock_component) { mock 'Freeswitch::Component::Input', :id => 'foo' } it 'should create an Input component and execute it asynchronously' do - Component::Input.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once + Component::Input.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once subject.execute_command command end end context 'with a Record component' do @@ -804,12 +830,12 @@ end let(:mock_component) { mock 'Freeswitch::Component::Record', :id => 'foo' } it 'should create a Record component and execute it asynchronously' do - Component::Record.expects(:new_link).once.with(command, subject).returns mock_component - mock_component.expects(:execute!).once + Component::Record.should_receive(:new_link).once.with(command, subject).and_return mock_component + mock_component.should_receive(:execute!).once subject.execute_command command end end context 'with a component command' do @@ -825,11 +851,11 @@ context "for a known component ID" do before { subject.register_component mock_component } it 'should send the command to the component for execution' do - mock_component.expects(:execute_command).once + mock_component.should_receive(:execute_command).once subject.execute_command command end end context "for a component which began executing but crashed" do @@ -859,11 +885,11 @@ component.wrapped_object.define_singleton_method(:oops) do raise 'Woops, I died' end - translator.expects(:handle_pb_event).once.with expected_event + translator.should_receive(:handle_pb_event).once.with expected_event lambda { component.oops }.should raise_error(/Woops, I died/) sleep 0.1 component.should_not be_alive subject.component_with_id(comp_id).should be_nil @@ -899,11 +925,11 @@ let :command do Punchblock::Command::Join.new :call_id => other_call_id end it "executes the proper uuid_bridge command" do - subject.wrapped_object.expects(:uuid_foo).once.with :bridge, other_call_id + subject.wrapped_object.should_receive(:uuid_foo).once.with :bridge, other_call_id subject.execute_command command expect { command.response 1 }.to raise_exception(Timeout::Error) end context "subsequently receiving a CHANNEL_BRIDGE event" do @@ -931,10 +957,10 @@ let :command do Punchblock::Command::Unjoin.new :call_id => other_call_id end it "executes the unjoin via transfer to park" do - subject.wrapped_object.expects(:uuid_foo).once.with :transfer, '-both park inline' + subject.wrapped_object.should_receive(:uuid_foo).once.with :transfer, '-both park inline' subject.execute_command command expect { command.response 1 }.to raise_exception(Timeout::Error) end context "subsequently receiving a CHANNEL_UNBRIDGE event" do