punchblock 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/lib/punchblock/component/output.rb +12 -4
  4. data/lib/punchblock/translator/asterisk.rb +3 -4
  5. data/lib/punchblock/translator/asterisk/agi_command.rb +3 -0
  6. data/lib/punchblock/translator/asterisk/call.rb +53 -7
  7. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +1 -1
  8. data/lib/punchblock/translator/asterisk/component/composed_prompt.rb +1 -1
  9. data/lib/punchblock/translator/asterisk/component/input.rb +1 -1
  10. data/lib/punchblock/translator/asterisk/component/mrcp_native_prompt.rb +14 -2
  11. data/lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb +39 -0
  12. data/lib/punchblock/translator/asterisk/component/output.rb +1 -1
  13. data/lib/punchblock/translator/asterisk/component/stop_by_redirect.rb +1 -1
  14. data/lib/punchblock/version.rb +1 -1
  15. data/spec/punchblock/translator/asterisk/call_spec.rb +250 -35
  16. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +55 -2
  17. data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +24 -5
  18. data/spec/punchblock/translator/asterisk/component/input_spec.rb +26 -5
  19. data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +42 -2
  20. data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +479 -0
  21. data/spec/punchblock/translator/asterisk/component/output_spec.rb +40 -17
  22. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +4 -3
  23. data/spec/punchblock/translator/asterisk_spec.rb +36 -9
  24. metadata +2 -2
@@ -112,22 +112,31 @@ module Punchblock
112
112
  context 'of type start'
113
113
 
114
114
  context 'of type Exec' do
115
+ let (:ami_event_result) { "200%20result=123%20(timeout)%0A" }
115
116
  let(:ami_event) do
116
117
  RubyAMI::Event.new 'AsyncAGI',
117
118
  "SubEvent" => "Exec",
118
119
  "Channel" => channel,
119
120
  "CommandId" => component_id,
120
121
  "Command" => "EXEC ANSWER",
121
- "Result" => "200%20result=123%20(timeout)%0A"
122
+ "Result" => ami_event_result
122
123
  end
123
124
 
125
+ let(:ami_event_ast13) do
126
+ RubyAMI::Event.new 'AsyncAGIExec',
127
+ "Channel" => channel,
128
+ "CommandId" => component_id,
129
+ "Command" => "EXEC ANSWER",
130
+ "Result" => ami_event_result
131
+ end
132
+
124
133
  let :expected_complete_reason do
125
134
  Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => 200,
126
135
  :result => 123,
127
136
  :data => 'timeout'
128
137
  end
129
138
 
130
- it 'should send a complete event' do
139
+ def should_send_complete_event
131
140
  subject.handle_ami_event ami_event
132
141
 
133
142
  complete_event = original_command.complete_event 0.5
@@ -138,6 +147,50 @@ module Punchblock
138
147
  expect(complete_event.reason).to eq(expected_complete_reason)
139
148
  end
140
149
 
150
+ it 'should send a complete event' do
151
+ should_send_complete_event
152
+ end
153
+
154
+ context 'with an AsyncAGIExec event' do
155
+ let(:ami_event) { ami_event_ast13 }
156
+
157
+ it 'should send a complete event' do
158
+ should_send_complete_event
159
+ end
160
+ end
161
+
162
+ context 'when the result contains illegal characters in the AGI response' do
163
+ let (:ami_event_result) { '$' }
164
+ let :expected_complete_reason do
165
+ Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => -1,
166
+ :result => nil,
167
+ :data => nil
168
+ end
169
+
170
+ def treat_as_failure
171
+ subject.handle_ami_event ami_event
172
+
173
+ complete_event = original_command.complete_event 0.5
174
+
175
+ expect(original_command).to be_complete
176
+
177
+ expect(complete_event.component_id).to eq(component_id.to_s)
178
+ expect(complete_event.reason).to eq(expected_complete_reason)
179
+ end
180
+
181
+ it 'treats it as a failure with code -1' do
182
+ treat_as_failure
183
+ end
184
+
185
+ context 'with an AsyncAGIExec event' do
186
+ let(:ami_event) { ami_event_ast13 }
187
+
188
+ it 'treats it as a failure with code -1' do
189
+ treat_as_failure
190
+ end
191
+ end
192
+ end
193
+
141
194
  context "when the command was ASYNCAGI BREAK" do
