punchblock 2.5.3 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63ef1da0eed86d39e520b4e10984e068c8dc62e6
4
- data.tar.gz: 6b48e400972ac2516337faae2a52f85089dab0cc
3
+ metadata.gz: ade03e2779ab1cdbeec2d6f3745571abc1713b81
4
+ data.tar.gz: accbf90369fded5cb31bb010b2fbe02c9f887ec3
5
5
  SHA512:
6
- metadata.gz: f8b33066c0816e1ac83c9911e3a8ba5b9ca717344d9bd970709a3555ba92e52f3170886ba3261d5b9b66ed95dd49e4edee63311723dc026f4f302d60c7e058d8
7
- data.tar.gz: 6156cb56046135c814b3e11fe1844236d075a227e12972c2efa63a56ad5079314791a131b2e22136222a7626497af1f23beca04596f0200bb0732ec67b4448cd
6
+ metadata.gz: 4b3c942cfd4e6f6acd1dd8d2042dc0ef7cd50fb795ef2a0e96fcfa97afba700c5778c2c89637a61d79704be3f0d2f0849aea353ce269dd67fb82f293cb4a0eb3
7
+ data.tar.gz: 3c0ddd90d0c21bd82de65d3d792fc0d0f898acd6d2e728a2a03482c86dd001665e38e7ce5c2a881a413bb5c0e9771c85c099f33323ae2d68db7033c93733e60d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # [develop](https://github.com/adhearsion/punchblock)
2
2
 
