punchblock 1.9.4 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +17 -0
  5. data/Gemfile +1 -0
  6. data/Guardfile +4 -0
  7. data/README.markdown +6 -0
  8. data/Rakefile +16 -0
  9. data/benchmarks/ami_event_name_comparison.rb +14 -0
  10. data/benchmarks/channel.rb +27 -0
  11. data/lib/punchblock/client.rb +2 -6
  12. data/lib/punchblock/command/accept.rb +3 -24
  13. data/lib/punchblock/command/answer.rb +3 -24
  14. data/lib/punchblock/command/dial.rb +24 -76
  15. data/lib/punchblock/command/hangup.rb +3 -19
  16. data/lib/punchblock/command/join.rb +21 -70
  17. data/lib/punchblock/command/mute.rb +3 -3
  18. data/lib/punchblock/command/redirect.rb +6 -39
  19. data/lib/punchblock/command/reject.rb +14 -54
  20. data/lib/punchblock/command/unjoin.rb +8 -40
  21. data/lib/punchblock/command/unmute.rb +3 -3
  22. data/lib/punchblock/command_node.rb +0 -17
  23. data/lib/punchblock/component/asterisk/agi/command.rb +20 -127
  24. data/lib/punchblock/component/asterisk/ami/action.rb +30 -117
  25. data/lib/punchblock/component/component_node.rb +1 -1
  26. data/lib/punchblock/component/input.rb +89 -268
  27. data/lib/punchblock/component/output.rb +106 -154
  28. data/lib/punchblock/component/prompt.rb +51 -0
  29. data/lib/punchblock/component/record.rb +41 -130
  30. data/lib/punchblock/component.rb +1 -0
  31. data/lib/punchblock/connection/asterisk.rb +31 -4
  32. data/lib/punchblock/connection/xmpp.rb +6 -14
  33. data/lib/punchblock/core_ext/blather/stanza.rb +1 -1
  34. data/lib/punchblock/event/active_speaker.rb +2 -10
  35. data/lib/punchblock/event/answered.rb +3 -3
  36. data/lib/punchblock/event/asterisk/ami/event.rb +15 -47
  37. data/lib/punchblock/event/complete.rb +26 -48
  38. data/lib/punchblock/event/dtmf.rb +3 -13
  39. data/lib/punchblock/event/end.rb +10 -11
  40. data/lib/punchblock/event/joined.rb +5 -25
  41. data/lib/punchblock/event/offer.rb +4 -25
  42. data/lib/punchblock/event/ringing.rb +3 -3
  43. data/lib/punchblock/event/unjoined.rb +5 -25
  44. data/lib/punchblock/event.rb +0 -10
  45. data/lib/punchblock/has_headers.rb +20 -26
  46. data/lib/punchblock/rayo_node.rb +46 -23
  47. data/lib/punchblock/ref.rb +39 -18
  48. data/lib/punchblock/translator/asterisk/agi_app.rb +15 -0
  49. data/lib/punchblock/translator/asterisk/agi_command.rb +3 -1
  50. data/lib/punchblock/translator/asterisk/ami_error_converter.rb +20 -0
  51. data/lib/punchblock/translator/asterisk/call.rb +60 -39
  52. data/lib/punchblock/translator/asterisk/channel.rb +41 -0
  53. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +4 -1
  54. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +4 -4
  55. data/lib/punchblock/translator/asterisk/component/composed_prompt.rb +62 -0
  56. data/lib/punchblock/translator/asterisk/component/input.rb +1 -0
  57. data/lib/punchblock/translator/asterisk/component/mrcp_native_prompt.rb +56 -0
  58. data/lib/punchblock/translator/asterisk/component/mrcp_prompt.rb +53 -0
  59. data/lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb +99 -0
  60. data/lib/punchblock/translator/asterisk/component/output.rb +30 -22
  61. data/lib/punchblock/translator/asterisk/component/record.rb +8 -6
  62. data/lib/punchblock/translator/asterisk/component.rb +6 -5
  63. data/lib/punchblock/translator/asterisk/unimrcp_app.rb +26 -0
  64. data/lib/punchblock/translator/asterisk.rb +24 -28
  65. data/lib/punchblock/translator/dtmf_recognizer.rb +39 -20
  66. data/lib/punchblock/translator/freeswitch/call.rb +15 -14
  67. data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +5 -4
  68. data/lib/punchblock/translator/freeswitch/component/flite_output.rb +1 -1
  69. data/lib/punchblock/translator/freeswitch/component/input.rb +5 -0
  70. data/lib/punchblock/translator/freeswitch/component/output.rb +2 -2
  71. data/lib/punchblock/translator/freeswitch/component/record.rb +19 -13
  72. data/lib/punchblock/translator/freeswitch/component/tts_output.rb +2 -2
  73. data/lib/punchblock/translator/freeswitch/component.rb +2 -5
  74. data/lib/punchblock/translator/freeswitch.rb +2 -2
  75. data/lib/punchblock/translator/input_component.rb +33 -13
  76. data/lib/punchblock/uri_list.rb +21 -0
  77. data/lib/punchblock/version.rb +1 -1
  78. data/lib/punchblock.rb +4 -3
  79. data/punchblock.gemspec +7 -3
  80. data/spec/punchblock/client/component_registry_spec.rb +1 -1
  81. data/spec/punchblock/client_spec.rb +10 -26
  82. data/spec/punchblock/command/accept_spec.rb +41 -7
  83. data/spec/punchblock/command/answer_spec.rb +51 -7
  84. data/spec/punchblock/command/dial_spec.rb +56 -14
  85. data/spec/punchblock/command/hangup_spec.rb +41 -7
  86. data/spec/punchblock/command/join_spec.rb +53 -11
  87. data/spec/punchblock/command/mute_spec.rb +19 -4
  88. data/spec/punchblock/command/redirect_spec.rb +40 -10
  89. data/spec/punchblock/command/reject_spec.rb +43 -11
  90. data/spec/punchblock/command/unjoin_spec.rb +40 -9
  91. data/spec/punchblock/command/unmute_spec.rb +19 -4
  92. data/spec/punchblock/command_node_spec.rb +0 -4
  93. data/spec/punchblock/component/asterisk/agi/command_spec.rb +16 -39
  94. data/spec/punchblock/component/asterisk/ami/action_spec.rb +50 -53
  95. data/spec/punchblock/component/component_node_spec.rb +3 -5
  96. data/spec/punchblock/component/input_spec.rb +194 -61
  97. data/spec/punchblock/component/output_spec.rb +194 -62
  98. data/spec/punchblock/component/prompt_spec.rb +132 -0
  99. data/spec/punchblock/component/record_spec.rb +70 -32
  100. data/spec/punchblock/connection/asterisk_spec.rb +17 -3
  101. data/spec/punchblock/connection/freeswitch_spec.rb +4 -4
  102. data/spec/punchblock/connection/xmpp_spec.rb +20 -38
  103. data/spec/punchblock/event/answered_spec.rb +12 -10
  104. data/spec/punchblock/event/asterisk/ami/event_spec.rb +27 -22
  105. data/spec/punchblock/event/complete_spec.rb +15 -19
  106. data/spec/punchblock/event/dtmf_spec.rb +5 -6
  107. data/spec/punchblock/event/end_spec.rb +20 -10
  108. data/spec/punchblock/event/joined_spec.rb +8 -7
  109. data/spec/punchblock/event/offer_spec.rb +41 -12
  110. data/spec/punchblock/event/ringing_spec.rb +12 -10
  111. data/spec/punchblock/event/started_speaking_spec.rb +5 -6
  112. data/spec/punchblock/event/stopped_speaking_spec.rb +5 -6
  113. data/spec/punchblock/event/unjoined_spec.rb +7 -7
  114. data/spec/punchblock/ref_spec.rb +86 -9
  115. data/spec/punchblock/translator/asterisk/call_spec.rb +317 -154
  116. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +28 -5
  117. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +15 -13
  118. data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +237 -0
  119. data/spec/punchblock/translator/asterisk/component/input_spec.rb +171 -14
  120. data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +652 -0
  121. data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +646 -0
  122. data/spec/punchblock/translator/asterisk/component/output_spec.rb +127 -77
  123. data/spec/punchblock/translator/asterisk/component/record_spec.rb +17 -8
  124. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +2 -2
  125. data/spec/punchblock/translator/asterisk/component_spec.rb +3 -7
  126. data/spec/punchblock/translator/asterisk_spec.rb +20 -24
  127. data/spec/punchblock/translator/freeswitch/call_spec.rb +103 -99
  128. data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +17 -8
  129. data/spec/punchblock/translator/freeswitch/component/input_spec.rb +26 -14
  130. data/spec/punchblock/translator/freeswitch/component/output_spec.rb +30 -52
  131. data/spec/punchblock/translator/freeswitch/component/record_spec.rb +23 -19
  132. data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +18 -8
  133. data/spec/punchblock/translator/freeswitch/component_spec.rb +4 -8
  134. data/spec/punchblock/translator/freeswitch_spec.rb +11 -14
  135. data/spec/punchblock/uri_list_spec.rb +49 -0
  136. data/spec/punchblock_spec.rb +11 -1
  137. data/spec/spec_helper.rb +7 -11
  138. data/spec/support/mock_connection_with_event_handler.rb +1 -1
  139. metadata +104 -24
  140. data/lib/punchblock/header.rb +0 -9
  141. data/lib/punchblock/key_value_pair_node.rb +0 -51
  142. data/spec/punchblock/header_spec.rb +0 -11