142
195
  let :original_command do
143
196
  Punchblock::Component::Asterisk::AGI::Command.new :name => 'ASYNCAGI BREAK'
@@ -76,11 +76,18 @@ module Punchblock
76
76
  original_command.request!
77
77
  end
78
78
 
79
+ let (:ast13mode) { false }
80
+
79
81
  def ami_event_for_dtmf(digit, position)
80
- RubyAMI::Event.new 'DTMF',
81
- 'Digit' => digit.to_s,
82
- 'Start' => position == :start ? 'Yes' : 'No',
83
- 'End' => position == :end ? 'Yes' : 'No'
82
+ if ast13mode
83
+ RubyAMI::Event.new 'DTMF' + (position == :start ? 'Begin' : '') + (position == :end ? 'End' : ''),
84
+ 'Digit' => digit.to_s
85
+ else
86
+ RubyAMI::Event.new 'DTMF',
87
+ 'Digit' => digit.to_s,
88
+ 'Start' => position == :start ? 'Yes' : 'No',
89
+ 'End' => position == :end ? 'Yes' : 'No'
90
+ end
84
91
  end
85
92
 
86
93
  def send_ami_events_for_dtmf(digit)
@@ -163,7 +170,7 @@ module Punchblock
163
170
  Punchblock::Component::Input::Complete::Match.new nlsml: expected_nlsml
164
171
  end
165
172
 
166
- it "should return a match complete event" do
173
+ def should_return_a_match_complete_event
167
174
  expected_event
168
175
  subject.execute
169
176
  expect(original_command.response(0.1)).to be_a Ref
@@ -171,6 +178,18 @@ module Punchblock
171
178
 
172
179
  expect(connection.events).to include(expected_event)
173
180
  end
181
+
182
+ it "should return a match complete event" do
183
+ should_return_a_match_complete_event
184
+ end
185
+
186
+ context 'with Asterisk 13 DTMFEnd event' do
187
+ let (:ast13mode) { true }
188
+
189
+ it "should return a match complete event" do
190
+ should_return_a_match_complete_event
191
+ end
192
+ end
174
193
  end
175
194
 
176
195
  context "when not receiving any DTMF input at all" do
@@ -52,11 +52,18 @@ module Punchblock
52
52
  { :mode => :dtmf, :grammar => { :value => grammar } }.merge(original_command_opts)
53
53
  end
54
54
 
55
+ let (:ast13mode) { false }
56
+
55
57
  def ami_event_for_dtmf(digit, position)
56
- RubyAMI::Event.new 'DTMF',
57
- 'Digit' => digit.to_s,
58
- 'Start' => position == :start ? 'Yes' : 'No',
59
- 'End' => position == :end ? 'Yes' : 'No'
58
+ if ast13mode
59
+ RubyAMI::Event.new 'DTMF' + (position == :start ? 'Begin' : '') + (position == :end ? 'End' : ''),
60
+ 'Digit' => digit.to_s
61
+ else
62
+ RubyAMI::Event.new 'DTMF',
63
+ 'Digit' => digit.to_s,
64
+ 'Start' => position == :start ? 'Yes' : 'No',
65
+ 'End' => position == :end ? 'Yes' : 'No'
66
+ end
60
67
  end
61
68
 
62
69
  def send_ami_events_for_dtmf(digit)
@@ -101,7 +108,14 @@ module Punchblock
101
108
  end
102
109
 
103
110
  it "should not leave the recognizer running" do
