punchblock 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/CHANGELOG.md +7 -0
- data/lib/punchblock.rb +1 -1
- data/lib/punchblock/component.rb +2 -0
- data/lib/punchblock/component/input.rb +9 -1
- data/lib/punchblock/component/output.rb +1 -1
- data/lib/punchblock/component/receive_fax.rb +24 -0
- data/lib/punchblock/component/send_fax.rb +62 -0
- data/lib/punchblock/connection/asterisk.rb +1 -1
- data/lib/punchblock/event/complete.rb +15 -1
- data/lib/punchblock/translator/asterisk.rb +30 -15
- data/lib/punchblock/translator/asterisk/call.rb +13 -27
- data/lib/punchblock/translator/asterisk/component.rb +4 -7
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +1 -5
- data/lib/punchblock/translator/asterisk/component/composed_prompt.rb +8 -9
- data/lib/punchblock/translator/asterisk/component/input.rb +2 -3
- data/lib/punchblock/translator/asterisk/component/mrcp_prompt.rb +9 -9
- data/lib/punchblock/translator/asterisk/component/output.rb +134 -39
- data/lib/punchblock/translator/asterisk/component/record.rb +2 -3
- data/lib/punchblock/translator/asterisk/component/stop_by_redirect.rb +2 -3
- data/lib/punchblock/translator/dtmf_recognizer.rb +2 -4
- data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +6 -1
- data/lib/punchblock/translator/freeswitch/component/flite_output.rb +1 -1
- data/lib/punchblock/translator/freeswitch/component/output.rb +12 -10
- data/lib/punchblock/translator/freeswitch/component/tts_output.rb +1 -1
- data/lib/punchblock/version.rb +1 -1
- data/spec/punchblock/component/input_spec.rb +91 -0
- data/spec/punchblock/component/output_spec.rb +1 -2
- data/spec/punchblock/component/receive_fax_spec.rb +111 -0
- data/spec/punchblock/component/send_fax_spec.rb +110 -0
- data/spec/punchblock/connection/asterisk_spec.rb +1 -1
- data/spec/punchblock/translator/asterisk/call_spec.rb +53 -79
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +0 -3
- data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +1 -1
- data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +2 -2
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +6 -6
- data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +3 -3
- data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +9 -11
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +902 -28
- data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +1 -1
- data/spec/punchblock/translator/asterisk/component_spec.rb +2 -9
- data/spec/punchblock/translator/asterisk_spec.rb +42 -94
- data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +5 -5
- data/spec/punchblock/translator/freeswitch/component/output_spec.rb +7 -3
- data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +17 -5
- metadata +67 -61
data/lib/punchblock/version.rb
CHANGED
@@ -166,6 +166,10 @@ module Punchblock
|
|
166
166
|
it { should be == grammar2 }
|
167
167
|
it { should_not be == grammar3 }
|
168
168
|
end
|
169
|
+
|
170
|
+
it "has children nested inside" do
|
171
|
+
subject.to_rayo.children.first.should be_a Nokogiri::XML::CDATA
|
172
|
+
end
|
169
173
|
end
|
170
174
|
|
171
175
|
describe 'with a grammar reference by URL' do
|
@@ -186,6 +190,14 @@ module Punchblock
|
|
186
190
|
end
|
187
191
|
end
|
188
192
|
end
|
193
|
+
|
194
|
+
describe "with a CPA grammar" do
|
195
|
+
subject { Input::Grammar.new url: "urn:xmpp:rayo:cpa:beep:1" }
|
196
|
+
|
197
|
+
it "has no children" do
|
198
|
+
subject.to_rayo.children.count.should == 0
|
199
|
+
end
|
200
|
+
end
|
189
201
|
end
|
190
202
|
|
191
203
|
describe "actions" do
|
@@ -378,6 +390,85 @@ module Punchblock
|
|
378
390
|
|
379
391
|
its(:name) { should be == :noinput }
|
380
392
|
end
|
393
|
+
|
394
|
+
describe Input::Signal do
|
395
|
+
let :stanza do
|
396
|
+
<<-MESSAGE
|
397
|
+
<signal xmlns="urn:xmpp:rayo:cpa:1" type="urn:xmpp:rayo:cpa:beep:1" duration="1000" value="8000"/>
|
398
|
+
MESSAGE
|
399
|
+
end
|
400
|
+
|
401
|
+
subject { RayoNode.from_xml(parse_stanza(stanza).root) }
|
402
|
+
|
403
|
+
it { should be_instance_of Input::Signal }
|
404
|
+
|
405
|
+
its(:name) { should be == :signal }
|
406
|
+
its(:type) { should be == 'urn:xmpp:rayo:cpa:beep:1' }
|
407
|
+
its(:duration) { should be == 1000 }
|
408
|
+
its(:value) { should be == '8000' }
|
409
|
+
|
410
|
+
describe "when creating from options" do
|
411
|
+
subject do
|
412
|
+
Input::Signal.new type: 'urn:xmpp:rayo:cpa:beep:1', duration: 1000, value: '8000'
|
413
|
+
end
|
414
|
+
|
415
|
+
its(:name) { should be == :signal }
|
416
|
+
its(:type) { should be == 'urn:xmpp:rayo:cpa:beep:1' }
|
417
|
+
its(:duration) { should be == 1000 }
|
418
|
+
its(:value) { should be == '8000' }
|
419
|
+
end
|
420
|
+
|
421
|
+
context "when in a complete event" do
|
422
|
+
let :stanza do
|
423
|
+
<<-MESSAGE
|
424
|
+
<complete xmlns='urn:xmpp:rayo:ext:1'>
|
425
|
+
<signal xmlns="urn:xmpp:rayo:cpa:1" type="urn:xmpp:rayo:cpa:beep:1" duration="1000" value="8000"/>
|
426
|
+
</complete>
|
427
|
+
MESSAGE
|
428
|
+
end
|
429
|
+
|
430
|
+
subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
|
431
|
+
|
432
|
+
it { should be_instance_of Input::Signal }
|
433
|
+
|
434
|
+
its(:name) { should be == :signal }
|
435
|
+
its(:type) { should be == 'urn:xmpp:rayo:cpa:beep:1' }
|
436
|
+
its(:duration) { should be == 1000 }
|
437
|
+
its(:value) { should be == '8000' }
|
438
|
+
end
|
439
|
+
|
440
|
+
describe "comparison" do
|
441
|
+
context "with the same options" do
|
442
|
+
it "should be equal" do
|
443
|
+
subject.should == RayoNode.from_xml(parse_stanza(stanza).root)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
context "with different type" do
|
448
|
+
let(:other_stanza) { '<signal xmlns="urn:xmpp:rayo:cpa:1" type="urn:xmpp:rayo:cpa:ring:1" duration="1000" value="8000"/>' }
|
449
|
+
|
450
|
+
it "should not be equal" do
|
451
|
+
subject.should_not == RayoNode.from_xml(parse_stanza(other_stanza).root)
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
context "with different duration" do
|
456
|
+
let(:other_stanza) { '<signal xmlns="urn:xmpp:rayo:cpa:1" type="urn:xmpp:rayo:cpa:beep:1" duration="100" value="8000"/>' }
|
457
|
+
|
458
|
+
it "should not be equal" do
|
459
|
+
subject.should_not == RayoNode.from_xml(parse_stanza(other_stanza).root)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
context "with different value" do
|
464
|
+
let(:other_stanza) { '<signal xmlns="urn:xmpp:rayo:cpa:1" type="urn:xmpp:rayo:cpa:beep:1" duration="1000" value="7000"/>' }
|
465
|
+
|
466
|
+
it "should not be equal" do
|
467
|
+
subject.should_not == RayoNode.from_xml(parse_stanza(other_stanza).root)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
381
472
|
end
|
382
473
|
end
|
383
474
|
end # Punchblock
|
@@ -12,7 +12,7 @@ module Punchblock
|
|
12
12
|
describe 'default values' do
|
13
13
|
its(:interrupt_on) { should be nil }
|
14
14
|
its(:start_offset) { should be nil }
|
15
|
-
its(:start_paused) { should be
|
15
|
+
its(:start_paused) { should be nil }
|
16
16
|
its(:repeat_interval) { should be nil }
|
17
17
|
its(:repeat_times) { should be nil }
|
18
18
|
its(:max_time) { should be nil }
|
@@ -84,7 +84,6 @@ module Punchblock
|
|
84
84
|
|
85
85
|
describe "exporting to Rayo" do
|
86
86
|
it "should export to XML that can be understood by its parser" do
|
87
|
-
puts subject.to_rayo.to_xml
|
88
87
|
new_instance = RayoNode.from_xml Nokogiri::XML(subject.to_rayo.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
|
89
88
|
new_instance.render_documents.should be == [Output::Document.new(content_type: 'text/uri-list', value: ['http://example.com/hello.mp3'])]
|
90
89
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module Punchblock
|
6
|
+
module Component
|
7
|
+
describe ReceiveFax do
|
8
|
+
it 'registers itself' do
|
9
|
+
RayoNode.class_from_registration(:receivefax, 'urn:xmpp:rayo:fax:1').should be == described_class
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "exporting to Rayo" do
|
13
|
+
it "should export to XML that can be understood by its parser" do
|
14
|
+
new_instance = RayoNode.from_xml subject.to_rayo
|
15
|
+
new_instance.should be_instance_of described_class
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should render to a parent node if supplied" do
|
19
|
+
doc = Nokogiri::XML::Document.new
|
20
|
+
parent = Nokogiri::XML::Node.new 'foo', doc
|
21
|
+
doc.root = parent
|
22
|
+
rayo_doc = subject.to_rayo(parent)
|
23
|
+
rayo_doc.should == parent
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "from a stanza" do
|
28
|
+
let :stanza do
|
29
|
+
<<-MESSAGE
|
30
|
+
<receivefax xmlns="urn:xmpp:rayo:fax:1"/>
|
31
|
+
MESSAGE
|
32
|
+
end
|
33
|
+
|
34
|
+
subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
|
35
|
+
|
36
|
+
it { should be_instance_of described_class }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "actions" do
|
40
|
+
let(:mock_client) { double 'Client' }
|
41
|
+
let(:command) { described_class.new }
|
42
|
+
|
43
|
+
before do
|
44
|
+
command.component_id = 'abc123'
|
45
|
+
command.target_call_id = '123abc'
|
46
|
+
command.client = mock_client
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#stop_action' do
|
50
|
+
subject { command.stop_action }
|
51
|
+
|
52
|
+
its(:to_xml) { should be == '<stop xmlns="urn:xmpp:rayo:ext:1"/>' }
|
53
|
+
its(:component_id) { should be == 'abc123' }
|
54
|
+
its(:target_call_id) { should be == '123abc' }
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#stop!' do
|
58
|
+
describe "when the command is executing" do
|
59
|
+
before do
|
60
|
+
command.request!
|
61
|
+
command.execute!
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should send its command properly" do
|
65
|
+
mock_client.should_receive(:execute_command).with(command.stop_action, :target_call_id => '123abc', :component_id => 'abc123')
|
66
|
+
command.stop!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "when the command is not executing" do
|
71
|
+
it "should raise an error" do
|
72
|
+
lambda { command.stop! }.should raise_error(InvalidActionError, "Cannot stop a ReceiveFax that is new")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ReceiveFax::Complete::Finish do
|
79
|
+
let :stanza do
|
80
|
+
<<-MESSAGE
|
81
|
+
<complete xmlns='urn:xmpp:rayo:ext:1'>
|
82
|
+
<finish xmlns='urn:xmpp:rayo:fax:complete:1'/>
|
83
|
+
<fax xmlns='urn:xmpp:rayo:fax:complete:1' url='http://shakespere.lit/faxes/fax1.tiff' resolution='595x841' size='12287492817' pages='3'/>
|
84
|
+
<metadata xmlns='urn:xmpp:rayo:fax:complete:1' name="fax-transfer-rate" value="10000" />
|
85
|
+
<metadata xmlns='urn:xmpp:rayo:fax:complete:1' name="foo" value="true" />
|
86
|
+
</complete>
|
87
|
+
MESSAGE
|
88
|
+
end
|
89
|
+
|
90
|
+
subject(:complete_node) { RayoNode.from_xml(parse_stanza(stanza).root) }
|
91
|
+
|
92
|
+
it "should understand a finish reason" do
|
93
|
+
subject.reason.should be_instance_of ReceiveFax::Complete::Finish
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "should make the fax data available" do
|
97
|
+
subject { complete_node.fax }
|
98
|
+
|
99
|
+
it { should be_instance_of ReceiveFax::Fax }
|
100
|
+
|
101
|
+
its(:url) { should be == 'http://shakespere.lit/faxes/fax1.tiff' }
|
102
|
+
its(:resolution) { should be == '595x841' }
|
103
|
+
its(:pages) { should be == 3 }
|
104
|
+
its(:size) { should be == 12287492817 }
|
105
|
+
end
|
106
|
+
|
107
|
+
its(:fax_metadata) { should == {'fax-transfer-rate' => '10000', 'foo' => 'true'} }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module Punchblock
|
6
|
+
module Component
|
7
|
+
describe SendFax do
|
8
|
+
it 'registers itself' do
|
9
|
+
RayoNode.class_from_registration(:sendfax, 'urn:xmpp:rayo:fax:1').should be == described_class
|
10
|
+
end
|
11
|
+
|
12
|
+
subject do
|
13
|
+
SendFax.new render_documents: [SendFax::FaxDocument.new(url: 'http://example.com/faxes/document.tiff', pages: [1..4,5,7..9])]
|
14
|
+
end
|
15
|
+
|
16
|
+
its(:render_documents) { should be == [SendFax::FaxDocument.new(url: 'http://example.com/faxes/document.tiff', pages: [1..4,5,7..9])] }
|
17
|
+
|
18
|
+
describe "exporting to Rayo" do
|
19
|
+
it "should export to XML that can be understood by its parser" do
|
20
|
+
new_instance = RayoNode.from_xml Nokogiri::XML(subject.to_rayo.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
|
21
|
+
new_instance.render_documents.should be == [SendFax::FaxDocument.new(url: 'http://example.com/faxes/document.tiff', pages: [1..4,5,7..9])]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "without optional attributes" do
|
26
|
+
subject do
|
27
|
+
SendFax.new render_documents: [SendFax::FaxDocument.new(url: 'http://example.com/faxes/document.tiff')]
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "exporting to Rayo" do
|
31
|
+
it "should export to XML that can be understood by its parser" do
|
32
|
+
new_instance = RayoNode.from_xml Nokogiri::XML(subject.to_rayo.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root
|
33
|
+
new_instance.render_documents.should be == [SendFax::FaxDocument.new(url: 'http://example.com/faxes/document.tiff')]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "from a rayo stanza" do
|
39
|
+
|
40
|
+
subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
|
41
|
+
|
42
|
+
let :stanza do
|
43
|
+
<<-MESSAGE
|
44
|
+
<sendfax xmlns='urn:xmpp:rayo:fax:1'>
|
45
|
+
<document xmlns='urn:xmpp:rayo:fax:1' url='http://shakespere.lit/my_fax.tiff' identity='+14045555555' header='Hello world' pages='1-4,5,7-9'/>
|
46
|
+
</sendfax>
|
47
|
+
MESSAGE
|
48
|
+
end
|
49
|
+
|
50
|
+
its(:render_documents) { should be == [SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555555', header: 'Hello world', pages: [1..4,5,7..9])] }
|
51
|
+
|
52
|
+
context "without optional attributes" do
|
53
|
+
let :stanza do
|
54
|
+
<<-MESSAGE
|
55
|
+
<sendfax xmlns='urn:xmpp:rayo:fax:1'>
|
56
|
+
<document xmlns='urn:xmpp:rayo:fax:1' url='http://shakespere.lit/my_fax.tiff'/>
|
57
|
+
</sendfax>
|
58
|
+
MESSAGE
|
59
|
+
end
|
60
|
+
|
61
|
+
its(:render_documents) { should be == [SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff')] }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe SendFax::FaxDocument do
|
67
|
+
it "registers itself" do
|
68
|
+
RayoNode.class_from_registration(:document, 'urn:xmpp:rayo:fax:1').should be == described_class
|
69
|
+
end
|
70
|
+
|
71
|
+
subject { SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555555', header: 'Hello world', pages: [1..4,5,7..9]) }
|
72
|
+
|
73
|
+
its(:url) { should == 'http://shakespere.lit/my_fax.tiff' }
|
74
|
+
its(:identity) { should == '+14045555555' }
|
75
|
+
its(:header) { should == 'Hello world' }
|
76
|
+
its(:pages) { should == [1..4,5,7..9] }
|
77
|
+
|
78
|
+
context "without optional attributes" do
|
79
|
+
subject { SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff') }
|
80
|
+
|
81
|
+
its(:url) { should == 'http://shakespere.lit/my_fax.tiff' }
|
82
|
+
its(:identity) { should be_nil }
|
83
|
+
its(:header) { should be_nil }
|
84
|
+
its(:pages) { should be_nil }
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "comparison" do
|
88
|
+
it "should be the same with the same attributes" do
|
89
|
+
should be == SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555555', header: 'Hello world', pages: [1..4,5,7..9])
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be different with a different url" do
|
93
|
+
should_not be == SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_other_fax.tiff', identity: '+14045555555', header: 'Hello world', pages: [1..4,5,7..9])
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be different with a different identity" do
|
97
|
+
should_not be == SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555556', header: 'Hello world', pages: [1..4,5,7..9])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should be different with a different header" do
|
101
|
+
should_not be == SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555555', header: 'Hello Paul', pages: [1..4,5,7..9])
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should be different with a different pages" do
|
105
|
+
should_not be == SendFax::FaxDocument.new(url: 'http://shakespere.lit/my_fax.tiff', identity: '+14045555555', header: 'Hello world', pages: [1..4,5,6..9])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -69,14 +69,6 @@ module Punchblock
|
|
69
69
|
|
70
70
|
before { translator.stub :handle_pb_event }
|
71
71
|
|
72
|
-
describe '#shutdown' do
|
73
|
-
it 'should terminate the actor' do
|
74
|
-
subject.shutdown
|
75
|
-
sleep 0.5
|
76
|
-
subject.should_not be_alive
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
72
|
describe '#register_component' do
|
81
73
|
it 'should make the component accessible by ID' do
|
82
74
|
component_id = 'abc123'
|
@@ -123,15 +115,15 @@ module Punchblock
|
|
123
115
|
describe '#send_progress' do
|
124
116
|
context "with a call that is already answered" do
|
125
117
|
it 'should not send the EXEC Progress command' do
|
126
|
-
subject.
|
127
|
-
subject.
|
118
|
+
subject.should_receive(:'answered?').and_return true
|
119
|
+
subject.should_receive(:execute_agi_command).with("EXEC Progress").never
|
128
120
|
subject.send_progress
|
129
121
|
end
|
130
122
|
end
|
131
123
|
|
132
124
|
context "with an unanswered call" do
|
133
125
|
before do
|
134
|
-
subject.
|
126
|
+
subject.should_receive(:'answered?').at_least(:once).and_return(false)
|
135
127
|
end
|
136
128
|
|
137
129
|
context "with a call that is outbound" do
|
@@ -143,7 +135,7 @@ module Punchblock
|
|
143
135
|
end
|
144
136
|
|
145
137
|
it 'should not send the EXEC Progress command' do
|
146
|
-
subject.
|
138
|
+
subject.should_receive(:execute_agi_command).with("EXEC Progress").never
|
147
139
|
subject.send_progress
|
148
140
|
end
|
149
141
|
end
|
@@ -154,12 +146,12 @@ module Punchblock
|
|
154
146
|
end
|
155
147
|
|
156
148
|
it 'should send the EXEC Progress command to a call that is inbound and not answered' do
|
157
|
-
subject.
|
149
|
+
subject.should_receive(:execute_agi_command).with("EXEC Progress").and_return code: 200, result: 0
|
158
150
|
subject.send_progress
|
159
151
|
end
|
160
152
|
|
161
153
|
it 'should send the EXEC Progress command only once if called twice' do
|
162
|
-
subject.
|
154
|
+
subject.should_receive(:execute_agi_command).with("EXEC Progress").once.and_return code: 200, result: 0
|
163
155
|
subject.send_progress
|
164
156
|
subject.send_progress
|
165
157
|
end
|
@@ -276,7 +268,7 @@ module Punchblock
|
|
276
268
|
subject.dial dial_command
|
277
269
|
accept_command = Command::Accept.new
|
278
270
|
accept_command.request!
|
279
|
-
subject.
|
271
|
+
subject.should_receive(:execute_agi_command).never
|
280
272
|
subject.execute_command accept_command
|
281
273
|
accept_command.response(0.5).should be true
|
282
274
|
end
|
@@ -297,13 +289,6 @@ module Punchblock
|
|
297
289
|
let(:cause) { '16' }
|
298
290
|
let(:cause_txt) { 'Normal Clearing' }
|
299
291
|
|
300
|
-
it "should cause the actor to be terminated" do
|
301
|
-
translator.should_receive(:handle_pb_event).twice
|
302
|
-
subject.process_ami_event ami_event
|
303
|
-
Celluloid::Actor.join(subject, 1)
|
304
|
-
subject.should_not be_alive
|
305
|
-
end
|
306
|
-
|
307
292
|
it "de-registers the call from the translator" do
|
308
293
|
translator.stub :handle_pb_event
|
309
294
|
translator.should_receive(:deregister_call).once.with(subject.id, subject.channel)
|
@@ -334,7 +319,7 @@ module Punchblock
|
|
334
319
|
component = subject.execute_command comp_command
|
335
320
|
comp_command.response(0.1).should be_a Ref
|
336
321
|
|
337
|
-
subject.
|
322
|
+
subject.process_ami_event ami_event
|
338
323
|
|
339
324
|
comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar root="foo"><rule id="foo"/></grammar>'}, :mode => :dtmf
|
340
325
|
comp_command.request!
|
@@ -657,7 +642,7 @@ module Punchblock
|
|
657
642
|
before do
|
658
643
|
translator.register_call other_call
|
659
644
|
command.request!
|
660
|
-
subject.
|
645
|
+
subject.should_receive(:execute_agi_command).and_return code: 200
|
661
646
|
subject.execute_command command
|
662
647
|
end
|
663
648
|
|
@@ -860,6 +845,15 @@ module Punchblock
|
|
860
845
|
subject.process_ami_event ami_event
|
861
846
|
end
|
862
847
|
|
848
|
+
context "when the event doesn't pass the filter" do
|
849
|
+
before { Asterisk.event_filter = ->(event) { false } }
|
850
|
+
after { Asterisk.event_filter = nil }
|
851
|
+
|
852
|
+
it 'does not send the AMI event to the connection as a PB event' do
|
853
|
+
translator.should_receive(:handle_pb_event).never
|
854
|
+
subject.process_ami_event ami_event
|
855
|
+
end
|
856
|
+
end
|
863
857
|
end
|
864
858
|
|
865
859
|
describe '#execute_command' do
|
@@ -871,7 +865,7 @@ module Punchblock
|
|
871
865
|
let(:command) { Command::Accept.new }
|
872
866
|
|
873
867
|
it "should send an EXEC RINGING AGI command and set the command's response" do
|
874
|
-
subject.
|
868
|
+
subject.should_receive(:execute_agi_command).with('EXEC RINGING').and_return code: 200
|
875
869
|
subject.execute_command command
|
876
870
|
command.response(0.5).should be true
|
877
871
|
end
|
@@ -880,7 +874,7 @@ module Punchblock
|
|
880
874
|
let(:message) { 'Some error' }
|
881
875
|
let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
|
882
876
|
|
883
|
-
before { subject.
|
877
|
+
before { subject.should_receive(:execute_agi_command).and_raise error }
|
884
878
|
|
885
879
|
it "should return an error with the message" do
|
886
880
|
subject.execute_command command
|
@@ -903,7 +897,7 @@ module Punchblock
|
|
903
897
|
|
904
898
|
it "with a :busy reason should send an EXEC Busy AGI command and set the command's response" do
|
905
899
|
command.reason = :busy
|
906
|
-
subject.
|
900
|
+
subject.should_receive(:execute_agi_command).with('EXEC Busy').and_return code: 200
|
907
901
|
subject.execute_command command
|
908
902
|
command.response(0.5).should be true
|
909
903
|
end
|
@@ -917,7 +911,7 @@ module Punchblock
|
|
917
911
|
|
918
912
|
it "with an :error reason should send an EXEC Congestion AGI command and set the command's response" do
|
919
913
|
command.reason = :error
|
920
|
-
subject.
|
914
|
+
subject.should_receive(:execute_agi_command).with('EXEC Congestion').and_return code: 200
|
921
915
|
subject.execute_command command
|
922
916
|
command.response(0.5).should be true
|
923
917
|
end
|
@@ -926,7 +920,7 @@ module Punchblock
|
|
926
920
|
let(:message) { 'Some error' }
|
927
921
|
let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
|
928
922
|
|
929
|
-
before { subject.
|
923
|
+
before { subject.should_receive(:execute_agi_command).and_raise error }
|
930
924
|
|
931
925
|
it "should return an error with the message" do
|
932
926
|
subject.execute_command command
|
@@ -948,13 +942,13 @@ module Punchblock
|
|
948
942
|
let(:command) { Command::Answer.new }
|
949
943
|
|
950
944
|
it "should send an ANSWER AGI command and set the command's response" do
|
951
|
-
subject.
|
945
|
+
subject.should_receive(:execute_agi_command).with('ANSWER').and_return code: 200
|
952
946
|
subject.execute_command command
|
953
947
|
command.response(0.5).should be true
|
954
948
|
end
|
955
949
|
|
956
950
|
it "should be answered" do
|
957
|
-
subject.
|
951
|
+
subject.should_receive(:execute_agi_command)
|
958
952
|
subject.execute_command command
|
959
953
|
subject.should be_answered
|
960
954
|
end
|
@@ -963,7 +957,7 @@ module Punchblock
|
|
963
957
|
let(:message) { 'Some error' }
|
964
958
|
let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
|
965
959
|
|
966
|
-
before { subject.
|
960
|
+
before { subject.should_receive(:execute_agi_command).and_raise error }
|
967
961
|
|
968
962
|
it "should return an error with the message" do
|
969
963
|
subject.execute_command command
|
@@ -1042,7 +1036,7 @@ module Punchblock
|
|
1042
1036
|
before { translator.should_receive(:call_with_id).with(other_call_id).and_return(other_call) }
|
1043
1037
|
|
1044
1038
|
it "executes the proper dialplan Bridge application" do
|
1045
|
-
subject.
|
1039
|
+
subject.should_receive(:execute_agi_command).with('EXEC Bridge', "#{other_channel},F(#{REDIRECT_CONTEXT},#{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY})").and_return code: 200
|
1046
1040
|
subject.execute_command command
|
1047
1041
|
end
|
1048
1042
|
|
@@ -1050,7 +1044,7 @@ module Punchblock
|
|
1050
1044
|
let(:message) { 'Some error' }
|
1051
1045
|
let(:error) { RubyAMI::Error.new.tap { |e| e.message = message } }
|
1052
1046
|
|
1053
|
-
before { subject.
|
1047
|
+
before { subject.should_receive(:execute_agi_command).and_raise error }
|
1054
1048
|
|
1055
1049
|
it "should return an error with the message" do
|
1056
1050
|
subject.execute_command command
|
@@ -1156,11 +1150,10 @@ module Punchblock
|
|
1156
1150
|
Punchblock::Component::Asterisk::AGI::Command.new :name => 'Answer'
|
1157
1151
|
end
|
1158
1152
|
|
1159
|
-
let(:mock_action) { Translator::Asterisk::Component::Asterisk::AGICommand.new(command, subject) }
|
1160
|
-
|
1161
1153
|
it 'should create an AGI command component actor and execute it asynchronously' do
|
1162
|
-
Component::Asterisk::AGICommand.
|
1163
|
-
|
1154
|
+
mock_action = Translator::Asterisk::Component::Asterisk::AGICommand.new(command, subject)
|
1155
|
+
Component::Asterisk::AGICommand.should_receive(:new).once.with(command, subject).and_return mock_action
|
1156
|
+
mock_action.should_receive(:execute).once
|
1164
1157
|
subject.execute_command command
|
1165
1158
|
end
|
1166
1159
|
end
|
@@ -1170,11 +1163,10 @@ module Punchblock
|
|
1170
1163
|
Punchblock::Component::Output.new
|
1171
1164
|
end
|
1172
1165
|
|
1173
|
-
let(:mock_action) { Translator::Asterisk::Component::Output.new(command, subject) }
|
1174
|
-
|
1175
1166
|
it 'should create an Output component and execute it asynchronously' do
|
1176
|
-
Component::Output.
|
1177
|
-
|
1167
|
+
mock_action = Translator::Asterisk::Component::Output.new(command, subject)
|
1168
|
+
Component::Output.should_receive(:new).once.with(command, subject).and_return mock_action
|
1169
|
+
mock_action.should_receive(:execute).once
|
1178
1170
|
subject.execute_command command
|
1179
1171
|
end
|
1180
1172
|
end
|
@@ -1184,11 +1176,10 @@ module Punchblock
|
|
1184
1176
|
Punchblock::Component::Input.new
|
1185
1177
|
end
|
1186
1178
|
|
1187
|
-
let(:mock_action) { Translator::Asterisk::Component::Input.new(command, subject) }
|
1188
|
-
|
1189
1179
|
it 'should create an Input component and execute it asynchronously' do
|
1190
|
-
Component::Input.
|
1191
|
-
|
1180
|
+
mock_action = Translator::Asterisk::Component::Input.new(command, subject)
|
1181
|
+
Component::Input.should_receive(:new).once.with(command, subject).and_return mock_action
|
1182
|
+
mock_action.should_receive(:execute).once
|
1192
1183
|
subject.execute_command command
|
1193
1184
|
end
|
1194
1185
|
end
|
@@ -1224,13 +1215,15 @@ module Punchblock
|
|
1224
1215
|
|
1225
1216
|
let(:mock_action) { Translator::Asterisk::Component::MRCPPrompt.new(command, subject) }
|
1226
1217
|
|
1218
|
+
before { mock_action}
|
1219
|
+
|
1227
1220
|
context "when the recognizer is unimrcp and the renderer is unimrcp" do
|
1228
1221
|
let(:recognizer) { :unimrcp }
|
1229
1222
|
let(:renderer) { :unimrcp }
|
1230
1223
|
|
1231
1224
|
it 'should create an MRCPPrompt component and execute it asynchronously' do
|
1232
|
-
Component::MRCPPrompt.should_receive(:
|
1233
|
-
mock_action.
|
1225
|
+
Component::MRCPPrompt.should_receive(:new).once.with(command, subject).and_return mock_action
|
1226
|
+
mock_action.should_receive(:execute).once
|
1234
1227
|
subject.execute_command command
|
1235
1228
|
end
|
1236
1229
|
end
|
@@ -1240,8 +1233,8 @@ module Punchblock
|
|
1240
1233
|
let(:renderer) { :asterisk }
|
1241
1234
|
|
1242
1235
|
it 'should create an MRCPPrompt component and execute it asynchronously' do
|
1243
|
-
Component::MRCPNativePrompt.should_receive(:
|
1244
|
-
mock_action.
|
1236
|
+
Component::MRCPNativePrompt.should_receive(:new).once.with(command, subject).and_return mock_action
|
1237
|
+
mock_action.should_receive(:execute).once
|
1245
1238
|
subject.execute_command command
|
1246
1239
|
end
|
1247
1240
|
end
|
@@ -1261,8 +1254,8 @@ module Punchblock
|
|
1261
1254
|
let(:renderer) { :unimrcp }
|
1262
1255
|
|
1263
1256
|
it 'should create a ComposedPrompt component and execute it asynchronously' do
|
1264
|
-
Component::ComposedPrompt.should_receive(:
|
1265
|
-
mock_action.
|
1257
|
+
Component::ComposedPrompt.should_receive(:new).once.with(command, subject).and_return mock_action
|
1258
|
+
mock_action.should_receive(:execute).once
|
1266
1259
|
subject.execute_command command
|
1267
1260
|
end
|
1268
1261
|
end
|
@@ -1273,11 +1266,10 @@ module Punchblock
|
|
1273
1266
|
Punchblock::Component::Record.new
|
1274
1267
|
end
|
1275
1268
|
|
1276
|
-
let(:mock_action) { Translator::Asterisk::Component::Record.new(command, subject) }
|
1277
|
-
|
1278
1269
|
it 'should create a Record component and execute it asynchronously' do
|
1279
|
-
Component::Record.
|
1280
|
-
|
1270
|
+
mock_action = Translator::Asterisk::Component::Record.new(command, subject)
|
1271
|
+
Component::Record.should_receive(:new).once.with(command, subject).and_return mock_action
|
1272
|
+
mock_action.should_receive(:execute).once
|
1281
1273
|
subject.execute_command command
|
1282
1274
|
end
|
1283
1275
|
end
|
@@ -1327,9 +1319,8 @@ module Punchblock
|
|
1327
1319
|
it 'sends an error in response to the command' do
|
1328
1320
|
component = subject.component_with_id comp_id
|
1329
1321
|
|
1330
|
-
component.
|
1331
|
-
|
1332
|
-
component.should_not be_alive
|
1322
|
+
component.send_complete_event Punchblock::Component::Asterisk::AGI::Command::Complete.new
|
1323
|
+
|
1333
1324
|
subject.component_with_id(comp_id).should be_nil
|
1334
1325
|
|
1335
1326
|
subsequent_command.request!
|
@@ -1339,25 +1330,6 @@ module Punchblock
|
|
1339
1330
|
end
|
1340
1331
|
|
1341
1332
|
context "by crashing" do
|
1342
|
-
it 'sends an error in response to the command' do
|
1343
|
-
component = subject.component_with_id comp_id
|
1344
|
-
|
1345
|
-
component.wrapped_object.define_singleton_method(:oops) do
|
1346
|
-
raise 'Woops, I died'
|
1347
|
-
end
|
1348
|
-
|
1349
|
-
translator.should_receive(:handle_pb_event).once.with expected_event
|
1350
|
-
|
1351
|
-
lambda { component.oops }.should raise_error(/Woops, I died/)
|
1352
|
-
sleep 0.1
|
1353
|
-
component.should_not be_alive
|
1354
|
-
subject.component_with_id(comp_id).should be_nil
|
1355
|
-
|
1356
|
-
subsequent_command.request!
|
1357
|
-
subject.execute_command subsequent_command
|
1358
|
-
subsequent_command.response.should be == ProtocolError.new.setup(:item_not_found, "Could not find a component with ID #{comp_id} for call #{subject.id}", subject.id, comp_id)
|
1359
|
-
end
|
1360
|
-
|
1361
1333
|
context "when we dispatch the command to it" do
|
1362
1334
|
it 'sends an error in response to the command' do
|
1363
1335
|
component = subject.component_with_id comp_id
|
@@ -1459,7 +1431,9 @@ module Punchblock
|
|
1459
1431
|
end
|
1460
1432
|
|
1461
1433
|
it 'should return the result' do
|
1462
|
-
fut = subject.
|
1434
|
+
fut = Celluloid::Future.new { subject.execute_agi_command 'EXEC ANSWER' }
|
1435
|
+
|
1436
|
+
sleep 0.25
|
1463
1437
|
|
1464
1438
|
subject.process_ami_event ami_event
|
1465
1439
|
|