@@ -7,10 +7,10 @@ module Punchblock
7
7
  class Freeswitch
8
8
  describe Call do
9
9
  let(:id) { Punchblock.new_uuid }
10
- let(:stream) { stub('RubyFS::Stream').as_null_object }
10
+ let(:stream) { double('RubyFS::Stream').as_null_object }
11
11
  let(:media_engine) { 'freeswitch' }
12
12
  let(:default_voice) { :hal }
13
- let(:translator) { Freeswitch.new stub('Connection::Freeswitch').as_null_object }
13
+ let(:translator) { Freeswitch.new double('Connection::Freeswitch').as_null_object }
14
14
  let(:es_env) do
15
15
  {
16
16
  :variable_direction => "inbound",
@@ -90,78 +90,78 @@ module Punchblock
90
90
 
91
91
  let :headers do
92
92
  {
93
- :x_variable_direction => "inbound",
94
- :x_variable_uuid => "3f0e1e18-c056-11e1-b099-fffeda3ce54f",
95
- :x_variable_session_id => "1",
96
- :x_variable_sip_local_network_addr => "109.148.160.137",
97
- :x_variable_sip_network_ip => "192.168.1.74",
98
- :x_variable_sip_network_port => "59253",
99
- :x_variable_sip_received_ip => "192.168.1.74",
100
- :x_variable_sip_received_port => "59253",
101
- :x_variable_sip_via_protocol => "udp",
102
- :x_variable_sip_authorized => "true",
103
- :x_variable_sip_number_alias => "1000",
104
- :x_variable_sip_auth_username => "1000",
105
- :x_variable_sip_auth_realm => "127.0.0.1",
106
- :x_variable_number_alias => "1000",
107
- :x_variable_user_name => "1000",
108
- :x_variable_domain_name => "127.0.0.1",
109
- :x_variable_record_stereo => "true",
110
- :x_variable_default_gateway => "example.com",
111
- :x_variable_default_areacode => "918",
112
- :x_variable_transfer_fallback_extension => "operator",
113
- :x_variable_toll_allow => "domestic,international,local",
114
- :x_variable_accountcode => "1000",
115
- :x_variable_user_context => "default",
116
- :x_variable_effective_caller_id_name => "Extension 1000",
117
- :x_variable_effective_caller_id_number => "1000",
118
- :x_variable_outbound_caller_id_name => "FreeSWITCH",
119
- :x_variable_outbound_caller_id_number => "0000000000",
120
- :x_variable_callgroup => "techsupport",
121
- :x_variable_sip_from_user => "1000",
122
- :x_variable_sip_from_uri => "1000@127.0.0.1",
123
- :x_variable_sip_from_host => "127.0.0.1",
124
- :x_variable_sip_from_user_stripped => "1000",
125
- :x_variable_sip_from_tag => "1248111553",
126
- :x_variable_sofia_profile_name => "internal",
127
- :x_variable_sip_full_via => "SIP/2.0/UDP 192.168.1.74:59253;rport=59253;branch=z9hG4bK2021947958",
128
- :x_variable_sip_full_from => "<sip:1000@127.0.0.1>;tag=1248111553",
129
- :x_variable_sip_full_to => "<sip:10@127.0.0.1>",
130
- :x_variable_sip_req_user => "10",
131
- :x_variable_sip_req_uri => "10@127.0.0.1",
132
- :x_variable_sip_req_host => "127.0.0.1",
133
- :x_variable_sip_to_user => "10",
134
- :x_variable_sip_to_uri => "10@127.0.0.1",
135
- :x_variable_sip_to_host => "127.0.0.1",
136
- :x_variable_sip_contact_user => "1000",
137
- :x_variable_sip_contact_port => "59253",
138
- :x_variable_sip_contact_uri => "1000@192.168.1.74:59253",
139
- :x_variable_sip_contact_host => "192.168.1.74",
140
- :x_variable_channel_name => "sofia/internal/1000@127.0.0.1",
141
- :x_variable_sip_call_id => "1251435211@127.0.0.1",
142
- :x_variable_sip_user_agent => "YATE/4.1.0",
143
- :x_variable_sip_via_host => "192.168.1.74",
144
- :x_variable_sip_via_port => "59253",
145
- :x_variable_sip_via_rport => "59253",
146
- :x_variable_max_forwards => "20",
147
- :x_variable_presence_id => "1000@127.0.0.1",
148
- :x_variable_switch_r_sdp => "v=0\r\no=yate 1340801245 1340801245 IN IP4 172.20.10.3\r\ns=SIP Call\r\nc=IN IP4 172.20.10.3\r\nt=0 0\r\nm=audio 25048 RTP/AVP 0 8 11 98 97 102 103 104 105 106 101\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:11 L16/8000\r\na=rtpmap:98 iLBC/8000\r\na=fmtp:98 mode=20\r\na=rtpmap:97 iLBC/8000\r\na=fmtp:97 mode=30\r\na=rtpmap:102 SPEEX/8000\r\na=rtpmap:103 SPEEX/16000\r\na=rtpmap:104 SPEEX/32000\r\na=rtpmap:105 iSAC/16000\r\na=rtpmap:106 iSAC/32000\r\na=rtpmap:101 telephone-event/8000\r\na=ptime:30\r\n",
149
- :x_variable_remote_media_ip => "172.20.10.3",
150
- :x_variable_remote_media_port => "25048",
151
- :x_variable_sip_audio_recv_pt => "0",
152
- :x_variable_sip_use_codec_name => "PCMU",
153
- :x_variable_sip_use_codec_rate => "8000",
154
- :x_variable_sip_use_codec_ptime => "30",
155
- :x_variable_read_codec => "PCMU",
156
- :x_variable_read_rate => "8000",
157
- :x_variable_write_codec => "PCMU",
158
- :x_variable_write_rate => "8000",
159
- :x_variable_endpoint_disposition => "RECEIVED",
160
- :x_variable_call_uuid => "3f0e1e18-c056-11e1-b099-fffeda3ce54f",
161
- :x_variable_open => "true",
162
- :x_variable_rfc2822_date => "Wed, 27 Jun 2012 13:47:25 +0100",
163
- :x_variable_export_vars => "RFC2822_DATE",
164
- :x_variable_current_application => "park"
93
+ 'X-variable_direction' => "inbound",
94
+ 'X-variable_uuid' => "3f0e1e18-c056-11e1-b099-fffeda3ce54f",
95
+ 'X-variable_session_id' => "1",
96
+ 'X-variable_sip_local_network_addr' => "109.148.160.137",
97
+ 'X-variable_sip_network_ip' => "192.168.1.74",
98
+ 'X-variable_sip_network_port' => "59253",
99
+ 'X-variable_sip_received_ip' => "192.168.1.74",
100
+ 'X-variable_sip_received_port' => "59253",
101
+ 'X-variable_sip_via_protocol' => "udp",
102
+ 'X-variable_sip_authorized' => "true",
103
+ 'X-variable_sip_number_alias' => "1000",
104
+ 'X-variable_sip_auth_username' => "1000",
105
+ 'X-variable_sip_auth_realm' => "127.0.0.1",
106
+ 'X-variable_number_alias' => "1000",
107
+ 'X-variable_user_name' => "1000",
108
+ 'X-variable_domain_name' => "127.0.0.1",
109
+ 'X-variable_record_stereo' => "true",
110
+ 'X-variable_default_gateway' => "example.com",
111
+ 'X-variable_default_areacode' => "918",
112
+ 'X-variable_transfer_fallback_extension' => "operator",
113
+ 'X-variable_toll_allow' => "domestic,international,local",
114
+ 'X-variable_accountcode' => "1000",
115
+ 'X-variable_user_context' => "default",
116
+ 'X-variable_effective_caller_id_name' => "Extension 1000",
117
+ 'X-variable_effective_caller_id_number' => "1000",
118
+ 'X-variable_outbound_caller_id_name' => "FreeSWITCH",
119
+ 'X-variable_outbound_caller_id_number' => "0000000000",
120
+ 'X-variable_callgroup' => "techsupport",
121
+ 'X-variable_sip_from_user' => "1000",
122
+ 'X-variable_sip_from_uri' => "1000@127.0.0.1",
123
+ 'X-variable_sip_from_host' => "127.0.0.1",
124
+ 'X-variable_sip_from_user_stripped' => "1000",
125
+ 'X-variable_sip_from_tag' => "1248111553",
126
+ 'X-variable_sofia_profile_name' => "internal",
127
+ 'X-variable_sip_full_via' => "SIP/2.0/UDP 192.168.1.74:59253;rport=59253;branch=z9hG4bK2021947958",
128
+ 'X-variable_sip_full_from' => "<sip:1000@127.0.0.1>;tag=1248111553",
129
+ 'X-variable_sip_full_to' => "<sip:10@127.0.0.1>",
130
+ 'X-variable_sip_req_user' => "10",
131
+ 'X-variable_sip_req_uri' => "10@127.0.0.1",
132
+ 'X-variable_sip_req_host' => "127.0.0.1",
133
+ 'X-variable_sip_to_user' => "10",
134
+ 'X-variable_sip_to_uri' => "10@127.0.0.1",
135
+ 'X-variable_sip_to_host' => "127.0.0.1",
136
+ 'X-variable_sip_contact_user' => "1000",
137
+ 'X-variable_sip_contact_port' => "59253",
138
+ 'X-variable_sip_contact_uri' => "1000@192.168.1.74:59253",
139
+ 'X-variable_sip_contact_host' => "192.168.1.74",
140
+ 'X-variable_channel_name' => "sofia/internal/1000@127.0.0.1",
141
+ 'X-variable_sip_call_id' => "1251435211@127.0.0.1",
142
+ 'X-variable_sip_user_agent' => "YATE/4.1.0",
143
+ 'X-variable_sip_via_host' => "192.168.1.74",
144
+ 'X-variable_sip_via_port' => "59253",
145
+ 'X-variable_sip_via_rport' => "59253",
146
+ 'X-variable_max_forwards' => "20",
147
+ 'X-variable_presence_id' => "1000@127.0.0.1",
148
+ 'X-variable_switch_r_sdp' => "v=0\r\no=yate 1340801245 1340801245 IN IP4 172.20.10.3\r\ns=SIP Call\r\nc=IN IP4 172.20.10.3\r\nt=0 0\r\nm=audio 25048 RTP/AVP 0 8 11 98 97 102 103 104 105 106 101\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:11 L16/8000\r\na=rtpmap:98 iLBC/8000\r\na=fmtp:98 mode=20\r\na=rtpmap:97 iLBC/8000\r\na=fmtp:97 mode=30\r\na=rtpmap:102 SPEEX/8000\r\na=rtpmap:103 SPEEX/16000\r\na=rtpmap:104 SPEEX/32000\r\na=rtpmap:105 iSAC/16000\r\na=rtpmap:106 iSAC/32000\r\na=rtpmap:101 telephone-event/8000\r\na=ptime:30\r\n",
149
+ 'X-variable_remote_media_ip' => "172.20.10.3",
150
+ 'X-variable_remote_media_port' => "25048",
151
+ 'X-variable_sip_audio_recv_pt' => "0",
152
+ 'X-variable_sip_use_codec_name' => "PCMU",
153
+ 'X-variable_sip_use_codec_rate' => "8000",
154
+ 'X-variable_sip_use_codec_ptime' => "30",
155
+ 'X-variable_read_codec' => "PCMU",
156
+ 'X-variable_read_rate' => "8000",
157
+ 'X-variable_write_codec' => "PCMU",
158
+ 'X-variable_write_rate' => "8000",
159
+ 'X-variable_endpoint_disposition' => "RECEIVED",
160
+ 'X-variable_call_uuid' => "3f0e1e18-c056-11e1-b099-fffeda3ce54f",
161
+ 'X-variable_open' => "true",
162
+ 'X-variable_rfc2822_date' => "Wed, 27 Jun 2012 13:47:25 +0100",
163
+ 'X-variable_export_vars' => "RFC2822_DATE",
164
+ 'X-variable_current_application' => "park"
165
165
  }
166
166
  end
167
167
 
@@ -176,7 +176,7 @@ module Punchblock
176
176
  describe '#register_component' do
177
177
  it 'should make the component accessible by ID' do
178
178
  component_id = 'abc123'
179
- component = mock 'Translator::Freeswitch::Component', :id => component_id
179
+ component = double 'Translator::Freeswitch::Component', :id => component_id
180
180
  subject.register_component component
181
181
  subject.component_with_id(component_id).should be component
182
182
  end
@@ -350,13 +350,13 @@ module Punchblock
350
350
 
351
351
  it "de-registers the call from the translator" do
352
352
  translator.stub :handle_pb_event
353
- translator.should_receive(:deregister_call).once.with(subject)
353
+ translator.should_receive(:deregister_call).once.with(id)
354
354
  subject.handle_es_event es_event
355
355
  end
356
356
 
357
357
  it "should cause all components to send complete events before sending end event" do
358
358
  ssml_doc = RubySpeech::SSML.draw { audio { 'foo.wav' } }
359
- comp_command = Punchblock::Component::Output.new :ssml => ssml_doc
359
+ comp_command = Punchblock::Component::Output.new :render_document => {:value => ssml_doc}
360
360
  comp_command.request!
361
361
  component = subject.execute_command comp_command
362
362
  comp_command.response(0.1).should be_a Ref
@@ -374,7 +374,6 @@ module Punchblock
374
374
  'NORMAL_CLEARING',
375
375
  'ORIGINATOR_CANCEL',
376
376
  'SYSTEM_SHUTDOWN',
377
- 'MANAGER_REQUEST',
378
377
  'BLIND_TRANSFER',
379
378
  'ATTENDED_TRANSFER',
380
379
  'PICKED_OFF',
@@ -392,6 +391,17 @@ module Punchblock
392
391
  end
393
392
  end
394
393
 
394
+ context "with a MANAGER_REQUEST cause" do
395
+ let(:cause) { 'MANAGER_REQUEST' }
396
+
397
+ it 'should send an end (hangup-command) event to the translator' do
398
+ expected_end_event = Punchblock::Event::End.new :reason => :hangup_command,
399
+ :target_call_id => subject.id
400
+ translator.should_receive(:handle_pb_event).with expected_end_event
401
+ subject.handle_es_event es_event
402
+ end
403
+ end
404
+
395
405
  context "with a user busy cause" do
396
406
  let(:cause) { 'USER_BUSY' }
397
407
 
@@ -500,7 +510,7 @@ module Punchblock
500
510
  end
501
511
 
502
512
  context 'with an event for a known component' do
503
- let(:mock_component_node) { mock 'Punchblock::Component::Output' }
513
+ let(:mock_component_node) { double 'Punchblock::Component::Output' }
504
514
  let :component do
505
515
  Component::Output.new mock_component_node, subject
506
516
  end
@@ -581,7 +591,7 @@ module Punchblock
581
591
  RubyFS::Event.new nil, :event_name => 'DTMF'
582
592
  end
583
593
 
584
- let(:response) { mock 'Response' }
594
+ let(:response) { double 'Response' }
585
595
 
586
596
  it 'should execute the handler' do
587
597
  response.should_receive(:call).once.with es_event
@@ -596,10 +606,8 @@ module Punchblock
596
606
  let(:other_call_id) { Punchblock.new_uuid }
597
607
 
598
608
  let :expected_joined do
599
- Punchblock::Event::Joined.new.tap do |joined|
600
- joined.target_call_id = subject.id
601
- joined.call_id = other_call_id
602
- end
609
+ Punchblock::Event::Joined.new target_call_id: subject.id,
610
+ call_uri: other_call_id
603
611
  end
604
612
 
605
613
  context "where this is the joining call" do
@@ -637,10 +645,8 @@ module Punchblock
637
645
  let(:other_call_id) { Punchblock.new_uuid }
638
646
 
639
647
  let :expected_unjoined do
640
- Punchblock::Event::Unjoined.new.tap do |joined|
641
- joined.target_call_id = subject.id
642
- joined.call_id = other_call_id
643
- end
648
+ Punchblock::Event::Unjoined.new target_call_id: subject.id,
649
+ call_uri: other_call_id
644
650
  end
645
651
 
646
652
  context "where this is the unjoining call" do
@@ -738,7 +744,7 @@ module Punchblock
738
744
  let(:command) { Command::Hangup.new }
739
745
 
740
746
  it "should send a hangup message and set the command's response" do
741
- expect_hangup_with_reason 'NORMAL_CLEARING'
747
+ expect_hangup_with_reason 'MANAGER_REQUEST'
742
748
  subject.execute_command command
743
749
  command.response(0.5).should be true
744
750
  end
@@ -874,7 +880,7 @@ module Punchblock
874
880
  end
875
881
 
876
882
  let :mock_component do
877
- mock 'Component', :id => component_id
883
+ double 'Component', :id => component_id
878
884
  end
879
885
 
880
886
  context "for a known component ID" do
@@ -888,19 +894,17 @@ module Punchblock
888
894
 
889
895
  context "for a component which began executing but crashed" do
890
896
  let :component_command do
891
- Punchblock::Component::Output.new :ssml => RubySpeech::SSML.draw
897
+ Punchblock::Component::Output.new :render_document => {:value => RubySpeech::SSML.draw}
892
898
  end
893
899
 
894
- let(:comp_id) { component_command.response.id }
900
+ let(:comp_id) { component_command.response.component_id }
895
901
 
896
902
  let(:subsequent_command) { Punchblock::Component::Stop.new :component_id => comp_id }
897
903
 
898
904
  let :expected_event do
899
- Punchblock::Event::Complete.new.tap do |e|
900
- e.target_call_id = subject.id
901
- e.component_id = comp_id
902
- e.reason = Punchblock::Event::Complete::Error.new
903
- end
905
+ Punchblock::Event::Complete.new target_call_id: subject.id,
906
+ component_id: comp_id,
907
+ reason: Punchblock::Event::Complete::Error.new
904
908
  end
905
909
 
906
910
  before do
@@ -951,7 +955,7 @@ module Punchblock
951
955
  let(:other_call_id) { Punchblock.new_uuid }
952
956
 
953
957
  let :command do
954
- Punchblock::Command::Join.new :call_id => other_call_id
958
+ Punchblock::Command::Join.new :call_uri => other_call_id
955
959
  end
956
960
 
957
961
  it "executes the proper uuid_bridge command" do
@@ -983,7 +987,7 @@ module Punchblock
983
987
  let(:other_call_id) { Punchblock.new_uuid }
984
988
 
985
989
  let :command do
986
- Punchblock::Command::Unjoin.new :call_id => other_call_id
990
+ Punchblock::Command::Unjoin.new :call_uri => other_call_id
987
991
  end
988
992
 
989
993
  it "executes the unjoin via transfer to park" do
@@ -25,7 +25,7 @@ module Punchblock
25
25
  end
26
26
 
27
27
  let :command_options do
28
- { :ssml => ssml_doc }
28
+ { :render_document => {:value => ssml_doc} }
29
29
  end
30
30
 
31
31
  def execute
@@ -43,16 +43,16 @@ module Punchblock
43
43
  let(:command_opts) { {} }
44
44
 
45
45
  let :command_options do
46
- { :ssml => ssml_doc }.merge(command_opts)
46
+ { :render_document => {:value => ssml_doc} }.merge(command_opts)
47
47
  end
48
48
 
49
49
  let :original_command do
50
50
  Punchblock::Component::Output.new command_options
51
51
  end
52
52
 
53
- describe 'ssml' do
53
+ describe 'document' do
54
54
  context 'unset' do
55
- let(:command_opts) { { :ssml => nil } }
55
+ let(:ssml_doc) { nil }
56
56
  it "should return an error and not execute any actions" do
57
57
  execute
58
58
  error = ProtocolError.new.setup 'option error', 'An SSML document is required.'
@@ -70,7 +70,16 @@ module Punchblock
70
70
  expect_playback
71
71
  execute
72
72
  subject.handle_es_event RubyFS::Event.new(nil, :event_name => "CHANNEL_EXECUTE_COMPLETE")
73
- original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
73
+ original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Finish
74
+ end
75
+ end
76
+
77
+ context 'with multiple documents' do
78
+ let(:command_opts) { { :render_documents => [{:value => ssml_doc}, {:value => ssml_doc}] } }
79
+ it "should return an error and not execute any actions" do
80
+ subject.execute
81
+ error = ProtocolError.new.setup 'option error', 'Only a single document is supported.'
82
+ original_command.response(0.1).should be == error
74
83
  end
75
84
  end
76
85
  end
@@ -215,11 +224,11 @@ module Punchblock
215
224
  end
216
225
  end
217
226
 
218
- context "set to :speech" do
219
- let(:command_opts) { { :interrupt_on => :speech } }
227
+ context "set to :voice" do
228
+ let(:command_opts) { { :interrupt_on => :voice } }
220
229
  it "should return an error and not execute any actions" do
221
230
  execute
222
- error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.'
231
+ error = ProtocolError.new.setup 'option error', 'An interrupt-on value of voice is unsupported.'
223
232
  original_command.response(0.1).should be == error
224
233
  end
225
234
  end
@@ -11,7 +11,7 @@ module Punchblock
11
11
 
12
12
  let(:id) { Punchblock.new_uuid }
13
13
  let(:translator) { Punchblock::Translator::Freeswitch.new connection }
14
- let(:mock_stream) { mock('RubyFS::Stream') }
14
+ let(:mock_stream) { double('RubyFS::Stream') }
15
15
  let(:call) { Punchblock::Translator::Freeswitch::Call.new id, translator, nil, mock_stream }
16
16
 
17
17
  let(:original_command_options) { {} }
@@ -74,13 +74,17 @@ module Punchblock
74
74
  sleep 0.5
75
75
  end
76
76
 
77
+ let :expected_nlsml do
78
+ RubySpeech::NLSML.draw do
79
+ interpretation confidence: 1 do
80
+ instance "dtmf-1 dtmf-2"
81
+ input "12", mode: :dtmf
82
+ end
83
+ end
84
+ end
85
+
77
86
  let :expected_event do
78
- Punchblock::Component::Input::Complete::Success.new :mode => :dtmf,
79
- :confidence => 1,
80
- :utterance => '12',
81
- :interpretation => 'dtmf-1 dtmf-2',
82
- :component_id => subject.id,
83
- :target_call_id => call.id
87
+ Punchblock::Component::Input::Complete::Match.new nlsml: expected_nlsml
84
88
  end
85
89
 
86
90
  it "should send a success complete event with the relevant data" do
@@ -100,8 +104,7 @@ module Punchblock
100
104
  end
101
105
 
102
106
  let :expected_event do
103
- Punchblock::Component::Input::Complete::NoMatch.new :component_id => subject.id,
104
- :target_call_id => call.id
107
+ Punchblock::Component::Input::Complete::NoMatch.new
105
108
  end
106
109
 
107
110
  it "should send a nomatch complete event" do
@@ -119,6 +122,15 @@ module Punchblock
119
122
  original_command.response(0.1).should be == error
120
123
  end
121
124
  end
125
+
126
+ context 'with multiple grammars' do
127
+ let(:original_command_opts) { { :grammars => [{:value => grammar}, {:value => grammar}] } }
128
+ it "should return an error and not execute any actions" do
129
+ subject.execute
130
+ error = ProtocolError.new.setup 'option error', 'Only a single grammar is supported.'
131
+ original_command.response(0.1).should be == error
132
+ end
133
+ end
122
134
  end
123
135
 
124
136
  describe 'mode' do
@@ -140,8 +152,8 @@ module Punchblock
140
152
  end
141
153
  end
142
154
 
143
- context 'speech' do
144
- let(:original_command_opts) { { :mode => :speech } }
155
+ context 'voice' do
156
+ let(:original_command_opts) { { :mode => :voice } }
145
157
  it "should return an error and not execute any actions" do
146
158
  subject.execute
147
159
  error = ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
@@ -167,7 +179,7 @@ module Punchblock
167
179
  send_dtmf 1
168
180
  sleep 1.5
169
181
  send_dtmf 2
170
- reason.should be_a Punchblock::Component::Input::Complete::Success
182
+ reason.should be_a Punchblock::Component::Input::Complete::Match
171
183
  end
172
184
 
173
185
  it "should cause a NoInput complete event to be sent after the timeout" do
@@ -218,10 +230,10 @@ module Punchblock
218
230
  send_dtmf 1
219
231
  sleep 0.5
220
232
  send_dtmf 2
221
- reason.should be_a Punchblock::Component::Input::Complete::Success
233
+ reason.should be_a Punchblock::Component::Input::Complete::Match
222
234
  end
223
235
 
224
- it "should cause a NoMatch complete event to be sent after the timeout" do
236
+ it "should cause a InterDigitTimeout complete event to be sent after the timeout" do
225
237
  subject.execute
226
238
  sleep 1.5
227
239
  send_dtmf 1
@@ -23,7 +23,7 @@ module Punchblock
23
23
  end
24
24
 
25
25
  let :command_options do
26
- { :ssml => ssml_doc }
26
+ { :render_document => {:value => ssml_doc} }
27
27
  end
28
28
 
29
29
  subject { Output.new original_command, mock_call }
@@ -45,16 +45,17 @@ module Punchblock
45
45
  let(:command_opts) { {} }
46
46
 
47
47
  let :command_options do
48
- { :ssml => ssml_doc }.merge(command_opts)
48
+ { :render_document => {:value => ssml_doc} }.merge(command_opts)
49
49
  end
50
50
 
51
51
  let :original_command do
52
52
  Punchblock::Component::Output.new command_options
53
53
  end
54
54
 
55
- describe 'ssml' do
55
+ describe 'document' do
56
56
  context 'unset' do
57
- let(:command_opts) { { :ssml => nil } }
57
+ let(:ssml_doc) { nil }
58
+
58
59
  it "should return an error and not execute any actions" do
59
60
  subject.execute
60
61
  error = ProtocolError.new.setup 'option error', 'An SSML document is required.'
@@ -62,38 +63,10 @@ module Punchblock
62
63
  end
63
64
  end
64
65
 
65
- context 'with a string (not SSML)' do
66
- let :command_options do
67
- { :text => 'Foo Bar' }
68
- end
69
-
70
- it "should return an unrenderable document error" do
71
- subject.execute
72
- error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.'
73
- original_command.response(0.1).should be == error
74
- end
75
-
76
- context 'with a single text node without spaces' do
77
- let(:audio_filename) { 'http://foo.com/bar.mp3' }
78
- let :command_options do
79
- {
80
- :ssml => RubySpeech::SSML.draw { string audio_filename }
81
- }
82
- end
83
-
84
- it 'should playback the audio file using the playback application' do
85
- expect_playback
86
- subject.execute
87
- end
88
- end
89
- end
90
-
91
66
  context 'with a single audio SSML node' do
92
67
  let(:audio_filename) { 'http://foo.com/bar.mp3' }
93
- let :command_options do
94
- {
95
- :ssml => RubySpeech::SSML.draw { audio :src => audio_filename }
96
- }
68
+ let :ssml_doc do
69
+ RubySpeech::SSML.draw { audio :src => audio_filename }
97
70
  end
98
71
 
99
72
  it 'should playback the audio file using the playback application' do
@@ -105,7 +78,7 @@ module Punchblock
105
78
  expect_playback
106
79
  subject.execute
107
80
  subject.handle_es_event RubyFS::Event.new(nil, :event_name => "CHANNEL_EXECUTE_COMPLETE", :application_response => 'FILE PLAYED')
108
- original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
81
+ original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Finish
109
82
  end
110
83
 
111
84
  context "when playback returns an error" do
@@ -125,13 +98,11 @@ module Punchblock
125
98
  context 'with multiple audio SSML nodes' do
126
99
  let(:audio_filename1) { 'http://foo.com/bar.mp3' }
127
100
  let(:audio_filename2) { 'http://foo.com/baz.mp3' }
128
- let :command_options do
129
- {
130
- :ssml => RubySpeech::SSML.draw do
131
- audio :src => audio_filename1
132
- audio :src => audio_filename2
133
- end
134
- }
101
+ let :ssml_doc do
102
+ RubySpeech::SSML.draw do
103
+ audio :src => audio_filename1
104
+ audio :src => audio_filename2
105
+ end
135
106
  end
136
107
 
137
108
  it 'should playback all audio files using playback' do
@@ -143,17 +114,15 @@ module Punchblock
143
114
  expect_playback([audio_filename1, audio_filename2].join('!'))
144
115
  subject.execute
145
116
  subject.handle_es_event RubyFS::Event.new(nil, :event_name => "CHANNEL_EXECUTE_COMPLETE", :application_response => "FILE PLAYED")
146
- original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
117
+ original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Finish
147
118
  end
148
119
  end
149
120
 
150
121
  context "with an SSML document containing elements other than <audio/>" do
151
- let :command_options do
152
- {
153
- :ssml => RubySpeech::SSML.draw do
154
- string "Foo Bar"
155
- end
156
- }
122
+ let :ssml_doc do
123
+ RubySpeech::SSML.draw do
124
+ string "Foo Bar"
125
+ end
157
126
  end
158
127
 
159
128
  it "should return an unrenderable document error" do
@@ -162,6 +131,15 @@ module Punchblock
162
131
  original_command.response(0.1).should be == error
163
132
  end
164
133
  end
134
+
135
+ context 'with multiple documents' do
136
+ let(:command_opts) { { :render_documents => [{:value => ssml_doc}, {:value => ssml_doc}] } }
137
+ it "should return an error and not execute any actions" do
138
+ subject.execute
139
+ error = ProtocolError.new.setup 'option error', 'Only a single document is supported.'
140
+ original_command.response(0.1).should be == error
141
+ end
142
+ end
165
143
  end
166
144
 
167
145
  describe 'start-offset' do
@@ -304,11 +282,11 @@ module Punchblock
304
282
  end
305
283
  end
306
284
 
307
- context "set to :speech" do
308
- let(:command_opts) { { :interrupt_on => :speech } }
285
+ context "set to :voice" do
286
+ let(:command_opts) { { :interrupt_on => :voice } }
309
287
  it "should return an error and not execute any actions" do
310
288
  subject.execute
311
- error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.'
289
+ error = ProtocolError.new.setup 'option error', 'An interrupt-on value of voice is unsupported.'
312
290
  original_command.response(0.1).should be == error
313
291
  end
314
292
  end