3
+ # [v2.6.0](https://github.com/adhearsion/punchblock/compare/v2.5.3...v2.6.0) - [2015-02-01](https://rubygems.org/gems/punchblock/versions/2.6.0)
4
+ * Feature: Support recognition-timeout settings on UniMRCP-based ASR on Asterisk ([#228](https://github.com/adhearsion/punchblock/pull/228))
5
+ * Feature: Implement redirect command on Asterisk ([#219](https://github.com/adhearsion/punchblock/pull/219))
6
+ * Feature: Allow arbitrary media headers ([#237](https://github.com/adhearsion/punchblock/pull/237))
7
+ * Bugfix: Complete 1-to-1 mapping of UniMRCP RECOG_COMPLETION_CAUSE values to Punchblock::Complete events
8
+
3
9
  # [v2.5.3](https://github.com/adhearsion/punchblock/compare/v2.5.2...v2.5.3) - [2014-09-22](https://rubygems.org/gems/punchblock/versions/2.5.3)
4
10
  * Bugfix: Prevent Asterisk translator death due to dead DTMF recognizers ([#221](https://github.com/adhearsion/punchblock/pull/221), [adhearsion/adhearsion#479](https://github.com/adhearsion/adhearsion/issues/479))
5
11
  * Bugfix: Be more intelligent about only stripping true file extensions off filenames for playback on Asterisk ([adhearsion/adhearsion#482](https://github.com/adhearsion/adhearsion/issues/482))
@@ -3,6 +3,9 @@
3
3
  module Punchblock
4
4
  module Component
5
5
  class Input < ComponentNode
6
+
7
+ include HasHeaders
8
+
6
9
  NLSML_NAMESPACE = 'http://www.ietf.org/xml/ns/mrcpv2'
7
10
 
8
11
  register :input, :input
@@ -34,6 +37,9 @@ module Punchblock
34
37
  # @return [Integer] Indicates (in the case of DTMF input) the amount of time (in milliseconds) between input digits which may expire before a timeout is triggered.
35
38
  attribute :inter_digit_timeout, Integer
36
39
 
40
+ # @return [Integer] Indicates the amount of time during input that recognition will occur before a timeout is triggered.
41
+ attribute :recognition_timeout, Integer
42
+
37
43
  attribute :grammars, Array, default: []
38
44
  def grammars=(others)
39
45
  super others.map { |other| Grammar.new(other) }
@@ -65,7 +71,8 @@ module Punchblock
65
71
  'terminator' => terminator,
66
72
  'sensitivity' => sensitivity,
67
73
  'initial-timeout' => initial_timeout,
68
- 'inter-digit-timeout' => inter_digit_timeout
74
+ 'inter-digit-timeout' => inter_digit_timeout,
75
+ 'recognition-timeout' => recognition_timeout
69
76
  }
70
77
  end
71
78
 
@@ -3,6 +3,9 @@
3
3
  module Punchblock
4
4
  module Component
5
5
  class Output < ComponentNode
6
+
7
+ include HasHeaders
8
+
6
9
  register :output, :output
7
10
 
8
11
  class Document < RayoNode
@@ -11,7 +11,7 @@ module Punchblock
11
11
  end
12
12
 
13
13
  def inherit(xml_node)
14
- xml_node.xpath('//ns:header', ns: self.class.registered_ns).to_a.each do |header|
14
+ xml_node.xpath('//ns:header', ns: RAYO_NAMESPACES[:core]).to_a.each do |header|
15
15
  if headers.has_key?(header[:name])
16
16
  headers[header[:name]] = [headers[header[:name]]]
17
17
  headers[header[:name]] << header[:value]
@@ -26,7 +26,7 @@ module Punchblock
26
26
  super
27
27
  headers.each do |name, value|
28
28
  Array(value).each do |v|
29
- root.header name: name, value: v
29
+ root.header name: name, value: v, xmlns: RAYO_NAMESPACES[:core]
30
30
  end
31
31
  end
32
32
  end
@@ -212,6 +212,15 @@ module Punchblock
212
212
  execute_agi_command 'EXEC Congestion'
213
213
  end
214
214
  command.response = true
215
+ when Command::Redirect
216
+ execute_agi_command 'EXEC Transfer', command.to
217
+ status = channel_var 'TRANSFERSTATUS'
218
+ command.response = case status
219
+ when 'SUCCESS'
220
+ true
221
+ else
222
+ ProtocolError.new.setup 'error', "TRANSFERSTATUS was #{status}", id
223
+ end
215
224
  when Punchblock::Component::Asterisk::AGI::Command
216
225
  execute_component Component::Asterisk::AGICommand, command
217
226
  when Punchblock::Component::Output
@@ -9,6 +9,16 @@ module Punchblock
9
9
  module MRCPRecogPrompt
10
10
  UniMRCPError = Class.new Punchblock::Error
11
11
 
12
+ MRCP_ERRORS = {
13
+ '004' => 'RECOGNIZE failed due to grammar load failure.',
14
+ '005' => 'RECOGNIZE failed due to grammar compilation failure.',
15
+ '006' => 'RECOGNIZE request terminated prematurely due to a recognizer error.',
16
+ '007' => 'RECOGNIZE request terminated because speech was too early.',
17
+ '009' => 'Failure accessing a URI.',
18
+ '010' => 'Language not supported.',
19
+ '016' => 'Any DEFINE-GRAMMAR error other than grammar-load-failure and grammar-compilation-failure.',
20
+ }
21
+
12
22
  def execute
13
23
  setup_defaults
14
24
  validate
@@ -17,8 +27,8 @@ module Punchblock
17
27
  complete
18
28
  rescue ChannelGoneError
19
29
  call_ended
20
- rescue UniMRCPError
21
- complete_with_error 'Terminated due to UniMRCP error'
30
+ rescue UniMRCPError => e
31
+ complete_with_error e.message
22
32
  rescue RubyAMI::Error => e
23
33
  complete_with_error "Terminated due to AMI error '#{e.message}'"
24
34
  rescue OptionError => e
@@ -34,6 +44,7 @@ module Punchblock
34
44
 
35
45
  raise OptionError, "An initial-timeout value must be -1 or a positive integer." if @initial_timeout < -1
36
46
  raise OptionError, "An inter-digit-timeout value must be -1 or a positive integer." if @inter_digit_timeout < -1
47
+ raise OptionError, "A recognition-timeout value must be -1, 0, or a positive integer." if @recognition_timeout < -1
37
48
  end
38
49
 
39
50
  def execute_app(app, *args)
@@ -48,6 +59,7 @@ module Punchblock
48
59
  opts[:spl] = input_node.language if input_node.language
49
60
  opts[:ct] = input_node.min_confidence if input_node.min_confidence
50
61
  opts[:sl] = input_node.sensitivity if input_node.sensitivity
62
+ opts[:t] = input_node.recognition_timeout if @recognition_timeout > -1
51
63
  yield opts
52
64
  end
53
65
  end
@@ -55,6 +67,7 @@ module Punchblock
55
67
  def setup_defaults
56
68
  @initial_timeout = input_node.initial_timeout || -1
57
69
  @inter_digit_timeout = input_node.inter_digit_timeout || -1
70
+ @recognition_timeout = input_node.recognition_timeout || -1
58
71
  end
59
72
 
60
73
  def grammars
@@ -84,20 +97,24 @@ module Punchblock
84
97
  end
85
98
 
86
99
  def complete
87
- case @call.channel_var('RECOG_STATUS')
100
+ case @call.channel_var('RECOG_STATUS')
88
101
  when 'INTERRUPTED'
89
102
  send_complete_event Punchblock::Component::Input::Complete::NoMatch.new
90
103
  when 'ERROR'
91
104
  raise UniMRCPError
92
- else
93
- send_complete_event case @call.channel_var('RECOG_COMPLETION_CAUSE')
94
- when '000'
105
+ else
106
+ send_complete_event case cause = @call.channel_var('RECOG_COMPLETION_CAUSE')
107
+ when '000', '008', '012'
95
108
  nlsml = RubySpeech.parse URI.decode(@call.channel_var('RECOG_RESULT'))
96
109
  Punchblock::Component::Input::Complete::Match.new nlsml: nlsml
97
- when '001'
110
+ when '001', '003', '013', '014', '015'
98
111
  Punchblock::Component::Input::Complete::NoMatch.new
99
- when '002'
112
+ when '002', '011'
100
113
  Punchblock::Component::Input::Complete::NoInput.new
114
+ when *MRCP_ERRORS.keys
115
+ raise UniMRCPError, MRCP_ERRORS[cause]
116
+ else
117
+ raise UniMRCPError
101
118
  end
102
119
  end
103
120
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Punchblock
4
- VERSION = "2.5.3"
4
+ VERSION = "2.6.0"
5
5
  end
@@ -19,8 +19,10 @@ module Punchblock
19
19
  :language => 'en-US',
20
20
  :initial_timeout => 2000,
21
21
  :inter_digit_timeout => 2000,
22
+ :recognition_timeout => 0,
22
23
  :sensitivity => 0.5,
23
- :min_confidence => 0.5
24
+ :min_confidence => 0.5,
25
+ :headers => { 'Confidence-Threshold' => '0.5', 'Sensitivity-Level' => '0.2' }
24
26
  end
25
27
 
26
28
  describe '#grammars' do
@@ -63,6 +65,11 @@ module Punchblock
63
65
  it { should be == 2000 }
64
66
  end
65
67
 
68
+ describe '#recognition_timeout' do
69
+ subject { super().recognition_timeout }
70
+ it { should be == 0 }
71
+ end
72
+
66
73
  describe '#sensitivity' do
67
74
  subject { super().sensitivity }
68
75
  it { should be == 0.5 }
@@ -106,6 +113,11 @@ module Punchblock
106
113
  end
107
114
  end
108
115
 
116
+ describe '#headers' do
117
+ subject { super().headers }
118
+ it { should be == { 'Confidence-Threshold' => '0.5', 'Sensitivity-Level' => '0.2' } }
119
+ end
120
+
109
121
  describe "exporting to Rayo" do
110
122
  it "should export to XML that can be understood by its parser" do
111
123
  new_instance = RayoNode.from_xml subject.to_rayo
@@ -118,8 +130,10 @@ module Punchblock
118
130
  expect(new_instance.language).to eq('en-US')
119
131
  expect(new_instance.initial_timeout).to eq(2000)
120
132
  expect(new_instance.inter_digit_timeout).to eq(2000)
133
+ expect(new_instance.recognition_timeout).to eq(0)
121
134
  expect(new_instance.sensitivity).to eq(0.5)
122
135
  expect(new_instance.min_confidence).to eq(0.5)
136
+ expect(new_instance.headers).to eq({ 'Confidence-Threshold' => '0.5', 'Sensitivity-Level' => '0.2' })
123
137
  end
124
138
 
125
139
  it "should wrap the grammar value in CDATA" do
@@ -148,6 +162,7 @@ module Punchblock
148
162
  language="en-US"
149
163
  initial-timeout="2000"
150
164
  inter-digit-timeout="2000"
165
+ recognition-timeout="0"
151
166
  sensitivity="0.5"
152
167
  min-confidence="0.5">
153
168
  <grammar content-type="application/grammar+custom">
@@ -156,6 +171,8 @@ module Punchblock
156
171
  <grammar content-type="application/grammar+custom">
157
172
  <![CDATA[ [10 DIGITS] ]]>
158
173
  </grammar>
174
+ <header xmlns='urn:xmpp:rayo:1' name="Confidence-Threshold" value="0.5" />
175
+ <header xmlns='urn:xmpp:rayo:1' name="Sensitivity-Level" value="0.2" />
159
176
  </input>
160
177
  MESSAGE
161
178
  end
@@ -204,6 +221,11 @@ module Punchblock
204
221
  it { should be == 2000 }
205
222
  end
206
223
 
224
+ describe '#recognition_timeout' do
225
+ subject { super().recognition_timeout }
226
+ it { should be == 0 }
227
+ end
228
+
207
229
  describe '#sensitivity' do
208
230
  subject { super().sensitivity }
209
231
  it { should be == 0.5 }
@@ -222,6 +244,11 @@ module Punchblock
222
244
  it { should be == [] }
223
245
  end
224
246
  end
247
+
248
+ describe '#headers' do
249
+ subject { super().headers }
250
+ it { should be == { 'Confidence-Threshold' => '0.5', 'Sensitivity-Level' => '0.2' } }
251
+ end
225
252
  end
226
253
 
227
254
  def grxml_doc(mode = :dtmf)
@@ -72,7 +72,8 @@ module Punchblock
72
72
  :max_time => 30000,
73
73
  :voice => 'allison',
74
74
  :renderer => 'swift',
75
- :render_document => {:value => ssml_doc}
75
+ :render_document => {:value => ssml_doc},
76
+ :headers => { 'Jump-Size' => '2', 'Kill-On-Barge-In' => 'false' }
76
77
  end
77
78
 
78
79
  describe '#interrupt_on' do
@@ -185,6 +186,11 @@ module Punchblock
185
186
  end
186
187
  end
187
188
 
189
+ describe '#headers' do
190
+ subject { super().headers }
191
+ it { should be == { 'Jump-Size' => '2', 'Kill-On-Barge-In' => 'false' } }
192
+ end
193
+
188
194
  describe "exporting to Rayo" do
189
195
  it "should export to XML that can be understood by its parser" do
190
196
  new_instance = RayoNode.from_xml Nokogiri::XML(subject.to_rayo.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
@@ -198,6 +204,7 @@ module Punchblock
198
204
  expect(new_instance.voice).to eq('allison')
199
205
  expect(new_instance.renderer).to eq('swift')
200
206
  expect(new_instance.render_documents).to eq([Output::Document.new(:value => ssml_doc)])
207
+ expect(new_instance.headers).to eq({ 'Jump-Size' => '2', 'Kill-On-Barge-In' => 'false' })
201
208
  end
202
209
 
203
210
  it "should wrap the document value in CDATA" do
@@ -258,6 +265,8 @@ module Punchblock
258
265
  </speak>
259
266
  ]]>
260
267
  </document>
268
+ <header xmlns='urn:xmpp:rayo:1' name="Jump-Size" value="2" />
269
+ <header xmlns='urn:xmpp:rayo:1' name="Kill-On-Barge-In" value="false" />
261
270
  </output>
262
271
  MESSAGE
263
272
  end
@@ -330,6 +339,11 @@ module Punchblock
330
339
  it { should be == [Output::Document.new(content_type: 'text/uri-list', value: ['http://example.com/hello.mp3', 'http://example.com/goodbye.mp3'])] }
331
340
  end
332
341
  end
342
+
343
+ describe '#headers' do
344
+ subject { super().headers }
345
+ it { should be == { 'Jump-Size' => '2', 'Kill-On-Barge-In' => 'false' } }
346
+ end
333
347
  end
334
348
 
335
349
  describe Output::Document do
@@ -1166,6 +1166,71 @@ module Punchblock
1166
1166
  end
1167
1167
  end
1168
1168
 
1169
+ context 'with a redirect command' do
1170
+ let(:command) { Command::Redirect.new to: 'other@place.com' }
1171
+
1172
+ let(:transferstatus) { 'SUCCESS' }
1173
+ let :status_event do
1174
+ RubyAMI::Event.new 'VarSet',
1175
+ "Privilege" => "dialplan,all",
1176
+ "Channel" => "SIP/1234-00000000",
1177
+ "Variable" => "TRANSFERSTATUS",
1178
+ "Value" => transferstatus,
1179
+ "Uniqueid" => "1326210224.0"
1180
+ end
1181
+
1182
+ before do
1183
+ subject.process_ami_event status_event
1184
+ end
1185
+
1186
+ it "should send an EXEC Transfer AGI command" do
1187
+ subject.should_receive(:execute_agi_command).with('EXEC Transfer', 'other@place.com').and_return code: 200
1188
+ subject.execute_command command
1189
+ command.response(0.5).should be true
1190
+ end
1191
+
1192
+ context "when TRANSFERSTATUS is 'FAILURE'" do
1193
+ let(:transferstatus) { 'FAILURE' }
1194
+
1195
+ it "should return an error" do
1196
+ subject.should_receive(:execute_agi_command).with('EXEC Transfer', 'other@place.com').and_return code: 200
1197
+ subject.execute_command command
1198
+ command.response(0.5).should be == ProtocolError.new.setup('error', 'TRANSFERSTATUS was FAILURE', subject.id)
1199
+ end
1200
+ end
1201
+
1202
+ context "when TRANSFERSTATUS is 'UNSUPPORTED'" do
1203
+ let(:transferstatus) { 'UNSUPPORTED' }
1204
+
1205
+ it "should return an error" do
1206
+ subject.should_receive(:execute_agi_command).with('EXEC Transfer', 'other@place.com').and_return code: 200
1207
+ subject.execute_command command
1208
+ command.response(0.5).should be == ProtocolError.new.setup('error', 'TRANSFERSTATUS was UNSUPPORTED', subject.id)
1209
+ end
1210
+ end
1211
+
1212
+ context "when the AMI commannd raises an error" do
1213
+ let(:message) { 'Some error' }
1214
+ let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
1215
+
1216
+ before { subject.should_receive(:execute_agi_command).and_raise error }
1217
+
1218
+ it "should return an error with the message" do
1219
+ subject.execute_command command
1220
+ command.response(0.5).should be == ProtocolError.new.setup('error', message, subject.id)
1221
+ end
1222
+
1223
+ context "because the channel is gone" do
1224
+ let(:error) { ChannelGoneError }
1225
+
1226
+ it "should return an :item_not_found event for the call" do
1227
+ subject.execute_command command
1228
+ command.response(0.5).should be == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{subject.id}", subject.id)
1229
+ end
1230
+ end
1231
+ end
1232
+ end
1233
+
1169
1234
  context 'with a reject command' do
1170
1235
  let(:command) { Command::Reject.new }
1171
1236
 
@@ -182,7 +182,7 @@ module Punchblock
182
182
  end
183
183
 
184
184
  context "when MRCPRecog completes" do
185
- context "with a match" do
185
+ shared_context "with a match" do
186
186
  let :expected_nlsml do
187
187
  RubySpeech::NLSML.draw do
188
188
  interpretation grammar: 'session:grammar-0', confidence: 0.43 do
@@ -201,39 +201,73 @@ module Punchblock
201
201
  end
202
202
  end
203
203
 
204
- context "with a nomatch cause" do
205
- let(:recog_completion_cause) { '001' }
204
+ context "with a match cause" do
205
+ %w{000 008 012}.each do |code|
206
+ context "when the MRCP recognition response code is #{code}" do
207
+ let(:recog_completion_cause) { code }
206
208
 
207
- it 'should send a nomatch complete event' do
208
- expected_complete_reason = Punchblock::Component::Input::Complete::NoMatch.new
209
- expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
210
- subject.execute
211
- expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
209
+ it_behaves_like 'with a match'
210
+ end
212
211
  end
213
212
  end
214
213
 
215
- context "with a noinput cause" do
216
- let(:recog_completion_cause) { '002' }
217
-
218
- it 'should send a nomatch complete event' do
219
- expected_complete_reason = Punchblock::Component::Input::Complete::NoInput.new
220
- expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
221
- subject.execute
222
- expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
214
+ context "with a nomatch cause" do
215
+ %w{001 003 013 014 015}.each do |code|
216
+ context "with value #{code}" do
217
+ let(:recog_completion_cause) { code }
218
+
219
+ it 'should send a nomatch complete event' do
220
+ expected_complete_reason = Punchblock::Component::Input::Complete::NoMatch.new
221
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
222
+ subject.execute
223
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
224
+ end
225
+ end
223
226
  end
224
227
  end
225
228
 
226
- context "when the RECOG_STATUS variable is set to 'ERROR'" do
227
- let(:recog_status) { 'ERROR' }
229
+ context "with a noinput cause" do
230
+ %w{002 011}.each do |code|
231
+ context "with value #{code}" do
232
+ let(:recog_completion_cause) { code }
233
+
234
+ specify do
235
+ expected_complete_reason = Punchblock::Component::Input::Complete::NoInput.new
236
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
237
+ subject.execute
238
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
239
+ end
240
+ end
241
+ end
242
+ end
228
243
 
229
- it "should send an error complete event" do
244
+ shared_context 'should send an error complete event' do
245
+ specify do
230
246
  expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
231
247
  subject.execute
232
248
  complete_reason = original_command.complete_event(0.1).reason
233
249
  expect(complete_reason).to be_a Punchblock::Event::Complete::Error
234
- expect(complete_reason.details).to eq("Terminated due to UniMRCP error")
235
250
  end
236
251
  end
252
+
253
+ context 'with an error cause' do
254
+ %w{004 005 006 007 009 010 016}.each do |code|
255
+ context "when the MRCP recognition response code is #{code}" do
256
+ let(:recog_completion_cause) { code }
257
+ it_behaves_like 'should send an error complete event'
258
+ end
259
+ end
260
+ end
261
+
262
+ context 'with an invalid cause' do
263
+ let(:recog_completion_cause) { '999' }
264
+ it_behaves_like 'should send an error complete event'
265
+ end
266
+
267
+ context "when the RECOG_STATUS variable is set to 'ERROR'" do
268
+ let(:recog_status) { 'ERROR' }
269
+ it_behaves_like 'should send an error complete event'
270
+ end
237
271
  end
238
272
 
239
273
  context "when we get a RubyAMI Error" do
@@ -174,7 +174,7 @@ module Punchblock
174
174
  end
175
175
 
176
176
  context "when SynthAndRecog completes" do
177
- context "with a match" do
177
+ shared_context "with a match" do
178
178
  let :expected_nlsml do
179
179
  RubySpeech::NLSML.draw do
180
180
  interpretation grammar: 'session:grammar-0', confidence: 0.43 do
@@ -193,49 +193,72 @@ module Punchblock
193
193
  end
194
194
  end
195
195
 
196
- context "with a nomatch cause" do
197
- let(:recog_completion_cause) { '001' }
196
+ context "with a match cause" do
197
+ %w{000 008 012}.each do |code|
198
+ context "when the MRCP recognition response code is #{code}" do
199
+ let(:recog_completion_cause) { code }
198
200
 
199
- it 'should send a nomatch complete event' do
200
- expected_complete_reason = Punchblock::Component::Input::Complete::NoMatch.new
201
- expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
202
- subject.execute
203
- expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
201
+ it_behaves_like 'with a match'
202
+ end
203
+ end
204
+ end
205
+
206
+ context "with a nomatch cause" do
207
+ %w{001 003 013 014 015}.each do |code|
208
+ context "with value #{code}" do
209
+ let(:recog_completion_cause) { code }
210
+
211
+ it 'should send a nomatch complete event' do
212
+ expected_complete_reason = Punchblock::Component::Input::Complete::NoMatch.new
213
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
214
+ subject.execute
215
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
216
+ end
217
+ end
204
218
  end
205
219
  end
206
220
 
207
221
  context "with a noinput cause" do
208
- let(:recog_completion_cause) { '002' }
222
+ %w{002 011}.each do |code|
223
+ context "with value #{code}" do
224
+ let(:recog_completion_cause) { code }
225
+
226
+ specify do
227
+ expected_complete_reason = Punchblock::Component::Input::Complete::NoInput.new
228
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
229
+ subject.execute
230
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
231
+ end
232
+ end
233
+ end
234
+ end
209
235
 
210
- it 'should send a nomatch complete event' do
211
- expected_complete_reason = Punchblock::Component::Input::Complete::NoInput.new
236
+ shared_context 'should send an error complete event' do
237
+ specify do
212
238
  expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
213
239
  subject.execute
214
- expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
240
+ complete_reason = original_command.complete_event(0.1).reason
241
+ expect(complete_reason).to be_a Punchblock::Event::Complete::Error
215
242
  end
216
243
  end
217
244
 
218
- context "when the RECOG_STATUS variable is set to 'INTERRUPTED' after a successful recognition" do
219
- let(:recog_status) { 'INTERRUPTED' }
220
-
221
- it "should send an error complete event" do
222
- expected_complete_reason = Punchblock::Component::Input::Complete::NoMatch.new
223
- expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
224
- subject.execute
225
- expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
245
+ context 'with an error cause' do
246
+ %w{004 005 006 007 009 010 016}.each do |code|
247
+ context "when the MRCP recognition response code is #{code}" do
248
+ let(:recog_completion_cause) { code }
249
+ it_behaves_like 'should send an error complete event'
250
+ end
226
251
  end
227
252
  end
228
253
 
254
+ context 'with an invalid cause' do
255
+ let(:recog_completion_cause) { '999' }
256
+ it_behaves_like 'should send an error complete event'
257
+ end
258
+
229
259
  context "when the RECOG_STATUS variable is set to 'ERROR'" do
230
260
  let(:recog_status) { 'ERROR' }
231
-
232
- it "should send an error complete event" do
233
- expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
234
- subject.execute
235
- complete_reason = original_command.complete_event(0.1).reason
236
- expect(complete_reason).to be_a Punchblock::Event::Complete::Error
237
- expect(complete_reason.details).to eq("Terminated due to UniMRCP error")
238
- end
261
+ it_behaves_like 'should send an error complete event'
239
262
  end
240
263
  end
241
264
 
@@ -511,6 +534,45 @@ module Punchblock
511
534
  end
512
535
  end
513
536
 
537
+ describe 'Input#recognition-timeout' do
538
+ context 'a positive number' do
539
+ let(:input_command_opts) { { recognition_timeout: 1000 } }
540
+
541
+ it 'should pass the t option to SynthAndRecog' do
542
+ expect_synthandrecog_with_options(/t=1000/)
543
+ subject.execute
544
+ end
545
+ end
546
+
547
+ context '0' do
548
+ let(:input_command_opts) { { recognition_timeout: 0 } }
549
+
550
+ it 'should pass the t option to SynthAndRecog' do
551
+ expect_synthandrecog_with_options(/t=0/)
552
+ subject.execute
553
+ end
554
+ end
555
+
556
+ context 'a negative number' do
557
+ let(:input_command_opts) { { recognition_timeout: -1000 } }
558
+
559
+ it "should return an error and not execute any actions" do
560
+ subject.execute
561
+ error = ProtocolError.new.setup 'option error', 'A recognition-timeout value must be -1, 0, or a positive integer.'
562
+ expect(original_command.response(0.1)).to eq(error)
563
+ end
564
+ end
565
+
566
+ context 'unset' do
567
+ let(:input_command_opts) { { recognition_timeout: nil } }
568
+
569
+ it 'should not pass any options to SynthAndRecog' do
570
+ expect_synthandrecog_with_options(//)
571
+ subject.execute
572
+ end
573
+ end
574
+ end
575
+
514
576
  describe 'Input#inter-digit-timeout' do
515
577
  context 'a positive number' do
516
578
  let(:input_command_opts) { { inter_digit_timeout: 1000 } }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: punchblock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.3
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Goecke
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-09-22 00:00:00.000000000 Z
13
+ date: 2015-02-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
@@ -559,7 +559,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
559
559
  version: 1.3.7
560
560
  requirements: []
561
561
  rubyforge_project: punchblock
562
- rubygems_version: 2.2.2
562
+ rubygems_version: 2.4.5
563
563
  signing_key:
564
564
  specification_version: 4
565
565
  summary: Punchblock is a telephony middleware library