punchblock 1.8.2 → 1.9.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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/lib/punchblock/component/asterisk/agi/command.rb +0 -11
- data/lib/punchblock/connection/asterisk.rb +3 -3
- data/lib/punchblock/translator/asterisk/agi_command.rb +40 -0
- data/lib/punchblock/translator/asterisk/call.rb +56 -47
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +13 -37
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +24 -41
- data/lib/punchblock/translator/asterisk/component/input.rb +2 -1
- data/lib/punchblock/translator/asterisk/component/output.rb +16 -21
- data/lib/punchblock/translator/asterisk/component/record.rb +11 -19
- data/lib/punchblock/translator/asterisk/component.rb +12 -9
- data/lib/punchblock/translator/asterisk.rb +16 -22
- data/lib/punchblock/translator/dtmf_recognizer.rb +4 -4
- data/lib/punchblock/translator/freeswitch/component/input.rb +2 -1
- data/lib/punchblock/translator/input_component.rb +2 -2
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -1
- data/spec/punchblock/connection/asterisk_spec.rb +8 -7
- data/spec/punchblock/translator/asterisk/call_spec.rb +262 -229
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +57 -29
- data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +40 -46
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +7 -7
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +84 -53
- data/spec/punchblock/translator/asterisk/component/record_spec.rb +55 -83
- data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +5 -1
- data/spec/punchblock/translator/asterisk/component_spec.rb +2 -10
- data/spec/punchblock/translator/asterisk_spec.rb +73 -100
- metadata +5 -10
|
@@ -11,8 +11,9 @@ module Punchblock
|
|
|
11
11
|
include HasMockCallbackConnection
|
|
12
12
|
|
|
13
13
|
let(:channel) { 'SIP/foo' }
|
|
14
|
-
let(:
|
|
15
|
-
let(:
|
|
14
|
+
let(:ami_client) { stub('AMI Client').as_null_object }
|
|
15
|
+
let(:translator) { Punchblock::Translator::Asterisk.new ami_client, connection }
|
|
16
|
+
let(:mock_call) { Punchblock::Translator::Asterisk::Call.new channel, translator, ami_client, connection }
|
|
16
17
|
let(:component_id) { Punchblock.new_uuid }
|
|
17
18
|
|
|
18
19
|
before { stub_uuids component_id }
|
|
@@ -23,31 +24,27 @@ module Punchblock
|
|
|
23
24
|
|
|
24
25
|
subject { AGICommand.new original_command, mock_call }
|
|
25
26
|
|
|
26
|
-
let :
|
|
27
|
-
RubyAMI::
|
|
27
|
+
let :response do
|
|
28
|
+
RubyAMI::Response.new
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
context 'initial execution' do
|
|
31
32
|
before { original_command.request! }
|
|
32
33
|
|
|
33
|
-
it 'should send the appropriate
|
|
34
|
-
|
|
34
|
+
it 'should send the appropriate action' do
|
|
35
|
+
ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'EXEC ANSWER', 'CommandID' => component_id).and_return(response)
|
|
35
36
|
subject.execute
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
context 'with some parameters' do
|
|
39
40
|
let(:params) { [1000, 'foo'] }
|
|
40
41
|
|
|
41
|
-
let :expected_action do
|
|
42
|
-
RubyAMI::Action.new 'AGI', 'Channel' => channel, 'Command' => 'WAIT FOR DIGIT "1000" "foo"', 'CommandID' => component_id
|
|
43
|
-
end
|
|
44
|
-
|
|
45
42
|
let :original_command do
|
|
46
43
|
Punchblock::Component::Asterisk::AGI::Command.new :name => 'WAIT FOR DIGIT', :params => params
|
|
47
44
|
end
|
|
48
45
|
|
|
49
|
-
it 'should send the appropriate
|
|
50
|
-
|
|
46
|
+
it 'should send the appropriate action' do
|
|
47
|
+
ami_client.should_receive(:send_action).once.with('AGI', 'Channel' => channel, 'Command' => 'WAIT FOR DIGIT "1000" "foo"', 'CommandID' => component_id).and_return(response)
|
|
51
48
|
subject.execute
|
|
52
49
|
end
|
|
53
50
|
end
|
|
@@ -56,7 +53,6 @@ module Punchblock
|
|
|
56
53
|
context 'when the AMI action completes' do
|
|
57
54
|
before do
|
|
58
55
|
original_command.request!
|
|
59
|
-
original_command.execute!
|
|
60
56
|
end
|
|
61
57
|
|
|
62
58
|
let :expected_response do
|
|
@@ -64,25 +60,26 @@ module Punchblock
|
|
|
64
60
|
end
|
|
65
61
|
|
|
66
62
|
let :response do
|
|
67
|
-
RubyAMI::Response.new
|
|
68
|
-
|
|
69
|
-
r['Message'] = 'Added AGI original_command to queue'
|
|
70
|
-
end
|
|
63
|
+
RubyAMI::Response.new 'ActionID' => "552a9d9f-46d7-45d8-a257-06fe95f48d99",
|
|
64
|
+
'Message' => 'Added AGI original_command to queue'
|
|
71
65
|
end
|
|
72
66
|
|
|
73
67
|
it 'should send the component node a ref with the action ID' do
|
|
74
|
-
|
|
75
|
-
subject.
|
|
68
|
+
ami_client.should_receive(:send_action).once.and_return response
|
|
69
|
+
subject.execute
|
|
70
|
+
original_command.response(1).should eql(expected_response)
|
|
76
71
|
end
|
|
77
72
|
|
|
78
73
|
context 'with an error' do
|
|
79
|
-
let :
|
|
74
|
+
let :response do
|
|
80
75
|
RubyAMI::Error.new.tap { |e| e.message = 'Action failed' }
|
|
81
76
|
end
|
|
82
77
|
|
|
83
78
|
it 'should send the component node false' do
|
|
84
|
-
|
|
85
|
-
subject.
|
|
79
|
+
ami_client.should_receive(:send_action).once.and_raise response
|
|
80
|
+
subject.execute
|
|
81
|
+
original_command.response(1).should be_false
|
|
82
|
+
subject.should_not be_alive
|
|
86
83
|
end
|
|
87
84
|
end
|
|
88
85
|
end
|
|
@@ -97,13 +94,12 @@ module Punchblock
|
|
|
97
94
|
|
|
98
95
|
context 'of type Exec' do
|
|
99
96
|
let(:ami_event) do
|
|
100
|
-
RubyAMI::Event.new
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
97
|
+
RubyAMI::Event.new 'AsyncAGI',
|
|
98
|
+
"SubEvent" => "Exec",
|
|
99
|
+
"Channel" => channel,
|
|
100
|
+
"CommandId" => component_id,
|
|
101
|
+
"Command" => "EXEC ANSWER",
|
|
102
|
+
"Result" => "200%20result=123%20(timeout)%0A"
|
|
107
103
|
end
|
|
108
104
|
|
|
109
105
|
let :expected_complete_reason do
|
|
@@ -122,6 +118,38 @@ module Punchblock
|
|
|
122
118
|
complete_event.component_id.should be == component_id.to_s
|
|
123
119
|
complete_event.reason.should be == expected_complete_reason
|
|
124
120
|
end
|
|
121
|
+
|
|
122
|
+
context "when the command was ASYNCAGI BREAK" do
|
|
123
|
+
let :original_command do
|
|
124
|
+
Punchblock::Component::Asterisk::AGI::Command.new :name => 'ASYNCAGI BREAK'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
let(:chan_var) { nil }
|
|
128
|
+
|
|
129
|
+
before do
|
|
130
|
+
response = RubyAMI::Response.new 'Value' => chan_var
|
|
131
|
+
ami_client.should_receive(:send_action).once.with('GetVar', 'Channel' => channel, 'Variable' => 'PUNCHBLOCK_END_ON_ASYNCAGI_BREAK').and_return response
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'should not send an end (hangup) event to the translator' do
|
|
135
|
+
translator.should_receive(:handle_pb_event).once.with kind_of(Punchblock::Event::Complete)
|
|
136
|
+
translator.should_receive(:handle_pb_event).never.with kind_of(Punchblock::Event::End)
|
|
137
|
+
subject.handle_ami_event ami_event
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context "when the PUNCHBLOCK_END_ON_ASYNCAGI_BREAK channel var is set" do
|
|
141
|
+
let(:chan_var) { 'true' }
|
|
142
|
+
|
|
143
|
+
it 'should send an end (hangup) event to the translator' do
|
|
144
|
+
expected_end_event = Punchblock::Event::End.new reason: :hangup,
|
|
145
|
+
target_call_id: mock_call.id
|
|
146
|
+
|
|
147
|
+
translator.should_receive(:handle_pb_event).once.with kind_of(Punchblock::Event::Complete)
|
|
148
|
+
translator.should_receive(:handle_pb_event).once.with expected_end_event
|
|
149
|
+
subject.handle_ami_event ami_event
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
125
153
|
end
|
|
126
154
|
end
|
|
127
155
|
end
|
|
@@ -10,18 +10,19 @@ module Punchblock
|
|
|
10
10
|
describe AMIAction do
|
|
11
11
|
include HasMockCallbackConnection
|
|
12
12
|
|
|
13
|
-
let(:
|
|
13
|
+
let(:ami_client) { stub('AMI Client').as_null_object }
|
|
14
|
+
let(:mock_translator) { Punchblock::Translator::Asterisk.new ami_client, connection }
|
|
14
15
|
|
|
15
16
|
let :original_command do
|
|
16
17
|
Punchblock::Component::Asterisk::AMI::Action.new :name => 'ExtensionStatus', :params => { :context => 'default', :exten => 'idonno' }
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let :expected_action do
|
|
22
|
-
RubyAMI::Action.new 'ExtensionStatus', 'Context' => 'default', 'Exten' => 'idonno'
|
|
20
|
+
before do
|
|
21
|
+
original_command.request!
|
|
23
22
|
end
|
|
24
23
|
|
|
24
|
+
subject { AMIAction.new original_command, mock_translator, ami_client }
|
|
25
|
+
|
|
25
26
|
context 'initial execution' do
|
|
26
27
|
let(:component_id) { Punchblock.new_uuid }
|
|
27
28
|
|
|
@@ -31,28 +32,21 @@ module Punchblock
|
|
|
31
32
|
|
|
32
33
|
before { stub_uuids component_id }
|
|
33
34
|
|
|
34
|
-
it 'should send the appropriate RubyAMI::Action and send the component node a ref
|
|
35
|
-
|
|
36
|
-
original_command.should_receive(:response=).once.with(expected_response)
|
|
35
|
+
it 'should send the appropriate RubyAMI::Action and send the component node a ref' do
|
|
36
|
+
ami_client.should_receive(:send_action).once.with('ExtensionStatus', 'Context' => 'default', 'Exten' => 'idonno').and_return(RubyAMI::Response.new)
|
|
37
37
|
subject.execute
|
|
38
|
+
original_command.response(1).should == expected_response
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
context 'when the AMI action completes' do
|
|
42
|
-
before do
|
|
43
|
-
original_command.request!
|
|
44
|
-
original_command.execute!
|
|
45
|
-
end
|
|
46
|
-
|
|
47
43
|
let :response do
|
|
48
|
-
RubyAMI::Response.new
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
r["Status"] = "-1"
|
|
55
|
-
end
|
|
44
|
+
RubyAMI::Response.new 'ActionID' => '552a9d9f-46d7-45d8-a257-06fe95f48d99',
|
|
45
|
+
'Message' => 'Channel status will follow',
|
|
46
|
+
'Exten' => 'idonno',
|
|
47
|
+
'Context' => 'default',
|
|
48
|
+
'Hint' => '',
|
|
49
|
+
'Status' => '-1'
|
|
56
50
|
end
|
|
57
51
|
|
|
58
52
|
let :expected_complete_reason do
|
|
@@ -61,8 +55,9 @@ module Punchblock
|
|
|
61
55
|
|
|
62
56
|
context 'for a non-causal action' do
|
|
63
57
|
it 'should send a complete event to the component node' do
|
|
58
|
+
ami_client.should_receive(:send_action).once.and_return response
|
|
64
59
|
subject.wrapped_object.should_receive(:send_complete_event).once.with expected_complete_reason
|
|
65
|
-
subject.
|
|
60
|
+
subject.execute
|
|
66
61
|
end
|
|
67
62
|
end
|
|
68
63
|
|
|
@@ -76,24 +71,20 @@ module Punchblock
|
|
|
76
71
|
end
|
|
77
72
|
|
|
78
73
|
let :event do
|
|
79
|
-
RubyAMI::Event.new
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
e['ChannelStateDesc'] = 'Up'
|
|
88
|
-
end
|
|
74
|
+
RubyAMI::Event.new 'CoreShowChannel', 'ActionID' => "552a9d9f-46d7-45d8-a257-06fe95f48d99",
|
|
75
|
+
'Channel' => 'SIP/127.0.0.1-00000013',
|
|
76
|
+
'UniqueID' => '1287686437.19',
|
|
77
|
+
'Context' => 'adhearsion',
|
|
78
|
+
'Extension' => '23432',
|
|
79
|
+
'Priority' => '2',
|
|
80
|
+
'ChannelState' => '6',
|
|
81
|
+
'ChannelStateDesc' => 'Up'
|
|
89
82
|
end
|
|
90
83
|
|
|
91
84
|
let :terminating_event do
|
|
92
|
-
RubyAMI::Event.new
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
e['ActionID'] = 'umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN'
|
|
96
|
-
end
|
|
85
|
+
RubyAMI::Event.new 'CoreShowChannelsComplete', 'EventList' => 'Complete',
|
|
86
|
+
'ListItems' => '3',
|
|
87
|
+
'ActionID' => 'umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN'
|
|
97
88
|
end
|
|
98
89
|
|
|
99
90
|
let :event_node do
|
|
@@ -112,21 +103,22 @@ module Punchblock
|
|
|
112
103
|
Punchblock::Component::Asterisk::AMI::Action::Complete::Success.new :message => 'Channel status will follow', :attributes => {:exten => "idonno", :context => "default", :hint => "", :status => "-1", :eventlist => 'Complete', :listitems => '3'}
|
|
113
104
|
end
|
|
114
105
|
|
|
106
|
+
before { ami_client.should_receive(:send_action).once.and_return response }
|
|
107
|
+
|
|
115
108
|
it 'should send events to the component node' do
|
|
116
109
|
event_node
|
|
117
110
|
original_command.register_handler :internal, Punchblock::Event::Asterisk::AMI::Event do |event|
|
|
118
111
|
@event = event
|
|
119
112
|
end
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
subject.handle_response response
|
|
123
|
-
action << terminating_event
|
|
113
|
+
response.events << event << terminating_event
|
|
114
|
+
subject.execute
|
|
124
115
|
@event.should be == event_node
|
|
125
116
|
end
|
|
126
117
|
|
|
127
118
|
it 'should send a complete event to the component node' do
|
|
128
|
-
|
|
129
|
-
|
|
119
|
+
response.events << event << terminating_event
|
|
120
|
+
|
|
121
|
+
subject.execute
|
|
130
122
|
|
|
131
123
|
original_command.complete_event(0.5).reason.should be == expected_complete_reason
|
|
132
124
|
end
|
|
@@ -138,12 +130,14 @@ module Punchblock
|
|
|
138
130
|
end
|
|
139
131
|
|
|
140
132
|
let :expected_complete_reason do
|
|
141
|
-
Punchblock::Event::Complete::Error.new :details => 'Action failed'
|
|
133
|
+
Punchblock::Event::Complete::Error.new :details => 'Action failed', component_id: subject.id
|
|
142
134
|
end
|
|
143
135
|
|
|
144
136
|
it 'should send a complete event to the component node' do
|
|
145
|
-
|
|
146
|
-
|
|
137
|
+
ami_client.should_receive(:send_action).once.and_raise error
|
|
138
|
+
expected_complete_reason
|
|
139
|
+
subject.execute
|
|
140
|
+
original_command.complete_event(0.5).reason.should be == expected_complete_reason
|
|
147
141
|
end
|
|
148
142
|
end
|
|
149
143
|
end
|
|
@@ -10,8 +10,9 @@ module Punchblock
|
|
|
10
10
|
include HasMockCallbackConnection
|
|
11
11
|
|
|
12
12
|
let(:media_engine) { nil }
|
|
13
|
-
let(:
|
|
14
|
-
let(:
|
|
13
|
+
let(:ami_client) { mock('AMI') }
|
|
14
|
+
let(:translator) { Punchblock::Translator::Asterisk.new ami_client, connection, media_engine }
|
|
15
|
+
let(:call) { Punchblock::Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
|
|
15
16
|
let(:original_command_options) { {} }
|
|
16
17
|
|
|
17
18
|
let :original_command do
|
|
@@ -53,11 +54,10 @@ module Punchblock
|
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def ami_event_for_dtmf(digit, position)
|
|
56
|
-
RubyAMI::Event.new
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
57
|
+
RubyAMI::Event.new 'DTMF',
|
|
58
|
+
'Digit' => digit.to_s,
|
|
59
|
+
'Start' => position == :start ? 'Yes' : 'No',
|
|
60
|
+
'End' => position == :end ? 'Yes' : 'No'
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def send_ami_events_for_dtmf(digit)
|
|
@@ -10,8 +10,9 @@ module Punchblock
|
|
|
10
10
|
include HasMockCallbackConnection
|
|
11
11
|
|
|
12
12
|
let(:media_engine) { nil }
|
|
13
|
-
let(:
|
|
14
|
-
let(:
|
|
13
|
+
let(:ami_client) { mock('AMI') }
|
|
14
|
+
let(:translator) { Punchblock::Translator::Asterisk.new ami_client, connection, media_engine }
|
|
15
|
+
let(:mock_call) { Punchblock::Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
|
|
15
16
|
|
|
16
17
|
let :original_command do
|
|
17
18
|
Punchblock::Component::Output.new command_options
|
|
@@ -70,24 +71,32 @@ module Punchblock
|
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
it "should execute Swift" do
|
|
73
|
-
mock_call.
|
|
74
|
+
mock_call.should_receive(:execute_agi_command).once.with 'EXEC Swift', ssml_with_options
|
|
74
75
|
subject.execute
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
it 'should send a complete event when Swift completes' do
|
|
78
|
-
|
|
79
|
-
def async_proxy.send_agi_action(*args, &block)
|
|
80
|
-
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
|
81
|
-
end
|
|
79
|
+
mock_call.should_receive(:execute_agi_command).and_return code: 200, result: 1
|
|
82
80
|
subject.execute
|
|
83
81
|
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
84
82
|
end
|
|
85
83
|
|
|
84
|
+
context "when we get a RubyAMI Error" do
|
|
85
|
+
it "should send an error complete event" do
|
|
86
|
+
error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
|
|
87
|
+
mock_call.should_receive(:execute_agi_command).and_raise error
|
|
88
|
+
subject.execute
|
|
89
|
+
complete_reason = original_command.complete_event(0.1).reason
|
|
90
|
+
complete_reason.should be_a Punchblock::Event::Complete::Error
|
|
91
|
+
complete_reason.details.should == "Terminated due to AMI error 'FooBar'"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
86
95
|
describe 'interrupt_on' do
|
|
87
96
|
context "set to nil" do
|
|
88
97
|
let(:command_opts) { { :interrupt_on => nil } }
|
|
89
98
|
it "should not add interrupt arguments" do
|
|
90
|
-
mock_call.
|
|
99
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Swift', ssml_with_options).and_return code: 200, result: 1
|
|
91
100
|
subject.execute
|
|
92
101
|
end
|
|
93
102
|
end
|
|
@@ -96,7 +105,7 @@ module Punchblock
|
|
|
96
105
|
let(:command_opts) { { :interrupt_on => :any } }
|
|
97
106
|
it "should add the interrupt options to the argument" do
|
|
98
107
|
expect_answered
|
|
99
|
-
mock_call.
|
|
108
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Swift', ssml_with_options('', '|1|1')).and_return code: 200, result: 1
|
|
100
109
|
subject.execute
|
|
101
110
|
end
|
|
102
111
|
end
|
|
@@ -105,7 +114,7 @@ module Punchblock
|
|
|
105
114
|
let(:command_opts) { { :interrupt_on => :dtmf } }
|
|
106
115
|
it "should add the interrupt options to the argument" do
|
|
107
116
|
expect_answered
|
|
108
|
-
mock_call.
|
|
117
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Swift', ssml_with_options('', '|1|1')).and_return code: 200, result: 1
|
|
109
118
|
subject.execute
|
|
110
119
|
end
|
|
111
120
|
end
|
|
@@ -124,7 +133,7 @@ module Punchblock
|
|
|
124
133
|
context "set to nil" do
|
|
125
134
|
let(:command_opts) { { :voice => nil } }
|
|
126
135
|
it "should not add a voice at the beginning of the argument" do
|
|
127
|
-
mock_call.
|
|
136
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Swift', ssml_with_options).and_return code: 200, result: 1
|
|
128
137
|
subject.execute
|
|
129
138
|
end
|
|
130
139
|
end
|
|
@@ -132,7 +141,7 @@ module Punchblock
|
|
|
132
141
|
context "set to Leonard" do
|
|
133
142
|
let(:command_opts) { { :voice => "Leonard" } }
|
|
134
143
|
it "should add a voice at the beginning of the argument" do
|
|
135
|
-
mock_call.
|
|
144
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Swift', ssml_with_options('Leonard^', '')).and_return code: 200, result: 1
|
|
136
145
|
subject.execute
|
|
137
146
|
end
|
|
138
147
|
end
|
|
@@ -159,14 +168,14 @@ module Punchblock
|
|
|
159
168
|
end
|
|
160
169
|
|
|
161
170
|
def expect_mrcpsynth_with_options(options)
|
|
162
|
-
mock_call.
|
|
171
|
+
mock_call.should_receive(:execute_agi_command).once.with do |*args|
|
|
163
172
|
args[0].should be == 'EXEC MRCPSynth'
|
|
164
173
|
args[2].should match options
|
|
165
|
-
end
|
|
174
|
+
end.and_return code: 200, result: 1
|
|
166
175
|
end
|
|
167
176
|
|
|
168
177
|
it "should execute MRCPSynth" do
|
|
169
|
-
mock_call.
|
|
178
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC MRCPSynth', ssml_doc.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }, '').and_return code: 200, result: 1
|
|
170
179
|
subject.execute
|
|
171
180
|
end
|
|
172
181
|
|
|
@@ -178,23 +187,31 @@ module Punchblock
|
|
|
178
187
|
end
|
|
179
188
|
|
|
180
189
|
it 'should escape TTS strings containing a comma' do
|
|
181
|
-
mock_call.
|
|
190
|
+
mock_call.should_receive(:execute_agi_command).once.with do |*args|
|
|
182
191
|
args[0].should be == 'EXEC MRCPSynth'
|
|
183
192
|
args[1].should match(/this\\, here\\, is a test/)
|
|
184
|
-
end
|
|
193
|
+
end.and_return code: 200, result: 1
|
|
185
194
|
subject.execute
|
|
186
195
|
end
|
|
187
196
|
end
|
|
188
197
|
|
|
189
198
|
it 'should send a complete event when MRCPSynth completes' do
|
|
190
|
-
|
|
191
|
-
def async_proxy.send_agi_action(*args, &block)
|
|
192
|
-
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
|
193
|
-
end
|
|
199
|
+
mock_call.should_receive(:execute_agi_command).and_return code: 200, result: 1
|
|
194
200
|
subject.execute
|
|
195
201
|
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
196
202
|
end
|
|
197
203
|
|
|
204
|
+
context "when we get a RubyAMI Error" do
|
|
205
|
+
it "should send an error complete event" do
|
|
206
|
+
error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
|
|
207
|
+
mock_call.should_receive(:execute_agi_command).and_raise error
|
|
208
|
+
subject.execute
|
|
209
|
+
complete_reason = original_command.complete_event(0.1).reason
|
|
210
|
+
complete_reason.should be_a Punchblock::Event::Complete::Error
|
|
211
|
+
complete_reason.details.should == "Terminated due to AMI error 'FooBar'"
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
198
215
|
describe 'ssml' do
|
|
199
216
|
context 'unset' do
|
|
200
217
|
let(:command_opts) { { :ssml => nil } }
|
|
@@ -360,11 +377,11 @@ module Punchblock
|
|
|
360
377
|
[:asterisk, nil].each do |media_engine|
|
|
361
378
|
context "with a media engine of #{media_engine.inspect}" do
|
|
362
379
|
def expect_playback(filename = audio_filename)
|
|
363
|
-
mock_call.
|
|
380
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Playback', filename).and_return code: 200
|
|
364
381
|
end
|
|
365
382
|
|
|
366
383
|
def expect_playback_noanswer
|
|
367
|
-
mock_call.
|
|
384
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Playback', audio_filename + ',noanswer').and_return code: 200
|
|
368
385
|
end
|
|
369
386
|
|
|
370
387
|
let(:audio_filename) { 'http://foo.com/bar.mp3' }
|
|
@@ -413,13 +430,22 @@ module Punchblock
|
|
|
413
430
|
def mock_call.answered?
|
|
414
431
|
true
|
|
415
432
|
end
|
|
416
|
-
|
|
417
|
-
def async_proxy.send_agi_action(*args, &block)
|
|
418
|
-
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
|
419
|
-
end
|
|
433
|
+
expect_playback
|
|
420
434
|
subject.execute
|
|
421
435
|
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
422
436
|
end
|
|
437
|
+
|
|
438
|
+
context "when we get a RubyAMI Error" do
|
|
439
|
+
it "should send an error complete event" do
|
|
440
|
+
expect_answered
|
|
441
|
+
error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
|
|
442
|
+
mock_call.should_receive(:execute_agi_command).and_raise error
|
|
443
|
+
subject.execute
|
|
444
|
+
complete_reason = original_command.complete_event(0.1).reason
|
|
445
|
+
complete_reason.should be_a Punchblock::Event::Complete::Error
|
|
446
|
+
complete_reason.details.should == "Terminated due to AMI error 'FooBar'"
|
|
447
|
+
end
|
|
448
|
+
end
|
|
423
449
|
end
|
|
424
450
|
|
|
425
451
|
context 'with a single text node without spaces' do
|
|
@@ -438,14 +464,23 @@ module Punchblock
|
|
|
438
464
|
|
|
439
465
|
it 'should send a complete event when the file finishes playback' do
|
|
440
466
|
expect_answered
|
|
441
|
-
|
|
442
|
-
def async_proxy.send_agi_action(*args, &block)
|
|
443
|
-
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
|
444
|
-
end
|
|
467
|
+
expect_playback
|
|
445
468
|
subject.execute
|
|
446
469
|
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
447
470
|
end
|
|
448
471
|
|
|
472
|
+
context "when we get a RubyAMI Error" do
|
|
473
|
+
it "should send an error complete event" do
|
|
474
|
+
expect_answered
|
|
475
|
+
error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
|
|
476
|
+
mock_call.should_receive(:execute_agi_command).and_raise error
|
|
477
|
+
subject.execute
|
|
478
|
+
complete_reason = original_command.complete_event(0.1).reason
|
|
479
|
+
complete_reason.should be_a Punchblock::Event::Complete::Error
|
|
480
|
+
complete_reason.details.should == "Terminated due to AMI error 'FooBar'"
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
449
484
|
context "with early media playback" do
|
|
450
485
|
it "should play the file with Playback" do
|
|
451
486
|
expect_answered false
|
|
@@ -507,10 +542,7 @@ module Punchblock
|
|
|
507
542
|
|
|
508
543
|
it 'should send a complete event after the final file has finished playback' do
|
|
509
544
|
expect_answered
|
|
510
|
-
|
|
511
|
-
def async_proxy.send_agi_action(*args, &block)
|
|
512
|
-
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
|
513
|
-
end
|
|
545
|
+
expect_playback [audio_filename1, audio_filename2].join('&')
|
|
514
546
|
latch = CountDownLatch.new 1
|
|
515
547
|
original_command.should_receive(:add_event).once.with do |e|
|
|
516
548
|
e.reason.should be_a Punchblock::Component::Output::Complete::Success
|
|
@@ -660,11 +692,10 @@ module Punchblock
|
|
|
660
692
|
|
|
661
693
|
describe 'interrupt_on' do
|
|
662
694
|
def ami_event_for_dtmf(digit, position)
|
|
663
|
-
RubyAMI::Event.new
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
end
|
|
695
|
+
RubyAMI::Event.new 'DTMF',
|
|
696
|
+
'Digit' => digit.to_s,
|
|
697
|
+
'Start' => position == :start ? 'Yes' : 'No',
|
|
698
|
+
'End' => position == :end ? 'Yes' : 'No'
|
|
668
699
|
end
|
|
669
700
|
|
|
670
701
|
def send_ami_events_for_dtmf(digit)
|
|
@@ -675,11 +706,10 @@ module Punchblock
|
|
|
675
706
|
let(:reason) { original_command.complete_event(5).reason }
|
|
676
707
|
let(:channel) { "SIP/1234-00000000" }
|
|
677
708
|
let :ami_event do
|
|
678
|
-
RubyAMI::Event.new
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
end
|
|
709
|
+
RubyAMI::Event.new 'AsyncAGI',
|
|
710
|
+
'SubEvent' => "Start",
|
|
711
|
+
'Channel' => channel,
|
|
712
|
+
'Env' => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
|
|
683
713
|
end
|
|
684
714
|
|
|
685
715
|
context "set to nil" do
|
|
@@ -699,7 +729,8 @@ module Punchblock
|
|
|
699
729
|
|
|
700
730
|
before do
|
|
701
731
|
expect_answered
|
|
702
|
-
|
|
732
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Playback', audio_filename)
|
|
733
|
+
subject.wrapped_object.should_receive(:send_success).and_return nil
|
|
703
734
|
end
|
|
704
735
|
|
|
705
736
|
context "when a DTMF digit is received" do
|
|
@@ -729,7 +760,8 @@ module Punchblock
|
|
|
729
760
|
|
|
730
761
|
before do
|
|
731
762
|
expect_answered
|
|
732
|
-
|
|
763
|
+
mock_call.should_receive(:execute_agi_command).once.with('EXEC Playback', audio_filename)
|
|
764
|
+
subject.wrapped_object.should_receive(:send_success).and_return nil
|
|
733
765
|
end
|
|
734
766
|
|
|
735
767
|
context "when a DTMF digit is received" do
|
|
@@ -787,7 +819,7 @@ module Punchblock
|
|
|
787
819
|
|
|
788
820
|
it "should use the media renderer set and not the platform default" do
|
|
789
821
|
expect_answered
|
|
790
|
-
mock_call.
|
|
822
|
+
mock_call.should_receive(:execute_agi_command).once.with 'EXEC Playback', audio_filename
|
|
791
823
|
subject.execute
|
|
792
824
|
end
|
|
793
825
|
end
|
|
@@ -809,11 +841,10 @@ module Punchblock
|
|
|
809
841
|
let(:reason) { original_command.complete_event(5).reason }
|
|
810
842
|
let(:channel) { "SIP/1234-00000000" }
|
|
811
843
|
let :ami_event do
|
|
812
|
-
RubyAMI::Event.new
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
end
|
|
844
|
+
RubyAMI::Event.new 'AsyncAGI',
|
|
845
|
+
'SubEvent' => "Start",
|
|
846
|
+
'Channel' => channel,
|
|
847
|
+
'Env' => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
|
|
817
848
|
end
|
|
818
849
|
|
|
819
850
|
before do
|