104
- expect(Celluloid::Actor.all.map { |a| a.class }).not_to include(Punchblock::Translator::DTMFRecognizer)
111
+ expect(Celluloid::Actor.all.any? { |a| a.class == Punchblock::Translator::DTMFRecognizer rescue false }).to eq(false)
112
+ end
113
+
114
+ context 'with an Asterisk 13 DTMFEnd event' do
115
+ let(:ast13mode) { true }
116
+ it "should send a success complete event with the relevant data" do
117
+ expect(reason).to eq(expected_event)
118
+ end
105
119
  end
106
120
  end
107
121
 
@@ -118,6 +132,13 @@ module Punchblock
118
132
  it "should send a nomatch complete event" do
119
133
  expect(reason).to eq(expected_event)
120
134
  end
135
+
136
+ context 'with an Asterisk 13 DTMFEnd event' do
137
+ let(:ast13mode) { true }
138
+ it "should send a nomatch complete event" do
139
+ expect(reason).to eq(expected_event)
140
+ end
141
+ end
121
142
  end
122
143
 
123
144
  context "dtmf event received after recognizer has terminated" do
@@ -51,10 +51,11 @@ module Punchblock
51
51
 
52
52
  let(:output_command_opts) { {} }
53
53
 
54
- let(:audio_filename) { 'http://example.com/hello.mp3' }
54
+ let(:audio_filename) { '/var/foo' }
55
+ let(:audio_path) { "file://#{audio_filename}.wav" }
55
56
 
56
57
  let :output_command_options do
57
- { render_document: {value: [audio_filename], content_type: 'text/uri-list'} }.merge(output_command_opts)
58
+ { render_document: {value: [audio_path], content_type: 'text/uri-list'} }.merge(output_command_opts)
58
59
  end
59
60
 
60
61
  let(:input_command_opts) { {} }
@@ -158,6 +159,23 @@ module Punchblock
158
159
  end
159
160
  end
160
161
 
162
+ context 'with multiple audio tags in SSML' do
163
+ let :ssml_doc do
164
+ RubySpeech::SSML.draw do
165
+ audio(src: audio_path)
166
+ audio(src: audio_path)
167
+ end
168
+ end
169
+
170
+ let(:output_command_options) { { render_documents: [{value: ssml_doc}] } }
171
+
172
+ it "should return an error and not execute any actions" do
173
+ subject.execute
174
+ error = ProtocolError.new.setup 'option error', 'Only one audio file is allowed.'
175
+ expect(original_command.response(0.1)).to eq(error)
176
+ end
177
+ end
178
+
161
179
  context 'unset' do
162
180
  let(:output_command_options) { {} }
163
181
 
@@ -181,6 +199,28 @@ module Punchblock
181
199
  expect(original_command.response(0.1)).to be_a Ref
182
200
  end
183
201
 
202
+ context "when the render document is SSML" do
203
+ let :ssml_doc do
204
+ RubySpeech::SSML.draw do
205
+ audio(src: audio_path)
206
+ end
207
+ end
208
+
209
+ let(:output_command_opts) do
210
+ {
211
+ renderer: renderer,
212
+ render_document: {value: ssml_doc}
213
+ }
214
+ end
215
+
216
+ it "should return a ref and execute MRCPRecog" do
217
+ param = ["\"#{grammar.to_doc.to_s.squish.gsub('"', '\"')}\"", "uer=1&b=1&f=#{audio_filename}"].join(',')
218
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC MRCPRecog', param).and_return code: 200, result: 1
219
+ subject.execute
220
+ expect(original_command.response(0.1)).to be_a Ref
221
+ end
222
+ end
223
+
184
224
  context "when MRCPRecog completes" do
185
225
  shared_context "with a match" do
186
226
  let :expected_nlsml do
@@ -612,6 +612,485 @@ module Punchblock
612
612
  end
613
613
  end
614
614
 
