adhearsion 2.6.4 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -9
  3. data/CHANGELOG.md +22 -7
  4. data/Gemfile +2 -0
  5. data/README.markdown +4 -5
  6. data/Rakefile +1 -2
  7. data/adhearsion.gemspec +19 -8
  8. data/features/cli_create.feature +19 -3
  9. data/features/step_definitions/cli_steps.rb +0 -11
  10. data/features/support/env.rb +3 -4
  11. data/lib/adhearsion.rb +48 -27
  12. data/lib/adhearsion/call.rb +34 -50
  13. data/lib/adhearsion/call_controller.rb +6 -12
  14. data/lib/adhearsion/call_controller/dial.rb +15 -53
  15. data/lib/adhearsion/call_controller/input.rb +39 -162
  16. data/lib/adhearsion/call_controller/input/ask_grammar_builder.rb +44 -0
  17. data/lib/adhearsion/call_controller/input/menu_builder.rb +136 -0
  18. data/lib/adhearsion/call_controller/input/prompt_builder.rb +78 -0
  19. data/lib/adhearsion/call_controller/input/result.rb +46 -0
  20. data/lib/adhearsion/call_controller/output.rb +48 -67
  21. data/lib/adhearsion/call_controller/output/abstract_player.rb +3 -3
  22. data/lib/adhearsion/call_controller/output/async_player.rb +3 -3
  23. data/lib/adhearsion/call_controller/output/player.rb +1 -1
  24. data/lib/adhearsion/call_controller/record.rb +23 -8
  25. data/lib/adhearsion/calls.rb +1 -1
  26. data/lib/adhearsion/cli_commands/ahn_command.rb +2 -65
  27. data/lib/adhearsion/cli_commands/thor_errors.rb +0 -6
  28. data/lib/adhearsion/configuration.rb +91 -39
  29. data/lib/adhearsion/core_ext/blather/stanza.rb +41 -0
  30. data/lib/adhearsion/core_ext/blather/stanza/presence.rb +13 -0
  31. data/lib/adhearsion/error.rb +5 -0
  32. data/lib/adhearsion/event.rb +21 -0
  33. data/lib/adhearsion/event/active_speaker.rb +11 -0
  34. data/lib/adhearsion/event/answered.rb +11 -0
  35. data/lib/adhearsion/event/asterisk.rb +10 -0
  36. data/lib/adhearsion/event/asterisk/ami.rb +34 -0
  37. data/lib/adhearsion/event/complete.rb +75 -0
  38. data/lib/adhearsion/event/dtmf.rb +11 -0
  39. data/lib/adhearsion/event/end.rb +22 -0
  40. data/lib/adhearsion/event/input_timers_started.rb +9 -0
  41. data/lib/adhearsion/event/joined.rb +17 -0
  42. data/lib/adhearsion/event/offer.rb +14 -0
  43. data/lib/adhearsion/event/ringing.rb +11 -0
  44. data/lib/adhearsion/event/started_speaking.rb +13 -0
  45. data/lib/adhearsion/event/stopped_speaking.rb +13 -0
  46. data/lib/adhearsion/event/unjoined.rb +17 -0
  47. data/lib/adhearsion/events.rb +47 -66
  48. data/lib/adhearsion/foundation.rb +0 -1
  49. data/lib/adhearsion/foundation/object.rb +0 -5
  50. data/lib/adhearsion/generators/app/app_generator.rb +4 -1
  51. data/lib/adhearsion/generators/app/templates/Gemfile.erb +2 -10
  52. data/lib/adhearsion/generators/app/templates/adhearsion.erb +9 -9
  53. data/lib/adhearsion/generators/app/templates/config.ru +7 -0
  54. data/lib/adhearsion/generators/app/templates/en.yml +4 -0
  55. data/lib/adhearsion/generators/app/templates/events.erb +2 -2
  56. data/lib/adhearsion/generators/app/templates/hello_world.wav +0 -0
  57. data/lib/adhearsion/generators/app/templates/simon_game.rb +2 -1
  58. data/lib/adhearsion/generators/app/templates/simon_game_spec.rb +2 -2
  59. data/lib/adhearsion/has_headers.rb +34 -0
  60. data/lib/adhearsion/http_server.rb +37 -0
  61. data/lib/adhearsion/initializer.rb +19 -153
  62. data/lib/adhearsion/logging.rb +6 -25
  63. data/lib/adhearsion/outbound_call.rb +5 -5
  64. data/lib/adhearsion/plugin.rb +1 -0
  65. data/lib/adhearsion/protocol_error.rb +26 -0
  66. data/lib/adhearsion/rayo.rb +30 -0
  67. data/lib/adhearsion/rayo/client.rb +62 -0
  68. data/lib/adhearsion/rayo/client/component_registry.rb +33 -0
  69. data/lib/adhearsion/rayo/command.rb +21 -0
  70. data/lib/adhearsion/rayo/command/accept.rb +16 -0
  71. data/lib/adhearsion/rayo/command/answer.rb +16 -0
  72. data/lib/adhearsion/rayo/command/dial.rb +57 -0
  73. data/lib/adhearsion/rayo/command/hangup.rb +16 -0
  74. data/lib/adhearsion/rayo/command/join.rb +43 -0
  75. data/lib/adhearsion/rayo/command/mute.rb +13 -0
  76. data/lib/adhearsion/rayo/command/redirect.rb +23 -0
  77. data/lib/adhearsion/rayo/command/reject.rb +40 -0
  78. data/lib/adhearsion/rayo/command/unjoin.rb +24 -0
  79. data/lib/adhearsion/rayo/command/unmute.rb +13 -0
  80. data/lib/adhearsion/rayo/command_node.rb +47 -0
  81. data/lib/adhearsion/rayo/component.rb +21 -0
  82. data/lib/adhearsion/rayo/component/asterisk.rb +13 -0
  83. data/lib/adhearsion/rayo/component/asterisk/agi.rb +14 -0
  84. data/lib/adhearsion/rayo/component/asterisk/agi/command.rb +46 -0
  85. data/lib/adhearsion/rayo/component/asterisk/ami.rb +14 -0
  86. data/lib/adhearsion/rayo/component/asterisk/ami/action.rb +61 -0
  87. data/lib/adhearsion/rayo/component/component_node.rb +90 -0
  88. data/lib/adhearsion/rayo/component/input.rb +186 -0
  89. data/lib/adhearsion/rayo/component/output.rb +471 -0
  90. data/lib/adhearsion/rayo/component/prompt.rb +53 -0
  91. data/lib/adhearsion/rayo/component/receive_fax.rb +26 -0
  92. data/lib/adhearsion/rayo/component/record.rb +165 -0
  93. data/lib/adhearsion/rayo/component/send_fax.rb +64 -0
  94. data/lib/adhearsion/rayo/component/stop.rb +11 -0
  95. data/lib/adhearsion/rayo/connection.rb +12 -0
  96. data/lib/adhearsion/rayo/connection/asterisk.rb +74 -0
  97. data/lib/adhearsion/rayo/connection/connected.rb +22 -0
  98. data/lib/adhearsion/rayo/connection/generic_connection.rb +22 -0
  99. data/lib/adhearsion/rayo/connection/xmpp.rb +198 -0
  100. data/lib/adhearsion/rayo/disconnected_error.rb +22 -0
  101. data/lib/adhearsion/{punchblock_plugin → rayo}/initializer.rb +19 -19
  102. data/lib/adhearsion/rayo/rayo_node.rb +127 -0
  103. data/lib/adhearsion/rayo/ref.rb +57 -0
  104. data/lib/adhearsion/statistics.rb +1 -1
  105. data/lib/adhearsion/tasks.rb +1 -2
  106. data/lib/adhearsion/tasks/configuration.rb +1 -1
  107. data/lib/adhearsion/tasks/environment.rb +0 -2
  108. data/lib/adhearsion/tasks/i18n.rb +49 -0
  109. data/lib/adhearsion/translator.rb +11 -0
  110. data/lib/adhearsion/translator/asterisk.rb +234 -0
  111. data/lib/adhearsion/translator/asterisk/agi_app.rb +17 -0
  112. data/lib/adhearsion/translator/asterisk/agi_command.rb +45 -0
  113. data/lib/adhearsion/translator/asterisk/ami_error_converter.rb +20 -0
  114. data/lib/adhearsion/translator/asterisk/call.rb +416 -0
  115. data/lib/adhearsion/translator/asterisk/channel.rb +43 -0
  116. data/lib/adhearsion/translator/asterisk/component.rb +88 -0
  117. data/lib/adhearsion/translator/asterisk/component/asterisk.rb +15 -0
  118. data/lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb +42 -0
  119. data/lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb +68 -0
  120. data/lib/adhearsion/translator/asterisk/component/composed_prompt.rb +76 -0
  121. data/lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb +137 -0
  122. data/lib/adhearsion/translator/asterisk/component/input.rb +34 -0
  123. data/lib/adhearsion/translator/asterisk/component/input_component.rb +90 -0
  124. data/lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb +71 -0
  125. data/lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb +55 -0
  126. data/lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb +165 -0
  127. data/lib/adhearsion/translator/asterisk/component/output.rb +233 -0
  128. data/lib/adhearsion/translator/asterisk/component/record.rb +101 -0
  129. data/lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb +30 -0
  130. data/lib/adhearsion/translator/asterisk/unimrcp_app.rb +28 -0
  131. data/lib/adhearsion/uri_list.rb +21 -0
  132. data/lib/adhearsion/version.rb +1 -1
  133. data/spec/adhearsion/call_controller/dial_spec.rb +79 -1420
  134. data/spec/adhearsion/call_controller/input_spec.rb +1141 -237
  135. data/spec/adhearsion/call_controller/output/async_player_spec.rb +10 -10
  136. data/spec/adhearsion/call_controller/output/player_spec.rb +8 -8
  137. data/spec/adhearsion/call_controller/output_spec.rb +162 -215
  138. data/spec/adhearsion/call_controller/record_spec.rb +15 -16
  139. data/spec/adhearsion/call_controller_spec.rb +23 -40
  140. data/spec/adhearsion/call_spec.rb +123 -129
  141. data/spec/adhearsion/calls_spec.rb +3 -3
  142. data/spec/adhearsion/configuration_spec.rb +94 -108
  143. data/spec/adhearsion/event/answered_spec.rb +50 -0
  144. data/spec/adhearsion/event/asterisk/Find Results +402 -0
  145. data/spec/adhearsion/event/asterisk/ami_spec.rb +81 -0
  146. data/spec/adhearsion/event/complete_spec.rb +176 -0
  147. data/spec/adhearsion/event/dtmf_spec.rb +35 -0
  148. data/spec/adhearsion/event/end_spec.rb +85 -0
  149. data/spec/adhearsion/event/input_timers_started_spec.rb +19 -0
  150. data/spec/adhearsion/event/joined_spec.rb +53 -0
  151. data/spec/adhearsion/event/offer_spec.rb +106 -0
  152. data/spec/adhearsion/event/ringing_spec.rb +50 -0
  153. data/spec/adhearsion/event/started_speaking_spec.rb +37 -0
  154. data/spec/adhearsion/event/stopped_speaking_spec.rb +37 -0
  155. data/spec/adhearsion/event/unjoined_spec.rb +48 -0
  156. data/spec/adhearsion/event/untitled +0 -0
  157. data/spec/adhearsion/events_spec.rb +19 -45
  158. data/spec/adhearsion/initializer_spec.rb +12 -184
  159. data/spec/adhearsion/logging_spec.rb +5 -20
  160. data/spec/adhearsion/outbound_call_spec.rb +13 -13
  161. data/spec/adhearsion/plugin_spec.rb +3 -4
  162. data/spec/adhearsion/protocol_error_spec.rb +91 -0
  163. data/spec/adhearsion/rayo/client/component_registry_spec.rb +26 -0
  164. data/spec/adhearsion/rayo/client_spec.rb +134 -0
  165. data/spec/adhearsion/rayo/command/accept_spec.rb +63 -0
  166. data/spec/adhearsion/rayo/command/answer_spec.rb +73 -0
  167. data/spec/adhearsion/rayo/command/dial_spec.rb +156 -0
  168. data/spec/adhearsion/rayo/command/hangup_spec.rb +63 -0
  169. data/spec/adhearsion/rayo/command/join_spec.rb +158 -0
  170. data/spec/adhearsion/rayo/command/mute_spec.rb +32 -0
  171. data/spec/adhearsion/rayo/command/redirect_spec.rb +89 -0
  172. data/spec/adhearsion/rayo/command/reject_spec.rb +117 -0
  173. data/spec/adhearsion/rayo/command/unjoin_spec.rb +82 -0
  174. data/spec/adhearsion/rayo/command/unmute_spec.rb +32 -0
  175. data/spec/adhearsion/rayo/command_node_spec.rb +101 -0
  176. data/spec/adhearsion/rayo/component/asterisk/agi/command_spec.rb +111 -0
  177. data/spec/adhearsion/rayo/component/asterisk/ami/action_spec.rb +173 -0
  178. data/spec/adhearsion/rayo/component/component_node_spec.rb +110 -0
  179. data/spec/adhearsion/rayo/component/input_spec.rb +715 -0
  180. data/spec/adhearsion/rayo/component/output_spec.rb +1030 -0
  181. data/spec/adhearsion/rayo/component/prompt_spec.rb +171 -0
  182. data/spec/adhearsion/rayo/component/receive_fax_spec.rb +136 -0
  183. data/spec/adhearsion/rayo/component/record_spec.rb +497 -0
  184. data/spec/adhearsion/rayo/component/send_fax_spec.rb +144 -0
  185. data/spec/adhearsion/rayo/connection/asterisk_spec.rb +118 -0
  186. data/spec/adhearsion/rayo/connection/xmpp_spec.rb +449 -0
  187. data/spec/adhearsion/rayo/initializer_spec.rb +353 -0
  188. data/spec/adhearsion/rayo/ref_spec.rb +168 -0
  189. data/spec/adhearsion/rayo_spec.rb +7 -0
  190. data/spec/adhearsion/router/route_spec.rb +1 -1
  191. data/spec/adhearsion/statistics_spec.rb +2 -5
  192. data/spec/adhearsion/translator/asterisk/call_spec.rb +2047 -0
  193. data/spec/adhearsion/translator/asterisk/component/asterisk/agi_command_spec.rb +256 -0
  194. data/spec/adhearsion/translator/asterisk/component/asterisk/ami_action_spec.rb +151 -0
  195. data/spec/adhearsion/translator/asterisk/component/composed_prompt_spec.rb +257 -0
  196. data/spec/adhearsion/translator/asterisk/component/input_spec.rb +571 -0
  197. data/spec/adhearsion/translator/asterisk/component/mrcp_native_prompt_spec.rb +774 -0
  198. data/spec/adhearsion/translator/asterisk/component/mrcp_prompt_spec.rb +1244 -0
  199. data/spec/adhearsion/translator/asterisk/component/output_spec.rb +1850 -0
  200. data/spec/adhearsion/translator/asterisk/component/record_spec.rb +426 -0
  201. data/spec/adhearsion/translator/asterisk/component/stop_by_redirect_spec.rb +62 -0
  202. data/spec/adhearsion/translator/asterisk/component_spec.rb +83 -0
  203. data/spec/adhearsion/translator/asterisk_spec.rb +685 -0
  204. data/spec/adhearsion/uri_list_spec.rb +88 -0
  205. data/spec/adhearsion_spec.rb +89 -14
  206. data/spec/fixtures/locale/en.yml +13 -0
  207. data/spec/fixtures/locale/it.yml +13 -0
  208. data/spec/spec_helper.rb +18 -5
  209. data/spec/support/call_controller_test_helpers.rb +3 -2
  210. data/spec/support/initializer_stubs.rb +3 -1
  211. data/spec/support/punchblock_examples.rb +65 -0
  212. data/spec/support/punchblock_mocks.rb +12 -0
  213. metadata +412 -70
  214. data/features/cli_daemon.feature +0 -20
  215. data/features/cli_restart.feature +0 -52
  216. data/features/cli_stop.feature +0 -50
  217. data/lib/adhearsion/call_controller/menu_dsl.rb +0 -21
  218. data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +0 -26
  219. data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +0 -43
  220. data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +0 -45
  221. data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +0 -11
  222. data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +0 -40
  223. data/lib/adhearsion/call_controller/menu_dsl/menu.rb +0 -207
  224. data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +0 -92
  225. data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +0 -60
  226. data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +0 -25
  227. data/lib/adhearsion/call_controller/utility.rb +0 -77
  228. data/lib/adhearsion/foundation/custom_daemonizer.rb +0 -52
  229. data/lib/adhearsion/punchblock_plugin.rb +0 -63
  230. data/scripts/cloc-1.64.pl +0 -10483
  231. data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +0 -76
  232. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +0 -60
  233. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +0 -61
  234. data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +0 -39
  235. data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +0 -17
  236. data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +0 -165
  237. data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +0 -420
  238. data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +0 -32
  239. data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +0 -40
  240. data/spec/adhearsion/call_controller/utility_spec.rb +0 -90
  241. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +0 -356
  242. data/spec/adhearsion/punchblock_plugin_spec.rb +0 -59
