spec/punchblock/translator/asterisk/component/output_spec.rb in punchblock-1.7.0 vs spec/punchblock/translator/asterisk/component/output_spec.rb in punchblock-1.7.1

- old
+ new

@@ -353,410 +353,410 @@ end end end end - context 'with a media engine of :asterisk' do - let(:media_engine) { :asterisk } + [:asterisk, nil].each do |media_engine| + context "with a media engine of #{media_engine.inspect}" do + def expect_playback(filename = audio_filename) + mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', filename + end - def expect_playback(filename = audio_filename) - mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', filename - end + def expect_playback_noanswer + mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', audio_filename + ',noanswer' + end - def expect_playback_noanswer - mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', audio_filename + ',noanswer' - end + let(:audio_filename) { 'http://foo.com/bar.mp3' } - let(:audio_filename) { 'http://foo.com/bar.mp3' } - - let :ssml_doc do - RubySpeech::SSML.draw do - audio :src => audio_filename + let :ssml_doc do + RubySpeech::SSML.draw do + audio :src => audio_filename + end end - end - let(:command_opts) { {} } + let(:command_opts) { {} } - let :command_options do - { :ssml => ssml_doc }.merge(command_opts) - end + let :command_options do + { :ssml => ssml_doc }.merge(command_opts) + end - let :original_command do - Punchblock::Component::Output.new command_options - end - - describe 'ssml' do - context 'unset' do - let(:command_opts) { { :ssml => nil } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'An SSML document is required.' - original_command.response(0.1).should be == error - end + let :original_command do + Punchblock::Component::Output.new command_options end - context 'with a single audio SSML node' do - let(:audio_filename) { 'http://foo.com/bar.mp3' } - let :command_options do - { - :ssml => RubySpeech::SSML.draw { audio :src => audio_filename } - } + describe 'ssml' do + context 'unset' do + let(:command_opts) { { :ssml => nil } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'An SSML document is required.' + original_command.response(0.1).should be == error + end end - it 'should playback the audio file using Playback' do - expect_answered - expect_playback - subject.execute - end + context 'with a single audio SSML node' do + let(:audio_filename) { 'http://foo.com/bar.mp3' } + let :command_options do + { + :ssml => RubySpeech::SSML.draw { audio :src => audio_filename } + } + end - it 'should send a complete event when the file finishes playback' do - def mock_call.answered? - true + it 'should playback the audio file using Playback' do + expect_answered + expect_playback + subject.execute end - def mock_call.send_agi_action!(*args, &block) - block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) + + it 'should send a complete event when the file finishes playback' do + def mock_call.answered? + true + end + def mock_call.send_agi_action!(*args, &block) + block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) + end + subject.execute + original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success end - subject.execute - original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success end - end - context 'with a single text node without spaces' do - let(:audio_filename) { 'tt-monkeys' } - let :command_options do - { - :ssml => RubySpeech::SSML.draw { string audio_filename } - } - end - - it 'should playback the audio file using Playback' do - expect_answered - expect_playback - subject.execute - end - - it 'should send a complete event when the file finishes playback' do - expect_answered - def mock_call.send_agi_action!(*args, &block) - block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) + context 'with a single text node without spaces' do + let(:audio_filename) { 'tt-monkeys' } + let :command_options do + { + :ssml => RubySpeech::SSML.draw { string audio_filename } + } end - subject.execute - original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success - end - context "with early media playback" do - it "should play the file with Playback" do - expect_answered false - expect_playback_noanswer - mock_call.should_receive(:send_progress) + it 'should playback the audio file using Playback' do + expect_answered + expect_playback subject.execute end - context "with interrupt_on set to something that is not nil" do - let(:audio_filename) { 'tt-monkeys' } - let :command_options do - { - :ssml => RubySpeech::SSML.draw { string audio_filename }, - :interrupt_on => :any - } + it 'should send a complete event when the file finishes playback' do + expect_answered + def mock_call.send_agi_action!(*args, &block) + block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) end - it "should return an error when the output is interruptible and it is early media" do + subject.execute + original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success + end + + context "with early media playback" do + it "should play the file with Playback" do expect_answered false - error = ProtocolError.new.setup 'option error', 'Interrupt digits are not allowed with early media.' + expect_playback_noanswer + mock_call.should_receive(:send_progress) subject.execute - original_command.response(0.1).should be == error end + + context "with interrupt_on set to something that is not nil" do + let(:audio_filename) { 'tt-monkeys' } + let :command_options do + { + :ssml => RubySpeech::SSML.draw { string audio_filename }, + :interrupt_on => :any + } + end + it "should return an error when the output is interruptible and it is early media" do + expect_answered false + error = ProtocolError.new.setup 'option error', 'Interrupt digits are not allowed with early media.' + subject.execute + original_command.response(0.1).should be == error + end + end end end - end - context 'with a string (not SSML)' do - let :command_options do - { :text => 'Foo Bar' } - end + context 'with a string (not SSML)' do + let :command_options do + { :text => 'Foo Bar' } + end - it "should return an unrenderable document error" do - subject.execute - error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.' - original_command.response(0.1).should be == error + it "should return an unrenderable document error" do + subject.execute + error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.' + original_command.response(0.1).should be == error + end end - end - context 'with multiple audio SSML nodes' do - let(:audio_filename1) { 'http://foo.com/bar.mp3' } - let(:audio_filename2) { 'http://foo.com/baz.mp3' } - let :command_options do - { - :ssml => RubySpeech::SSML.draw do - audio :src => audio_filename1 - audio :src => audio_filename2 - end - } - end + context 'with multiple audio SSML nodes' do + let(:audio_filename1) { 'http://foo.com/bar.mp3' } + let(:audio_filename2) { 'http://foo.com/baz.mp3' } + let :command_options do + { + :ssml => RubySpeech::SSML.draw do + audio :src => audio_filename1 + audio :src => audio_filename2 + end + } + end - it 'should playback all audio files using Playback' do - latch = CountDownLatch.new 2 - expect_playback [audio_filename1, audio_filename2].join('&') - expect_answered - subject.execute - latch.wait 2 - sleep 2 - end - - it 'should send a complete event after the final file has finished playback' do - expect_answered - def mock_call.send_agi_action!(*args, &block) - block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) + it 'should playback all audio files using Playback' do + latch = CountDownLatch.new 2 + expect_playback [audio_filename1, audio_filename2].join('&') + expect_answered + subject.execute + latch.wait 2 + sleep 2 end - latch = CountDownLatch.new 1 - original_command.should_receive(:add_event).once.with do |e| - e.reason.should be_a Punchblock::Component::Output::Complete::Success - latch.countdown! - end - subject.execute - latch.wait(2).should be_true - end - end - context "with an SSML document containing elements other than <audio/>" do - let :command_options do - { - :ssml => RubySpeech::SSML.draw do - string "Foo Bar" + it 'should send a complete event after the final file has finished playback' do + expect_answered + def mock_call.send_agi_action!(*args, &block) + block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1) end - } + latch = CountDownLatch.new 1 + original_command.should_receive(:add_event).once.with do |e| + e.reason.should be_a Punchblock::Component::Output::Complete::Success + latch.countdown! + end + subject.execute + latch.wait(2).should be_true + end end - it "should return an unrenderable document error" do - subject.execute - error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.' - original_command.response(0.1).should be == error - end - end - end + context "with an SSML document containing elements other than <audio/>" do + let :command_options do + { + :ssml => RubySpeech::SSML.draw do + string "Foo Bar" + end + } + end - describe 'start-offset' do - context 'unset' do - let(:command_opts) { { :start_offset => nil } } - it 'should not pass any options to Playback' do - expect_answered - expect_playback - subject.execute + it "should return an unrenderable document error" do + subject.execute + error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.' + original_command.response(0.1).should be == error + end end end - context 'set' do - let(:command_opts) { { :start_offset => 10 } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A start_offset value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'start-offset' do + context 'unset' do + let(:command_opts) { { :start_offset => nil } } + it 'should not pass any options to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'start-paused' do - context 'false' do - let(:command_opts) { { :start_paused => false } } - it 'should not pass any options to Playback' do - expect_answered - expect_playback - subject.execute + context 'set' do + let(:command_opts) { { :start_offset => 10 } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A start_offset value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - context 'true' do - let(:command_opts) { { :start_paused => true } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A start_paused value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'start-paused' do + context 'false' do + let(:command_opts) { { :start_paused => false } } + it 'should not pass any options to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'repeat-interval' do - context 'unset' do - let(:command_opts) { { :repeat_interval => nil } } - it 'should not pass any options to Playback' do - expect_answered - expect_playback - subject.execute + context 'true' do + let(:command_opts) { { :start_paused => true } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A start_paused value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - context 'set' do - let(:command_opts) { { :repeat_interval => 10 } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'repeat-interval' do + context 'unset' do + let(:command_opts) { { :repeat_interval => nil } } + it 'should not pass any options to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'repeat-times' do - context 'unset' do - let(:command_opts) { { :repeat_times => nil } } - it 'should not pass any options to Playback' do - expect_answered - expect_playback - subject.execute + context 'set' do + let(:command_opts) { { :repeat_interval => 10 } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - context 'set' do - let(:command_opts) { { :repeat_times => 2 } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'repeat-times' do + context 'unset' do + let(:command_opts) { { :repeat_times => nil } } + it 'should not pass any options to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'max-time' do - context 'unset' do - let(:command_opts) { { :max_time => nil } } - it 'should not pass any options to Playback' do - expect_answered - expect_playback - subject.execute + context 'set' do + let(:command_opts) { { :repeat_times => 2 } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - context 'set' do - let(:command_opts) { { :max_time => 30 } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A max_time value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'max-time' do + context 'unset' do + let(:command_opts) { { :max_time => nil } } + it 'should not pass any options to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'voice' do - context 'unset' do - let(:command_opts) { { :voice => nil } } - it 'should not pass the v option to Playback' do - expect_answered - expect_playback - subject.execute + context 'set' do + let(:command_opts) { { :max_time => 30 } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A max_time value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - context 'set' do - let(:command_opts) { { :voice => 'alison' } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'A voice value is unsupported on Asterisk.' - original_command.response(0.1).should be == error + describe 'voice' do + context 'unset' do + let(:command_opts) { { :voice => nil } } + it 'should not pass the v option to Playback' do + expect_answered + expect_playback + subject.execute + end end - end - end - describe 'interrupt_on' do - def ami_event_for_dtmf(digit, position) - RubyAMI::Event.new('DTMF').tap do |e| - e['Digit'] = digit.to_s - e['Start'] = position == :start ? 'Yes' : 'No' - e['End'] = position == :end ? 'Yes' : 'No' + context 'set' do + let(:command_opts) { { :voice => 'alison' } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'A voice value is unsupported on Asterisk.' + original_command.response(0.1).should be == error + end end end - def send_ami_events_for_dtmf(digit) - mock_call.process_ami_event ami_event_for_dtmf(digit, :start) - mock_call.process_ami_event ami_event_for_dtmf(digit, :end) - end - - let(:reason) { original_command.complete_event(5).reason } - let(:channel) { "SIP/1234-00000000" } - let :ami_event do - RubyAMI::Event.new('AsyncAGI').tap do |e| - e['SubEvent'] = "Start" - e['Channel'] = channel - e['Env'] = "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A" + describe 'interrupt_on' do + def ami_event_for_dtmf(digit, position) + RubyAMI::Event.new('DTMF').tap do |e| + e['Digit'] = digit.to_s + e['Start'] = position == :start ? 'Yes' : 'No' + e['End'] = position == :end ? 'Yes' : 'No' + end end - end - context "set to nil" do - let(:command_opts) { { :interrupt_on => nil } } - it "does not redirect the call" do - expect_answered - expect_playback - mock_call.should_receive(:redirect_back!).never - subject.execute - original_command.response(0.1).should be_a Ref - send_ami_events_for_dtmf 1 + def send_ami_events_for_dtmf(digit) + mock_call.process_ami_event ami_event_for_dtmf(digit, :start) + mock_call.process_ami_event ami_event_for_dtmf(digit, :end) end - end - context "set to :any" do - let(:command_opts) { { :interrupt_on => :any } } - - before do - expect_answered - expect_playback + let(:reason) { original_command.complete_event(5).reason } + let(:channel) { "SIP/1234-00000000" } + let :ami_event do + RubyAMI::Event.new('AsyncAGI').tap do |e| + e['SubEvent'] = "Start" + e['Channel'] = channel + e['Env'] = "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A" + end end - context "when a DTMF digit is received" do - it "sends the correct complete event" do - mock_call.should_receive :redirect_back! + context "set to nil" do + let(:command_opts) { { :interrupt_on => nil } } + it "does not redirect the call" do + expect_answered + expect_playback + mock_call.should_receive(:redirect_back!).never subject.execute original_command.response(0.1).should be_a Ref - original_command.should_not be_complete send_ami_events_for_dtmf 1 - mock_call.process_ami_event! ami_event - sleep 0.2 - original_command.should be_complete - reason.should be_a Punchblock::Component::Output::Complete::Success end + end - it "redirects the call back to async AGI" do - mock_call.should_receive(:redirect_back!).once - subject.execute - original_command.response(0.1).should be_a Ref - send_ami_events_for_dtmf 1 + context "set to :any" do + let(:command_opts) { { :interrupt_on => :any } } + + before do + expect_answered + expect_playback end - end - end - context "set to :dtmf" do - let(:command_opts) { { :interrupt_on => :dtmf } } + context "when a DTMF digit is received" do + it "sends the correct complete event" do + mock_call.should_receive :redirect_back! + subject.execute + original_command.response(0.1).should be_a Ref + original_command.should_not be_complete + send_ami_events_for_dtmf 1 + mock_call.process_ami_event! ami_event + sleep 0.2 + original_command.should be_complete + reason.should be_a Punchblock::Component::Output::Complete::Success + end - before do - expect_answered - expect_playback + it "redirects the call back to async AGI" do + mock_call.should_receive(:redirect_back!).once + subject.execute + original_command.response(0.1).should be_a Ref + send_ami_events_for_dtmf 1 + end + end end - context "when a DTMF digit is received" do - it "sends the correct complete event" do - mock_call.should_receive :redirect_back! - subject.execute - original_command.response(0.1).should be_a Ref - original_command.should_not be_complete - send_ami_events_for_dtmf 1 - mock_call.process_ami_event! ami_event - sleep 0.2 - original_command.should be_complete - reason.should be_a Punchblock::Component::Output::Complete::Success + context "set to :dtmf" do + let(:command_opts) { { :interrupt_on => :dtmf } } + + before do + expect_answered + expect_playback end - it "redirects the call back to async AGI" do - mock_call.should_receive(:redirect_back!).once - subject.execute - original_command.response(0.1).should be_a Ref - send_ami_events_for_dtmf 1 + context "when a DTMF digit is received" do + it "sends the correct complete event" do + mock_call.should_receive :redirect_back! + subject.execute + original_command.response(0.1).should be_a Ref + original_command.should_not be_complete + send_ami_events_for_dtmf 1 + mock_call.process_ami_event! ami_event + sleep 0.2 + original_command.should be_complete + reason.should be_a Punchblock::Component::Output::Complete::Success + end + + it "redirects the call back to async AGI" do + mock_call.should_receive(:redirect_back!).once + subject.execute + original_command.response(0.1).should be_a Ref + send_ami_events_for_dtmf 1 + end end end - end - context "set to :speech" do - let(:command_opts) { { :interrupt_on => :speech } } - it "should return an error and not execute any actions" do - subject.execute - error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.' - original_command.response(0.1).should be == error + context "set to :speech" do + let(:command_opts) { { :interrupt_on => :speech } } + it "should return an error and not execute any actions" do + subject.execute + error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.' + original_command.response(0.1).should be == error + end end end end end