615
+ describe 'Input#max-silence' do
616
+ context 'a positive number' do
617
+ let(:input_command_opts) { { max_silence: 1000 } }
618
+
619
+ it 'should pass the sint option to SynthAndRecog' do
620
+ expect_synthandrecog_with_options(/sint=1000/)
621
+ subject.execute
622
+ end
623
+ end
624
+
625
+ context '0' do
626
+ let(:input_command_opts) { { max_silence: 0 } }
627
+
628
+ it 'should pass the sint option to SynthAndRecog' do
629
+ expect_synthandrecog_with_options(/sint=0/)
630
+ subject.execute
631
+ end
632
+ end
633
+
634
+ context 'a negative number' do
635
+ let(:input_command_opts) { { max_silence: -1000 } }
636
+
637
+ it "should return an error and not execute any actions" do
638
+ subject.execute
639
+ error = ProtocolError.new.setup 'option error', 'A max-silence value must be -1, 0, or a positive integer.'
640
+ expect(original_command.response(0.1)).to eq(error)
641
+ end
642
+ end
643
+
644
+ context 'unset' do
645
+ let(:input_command_opts) { { max_silence: nil } }
646
+
647
+ it 'should not pass any options to SynthAndRecog' do
648
+ expect_synthandrecog_with_options(//)
649
+ subject.execute
650
+ end
651
+ end
652
+ end
653
+
654
+ describe 'Input#speech-complete-timeout' do
655
+ context 'a positive number' do
656
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => 1000 } } }
657
+
658
+ it 'should pass the sct option to SynthAndRecog' do
659
+ expect_synthandrecog_with_options(/sct=1000/)
660
+ subject.execute
661
+ end
662
+ end
663
+
664
+ context '0' do
665
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => 0 } } }
666
+
667
+ it 'should pass the sct option to SynthAndRecog' do
668
+ expect_synthandrecog_with_options(/sct=0/)
669
+ subject.execute
670
+ end
671
+ end
672
+
673
+ context 'a negative number' do
674
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => -1000 } } }
675
+
676
+ it "should return an error and not execute any actions" do
677
+ subject.execute
678
+ error = ProtocolError.new.setup 'option error', 'A speech-complete-timeout value must be -1, 0, or a positive integer.'
679
+ expect(original_command.response(0.1)).to eq(error)
680
+ end
681
+ end
682
+
683
+ context 'unset' do
684
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => nil } } }
685
+
686
+ it 'should not pass any options to SynthAndRecog' do
687
+ expect_synthandrecog_with_options(//)
688
+ subject.execute
689
+ end
690
+ end
691
+ end
692
+
693
+ describe 'Input#headers(Speed-Vs-Accuracy)' do
694
+ context 'a positive number' do
695
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => 1 } } }
696
+
697
+ it 'should pass the sva option to SynthAndRecog' do
698
+ expect_synthandrecog_with_options(/sva=1/)
699
+ subject.execute
700
+ end
701
+ end
702
+
703
+ context '0' do
704
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => 0 } } }
705
+
706
+ it 'should pass the sva option to SynthAndRecog' do
707
+ expect_synthandrecog_with_options(/sva=0/)
708
+ subject.execute
709
+ end
710
+ end
711
+
712
+ context 'a negative number' do
713
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => -1000 } } }
714
+
715
+ it "should return an error and not execute any actions" do
716
+ subject.execute
717
+ error = ProtocolError.new.setup 'option error', 'A speed-vs-accuracy value must be a positive integer.'
718
+ expect(original_command.response(0.1)).to eq(error)
719
+ end
720
+ end
721
+
722
+ context 'unset' do
723
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => nil } } }
724
+
725
+ it 'should not pass any options to SynthAndRecog' do
726
+ expect_synthandrecog_with_options(//)
727
+ subject.execute
728
+ end
729
+ end
730
+ end
731
+
732
+ describe 'Input#headers(N-Best-List-Length)' do
733
+ context 'a positive number' do
734
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => 5 } } }
735
+
736
+ it 'should pass the nb option to SynthAndRecog' do
737
+ expect_synthandrecog_with_options(/nb=5/)
738
+ subject.execute
739
+ end
740
+ end
741
+
742
+ context '0' do
743
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => 0 } } }
744
+
745
+ it "should return an error and not execute any actions" do
746
+ subject.execute
747
+ error = ProtocolError.new.setup 'option error', 'An n-best-list-length value must be a positive integer.'
748
+ expect(original_command.response(0.1)).to eq(error)
749
+ end
750
+ end
751
+
752
+ context 'a negative number' do
753
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => -1000 } } }
754
+
755
+ it "should return an error and not execute any actions" do
756
+ subject.execute
757
+ error = ProtocolError.new.setup 'option error', 'An n-best-list-length value must be a positive integer.'
758
+ expect(original_command.response(0.1)).to eq(error)
759
+ end
760
+ end
761
+
762
+ context 'unset' do
763
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => nil } } }
764
+
765
+ it 'should not pass any options to SynthAndRecog' do
766
+ expect_synthandrecog_with_options(//)
767
+ subject.execute
768
+ end
769
+ end
770
+ end
771
+
772
+ describe 'Input#headers(Start-Input-Timers)' do
773
+ context 'true' do
774
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => true } } }
775
+
776
+ it 'should pass the sit option to SynthAndRecog' do
777
+ expect_synthandrecog_with_options(/sit=true/)
778
+ subject.execute
779
+ end
780
+ end
781
+
782
+ context 'false' do
783
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => false } } }
784
+
785
+ it 'should pass the sit option to SynthAndRecog' do
786
+ expect_synthandrecog_with_options(/sit=false/)
787
+ subject.execute
788
+ end
789
+ end
790
+
791
+ context 'unset' do
792
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => nil } } }
793
+
794
+ it 'should not pass any options to SynthAndRecog' do
795
+ expect_synthandrecog_with_options(//)
796
+ subject.execute
797
+ end
798
+ end
799
+ end
800
+
801
+ describe 'Input#headers(DTMF-Terminate-Timeout)' do
802
+ context 'a positive number' do
803
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => 1000 } } }
804
+
805
+ it 'should pass the dtt option to SynthAndRecog' do
806
+ expect_synthandrecog_with_options(/dtt=1000/)
807
+ subject.execute
808
+ end
809
+ end
810
+
811
+ context '0' do
812
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => 0 } } }
813
+
814
+ it 'should pass the dtt option to SynthAndRecog' do
815
+ expect_synthandrecog_with_options(/dtt=0/)
816
+ subject.execute
817
+ end
818
+ end
819
+
820
+ context 'a negative number' do
821
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => -1000 } } }
822
+
823
+ it "should return an error and not execute any actions" do
824
+ subject.execute
825
+ error = ProtocolError.new.setup 'option error', 'A dtmf-terminate-timeout value must be -1, 0, or a positive integer.'
826
+ expect(original_command.response(0.1)).to eq(error)
827
+ end
828
+ end
829
+
830
+ context 'unset' do
831
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => nil } } }
832
+
833
+ it 'should not pass any options to SynthAndRecog' do
834
+ expect_synthandrecog_with_options(//)
835
+ subject.execute
836
+ end
837
+ end
838
+ end
839
+
840
+ describe 'Input#headers(Save-Waveform)' do
841
+ context 'true' do
842
+ let(:input_command_opts) { { headers: {"Save-Waveform" => true } } }
843
+
844
+ it 'should pass the sw option to SynthAndRecog' do
845
+ expect_synthandrecog_with_options(/sw=true/)
846
+ subject.execute
847
+ end
848
+ end
849
+
850
+ context 'false' do
851
+ let(:input_command_opts) { { headers: {"Save-Waveform" => false } } }
852
+
853
+ it 'should pass the sw option to SynthAndRecog' do
854
+ expect_synthandrecog_with_options(/sw=false/)
855
+ subject.execute
856
+ end
857
+ end
858
+
859
+ context 'unset' do
860
+ let(:input_command_opts) { { headers: {"Save-Waveform" => nil } } }
861
+
862
+ it 'should not pass any options to SynthAndRecog' do
863
+ expect_synthandrecog_with_options(//)
864
+ subject.execute
865
+ end
866
+ end
867
+ end
868
+
869
+ describe 'Input#headers(New-Audio-Channel)' do
870
+ context 'true' do
871
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => true } } }
872
+
873
+ it 'should pass the nac option to SynthAndRecog' do
874
+ expect_synthandrecog_with_options(/nac=true/)
875
+ subject.execute
876
+ end
877
+ end
878
+
879
+ context 'false' do
880
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => false } } }
881
+
882
+ it 'should pass the nac option to SynthAndRecog' do
883
+ expect_synthandrecog_with_options(/nac=false/)
884
+ subject.execute
885
+ end
886
+ end
887
+
888
+ context 'unset' do
889
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => nil } } }
890
+
891
+ it 'should not pass any options to SynthAndRecog' do
892
+ expect_synthandrecog_with_options(//)
893
+ subject.execute
894
+ end
895
+ end
896
+ end
897
+
898
+ describe 'Input#headers(Recognition-Mode)' do
899
+ context 'a string' do
900
+ let(:input_command_opts) { { headers: {"Recognition-Mode" => "hotword" } } }
901
+
902
+ it 'should pass the rm option to SynthAndRecog' do
903
+ expect_synthandrecog_with_options(/rm=hotword/)
904
+ subject.execute
905
+ end
906
+ end
907
+
908
+ context 'unset' do
909
+ let(:input_command_opts) { { headers: {"Recognition-Mode" => nil } } }
910
+
911
+ it 'should not pass any options to SynthAndRecog' do
912
+ expect_synthandrecog_with_options(//)
913
+ subject.execute
914
+ end
915
+ end
916
+ end
917
+
918
+ describe 'Input#headers(Hotword-Max-Duration)' do
919
+ context 'a positive number' do
920
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => 1000 } } }
921
+
922
+ it 'should pass the hmaxd option to SynthAndRecog' do
923
+ expect_synthandrecog_with_options(/hmaxd=1000/)
924
+ subject.execute
925
+ end
926
+ end
927
+
928
+ context '0' do
929
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => 0 } } }
930
+
931
+ it 'should pass the hmaxd option to SynthAndRecog' do
932
+ expect_synthandrecog_with_options(/hmaxd=0/)
933
+ subject.execute
934
+ end
935
+ end
936
+
937
+ context 'a negative number' do
938
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => -1000 } } }
939
+
940
+ it "should return an error and not execute any actions" do
941
+ subject.execute
942
+ error = ProtocolError.new.setup 'option error', 'A hotword-max-duration value must be -1, 0, or a positive integer.'
943
+ expect(original_command.response(0.1)).to eq(error)
944
+ end
945
+ end
946
+
947
+ context 'unset' do
948
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => nil } } }
949
+
950
+ it 'should not pass any options to SynthAndRecog' do
951
+ expect_synthandrecog_with_options(//)
952
+ subject.execute
953
+ end
954
+ end
955
+ end
956
+
957
+ describe 'Input#headers(Hotword-Min-Duration)' do
958
+ context 'a positive number' do
959
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => 1000 } } }
960
+
961
+ it 'should pass the hmind option to SynthAndRecog' do
962
+ expect_synthandrecog_with_options(/hmind=1000/)
963
+ subject.execute
964
+ end
965
+ end
966
+
967
+ context '0' do
968
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => 0 } } }
969
+
970
+ it 'should pass the hmind option to SynthAndRecog' do
971
+ expect_synthandrecog_with_options(/hmind=0/)
972
+ subject.execute
973
+ end
974
+ end
975
+
976
+ context 'a negative number' do
977
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => -1000 } } }
978
+
979
+ it "should return an error and not execute any actions" do
980
+ subject.execute
981
+ error = ProtocolError.new.setup 'option error', 'A hotword-min-duration value must be -1, 0, or a positive integer.'
982
+ expect(original_command.response(0.1)).to eq(error)
983
+ end
984
+ end
985
+
986
+ context 'unset' do
987
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => nil } } }
988
+
989
+ it 'should not pass any options to SynthAndRecog' do
990
+ expect_synthandrecog_with_options(//)
991
+ subject.execute
992
+ end
993
+ end
994
+ end
995
+
996
+ describe 'Input#headers(Clear-DTMF-Buffer)' do
997
+ context 'true' do
998
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => true } } }
999
+
1000
+ it 'should pass the cdb option to SynthAndRecog' do
1001
+ expect_synthandrecog_with_options(/cdb=true/)
1002
+ subject.execute
1003
+ end
1004
+ end
1005
+
1006
+ context 'false' do
1007
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => false } } }
1008
+
1009
+ it 'should pass the cdb option to SynthAndRecog' do
1010
+ expect_synthandrecog_with_options(/cdb=false/)
1011
+ subject.execute
1012
+ end
1013
+ end
1014
+
1015
+ context 'unset' do
1016
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => nil } } }
1017
+
1018
+ it 'should not pass any options to SynthAndRecog' do
1019
+ expect_synthandrecog_with_options(//)
1020
+ subject.execute
1021
+ end
1022
+ end
1023
+ end
1024
+
1025
+ describe 'Input#headers(Early-No-Match)' do
1026
+ context 'true' do
1027
+ let(:input_command_opts) { { headers: {"Early-No-Match" => true } } }
1028
+
1029
+ it 'should pass the enm option to SynthAndRecog' do
1030
+ expect_synthandrecog_with_options(/enm=true/)
1031
+ subject.execute
1032
+ end
1033
+ end
1034
+
1035
+ context 'a negative number' do
1036
+ let(:input_command_opts) { { headers: {"Early-No-Match" => false } } }
1037
+
1038
+ it "should return an error and not execute any actions" do
1039
+ expect_synthandrecog_with_options(/enm=false/)
1040
+ subject.execute
1041
+ end
1042
+ end
1043
+
1044
+ context 'unset' do
1045
+ let(:input_command_opts) { { headers: {"Early-No-Match" => nil } } }
1046
+
1047
+ it 'should not pass any options to SynthAndRecog' do
1048
+ expect_synthandrecog_with_options(//)
1049
+ subject.execute
1050
+ end
1051
+ end
1052
+ end
1053
+
1054
+ describe 'Input#headers(Input-Waveform-URI)' do
1055
+ context 'a positive number' do
1056
+ let(:input_command_opts) { { headers: {"Input-Waveform-URI" => 'http://wave.form.com' } } }
1057
+
1058
+ it 'should pass the iwu option to SynthAndRecog' do
1059
+ expect_synthandrecog_with_options(/iwu=http:\/\/wave\.form\.com/)
1060
+ subject.execute
1061
+ end
1062
+ end
1063
+
1064
+ context 'unset' do
1065
+ let(:input_command_opts) { { headers: {"Input-Waveform-URI" => nil } } }
1066
+
1067
+ it 'should not pass any options to SynthAndRecog' do
1068
+ expect_synthandrecog_with_options(//)
1069
+ subject.execute
1070
+ end
1071
+ end
1072
+ end
1073
+
1074
+ describe 'Input#headers(Media-Type)' do
1075
+ context 'a string' do
1076
+ let(:input_command_opts) { { headers: {"Media-Type" => "foo/type" } } }
1077
+
1078
+ it 'should pass the mt option to SynthAndRecog' do
1079
+ expect_synthandrecog_with_options(/mt=foo\/type/)
1080
+ subject.execute
1081
+ end
1082
+ end
1083
+
1084
+ context 'unset' do
1085
+ let(:input_command_opts) { { headers: {"Media-Type" => nil } } }
1086
+
1087
+ it 'should not pass any options to SynthAndRecog' do
1088
+ expect_synthandrecog_with_options(//)
1089
+ subject.execute
1090
+ end
1091
+ end
1092
+ end
1093
+
615
1094
  describe 'Input#mode' do
616
1095
  pending
617
1096
  end