@@ -0,0 +1,774 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ module Translator
7
+ class Asterisk
8
+ module Component
9
+ describe MRCPNativePrompt do
10
+ include HasMockCallbackConnection
11
+
12
+ let(:ami_client) { double('AMI') }
13
+ let(:translator) { Translator::Asterisk.new ami_client, connection }
14
+ let(:mock_call) { Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
15
+
16
+ let :ssml_doc do
17
+ RubySpeech::SSML.draw do
18
+ say_as(:interpret_as => :cardinal) { 'FOO' }
19
+ end
20
+ end
21
+
22
+ let :voice_grammar do
23
+ RubySpeech::GRXML.draw :mode => 'voice', :root => 'color' do
24
+ rule id: 'color' do
25
+ one_of do
26
+ item { 'red' }
27
+ item { 'blue' }
28
+ item { 'green' }
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ let :dtmf_grammar do
35
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
36
+ rule id: 'digit' do
37
+ one_of do
38
+ 0.upto(9) { |d| item { d.to_s } }
39
+ end
40
+ end
41
+
42
+ rule id: 'pin', scope: 'public' do
43
+ item repeat: '2' do
44
+ ruleref uri: '#digit'
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ let(:grammar) { voice_grammar }
51
+
52
+ let(:output_command_opts) { {} }
53
+
54
+ let(:audio_filename) { '/var/foo' }
55
+ let(:audio_path) { "file://#{audio_filename}.wav" }
56
+
57
+ let :output_command_options do
58
+ { render_document: {value: [audio_path], content_type: 'text/uri-list'} }.merge(output_command_opts)
59
+ end
60
+
61
+ let(:input_command_opts) { {} }
62
+
63
+ let :input_command_options do
64
+ { grammar: {value: grammar} }.merge(input_command_opts)
65
+ end
66
+
67
+ let(:command_options) { {} }
68
+
69
+ let :output_command do
70
+ Adhearsion::Rayo::Component::Output.new output_command_options
71
+ end
72
+
73
+ let :input_command do
74
+ Adhearsion::Rayo::Component::Input.new input_command_options
75
+ end
76
+
77
+ let :original_command do
78
+ Adhearsion::Rayo::Component::Prompt.new output_command, input_command, command_options
79
+ end
80
+
81
+ let(:recog_status) { 'OK' }
82
+ let(:recog_completion_cause) { '000' }
83
+ let(:recog_result) { "%3C?xml%20version=%221.0%22?%3E%3Cresult%3E%0D%0A%3Cinterpretation%20grammar=%22session:grammar-0%22%20confidence=%220.43%22%3E%3Cinput%20mode=%22speech%22%3EHello%3C/input%3E%3Cinstance%3EHello%3C/instance%3E%3C/interpretation%3E%3C/result%3E" }
84
+
85
+ subject { described_class.new original_command, mock_call }
86
+
87
+ before do
88
+ original_command.request!
89
+ {
90
+ 'RECOG_STATUS' => recog_status,
91
+ 'RECOG_COMPLETION_CAUSE' => recog_completion_cause,
92
+ 'RECOG_RESULT' => recog_result
93
+ }.each do |var, val|
94
+ allow(mock_call).to receive(:channel_var).with(var).and_return val
95
+ end
96
+ end
97
+
98
+ context 'with an invalid recognizer' do
99
+ let(:input_command_opts) { { recognizer: 'foobar' } }
100
+
101
+ it "should return an error and not execute any actions" do
102
+ subject.execute
103
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'The recognizer foobar is unsupported.'
104
+ expect(original_command.response(0.1)).to eq(error)
105
+ end
106
+ end
107
+
108
+ [:asterisk].each do |recognizer|
109
+ context "with a recognizer #{recognizer.inspect}" do
110
+ let(:input_command_opts) { { recognizer: recognizer } }
111
+
112
+ it "should return an error and not execute any actions" do
113
+ subject.execute
114
+ error = Adhearsion::ProtocolError.new.setup 'option error', "The recognizer #{recognizer} is unsupported."
115
+ expect(original_command.response(0.1)).to eq(error)
116
+ end
117
+ end
118
+ end
119
+
120
+ def expect_mrcprecog_with_options(options)
121
+ expect_app_with_options 'MRCPRecog', options
122
+ end
123
+
124
+ def expect_app_with_options(app, options)
125
+ expect(mock_call).to receive(:execute_agi_command).once { |*args|
126
+ expect(args[0]).to eq("EXEC #{app}")
127
+ expect(args[1]).to match options
128
+ {code: 200, result: 1}
129
+ }
130
+ end
131
+
132
+ describe 'Output#document' do
133
+ context 'with multiple inline documents' do
134
+ let(:output_command_options) { { render_documents: [{value: ssml_doc}, {value: ssml_doc}] } }
135
+
136
+ it "should return an error and not execute any actions" do
137
+ subject.execute
138
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only one document is allowed.'
139
+ expect(original_command.response(0.1)).to eq(error)
140
+ end
141
+ end
142
+
143
+ context 'with a document by URI' do
144
+ let(:output_command_options) { { render_documents: [{url: 'http://example.com/doc1.ssml'}] } }
145
+
146
+ it "should return an error and not execute any actions" do
147
+ subject.execute
148
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only inline documents are allowed.'
149
+ expect(original_command.response(0.1)).to eq(error)
150
+ end
151
+ end
152
+
153
+ context 'with a urilist > size 1' do
154
+ let(:output_command_options) { { render_documents: [{content_type: 'text/uri-list', value: ['http://example.com/hello.mp3', 'http://example.com/goodbye.mp3']}] } }
155
+
156
+ it "should return an error and not execute any actions" do
157
+ subject.execute
158
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only one audio file is allowed.'
159
+ expect(original_command.response(0.1)).to eq(error)
160
+ end
161
+ end
162
+
163
+ context 'with multiple audio tags in SSML' do
164
+ let :ssml_doc do
165
+ RubySpeech::SSML.draw do
166
+ audio(src: audio_path)
167
+ audio(src: audio_path)
168
+ end
169
+ end
170
+
171
+ let(:output_command_options) { { render_documents: [{value: ssml_doc}] } }
172
+
173
+ it "should return an error and not execute any actions" do
174
+ subject.execute
175
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only one audio file is allowed.'
176
+ expect(original_command.response(0.1)).to eq(error)
177
+ end
178
+ end
179
+
180
+ context 'unset' do
181
+ let(:output_command_options) { {} }
182
+
183
+ it "should return an error and not execute any actions" do
184
+ subject.execute
185
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A document is required.'
186
+ expect(original_command.response(0.1)).to eq(error)
187
+ end
188
+ end
189
+ end
190
+
191
+ describe 'Output#renderer' do
192
+ [nil, :asterisk].each do |renderer|
193
+ context renderer.to_s do
194
+ let(:output_command_opts) { { renderer: renderer } }
195
+
196
+ it "should return a ref and execute MRCPRecog" do
197
+ param = ["\"#{grammar.to_doc.to_s.squish.gsub('"', '\"')}\"", "uer=1&b=1&f=#{audio_filename}"].join(',')
198
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC MRCPRecog', param).and_return code: 200, result: 1
199
+ subject.execute
200
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
201
+ end
202
+
203
+ context "when the render document is SSML" do
204
+ let :ssml_doc do
205
+ RubySpeech::SSML.draw do
206
+ audio(src: audio_path)
207
+ end
208
+ end
209
+
210
+ let(:output_command_opts) do
211
+ {
212
+ renderer: renderer,
213
+ render_document: {value: ssml_doc}
214
+ }
215
+ end
216
+
217
+ it "should return a ref and execute MRCPRecog" do
218
+ param = ["\"#{grammar.to_doc.to_s.squish.gsub('"', '\"')}\"", "uer=1&b=1&f=#{audio_filename}"].join(',')
219
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC MRCPRecog', param).and_return code: 200, result: 1
220
+ subject.execute
221
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
222
+ end
223
+ end
224
+
225
+ context "when MRCPRecog completes" do
226
+ shared_context "with a match" do
227
+ let :expected_nlsml do
228
+ RubySpeech::NLSML.draw do
229
+ interpretation grammar: 'session:grammar-0', confidence: 0.43 do
230
+ input 'Hello', mode: :speech
231
+ instance 'Hello'
232
+ end
233
+ end
234
+ end
235
+
236
+ it 'should send a match complete event' do
237
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
238
+
239
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
240
+ subject.execute
241
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
242
+ end
243
+ end
244
+
245
+ context "with a match cause" do
246
+ %w{000 008 012}.each do |code|
247
+ context "when the MRCP recognition response code is #{code}" do
248
+ let(:recog_completion_cause) { code }
249
+
250
+ it_behaves_like 'with a match'
251
+ end
252
+ end
253
+ end
254
+
255
+ context "with a nomatch cause" do
256
+ %w{001 003 013 014 015}.each do |code|
257
+ context "with value #{code}" do
258
+ let(:recog_completion_cause) { code }
259
+
260
+ it 'should send a nomatch complete event' do
261
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
262
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
263
+ subject.execute
264
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ context "with a noinput cause" do
271
+ %w{002 011}.each do |code|
272
+ context "with value #{code}" do
273
+ let(:recog_completion_cause) { code }
274
+
275
+ specify do
276
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::NoInput.new
277
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
278
+ subject.execute
279
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ shared_context 'should send an error complete event' do
286
+ specify do
287
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
288
+ subject.execute
289
+ complete_reason = original_command.complete_event(0.1).reason
290
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Error
291
+ end
292
+ end
293
+
294
+ context 'with an error cause' do
295
+ %w{004 005 006 007 009 010 016}.each do |code|
296
+ context "when the MRCP recognition response code is #{code}" do
297
+ let(:recog_completion_cause) { code }
298
+ it_behaves_like 'should send an error complete event'
299
+ end
300
+ end
301
+ end
302
+
303
+ context 'with an invalid cause' do
304
+ let(:recog_completion_cause) { '999' }
305
+ it_behaves_like 'should send an error complete event'
306
+ end
307
+
308
+ context "when the RECOG_STATUS variable is set to 'ERROR'" do
309
+ let(:recog_status) { 'ERROR' }
310
+ it_behaves_like 'should send an error complete event'
311
+ end
312
+ end
313
+
314
+ context "when we get a RubyAMI Error" do
315
+ it "should send an error complete event" do
316
+ error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
317
+ expect(mock_call).to receive(:execute_agi_command).and_raise error
318
+ subject.execute
319
+ complete_reason = original_command.complete_event(0.1).reason
320
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Error
321
+ expect(complete_reason.details).to eq("Terminated due to AMI error 'FooBar'")
322
+ end
323
+ end
324
+
325
+ context "when the channel is gone" do
326
+ it "should send an error complete event" do
327
+ error = ChannelGoneError.new 'FooBar'
328
+ expect(mock_call).to receive(:execute_agi_command).and_raise error
329
+ subject.execute
330
+ complete_reason = original_command.complete_event(0.1).reason
331
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Hangup
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ [:foobar, :swift, :unimrcp].each do |renderer|
338
+ context renderer.to_s do
339
+ let(:output_command_opts) { { renderer: renderer } }
340
+
341
+ it "should return an error and not execute any actions" do
342
+ subject.execute
343
+ error = Adhearsion::ProtocolError.new.setup 'option error', "The renderer #{renderer} is unsupported."
344
+ expect(original_command.response(0.1)).to eq(error)
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ describe 'barge_in' do
351
+ context 'unset' do
352
+ let(:command_options) { { barge_in: nil } }
353
+
354
+ it 'should pass the b=1 option to MRCPRecog' do
355
+ expect_mrcprecog_with_options(/b=1/)
356
+ subject.execute
357
+ end
358
+ end
359
+
360
+ context 'true' do
361
+ let(:command_options) { { barge_in: true } }
362
+
363
+ it 'should pass the b=1 option to MRCPRecog' do
364
+ expect_mrcprecog_with_options(/b=1/)
365
+ subject.execute
366
+ end
367
+ end
368
+
369
+ context 'false' do
370
+ let(:command_options) { { barge_in: false } }
371
+
372
+ it 'should pass the b=0 option to MRCPRecog' do
373
+ expect_mrcprecog_with_options(/b=0/)
374
+ subject.execute
375
+ end
376
+ end
377
+ end
378
+
379
+ describe 'Output#voice' do
380
+ context 'unset' do
381
+ let(:output_command_opts) { { voice: nil } }
382
+
383
+ it 'should not pass any options to MRCPRecog' do
384
+ expect_mrcprecog_with_options(//)
385
+ subject.execute
386
+ end
387
+ end
388
+
389
+ context 'set' do
390
+ let(:output_command_opts) { { voice: 'alison' } }
391
+
392
+ it 'should not pass any options to MRCPRecog' do
393
+ expect_mrcprecog_with_options(//)
394
+ subject.execute
395
+ end
396
+ end
397
+ end
398
+
399
+ describe 'Output#start-offset' do
400
+ context 'unset' do
401
+ let(:output_command_opts) { { start_offset: nil } }
402
+ it 'should not pass any options to MRCPRecog' do
403
+ expect_mrcprecog_with_options(//)
404
+ subject.execute
405
+ end
406
+ end
407
+
408
+ context 'set' do
409
+ let(:output_command_opts) { { start_offset: 10 } }
410
+ it "should return an error and not execute any actions" do
411
+ subject.execute
412
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A start_offset value is unsupported on Asterisk.'
413
+ expect(original_command.response(0.1)).to eq(error)
414
+ end
415
+ end
416
+ end
417
+
418
+ describe 'Output#start-paused' do
419
+ context 'false' do
420
+ let(:output_command_opts) { { start_paused: false } }
421
+ it 'should not pass any options to MRCPRecog' do
422
+ expect_mrcprecog_with_options(//)
423
+ subject.execute
424
+ end
425
+ end
426
+
427
+ context 'true' do
428
+ let(:output_command_opts) { { start_paused: true } }
429
+ it "should return an error and not execute any actions" do
430
+ subject.execute
431
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A start_paused value is unsupported on Asterisk.'
432
+ expect(original_command.response(0.1)).to eq(error)
433
+ end
434
+ end
435
+ end
436
+
437
+ describe 'Output#repeat-interval' do
438
+ context 'unset' do
439
+ let(:output_command_opts) { { repeat_interval: nil } }
440
+ it 'should not pass any options to MRCPRecog' do
441
+ expect_mrcprecog_with_options(//)
442
+ subject.execute
443
+ end
444
+ end
445
+
446
+ context 'set' do
447
+ let(:output_command_opts) { { repeat_interval: 10 } }
448
+ it "should return an error and not execute any actions" do
449
+ subject.execute
450
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported on Asterisk.'
451
+ expect(original_command.response(0.1)).to eq(error)
452
+ end
453
+ end
454
+ end
455
+
456
+ describe 'Output#repeat-times' do
457
+ context 'unset' do
458
+ let(:output_command_opts) { { repeat_times: nil } }
459
+ it 'should not pass any options to MRCPRecog' do
460
+ expect_mrcprecog_with_options(//)
461
+ subject.execute
462
+ end
463
+ end
464
+
465
+ context 'set' do
466
+ let(:output_command_opts) { { repeat_times: 2 } }
467
+ it "should return an error and not execute any actions" do
468
+ subject.execute
469
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported on Asterisk.'
470
+ expect(original_command.response(0.1)).to eq(error)
471
+ end
472
+ end
473
+ end
474
+
475
+ describe 'Output#max-time' do
476
+ context 'unset' do
477
+ let(:output_command_opts) { { max_time: nil } }
478
+ it 'should not pass any options to MRCPRecog' do
479
+ expect_mrcprecog_with_options(//)
480
+ subject.execute
481
+ end
482
+ end
483
+
484
+ context 'set' do
485
+ let(:output_command_opts) { { max_time: 30 } }
486
+ it "should return an error and not execute any actions" do
487
+ subject.execute
488
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A max_time value is unsupported on Asterisk.'
489
+ expect(original_command.response(0.1)).to eq(error)
490
+ end
491
+ end
492
+ end
493
+
494
+ describe 'Output#interrupt_on' do
495
+ context 'unset' do
496
+ let(:output_command_opts) { { interrupt_on: nil } }
497
+ it 'should not pass any options to MRCPRecog' do
498
+ expect_mrcprecog_with_options(//)
499
+ subject.execute
500
+ end
501
+ end
502
+
503
+ context 'set' do
504
+ let(:output_command_opts) { { interrupt_on: :dtmf } }
505
+ it "should return an error and not execute any actions" do
506
+ subject.execute
507
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A interrupt_on value is unsupported on Asterisk.'
508
+ expect(original_command.response(0.1)).to eq(error)
509
+ end
510
+ end
511
+ end
512
+
513
+ describe 'Input#grammar' do
514
+ context 'with multiple inline grammars' do
515
+ let(:input_command_options) { { grammars: [{value: voice_grammar}, {value: dtmf_grammar}] } }
516
+
517
+ it "should return a ref and execute MRCPRecog" do
518
+ param = ["\"#{[voice_grammar.to_doc.to_s, dtmf_grammar.to_doc.to_s].join(',').squish.gsub('"', '\"')}\"", "uer=1&b=1&f=#{audio_filename}"].join(',')
519
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC MRCPRecog', param).and_return code: 200, result: 1
520
+ subject.execute
521
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
522
+ end
523
+ end
524
+
525
+ context 'with multiple grammars by URI' do
526
+ let(:input_command_options) { { grammars: [{url: 'http://example.com/grammar1.grxml'}, {url: 'http://example.com/grammar2.grxml'}] } }
527
+
528
+ it "should return a ref and execute MRCPRecog" do
529
+ param = ["\"#{"http://example.com/grammar1.grxml,http://example.com/grammar2.grxml".squish.gsub('"', '\"')}\"", "uer=1&b=1&f=#{audio_filename}"].join(',')
530
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC MRCPRecog', param).and_return code: 200, result: 1
531
+ subject.execute
532
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
533
+ end
534
+ end
535
+
536
+ context 'unset' do
537
+ let(:input_command_options) { {} }
538
+
539
+ it "should return an error and not execute any actions" do
540
+ subject.execute
541
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A grammar is required.'
542
+ expect(original_command.response(0.1)).to eq(error)
543
+ end
544
+ end
545
+ end
546
+
547
+ describe 'Input#initial-timeout' do
548
+ context 'a positive number' do
549
+ let(:input_command_opts) { { initial_timeout: 1000 } }
550
+
551
+ it 'should pass the nit option to MRCPRecog' do
552
+ expect_mrcprecog_with_options(/nit=1000/)
553
+ subject.execute
554
+ end
555
+ end
556
+
557
+ context '-1' do
558
+ let(:input_command_opts) { { initial_timeout: -1 } }
559
+
560
+ it 'should not pass any options to MRCPRecog' do
561
+ expect_mrcprecog_with_options(//)
562
+ subject.execute
563
+ end
564
+ end
565
+
566
+ context 'unset' do
567
+ let(:input_command_opts) { { initial_timeout: nil } }
568
+
569
+ it 'should not pass any options to MRCPRecog' do
570
+ expect_mrcprecog_with_options(//)
571
+ subject.execute
572
+ end
573
+ end
574
+
575
+ context 'a negative number other than -1' do
576
+ let(:input_command_opts) { { initial_timeout: -1000 } }
577
+
578
+ it "should return an error and not execute any actions" do
579
+ subject.execute
580
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An initial-timeout value must be -1 or a positive integer.'
581
+ expect(original_command.response(0.1)).to eq(error)
582
+ end
583
+ end
584
+ end
585
+
586
+ describe 'Input#inter-digit-timeout' do
587
+ context 'a positive number' do
588
+ let(:input_command_opts) { { inter_digit_timeout: 1000 } }
589
+
590
+ it 'should pass the dit option to MRCPRecog' do
591
+ expect_mrcprecog_with_options(/dit=1000/)
592
+ subject.execute
593
+ end
594
+ end
595
+
596
+ context '-1' do
597
+ let(:input_command_opts) { { inter_digit_timeout: -1 } }
598
+
599
+ it 'should not pass any options to MRCPRecog' do
600
+ expect_mrcprecog_with_options(//)
601
+ subject.execute
602
+ end
603
+ end
604
+
605
+ context 'unset' do
606
+ let(:input_command_opts) { { inter_digit_timeout: nil } }
607
+
608
+ it 'should not pass any options to MRCPRecog' do
609
+ expect_mrcprecog_with_options(//)
610
+ subject.execute
611
+ end
612
+ end
613
+
614
+ context 'a negative number other than -1' do
615
+ let(:input_command_opts) { { inter_digit_timeout: -1000 } }
616
+
617
+ it "should return an error and not execute any actions" do
618
+ subject.execute
619
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An inter-digit-timeout value must be -1 or a positive integer.'
620
+ expect(original_command.response(0.1)).to eq(error)
621
+ end
622
+ end
623
+ end
624
+
625
+ describe 'Input#mode' do
626
+ skip
627
+ end
628
+
629
+ describe 'Input#terminator' do
630
+ context 'a string' do
631
+ let(:input_command_opts) { { terminator: '#' } }
632
+
633
+ it 'should pass the dttc option to SynthAndRecog' do
634
+ expect_mrcprecog_with_options(/dttc=#/)
635
+ subject.execute
636
+ end
637
+ end
638
+
639
+ context 'unset' do
640
+ let(:input_command_opts) { { terminator: nil } }
641
+
642
+ it 'should not pass any options to SynthAndRecog' do
643
+ expect_mrcprecog_with_options(//)
644
+ subject.execute
645
+ end
646
+ end
647
+ end
648
+
649
+ describe 'Input#recognizer' do
650
+ skip
651
+ end
652
+
653
+ describe 'Input#sensitivity' do
654
+ context 'a string' do
655
+ let(:input_command_opts) { { sensitivity: '0.2' } }
656
+
657
+ it 'should pass the sl option to MRCPRecog' do
658
+ expect_mrcprecog_with_options(/sl=0.2/)
659
+ subject.execute
660
+ end
661
+ end
662
+
663
+ context 'unset' do
664
+ let(:input_command_opts) { { sensitivity: nil } }
665
+
666
+ it 'should not pass any options to MRCPRecog' do
667
+ expect_mrcprecog_with_options(//)
668
+ subject.execute
669
+ end
670
+ end
671
+ end
672
+
673
+ describe 'Input#min-confidence' do
674
+ context 'a string' do
675
+ let(:input_command_opts) { { min_confidence: '0.5' } }
676
+
677
+ it 'should pass the ct option to MRCPRecog' do
678
+ expect_mrcprecog_with_options(/ct=0.5/)
679
+ subject.execute
680
+ end
681
+ end
682
+
683
+ context 'unset' do
684
+ let(:input_command_opts) { { min_confidence: nil } }
685
+
686
+ it 'should not pass any options to MRCPRecog' do
687
+ expect_mrcprecog_with_options(//)
688
+ subject.execute
689
+ end
690
+ end
691
+ end
692
+
693
+ describe 'Input#max-silence' do
694
+ skip
695
+ end
696
+
697
+ describe 'Input#match-content-type' do
698
+ skip
699
+ end
700
+
701
+ describe 'Input#language' do
702
+ context 'a string' do
703
+ let(:input_command_opts) { { language: 'en-GB' } }
704
+
705
+ it 'should pass the spl option to MRCPRecog' do
706
+ expect_mrcprecog_with_options(/spl=en-GB/)
707
+ subject.execute
708
+ end
709
+ end
710
+
711
+ context 'unset' do
712
+ let(:input_command_opts) { { language: nil } }
713
+
714
+ it 'should not pass any options to MRCPRecog' do
715
+ expect_mrcprecog_with_options(//)
716
+ subject.execute
717
+ end
718
+ end
719
+ end
720
+
721
+ describe "#execute_command" do
722
+ context "with a command it does not understand" do
723
+ let(:command) { Adhearsion::Rayo::Component::Output::Pause.new }
724
+
725
+ before { command.request! }
726
+ it "returns a Adhearsion::ProtocolError response" do
727
+ subject.execute_command command
728
+ expect(command.response(0.1)).to be_a Adhearsion::ProtocolError
729
+ end
730
+ end
731
+
732
+ context "with a Stop command" do
733
+ let(:command) { Adhearsion::Rayo::Component::Stop.new }
734
+ let(:reason) { original_command.complete_event(5).reason }
735
+ let(:channel) { "SIP/1234-00000000" }
736
+ let :ami_event do
737
+ RubyAMI::Event.new 'AsyncAGI',
738
+ 'SubEvent' => "Start",
739
+ 'Channel' => channel,
740
+ '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"
741
+ end
742
+
743
+ before do
744
+ command.request!
745
+ original_command.execute!
746
+ end
747
+
748
+ it "sets the command response to true" do
749
+ expect(mock_call).to receive(:redirect_back)
750
+ subject.execute_command command
751
+ expect(command.response(0.1)).to eq(true)
752
+ end
753
+
754
+ it "sends the correct complete event" do
755
+ expect(mock_call).to receive(:redirect_back)
756
+ subject.execute_command command
757
+ expect(original_command).not_to be_complete
758
+ mock_call.process_ami_event ami_event
759
+ expect(reason).to be_a Adhearsion::Event::Complete::Stop
760
+ expect(original_command).to be_complete
761
+ end
762
+
763
+ it "redirects the call by unjoining it" do
764
+ expect(mock_call).to receive(:redirect_back)
765
+ subject.execute_command command
766
+ end
767
+ end
768
+ end
769
+
770
+ end
771
+ end
772
+ end
773
+ end
774
+ end