adhearsion 2.6.4 → 3.0.0.beta1

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.
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