punchblock 1.3.0 → 1.4.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.
- data/CHANGELOG.md +5 -0
- data/lib/punchblock.rb +1 -1
- data/lib/punchblock/connection.rb +1 -0
- data/lib/punchblock/connection/asterisk.rb +0 -1
- data/lib/punchblock/connection/freeswitch.rb +49 -0
- data/lib/punchblock/event/offer.rb +1 -1
- data/lib/punchblock/translator.rb +5 -0
- data/lib/punchblock/translator/asterisk.rb +16 -28
- data/lib/punchblock/translator/asterisk/call.rb +4 -21
- data/lib/punchblock/translator/asterisk/component.rb +0 -5
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +0 -3
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +0 -1
- data/lib/punchblock/translator/asterisk/component/input.rb +7 -97
- data/lib/punchblock/translator/asterisk/component/output.rb +0 -4
- data/lib/punchblock/translator/asterisk/component/record.rb +0 -2
- data/lib/punchblock/translator/freeswitch.rb +153 -0
- data/lib/punchblock/translator/freeswitch/call.rb +265 -0
- data/lib/punchblock/translator/freeswitch/component.rb +92 -0
- data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +57 -0
- data/lib/punchblock/translator/freeswitch/component/flite_output.rb +17 -0
- data/lib/punchblock/translator/freeswitch/component/input.rb +29 -0
- data/lib/punchblock/translator/freeswitch/component/output.rb +56 -0
- data/lib/punchblock/translator/freeswitch/component/record.rb +79 -0
- data/lib/punchblock/translator/freeswitch/component/tts_output.rb +26 -0
- data/lib/punchblock/translator/input_component.rb +108 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +3 -2
- data/spec/punchblock/connection/freeswitch_spec.rb +90 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +23 -2
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +3 -3
- data/spec/punchblock/translator/asterisk_spec.rb +1 -1
- data/spec/punchblock/translator/freeswitch/call_spec.rb +922 -0
- data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +279 -0
- data/spec/punchblock/translator/freeswitch/component/input_spec.rb +312 -0
- data/spec/punchblock/translator/freeswitch/component/output_spec.rb +369 -0
- data/spec/punchblock/translator/freeswitch/component/record_spec.rb +373 -0
- data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +285 -0
- data/spec/punchblock/translator/freeswitch/component_spec.rb +118 -0
- data/spec/punchblock/translator/freeswitch_spec.rb +597 -0
- data/spec/punchblock_spec.rb +11 -0
- data/spec/spec_helper.rb +1 -0
- metadata +52 -7
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
module Punchblock
|
|
6
|
+
module Translator
|
|
7
|
+
class Freeswitch
|
|
8
|
+
module Component
|
|
9
|
+
describe FliteOutput do
|
|
10
|
+
let(:connection) do
|
|
11
|
+
mock_connection_with_event_handler do |event|
|
|
12
|
+
original_command.add_event event
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
let(:media_engine) { :flite }
|
|
16
|
+
let(:default_voice) { nil }
|
|
17
|
+
let(:translator) { Punchblock::Translator::Freeswitch.new connection }
|
|
18
|
+
let(:mock_call) { Punchblock::Translator::Freeswitch::Call.new 'foo', translator }
|
|
19
|
+
|
|
20
|
+
let :original_command do
|
|
21
|
+
Punchblock::Component::Output.new command_options
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let :ssml_doc do
|
|
25
|
+
RubySpeech::SSML.draw do
|
|
26
|
+
say_as(:interpret_as => :cardinal) { 'FOO' }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
let :command_options do
|
|
31
|
+
{ :ssml => ssml_doc }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def execute
|
|
35
|
+
subject.execute media_engine, default_voice
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
subject { described_class.new original_command, mock_call }
|
|
39
|
+
|
|
40
|
+
describe '#execute' do
|
|
41
|
+
before { original_command.request! }
|
|
42
|
+
def expect_playback(voice = :kal)
|
|
43
|
+
subject.wrapped_object.expects(:application).once.with :speak, "#{media_engine}|#{voice}|FOO"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
let(:command_opts) { {} }
|
|
47
|
+
|
|
48
|
+
let :command_options do
|
|
49
|
+
{ :ssml => ssml_doc }.merge(command_opts)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
let :original_command do
|
|
53
|
+
Punchblock::Component::Output.new command_options
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe 'ssml' do
|
|
57
|
+
context 'unset' do
|
|
58
|
+
let(:command_opts) { { :ssml => nil } }
|
|
59
|
+
it "should return an error and not execute any actions" do
|
|
60
|
+
execute
|
|
61
|
+
error = ProtocolError.new.setup 'option error', 'An SSML document is required.'
|
|
62
|
+
original_command.response(0.1).should be == error
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context 'with an SSML node' do
|
|
67
|
+
it 'should speak the document using the speak application' do
|
|
68
|
+
expect_playback
|
|
69
|
+
execute
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'should send a complete event when the speak finishes' do
|
|
73
|
+
expect_playback.yields true
|
|
74
|
+
execute
|
|
75
|
+
subject.handle_es_event RubyFS::Event.new(nil, :event_name => "CHANNEL_EXECUTE_COMPLETE")
|
|
76
|
+
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe 'start-offset' do
|
|
82
|
+
context 'unset' do
|
|
83
|
+
let(:command_opts) { { :start_offset => nil } }
|
|
84
|
+
it 'should not pass any options to Playback' do
|
|
85
|
+
expect_playback
|
|
86
|
+
execute
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context 'set' do
|
|
91
|
+
let(:command_opts) { { :start_offset => 10 } }
|
|
92
|
+
it "should return an error and not execute any actions" do
|
|
93
|
+
execute
|
|
94
|
+
error = ProtocolError.new.setup 'option error', 'A start_offset value is unsupported.'
|
|
95
|
+
original_command.response(0.1).should be == error
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe 'start-paused' do
|
|
101
|
+
context 'false' do
|
|
102
|
+
let(:command_opts) { { :start_paused => false } }
|
|
103
|
+
it 'should not pass any options to Playback' do
|
|
104
|
+
expect_playback
|
|
105
|
+
execute
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'true' do
|
|
110
|
+
let(:command_opts) { { :start_paused => true } }
|
|
111
|
+
it "should return an error and not execute any actions" do
|
|
112
|
+
execute
|
|
113
|
+
error = ProtocolError.new.setup 'option error', 'A start_paused value is unsupported.'
|
|
114
|
+
original_command.response(0.1).should be == error
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe 'repeat-interval' do
|
|
120
|
+
context 'unset' do
|
|
121
|
+
let(:command_opts) { { :repeat_interval => nil } }
|
|
122
|
+
it 'should not pass any options to Playback' do
|
|
123
|
+
expect_playback
|
|
124
|
+
execute
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'set' do
|
|
129
|
+
let(:command_opts) { { :repeat_interval => 10 } }
|
|
130
|
+
it "should return an error and not execute any actions" do
|
|
131
|
+
execute
|
|
132
|
+
error = ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported.'
|
|
133
|
+
original_command.response(0.1).should be == error
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe 'repeat-times' do
|
|
139
|
+
context 'unset' do
|
|
140
|
+
let(:command_opts) { { :repeat_times => nil } }
|
|
141
|
+
it 'should not pass any options to Playback' do
|
|
142
|
+
expect_playback
|
|
143
|
+
execute
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context 'set' do
|
|
148
|
+
let(:command_opts) { { :repeat_times => 2 } }
|
|
149
|
+
it "should return an error and not execute any actions" do
|
|
150
|
+
execute
|
|
151
|
+
error = ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported.'
|
|
152
|
+
original_command.response(0.1).should be == error
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe 'max-time' do
|
|
158
|
+
context 'unset' do
|
|
159
|
+
let(:command_opts) { { :max_time => nil } }
|
|
160
|
+
it 'should not pass any options to Playback' do
|
|
161
|
+
expect_playback
|
|
162
|
+
execute
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'set' do
|
|
167
|
+
let(:command_opts) { { :max_time => 30 } }
|
|
168
|
+
it "should return an error and not execute any actions" do
|
|
169
|
+
execute
|
|
170
|
+
error = ProtocolError.new.setup 'option error', 'A max_time value is unsupported.'
|
|
171
|
+
original_command.response(0.1).should be == error
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe 'voice' do
|
|
177
|
+
context 'unset' do
|
|
178
|
+
let(:command_opts) { { :voice => nil } }
|
|
179
|
+
it 'should use the default voice' do
|
|
180
|
+
expect_playback
|
|
181
|
+
execute
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context 'set' do
|
|
186
|
+
let(:command_opts) { { :voice => 'alison' } }
|
|
187
|
+
it "should execute speak with the specified voice" do
|
|
188
|
+
expect_playback 'alison'
|
|
189
|
+
execute
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
describe 'interrupt_on' do
|
|
195
|
+
context "set to nil" do
|
|
196
|
+
let(:command_opts) { { :interrupt_on => nil } }
|
|
197
|
+
it "should not pass any digits to Playback" do
|
|
198
|
+
expect_playback
|
|
199
|
+
execute
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
context "set to :any" do
|
|
204
|
+
let(:command_opts) { { :interrupt_on => :any } }
|
|
205
|
+
it "should return an error and not execute any actions" do
|
|
206
|
+
execute
|
|
207
|
+
error = ProtocolError.new.setup 'option error', 'An interrupt-on value of any is unsupported.'
|
|
208
|
+
original_command.response(0.1).should be == error
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
context "set to :dtmf" do
|
|
213
|
+
let(:command_opts) { { :interrupt_on => :dtmf } }
|
|
214
|
+
it "should return an error and not execute any actions" do
|
|
215
|
+
execute
|
|
216
|
+
error = ProtocolError.new.setup 'option error', 'An interrupt-on value of dtmf is unsupported.'
|
|
217
|
+
original_command.response(0.1).should be == error
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context "set to :speech" do
|
|
222
|
+
let(:command_opts) { { :interrupt_on => :speech } }
|
|
223
|
+
it "should return an error and not execute any actions" do
|
|
224
|
+
execute
|
|
225
|
+
error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.'
|
|
226
|
+
original_command.response(0.1).should be == error
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
describe "#execute_command" do
|
|
233
|
+
context "with a command it does not understand" do
|
|
234
|
+
let(:command) { Punchblock::Component::Output::Pause.new }
|
|
235
|
+
|
|
236
|
+
before { command.request! }
|
|
237
|
+
|
|
238
|
+
it "returns a ProtocolError response" do
|
|
239
|
+
subject.execute_command command
|
|
240
|
+
command.response(0.1).should be_a ProtocolError
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
context "with a Stop command" do
|
|
245
|
+
let(:command) { Punchblock::Component::Stop.new }
|
|
246
|
+
let(:reason) { original_command.complete_event(5).reason }
|
|
247
|
+
|
|
248
|
+
before do
|
|
249
|
+
command.request!
|
|
250
|
+
original_command.request!
|
|
251
|
+
original_command.execute!
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it "sets the command response to true" do
|
|
255
|
+
subject.wrapped_object.expects(:application)
|
|
256
|
+
subject.execute_command command
|
|
257
|
+
command.response(0.1).should be == true
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "sends the correct complete event" do
|
|
261
|
+
subject.wrapped_object.expects(:application)
|
|
262
|
+
original_command.should_not be_complete
|
|
263
|
+
subject.execute_command command
|
|
264
|
+
reason.should be_a Punchblock::Event::Complete::Stop
|
|
265
|
+
original_command.should be_complete
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "breaks the current dialplan application" do
|
|
269
|
+
subject.wrapped_object.expects(:application).once.with 'break'
|
|
270
|
+
subject.execute_command command
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
module Punchblock
|
|
6
|
+
module Translator
|
|
7
|
+
class Freeswitch
|
|
8
|
+
module Component
|
|
9
|
+
describe Input do
|
|
10
|
+
let(:connection) do
|
|
11
|
+
mock_connection_with_event_handler do |event|
|
|
12
|
+
original_command.add_event event
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
let(:id) { Punchblock.new_uuid }
|
|
16
|
+
let(:translator) { Punchblock::Translator::Freeswitch.new connection }
|
|
17
|
+
let(:mock_stream) { mock('RubyFS::Stream') }
|
|
18
|
+
let(:call) { Punchblock::Translator::Freeswitch::Call.new id, translator, nil, mock_stream }
|
|
19
|
+
|
|
20
|
+
let(:original_command_options) { {} }
|
|
21
|
+
|
|
22
|
+
let :original_command do
|
|
23
|
+
Punchblock::Component::Input.new original_command_options
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let :grammar do
|
|
27
|
+
RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
|
|
28
|
+
rule id: 'digit' do
|
|
29
|
+
one_of do
|
|
30
|
+
0.upto(9) { |d| item { d.to_s } }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
rule id: 'pin', scope: 'public' do
|
|
35
|
+
item repeat: '2' do
|
|
36
|
+
ruleref uri: '#digit'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subject { Input.new original_command, call }
|
|
43
|
+
|
|
44
|
+
describe '#execute' do
|
|
45
|
+
before { original_command.request! }
|
|
46
|
+
|
|
47
|
+
let(:original_command_opts) { {} }
|
|
48
|
+
|
|
49
|
+
let :original_command_options do
|
|
50
|
+
{ :mode => :dtmf, :grammar => { :value => grammar } }.merge(original_command_opts)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def dtmf_event(digit)
|
|
54
|
+
RubyFS::Event.new nil, {
|
|
55
|
+
:event_name => 'DTMF',
|
|
56
|
+
:dtmf_digit => digit.to_s,
|
|
57
|
+
:dtmf_duration => "1600"
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def send_dtmf(digit)
|
|
62
|
+
call.handle_es_event dtmf_event(digit)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
let(:reason) { original_command.complete_event(5).reason }
|
|
66
|
+
|
|
67
|
+
describe "receiving DTMF events" do
|
|
68
|
+
before do
|
|
69
|
+
subject.execute
|
|
70
|
+
expected_event
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "when a match is found" do
|
|
74
|
+
before do
|
|
75
|
+
send_dtmf 1
|
|
76
|
+
send_dtmf 2
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
let :expected_event do
|
|
80
|
+
Punchblock::Component::Input::Complete::Success.new :mode => :dtmf,
|
|
81
|
+
:confidence => 1,
|
|
82
|
+
:utterance => '12',
|
|
83
|
+
:interpretation => 'dtmf-1 dtmf-2',
|
|
84
|
+
:component_id => subject.id,
|
|
85
|
+
:target_call_id => call.id
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "should send a success complete event with the relevant data" do
|
|
89
|
+
reason.should be == expected_event
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should not process further dtmf events" do
|
|
93
|
+
subject.expects(:process_dtmf!).never
|
|
94
|
+
send_dtmf 3
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context "when the match is invalid" do
|
|
99
|
+
before do
|
|
100
|
+
send_dtmf 1
|
|
101
|
+
send_dtmf '#'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
let :expected_event do
|
|
105
|
+
Punchblock::Component::Input::Complete::NoMatch.new :component_id => subject.id,
|
|
106
|
+
:target_call_id => call.id
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should send a nomatch complete event" do
|
|
110
|
+
reason.should be == expected_event
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe 'grammar' do
|
|
116
|
+
context 'unset' do
|
|
117
|
+
let(:original_command_opts) { { :grammar => nil } }
|
|
118
|
+
it "should return an error and not execute any actions" do
|
|
119
|
+
subject.execute
|
|
120
|
+
error = ProtocolError.new.setup 'option error', 'A grammar document is required.'
|
|
121
|
+
original_command.response(0.1).should be == error
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe 'mode' do
|
|
127
|
+
context 'unset' do
|
|
128
|
+
let(:original_command_opts) { { :mode => nil } }
|
|
129
|
+
it "should return an error and not execute any actions" do
|
|
130
|
+
subject.execute
|
|
131
|
+
error = ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
|
|
132
|
+
original_command.response(0.1).should be == error
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context 'any' do
|
|
137
|
+
let(:original_command_opts) { { :mode => :any } }
|
|
138
|
+
it "should return an error and not execute any actions" do
|
|
139
|
+
subject.execute
|
|
140
|
+
error = ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
|
|
141
|
+
original_command.response(0.1).should be == error
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context 'speech' do
|
|
146
|
+
let(:original_command_opts) { { :mode => :speech } }
|
|
147
|
+
it "should return an error and not execute any actions" do
|
|
148
|
+
subject.execute
|
|
149
|
+
error = ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
|
|
150
|
+
original_command.response(0.1).should be == error
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe 'terminator' do
|
|
156
|
+
pending
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe 'recognizer' do
|
|
160
|
+
pending
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe 'initial-timeout' do
|
|
164
|
+
context 'a positive number' do
|
|
165
|
+
let(:original_command_opts) { { :initial_timeout => 1000 } }
|
|
166
|
+
|
|
167
|
+
it "should not cause a NoInput if first input is received in time" do
|
|
168
|
+
subject.execute
|
|
169
|
+
send_dtmf 1
|
|
170
|
+
sleep 1.5
|
|
171
|
+
send_dtmf 2
|
|
172
|
+
reason.should be_a Punchblock::Component::Input::Complete::Success
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "should cause a NoInput complete event to be sent after the timeout" do
|
|
176
|
+
subject.execute
|
|
177
|
+
sleep 1.5
|
|
178
|
+
send_dtmf 1
|
|
179
|
+
send_dtmf 2
|
|
180
|
+
reason.should be_a Punchblock::Component::Input::Complete::NoInput
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context '-1' do
|
|
185
|
+
let(:original_command_opts) { { :initial_timeout => -1 } }
|
|
186
|
+
|
|
187
|
+
it "should not start a timer" do
|
|
188
|
+
subject.wrapped_object.expects(:begin_initial_timer).never
|
|
189
|
+
subject.execute
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
context 'unset' do
|
|
194
|
+
let(:original_command_opts) { { :initial_timeout => nil } }
|
|
195
|
+
|
|
196
|
+
it "should not start a timer" do
|
|
197
|
+
subject.wrapped_object.expects(:begin_initial_timer).never
|
|
198
|
+
subject.execute
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context 'a negative number other than -1' do
|
|
203
|
+
let(:original_command_opts) { { :initial_timeout => -1000 } }
|
|
204
|
+
|
|
205
|
+
it "should return an error and not execute any actions" do
|
|
206
|
+
subject.execute
|
|
207
|
+
error = ProtocolError.new.setup 'option error', 'An initial timeout value that is negative (and not -1) is invalid.'
|
|
208
|
+
original_command.response(0.1).should be == error
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
describe 'inter-digit-timeout' do
|
|
214
|
+
context 'a positive number' do
|
|
215
|
+
let(:original_command_opts) { { :inter_digit_timeout => 1000 } }
|
|
216
|
+
|
|
217
|
+
it "should not prevent a Match if input is received in time" do
|
|
218
|
+
subject.execute
|
|
219
|
+
sleep 1.5
|
|
220
|
+
send_dtmf 1
|
|
221
|
+
sleep 0.5
|
|
222
|
+
send_dtmf 2
|
|
223
|
+
reason.should be_a Punchblock::Component::Input::Complete::Success
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it "should cause a NoMatch complete event to be sent after the timeout" do
|
|
227
|
+
subject.execute
|
|
228
|
+
sleep 1.5
|
|
229
|
+
send_dtmf 1
|
|
230
|
+
sleep 1.5
|
|
231
|
+
send_dtmf 2
|
|
232
|
+
reason.should be_a Punchblock::Component::Input::Complete::NoMatch
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
context '-1' do
|
|
237
|
+
let(:original_command_opts) { { :inter_digit_timeout => -1 } }
|
|
238
|
+
|
|
239
|
+
it "should not start a timer" do
|
|
240
|
+
subject.wrapped_object.expects(:begin_inter_digit_timer).never
|
|
241
|
+
subject.execute
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
context 'unset' do
|
|
246
|
+
let(:original_command_opts) { { :inter_digit_timeout => nil } }
|
|
247
|
+
|
|
248
|
+
it "should not start a timer" do
|
|
249
|
+
subject.wrapped_object.expects(:begin_inter_digit_timer).never
|
|
250
|
+
subject.execute
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
context 'a negative number other than -1' do
|
|
255
|
+
let(:original_command_opts) { { :inter_digit_timeout => -1000 } }
|
|
256
|
+
|
|
257
|
+
it "should return an error and not execute any actions" do
|
|
258
|
+
subject.execute
|
|
259
|
+
error = ProtocolError.new.setup 'option error', 'An inter-digit timeout value that is negative (and not -1) is invalid.'
|
|
260
|
+
original_command.response(0.1).should be == error
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
describe 'sensitivity' do
|
|
266
|
+
pending
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
describe 'min-confidence' do
|
|
270
|
+
pending
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
describe 'max-silence' do
|
|
274
|
+
pending
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
describe "#execute_command" do
|
|
279
|
+
context "with a command it does not understand" do
|
|
280
|
+
let(:command) { Punchblock::Component::Output::Pause.new }
|
|
281
|
+
|
|
282
|
+
before { command.request! }
|
|
283
|
+
|
|
284
|
+
it "returns a ProtocolError response" do
|
|
285
|
+
subject.execute_command command
|
|
286
|
+
command.response(0.1).should be_a ProtocolError
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
context "with a Stop command" do
|
|
291
|
+
let(:command) { Punchblock::Component::Stop.new }
|
|
292
|
+
let(:reason) { original_command.complete_event(5).reason }
|
|
293
|
+
|
|
294
|
+
before do
|
|
295
|
+
command.request!
|
|
296
|
+
original_command.request!
|
|
297
|
+
original_command.execute!
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "sets the command response to true" do
|
|
301
|
+
subject.execute_command command
|
|
302
|
+
command.response(0.1).should be == true
|
|
303
|
+
reason.should be_a Punchblock::Event::Complete::Stop
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|