punchblock 2.5.3 → 2.6.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.
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