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,1244 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ module Translator
7
+ class Asterisk
8
+ module Component
9
+ describe MRCPPrompt 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 :output_command_options do
55
+ { render_document: {value: ssml_doc} }.merge(output_command_opts)
56
+ end
57
+
58
+ let(:input_command_opts) { {} }
59
+
60
+ let :input_command_options do
61
+ { grammar: {value: grammar} }.merge(input_command_opts)
62
+ end
63
+
64
+ let(:command_options) { {} }
65
+
66
+ let :output_command do
67
+ Adhearsion::Rayo::Component::Output.new output_command_options
68
+ end
69
+
70
+ let :input_command do
71
+ Adhearsion::Rayo::Component::Input.new input_command_options
72
+ end
73
+
74
+ let :original_command do
75
+ Adhearsion::Rayo::Component::Prompt.new output_command, input_command, command_options
76
+ end
77
+
78
+ let(:recog_status) { 'OK' }
79
+ let(:recog_completion_cause) { '000' }
80
+ 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" }
81
+
82
+ subject { described_class.new original_command, mock_call }
83
+
84
+ before do
85
+ original_command.request!
86
+ {
87
+ 'RECOG_STATUS' => recog_status,
88
+ 'RECOG_COMPLETION_CAUSE' => recog_completion_cause,
89
+ 'RECOG_RESULT' => recog_result
90
+ }.each do |var, val|
91
+ allow(mock_call).to receive(:channel_var).with(var).and_return val
92
+ end
93
+ end
94
+
95
+ context 'with an invalid recognizer' do
96
+ let(:input_command_opts) { { recognizer: 'foobar' } }
97
+
98
+ it "should return an error and not execute any actions" do
99
+ subject.execute
100
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'The recognizer foobar is unsupported.'
101
+ expect(original_command.response(0.1)).to eq(error)
102
+ end
103
+ end
104
+
105
+ [:asterisk].each do |recognizer|
106
+ context "with a recognizer #{recognizer.inspect}" do
107
+ let(:input_command_opts) { { recognizer: recognizer } }
108
+
109
+ it "should return an error and not execute any actions" do
110
+ subject.execute
111
+ error = Adhearsion::ProtocolError.new.setup 'option error', "The recognizer #{recognizer} is unsupported."
112
+ expect(original_command.response(0.1)).to eq(error)
113
+ end
114
+ end
115
+ end
116
+
117
+ def expect_mrcpsynth_with_options(options)
118
+ expect_app_with_options 'MRCPSynth', options
119
+ end
120
+
121
+ def expect_synthandrecog_with_options(options)
122
+ expect_app_with_options 'SynthAndRecog', options
123
+ end
124
+
125
+ def expect_app_with_options(app, options)
126
+ expect(mock_call).to receive(:execute_agi_command).once { |*args|
127
+ expect(args[0]).to eq("EXEC #{app}")
128
+ expect(args[1]).to match options
129
+ {code: 200, result: 1}
130
+ }
131
+ end
132
+
133
+ describe 'Output#document' do
134
+ context 'with multiple inline documents' do
135
+ let(:output_command_options) { { render_documents: [{value: ssml_doc}, {value: ssml_doc}] } }
136
+
137
+ it "should return an error and not execute any actions" do
138
+ subject.execute
139
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only one document is allowed.'
140
+ expect(original_command.response(0.1)).to eq(error)
141
+ end
142
+ end
143
+
144
+ context 'with multiple documents by URI' do
145
+ let(:output_command_options) { { render_documents: [{url: 'http://example.com/doc1.ssml'}, {url: 'http://example.com/doc2.ssml'}] } }
146
+
147
+ it "should return an error and not execute any actions" do
148
+ subject.execute
149
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only one document is allowed.'
150
+ expect(original_command.response(0.1)).to eq(error)
151
+ end
152
+ end
153
+
154
+ context 'unset' do
155
+ let(:output_command_options) { {} }
156
+
157
+ it "should return an error and not execute any actions" do
158
+ subject.execute
159
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An SSML document is required.'
160
+ expect(original_command.response(0.1)).to eq(error)
161
+ end
162
+ end
163
+ end
164
+
165
+ describe 'Output#renderer' do
166
+ [nil, :unimrcp].each do |renderer|
167
+ context renderer.to_s do
168
+ let(:output_command_opts) { { renderer: renderer } }
169
+
170
+ it "should return a ref and execute SynthAndRecog" do
171
+ param = [ssml_doc.to_doc, grammar.to_doc].map { |o| "\"#{o.to_s.squish.gsub('"', '\"')}\"" }.push('uer=1&b=1').join(',')
172
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC SynthAndRecog', param).and_return code: 200, result: 1
173
+ subject.execute
174
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
175
+ end
176
+
177
+ context "when SynthAndRecog completes" do
178
+ shared_context "with a match" do
179
+ let :expected_nlsml do
180
+ RubySpeech::NLSML.draw do
181
+ interpretation grammar: 'session:grammar-0', confidence: 0.43 do
182
+ input 'Hello', mode: :speech
183
+ instance 'Hello'
184
+ end
185
+ end
186
+ end
187
+
188
+ it 'should send a match complete event' do
189
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
190
+
191
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
192
+ subject.execute
193
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
194
+ end
195
+ end
196
+
197
+ context "with a match cause" do
198
+ %w{000 008 012}.each do |code|
199
+ context "when the MRCP recognition response code is #{code}" do
200
+ let(:recog_completion_cause) { code }
201
+
202
+ it_behaves_like 'with a match'
203
+ end
204
+ end
205
+ end
206
+
207
+ context "with a nomatch cause" do
208
+ %w{001 003 013 014 015}.each do |code|
209
+ context "with value #{code}" do
210
+ let(:recog_completion_cause) { code }
211
+
212
+ it 'should send a nomatch complete event' do
213
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
214
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
215
+ subject.execute
216
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ context "with a noinput cause" do
223
+ %w{002 011}.each do |code|
224
+ context "with value #{code}" do
225
+ let(:recog_completion_cause) { code }
226
+
227
+ specify do
228
+ expected_complete_reason = Adhearsion::Rayo::Component::Input::Complete::NoInput.new
229
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
230
+ subject.execute
231
+ expect(original_command.complete_event(0.1).reason).to eq(expected_complete_reason)
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ shared_context 'should send an error complete event' do
238
+ specify do
239
+ expect(mock_call).to receive(:execute_agi_command).and_return code: 200, result: 1
240
+ subject.execute
241
+ complete_reason = original_command.complete_event(0.1).reason
242
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Error
243
+ end
244
+ end
245
+
246
+ context 'with an error cause' do
247
+ %w{004 005 006 007 009 010 016}.each do |code|
248
+ context "when the MRCP recognition response code is #{code}" do
249
+ let(:recog_completion_cause) { code }
250
+ it_behaves_like 'should send an error complete event'
251
+ end
252
+ end
253
+ end
254
+
255
+ context 'with an invalid cause' do
256
+ let(:recog_completion_cause) { '999' }
257
+ it_behaves_like 'should send an error complete event'
258
+ end
259
+
260
+ context "when the RECOG_STATUS variable is set to 'ERROR'" do
261
+ let(:recog_status) { 'ERROR' }
262
+ it_behaves_like 'should send an error complete event'
263
+ end
264
+ end
265
+
266
+ context "when we get a RubyAMI Error" do
267
+ it "should send an error complete event" do
268
+ error = RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
269
+ expect(mock_call).to receive(:execute_agi_command).and_raise error
270
+ subject.execute
271
+ complete_reason = original_command.complete_event(0.1).reason
272
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Error
273
+ expect(complete_reason.details).to eq("Terminated due to AMI error 'FooBar'")
274
+ end
275
+ end
276
+
277
+ context "when the channel is gone" do
278
+ it "should send an error complete event" do
279
+ error = ChannelGoneError.new 'FooBar'
280
+ expect(mock_call).to receive(:execute_agi_command).and_raise error
281
+ subject.execute
282
+ complete_reason = original_command.complete_event(0.1).reason
283
+ expect(complete_reason).to be_a Adhearsion::Event::Complete::Hangup
284
+ end
285
+ end
286
+ end
287
+ end
288
+
289
+ [:foobar, :swift, :asterisk].each do |renderer|
290
+ context renderer.to_s do
291
+ let(:output_command_opts) { { renderer: renderer } }
292
+
293
+ it "should return an error and not execute any actions" do
294
+ subject.execute
295
+ error = Adhearsion::ProtocolError.new.setup 'option error', "The renderer #{renderer} is unsupported."
296
+ expect(original_command.response(0.1)).to eq(error)
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ describe 'barge_in' do
303
+ context 'unset' do
304
+ let(:command_options) { { barge_in: nil } }
305
+
306
+ it 'should pass the b=1 option to SynthAndRecog' do
307
+ expect_synthandrecog_with_options(/b=1/)
308
+ subject.execute
309
+ end
310
+ end
311
+
312
+ context 'true' do
313
+ let(:command_options) { { barge_in: true } }
314
+
315
+ it 'should pass the b=1 option to SynthAndRecog' do
316
+ expect_synthandrecog_with_options(/b=1/)
317
+ subject.execute
318
+ end
319
+ end
320
+
321
+ context 'false' do
322
+ let(:command_options) { { barge_in: false } }
323
+
324
+ it 'should pass the b=0 option to SynthAndRecog' do
325
+ expect_synthandrecog_with_options(/b=0/)
326
+ subject.execute
327
+ end
328
+ end
329
+ end
330
+
331
+ describe 'Output#voice' do
332
+ context 'unset' do
333
+ let(:output_command_opts) { { voice: nil } }
334
+
335
+ it 'should not pass the vn option to SynthAndRecog' do
336
+ expect_synthandrecog_with_options(//)
337
+ subject.execute
338
+ end
339
+ end
340
+
341
+ context 'set' do
342
+ let(:output_command_opts) { { voice: 'alison' } }
343
+
344
+ it 'should pass the vn option to SynthAndRecog' do
345
+ expect_synthandrecog_with_options(/vn=alison/)
346
+ subject.execute
347
+ end
348
+ end
349
+ end
350
+
351
+ describe 'Output#start-offset' do
352
+ context 'unset' do
353
+ let(:output_command_opts) { { start_offset: nil } }
354
+ it 'should not pass any options to SynthAndRecog' do
355
+ expect_synthandrecog_with_options(//)
356
+ subject.execute
357
+ end
358
+ end
359
+
360
+ context 'set' do
361
+ let(:output_command_opts) { { start_offset: 10 } }
362
+ it "should return an error and not execute any actions" do
363
+ subject.execute
364
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A start_offset value is unsupported on Asterisk.'
365
+ expect(original_command.response(0.1)).to eq(error)
366
+ end
367
+ end
368
+ end
369
+
370
+ describe 'Output#start-paused' do
371
+ context 'false' do
372
+ let(:output_command_opts) { { start_paused: false } }
373
+ it 'should not pass any options to SynthAndRecog' do
374
+ expect_synthandrecog_with_options(//)
375
+ subject.execute
376
+ end
377
+ end
378
+
379
+ context 'true' do
380
+ let(:output_command_opts) { { start_paused: true } }
381
+ it "should return an error and not execute any actions" do
382
+ subject.execute
383
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A start_paused value is unsupported on Asterisk.'
384
+ expect(original_command.response(0.1)).to eq(error)
385
+ end
386
+ end
387
+ end
388
+
389
+ describe 'Output#repeat-interval' do
390
+ context 'unset' do
391
+ let(:output_command_opts) { { repeat_interval: nil } }
392
+ it 'should not pass any options to SynthAndRecog' do
393
+ expect_synthandrecog_with_options(//)
394
+ subject.execute
395
+ end
396
+ end
397
+
398
+ context 'set' do
399
+ let(:output_command_opts) { { repeat_interval: 10 } }
400
+ it "should return an error and not execute any actions" do
401
+ subject.execute
402
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported on Asterisk.'
403
+ expect(original_command.response(0.1)).to eq(error)
404
+ end
405
+ end
406
+ end
407
+
408
+ describe 'Output#repeat-times' do
409
+ context 'unset' do
410
+ let(:output_command_opts) { { repeat_times: nil } }
411
+ it 'should not pass any options to SynthAndRecog' do
412
+ expect_synthandrecog_with_options(//)
413
+ subject.execute
414
+ end
415
+ end
416
+
417
+ context 'set' do
418
+ let(:output_command_opts) { { repeat_times: 2 } }
419
+ it "should return an error and not execute any actions" do
420
+ subject.execute
421
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported on Asterisk.'
422
+ expect(original_command.response(0.1)).to eq(error)
423
+ end
424
+ end
425
+ end
426
+
427
+ describe 'Output#max-time' do
428
+ context 'unset' do
429
+ let(:output_command_opts) { { max_time: nil } }
430
+ it 'should not pass any options to SynthAndRecog' do
431
+ expect_synthandrecog_with_options(//)
432
+ subject.execute
433
+ end
434
+ end
435
+
436
+ context 'set' do
437
+ let(:output_command_opts) { { max_time: 30 } }
438
+ it "should return an error and not execute any actions" do
439
+ subject.execute
440
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A max_time value is unsupported on Asterisk.'
441
+ expect(original_command.response(0.1)).to eq(error)
442
+ end
443
+ end
444
+ end
445
+
446
+ describe 'Output#interrupt_on' do
447
+ context 'unset' do
448
+ let(:output_command_opts) { { interrupt_on: nil } }
449
+ it 'should not pass any options to SynthAndRecog' do
450
+ expect_synthandrecog_with_options(//)
451
+ subject.execute
452
+ end
453
+ end
454
+
455
+ context 'set' do
456
+ let(:output_command_opts) { { interrupt_on: :dtmf } }
457
+ it "should return an error and not execute any actions" do
458
+ subject.execute
459
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A interrupt_on value is unsupported on Asterisk.'
460
+ expect(original_command.response(0.1)).to eq(error)
461
+ end
462
+ end
463
+ end
464
+
465
+ describe 'Output#grammar' do
466
+ context 'with multiple inline grammars' do
467
+ let(:input_command_options) { { grammars: [{value: voice_grammar}, {value: dtmf_grammar}] } }
468
+
469
+ it "should return a ref and execute SynthAndRecog" do
470
+ param = [ssml_doc.to_doc, [voice_grammar.to_doc.to_s, dtmf_grammar.to_doc.to_s].join(',')].map { |o| "\"#{o.to_s.squish.gsub('"', '\"')}\"" }.push('uer=1&b=1').join(',')
471
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC SynthAndRecog', param).and_return code: 200, result: 1
472
+ subject.execute
473
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
474
+ end
475
+ end
476
+
477
+ context 'with multiple grammars by URI' do
478
+ let(:input_command_options) { { grammars: [{url: 'http://example.com/grammar1.grxml'}, {url: 'http://example.com/grammar2.grxml'}] } }
479
+
480
+ it "should return a ref and execute SynthAndRecog" do
481
+ param = [ssml_doc.to_doc, ['http://example.com/grammar1.grxml', 'http://example.com/grammar2.grxml'].join(',')].map { |o| "\"#{o.to_s.squish.gsub('"', '\"')}\"" }.push('uer=1&b=1').join(',')
482
+ expect(mock_call).to receive(:execute_agi_command).once.with('EXEC SynthAndRecog', param).and_return code: 200, result: 1
483
+ subject.execute
484
+ expect(original_command.response(0.1)).to be_a Adhearsion::Rayo::Ref
485
+ end
486
+ end
487
+
488
+ context 'unset' do
489
+ let(:input_command_options) { {} }
490
+
491
+ it "should return an error and not execute any actions" do
492
+ subject.execute
493
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A grammar is required.'
494
+ expect(original_command.response(0.1)).to eq(error)
495
+ end
496
+ end
497
+ end
498
+
499
+ describe 'Input#initial-timeout' do
500
+ context 'a positive number' do
501
+ let(:input_command_opts) { { initial_timeout: 1000 } }
502
+
503
+ it 'should pass the nit option to SynthAndRecog' do
504
+ expect_synthandrecog_with_options(/nit=1000/)
505
+ subject.execute
506
+ end
507
+ end
508
+
509
+ context '-1' do
510
+ let(:input_command_opts) { { initial_timeout: -1 } }
511
+
512
+ it 'should not pass any options to SynthAndRecog' do
513
+ expect_synthandrecog_with_options(//)
514
+ subject.execute
515
+ end
516
+ end
517
+
518
+ context 'unset' do
519
+ let(:input_command_opts) { { initial_timeout: nil } }
520
+
521
+ it 'should not pass any options to SynthAndRecog' do
522
+ expect_synthandrecog_with_options(//)
523
+ subject.execute
524
+ end
525
+ end
526
+
527
+ context 'a negative number other than -1' do
528
+ let(:input_command_opts) { { initial_timeout: -1000 } }
529
+
530
+ it "should return an error and not execute any actions" do
531
+ subject.execute
532
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An initial-timeout value must be -1 or a positive integer.'
533
+ expect(original_command.response(0.1)).to eq(error)
534
+ end
535
+ end
536
+ end
537
+
538
+ describe 'Input#recognition-timeout' do
539
+ context 'a positive number' do
540
+ let(:input_command_opts) { { recognition_timeout: 1000 } }
541
+
542
+ it 'should pass the t option to SynthAndRecog' do
543
+ expect_synthandrecog_with_options(/t=1000/)
544
+ subject.execute
545
+ end
546
+ end
547
+
548
+ context '0' do
549
+ let(:input_command_opts) { { recognition_timeout: 0 } }
550
+
551
+ it 'should pass the t option to SynthAndRecog' do
552
+ expect_synthandrecog_with_options(/t=0/)
553
+ subject.execute
554
+ end
555
+ end
556
+
557
+ context 'a negative number' do
558
+ let(:input_command_opts) { { recognition_timeout: -1000 } }
559
+
560
+ it "should return an error and not execute any actions" do
561
+ subject.execute
562
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A recognition-timeout value must be -1, 0, or a positive integer.'
563
+ expect(original_command.response(0.1)).to eq(error)
564
+ end
565
+ end
566
+
567
+ context 'unset' do
568
+ let(:input_command_opts) { { recognition_timeout: nil } }
569
+
570
+ it 'should not pass any options to SynthAndRecog' do
571
+ expect_synthandrecog_with_options(//)
572
+ subject.execute
573
+ end
574
+ end
575
+ end
576
+
577
+ describe 'Input#inter-digit-timeout' do
578
+ context 'a positive number' do
579
+ let(:input_command_opts) { { inter_digit_timeout: 1000 } }
580
+
581
+ it 'should pass the dit option to SynthAndRecog' do
582
+ expect_synthandrecog_with_options(/dit=1000/)
583
+ subject.execute
584
+ end
585
+ end
586
+
587
+ context '-1' do
588
+ let(:input_command_opts) { { inter_digit_timeout: -1 } }
589
+
590
+ it 'should not pass any options to SynthAndRecog' do
591
+ expect_synthandrecog_with_options(//)
592
+ subject.execute
593
+ end
594
+ end
595
+
596
+ context 'unset' do
597
+ let(:input_command_opts) { { inter_digit_timeout: nil } }
598
+
599
+ it 'should not pass any options to SynthAndRecog' do
600
+ expect_synthandrecog_with_options(//)
601
+ subject.execute
602
+ end
603
+ end
604
+
605
+ context 'a negative number other than -1' do
606
+ let(:input_command_opts) { { inter_digit_timeout: -1000 } }
607
+
608
+ it "should return an error and not execute any actions" do
609
+ subject.execute
610
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An inter-digit-timeout value must be -1 or a positive integer.'
611
+ expect(original_command.response(0.1)).to eq(error)
612
+ end
613
+ end
614
+ end
615
+
616
+ describe 'Input#max-silence' do
617
+ context 'a positive number' do
618
+ let(:input_command_opts) { { max_silence: 1000 } }
619
+
620
+ it 'should pass the sint option to SynthAndRecog' do
621
+ expect_synthandrecog_with_options(/sint=1000/)
622
+ subject.execute
623
+ end
624
+ end
625
+
626
+ context '0' do
627
+ let(:input_command_opts) { { max_silence: 0 } }
628
+
629
+ it 'should pass the sint option to SynthAndRecog' do
630
+ expect_synthandrecog_with_options(/sint=0/)
631
+ subject.execute
632
+ end
633
+ end
634
+
635
+ context 'a negative number' do
636
+ let(:input_command_opts) { { max_silence: -1000 } }
637
+
638
+ it "should return an error and not execute any actions" do
639
+ subject.execute
640
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A max-silence value must be -1, 0, or a positive integer.'
641
+ expect(original_command.response(0.1)).to eq(error)
642
+ end
643
+ end
644
+
645
+ context 'unset' do
646
+ let(:input_command_opts) { { max_silence: nil } }
647
+
648
+ it 'should not pass any options to SynthAndRecog' do
649
+ expect_synthandrecog_with_options(//)
650
+ subject.execute
651
+ end
652
+ end
653
+ end
654
+
655
+ describe 'Input#speech-complete-timeout' do
656
+ context 'a positive number' do
657
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => 1000 } } }
658
+
659
+ it 'should pass the sct option to SynthAndRecog' do
660
+ expect_synthandrecog_with_options(/sct=1000/)
661
+ subject.execute
662
+ end
663
+ end
664
+
665
+ context '0' do
666
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => 0 } } }
667
+
668
+ it 'should pass the sct option to SynthAndRecog' do
669
+ expect_synthandrecog_with_options(/sct=0/)
670
+ subject.execute
671
+ end
672
+ end
673
+
674
+ context 'a negative number' do
675
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => -1000 } } }
676
+
677
+ it "should return an error and not execute any actions" do
678
+ subject.execute
679
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A speech-complete-timeout value must be -1, 0, or a positive integer.'
680
+ expect(original_command.response(0.1)).to eq(error)
681
+ end
682
+ end
683
+
684
+ context 'unset' do
685
+ let(:input_command_opts) { { headers: {"Speech-Complete-Timeout" => nil } } }
686
+
687
+ it 'should not pass any options to SynthAndRecog' do
688
+ expect_synthandrecog_with_options(//)
689
+ subject.execute
690
+ end
691
+ end
692
+ end
693
+
694
+ describe 'Input#headers(Speed-Vs-Accuracy)' do
695
+ context 'a positive number' do
696
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => 1 } } }
697
+
698
+ it 'should pass the sva option to SynthAndRecog' do
699
+ expect_synthandrecog_with_options(/sva=1/)
700
+ subject.execute
701
+ end
702
+ end
703
+
704
+ context '0' do
705
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => 0 } } }
706
+
707
+ it 'should pass the sva option to SynthAndRecog' do
708
+ expect_synthandrecog_with_options(/sva=0/)
709
+ subject.execute
710
+ end
711
+ end
712
+
713
+ context 'a negative number' do
714
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => -1000 } } }
715
+
716
+ it "should return an error and not execute any actions" do
717
+ subject.execute
718
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A speed-vs-accuracy value must be a positive integer.'
719
+ expect(original_command.response(0.1)).to eq(error)
720
+ end
721
+ end
722
+
723
+ context 'unset' do
724
+ let(:input_command_opts) { { headers: {"Speed-Vs-Accuracy" => nil } } }
725
+
726
+ it 'should not pass any options to SynthAndRecog' do
727
+ expect_synthandrecog_with_options(//)
728
+ subject.execute
729
+ end
730
+ end
731
+ end
732
+
733
+ describe 'Input#headers(N-Best-List-Length)' do
734
+ context 'a positive number' do
735
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => 5 } } }
736
+
737
+ it 'should pass the nb option to SynthAndRecog' do
738
+ expect_synthandrecog_with_options(/nb=5/)
739
+ subject.execute
740
+ end
741
+ end
742
+
743
+ context '0' do
744
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => 0 } } }
745
+
746
+ it "should return an error and not execute any actions" do
747
+ subject.execute
748
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An n-best-list-length value must be a positive integer.'
749
+ expect(original_command.response(0.1)).to eq(error)
750
+ end
751
+ end
752
+
753
+ context 'a negative number' do
754
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => -1000 } } }
755
+
756
+ it "should return an error and not execute any actions" do
757
+ subject.execute
758
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An n-best-list-length value must be a positive integer.'
759
+ expect(original_command.response(0.1)).to eq(error)
760
+ end
761
+ end
762
+
763
+ context 'unset' do
764
+ let(:input_command_opts) { { headers: {"N-Best-List-Length" => nil } } }
765
+
766
+ it 'should not pass any options to SynthAndRecog' do
767
+ expect_synthandrecog_with_options(//)
768
+ subject.execute
769
+ end
770
+ end
771
+ end
772
+
773
+ describe 'Input#headers(Start-Input-Timers)' do
774
+ context 'true' do
775
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => true } } }
776
+
777
+ it 'should pass the sit option to SynthAndRecog' do
778
+ expect_synthandrecog_with_options(/sit=true/)
779
+ subject.execute
780
+ end
781
+ end
782
+
783
+ context 'false' do
784
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => false } } }
785
+
786
+ it 'should pass the sit option to SynthAndRecog' do
787
+ expect_synthandrecog_with_options(/sit=false/)
788
+ subject.execute
789
+ end
790
+ end
791
+
792
+ context 'unset' do
793
+ let(:input_command_opts) { { headers: {"Start-Input-Timers" => nil } } }
794
+
795
+ it 'should not pass any options to SynthAndRecog' do
796
+ expect_synthandrecog_with_options(//)
797
+ subject.execute
798
+ end
799
+ end
800
+ end
801
+
802
+ describe 'Input#headers(DTMF-Terminate-Timeout)' do
803
+ context 'a positive number' do
804
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => 1000 } } }
805
+
806
+ it 'should pass the dtt option to SynthAndRecog' do
807
+ expect_synthandrecog_with_options(/dtt=1000/)
808
+ subject.execute
809
+ end
810
+ end
811
+
812
+ context '0' do
813
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => 0 } } }
814
+
815
+ it 'should pass the dtt option to SynthAndRecog' do
816
+ expect_synthandrecog_with_options(/dtt=0/)
817
+ subject.execute
818
+ end
819
+ end
820
+
821
+ context 'a negative number' do
822
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => -1000 } } }
823
+
824
+ it "should return an error and not execute any actions" do
825
+ subject.execute
826
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A dtmf-terminate-timeout value must be -1, 0, or a positive integer.'
827
+ expect(original_command.response(0.1)).to eq(error)
828
+ end
829
+ end
830
+
831
+ context 'unset' do
832
+ let(:input_command_opts) { { headers: {"DTMF-Terminate-Timeout" => nil } } }
833
+
834
+ it 'should not pass any options to SynthAndRecog' do
835
+ expect_synthandrecog_with_options(//)
836
+ subject.execute
837
+ end
838
+ end
839
+ end
840
+
841
+ describe 'Input#headers(Save-Waveform)' do
842
+ context 'true' do
843
+ let(:input_command_opts) { { headers: {"Save-Waveform" => true } } }
844
+
845
+ it 'should pass the sw option to SynthAndRecog' do
846
+ expect_synthandrecog_with_options(/sw=true/)
847
+ subject.execute
848
+ end
849
+ end
850
+
851
+ context 'false' do
852
+ let(:input_command_opts) { { headers: {"Save-Waveform" => false } } }
853
+
854
+ it 'should pass the sw option to SynthAndRecog' do
855
+ expect_synthandrecog_with_options(/sw=false/)
856
+ subject.execute
857
+ end
858
+ end
859
+
860
+ context 'unset' do
861
+ let(:input_command_opts) { { headers: {"Save-Waveform" => nil } } }
862
+
863
+ it 'should not pass any options to SynthAndRecog' do
864
+ expect_synthandrecog_with_options(//)
865
+ subject.execute
866
+ end
867
+ end
868
+ end
869
+
870
+ describe 'Input#headers(New-Audio-Channel)' do
871
+ context 'true' do
872
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => true } } }
873
+
874
+ it 'should pass the nac option to SynthAndRecog' do
875
+ expect_synthandrecog_with_options(/nac=true/)
876
+ subject.execute
877
+ end
878
+ end
879
+
880
+ context 'false' do
881
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => false } } }
882
+
883
+ it 'should pass the nac option to SynthAndRecog' do
884
+ expect_synthandrecog_with_options(/nac=false/)
885
+ subject.execute
886
+ end
887
+ end
888
+
889
+ context 'unset' do
890
+ let(:input_command_opts) { { headers: {"New-Audio-Channel" => nil } } }
891
+
892
+ it 'should not pass any options to SynthAndRecog' do
893
+ expect_synthandrecog_with_options(//)
894
+ subject.execute
895
+ end
896
+ end
897
+ end
898
+
899
+ describe 'Input#headers(Recognition-Mode)' do
900
+ context 'a string' do
901
+ let(:input_command_opts) { { headers: {"Recognition-Mode" => "hotword" } } }
902
+
903
+ it 'should pass the rm option to SynthAndRecog' do
904
+ expect_synthandrecog_with_options(/rm=hotword/)
905
+ subject.execute
906
+ end
907
+ end
908
+
909
+ context 'unset' do
910
+ let(:input_command_opts) { { headers: {"Recognition-Mode" => nil } } }
911
+
912
+ it 'should not pass any options to SynthAndRecog' do
913
+ expect_synthandrecog_with_options(//)
914
+ subject.execute
915
+ end
916
+ end
917
+ end
918
+
919
+ describe 'Input#headers(Hotword-Max-Duration)' do
920
+ context 'a positive number' do
921
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => 1000 } } }
922
+
923
+ it 'should pass the hmaxd option to SynthAndRecog' do
924
+ expect_synthandrecog_with_options(/hmaxd=1000/)
925
+ subject.execute
926
+ end
927
+ end
928
+
929
+ context '0' do
930
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => 0 } } }
931
+
932
+ it 'should pass the hmaxd option to SynthAndRecog' do
933
+ expect_synthandrecog_with_options(/hmaxd=0/)
934
+ subject.execute
935
+ end
936
+ end
937
+
938
+ context 'a negative number' do
939
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => -1000 } } }
940
+
941
+ it "should return an error and not execute any actions" do
942
+ subject.execute
943
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A hotword-max-duration value must be -1, 0, or a positive integer.'
944
+ expect(original_command.response(0.1)).to eq(error)
945
+ end
946
+ end
947
+
948
+ context 'unset' do
949
+ let(:input_command_opts) { { headers: {"Hotword-Max-Duration" => nil } } }
950
+
951
+ it 'should not pass any options to SynthAndRecog' do
952
+ expect_synthandrecog_with_options(//)
953
+ subject.execute
954
+ end
955
+ end
956
+ end
957
+
958
+ describe 'Input#headers(Hotword-Min-Duration)' do
959
+ context 'a positive number' do
960
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => 1000 } } }
961
+
962
+ it 'should pass the hmind option to SynthAndRecog' do
963
+ expect_synthandrecog_with_options(/hmind=1000/)
964
+ subject.execute
965
+ end
966
+ end
967
+
968
+ context '0' do
969
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => 0 } } }
970
+
971
+ it 'should pass the hmind option to SynthAndRecog' do
972
+ expect_synthandrecog_with_options(/hmind=0/)
973
+ subject.execute
974
+ end
975
+ end
976
+
977
+ context 'a negative number' do
978
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => -1000 } } }
979
+
980
+ it "should return an error and not execute any actions" do
981
+ subject.execute
982
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A hotword-min-duration value must be -1, 0, or a positive integer.'
983
+ expect(original_command.response(0.1)).to eq(error)
984
+ end
985
+ end
986
+
987
+ context 'unset' do
988
+ let(:input_command_opts) { { headers: {"Hotword-Min-Duration" => nil } } }
989
+
990
+ it 'should not pass any options to SynthAndRecog' do
991
+ expect_synthandrecog_with_options(//)
992
+ subject.execute
993
+ end
994
+ end
995
+ end
996
+
997
+ describe 'Input#headers(Clear-DTMF-Buffer)' do
998
+ context 'true' do
999
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => true } } }
1000
+
1001
+ it 'should pass the cdb option to SynthAndRecog' do
1002
+ expect_synthandrecog_with_options(/cdb=true/)
1003
+ subject.execute
1004
+ end
1005
+ end
1006
+
1007
+ context 'false' do
1008
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => false } } }
1009
+
1010
+ it 'should pass the cdb option to SynthAndRecog' do
1011
+ expect_synthandrecog_with_options(/cdb=false/)
1012
+ subject.execute
1013
+ end
1014
+ end
1015
+
1016
+ context 'unset' do
1017
+ let(:input_command_opts) { { headers: {"Clear-DTMF-Buffer" => nil } } }
1018
+
1019
+ it 'should not pass any options to SynthAndRecog' do
1020
+ expect_synthandrecog_with_options(//)
1021
+ subject.execute
1022
+ end
1023
+ end
1024
+ end
1025
+
1026
+ describe 'Input#headers(Early-No-Match)' do
1027
+ context 'true' do
1028
+ let(:input_command_opts) { { headers: {"Early-No-Match" => true } } }
1029
+
1030
+ it 'should pass the enm option to SynthAndRecog' do
1031
+ expect_synthandrecog_with_options(/enm=true/)
1032
+ subject.execute
1033
+ end
1034
+ end
1035
+
1036
+ context 'a negative number' do
1037
+ let(:input_command_opts) { { headers: {"Early-No-Match" => false } } }
1038
+
1039
+ it "should return an error and not execute any actions" do
1040
+ expect_synthandrecog_with_options(/enm=false/)
1041
+ subject.execute
1042
+ end
1043
+ end
1044
+
1045
+ context 'unset' do
1046
+ let(:input_command_opts) { { headers: {"Early-No-Match" => nil } } }
1047
+
1048
+ it 'should not pass any options to SynthAndRecog' do
1049
+ expect_synthandrecog_with_options(//)
1050
+ subject.execute
1051
+ end
1052
+ end
1053
+ end
1054
+
1055
+ describe 'Input#headers(Input-Waveform-URI)' do
1056
+ context 'a positive number' do
1057
+ let(:input_command_opts) { { headers: {"Input-Waveform-URI" => 'http://wave.form.com' } } }
1058
+
1059
+ it 'should pass the iwu option to SynthAndRecog' do
1060
+ expect_synthandrecog_with_options(/iwu=http:\/\/wave\.form\.com/)
1061
+ subject.execute
1062
+ end
1063
+ end
1064
+
1065
+ context 'unset' do
1066
+ let(:input_command_opts) { { headers: {"Input-Waveform-URI" => nil } } }
1067
+
1068
+ it 'should not pass any options to SynthAndRecog' do
1069
+ expect_synthandrecog_with_options(//)
1070
+ subject.execute
1071
+ end
1072
+ end
1073
+ end
1074
+
1075
+ describe 'Input#headers(Media-Type)' do
1076
+ context 'a string' do
1077
+ let(:input_command_opts) { { headers: {"Media-Type" => "foo/type" } } }
1078
+
1079
+ it 'should pass the mt option to SynthAndRecog' do
1080
+ expect_synthandrecog_with_options(/mt=foo\/type/)
1081
+ subject.execute
1082
+ end
1083
+ end
1084
+
1085
+ context 'unset' do
1086
+ let(:input_command_opts) { { headers: {"Media-Type" => nil } } }
1087
+
1088
+ it 'should not pass any options to SynthAndRecog' do
1089
+ expect_synthandrecog_with_options(//)
1090
+ subject.execute
1091
+ end
1092
+ end
1093
+ end
1094
+
1095
+ describe 'Input#mode' do
1096
+ skip
1097
+ end
1098
+
1099
+ describe 'Input#terminator' do
1100
+ context 'a string' do
1101
+ let(:input_command_opts) { { terminator: '#' } }
1102
+
1103
+ it 'should pass the dttc option to SynthAndRecog' do
1104
+ expect_synthandrecog_with_options(/dttc=#/)
1105
+ subject.execute
1106
+ end
1107
+ end
1108
+
1109
+ context 'unset' do
1110
+ let(:input_command_opts) { { terminator: nil } }
1111
+
1112
+ it 'should not pass any options to SynthAndRecog' do
1113
+ expect_synthandrecog_with_options(//)
1114
+ subject.execute
1115
+ end
1116
+ end
1117
+ end
1118
+
1119
+ describe 'Input#recognizer' do
1120
+ skip
1121
+ end
1122
+
1123
+ describe 'Input#sensitivity' do
1124
+ context 'a string' do
1125
+ let(:input_command_opts) { { sensitivity: '0.2' } }
1126
+
1127
+ it 'should pass the sl option to SynthAndRecog' do
1128
+ expect_synthandrecog_with_options(/sl=0.2/)
1129
+ subject.execute
1130
+ end
1131
+ end
1132
+
1133
+ context 'unset' do
1134
+ let(:input_command_opts) { { sensitivity: nil } }
1135
+
1136
+ it 'should not pass any options to SynthAndRecog' do
1137
+ expect_synthandrecog_with_options(//)
1138
+ subject.execute
1139
+ end
1140
+ end
1141
+ end
1142
+
1143
+ describe 'Input#min-confidence' do
1144
+ context 'a string' do
1145
+ let(:input_command_opts) { { min_confidence: '0.5' } }
1146
+
1147
+ it 'should pass the ct option to SynthAndRecog' do
1148
+ expect_synthandrecog_with_options(/ct=0.5/)
1149
+ subject.execute
1150
+ end
1151
+ end
1152
+
1153
+ context 'unset' do
1154
+ let(:input_command_opts) { { min_confidence: nil } }
1155
+
1156
+ it 'should not pass any options to SynthAndRecog' do
1157
+ expect_synthandrecog_with_options(//)
1158
+ subject.execute
1159
+ end
1160
+ end
1161
+ end
1162
+
1163
+ describe 'Input#max-silence' do
1164
+ skip
1165
+ end
1166
+
1167
+ describe 'Input#match-content-type' do
1168
+ skip
1169
+ end
1170
+
1171
+ describe 'Input#language' do
1172
+ context 'a string' do
1173
+ let(:input_command_opts) { { language: 'en-GB' } }
1174
+
1175
+ it 'should pass the spl option to SynthAndRecog' do
1176
+ expect_synthandrecog_with_options(/spl=en-GB/)
1177
+ subject.execute
1178
+ end
1179
+ end
1180
+
1181
+ context 'unset' do
1182
+ let(:input_command_opts) { { language: nil } }
1183
+
1184
+ it 'should not pass any options to SynthAndRecog' do
1185
+ expect_synthandrecog_with_options(//)
1186
+ subject.execute
1187
+ end
1188
+ end
1189
+ end
1190
+
1191
+ describe "#execute_command" do
1192
+ context "with a command it does not understand" do
1193
+ let(:command) { Adhearsion::Rayo::Component::Output::Pause.new }
1194
+
1195
+ before { command.request! }
1196
+ it "returns a Adhearsion::ProtocolError response" do
1197
+ subject.execute_command command
1198
+ expect(command.response(0.1)).to be_a Adhearsion::ProtocolError
1199
+ end
1200
+ end
1201
+
1202
+ context "with a Stop command" do
1203
+ let(:command) { Adhearsion::Rayo::Component::Stop.new }
1204
+ let(:reason) { original_command.complete_event(5).reason }
1205
+ let(:channel) { "SIP/1234-00000000" }
1206
+ let :ami_event do
1207
+ RubyAMI::Event.new 'AsyncAGI',
1208
+ 'SubEvent' => "Start",
1209
+ 'Channel' => channel,
1210
+ '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"
1211
+ end
1212
+
1213
+ before do
1214
+ command.request!
1215
+ original_command.execute!
1216
+ end
1217
+
1218
+ it "sets the command response to true" do
1219
+ expect(mock_call).to receive(:redirect_back)
1220
+ subject.execute_command command
1221
+ expect(command.response(0.1)).to eq(true)
1222
+ end
1223
+
1224
+ it "sends the correct complete event" do
1225
+ expect(mock_call).to receive(:redirect_back)
1226
+ subject.execute_command command
1227
+ expect(original_command).not_to be_complete
1228
+ mock_call.process_ami_event ami_event
1229
+ expect(reason).to be_a Adhearsion::Event::Complete::Stop
1230
+ expect(original_command).to be_complete
1231
+ end
1232
+
1233
+ it "redirects the call by unjoining it" do
1234
+ expect(mock_call).to receive(:redirect_back)
1235
+ subject.execute_command command
1236
+ end
1237
+ end
1238
+ end
1239
+
1240
+ end
1241
+ end
1242
+ end
1243
+ end
1244
+ end