adhearsion 1.2.6 → 2.0.0.alpha1

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 (236) hide show
  1. data/.gitignore +17 -10
  2. data/CHANGELOG.md +273 -0
  3. data/Gemfile +1 -1
  4. data/Guardfile +17 -0
  5. data/README.markdown +61 -9
  6. data/Rakefile +16 -48
  7. data/adhearsion.gemspec +21 -7
  8. data/bin/ahn +3 -1
  9. data/cucumber.yml +4 -0
  10. data/features/app_generator.feature +42 -0
  11. data/features/cli.feature +108 -0
  12. data/features/step_definitions/app_generator_steps.rb +6 -0
  13. data/features/step_definitions/cli_steps.rb +74 -0
  14. data/features/support/aruba_helper.rb +22 -0
  15. data/features/support/env.rb +37 -0
  16. data/features/support/utils.rb +8 -0
  17. data/lib/adhearsion.rb +85 -41
  18. data/lib/adhearsion/call.rb +176 -0
  19. data/lib/adhearsion/call_controller.rb +134 -0
  20. data/lib/adhearsion/call_controller/dial.rb +70 -0
  21. data/lib/adhearsion/call_controller/input.rb +173 -0
  22. data/lib/adhearsion/call_controller/menu.rb +124 -0
  23. data/lib/adhearsion/call_controller/output.rb +267 -0
  24. data/lib/adhearsion/call_controller/record.rb +42 -0
  25. data/lib/adhearsion/call_controller/utility.rb +60 -0
  26. data/lib/adhearsion/calls.rb +81 -0
  27. data/lib/adhearsion/cli.rb +1 -3
  28. data/lib/adhearsion/cli_commands.rb +142 -0
  29. data/lib/adhearsion/configuration.rb +149 -0
  30. data/lib/adhearsion/console.rb +19 -8
  31. data/lib/adhearsion/dialplan_controller.rb +9 -0
  32. data/lib/adhearsion/events.rb +84 -0
  33. data/lib/adhearsion/foundation/all.rb +0 -7
  34. data/lib/adhearsion/foundation/custom_daemonizer.rb +4 -6
  35. data/lib/adhearsion/foundation/exception_handler.rb +9 -0
  36. data/lib/adhearsion/foundation/object.rb +26 -8
  37. data/lib/adhearsion/foundation/synchronized_hash.rb +3 -6
  38. data/lib/adhearsion/foundation/thread_safety.rb +17 -1
  39. data/lib/adhearsion/generators/app/app_generator.rb +4 -13
  40. data/lib/adhearsion/generators/app/templates/Gemfile +10 -5
  41. data/lib/adhearsion/generators/app/templates/Procfile +1 -0
  42. data/lib/adhearsion/generators/app/templates/README.md +28 -0
  43. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +41 -0
  44. data/lib/adhearsion/generators/app/templates/{components/simon_game → lib}/simon_game.rb +6 -18
  45. data/lib/adhearsion/generators/app/templates/script/ahn +2 -2
  46. data/lib/adhearsion/initializer.rb +151 -293
  47. data/lib/adhearsion/initializer/logging.rb +33 -0
  48. data/lib/adhearsion/logging.rb +65 -69
  49. data/lib/adhearsion/menu_dsl.rb +15 -0
  50. data/lib/adhearsion/menu_dsl/calculated_match.rb +39 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +41 -0
  52. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +18 -0
  53. data/lib/adhearsion/menu_dsl/match_calculator.rb +36 -0
  54. data/lib/adhearsion/{voip/menu_state_machine/menu_class.rb → menu_dsl/menu.rb} +38 -40
  55. data/lib/adhearsion/menu_dsl/menu_builder.rb +69 -0
  56. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +55 -0
  57. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +21 -0
  58. data/lib/adhearsion/outbound_call.rb +64 -0
  59. data/lib/adhearsion/plugin.rb +319 -0
  60. data/lib/adhearsion/plugin/collection.rb +19 -0
  61. data/lib/adhearsion/plugin/initializer.rb +37 -0
  62. data/lib/adhearsion/plugin/methods_container.rb +6 -0
  63. data/lib/adhearsion/process.rb +94 -0
  64. data/lib/adhearsion/punchblock_plugin.rb +29 -0
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +137 -0
  66. data/lib/adhearsion/router.rb +30 -0
  67. data/lib/adhearsion/router/route.rb +42 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -2
  69. data/lib/adhearsion/tasks.rb +14 -9
  70. data/lib/adhearsion/tasks/configuration.rb +26 -0
  71. data/lib/adhearsion/tasks/plugins.rb +17 -0
  72. data/lib/adhearsion/version.rb +8 -14
  73. data/spec/adhearsion/call_controller/dial_spec.rb +138 -0
  74. data/spec/adhearsion/call_controller/input_spec.rb +278 -0
  75. data/spec/adhearsion/call_controller/menu_spec.rb +120 -0
  76. data/spec/adhearsion/call_controller/output_spec.rb +466 -0
  77. data/spec/adhearsion/call_controller/record_spec.rb +125 -0
  78. data/spec/adhearsion/call_controller_spec.rb +395 -0
  79. data/spec/adhearsion/call_spec.rb +438 -0
  80. data/spec/adhearsion/calls_spec.rb +47 -0
  81. data/spec/adhearsion/configuration_spec.rb +308 -0
  82. data/spec/adhearsion/dialplan_controller_spec.rb +26 -0
  83. data/spec/adhearsion/events_spec.rb +112 -0
  84. data/spec/adhearsion/initializer/logging_spec.rb +58 -0
  85. data/spec/adhearsion/initializer_spec.rb +209 -122
  86. data/spec/adhearsion/logging_spec.rb +58 -47
  87. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +56 -0
  88. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +57 -0
  89. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +33 -0
  90. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +13 -0
  91. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +118 -0
  92. data/spec/adhearsion/menu_dsl/menu_spec.rb +210 -0
  93. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +28 -0
  94. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +36 -0
  95. data/spec/adhearsion/menu_dsl_spec.rb +12 -0
  96. data/spec/adhearsion/outbound_call_spec.rb +174 -0
  97. data/spec/adhearsion/plugin_spec.rb +489 -0
  98. data/spec/adhearsion/process_spec.rb +34 -0
  99. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +294 -0
  100. data/spec/adhearsion/router/route_spec.rb +99 -0
  101. data/spec/adhearsion/router_spec.rb +106 -0
  102. data/spec/adhearsion_spec.rb +46 -0
  103. data/spec/spec_helper.rb +14 -14
  104. data/spec/support/call_controller_test_helpers.rb +48 -0
  105. data/spec/support/initializer_stubs.rb +8 -13
  106. data/spec/support/punchblock_mocks.rb +6 -0
  107. metadata +255 -253
  108. data/CHANGELOG +0 -174
  109. data/bin/ahnctl +0 -68
  110. data/bin/jahn +0 -43
  111. data/examples/asterisk_manager_interface/standalone.rb +0 -51
  112. data/lib/adhearsion/commands.rb +0 -302
  113. data/lib/adhearsion/component_manager.rb +0 -278
  114. data/lib/adhearsion/component_manager/component_tester.rb +0 -54
  115. data/lib/adhearsion/component_manager/spec_framework.rb +0 -18
  116. data/lib/adhearsion/events_support.rb +0 -65
  117. data/lib/adhearsion/foundation/blank_slate.rb +0 -3
  118. data/lib/adhearsion/foundation/event_socket.rb +0 -205
  119. data/lib/adhearsion/foundation/future_resource.rb +0 -36
  120. data/lib/adhearsion/foundation/metaprogramming.rb +0 -17
  121. data/lib/adhearsion/foundation/numeric.rb +0 -13
  122. data/lib/adhearsion/foundation/pseudo_guid.rb +0 -10
  123. data/lib/adhearsion/foundation/relationship_properties.rb +0 -42
  124. data/lib/adhearsion/foundation/string.rb +0 -26
  125. data/lib/adhearsion/generators/app/templates/.ahnrc +0 -34
  126. data/lib/adhearsion/generators/app/templates/README +0 -8
  127. data/lib/adhearsion/generators/app/templates/components/ami_remote/ami_remote.rb +0 -15
  128. data/lib/adhearsion/generators/app/templates/components/disabled/HOW_TO_ENABLE +0 -7
  129. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/README.markdown +0 -47
  130. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.rb +0 -34
  131. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.yml +0 -12
  132. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/README.markdown +0 -3
  133. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +0 -11
  134. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  135. data/lib/adhearsion/generators/app/templates/config/startup.rb +0 -81
  136. data/lib/adhearsion/generators/app/templates/dialplan.rb +0 -3
  137. data/lib/adhearsion/generators/app/templates/events.rb +0 -33
  138. data/lib/adhearsion/host_definitions.rb +0 -67
  139. data/lib/adhearsion/initializer/asterisk.rb +0 -86
  140. data/lib/adhearsion/initializer/configuration.rb +0 -324
  141. data/lib/adhearsion/initializer/database.rb +0 -60
  142. data/lib/adhearsion/initializer/drb.rb +0 -31
  143. data/lib/adhearsion/initializer/freeswitch.rb +0 -22
  144. data/lib/adhearsion/initializer/ldap.rb +0 -57
  145. data/lib/adhearsion/initializer/rails.rb +0 -41
  146. data/lib/adhearsion/initializer/xmpp.rb +0 -42
  147. data/lib/adhearsion/tasks/components.rb +0 -32
  148. data/lib/adhearsion/tasks/database.rb +0 -5
  149. data/lib/adhearsion/tasks/deprecations.rb +0 -59
  150. data/lib/adhearsion/tasks/generating.rb +0 -20
  151. data/lib/adhearsion/tasks/lint.rb +0 -4
  152. data/lib/adhearsion/voip/asterisk.rb +0 -4
  153. data/lib/adhearsion/voip/asterisk/agi_server.rb +0 -121
  154. data/lib/adhearsion/voip/asterisk/commands.rb +0 -1966
  155. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +0 -140
  156. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +0 -102
  157. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +0 -250
  158. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +0 -240
  159. data/lib/adhearsion/voip/asterisk/config_manager.rb +0 -64
  160. data/lib/adhearsion/voip/asterisk/manager_interface.rb +0 -697
  161. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +0 -1681
  162. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +0 -341
  163. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +0 -78
  164. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +0 -87
  165. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +0 -80
  166. data/lib/adhearsion/voip/call.rb +0 -521
  167. data/lib/adhearsion/voip/call_routing.rb +0 -64
  168. data/lib/adhearsion/voip/commands.rb +0 -17
  169. data/lib/adhearsion/voip/constants.rb +0 -39
  170. data/lib/adhearsion/voip/conveniences.rb +0 -18
  171. data/lib/adhearsion/voip/dial_plan.rb +0 -252
  172. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +0 -151
  173. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +0 -37
  174. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +0 -27
  175. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +0 -124
  176. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +0 -69
  177. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +0 -16
  178. data/lib/adhearsion/voip/dsl/numerical_string.rb +0 -128
  179. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +0 -48
  180. data/lib/adhearsion/voip/freeswitch/event_handler.rb +0 -58
  181. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +0 -129
  182. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +0 -38
  183. data/lib/adhearsion/voip/freeswitch/oes_server.rb +0 -195
  184. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +0 -80
  185. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +0 -123
  186. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +0 -57
  187. data/lib/adhearsion/xmpp/connection.rb +0 -61
  188. data/lib/theatre.rb +0 -147
  189. data/lib/theatre/README.markdown +0 -64
  190. data/lib/theatre/callback_definition_loader.rb +0 -86
  191. data/lib/theatre/guid.rb +0 -23
  192. data/lib/theatre/invocation.rb +0 -131
  193. data/lib/theatre/namespace_manager.rb +0 -153
  194. data/lib/theatre/version.rb +0 -2
  195. data/spec/adhearsion/cli_spec.rb +0 -306
  196. data/spec/adhearsion/component_manager_spec.rb +0 -292
  197. data/spec/adhearsion/constants_spec.rb +0 -8
  198. data/spec/adhearsion/drb_spec.rb +0 -65
  199. data/spec/adhearsion/fixtures/dialplan.rb +0 -3
  200. data/spec/adhearsion/foundation/event_socket_spec.rb +0 -168
  201. data/spec/adhearsion/host_definitions_spec.rb +0 -79
  202. data/spec/adhearsion/initializer/configuration_spec.rb +0 -291
  203. data/spec/adhearsion/initializer/loading_spec.rb +0 -154
  204. data/spec/adhearsion/initializer/paths_spec.rb +0 -74
  205. data/spec/adhearsion/relationship_properties_spec.rb +0 -54
  206. data/spec/adhearsion/voip/asterisk/agi_server_spec.rb +0 -473
  207. data/spec/adhearsion/voip/asterisk/ami/ami_spec.rb +0 -550
  208. data/spec/adhearsion/voip/asterisk/ami/lexer/ami_fixtures.yml +0 -30
  209. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story +0 -291
  210. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story.rb +0 -241
  211. data/spec/adhearsion/voip/asterisk/ami/lexer/story_helper.rb +0 -124
  212. data/spec/adhearsion/voip/asterisk/commands_spec.rb +0 -3241
  213. data/spec/adhearsion/voip/asterisk/config_file_generators/agents_spec.rb +0 -251
  214. data/spec/adhearsion/voip/asterisk/config_file_generators/queues_spec.rb +0 -323
  215. data/spec/adhearsion/voip/asterisk/config_file_generators/voicemail_spec.rb +0 -306
  216. data/spec/adhearsion/voip/asterisk/config_manager_spec.rb +0 -127
  217. data/spec/adhearsion/voip/asterisk/menu_command/calculated_match_spec.rb +0 -109
  218. data/spec/adhearsion/voip/asterisk/menu_command/matchers_spec.rb +0 -97
  219. data/spec/adhearsion/voip/call_routing_spec.rb +0 -125
  220. data/spec/adhearsion/voip/dialplan_manager_spec.rb +0 -468
  221. data/spec/adhearsion/voip/dsl/dialing_dsl_spec.rb +0 -270
  222. data/spec/adhearsion/voip/dsl/dispatcher_spec.rb +0 -82
  223. data/spec/adhearsion/voip/dsl/dispatcher_spec_helper.rb +0 -45
  224. data/spec/adhearsion/voip/dsl/parser_spec.rb +0 -69
  225. data/spec/adhearsion/voip/freeswitch/basic_connection_manager_spec.rb +0 -39
  226. data/spec/adhearsion/voip/freeswitch/inbound_connection_manager_spec.rb +0 -39
  227. data/spec/adhearsion/voip/freeswitch/oes_server_spec.rb +0 -9
  228. data/spec/adhearsion/voip/numerical_string_spec.rb +0 -61
  229. data/spec/adhearsion/voip/phone_number_spec.rb +0 -45
  230. data/spec/support/the_following_code.rb +0 -3
  231. data/spec/theatre/dsl_examples/simple_before_call.rb +0 -7
  232. data/spec/theatre/dsl_spec.rb +0 -69
  233. data/spec/theatre/invocation_spec.rb +0 -182
  234. data/spec/theatre/namespace_spec.rb +0 -125
  235. data/spec/theatre/spec_helper_spec.rb +0 -28
  236. data/spec/theatre/theatre_class_spec.rb +0 -148
