punchblock 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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