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