@@ -1,124 +0,0 @@
1
- require 'rubygems'
2
- gem 'rspec', "= 1.1.4"
3
- require 'spec/story'
4
- require File.dirname(__FILE__) + "/../../../../../lib/adhearsion"
5
-
6
- RAGEL_FILES = %w[lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb]
7
-
8
- def regenerate_ragel
9
- # Check Ragel version:
10
- ragel_version_match = `ragel --version`.match(/(\d)\.(\d)+/)
11
- abort "Could not get Ragel version! Is it installed? You must have at least version 6.3" unless ragel_version_match
12
- big, small = ragel_version_match.captures.map { |n| n.to_i }
13
- if big < 6 || (big == 6 && small < 3)
14
- abort "Please upgrade Ragel! You're on version #{ragel_version_match[0]} and must be on 6.3 or later"
15
- end
16
-
17
- RAGEL_FILES.each do |ragel_file|
18
- ruby_file = ragel_file.sub(".rl.rb", ".rb")
19
- puts `ragel -n -R #{ragel_file} -o #{ruby_file} 2>&1`
20
- raise "Failed generating code from Ragel file #{ragel_file}" if $?.to_i.nonzero?
21
- end
22
- end
23
-
24
- Dir.chdir(File.dirname(__FILE__) + "/../../../../..") { regenerate_ragel }
25
-
26
- require 'adhearsion/voip/asterisk/manager_interface/ami_lexer'
27
-
28
- FIXTURES = YAML.load_file File.dirname(__FILE__) + "/ami_fixtures.yml"
29
-
30
- def fixture(path, overrides={})
31
- path_segments = path.split '/'
32
- selected_event = path_segments.inject(FIXTURES.clone) do |hash, segment|
33
- raise ArgumentError, path + " not found!" unless hash
34
- hash[segment.to_sym]
35
- end
36
-
37
- # Downcase all keys in the event and the overrides
38
- selected_event = selected_event.inject({}) do |downcased_hash,(key,value)|
39
- downcased_hash[key.to_s.downcase] = value
40
- downcased_hash
41
- end
42
- overrides = overrides.inject({}) do |downcased_hash,(key,value)|
43
- downcased_hash[key.to_s.downcase] = value
44
- downcased_hash
45
- end
46
-
47
- # Replace variables in the selected_event with any overrides, ignoring case of the key
48
- keys_with_variables = selected_event.select { |(key, value)| value.kind_of?(Symbol) || value.kind_of?(Hash) }
49
-
50
- keys_with_variables.each do |original_key, variable_type|
51
- # Does an override an exist in the supplied list?
52
- if overriden_pair = overrides.find { |(key, value)| key == original_key }
53
- # We have an override! Let's replace the template value in the event with the overriden value
54
- selected_event[original_key] = overriden_pair.last
55
- else
56
- # Based on the type, let's generate a placeholder.
57
- selected_event[original_key] = case variable_type
58
- when :string
59
- rand(100000).to_s
60
- when Hash
61
- if variable_type.has_key? "one_of"
62
- # Choose a random possibility
63
- possibilities = variable_type['one_of']
64
- possibilities[rand(possibilities.size)]
65
- else
66
- raise "Unrecognized Hash fixture property! ##{variable_type.keys.to_sentence}"
67
- end
68
- else
69
- raise "Unrecognized fixture variable type #{variable_type}!"
70
- end
71
- end
72
-
73
- end
74
- hash_to_stanza(selected_event).tap do |event|
75
- selected_event.each_pair do |key, value|
76
- event.meta_def(key) { value }
77
- end
78
- end
79
- end
80
-
81
- def hash_to_stanza(hash)
82
- ordered_hash = hash.to_a
83
- starter = hash.find { |(key, value)| key.strip =~ /^(Response|Action)$/i }
84
- ordered_hash.unshift ordered_hash.delete(starter) if starter
85
- ordered_hash.inject(String.new) do |stanza,(key, value)|
86
- stanza + "#{key}: #{value}\r\n"
87
- end + "\r\n"
88
- end
89
-
90
- def format_newlines(string)
91
- # HOLY FUCK THIS IS UGLY
92
- tmp_replacement = random_string
93
- string.gsub("\r\n", tmp_replacement).
94
- gsub("\n", "\r\n").
95
- gsub(tmp_replacement, "\r\n")
96
- end
97
-
98
- def random_string
99
- (rand(1_000_000_000_000) + 1_000_000_000).to_s
100
- end
101
-
102
- def follows_body_text(name)
103
- case name
104
- when "ragel_description"
105
- "Ragel is a software development tool that allows user actions to
106
- be embedded into the transitions of a regular expression's corresponding state machine,
107
- eliminating the need to switch from the regular expression engine and user code execution
108
- environment and back again."
109
- when "with_colon_after_first_line"
110
- "Host Username Refresh State Reg.Time \r\nlax.teliax.net:5060 jicksta 105 Registered Tue, 11 Nov 2008 02:29:55"
111
- when "show_channels_from_wayne"
112
- "Channel Location State Application(Data)\r\n0 active channels\r\n0 active calls"
113
- when "empty_string"
114
- ""
115
- end
116
-
117
- end
118
-
119
- def syntax_error_data(name)
120
- case name
121
- when "immediate_packet_with_colon"
122
- "!IJ@MHY:!&@B*!B @ ! @^! @ !@ !\r!@ ! @ !@ ! !!m, \n\\n\n"
123
- end
124
- end
@@ -1,3241 +0,0 @@
1
- require 'spec_helper'
2
- require 'adhearsion/voip/menu_state_machine/menu_class'
3
- require 'adhearsion/voip/menu_state_machine/menu_builder'
4
-
5
- module DialplanCommandTestHelpers
6
- def self.included(test_case)
7
- test_case.send(:attr_reader, :mock_call, :input, :output)
8
-
9
- test_case.before do
10
- Adhearsion::Configuration.configure { |config| config.enable_asterisk() }
11
-
12
- @input = MockSocket.new
13
- @output = MockSocket.new
14
- @mock_call = Object.new
15
- @mock_call.metaclass.send(:attr_reader, :call)
16
- @mock_call.instance_variable_set(:@call, MockCall.new)
17
- mock_call.extend(Adhearsion::VoIP::Asterisk::Commands)
18
- flexmock(mock_call) do |call|
19
- call.should_receive(:from_pbx).and_return(input)
20
- call.should_receive(:to_pbx).and_return(output)
21
- end
22
- end
23
-
24
- test_case.after do
25
- pbx_output_should_be_empty
26
- end
27
- end
28
-
29
- class MockCall
30
- attr_accessor :variables
31
-
32
- def initialize
33
- @variables = {}
34
- end
35
-
36
- def with_command_lock
37
- yield
38
- end
39
- end
40
-
41
- class MockSocket
42
-
43
- def empty?
44
- messages.empty?
45
- end
46
-
47
- def print(message)
48
- messages << message
49
- end
50
-
51
- def puts(message)
52
- messages << message.chomp + "\n"
53
- end
54
-
55
- def read
56
- messages.shift
57
- end
58
-
59
- def gets
60
- read
61
- end
62
-
63
- def messages
64
- @messages ||= []
65
- end
66
- end
67
-
68
-
69
- private
70
-
71
- def should_pass_control_to_a_context_that_throws(symbol, &block)
72
- did_the_rescue_block_get_executed = false
73
- begin
74
- yield
75
- rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => cpe
76
- did_the_rescue_block_get_executed = true
77
- cpe.target.should throw_symbol symbol
78
- rescue => e
79
- did_the_rescue_block_get_executed = true
80
- raise e
81
- ensure
82
- did_the_rescue_block_get_executed.should be true
83
- end
84
- end
85
-
86
- def should_throw(sym=nil,&block)
87
- block.should throw_symbol(*[sym].compact)
88
- end
89
-
90
- def mock_route_calculation_with(*definitions)
91
- flexmock(Adhearsion::VoIP::DSL::DialingDSL).should_receive(:calculate_routes_for).and_return(definitions)
92
- end
93
-
94
- def pbx_should_have_been_sent(message)
95
- output.gets.chomp.should == message
96
- end
97
-
98
- def pbx_should_respond_with(message)
99
- input.print message
100
- end
101
-
102
- def pbx_should_respond_with_digits(string_of_digits)
103
- pbx_should_respond_with "200 result=#{string_of_digits}"
104
- end
105
-
106
- def pbx_should_respond_with_digits_and_timeout(string_of_digits)
107
- pbx_should_respond_with "200 result=#{string_of_digits} (timeout)"
108
- end
109
-
110
- def pbx_should_respond_to_timeout(timeout)
111
- pbx_should_respond_with "200 result=#{timeout}"
112
- end
113
-
114
- def pbx_should_respond_with_value(value)
115
- pbx_should_respond_with pbx_value_response value
116
- end
117
-
118
- def pbx_should_respond_with_success(success_code = nil)
119
- pbx_should_respond_with pbx_success_response(success_code)
120
- end
121
- alias does_not_read_data_back pbx_should_respond_with_success
122
-
123
- def pbx_should_respond_with_failure(failure_code = nil)
124
- pbx_should_respond_with(pbx_failure_response(failure_code))
125
- end
126
-
127
- def pbx_should_respond_with_successful_background_response(digit=0)
128
- pbx_should_respond_with_success digit.kind_of?(String) ? digit[0] : digit
129
- end
130
-
131
- def pbx_should_respond_with_playback_success
132
- pbx_should_respond_with pbx_raw_response
133
- mock_call.should_receive(:get_variable).once.with('PLAYBACKSTATUS').and_return 'SUCCESS'
134
- end
135
-
136
- def pbx_should_respond_with_playback_failure
137
- pbx_should_respond_with pbx_raw_response
138
- mock_call.should_receive(:get_variable).once.with('PLAYBACKSTATUS').and_return 'FAILED'
139
- end
140
-
141
- def pbx_should_respond_with_stream_file_success(success_code = nil, endpos = '20000')
142
- pbx_should_respond_with pbx_raw_stream_file_response(success_code, endpos)
143
- end
144
-
145
- def pbx_should_respond_with_stream_file_failure_on_open(endpos = nil)
146
- pbx_should_respond_with pbx_raw_stream_file_response(nil, endpos)
147
- end
148
-
149
- def pbx_should_respond_with_a_wait_for_digit_timeout
150
- pbx_should_respond_with_successful_background_response 0
151
- end
152
-
153
- def pbx_success_response(success_code = nil)
154
- "200 result=#{success_code || default_success_code}"
155
- end
156
-
157
- def pbx_raw_response(code = nil)
158
- "200 result=#{code || default_code}\n"
159
- end
160
-
161
- def pbx_raw_stream_file_response(code = nil, endpos = nil)
162
- "200 result=#{code || default_code} endpos=#{endpos || default_code}\n"
163
- end
164
-
165
- def pbx_value_response(value)
166
- "200 result=1 (#{value})"
167
- end
168
-
169
- def pbx_result_response(value)
170
- "200 result=#{value.ord}"
171
- end
172
-
173
- def default_success_code
174
- '1'
175
- end
176
-
177
- def default_code
178
- '0'
179
- end
180
-
181
- def pbx_failure_response(failure_code = nil)
182
- "200 result=#{failure_code || default_failure_code}"
183
- end
184
-
185
- def default_failure_code
186
- '-1'
187
- end
188
-
189
- def output_stream_matches(pattern)
190
- output.gets.should match pattern
191
- end
192
-
193
- module OutputStreamMatchers
194
- def pbx_was_asked_to_play(*audio_files)
195
- audio_files.flatten.each do |audio_file|
196
- output_stream_matches(/playback "#{audio_file}"/)
197
- end
198
- end
199
-
200
- def pbx_was_asked_to_play_number(number)
201
- output_stream_matches(/saynumber "#{number}"/)
202
- end
203
-
204
- def pbx_was_asked_to_play_time(number)
205
- output_stream_matches(/sayunixtime "#{number}"/)
206
- end
207
-
208
- def pbx_was_asked_to_stream(*audio_files)
209
- audio_files.flatten.each do |audio_file|
210
- output_stream_matches /^STREAM FILE "#{audio_file}" "1234567890\*#"\n$/
211
- end
212
- end
213
-
214
- def pbx_was_asked_to_execute(application, *options)
215
- output_stream_matches(/exec saydigits "#{options.join('|')}"/i)
216
- end
217
-
218
- def pbx_output_should_be_empty
219
- output.messages.should be_empty, output.messages.inspect
220
- end
221
- end
222
- include OutputStreamMatchers
223
-
224
- def assert_success(response)
225
- response.should == pbx_success_response
226
- end
227
-
228
- end
229
-
230
-
231
- module MenuBuilderTestHelper
232
- def builder_should_match_with_these_quantities_of_calculated_matches(checks)
233
- checks.each do |check,hash|
234
- hash.each_pair do |method_name,intended_quantity|
235
- builder.calculate_matches_for(check).send(method_name).should == intended_quantity
236
- end
237
- end
238
- end
239
- end
240
-
241
- module MenuTestHelper
242
-
243
- def pbx_should_send_digits(*digits)
244
- digits.each do |digit|
245
- digit = nil if digit == :timeout
246
- mock_call.should_receive(:interruptible_play).once.and_return(digit)
247
- end
248
- end
249
- end
250
-
251
- module ConfirmationManagerTestHelper
252
- def encode_hash(hash)
253
- Adhearsion::DialPlan::ConfirmationManager.encode_hash_for_dial_macro_argument(hash)
254
- end
255
- end
256
-
257
- describe 'Asterisk VoIP Commands' do
258
- include DialplanCommandTestHelpers
259
-
260
- it "a call can write back to the PBX" do
261
- message = 'oh hai'
262
- mock_call.write message
263
- pbx_should_have_been_sent message
264
- end
265
- end
266
- describe 'hangup command' do
267
- include DialplanCommandTestHelpers
268
-
269
- it "hanging up a call succesfully writes HANGUP back to the PBX and a success resopnse is returned" do
270
- pbx_should_respond_with_success
271
- response = mock_call.hangup
272
- pbx_should_have_been_sent 'HANGUP'
273
- response.should == pbx_success_response
274
- end
275
- end
276
-
277
- describe 'receiving a hangup' do
278
- include DialplanCommandTestHelpers
279
-
280
- it "should treat a ECONNRESET as a hangup" do
281
- pbx_should_respond_with_success
282
- def input.gets()
283
- raise Errno::ECONNRESET
284
- end
285
- the_following_code {
286
- mock_call.read()
287
- }.should raise_error(Adhearsion::Hangup)
288
- end
289
- end
290
-
291
- describe "writing a command" do
292
- include DialplanCommandTestHelpers
293
-
294
- it "should strip out excess whitespace" do
295
- pbx_should_respond_with_success
296
- mock_call.should_receive(:write).with "EXEC Ringing"
297
- mock_call.raw_response "EXEC \nRinging\n\n"
298
- end
299
- end
300
-
301
- describe 'The #interruptible_play method' do
302
-
303
- include DialplanCommandTestHelpers
304
-
305
- it 'should return a string for the digit that was pressed' do
306
- digits = %w{0 1 # * 9}.map{|c| c.ord}
307
- file = "file_doesnt_matter"
308
- digits.each { |digit| pbx_should_respond_with_stream_file_success digit }
309
- digits.map { |digit| mock_call.interruptible_play file }.should == digits.map(&:chr)
310
- digits.size.times { pbx_was_asked_to_stream file }
311
- end
312
-
313
- it "should return nil if no digit was pressed" do
314
- pbx_should_respond_with_stream_file_success 0
315
- file = 'foobar'
316
- mock_call.interruptible_play(file).should be nil
317
- pbx_was_asked_to_stream file
318
- end
319
-
320
- it 'should return nil if no digit was pressed, even if the sound file is not found' do
321
- pbx_should_respond_with_stream_file_failure_on_open
322
- file = 'foobar'
323
- mock_call.interruptible_play(file).should be nil
324
- pbx_was_asked_to_stream file
325
- end
326
-
327
- it "should play a series of files, stopping the series when a digit is played" do
328
- stubbed_keypad_input = [0, 0, ?3.ord]
329
- stubbed_keypad_input.each do |digit|
330
- pbx_should_respond_with_stream_file_success digit
331
- end
332
-
333
- play_files = (100..105).map &:to_s
334
- played_files = (100..102).map &:to_s
335
- mock_call.interruptible_play(*play_files).should == '3'
336
- pbx_was_asked_to_stream played_files
337
- end
338
-
339
- it 'should play a series of files, stopping the series when a digit is played, even if the sound files cannot be found' do
340
- pbx_should_respond_with_stream_file_success 0
341
- pbx_should_respond_with_stream_file_success 0
342
- pbx_should_respond_with_stream_file_failure_on_open
343
- pbx_should_respond_with_stream_file_success ?9.ord
344
-
345
- play_files = ('sound1'..'sound6').map &:to_s
346
- played_files = ('sound1'..'sound4').map &:to_s
347
- mock_call.interruptible_play(*play_files).should == '9'
348
- pbx_was_asked_to_stream played_files
349
- end
350
- end
351
-
352
- describe 'The #interruptible_play! method' do
353
- include DialplanCommandTestHelpers
354
-
355
- it 'should return a string for the digit that was pressed' do
356
- digits = %w{0 1 # * 9}.map{|c| c.ord}
357
- file = "file_doesnt_matter"
358
- digits.each { |digit| pbx_should_respond_with_stream_file_success digit }
359
- digits.map { |digit| mock_call.interruptible_play! file }.should == digits.map(&:chr)
360
- digits.size.times { pbx_was_asked_to_stream file }
361
- end
362
-
363
- it "should return nil if no digit was pressed" do
364
- pbx_should_respond_with_stream_file_success 0
365
- file = 'foobar'
366
- mock_call.interruptible_play!(file).should be nil
367
- pbx_was_asked_to_stream file
368
- end
369
-
370
- it 'should raise an error when the sound file is not found' do
371
- pbx_should_respond_with_stream_file_failure_on_open
372
- file = 'foobar'
373
- the_following_code {
374
- mock_call.interruptible_play! file
375
- }.should raise_error Adhearsion::VoIP::PlaybackError
376
- pbx_was_asked_to_stream file
377
- end
378
-
379
- it "should play a series of files, stopping the series when a digit is played" do
380
- stubbed_keypad_input = [0, 0, ?3.ord]
381
- stubbed_keypad_input.each do |digit|
382
- pbx_should_respond_with_stream_file_success digit
383
- end
384
-
385
- play_files = (100..105).map &:to_s
386
- played_files = (100..102).map &:to_s
387
- mock_call.interruptible_play!(*play_files).should == '3'
388
- pbx_was_asked_to_stream played_files
389
- end
390
-
391
- it 'should play a series of files, raising an error if a sound file cannot be found' do
392
- pbx_should_respond_with_stream_file_success 0
393
- pbx_should_respond_with_stream_file_failure_on_open
394
-
395
- play_files = ('sound1'..'sound6').map &:to_s
396
- played_files = ('sound1'..'sound2').map &:to_s
397
- the_following_code {
398
- mock_call.interruptible_play! *play_files
399
- }.should raise_error Adhearsion::VoIP::PlaybackError
400
- pbx_was_asked_to_stream played_files
401
- end
402
-
403
- it 'should raise an error if an audio file cannot be found' do
404
- pbx_should_respond_with_stream_file_failure_on_open
405
- audio_file = 'nixon-tapes'
406
- the_following_code {
407
- mock_call.interruptible_play! audio_file
408
- }.should raise_error Adhearsion::VoIP::PlaybackError
409
- pbx_was_asked_to_stream audio_file
410
- end
411
-
412
- it 'should raise an error when audio files cannot be found' do
413
- pbx_should_respond_with_stream_file_success
414
- pbx_should_respond_with_stream_file_failure_on_open # 'paperz' is the only audio that is missing
415
- audio_files = ['rock', 'paperz', 'scissors']
416
-
417
- the_following_code {
418
- mock_call.interruptible_play! audio_files
419
- }.should raise_error Adhearsion::VoIP::PlaybackError
420
- pbx_was_asked_to_stream ['rock', 'paperz'] # stop short before playing with scissors!
421
- end
422
- end
423
-
424
- describe 'The #wait_for_digit method' do
425
-
426
- include DialplanCommandTestHelpers
427
-
428
- it 'should return a string for the digit that was pressed' do
429
- digits = %w{0 1 # * 9}.map{|c| c.ord}
430
- digits.each { |digit| pbx_should_respond_with_success digit }
431
- digits.map { |digit| mock_call.send(:wait_for_digit) }.should == digits.map(&:chr)
432
- digits.size.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "-1"' }
433
- end
434
-
435
- it "the timeout given must be converted to milliseconds" do
436
- pbx_should_respond_with_success 0
437
- mock_call.send(:wait_for_digit, 1)
438
- pbx_should_have_been_sent 'WAIT FOR DIGIT "1000"'
439
- end
440
- end
441
-
442
- describe 'The #answer method' do
443
- include DialplanCommandTestHelpers
444
-
445
- it 'should send ANSWER over the AGI socket' do
446
- does_not_read_data_back
447
- mock_call.answer
448
- pbx_should_have_been_sent 'ANSWER'
449
- end
450
-
451
- end
452
-
453
- describe 'The #execute method' do
454
- include DialplanCommandTestHelpers
455
-
456
- it 'execute writes exec and app name to the PBX' do
457
- pbx_should_respond_with_success
458
- assert_success mock_call.execute(:foo)
459
- pbx_should_have_been_sent 'EXEC foo'
460
- end
461
-
462
- it 'execute returns false if the command was not executed successfully by the PBX' do
463
- pbx_should_respond_with_failure
464
- mock_call.execute(:foo).should_not be true
465
- pbx_should_have_been_sent 'EXEC foo'
466
- end
467
-
468
- it 'execute can accept arguments after the app name which get translated into pipe-delimited arguments to the PBX' do
469
- pbx_should_respond_with_success
470
- mock_call.execute :foo, 'bar', 'baz', 'hi'
471
- pbx_should_have_been_sent 'EXEC foo "bar"|"baz"|"hi"'
472
- end
473
-
474
- it "should raise a Hangup exception when nil is returned when reading a command from Asterisk" do
475
- flexmock(input).should_receive(:gets).once.and_return nil
476
- the_following_code {
477
- mock_call.execute :foo, "bar"
478
- }.should raise_error Adhearsion::Hangup
479
- pbx_should_have_been_sent 'EXEC foo "bar"'
480
- end
481
-
482
- it "should raise a ArgumentError if given a null byte in the arguments" do
483
- the_following_code {
484
- mock_call.execute :foo, "bar\0"
485
- }.should raise_error ArgumentError
486
- end
487
- end
488
-
489
- describe 'The #inline_return_value method' do
490
- include DialplanCommandTestHelpers
491
-
492
- it 'should return nil when given false or nil' do
493
- mock_call.inline_return_value(false).should be nil
494
- mock_call.inline_return_value(nil).should be nil
495
- end
496
-
497
- it 'should return nil when given an empty AGI value (0)' do
498
- mock_call.inline_return_value(pbx_result_response(0)).should be nil
499
- end
500
-
501
-
502
- it 'should raise AGIProtocolError with an invalid response' do
503
- expect {
504
- mock_call.inline_return_value("500 result=foo\n")
505
- }.to raise_error Adhearsion::VoIP::Asterisk::AGIProtocolError
506
-
507
- expect {
508
- mock_call.inline_return_value('Hey man, not so loud!')
509
- }.to raise_error Adhearsion::VoIP::Asterisk::AGIProtocolError
510
- end
511
-
512
- it 'should parse the return value' do
513
- mock_call.inline_return_value(pbx_result_response(5)).should == '5'
514
- end
515
- end
516
-
517
- describe 'The #inline_result_with_return_value method' do
518
- include DialplanCommandTestHelpers
519
-
520
- it 'should return nil when given false or nil' do
521
- mock_call.inline_result_with_return_value(false).should be nil
522
- mock_call.inline_result_with_return_value(nil).should be nil
523
- end
524
-
525
- it 'should return nil when given an empty AGI value (0)' do
526
- mock_call.inline_result_with_return_value(pbx_result_response(0)).should be nil
527
- end
528
-
529
- it 'should raise AGIProtocolError with an invalid response' do
530
- expect {
531
- mock_call.inline_result_with_return_value("500 result=1 (foo)\n")
532
- }.to raise_error Adhearsion::VoIP::Asterisk::AGIProtocolError
533
-
534
- expect {
535
- mock_call.inline_result_with_return_value('Hey man, not so loud!')
536
- }.to raise_error Adhearsion::VoIP::Asterisk::AGIProtocolError
537
- end
538
-
539
- it 'should parse the return value' do
540
- mock_call.inline_result_with_return_value(pbx_value_response(5)).should == '5'
541
- end
542
- end
543
-
544
- describe 'The #play_or_speak method' do
545
- include DialplanCommandTestHelpers
546
-
547
- it 'should play a sound file if one exists' do
548
- pbx_should_respond_with_playback_success
549
- audio_file = "cents-per-minute"
550
- mock_call.play_or_speak({audio_file => {}}).should be nil
551
- pbx_was_asked_to_play audio_file
552
- end
553
-
554
- it 'should play a sound file via interruptible_play if file exists and interrupbible set and return key pressed and return the key press value' do
555
- audio_file = "cents-per-minute"
556
- mock_call.should_receive(:interruptible_play!).with(audio_file).once.and_return '#'
557
- mock_call.play_or_speak({audio_file => {:interruptible => true}}).should == '#'
558
- end
559
-
560
- it 'should play a sound file via interruptible_play if file exists and interrupbible set' do
561
- audio_file = "cents-per-minute"
562
- mock_call.should_receive(:interruptible_play!).with(audio_file).once.and_return nil
563
- mock_call.play_or_speak({audio_file => {:interruptible => true}}).should == nil
564
- end
565
-
566
- it 'should raise and error if a sound file does not exist and there is not text specified to fall back to' do
567
- audio_file = "nixon tapes"
568
- mock_call.should_receive(:play!).with(audio_file).and_raise Adhearsion::VoIP::PlaybackError
569
- the_following_code {
570
- mock_call.play_or_speak({audio_file => { :engine => :unimrcp}})
571
- }.should raise_error ArgumentError
572
- end
573
-
574
- it 'should not send the command to play if the audio file is blank' do
575
- mock_call.should_receive(:speak).with('hello', {:engine=>:unimrcp}).once.and_return nil
576
- mock_call.play_or_speak({'' => { :text => 'hello', :engine => :unimrcp }}).should be nil
577
- end
578
-
579
- it 'should not send the command to play if the audio file is nil' do
580
- mock_call.should_receive(:speak).with('hello', {:engine=>:unimrcp}).once.and_return nil
581
- mock_call.play_or_speak({nil => { :text => 'hello', :engine => :unimrcp }}).should be nil
582
- end
583
-
584
- it 'should speak the text if a sound file does not exist' do
585
- audio_file = "nixon tapes"
586
- mock_call.should_receive(:play!).with(audio_file).and_raise Adhearsion::VoIP::PlaybackError
587
- mock_call.should_receive(:speak).with('hello', {:engine=>:unimrcp}).once.and_return nil
588
- mock_call.play_or_speak({audio_file => { :text => 'hello', :engine => :unimrcp }}).should be nil
589
- end
590
-
591
- it 'should speak the text if a sound file does not exist and pass back the entered text if a key is pressed' do
592
- audio_file = "nixon tapes"
593
- mock_call.should_receive(:interruptible_play!).with(audio_file).and_raise Adhearsion::VoIP::PlaybackError
594
- mock_call.should_receive(:speak).with('hello', {:engine=>:unimrcp, :interruptible => true}).once.and_return '5'
595
- mock_call.play_or_speak({audio_file => { :text => 'hello', :engine => :unimrcp, :interruptible => true
596
- }}).should == '5'
597
- end
598
-
599
- end
600
-
601
- describe 'The #play method' do
602
- include DialplanCommandTestHelpers
603
-
604
- it 'passing a single string to play results in the playback application being executed with that file name on the PBX' do
605
- pbx_should_respond_with_playback_success
606
- audio_file = "cents-per-minute"
607
- mock_call.play(audio_file).should be true
608
- pbx_was_asked_to_play audio_file
609
- end
610
-
611
- it 'multiple strings can be passed to play, causing multiple playback commands to be issued' do
612
- 2.times do
613
- pbx_should_respond_with_playback_success
614
- end
615
- audio_files = ["cents-per-minute", 'o-hai']
616
- mock_call.play(audio_files).should be true
617
- pbx_was_asked_to_play audio_files
618
- end
619
-
620
- it 'should return false if an audio file cannot be found' do
621
- pbx_should_respond_with_playback_failure
622
- audio_file = 'nixon-tapes'
623
- mock_call.play(audio_file).should be false
624
- pbx_was_asked_to_play audio_file
625
- end
626
-
627
- it 'should return false when audio files cannot be found' do
628
- pbx_should_respond_with_playback_success
629
- pbx_should_respond_with_playback_failure # 'paperz' is the only audio that is missing
630
- pbx_should_respond_with_playback_success
631
- audio_files = ['rock', 'paperz', 'scissors']
632
-
633
- mock_call.play(audio_files).should be false
634
- pbx_was_asked_to_play audio_files
635
- end
636
-
637
- it 'If a number is passed to play(), the saynumber application is executed with the number as an argument' do
638
- pbx_should_respond_with_success
639
- mock_call.play(123).should be true
640
- pbx_was_asked_to_play_number(123)
641
- end
642
-
643
- it 'if a string representation of a number is passed to play(), the saynumber application is executed with the number as an argument' do
644
- pbx_should_respond_with_success
645
- mock_call.play('123').should be true
646
- pbx_was_asked_to_play_number(123)
647
- end
648
-
649
- it 'If a Time is passed to play(), the SayUnixTime application will be executed with the time since the UNIX epoch in seconds as an argument' do
650
- time = Time.parse("12/5/2000")
651
- pbx_should_respond_with_success
652
- mock_call.play(time).should be true
653
- pbx_was_asked_to_play_time(time.to_i)
654
- end
655
-
656
- it 'If a Date is passed to play(), the SayUnixTime application will be executed with the date passed in' do
657
- date = Date.parse('2011-01-23')
658
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",'BdY').and_return pbx_raw_response
659
- mock_call.play(date).should be true
660
- end
661
-
662
- it 'If a Date or Time is passed to play_time(), the SayUnixTime application will be executed with the date and format passed in' do
663
- date, format = Date.parse('2011-01-23'), 'ABdY'
664
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return "200 result=0\n"
665
- mock_call.play_time(date, :format => format).should == pbx_raw_response
666
-
667
- time, format = Time.at(875121313), 'BdY \'digits/at\' IMp'
668
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",format).and_return pbx_raw_response
669
- mock_call.play_time(time, :format => format).should == pbx_raw_response
670
- end
671
-
672
- it 'If a Time object is passed to play_time, the SayUnixTime application will be executed with the default parameters' do
673
- time = Time.at(875121313)
674
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",'').and_return pbx_raw_response
675
- mock_call.play_time(time).should == pbx_raw_response
676
- end
677
-
678
- it 'If an object other than Time, DateTime, or Date is passed to play_time false will be returned' do
679
- non_time = 'blah'
680
- mock_call.play_time(non_time).should be false
681
- end
682
-
683
- it 'If an array containing a Date/DateTime/Time object and a hash is passed to play(), the SayUnixTime application will be executed with the object passed in with the specified format and timezone' do
684
- date, format = Date.parse('2011-01-23'), 'ABdY'
685
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return pbx_raw_response
686
- mock_call.play([date, {:format => format}]).should be true
687
-
688
- time, timezone = Time.at(1295843084), 'US/Eastern'
689
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,'').and_return pbx_raw_response
690
- mock_call.play([time, {:timezone => timezone}]).should be true
691
-
692
- time, timezone, format = Time.at(1295843084), 'US/Eastern', 'ABdY \'digits/at\' IMp'
693
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,format).and_return pbx_raw_response
694
- mock_call.play([time, {:timezone => timezone, :format => format}]).should be true
695
- end
696
-
697
- it 'If a string matching dollars and (optionally) cents is passed to play(), a series of command will be executed to read the dollar amount', :ignore => true do
698
- #TODO: I think we should not have this be part of play(). Too much functionality in one method. Too much overloading. When we want to support multiple
699
- # currencies, it'll be completely unwieldy. I'd suggest play_currency as a separate method. - Chad
700
- end
701
- end
702
-
703
- describe 'The #play! method' do
704
- include DialplanCommandTestHelpers
705
-
706
- it 'should accept multiple strings to play, causing multiple playback commands to be issued' do
707
- 2.times do
708
- pbx_should_respond_with_playback_success
709
- end
710
- audio_files = ["cents-per-minute", 'o-hai']
711
- mock_call.play!(audio_files).should be true
712
- pbx_was_asked_to_play audio_files
713
- end
714
-
715
- it 'should raise an error if an audio file cannot be found' do
716
- pbx_should_respond_with_playback_failure
717
- audio_file = 'nixon-tapes'
718
- the_following_code {
719
- mock_call.play! audio_file
720
- }.should raise_error Adhearsion::VoIP::PlaybackError
721
- pbx_was_asked_to_play audio_file
722
- end
723
-
724
- it 'should raise an error when audio files cannot be found' do
725
- pbx_should_respond_with_playback_success
726
- pbx_should_respond_with_playback_failure # 'paperz' is the only audio that is missing
727
- audio_files = ['rock', 'paperz', 'scissors']
728
-
729
- the_following_code {
730
- mock_call.play! audio_files
731
- }.should raise_error Adhearsion::VoIP::PlaybackError
732
- pbx_was_asked_to_play ['rock', 'paperz'] # stop short before playing with scissors!
733
- end
734
- end
735
-
736
- describe 'the #record method' do
737
- include DialplanCommandTestHelpers
738
-
739
- it 'should return the recorded file name if the user hangs up during the recording' do
740
- mock_call.should_receive(:response).once.with('RECORD FILE', 'foo', 'gsm', '#', -1, 0, 'BEEP').and_return("200 result=-1 (hangup) endpos=167840\n")
741
- mock_call.record('foo').should == 'foo.gsm'
742
- end
743
-
744
- it 'create a default filename if no file is specifed and icrement it on subsequent calls' do
745
- mock_call.call.variables.delete :recording_counter
746
- mock_call.should_receive(:response).once.with('RECORD FILE', '/tmp/recording_0', 'gsm', '26', -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
747
- mock_call.should_receive(:response).once.with('RECORD FILE', '/tmp/recording_1', 'gsm', '26', -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
748
- mock_call.record(:beep => nil, :escapedigits => '26').should == '/tmp/recording_0.gsm'
749
- mock_call.record(:beep => nil, :escapedigits => '26').should == '/tmp/recording_1.gsm'
750
- end
751
-
752
- it 'should not modify the passed-in file name' do
753
- mock_call.should_receive(:response).once.with('RECORD FILE', 'foo', 'wav', '#', -1, 0, 'BEEP').and_return("200 result=-1 (hangup) endpos=167840\n")
754
- filename = 'foo.wav'
755
- mock_call.record(filename).should == 'foo.wav'
756
- filename.should == 'foo.wav'
757
- end
758
-
759
- it 'determine the format from the filename' do
760
- mock_call.should_receive(:response).once.with('RECORD FILE', 'foo', 'wav', '26', -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
761
- mock_call.record('foo.wav', :beep => nil, :escapedigits => '26').should == 'foo.wav'
762
- end
763
-
764
- it 'set the format of a file via the :format option' do
765
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "wav", "#", 2000, 0).and_return("200 result=0 (timeout) endpos=21600\n")
766
- mock_call.record('foo', :beep => nil, :maxduration => 2, :format => 'wav').should == 'foo.wav'
767
- end
768
-
769
- it 'set the format of a file via the :format option over-riding a implicit format' do
770
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo.wav", "mpeg", "#", 2000, 0).and_return("200 result=0 (timeout) endpos=21600\n")
771
- mock_call.record('foo.wav', :beep => nil, :maxduration => 2, :format => 'mpeg').should == 'foo.wav.mpeg'
772
- end
773
- end
774
-
775
- describe 'the #record_to_file method' do
776
- include DialplanCommandTestHelpers
777
-
778
- it 'should return :hangup if the user hangs up during the recording' do
779
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, "BEEP").and_return("200 result=-1 (hangup) endpos=167840\n")
780
- mock_call.record_to_file('foo').should == :hangup
781
- end
782
-
783
- it 'should return :write error if the recording had a problem writing the file' do
784
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, "BEEP").and_return("200 result=-1 (writefile) endpos=167840\n")
785
- mock_call.record_to_file('foo').should == :write_error
786
- end
787
-
788
- it 'should return :success_dtmf if the recording was completed successfully with a dtmf tone to end' do
789
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, "BEEP").and_return("200 result=35 (dtmf) endpos=29120\n")
790
- mock_call.record_to_file('foo').should == :success_dtmf
791
- end
792
-
793
- it 'should return :success_timeout if the recording was completed successfully by timing out with silence' do
794
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, "BEEP").and_return("200 result=0 (timeout) endpos=21600\n")
795
- mock_call.record_to_file('foo').should == :success_timeout
796
- end
797
-
798
- it 'not send a beep if a :beep=>nil is passed in' do
799
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
800
- mock_call.record_to_file('foo', :beep => nil).should == :success_timeout
801
- end
802
-
803
- it 'set the silence if it is passed in' do
804
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, 's=2').and_return("200 result=0 (timeout) endpos=21600\n")
805
- mock_call.record_to_file('foo', :beep => nil, :silence => 2).should == :success_timeout
806
- end
807
-
808
- it 'set the maxduration if it is passed in' do
809
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", 2000, 0).and_return("200 result=0 (timeout) endpos=21600\n")
810
- mock_call.record_to_file('foo', :beep => nil, :maxduration => 2).should == :success_timeout
811
- end
812
-
813
- it 'set the format of a file via the :format option' do
814
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "wav", "#", 2000, 0).and_return("200 result=0 (timeout) endpos=21600\n")
815
- mock_call.record_to_file('foo', :beep => nil, :maxduration => 2, :format => 'wav').should == :success_timeout
816
- end
817
-
818
- it 'set the format of a file via the :format option over-riding a implicit format' do
819
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo.wav", "mpeg", "#", 2000, 0).and_return("200 result=0 (timeout) endpos=21600\n")
820
- mock_call.record_to_file('foo.wav', :beep => nil, :maxduration => 2, :format => 'mpeg').should == :success_timeout
821
- end
822
-
823
- it 'set the escapedigits if it is passed in' do
824
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
825
- mock_call.record_to_file('foo', :beep => nil, :escapedigits => '26').should == :success_timeout
826
- end
827
-
828
- it 'play a passed in beep file if it is passed in' do
829
- mock_call.should_receive(:execute).once.with(:playback, 'my_awesome_beep.wav').and_return(true)
830
- pbx_should_respond_with_playback_success
831
- pbx_should_respond_with_playback_success
832
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
833
- mock_call.record_to_file('foo', :beep => 'my_awesome_beep.wav', :escapedigits => '26').should == :success_timeout
834
- end
835
-
836
- it "should silently fail if the beep file passed in can't be played" do
837
- mock_call.should_receive(:execute).once.with(:playback, 'my_awesome_beep.wav').and_return(true)
838
- pbx_should_respond_with_playback_failure
839
- pbx_should_respond_with_playback_failure
840
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
841
- mock_call.record_to_file('foo', :beep => 'my_awesome_beep.wav', :escapedigits => '26').should == :success_timeout
842
- end
843
-
844
- it 'determine the format from the filename' do
845
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "wav", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
846
- mock_call.record_to_file('foo.wav', :beep => nil, :escapedigits => '26').should == :success_timeout
847
- end
848
-
849
- it 'create a default filename if no file is specifed and icrement it on subsequent calls' do
850
- mock_call.should_receive(:new_guid).once.and_return('2345')
851
- mock_call.should_receive(:new_guid).once.and_return('4322')
852
- mock_call.should_receive(:response).once.with("RECORD FILE", "/tmp/recording_2345_0", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
853
- mock_call.should_receive(:response).once.with("RECORD FILE", "/tmp/recording_4322_1", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
854
- mock_call.record_to_file(:beep => nil, :escapedigits => '26').should == :success_timeout
855
- mock_call.record_to_file(:beep => nil, :escapedigits => '26').should == :success_timeout
856
- end
857
- end
858
-
859
- describe 'The #record_to_file! method' do
860
- include DialplanCommandTestHelpers
861
-
862
- it "should throw an exception the beep file passed in can't be played" do
863
- mock_call.should_receive(:execute).once.with(:playback, 'my_awesome_beep.wav').and_return(false)
864
- pbx_should_respond_with_playback_failure
865
- pbx_should_respond_with_playback_failure
866
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
867
- the_following_code {
868
- mock_call.record_to_file!('foo', :beep => 'my_awesome_beep.wav', :escapedigits => '26').should == :success_timeout
869
- }.should raise_error Adhearsion::VoIP::PlaybackError
870
- end
871
-
872
- it 'should throw RecordError if the recording had a problem writing the file' do
873
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "gsm", "#", -1, 0, "BEEP").and_return("200 result=-1 (writefile) endpos=167840\n")
874
- the_following_code {
875
- mock_call.record_to_file!('foo').should == :write_error
876
- }.should raise_error Adhearsion::VoIP::RecordError
877
- end
878
-
879
- it 'should be able get a response from a successfull call' do
880
- mock_call.should_receive(:response).once.with("RECORD FILE", "foo", "wav", "26", -1, 0).and_return("200 result=0 (timeout) endpos=21600\n")
881
- mock_call.record_to_file!('foo.wav', :beep => nil, :escapedigits => '26').should == :success_timeout
882
- end
883
- end
884
-
885
- describe 'The #input method' do
886
-
887
- include DialplanCommandTestHelpers
888
-
889
- it 'should raise an error when the number of digits expected is -1 (this is deprecated behavior)' do
890
- the_following_code {
891
- mock_call.input(-1)
892
- }.should raise_error ArgumentError
893
- end
894
-
895
- it 'input() calls wait_for_digit the specified number of times (when no sound files are given)' do
896
- mock_call.should_receive(:interruptible_play!).never
897
- mock_call.should_receive(:wait_for_digit).times(4).with(-1).and_return('1', '2', '3', '4')
898
- mock_call.input(4).should == '1234'
899
- end
900
-
901
- it 'should execute wait_for_digit if no digit is pressed during interruptible_play!' do
902
- sound_files = %w[one two three]
903
- mock_call.should_receive(:interruptible_play!).once.with('one').and_return nil
904
- mock_call.should_receive(:interruptible_play!).once.with('two').and_return nil
905
- mock_call.should_receive(:interruptible_play!).once.with('three').and_return nil
906
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_throw :digit_request
907
- should_throw(:digit_request) { mock_call.input(10, :play => sound_files) }
908
- end
909
-
910
- it 'waits for digits with :initial_timeout and :interdigit_timeout' do
911
- initial_timeout = 6.seconds
912
- interdigit_timeout = 3.seconds
913
- mock_call.should_receive(:interruptible_play!).never
914
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
915
- mock_call.should_receive(:wait_for_digit).times(3).with(interdigit_timeout).and_return '2', '3', '4'
916
- mock_call.input(4, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '1234'
917
- end
918
-
919
- it 'waits for digits with :initial_timeout when nothing is pressed' do
920
- initial_timeout = 6.seconds
921
- interdigit_timeout = 3.seconds
922
- mock_call.should_receive(:interruptible_play!).never
923
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return nil
924
- mock_call.input(4, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq ''
925
- end
926
-
927
- it 'waits for digits, ignoring :timeout if :initial_timeout and :interdigit_timeout are provided' do
928
- timeout = 99.hours
929
- initial_timeout = 2.seconds
930
- interdigit_timeout = 1.second
931
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
932
- mock_call.should_receive(:wait_for_digit).times(3).with(interdigit_timeout).and_return '2', '3', '4'
933
- mock_call.input(4, :timeout => timeout, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '1234'
934
- end
935
-
936
- it 'waits for digits, and given a :timeout and :initial_timeout, defaults :interdigit_timeout to :timeout' do
937
- initial_timeout = 20.seconds
938
- timeout = 10.seconds
939
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
940
- mock_call.should_receive(:wait_for_digit).times(2).with(timeout).and_return '2', '3'
941
- mock_call.input(3, :timeout => timeout, :initial_timeout => initial_timeout).should eq '123'
942
- end
943
-
944
- it 'waits for digits, and given a :timeout and :interdigit_timeout, defaults :initial_timeout to :timeout' do
945
- timeout = 12.seconds
946
- interdigit_timeout = 6.seconds
947
- mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return '1'
948
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
949
- mock_call.input(5, :timeout => timeout, :interdigit_timeout => interdigit_timeout).should eq '12345'
950
- end
951
-
952
- it 'waits for digits for :initial_timeout if sound playback is not interrupted' do
953
- initial_timeout = 8.seconds
954
- interdigit_timeout = 4.seconds
955
- sound_files = %w[ready set go]
956
- mock_call.should_receive(:interruptible_play!).once.with('ready').and_return nil
957
- mock_call.should_receive(:interruptible_play!).once.with('set').and_return nil
958
- mock_call.should_receive(:interruptible_play!).once.with('go').and_return nil
959
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
960
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
961
- mock_call.input(5, :play => sound_files, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12345'
962
- end
963
-
964
- it 'ignores :initial_timeout if sound playback is interrupted' do
965
- initial_timeout = 8.seconds
966
- interdigit_timeout = 4.seconds
967
- sound_files = %w[ready set go]
968
- mock_call.should_receive(:interruptible_play!).once.with('ready').and_return nil
969
- mock_call.should_receive(:interruptible_play!).once.with('set').and_return '*'
970
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
971
- mock_call.input(5, :play => sound_files, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '*2345'
972
- end
973
-
974
- it 'waits for digits for :initial_timeout if speech is not interrupted' do
975
- initial_timeout = 10.seconds
976
- interdigit_timeout = 5.seconds
977
- text = "What's your area code?"
978
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return nil
979
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
980
- mock_call.should_receive(:wait_for_digit).times(2).with(interdigit_timeout).and_return '2', '0'
981
- mock_call.input(3, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :speak => {:text => text}).should eq '120'
982
- end
983
-
984
- it 'ignores :initial_timeout if speech is interrupted' do
985
- initial_timeout = 10.seconds
986
- interdigit_timeout = 5.seconds
987
- text = "What's your area code?"
988
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return '3'
989
- mock_call.should_receive(:wait_for_digit).times(2).with(interdigit_timeout).and_return '1', '2'
990
- mock_call.input(3, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :speak => {:text => text}).should eq '312'
991
- end
992
-
993
- it 'should default the :accept_key to "#" when unlimited digits are to be collected' do
994
- mock_call.should_receive(:wait_for_digit).times(2).with(-1).and_return '*', '#'
995
- mock_call.input.should == '*'
996
- end
997
-
998
- it 'should raise an exception when unlimited digits are to be collected and :accept_key => false' do
999
- flexstub(mock_call).should_receive(:read).and_return
1000
- the_following_code {
1001
- mock_call.input(:accept_key => false)
1002
- }.should raise_error ArgumentError
1003
- pbx_should_have_been_sent 'WAIT FOR DIGIT "-1"'
1004
- end
1005
-
1006
- it 'when :accept_key is false and input() is collecting a finite number of digits, it should allow all DTMFs' do
1007
- all_digits = %w[0 1 2 3 # * 4 5 6 7 8 9]
1008
- mock_call.should_receive(:wait_for_digit).times(all_digits.size).with(-1).and_return(*all_digits)
1009
- the_following_code {
1010
- mock_call.input(all_digits.size, :accept_key => false)
1011
- }.should_not raise_error ArgumentError
1012
- end
1013
-
1014
- it 'should terminate early when the passed block returns something truthy' do
1015
- three_digits = %w[9 3 0]
1016
- mock_call.should_receive(:wait_for_digit).times(2).with(-1).and_return(*three_digits)
1017
- mock_call.input(3, :accept_key => false) { |buffer| buffer.size == 2 }.should == '93'
1018
- end
1019
-
1020
- it "Input timing out when digits are pressed returns only the collected digits" do
1021
- timeout = 1.day
1022
- mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '5', nil
1023
- mock_call.input(9, :timeout => timeout).should == '5'
1024
- end
1025
-
1026
- it 'passes wait_for_digit the :timeout option when one is given' do
1027
- timeout = 1.minute
1028
- mock_call.should_receive(:interruptible_play!).never
1029
- mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '1', '2'
1030
- mock_call.input(2, :timeout => timeout).should == '12'
1031
- end
1032
-
1033
- it 'executes interruptible_play!() with all of the files given to :play' do
1034
- sound_files = %w[foo bar qaz]
1035
- mock_call.should_receive(:interruptible_play!).once.with('foo').and_return nil
1036
- mock_call.should_receive(:interruptible_play!).once.with('bar').and_return nil
1037
- mock_call.should_receive(:interruptible_play!).once.with('qaz').and_return '#'
1038
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_return '*'
1039
- mock_call.input(2, :play => sound_files).should == '#*'
1040
- end
1041
-
1042
- it 'executes #play! when :interruptible is set to false' do
1043
- sound_files = %w[foo bar qaz]
1044
- mock_call.should_receive(:play!).once.with('foo').and_return true
1045
- mock_call.should_receive(:play!).once.with('bar').and_return true
1046
- mock_call.should_receive(:play!).once.with('qaz').and_return true
1047
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_return '*'
1048
- mock_call.input(1, :play => sound_files, :interruptible => false).should == '*'
1049
- end
1050
-
1051
- it 'pressing the terminating key before any other digits returns an empty string' do
1052
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_return '*'
1053
- mock_call.input(:accept_key => '*').should == ''
1054
- end
1055
-
1056
- it 'should execute wait_for_digit first if no sound files are given' do
1057
- mock_call.should_receive(:interruptible_play!).never
1058
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_throw :digit_request
1059
- should_throw(:digit_request) { mock_call.input(1) }
1060
- end
1061
-
1062
- it "Input timing out when digits are pressed returns only the collected digits" do
1063
- timeout = 1.day
1064
- mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '5', nil
1065
- mock_call.input(9, :timeout => timeout).should == '5'
1066
- end
1067
-
1068
- it 'should execute wait_for_digit, even if some interruptible sound files are not found' do
1069
- pbx_should_respond_with_stream_file_failure_on_open
1070
- file = 'foobar'
1071
- initial_timeout = 1.hour
1072
- interdigit_timeout = 1.minute
1073
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '8'
1074
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '9'
1075
- mock_call.input(2, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :play => file).should == '89'
1076
- pbx_was_asked_to_stream file
1077
- end
1078
-
1079
- it 'should execute wait_for_digit, even if some uninterruptible sound files are not found' do
1080
- pbx_should_respond_with_playback_failure
1081
- file = 'foobar'
1082
- initial_timeout = 1.hour
1083
- interdigit_timeout = 1.minute
1084
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '8'
1085
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '9'
1086
- mock_call.input(2, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :play => file, :interruptible => false).should == '89'
1087
- pbx_was_asked_to_play file
1088
- end
1089
-
1090
- it 'should return an empty string if no keys are pressed, even if the sound file is not found' do
1091
- pbx_should_respond_with_stream_file_failure_on_open
1092
- file = 'foobar'
1093
- timeout = 1.second
1094
- mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil
1095
- mock_call.input(5, :timeout => timeout, :play => file).should == ''
1096
- pbx_was_asked_to_stream file
1097
- end
1098
-
1099
- it 'should play a series of files, collecting digits even if some of the sound files cannot be found' do
1100
- pbx_should_respond_with_stream_file_success 0
1101
- pbx_should_respond_with_stream_file_success 0
1102
- pbx_should_respond_with_stream_file_failure_on_open
1103
- pbx_should_respond_with_stream_file_success ?1.ord
1104
-
1105
- play_files = ('sound1'..'sound6').map &:to_s
1106
- played_files = ('sound1'..'sound4').map &:to_s
1107
- timeout = 1.minute
1108
- mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '2', '3'
1109
- mock_call.input(3, :timeout => timeout, :play => play_files).should == '123'
1110
- pbx_was_asked_to_stream played_files
1111
- end
1112
-
1113
- it 'should play a series of 4 interruptible sounds, collecting digits even if some of the sound files cannot be found' do
1114
- pbx_should_respond_with_stream_file_success 0
1115
- pbx_should_respond_with_stream_file_success 0
1116
- pbx_should_respond_with_stream_file_failure_on_open
1117
- pbx_should_respond_with_stream_file_success 0
1118
- pbx_should_respond_with_stream_file_success ?1.ord
1119
-
1120
- play_files = ('sound1'..'sound8').map &:to_s
1121
- played_files = ('sound1'..'sound5').map &:to_s
1122
- timeout = 1.second
1123
- mock_call.should_receive(:wait_for_digit).times(3).with(timeout).and_return '2', '3', '4'
1124
- mock_call.input(4, :timeout => timeout, :play => play_files).should == '1234'
1125
- pbx_was_asked_to_stream played_files
1126
- end
1127
-
1128
- it 'should not raise an exception if the sound file is unplayable' do
1129
- pbx_should_respond_with_stream_file_failure_on_open
1130
- file = 'foobar'
1131
- mock_call.should_receive(:wait_for_digit).once.with -1
1132
- the_following_code {
1133
- mock_call.input 1, :play => file
1134
- }.should_not raise_error
1135
- pbx_was_asked_to_stream file
1136
- end
1137
-
1138
- it 'should default to playing interruptible prompts' do
1139
- mock_call.should_receive(:interruptible_play!).once.with('does_not_matter')
1140
- mock_call.should_receive(:wait_for_digit).once.with -1
1141
- mock_call.input(1, :play => 'does_not_matter')
1142
- end
1143
-
1144
- it 'should render uninterruptible prompts' do
1145
- mock_call.should_receive(:play!).once.with('does_not_matter')
1146
- mock_call.should_receive(:wait_for_digit).once.with -1
1147
- mock_call.input(1, :play => 'does_not_matter', :interruptible => false)
1148
- end
1149
-
1150
- it 'should fall back to speaking TTS if sound file is unplayable' do
1151
- pbx_should_respond_with_stream_file_failure_on_open
1152
- mock_call.should_receive(:speak).once.with("The sound file was not available", :interruptible => true)
1153
- mock_call.should_receive(:wait_for_digit).once.with -1
1154
- mock_call.input(1, :play => 'unavailable sound file', :speak => {:text => "The sound file was not available"})
1155
- @output.read.should == "STREAM FILE \"unavailable sound file\" \"1234567890*#\"\n"
1156
- end
1157
-
1158
- it 'waits for digits for :initial_timeout if fall back TTS is not interrupted' do
1159
- initial_timeout = 10.seconds
1160
- interdigit_timeout = 5.seconds
1161
- text = "What's your sign?"
1162
- missing_sound_file = 'missing-sound-file'
1163
- pbx_should_respond_with_stream_file_failure_on_open
1164
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return nil
1165
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1166
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '2'
1167
- mock_call.input(2, :play => missing_sound_file, :speak => {:text => text}, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12'
1168
- @output.read.should == "STREAM FILE \"#{missing_sound_file}\" \"1234567890*#\"\n"
1169
- end
1170
-
1171
- it 'ignores :initial_timeout if fall back TTS is interrupted' do
1172
- initial_timeout = 10.seconds
1173
- interdigit_timeout = 5.seconds
1174
- text = "What's your sign?"
1175
- missing_sound_file = 'missing-sound-file'
1176
- pbx_should_respond_with_stream_file_failure_on_open
1177
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return '1'
1178
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '2'
1179
- mock_call.input(2, :play => missing_sound_file, :speak => {:text => text}, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12'
1180
- @output.read.should == "STREAM FILE \"#{missing_sound_file}\" \"1234567890*#\"\n"
1181
- end
1182
-
1183
- it 'should allow uninterruptible TTS prompts' do
1184
- mock_call.should_receive(:speak).once.with("The sound file was not available", :interruptible => false)
1185
- mock_call.should_receive(:wait_for_digit).once.with -1
1186
- mock_call.input(1, :speak => {:text => "The sound file was not available"}, :interruptible => false)
1187
- end
1188
-
1189
- it 'should play a series of 4 uninterruptible sounds, collecting digits even if some of the sound files cannot be found' do
1190
- pbx_should_respond_with_playback_success
1191
- pbx_should_respond_with_playback_failure
1192
- pbx_should_respond_with_playback_success
1193
-
1194
- files = ('sound1'..'sound3').map &:to_s
1195
- timeout = 1.second
1196
- mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '6', '7'
1197
- mock_call.input(2, :timeout => timeout, :play => files, :interruptible => false).should == '67'
1198
- pbx_was_asked_to_play files
1199
- end
1200
-
1201
- it 'should raise ArgumentError if passed an empty :accept_key' do
1202
- expect { mock_call.input 1, :accept_key => "" }.to raise_error ArgumentError
1203
- end
1204
-
1205
- end
1206
-
1207
- describe 'The #input! method' do
1208
-
1209
- include DialplanCommandTestHelpers
1210
-
1211
- it 'should raise an error when the number of digits expected is -1 (this is deprecated behavior)' do
1212
- the_following_code {
1213
- mock_call.input! -1
1214
- }.should raise_error ArgumentError
1215
- end
1216
-
1217
- it 'should execute wait_for_digit if no digit is pressed during interruptible_play!' do
1218
- sound_files = %w[one two three]
1219
- mock_call.should_receive(:interruptible_play!).once.with('one').and_return nil
1220
- mock_call.should_receive(:interruptible_play!).once.with('two').and_return nil
1221
- mock_call.should_receive(:interruptible_play!).once.with('three').and_return nil
1222
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_throw :digit_request
1223
- should_throw(:digit_request) { mock_call.input! 10, :play => sound_files }
1224
- end
1225
-
1226
- it 'waits for digits with :initial_timeout and :interdigit_timeout' do
1227
- initial_timeout = 6.seconds
1228
- interdigit_timeout = 3.seconds
1229
- mock_call.should_receive(:interruptible_play!).never
1230
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1231
- mock_call.should_receive(:wait_for_digit).times(3).with(interdigit_timeout).and_return '2', '3', '4'
1232
- mock_call.input!(4, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '1234'
1233
- end
1234
-
1235
- it 'waits for digits with :initial_timeout when nothing is pressed' do
1236
- initial_timeout = 6.seconds
1237
- interdigit_timeout = 3.seconds
1238
- mock_call.should_receive(:interruptible_play!).never
1239
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return nil
1240
- mock_call.input!(4, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq ''
1241
- end
1242
-
1243
- it 'waits for digits, ignoring :timeout if :initial_timeout and :interdigit_timeout are provided' do
1244
- timeout = 99.hours
1245
- initial_timeout = 2.seconds
1246
- interdigit_timeout = 1.second
1247
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1248
- mock_call.should_receive(:wait_for_digit).times(3).with(interdigit_timeout).and_return '2', '3', '4'
1249
- mock_call.input!(4, :timeout => timeout, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '1234'
1250
- end
1251
-
1252
- it 'waits for digits, and given a :timeout and :initial_timeout, defaults :interdigit_timeout to :timeout' do
1253
- initial_timeout = 20.seconds
1254
- timeout = 10.seconds
1255
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1256
- mock_call.should_receive(:wait_for_digit).times(2).with(timeout).and_return '2', '3'
1257
- mock_call.input!(3, :timeout => timeout, :initial_timeout => initial_timeout).should eq '123'
1258
- end
1259
-
1260
- it 'waits for digits, and given a :timeout and :interdigit_timeout, defaults :initial_timeout to :timeout' do
1261
- timeout = 12.seconds
1262
- interdigit_timeout = 6.seconds
1263
- mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return '1'
1264
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
1265
- mock_call.input!(5, :timeout => timeout, :interdigit_timeout => interdigit_timeout).should eq '12345'
1266
- end
1267
-
1268
- it 'waits for digits for :initial_timeout if sound playback is not interrupted' do
1269
- initial_timeout = 8.seconds
1270
- interdigit_timeout = 4.seconds
1271
- sound_files = %w[ready set go]
1272
- mock_call.should_receive(:interruptible_play!).once.with('ready').and_return nil
1273
- mock_call.should_receive(:interruptible_play!).once.with('set').and_return nil
1274
- mock_call.should_receive(:interruptible_play!).once.with('go').and_return nil
1275
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1276
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
1277
- mock_call.input!(5, :play => sound_files, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12345'
1278
- end
1279
-
1280
- it 'ignores :initial_timeout if sound playback is interrupted' do
1281
- initial_timeout = 8.seconds
1282
- interdigit_timeout = 4.seconds
1283
- sound_files = %w[ready set go]
1284
- mock_call.should_receive(:interruptible_play!).once.with('ready').and_return nil
1285
- mock_call.should_receive(:interruptible_play!).once.with('set').and_return '*'
1286
- mock_call.should_receive(:wait_for_digit).times(4).with(interdigit_timeout).and_return '2', '3', '4', '5'
1287
- mock_call.input!(5, :play => sound_files, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '*2345'
1288
- end
1289
-
1290
- it 'waits for digits for :initial_timeout if speech is not interrupted' do
1291
- initial_timeout = 10.seconds
1292
- interdigit_timeout = 5.seconds
1293
- text = "What's your area code?"
1294
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return nil
1295
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1296
- mock_call.should_receive(:wait_for_digit).times(2).with(interdigit_timeout).and_return '2', '0'
1297
- mock_call.input!(3, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :speak => {:text => text}).should eq '120'
1298
- end
1299
-
1300
- it 'ignores :initial_timeout if speech is interrupted' do
1301
- initial_timeout = 10.seconds
1302
- interdigit_timeout = 5.seconds
1303
- text = "What's your area code?"
1304
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return '3'
1305
- mock_call.should_receive(:wait_for_digit).times(2).with(interdigit_timeout).and_return '1', '2'
1306
- mock_call.input!(3, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout, :speak => {:text => text}).should eq '312'
1307
- end
1308
-
1309
- it 'executes interruptible_play!() with all of the files given to :play' do
1310
- sound_files = %w[foo bar qaz]
1311
- mock_call.should_receive(:interruptible_play!).once.with('foo').and_return nil
1312
- mock_call.should_receive(:interruptible_play!).once.with('bar').and_return nil
1313
- mock_call.should_receive(:interruptible_play!).once.with('qaz').and_return '#'
1314
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_return '*'
1315
- mock_call.input!(2, :play => sound_files).should == '#*'
1316
- end
1317
-
1318
- it 'executes play!() with all of the files given to :play' do
1319
- sound_files = %w[foo bar qaz]
1320
- mock_call.should_receive(:play!).once.with('foo').and_return true
1321
- mock_call.should_receive(:play!).once.with('bar').and_return true
1322
- mock_call.should_receive(:play!).once.with('qaz').and_return true
1323
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_return '*'
1324
- mock_call.input!(1, :play => sound_files, :interruptible => false).should == '*'
1325
- end
1326
-
1327
- it 'should execute wait_for_digit first if no sound files are given' do
1328
- mock_call.should_receive(:interruptible_play!).never
1329
- mock_call.should_receive(:wait_for_digit).once.with(-1).and_throw :digit_request
1330
- should_throw(:digit_request) { mock_call.input! 1 }
1331
- end
1332
-
1333
- it 'should raise an error when the sound file is not found' do
1334
- pbx_should_respond_with_stream_file_failure_on_open
1335
- file = 'foobar'
1336
- mock_call.should_receive(:wait_for_digit).never
1337
- the_following_code {
1338
- mock_call.input! 1, :play => file
1339
- }.should raise_error Adhearsion::VoIP::PlaybackError
1340
- pbx_was_asked_to_stream file
1341
- end
1342
-
1343
- it 'should fall back to speaking TTS if sound file is unplayable' do
1344
- pbx_should_respond_with_stream_file_failure_on_open
1345
- mock_call.should_receive(:speak).once.with("The sound file was not available", :interruptible => true)
1346
- mock_call.should_receive(:wait_for_digit).once.with -1
1347
- mock_call.input!(1, :play => 'unavailable sound file', :speak => {:text => "The sound file was not available"})
1348
- @output.read.should == "STREAM FILE \"unavailable sound file\" \"1234567890*#\"\n"
1349
- end
1350
-
1351
- it 'waits for digits for :initial_timeout if fall back TTS is not interrupted' do
1352
- initial_timeout = 10.seconds
1353
- interdigit_timeout = 5.seconds
1354
- text = "What's your sign?"
1355
- missing_sound_file = 'missing-sound-file'
1356
- pbx_should_respond_with_stream_file_failure_on_open
1357
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return nil
1358
- mock_call.should_receive(:wait_for_digit).once.with(initial_timeout).and_return '1'
1359
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '2'
1360
- mock_call.input!(2, :play => missing_sound_file, :speak => {:text => text}, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12'
1361
- @output.read.should == "STREAM FILE \"#{missing_sound_file}\" \"1234567890*#\"\n"
1362
- end
1363
-
1364
- it 'ignores :initial_timeout if fall back TTS is interrupted' do
1365
- initial_timeout = 10.seconds
1366
- interdigit_timeout = 5.seconds
1367
- text = "What's your sign?"
1368
- missing_sound_file = 'missing-sound-file'
1369
- pbx_should_respond_with_stream_file_failure_on_open
1370
- mock_call.should_receive(:speak).once.with(text, :interruptible => true).and_return '1'
1371
- mock_call.should_receive(:wait_for_digit).once.with(interdigit_timeout).and_return '2'
1372
- mock_call.input!(2, :play => missing_sound_file, :speak => {:text => text}, :initial_timeout => initial_timeout, :interdigit_timeout => interdigit_timeout).should eq '12'
1373
- @output.read.should == "STREAM FILE \"#{missing_sound_file}\" \"1234567890*#\"\n"
1374
- end
1375
-
1376
- it 'should play a series of interruptible files, raising an error if a sound file cannot be found' do
1377
- pbx_should_respond_with_stream_file_success 0
1378
- pbx_should_respond_with_stream_file_failure_on_open
1379
- mock_call.should_receive(:wait_for_digit).never
1380
-
1381
- play_files = ('sound1'..'sound6').map &:to_s
1382
- played_files = ('sound1'..'sound2').map &:to_s
1383
- the_following_code {
1384
- mock_call.input! 10, :play => play_files, :timeout => 5.seconds
1385
- }.should raise_error Adhearsion::VoIP::PlaybackError
1386
- pbx_was_asked_to_stream played_files
1387
- end
1388
-
1389
- it 'should play a series of uninterruptible files, raising an error if a sound file cannot be found' do
1390
- pbx_should_respond_with_playback_success
1391
- pbx_should_respond_with_playback_failure
1392
-
1393
- play_files = ('sound1'..'sound6').map &:to_s
1394
- played_files = ('sound1'..'sound2').map &:to_s
1395
- the_following_code {
1396
- mock_call.input! 10, :play => play_files, :timeout => 5.seconds, :interruptible => false
1397
- }.should raise_error Adhearsion::VoIP::PlaybackError
1398
- pbx_was_asked_to_play played_files
1399
- end
1400
-
1401
- it 'should raise ArgumentError if passed an empty :accept_key' do
1402
- expect { mock_call.input 1, :accept_key => "" }.to raise_error ArgumentError
1403
- end
1404
-
1405
- end
1406
-
1407
- describe "The #variable method" do
1408
-
1409
- include DialplanCommandTestHelpers
1410
-
1411
- it "should call set_variable for every Hash-key argument given" do
1412
- args = [:ohai, "ur_home_erly"]
1413
- mock_call.should_receive(:set_variable).once.with(*args)
1414
- mock_call.variable Hash[*args]
1415
- end
1416
-
1417
- it "should call set_variable for every Hash-key argument given" do
1418
- many_args = { :a => :b, :c => :d, :e => :f, :g => :h}
1419
- mock_call.should_receive(:set_variable).times(many_args.size)
1420
- mock_call.variable many_args
1421
- end
1422
-
1423
- it "should call get_variable for every String given" do
1424
- variables = ["foo", "bar", :qaz, :qwerty, :baz]
1425
- variables.each do |var|
1426
- mock_call.should_receive(:get_variable).once.with(var).and_return("X")
1427
- end
1428
- mock_call.variable(*variables)
1429
- end
1430
-
1431
- it "should NOT return an Array when just one arg is given" do
1432
- mock_call.should_receive(:get_variable).once.and_return "lol"
1433
- mock_call.variable(:foo).should_not be_a_kind_of Array
1434
- end
1435
-
1436
- it "should raise an ArgumentError when a Hash and normal args are given" do
1437
- the_following_code {
1438
- mock_call.variable 5,4,3,2,1, :foo => :bar
1439
- }.should raise_error ArgumentError
1440
- end
1441
-
1442
- end
1443
-
1444
- describe "The #set_variable method" do
1445
-
1446
- include DialplanCommandTestHelpers
1447
-
1448
- it "variables and values are properly quoted" do
1449
- mock_call.should_receive(:raw_response).once.with 'SET VARIABLE "foo" "i can \\" has ruby?"'
1450
- mock_call.set_variable 'foo', 'i can " has ruby?'
1451
- end
1452
-
1453
- it "to_s() is effectively called on both the key and the value" do
1454
- mock_call.should_receive(:raw_response).once.with 'SET VARIABLE "QAZ" "QWERTY"'
1455
- mock_call.set_variable :QAZ, :QWERTY
1456
- end
1457
-
1458
- end
1459
-
1460
- describe "The #sip_add_header method" do
1461
- include DialplanCommandTestHelpers
1462
-
1463
- it "values are properly quoted" do
1464
- mock_call.should_receive(:raw_response).once.with 'EXEC SIPAddHeader "x-ahn-header: rubyrox"'
1465
- mock_call.sip_add_header "x-ahn-header", "rubyrox"
1466
- end
1467
- end
1468
-
1469
- describe "The #sip_get_header method" do
1470
- include DialplanCommandTestHelpers
1471
-
1472
- it "properly formats the AGI request" do
1473
- value = 'jason-was-here'
1474
- mock_call.should_receive(:raw_response).once.with('GET VARIABLE "SIP_HEADER(x-ahn-header)"').and_return "200 result=1 (#{value})"
1475
- mock_call.sip_get_header("x-ahn-header").should == value
1476
- end
1477
-
1478
- it "properly formats the AGI request using the method alias" do
1479
- value = 'jason-was-here'
1480
- mock_call.should_receive(:raw_response).once.with('GET VARIABLE "SIP_HEADER(x-ahn-header)"').and_return "200 result=1 (#{value})"
1481
- mock_call.sip_header("x-ahn-header").should == value
1482
- end
1483
- end
1484
-
1485
- describe 'The #voicemail command' do
1486
-
1487
- include DialplanCommandTestHelpers
1488
-
1489
- it 'should not send the context name when none is given' do
1490
- mailbox_number = 123
1491
- mock_call.should_receive(:execute).once.with('voicemail', 123, '').and_throw :sent_voicemail!
1492
- should_throw(:sent_voicemail!) { mock_call.voicemail 123 }
1493
- end
1494
-
1495
- it 'should send the context name when one is given' do
1496
- mailbox_number, context_name = 333, 'doesntmatter'
1497
- mock_call.should_receive(:execute).once.with('voicemail', "#{mailbox_number}@#{context_name}", '').and_throw :sent_voicemail!
1498
- should_throw(:sent_voicemail!) { mock_call.voicemail(context_name => mailbox_number) }
1499
- end
1500
-
1501
- it 'should pass in the s option if :skip => true' do
1502
- mailbox_number = '012'
1503
- mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 's').and_throw :sent_voicemail!
1504
- should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :skip => true) }
1505
- end
1506
-
1507
- it 'should combine mailbox numbers with the context name given when both are given' do
1508
- pbx_should_respond_with_value 'SUCCESS'
1509
- context = "lolcats"
1510
- mailboxes = [1,2,3,4,5]
1511
- mailboxes_with_context = mailboxes.map { |mailbox| "#{mailbox}@#{context}"}
1512
- mock_call.should_receive(:execute).once.with('voicemail', mailboxes_with_context.join('&'), '')
1513
- mock_call.voicemail context => mailboxes
1514
- pbx_should_have_been_sent 'GET VARIABLE "VMSTATUS"'
1515
- end
1516
-
1517
- it 'should raise an argument error if the mailbox number is not numerical' do
1518
- the_following_code {
1519
- mock_call.voicemail :foo => "bar"
1520
- }.should raise_error ArgumentError
1521
- end
1522
-
1523
- it 'should raise an argument error if too many arguments are supplied' do
1524
- the_following_code {
1525
- mock_call.voicemail "wtfisthisargument", :context_name => 123, :greeting => :busy
1526
- }.should raise_error ArgumentError
1527
- end
1528
-
1529
- it 'should raise an ArgumentError if multiple context names are given' do
1530
- the_following_code {
1531
- mock_call.voicemail :one => [1,2,3], :two => [11,22,33]
1532
- }.should raise_error ArgumentError
1533
- end
1534
-
1535
- it "should raise an ArgumentError when the :greeting value isn't recognized" do
1536
- the_following_code {
1537
- mock_call.voicemail :context_name => 123, :greeting => :zomgz
1538
- }.should raise_error ArgumentError
1539
- end
1540
-
1541
- it 'should pass in the u option if :greeting => :unavailable' do
1542
- mailbox_number = '776'
1543
- mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'u').and_throw :sent_voicemail!
1544
- should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :unavailable) }
1545
- end
1546
-
1547
- it 'should pass in both the skip and greeting options if both are supplied' do
1548
- mailbox_number = '4'
1549
- mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'u').and_throw :sent_voicemail!
1550
- should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :unavailable) }
1551
- end
1552
-
1553
- it 'should raise an ArgumentError if mailbox_number is blank?()' do
1554
- the_following_code {
1555
- mock_call.voicemail ''
1556
- }.should raise_error ArgumentError
1557
-
1558
- the_following_code {
1559
- mock_call.voicemail nil
1560
- }.should raise_error ArgumentError
1561
- end
1562
-
1563
- it 'should pass in the b option if :gretting => :busy' do
1564
- mailbox_number = '1'
1565
- mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'b').and_throw :sent_voicemail!
1566
- should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :busy) }
1567
- end
1568
-
1569
- it 'should return true if VMSTATUS == "SUCCESS"' do
1570
- mock_call.should_receive(:execute).once
1571
- mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "SUCCESS"
1572
- mock_call.voicemail(3).should be true
1573
- end
1574
-
1575
- it 'should return false if VMSTATUS == "USEREXIT"' do
1576
- mock_call.should_receive(:execute).once
1577
- mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "USEREXIT"
1578
- mock_call.voicemail(2).should be false
1579
- end
1580
-
1581
- it 'should return nil if VMSTATUS == "FAILED"' do
1582
- mock_call.should_receive(:execute).once
1583
- mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "FAILED"
1584
- mock_call.voicemail(2).should be nil
1585
- end
1586
-
1587
- end
1588
-
1589
- describe 'The voicemail_main command' do
1590
-
1591
- include DialplanCommandTestHelpers
1592
-
1593
- #it "should not pass in the context or the delimiting @ sign if you don't supply one"
1594
-
1595
- it "the :folder Hash key argument should wrap the value in a()" do
1596
- folder = "foobar"
1597
- mailbox = 81
1598
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}","a(#{folder})")
1599
- mock_call.voicemail_main :mailbox => mailbox, :folder => folder
1600
- end
1601
-
1602
- it ':authenticate should pass in the "s" option if given false' do
1603
- mailbox = 333
1604
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}","s")
1605
- mock_call.voicemail_main :mailbox => mailbox, :authenticate => false
1606
- end
1607
-
1608
- it ':authenticate should pass in the s option if given false' do
1609
- mailbox = 55
1610
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}")
1611
- mock_call.voicemail_main :mailbox => mailbox, :authenticate => true
1612
- end
1613
-
1614
- it 'should not pass any flags only a mailbox is given' do
1615
- mailbox = "1"
1616
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}")
1617
- mock_call.voicemail_main :mailbox => mailbox
1618
- end
1619
-
1620
- it 'when given no mailbox or context an empty string should be passed to execute as the first argument' do
1621
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "", "s")
1622
- mock_call.voicemail_main :authenticate => false
1623
- end
1624
-
1625
- it 'should properly concatenate the options when given multiple ones' do
1626
- folder = "ohai"
1627
- mailbox = 9999
1628
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}", "sa(#{folder})")
1629
- mock_call.voicemail_main :mailbox => mailbox, :authenticate => false, :folder => folder
1630
- end
1631
-
1632
- it 'should not require any arguments' do
1633
- mock_call.should_receive(:execute).once.with("VoiceMailMain")
1634
- mock_call.voicemail_main
1635
- end
1636
-
1637
- it 'should pass in the "@context_name" part in if a :context is given and no mailbox is given' do
1638
- context_name = "icanhascheezburger"
1639
- mock_call.should_receive(:execute).once.with("VoiceMailMain", "@#{context_name}")
1640
- mock_call.voicemail_main :context => context_name
1641
- end
1642
-
1643
- it "should raise an exception if the folder has a space or malformed characters in it" do
1644
- ["i has a space", "exclaim!", ",", ""].each do |bad_folder_name|
1645
- the_following_code {
1646
- mock_call.voicemail_main :mailbox => 123, :folder => bad_folder_name
1647
- }.should raise_error ArgumentError
1648
- end
1649
- end
1650
-
1651
- end
1652
-
1653
- describe 'the check_voicemail command' do
1654
-
1655
- include DialplanCommandTestHelpers
1656
-
1657
- it "should simply execute voicemail_main with no arguments after warning" do
1658
- flexmock(ahn_log.agi).should_receive(:warn).once.with(String)
1659
- mock_call.should_receive(:voicemail_main).once.and_return :mocked_out
1660
- mock_call.check_voicemail.should be :mocked_out
1661
- end
1662
-
1663
- end
1664
-
1665
-
1666
- describe "The queue management abstractions" do
1667
-
1668
- include DialplanCommandTestHelpers
1669
-
1670
- it 'should not create separate objects for queues with basically the same name' do
1671
- mock_call.queue('foo').should be mock_call.queue('foo')
1672
- mock_call.queue('bar').should be mock_call.queue(:bar)
1673
- end
1674
-
1675
- it "queue() should return an instance of QueueProxy" do
1676
- mock_call.queue("foobar").should be_a_kind_of Adhearsion::VoIP::Asterisk::Commands::QueueProxy
1677
- end
1678
-
1679
- it "a QueueProxy should respond to join!(), members()" do
1680
- %w[join! agents].each do |method|
1681
- mock_call.queue('foobar').should respond_to(method)
1682
- end
1683
- end
1684
-
1685
- it 'a QueueProxy should return a QueueAgentsListProxy when members() is called' do
1686
- mock_call.queue('foobar').agents.should be_a_kind_of(Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueAgentsListProxy)
1687
- end
1688
-
1689
- it 'join! should properly join a queue' do
1690
- mock_call.should_receive(:execute).once.with("queue", "foobaz", "", '', '', '', '')
1691
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "FULL"
1692
- mock_call.queue("foobaz").join!
1693
- end
1694
-
1695
- it 'should return a symbol representing the result of joining the queue' do
1696
- does_not_read_data_back
1697
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "TIMEOUT"
1698
- mock_call.queue('monkey').join!.should be :timeout
1699
- pbx_should_have_been_sent 'EXEC queue "monkey"|""|""|""|""|""'
1700
- end
1701
-
1702
- it 'should return :completed after joining the queue and being connected' do
1703
- does_not_read_data_back
1704
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return nil
1705
- mock_call.queue('monkey').join!.should be :completed
1706
- pbx_should_have_been_sent 'EXEC queue "monkey"|""|""|""|""|""'
1707
- end
1708
-
1709
- it 'should join a queue with a timeout properly' do
1710
- mock_call.should_receive(:execute).once.with("queue", "foobaz", "", '', '', '60', '')
1711
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1712
- mock_call.queue("foobaz").join! :timeout => 1.minute
1713
- end
1714
-
1715
- it 'should join a queue with an announcement file properly' do
1716
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "", '', 'custom_announcement_file_here', '', '')
1717
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1718
- mock_call.queue("roflcopter").join! :announce => 'custom_announcement_file_here'
1719
- end
1720
-
1721
- it 'should join a queue with an agi script properly' do
1722
- mock_call.should_receive(:execute).once.with("queue", 'support', '', '', '', '','agi://localhost/queue_agi_test')
1723
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINUNAVAIL"
1724
- mock_call.queue("support").join! :agi => 'agi://localhost/queue_agi_test'
1725
- end
1726
-
1727
- it 'should join a queue with allow_transfer properly' do
1728
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "Tt", '', '', '', '')
1729
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1730
- mock_call.queue("roflcopter").join! :allow_transfer => :everyone
1731
-
1732
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "T", '', '', '', '')
1733
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1734
- mock_call.queue("roflcopter").join! :allow_transfer => :caller
1735
-
1736
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "t", '', '', '', '')
1737
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1738
- mock_call.queue("roflcopter").join! :allow_transfer => :agent
1739
- end
1740
-
1741
- it 'should join a queue with allow_hangup properly' do
1742
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "Hh", '', '', '', '')
1743
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1744
- mock_call.queue("roflcopter").join! :allow_hangup => :everyone
1745
-
1746
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "H", '', '', '', '')
1747
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1748
- mock_call.queue("roflcopter").join! :allow_hangup => :caller
1749
-
1750
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "h", '', '', '', '')
1751
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1752
- mock_call.queue("roflcopter").join! :allow_hangup => :agent
1753
- end
1754
-
1755
- it 'should join a queue properly with the :play argument' do
1756
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "r", '', '', '', '')
1757
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1758
- mock_call.queue("roflcopter").join! :play => :ringing
1759
-
1760
- mock_call.should_receive(:execute).once.with("queue", "roflcopter", "", '', '', '', '')
1761
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1762
- mock_call.queue("roflcopter").join! :play => :music
1763
- end
1764
-
1765
- it 'joining a queue with many options specified' do
1766
- mock_call.should_receive(:execute).once.with("queue", "q", "rtHh", '', '', '120', '')
1767
- mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
1768
- mock_call.queue('q').join! :allow_transfer => :agent, :timeout => 2.minutes,
1769
- :play => :ringing, :allow_hangup => :everyone
1770
- end
1771
-
1772
- it 'join!() should raise an ArgumentError when unrecognized Hash key arguments are given' do
1773
- the_following_code {
1774
- mock_call.queue('iwearmysunglassesatnight').join! :misspelled => true
1775
- }.should raise_error ArgumentError
1776
- end
1777
-
1778
- it 'should fetch the members with the name given to queue()' do
1779
- mock_call.should_receive(:variable).once.with("QUEUE_MEMBER_COUNT(jay)").and_return 5
1780
- mock_call.queue('jay').agents.size.should == 5
1781
- end
1782
-
1783
- it 'should not fetch a QUEUE_MEMBER_COUNT each time count() is called when caching is enabled' do
1784
- mock_call.should_receive(:variable).once.with("QUEUE_MEMBER_COUNT(sales)").and_return 0
1785
- 10.times do
1786
- mock_call.queue('sales').agents(:cache => true).size
1787
- end
1788
- end
1789
-
1790
- it 'should raise an argument error if the members() method receives an unrecognized symbol' do
1791
- the_following_code {
1792
- mock_call.queue('foobarz').agents(:cached => true) # common typo
1793
- }.should raise_error ArgumentError
1794
- end
1795
-
1796
- it 'when fetching agents, it should properly split by the supported delimiters' do
1797
- queue_name = "doesnt_matter"
1798
- mock_call.should_receive(:get_variable).with("QUEUE_MEMBER_LIST(#{queue_name})").and_return('Agent/007,Agent/003,Zap/2')
1799
- mock_call.queue(queue_name).agents(:cache => true).to_a.size.should == 3
1800
- end
1801
-
1802
- it 'when fetching agents, each array index should be an instance of AgentProxy' do
1803
- queue_name = 'doesnt_matter'
1804
- mock_call.should_receive(:get_variable).with("QUEUE_MEMBER_LIST(#{queue_name})").and_return('Agent/007,Agent/003,Zap/2')
1805
- agents = mock_call.queue(queue_name).agents(:cache => true).to_a
1806
- agents.size.should > 0
1807
- agents.each do |agent|
1808
- agent.should be_a_kind_of Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy
1809
- end
1810
- end
1811
-
1812
- it 'should properly retrieve metadata for an AgentProxy instance' do
1813
- agent_id, metadata_name = '22', 'status'
1814
- mock_env = flexmock "a mock ExecutionEnvironment"
1815
- mock_queue = flexmock "a queue that references our mock ExecutionEnvironment", :environment => mock_env, :name => "doesntmatter"
1816
- mock_env.should_receive(:variable).once.with("AGENT(#{agent_id}:#{metadata_name})")
1817
- agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new("Agent/#{agent_id}", mock_queue)
1818
- agent.send(:agent_metadata, metadata_name)
1819
- end
1820
-
1821
- it 'AgentProxy#logged_in? should return true if the "state" of an agent == LOGGEDIN' do
1822
- mock_env = flexmock "a mock ExecutionEnvironment"
1823
- mock_queue = flexmock "a queue that references our mock ExecutionEnvironment", :environment => mock_env, :name => "doesntmatter"
1824
-
1825
- agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new('Agent/123', mock_queue)
1826
- flexmock(agent).should_receive(:agent_metadata).once.with('status').and_return 'LOGGEDIN'
1827
- agent.logged_in?.should be true
1828
-
1829
- flexmock(agent).should_receive(:agent_metadata).once.with('status').and_return 'LOGGEDOUT'
1830
- agent.logged_in?.should_not be true
1831
- end
1832
-
1833
- it 'the AgentProxy should populate its own "id" property to the numerical ID of the "interface" with which it was constructed' do
1834
- mock_queue = flexmock :name => "doesntmatter"
1835
- id = '123'
1836
-
1837
- agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new("Agent/#{id}", mock_queue)
1838
- agent.id.should == id
1839
-
1840
- agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new(id, mock_queue)
1841
- agent.id.should == id
1842
- end
1843
-
1844
- it 'QueueAgentsListProxy#<<() should new the channel driver given as the argument to the system' do
1845
- queue_name, agent_channel = "metasyntacticvariablesftw", "Agent/123"
1846
- mock_call.should_receive('execute').once.with("AddQueueMember", queue_name, agent_channel, "", "", "", "")
1847
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1848
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1849
- mock_call.queue(queue_name).agents.new agent_channel
1850
- end
1851
-
1852
- it 'when a queue agent is dynamically added and the queue does not exist, a QueueDoesNotExistError should be raised' do
1853
- does_not_read_data_back
1854
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('NOSUCHQUEUE')
1855
- the_following_code {
1856
- mock_call.queue('this_should_not_exist').agents.new 'Agent/911'
1857
- }.should raise_error Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
1858
- pbx_should_have_been_sent 'EXEC AddQueueMember "this_should_not_exist"|"Agent/911"|""|""|""|""'
1859
- end
1860
-
1861
- it 'when a queue agent is dynamiaclly added and the adding was successful, an AgentProxy should be returned' do
1862
- mock_call.should_receive(:get_variable).once.with("AQMSTATUS").and_return("ADDED")
1863
- mock_call.should_receive(:execute).once.with("AddQueueMember", "lalala", "Agent/007", "", "", "", "")
1864
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(lalala)").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1865
- return_value = mock_call.queue('lalala').agents.new "Agent/007"
1866
- return_value.kind_of?(Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy).should be true
1867
- end
1868
-
1869
- it 'when a queue agent is dynamiaclly added and the adding was unsuccessful, a false should be returned' do
1870
- mock_call.should_receive(:get_variable).once.with("AQMSTATUS").and_return("MEMBERALREADY")
1871
- mock_call.should_receive(:execute).once.with("AddQueueMember", "lalala", "Agent/007", "", "", "", "")
1872
- return_value = mock_call.queue('lalala').agents.new "Agent/007"
1873
- return_value.should be false
1874
- end
1875
-
1876
- it 'should raise an argument when an unrecognized key is given to add()' do
1877
- the_following_code {
1878
- mock_call.queue('q').agents.new :foo => "bar"
1879
- }.should raise_error ArgumentError
1880
- end
1881
-
1882
- it 'should execute AddQueueMember with the penalty properly' do
1883
- queue_name = 'name_does_not_matter'
1884
- mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, 'Agent/007', 10, '', '','')
1885
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1886
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1887
- mock_call.queue(queue_name).agents.new 'Agent/007', :penalty => 10
1888
- end
1889
-
1890
- it 'should execute AddQueueMember with the state_interface properly' do
1891
- queue_name = 'name_does_not_matter'
1892
- mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, 'Agent/007', '', '', '','SIP/2302')
1893
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1894
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1895
- mock_call.queue(queue_name).agents.new 'Agent/007', :state_interface => 'SIP/2302'
1896
- end
1897
-
1898
- it 'should execute AddQueueMember properly when the name is given' do
1899
- queue_name, agent_name = 'name_does_not_matter', 'Jay Phillips'
1900
- mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, 'Agents/007', '', '', agent_name,'')
1901
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1902
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1903
- mock_call.queue(queue_name).agents.new 'Agents/007', :name => agent_name
1904
- end
1905
-
1906
- it 'should execute AddQueueMember properly when the name, penalty, and interface is given' do
1907
- queue_name, agent_name, interface, penalty = 'name_does_not_matter', 'Jay Phillips', 'Agent/007', 4
1908
- mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, interface, penalty, '', agent_name,'')
1909
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1910
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1911
- mock_call.queue(queue_name).agents.new interface, :name => agent_name, :penalty => penalty
1912
- end
1913
-
1914
- it 'should execute AddQueueMember properly when the name, penalty, interface, and state_interface is given' do
1915
- queue_name, agent_name, interface, penalty, state_interface = 'name_does_not_matter', 'Jay Phillips', 'Agent/007', 4, 'SIP/2302'
1916
- mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, interface, penalty, '', agent_name, state_interface)
1917
- mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
1918
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/007,SIP/2302,Local/2510@from-internal"
1919
- mock_call.queue(queue_name).agents.new interface, :name => agent_name, :penalty => penalty, :state_interface => state_interface
1920
- end
1921
-
1922
- it 'should return a correct boolean for exists?()' do
1923
- mock_call.should_receive(:execute).once.with("RemoveQueueMember", "kablamm", "SIP/AdhearsionQueueExistenceCheck")
1924
- mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOTINQUEUE"
1925
- mock_call.queue("kablamm").exists?.should be true
1926
-
1927
- mock_call.should_receive(:execute).once.with("RemoveQueueMember", "monkey", "SIP/AdhearsionQueueExistenceCheck")
1928
- mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOSUCHQUEUE"
1929
- mock_call.queue("monkey").exists?.should be false
1930
- end
1931
-
1932
- it 'should pause an agent properly from a certain queue' do
1933
- does_not_read_data_back
1934
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(lolcats)").and_return "Agent/007,Agent/008"
1935
- mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "PAUSED"
1936
-
1937
- agents = mock_call.queue('lolcats').agents :cache => true
1938
- agents.last.pause!.should be true
1939
- pbx_should_have_been_sent 'EXEC PauseQueueMember "lolcats"|"Agent/008"'
1940
- end
1941
-
1942
- it 'should pause an agent properly from a certain queue and return false when the agent did not exist' do
1943
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(lolcats)").and_return "Agent/007,Agent/008"
1944
- mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "NOTFOUND"
1945
- mock_call.should_receive(:execute).once.with("PauseQueueMember", 'lolcats', "Agent/008")
1946
-
1947
- agents = mock_call.queue('lolcats').agents :cache => true
1948
- agents.last.pause!.should be false
1949
- end
1950
-
1951
- it 'should pause an agent globally properly' do
1952
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(family)").and_return "Agent/Jay"
1953
- mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "PAUSED"
1954
- mock_call.should_receive(:execute).once.with("PauseQueueMember", nil, "Agent/Jay")
1955
-
1956
- mock_call.queue('family').agents.first.pause! :everywhere => true
1957
- end
1958
-
1959
- it 'should unpause an agent properly' do
1960
- queue_name = "name with spaces"
1961
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/Jay"
1962
- mock_call.should_receive(:get_variable).once.with("UPQMSTATUS").and_return "UNPAUSED"
1963
- mock_call.should_receive(:execute).once.with("UnpauseQueueMember", queue_name, "Agent/Jay")
1964
-
1965
- mock_call.queue(queue_name).agents.first.unpause!.should be true
1966
- end
1967
-
1968
- it 'should unpause an agent globally properly' do
1969
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
1970
- mock_call.should_receive(:get_variable).once.with("UPQMSTATUS").and_return "UNPAUSED"
1971
- mock_call.should_receive(:execute).once.with("UnpauseQueueMember", nil, "Agent/Tom")
1972
-
1973
- mock_call.queue('FOO').agents.first.unpause!(:everywhere => true).should be true
1974
- end
1975
-
1976
- it 'waiting_count for a queue that does exist' do
1977
- mock_call.should_receive(:get_variable).once.with("QUEUE_WAITING_COUNT(q)").and_return "50"
1978
- flexmock(mock_call.queue('q')).should_receive(:exists?).once.and_return true
1979
- mock_call.queue('q').waiting_count.should == 50
1980
- end
1981
-
1982
- it 'waiting_count for a queue that does not exist' do
1983
- the_following_code {
1984
- flexmock(mock_call.queue('q')).should_receive(:exists?).once.and_return false
1985
- mock_call.queue('q').waiting_count
1986
- }.should raise_error Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
1987
- end
1988
-
1989
- it 'empty? should call waiting_count' do
1990
- queue = mock_call.queue 'testing_empty'
1991
- flexmock(queue).should_receive(:waiting_count).once.and_return 0
1992
- queue.empty?.should be true
1993
-
1994
- queue = mock_call.queue 'testing_empty'
1995
- flexmock(queue).should_receive(:waiting_count).once.and_return 99
1996
- queue.empty?.should_not be true
1997
- end
1998
-
1999
- it 'any? should call waiting_count' do
2000
- queue = mock_call.queue 'testing_empty'
2001
- flexmock(queue).should_receive(:waiting_count).once.and_return 0
2002
- queue.any?.should be false
2003
-
2004
- queue = mock_call.queue 'testing_empty'
2005
- flexmock(queue).should_receive(:waiting_count).once.and_return 99
2006
- queue.any?.should be true
2007
- end
2008
-
2009
- it 'should remove an agent properly' do
2010
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
2011
- mock_call.should_receive(:execute).once.with('RemoveQueueMember', 'FOO', 'Agent/Tom')
2012
- mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "REMOVED"
2013
- mock_call.queue('FOO').agents.first.remove!.should be true
2014
- end
2015
-
2016
- it 'should remove an agent properly' do
2017
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
2018
- mock_call.should_receive(:execute).once.with('RemoveQueueMember', 'FOO', 'Agent/Tom')
2019
- mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOTINQUEUE"
2020
- mock_call.queue('FOO').agents.first.remove!.should be false
2021
- end
2022
-
2023
- it "should raise a QueueDoesNotExistError when removing an agent from a queue that doesn't exist" do
2024
- mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(cool_people)").and_return "Agent/ZeroCool"
2025
- mock_call.should_receive(:execute).once.with("RemoveQueueMember", "cool_people", "Agent/ZeroCool")
2026
- mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOSUCHQUEUE"
2027
- the_following_code {
2028
- mock_call.queue("cool_people").agents.first.remove!
2029
- }.should raise_error Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
2030
- end
2031
-
2032
- it "should log an agent in properly with no agent id given" do
2033
- mock_call.should_receive(:execute).once.with('AgentLogin', nil, 's')
2034
- mock_call.queue('barrel_o_agents').agents.login!
2035
- end
2036
-
2037
- it 'should remove "Agent/" before the agent ID given if necessary when logging an agent in' do
2038
- mock_call.should_receive(:execute).once.with('AgentLogin', '007', 's')
2039
- mock_call.queue('barrel_o_agents').agents.login! 'Agent/007'
2040
-
2041
- mock_call.should_receive(:execute).once.with('AgentLogin', '007', 's')
2042
- mock_call.queue('barrel_o_agents').agents.login! '007'
2043
- end
2044
-
2045
- it 'should add an agent silently properly' do
2046
- mock_call.should_receive(:execute).once.with('AgentLogin', '007', '')
2047
- mock_call.queue('barrel_o_agents').agents.login! 'Agent/007', :silent => false
2048
-
2049
- mock_call.should_receive(:execute).once.with('AgentLogin', '008', 's')
2050
- mock_call.queue('barrel_o_agents').agents.login! 'Agent/008', :silent => true
2051
- end
2052
-
2053
- it 'logging an agent in should raise an ArgumentError is unrecognized arguments are given' do
2054
- the_following_code {
2055
- mock_call.queue('ohai').agents.login! 1,2,3,4,5
2056
- }.should raise_error ArgumentError
2057
-
2058
- the_following_code {
2059
- mock_call.queue('lols').agents.login! 1337, :sssssilent => false
2060
- }.should raise_error ArgumentError
2061
-
2062
- the_following_code {
2063
- mock_call.queue('qwerty').agents.login! 777, 6,5,4,3,2,1, :wee => :wee
2064
- }.should raise_error ArgumentError
2065
- end
2066
-
2067
- end
2068
-
2069
- describe 'the menu() method' do
2070
-
2071
- include DialplanCommandTestHelpers
2072
-
2073
- it "should instantiate a new Menu object with only the Hash given as menu() options" do
2074
- args = [1,2,3,4,5, {:timeout => 1.year, :tries => (1.0/0.0)}]
2075
-
2076
- flexmock(Adhearsion::VoIP::Menu).should_receive(:new).once.
2077
- with(args.last).and_throw(:instantiating_menu!)
2078
-
2079
- should_throw(:instantiating_menu!) { mock_call.menu(*args) }
2080
- end
2081
-
2082
- it "should jump to a context when a timeout is encountered and there is at least one exact match" do
2083
- pbx_should_respond_with_successful_background_response ?5.ord
2084
- pbx_should_respond_with_successful_background_response ?4.ord
2085
- pbx_should_respond_with_a_wait_for_digit_timeout
2086
-
2087
- context_named_main = Adhearsion::DialPlan::DialplanContextProc.new(:main) { throw :inside_main! }
2088
- context_named_other = Adhearsion::DialPlan::DialplanContextProc.new(:other) { throw :inside_other! }
2089
- flexmock(mock_call).should_receive(:main).once.and_return(context_named_main)
2090
- flexmock(mock_call).should_receive(:other).never
2091
-
2092
- should_pass_control_to_a_context_that_throws :inside_main! do
2093
- mock_call.menu do |link|
2094
- link.main 54
2095
- link.other 543
2096
- end
2097
- end
2098
- 3.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"' }
2099
- end
2100
-
2101
- it "when the 'extension' variable is changed, it should be an instance of PhoneNumber" do
2102
- pbx_should_respond_with_successful_background_response ?5.ord
2103
- foobar_context = Adhearsion::DialPlan::DialplanContextProc.new(:foobar) { throw :foobar! }
2104
- mock_call.should_receive(:foobar).once.and_return foobar_context
2105
- should_pass_control_to_a_context_that_throws :foobar! do
2106
- mock_call.menu do |link|
2107
- link.foobar 5
2108
- end
2109
- end
2110
- 5.should === mock_call.extension
2111
- mock_call.extension.__real_string.should == "5"
2112
- pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"'
2113
- end
2114
-
2115
- end
2116
-
2117
- describe 'the Menu class' do
2118
-
2119
- include DialplanCommandTestHelpers
2120
-
2121
- it "should yield a MenuBuilder when instantiated" do
2122
- lambda {
2123
- Adhearsion::VoIP::Menu.new do |block_argument|
2124
- block_argument.should be_a_kind_of Adhearsion::VoIP::MenuBuilder
2125
- throw :inside_block
2126
- end
2127
- }.should throw_symbol :inside_block
2128
- end
2129
-
2130
- it "should invoke wait_for_digit instead of interruptible_play when no sound files are given" do
2131
- mock_call.should_receive(:wait_for_digit).once.with(5).and_return '#'
2132
- mock_call.menu { |link| link.does_not_match 3 }
2133
- end
2134
-
2135
- it 'should invoke interruptible_play when sound files are given only for the first digit' do
2136
- sound_files = %w[i like big butts and i cannot lie]
2137
- timeout = 1337
2138
-
2139
- mock_call.should_receive(:interruptible_play).once.with(*sound_files).and_return nil
2140
- mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil
2141
-
2142
- mock_call.menu(sound_files, :timeout => timeout) { |link| link.qwerty 12345 }
2143
- end
2144
-
2145
- it 'if the call to interruptible_play receives a timeout, it should execute wait_for_digit with the timeout given' do
2146
- sound_files = %w[i like big butts and i cannot lie]
2147
- timeout = 987
2148
-
2149
- mock_call.should_receive(:interruptible_play).once.with(*sound_files).and_return nil
2150
- mock_call.should_receive(:wait_for_digit).with(timeout).and_return
2151
-
2152
- mock_call.menu(sound_files, :timeout => timeout) { |link| link.foobar 911 }
2153
- end
2154
-
2155
- it "should work when no files are given to be played and a timeout is reached on the first digit" do
2156
- timeout = 12
2157
- [:on_premature_timeout, :on_failure].each do |usage_case|
2158
- should_throw :got_here! do
2159
- mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil # Simulates timeout
2160
- mock_call.menu :timeout => timeout do |link|
2161
- link.foobar 0
2162
- link.__send__(usage_case) { throw :got_here! }
2163
- end
2164
- end
2165
- end
2166
- end
2167
-
2168
- it "should default the timeout to five seconds" do
2169
- mock_call.should_receive(:wait_for_digit).once.with(5).and_return nil
2170
- mock_call.menu { |link| link.foobar 22 }
2171
- end
2172
-
2173
- it "when matches fail due to timeouts, the menu should repeat :tries times" do
2174
- tries, times_timed_out = 10, 0
2175
-
2176
- tries.times do
2177
- pbx_should_respond_with_successful_background_response ?4.ord
2178
- pbx_should_respond_with_successful_background_response ?0.ord
2179
- pbx_should_respond_with_a_wait_for_digit_timeout
2180
- end
2181
-
2182
- should_throw :inside_failure_callback do
2183
- mock_call.menu :tries => tries do |link|
2184
- link.pattern_longer_than_our_test_input 400
2185
- link.on_premature_timeout { times_timed_out += 1 }
2186
- link.on_invalid { raise "should never get here!" }
2187
- link.on_failure { throw :inside_failure_callback }
2188
- end
2189
- end
2190
- times_timed_out.should be tries
2191
- 30.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"' }
2192
- end
2193
-
2194
- it "when matches fail due to invalid input, the menu should repeat :tries times" do
2195
- tries = 10
2196
- times_invalid = 0
2197
-
2198
- tries.times do
2199
- pbx_should_respond_with_successful_background_response ?0.ord
2200
- end
2201
-
2202
- should_throw :inside_failure_callback do
2203
- mock_call.menu :tries => tries do |link|
2204
- link.be_leet 1337
2205
- link.on_premature_timeout { raise "should never get here!" }
2206
- link.on_invalid { times_invalid += 1 }
2207
- link.on_failure { throw :inside_failure_callback }
2208
- end
2209
- end
2210
- times_invalid.should be tries
2211
- 10.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"' }
2212
- end
2213
-
2214
- it "invoke on_invalid callback when an invalid extension was entered" do
2215
- pbx_should_respond_with_successful_background_response ?5.ord
2216
- pbx_should_respond_with_successful_background_response ?5.ord
2217
- pbx_should_respond_with_successful_background_response ?5.ord
2218
- should_throw :inside_invalid_callback do
2219
- mock_call.menu do |link|
2220
- link.onetwothree 123
2221
- link.on_invalid { throw :inside_invalid_callback }
2222
- end
2223
- end
2224
- pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"'
2225
- end
2226
-
2227
- it "invoke on_premature_timeout when a timeout is encountered" do
2228
- pbx_should_respond_with_successful_background_response ?9.ord
2229
- pbx_should_respond_with_a_wait_for_digit_timeout
2230
-
2231
- should_throw :inside_timeout do
2232
- mock_call.menu :timeout => 1 do |link|
2233
- link.something 999
2234
- link.on_premature_timeout { throw :inside_timeout }
2235
- end
2236
- end
2237
- 2.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "1000"' }
2238
- end
2239
-
2240
- end
2241
-
2242
- describe "the Menu class's high-level judgment" do
2243
-
2244
- include DialplanCommandTestHelpers
2245
-
2246
- it "should match things in ambiguous ranges properly" do
2247
- pbx_should_respond_with_successful_background_response ?1.ord
2248
- pbx_should_respond_with_successful_background_response ?1.ord
2249
- pbx_should_respond_with_successful_background_response ?1.ord
2250
- pbx_should_respond_with_a_wait_for_digit_timeout
2251
-
2252
- main_context = Adhearsion::DialPlan::DialplanContextProc.new(:main) { throw :got_here! }
2253
- mock_call.should_receive(:main).and_return main_context
2254
-
2255
- should_pass_control_to_a_context_that_throws :got_here! do
2256
- mock_call.menu do |link|
2257
- link.blah 1
2258
- link.main 11..11111
2259
- end
2260
- end
2261
- 111.should === mock_call.extension
2262
- 4.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"' }
2263
- end
2264
-
2265
- it 'should match things in a range when there are many other non-matching patterns' do
2266
- pbx_should_respond_with_successful_background_response ?9.ord
2267
- pbx_should_respond_with_successful_background_response ?9.ord
2268
- pbx_should_respond_with_successful_background_response ?5.ord
2269
-
2270
- conferences_context = Adhearsion::DialPlan::DialplanContextProc.new(:conferences) { throw :got_here! }
2271
- mock_call.should_receive(:conferences).and_return conferences_context
2272
-
2273
- should_pass_control_to_a_context_that_throws :got_here! do
2274
- mock_call.menu do |link|
2275
- link.sales 1
2276
- link.tech_support 2
2277
- link.finance 3
2278
- link.conferences 900..999
2279
- end
2280
- end
2281
- 3.times { pbx_should_have_been_sent 'WAIT FOR DIGIT "5000"' }
2282
- end
2283
-
2284
- end
2285
-
2286
- describe 'the MenuBuilder' do
2287
-
2288
- include MenuBuilderTestHelper
2289
-
2290
- attr_reader :builder
2291
- before(:each) do
2292
- @builder = Adhearsion::VoIP::MenuBuilder.new
2293
- end
2294
-
2295
- it "should convert each pattern given to it into a MatchCalculator instance" do
2296
- builder.tap do |link|
2297
- link.foo 1,2,3
2298
- link.bar "4", "5", 6
2299
- end
2300
-
2301
- builder.weighted_match_calculators.size.should == 6
2302
- builder.weighted_match_calculators.each do |match_calculator|
2303
- match_calculator.should be_a_kind_of Adhearsion::VoIP::MatchCalculator
2304
- end
2305
- end
2306
-
2307
- it "conflicting ranges" do
2308
- builder.tap do |link|
2309
- link.hundreds 100...200
2310
- link.thousands 1_000...2_000
2311
- link.tenthousands 10_000...20_000
2312
- end
2313
-
2314
- builder_should_match_with_these_quantities_of_calculated_matches \
2315
- 1 => { :exact_match_count => 0, :potential_match_count => 11100 },
2316
- 10 => { :exact_match_count => 0, :potential_match_count => 1110 },
2317
- 100 => { :exact_match_count => 1, :potential_match_count => 110 },
2318
- 1_000 => { :exact_match_count => 1, :potential_match_count => 10 },
2319
- 10_000 => { :exact_match_count => 1, :potential_match_count => 0 },
2320
- 100_000 => { :exact_match_count => 0, :potential_match_count => 0 }
2321
-
2322
- end
2323
-
2324
- it 'a String query ran against multiple Numeric patterns and a range' do
2325
- builder.tap do |link|
2326
- link.sales 1
2327
- link.tech_support 2
2328
- link.finance 3
2329
- link.conferences 900..999
2330
- end
2331
- match = builder.calculate_matches_for "995"
2332
- require 'pp'
2333
- # pp match
2334
- match.potential_match?.should_not be true
2335
- match.exact_match?.should be true
2336
- match.actual_exact_matches.should == ["995"]
2337
- end
2338
-
2339
- it "multiple patterns given at once" do
2340
- builder.tap do |link|
2341
- link.multiple_patterns 1,2,3,4,5,6,7,8,9
2342
- link.multiple_patterns 100..199, 200..299, 300..399, 400..499, 500..599,
2343
- 600..699, 700..799, 800..899, 900..999
2344
- end
2345
- 1.upto 9 do |num|
2346
- builder.calculate_matches_for(num).tap do |matches_of_num|
2347
- matches_of_num.potential_match_count.should == 100
2348
- matches_of_num.exact_match_count.should == 1
2349
- end
2350
- builder.calculate_matches_for((num * 100) + 5).tap do |matches_of_num|
2351
- matches_of_num.potential_match_count.should == 0
2352
- matches_of_num.exact_match_count.should == 1
2353
- end
2354
- end
2355
- end
2356
-
2357
- it "numeric literals that don't match but ultimately would" do
2358
- builder.tap do |link|
2359
- link.nineninenine 999
2360
- link.shouldnt_match 4444
2361
- end
2362
- builder.calculate_matches_for(9).potential_match_count.should == 1
2363
- end
2364
-
2365
- it "three fixnums that obviously don't conflict" do
2366
- builder.tap do |link|
2367
- link.one 1
2368
- link.two 2
2369
- link.three 3
2370
- end
2371
- [[1,2,3,4,'#'], [1,1,1,0,0]].transpose.each do |(input,expected_matches)|
2372
- matches = builder.calculate_matches_for input
2373
- matches.exact_match_count.should be expected_matches
2374
- end
2375
- end
2376
-
2377
- it "numerical digits mixed with special digits" do
2378
- builder.tap do |link|
2379
- link.one '5*11#3'
2380
- link.two '5***'
2381
- link.three '###'
2382
- end
2383
-
2384
- builder_should_match_with_these_quantities_of_calculated_matches \
2385
- '5' => { :potential_match_count => 2, :exact_match_count => 0 },
2386
- '*' => { :potential_match_count => 0, :exact_match_count => 0 },
2387
- '5**' => { :potential_match_count => 1, :exact_match_count => 0 },
2388
- '5*1' => { :potential_match_count => 1, :exact_match_count => 0 },
2389
- '5*11#3' => { :potential_match_count => 0, :exact_match_count => 1 },
2390
- '5*11#4' => { :potential_match_count => 0, :exact_match_count => 0 },
2391
- '5***' => { :potential_match_count => 0, :exact_match_count => 1 },
2392
- '###' => { :potential_match_count => 0, :exact_match_count => 1 },
2393
- '##*' => { :potential_match_count => 0, :exact_match_count => 0 }
2394
-
2395
- end
2396
-
2397
- it 'a Fixnum exact match conflicting with a Range that would ultimately match' do
2398
- builder.tap do |link|
2399
- link.single_digit 1
2400
- link.range 100..200
2401
- end
2402
- matches = builder.calculate_matches_for 1
2403
- matches.potential_match_count.should == 100
2404
- end
2405
-
2406
- end
2407
-
2408
- describe 'say_digits command' do
2409
- include DialplanCommandTestHelpers
2410
- it 'Can execute the saydigits application using say_digits' do
2411
- digits = "12345"
2412
- pbx_should_respond_with_success
2413
- mock_call.say_digits digits
2414
- pbx_was_asked_to_execute "saydigits", digits
2415
- end
2416
-
2417
- it 'Digits that include pound, star, and minus are considered valid' do
2418
- digits = "1#2*3-4"
2419
- mock_call.should_receive(:execute).once.with("saydigits", digits)
2420
- mock_call.say_digits digits
2421
- end
2422
-
2423
- it 'Cannot pass non-integers into say_digits. Will raise an ArgumentError' do
2424
- the_following_code {
2425
- mock_call.say_digits 'abc'
2426
- }.should raise_error(ArgumentError)
2427
-
2428
- the_following_code {
2429
- mock_call.say_digits '1.20'
2430
- }.should raise_error(ArgumentError)
2431
- end
2432
-
2433
- it 'Digits that start with a 0 are considered valid and parsed properly' do
2434
- digits = "0123"
2435
- mock_call.should_receive(:execute).once.with("saydigits", digits)
2436
- mock_call.say_digits digits
2437
- end
2438
-
2439
- end
2440
-
2441
- describe 'the enable_feature command' do
2442
-
2443
- include DialplanCommandTestHelpers
2444
-
2445
- it 'it should fetch the variable for DYNAMIC_FEATURES at first' do
2446
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_throw :got_variable
2447
- should_throw :got_variable do
2448
- mock_call.enable_feature :foobar
2449
- end
2450
- end
2451
-
2452
- it 'should check Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS mapping for configuration setters' do
2453
- feature_name = :attended_transfer
2454
-
2455
- assertion = lambda do |arg|
2456
- arg.should == :this_is_the_right_arg
2457
- throw :inside_assertion!
2458
- end
2459
-
2460
- # I had to do this ugly hack because of a bug in Flexmock which prevented me from mocking out Hash#[] :(
2461
- old_hash_feature_extension = Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name]
2462
- begin
2463
- Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = assertion
2464
- # </uglyhack>
2465
- should_throw :inside_assertion! do
2466
- mock_call.enable_feature(feature_name, :this_is_the_right_arg)
2467
- end
2468
- ensure
2469
- Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = old_hash_feature_extension
2470
- end
2471
- end
2472
-
2473
- it 'should separate enabled features with a "#"' do
2474
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return("one")
2475
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'one#bar')
2476
- mock_call.enable_feature "bar"
2477
- end
2478
-
2479
- it 'should not add duplicate enabled dynamic features' do
2480
- mock_call.should_receive(:variable).once.and_return('eins#zwei')
2481
- mock_call.enable_feature "eins"
2482
- end
2483
-
2484
- it 'should raise an ArgumentError if optional options are given when DYNAMIC_FEATURE_EXTENSIONS does not have a key for the feature name' do
2485
- the_following_code {
2486
- mock_call.enable_feature :this_is_not_recognized, :these_features => "are not going to be recognized"
2487
- }.should raise_error ArgumentError
2488
- end
2489
-
2490
- it 'enabling :attended_transfer should actually enable the atxfer feature' do
2491
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return ''
2492
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
2493
- mock_call.enable_feature :attended_transfer
2494
- end
2495
-
2496
- it 'the :context optional option when enabling :attended_transfer should set the TRANSFER_CONTEXT variable to the String supplied as a Hash value' do
2497
- context_name = "direct_dial"
2498
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return ''
2499
- mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
2500
- mock_call.should_receive(:variable).once.with("TRANSFER_CONTEXT" => context_name)
2501
- mock_call.enable_feature :attended_transfer, :context => context_name
2502
- end
2503
-
2504
- it 'enabling :attended_transfer should not add a duplicate if atxfer has been enabled, but it should still set the TRANSFER_CONTEXT variable' do
2505
- context_name = 'blah'
2506
- mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'atxfer'
2507
- mock_call.should_receive(:variable).once.with('TRANSFER_CONTEXT' => context_name)
2508
- mock_call.enable_feature :attended_transfer, :context => context_name
2509
- end
2510
-
2511
- end
2512
-
2513
- describe 'the disable_feature command' do
2514
-
2515
- include DialplanCommandTestHelpers
2516
-
2517
- it "should properly remove the feature from the DYNAMIC_FEATURES variable" do
2518
- mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'foobar#qaz'
2519
- mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES' => 'qaz')
2520
- mock_call.disable_feature "foobar"
2521
- end
2522
-
2523
- it "should not re-set the variable if the feature wasn't enabled in the first place" do
2524
- mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'atxfer'
2525
- mock_call.should_receive(:variable).never
2526
- mock_call.disable_feature "jay"
2527
- end
2528
-
2529
- end
2530
-
2531
- describe 'jump_to command' do
2532
-
2533
- include DialplanCommandTestHelpers
2534
-
2535
- it 'when given a DialplanContextProc as the only argument, it should raise a ControlPassingException with that as the target' do
2536
- # Having to do this ugly hack because I can't do anything with the exception once I set up an expectation with it normally.
2537
- dialplan_context = Adhearsion::DialPlan::DialplanContextProc.new("my_context") {}
2538
- should_throw :finishing_the_rescue_block do
2539
- begin
2540
- mock_call.jump_to dialplan_context
2541
- rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => cpe
2542
- cpe.target.should be dialplan_context
2543
- throw :finishing_the_rescue_block
2544
- end
2545
- end
2546
- end
2547
-
2548
- it 'when given a String, it should perform a lookup of the context name' do
2549
- context_name = 'cool_context'
2550
- mock_call.should_receive(context_name).once.and_throw :found_context!
2551
- should_throw :found_context! do
2552
- mock_call.jump_to context_name
2553
- end
2554
- end
2555
-
2556
- it 'when given a Symbol, it should perform a lookup of the context name' do
2557
- context_name = :cool_context
2558
- mock_call.should_receive(context_name).once.and_throw :found_context!
2559
- should_throw :found_context! do
2560
- mock_call.jump_to context_name
2561
- end
2562
- end
2563
-
2564
- it "a clearly invalid context name should raise a ContextNotFoundException" do
2565
- bad_context_name = ' ZOMGS this is A REALLY! STUPID context name . wtf were you thinking?!'
2566
- the_following_code {
2567
- mock_call.jump_to bad_context_name
2568
- }.should raise_error Adhearsion::VoIP::DSL::Dialplan::ContextNotFoundException
2569
- end
2570
-
2571
- it 'when given an :extension override, the new value should be boxed in a PhoneNumber' do
2572
- my_context = Adhearsion::DialPlan::DialplanContextProc.new("my_context") {}
2573
- begin
2574
- mock_call.jump_to my_context, :extension => 1337
2575
- rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException
2576
- # Eating this exception
2577
- end
2578
- mock_call.extension.__real_num.should == 1337
2579
- end
2580
-
2581
- it 'other overrides should be simply metadef()d' do
2582
- test_context = Adhearsion::DialPlan::DialplanContextProc.new("test_context") {}
2583
- begin
2584
- mock_call.jump_to test_context, :caller_id => 1_444_555_6666
2585
- rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException
2586
- # Eating this exception
2587
- end
2588
- mock_call.caller_id.should == 1_444_555_6666
2589
- end
2590
-
2591
- end
2592
-
2593
- describe "get variable command" do
2594
- include DialplanCommandTestHelpers
2595
-
2596
- it "Getting a variable that isn't set returns nothing" do
2597
- pbx_should_respond_with "200 result=0"
2598
- mock_call.get_variable('OMGURFACE').should be nil
2599
- pbx_should_have_been_sent 'GET VARIABLE "OMGURFACE"'
2600
- end
2601
-
2602
- it 'An empty variable should return an empty String' do
2603
- pbx_should_respond_with_value ""
2604
- mock_call.get_variable('kablamm').should == ""
2605
- pbx_should_have_been_sent 'GET VARIABLE "kablamm"'
2606
- end
2607
-
2608
- it "Getting a variable that is set returns its value" do
2609
- unique_id = "1192470850.1"
2610
- pbx_should_respond_with_value unique_id
2611
- variable_value_returned = mock_call.get_variable('UNIQUEID')
2612
- variable_value_returned.should == unique_id
2613
- pbx_should_have_been_sent 'GET VARIABLE "UNIQUEID"'
2614
- end
2615
- end
2616
-
2617
- describe "duration_of command" do
2618
- include DialplanCommandTestHelpers
2619
-
2620
- it "Duration of must take a block" do
2621
- the_following_code {
2622
- mock_call.duration_of
2623
- }.should raise_error(LocalJumpError)
2624
- end
2625
-
2626
- it "Passed block to duration of is actually executed" do
2627
- the_following_code {
2628
- mock_call.duration_of {
2629
- throw :inside_duration_of
2630
- }
2631
- }.should throw_symbol :inside_duration_of
2632
- end
2633
-
2634
- it "Duration of block is returned" do
2635
- start_time = Time.parse('9:25:00')
2636
- end_time = Time.parse('9:25:05')
2637
- expected_duration = end_time - start_time
2638
-
2639
- flexmock(Time).should_receive(:now).twice.and_return(start_time, end_time)
2640
- duration = mock_call.duration_of {
2641
- # This doesn't matter
2642
- }
2643
-
2644
- duration.should == expected_duration
2645
- end
2646
- end
2647
-
2648
- describe "Dial command" do
2649
- include DialplanCommandTestHelpers
2650
-
2651
- it "should set the caller id if the caller_id option is specified" do
2652
- does_not_read_data_back
2653
- mock_call.should_receive(:set_caller_id_number).once
2654
- mock_call.dial 123, :caller_id => "1234678901"
2655
- pbx_should_have_been_sent 'EXEC Dial "123"|""|""'
2656
- end
2657
-
2658
- it 'should raise an exception when unknown hash key arguments are given to it' do
2659
- the_following_code {
2660
- mock_call.dial 123, :asjndfhasndfahsbdfbhasbdfhabsd => "asbdfhabshdfbajhshfbajsf"
2661
- }.should raise_error ArgumentError
2662
- end
2663
-
2664
- it 'should set the caller ID name when given the :name hash key argument' do
2665
- does_not_read_data_back
2666
- name = "Jay Phillips"
2667
- mock_call.should_receive(:set_caller_id_name).once.with(name)
2668
- mock_call.dial "BlahBlahBlah", :name => name
2669
- pbx_should_have_been_sent 'EXEC Dial "BlahBlahBlah"|""|""'
2670
- end
2671
-
2672
- it "should raise an exception when a non-numerical caller_id is specified" do
2673
- the_following_code {
2674
- mock_call.dial 911, :caller_id => "zomgz"
2675
- }.should raise_error ArgumentError
2676
- end
2677
-
2678
- it "should not raise an exception when a caller_id is specified in E.164 format (with '+' sign)" do
2679
- the_following_code {
2680
- mock_call.dial 911, :caller_id => "+123456789"
2681
- }.should_not raise_error ArgumentError
2682
- pbx_should_have_been_sent 'SET VARIABLE "CALLERID(num)" "+123456789"'
2683
- end
2684
-
2685
- it 'should pass the value of the :confirm key to dial_macro_option_compiler()' do
2686
- does_not_read_data_back
2687
- value_of_confirm_key = {:play => "ohai", :timeout => 30}
2688
- mock_call.should_receive(:dial_macro_option_compiler).once.with value_of_confirm_key
2689
- mock_call.dial 123, :confirm => value_of_confirm_key
2690
- pbx_should_have_been_sent 'EXEC Dial "123"|""|""'
2691
- end
2692
-
2693
- it "should add the return value of dial_macro_option_compiler to the :options key's value given to the dial command" do
2694
- channel = "SIP/1337"
2695
- macro_arg = "THISSHOULDGETPASSEDTOASTERISK"
2696
- timeout = 10
2697
- options = 'hH'
2698
-
2699
- mock_call.should_receive(:dial_macro_option_compiler).once.and_return macro_arg
2700
- mock_call.should_receive(:execute).with('Dial', channel, timeout, options + macro_arg)
2701
- mock_call.dial channel, :for => timeout, :confirm => true, :options => options
2702
- end
2703
-
2704
- it 'should add the return value of dial_macro_option_compiler to the options field when NO :options are given' do
2705
- channel = "SIP/1337"
2706
- macro_arg = "THISSHOULDGETPASSEDTOASTERISK"
2707
- timeout = 10
2708
- options = 'hH'
2709
-
2710
- mock_call.should_receive(:dial_macro_option_compiler).once.and_return macro_arg
2711
- mock_call.should_receive(:execute).with('Dial', channel, timeout, options + macro_arg)
2712
- mock_call.dial channel, :for => timeout, :confirm => true, :options => options
2713
- end
2714
-
2715
- end
2716
-
2717
- describe "The Dial command's :confirm option setting builder" do
2718
-
2719
- include DialplanCommandTestHelpers
2720
-
2721
- attr_reader :formatter
2722
- before :each do
2723
- @formatter = mock_call.method :dial_macro_option_compiler
2724
- end
2725
-
2726
- it 'should allow passing in the :confirm named argument with true' do
2727
- the_following_code {
2728
- formatter.call true
2729
- }.should_not raise_error ArgumentError
2730
- end
2731
-
2732
- it 'should separate :play options with "++"' do
2733
- sound_files = *1..10
2734
- formatter.call(:play => sound_files).should include sound_files.join('++')
2735
- end
2736
-
2737
- it 'should raise an ArgumentError if an invalid Hash key is given' do
2738
- the_following_code {
2739
- formatter.call :this_symbol_is_not_valid => 123
2740
- }.should raise_error ArgumentError
2741
- end
2742
-
2743
- it "should raise an ArgumentError if the argument's class is not recognized" do
2744
- the_following_code {
2745
- formatter.call Time.now # Time is an example strange case
2746
- }.should raise_error ArgumentError
2747
- end
2748
-
2749
- it 'should return the contents within a M() Dial argument' do
2750
- formatter.call(true).should =~ /^M\(.+\)$/
2751
- end
2752
-
2753
- it 'should replace the default macro name when given the :macro options' do
2754
- macro_name = "ivegotalovelybunchofcoconuts"
2755
- formatter.call(:macro => macro_name).starts_with?("M(#{macro_name}").should be true
2756
- end
2757
-
2758
- it 'should allow a symbol macro name' do
2759
- the_following_code {
2760
- formatter.call(:macro => :foo)
2761
- }.should_not raise_error ArgumentError
2762
- end
2763
-
2764
- it 'should only allow alphanumeric and underscores in the macro name' do
2765
- bad_options = ["this has a space", "foo,bar", 'exists?', 'x^z', '', "!&@&*^!@"]
2766
- bad_options.each do |bad_option|
2767
- the_following_code {
2768
- formatter.call(:macro => bad_option)
2769
- }.should raise_error ArgumentError
2770
- end
2771
- end
2772
-
2773
- it 'should confirm :timeout => :none to 0' do
2774
- formatter.call(:timeout => :none).should include "timeout:0"
2775
- end
2776
-
2777
- it 'should separate the macro name and the arguments with a caret (^)' do
2778
- formatter.call(:macro => "jay").should =~ /M\(jay\^.+/
2779
- end
2780
-
2781
- it 'should raise an ArgumentError if a caret existed anywhere in the resulting String' do
2782
- # FIXME: Duplicate hash key
2783
- bad_options = [{:play => "foo^bar", :key => "^", :play => ["hello-world", 'lol^cats']}]
2784
- bad_options.each do |bad_option|
2785
- the_following_code {
2786
- formatter.call(bad_option)
2787
- }.should raise_error ArgumentError
2788
- end
2789
- end
2790
-
2791
- it 'should raise an ArgumentError if the :key is not [0-9#*]' do
2792
- bad_options = %w[& A $ @ . )]
2793
- bad_options.each do |bad_option|
2794
- the_following_code {
2795
- formatter.call :key => bad_option
2796
- }.should raise_error ArgumentError
2797
- end
2798
- end
2799
-
2800
- it 'should raise an ArgumentError if the key is longer than one digit' do
2801
- the_following_code {
2802
- formatter.call :key => "55"
2803
- }.should raise_error ArgumentError
2804
- end
2805
-
2806
- it 'should raise an ArgumentError if the timeout is not numeric and not :none' do
2807
- bad_options = [:nonee, Time.now, method(:inspect)]
2808
- bad_options.each do |bad_option|
2809
- the_following_code {
2810
- formatter.call bad_option
2811
- }.should raise_error ArgumentError
2812
- end
2813
- end
2814
-
2815
- it 'should support passing a String argument as a timeout' do
2816
- the_following_code {
2817
- formatter.call :timeout => "123"
2818
- }.should_not raise_error ArgumentError
2819
- end
2820
-
2821
- it 'should raise an ArgumentError if given a Float' do
2822
- the_following_code {
2823
- formatter.call :timeout => 100.0012
2824
- }.should raise_error ArgumentError
2825
- end
2826
-
2827
- it 'should allow passing a ActiveSupport::Duration to :timeout' do
2828
- the_following_code {
2829
- formatter.call :timeout => 3.minutes
2830
- }.should_not raise_error ArgumentError
2831
- end
2832
-
2833
- end
2834
-
2835
- describe 'the dtmf command' do
2836
-
2837
- include DialplanCommandTestHelpers
2838
-
2839
- it 'should send the proper AGI command' do
2840
- does_not_read_data_back
2841
- digits = '8404#4*'
2842
- mock_call.dtmf digits
2843
- pbx_should_have_been_sent "EXEC SendDTMF \"#{digits}\""
2844
- end
2845
- end
2846
-
2847
- describe "the last_dial_status command and family" do
2848
-
2849
- include DialplanCommandTestHelpers
2850
-
2851
- it 'should convert common DIALSTATUS variables to their appropriate symbols' do
2852
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
2853
- mock_call.last_dial_status.should be :answered
2854
-
2855
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CONGESTION')
2856
- mock_call.last_dial_status.should be :congested
2857
-
2858
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("BUSY")
2859
- mock_call.last_dial_status.should be :busy
2860
-
2861
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("CANCEL")
2862
- mock_call.last_dial_status.should be :cancelled
2863
-
2864
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("NOANSWER")
2865
- mock_call.last_dial_status.should be :unanswered
2866
-
2867
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("CHANUNAVAIL")
2868
- mock_call.last_dial_status.should be :channel_unavailable
2869
-
2870
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("THISISNOTVALID")
2871
- mock_call.last_dial_status.should be :unknown
2872
- end
2873
-
2874
- it 'last_dial_successful? should return true if last_dial_status == :answered' do
2875
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
2876
- mock_call.last_dial_successful?.should be true
2877
-
2878
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CHANUNAVAIL')
2879
- mock_call.last_dial_successful?.should be false
2880
- end
2881
-
2882
- it 'last_dial_unsuccessful? should be the opposite of last_dial_successful?' do
2883
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
2884
- mock_call.last_dial_unsuccessful?.should be false
2885
-
2886
- mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CHANUNAVAIL')
2887
- mock_call.last_dial_unsuccessful?.should be true
2888
- end
2889
-
2890
- it 'last_dial_status should not blow up if variable() returns nil. it should return :cancelled' do
2891
- the_following_code {
2892
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return nil
2893
- mock_call.last_dial_status.should be :cancelled
2894
-
2895
- mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return nil
2896
- mock_call.last_dial_successful?.should be false
2897
- }.should_not raise_error
2898
- end
2899
-
2900
- end
2901
-
2902
- describe "set_caller_id_number command" do
2903
- include DialplanCommandTestHelpers
2904
-
2905
- it "should encapsulate the number with quotes" do
2906
- caller_id = "14445556666"
2907
- mock_call.should_receive(:raw_response).once.with(%(SET VARIABLE "CALLERID(num)" "#{caller_id}")).and_return true
2908
- mock_call.send(:set_caller_id_number, caller_id)
2909
- end
2910
- end
2911
-
2912
- describe 'set_caller_id_name command' do
2913
- include DialplanCommandTestHelpers
2914
-
2915
- it "should wrap the name in quotes" do
2916
- name = "Jay Phillips"
2917
- mock_call.should_receive(:raw_response).once.with(%(SET VARIABLE "CALLERID(name)" "#{name}")).and_return true
2918
- mock_call.send(:set_caller_id_name, name)
2919
- end
2920
- end
2921
-
2922
- describe "speak command" do
2923
- include DialplanCommandTestHelpers
2924
-
2925
- before :all do
2926
- @speech_engines = Adhearsion::VoIP::Asterisk::Commands::SpeechEngines
2927
- end
2928
-
2929
- it "executes the command SpeechEngine gives it based on the engine name" do
2930
- pbx_should_respond_with_success
2931
- flexmock(@speech_engines).should_receive(:cepstral).once
2932
- mock_call.speak "Spoken text doesn't matter", :engine => :cepstral
2933
- end
2934
-
2935
- it "raises an InvalidSpeechEngine exception when the engine is 'none'" do
2936
- the_following_code {
2937
- mock_call.speak("o hai!", :engine => :none)
2938
- }.should raise_error @speech_engines::InvalidSpeechEngine
2939
- end
2940
-
2941
- it "should default its engine to :none" do
2942
- the_following_code {
2943
- flexmock(@speech_engines).should_receive(:none).once.
2944
- and_raise(@speech_engines::InvalidSpeechEngine)
2945
- mock_call.speak "ruby ruby ruby ruby!"
2946
- }.should raise_error @speech_engines::InvalidSpeechEngine
2947
- end
2948
-
2949
- it 'should default to a configured TTS engine' do
2950
- Adhearsion::Configuration.configure {|c| c.asterisk.speech_engine = :unimrcp }
2951
- flexmock(@speech_engines).should_receive(:unimrcp).once
2952
- mock_call.speak 'What say you, sir?'
2953
- end
2954
-
2955
- it 'should allow the caller to override the default configured TTS engine' do
2956
- Adhearsion::Configuration.configure {|c| c.asterisk.speech_engine = :unimrcp }
2957
- flexmock(@speech_engines).should_receive(:cepstral).once
2958
- mock_call.speak 'What say you now, sir?', :engine => :cepstral
2959
- end
2960
-
2961
- it 'should default to uninterruptible TTS rendering' do
2962
- flexmock(@speech_engines).should_receive(:cepstral).once.with(mock_call, 'hello', {:interruptible => false})
2963
- mock_call.speak 'hello', :engine => :cepstral
2964
- end
2965
-
2966
- it 'should allow setting TTS rendering interruptible' do
2967
- flexmock(@speech_engines).should_receive(:cepstral).once.with(mock_call, 'hello', {:interruptible => true})
2968
- mock_call.speak 'hello', :engine => :cepstral, :interruptible => true
2969
- end
2970
-
2971
- it "should stringify the text" do
2972
- flexmock(@speech_engines).should_receive(:cepstral).once.with(mock_call, 'hello', {:interruptible => false})
2973
- mock_call.speak :hello, :engine => :cepstral
2974
- end
2975
-
2976
- context "with the engine :cepstral" do
2977
- it "should execute Swift"do
2978
- pbx_should_respond_with_value 0
2979
- mock_call.should_receive(:execute).with('Swift', 'hello')
2980
- @speech_engines.cepstral(mock_call, 'hello')
2981
- @output.read.should == "GET VARIABLE \"SWIFT_DTMF\"\n"
2982
- end
2983
-
2984
- it "should properly escape commas in the TTS string" do
2985
- pbx_should_respond_with_value 0
2986
- mock_call.should_receive(:execute).with('Swift', 'Once\, a long\, long time ago\, ...')
2987
- @speech_engines.cepstral(mock_call, 'Once, a long, long time ago, ...')
2988
- @output.read.should == "GET VARIABLE \"SWIFT_DTMF\"\n"
2989
- end
2990
-
2991
- it "should properly escape double-quotes (for XML) in the TTS string" do
2992
- mock_call.should_receive(:raw_response).once.with('EXEC MRCPSynth "<speak xmlns=\\\\\"http://www.w3.org/2001/10/synthesis\\\\\" version=\\\\\"1.0\\\\\" xml:lang=\\\\\"en-US\\\\\"> <voice name=\\\\\"Paul\\\\\"> <prosody rate=\\\\\"1.0\\\\\">Howdy, stranger. How are you today?</prosody> </voice> </speak>"').and_return pbx_success_response
2993
- @speech_engines.unimrcp(mock_call, '<speak xmlns="http://www.w3.org/2001/10/synthesis" version="1.0" xml:lang="en-US"> <voice name="Paul"> <prosody rate="1.0">Howdy, stranger. How are you today?</prosody> </voice> </speak>')
2994
- end
2995
-
2996
- context "with barge in digits set" do
2997
- it "should return the digit when :interruptible = true" do
2998
- mock_call.should_receive(:execute).once.with('Swift', 'hello', 1, 1).and_return pbx_success_response
2999
- mock_call.should_receive(:get_variable).once.with('SWIFT_DTMF').and_return ?1
3000
- @speech_engines.cepstral(mock_call, 'hello', :interruptible => true).should == ?1
3001
- end
3002
- end
3003
- end
3004
-
3005
- context "with the engine :unimrcp" do
3006
- it "should execute MRCPSynth" do
3007
- pbx_should_respond_with_success
3008
- mock_call.should_receive(:execute).with('MRCPSynth', 'hello').once.and_return pbx_success_response
3009
- @speech_engines.unimrcp(mock_call, 'hello')
3010
- end
3011
-
3012
- context "with barge in digits set" do
3013
- it "should pass the i option for MRCPSynth" do
3014
- mock_call.should_receive(:execute).with('MRCPSynth', 'hello', 'i=any').once.and_return pbx_result_response 0
3015
- @speech_engines.unimrcp(mock_call, 'hello', :interrupt_digits => 'any')
3016
- end
3017
- end
3018
- end
3019
-
3020
- context "with the engine :tropo" do
3021
- it "should execute tropo" do
3022
- pbx_should_respond_with_success
3023
- response = '200 result=' + {:interpretation => '1'}.to_json
3024
- mock_call.should_receive(:raw_response).with(/Ask/i, 'hello').once.and_return response
3025
- @speech_engines.tropo(mock_call, 'hello').should == "1"
3026
- end
3027
-
3028
- context "with :interruptible set to false"do
3029
- it "should pass the :bargein => false option for Tropo Ask" do
3030
- response = '200 result=' + {:interpretation => '1'}.to_json
3031
- mock_call.should_receive(:raw_response).with(/Ask/i, 'hello', {:bargein => false}.to_json).once.and_return response
3032
- @speech_engines.tropo(mock_call, 'hello', :interruptible => false)
3033
- end
3034
- end
3035
- end
3036
-
3037
- it "properly escapes spoken text" do
3038
- pending 'What are the escaping needs?'
3039
- end
3040
- end
3041
-
3042
- describe 'The join command' do
3043
-
3044
- include DialplanCommandTestHelpers
3045
-
3046
- it "should pass the 'd' flag when no options are given" do
3047
- conference_id = "123"
3048
- mock_call.should_receive(:execute).once.with("MeetMe", conference_id, "d", nil)
3049
- mock_call.join conference_id
3050
- end
3051
-
3052
- it "should pass through any given flags with 'd' appended to it if necessary" do
3053
- conference_id, flags = "1000", "zomgs"
3054
- mock_call.should_receive(:execute).once.with("MeetMe", conference_id, flags + "d", nil)
3055
- mock_call.join conference_id, :options => flags
3056
- end
3057
-
3058
- it "should NOT pass the 'd' flag when requiring static conferences" do
3059
- conference_id, options = "1000", {:use_static_conf => true}
3060
- mock_call.should_receive(:execute).once.with("MeetMe", conference_id, "", nil)
3061
- mock_call.join conference_id, options
3062
- end
3063
-
3064
- it "should raise an ArgumentError when the pin is not numerical" do
3065
- the_following_code {
3066
- mock_call.should_receive(:execute).never
3067
- mock_call.join 3333, :pin => "letters are bad, mkay?!1"
3068
- }.should raise_error ArgumentError
3069
- end
3070
-
3071
- it "should strip out illegal characters from a conference name" do
3072
- bizarre_conference_name = "a- bc!d&&e--`"
3073
- normal_conference_name = "abcde"
3074
- mock_call.should_receive(:execute).twice.with("MeetMe", normal_conference_name, "d", nil)
3075
-
3076
- mock_call.join bizarre_conference_name
3077
- mock_call.join normal_conference_name
3078
- end
3079
-
3080
- it "should allow textual conference names" do
3081
- the_following_code {
3082
- mock_call.should_receive(:execute).once.with_any_args
3083
- mock_call.join "david bowie's pants"
3084
- }.should_not raise_error
3085
- end
3086
-
3087
- end
3088
-
3089
-
3090
- describe 'the DialPlan::ConfirmationManager' do
3091
-
3092
- include ConfirmationManagerTestHelper
3093
- include DialplanCommandTestHelpers
3094
-
3095
- attr_reader :example_encoded_hash, :example_encoded_hash_without_macro_name
3096
- before :each do
3097
- @example_encoded_hash_without_macro_name = 'timeout:20!play:foo-bar++qaz_qwerty.gsm!key:#'
3098
- @example_encoded_hash = 'confirm!' + @example_encoded_hash_without_macro_name
3099
- end
3100
-
3101
- it '::decode_hash() should convert the String of key/value escaped pairs into a Hash with Symbol keys when the macro name is not given' do
3102
- Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash).should ==
3103
- {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#'}
3104
- end
3105
-
3106
- it '::decode_hash() should convert the String of key/value escaped pairs into a Hash with Symbol keys when the macro name is not given' do
3107
- Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash_without_macro_name).should ==
3108
- {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#'}
3109
- end
3110
-
3111
- it '::decode_hash() should split the sound files in the :play key to an array by splitting by "++"' do
3112
- decoded_sound_files = Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash)[:play]
3113
- decoded_sound_files.should be_a_kind_of Array
3114
- decoded_sound_files.size.should == 2
3115
- end
3116
-
3117
- it 'a call to a party which is acknowledged with the proper key during the call to interruptible_play' do
3118
- variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
3119
- encoded_variables = {:network_script => encode_hash(variables)}
3120
- io_mock = StringIO.new
3121
-
3122
- mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
3123
- mock_call.should_receive(:variables).once.and_return encoded_variables
3124
-
3125
- sound_files = variables[:play]
3126
-
3127
- manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
3128
-
3129
- flexstub(manager).should_receive(:result_digit_from).and_return ?0.ord
3130
- flexstub(manager).should_receive(:raw_response).and_return nil
3131
-
3132
- flexmock(manager).should_receive(:answer).once
3133
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return '#'
3134
-
3135
- manager.handle
3136
- end
3137
-
3138
- it 'when an timeout is encountered, it should set the MACRO_RESULT variable to CONTINUE' do
3139
- variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
3140
- encoded_variables = {:network_script => encode_hash(variables)}
3141
- io_mock = StringIO.new
3142
-
3143
- mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
3144
- mock_call.should_receive(:variables).once.and_return encoded_variables
3145
-
3146
- sound_files = variables[:play]
3147
-
3148
- manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
3149
-
3150
- flexstub(manager).should_receive(:result_digit_from).and_return ?0.ord
3151
- flexstub(manager).should_receive(:raw_response).and_return nil
3152
-
3153
- flexmock(manager).should_receive(:answer).once
3154
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return nil
3155
- flexmock(manager).should_receive(:wait_for_digit).once.with(20).and_return nil
3156
-
3157
- flexmock(manager).should_receive(:variable).once.with("MACRO_RESULT" => 'CONTINUE')
3158
-
3159
- manager.handle
3160
- end
3161
-
3162
- it 'should wait the :timeout number of seconds if no digit was received when playing the files and continue when the right key is pressed' do
3163
- variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
3164
- encoded_variables = {:network_script => encode_hash(variables)}
3165
- io_mock = StringIO.new
3166
-
3167
- mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
3168
- mock_call.should_receive(:variables).once.and_return encoded_variables
3169
-
3170
- sound_files = variables[:play]
3171
-
3172
- manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
3173
-
3174
- flexstub(manager).should_receive(:result_digit_from).and_return ?0.ord
3175
- flexstub(manager).should_receive(:raw_response).and_return nil
3176
-
3177
- flexmock(manager).should_receive(:answer).once
3178
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return nil
3179
- flexmock(manager).should_receive(:wait_for_digit).once.with(20).and_return '#'
3180
-
3181
- manager.handle
3182
- end
3183
-
3184
- it 'should restart playback if the key received was not recognized' do
3185
- variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '2', :macro => 'confirmer'}
3186
- encoded_variables = {:network_script => encode_hash(variables)}
3187
- io_mock = StringIO.new
3188
-
3189
- mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
3190
- mock_call.should_receive(:variables).once.and_return encoded_variables
3191
-
3192
- sound_files = variables[:play]
3193
-
3194
- manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
3195
-
3196
- flexstub(manager).should_receive(:result_digit_from).and_return ?0.ord
3197
- flexstub(manager).should_receive(:raw_response).and_return nil
3198
-
3199
- flexmock(manager).should_receive(:answer).once
3200
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return '3' # not :key
3201
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return '#' # not :key
3202
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return '1' # not :key
3203
- flexmock(manager).should_receive(:interruptible_play).once.with(*sound_files).and_return '2' # matches :key
3204
-
3205
- flexmock(manager).should_receive(:wait_for_digit).never # We never let it get to the point where it may timeout
3206
- flexmock(manager).should_receive(:variable).never # We succeed by not setting the MACRO_RESULT variable
3207
-
3208
- manager.handle
3209
- end
3210
-
3211
- end
3212
-
3213
- describe 'say_phonetic command' do
3214
- include DialplanCommandTestHelpers
3215
- it 'Can execute the sayphonetic application using say_phonetic' do
3216
- text = 'Say This'
3217
- mock_call.should_receive(:execute).once.with("sayphonetic", text)
3218
- mock_call.say_phonetic text
3219
- end
3220
-
3221
- it 'Can use special characters with say_phonetic' do
3222
- text = '*Say This!*'
3223
- mock_call.should_receive(:execute).once.with("sayphonetic", text)
3224
- mock_call.say_phonetic text
3225
- end
3226
- end
3227
-
3228
- describe 'say_chars command' do
3229
- include DialplanCommandTestHelpers
3230
- it 'Can execute the sayalpha application using say_chars' do
3231
- text = 'ha124d9'
3232
- mock_call.should_receive(:execute).once.with("sayalpha", text)
3233
- mock_call.say_chars text
3234
- end
3235
-
3236
- it 'Can use special characters with say_chars' do
3237
- text = "1a2.#"
3238
- mock_call.should_receive(:execute).once.with("sayalpha", text)
3239
- mock_call.say_chars text
3240
- end
3241
- end