sevenscale-adhearsion 0.7.1000

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 (150) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +456 -0
  3. data/Manifest.txt +149 -0
  4. data/README.txt +6 -0
  5. data/Rakefile +48 -0
  6. data/ahn_generators/component/USAGE +5 -0
  7. data/ahn_generators/component/component_generator.rb +57 -0
  8. data/ahn_generators/component/templates/configuration.rb +0 -0
  9. data/ahn_generators/component/templates/lib/lib.rb.erb +3 -0
  10. data/ahn_generators/component/templates/test/test.rb.erb +12 -0
  11. data/ahn_generators/component/templates/test/test_helper.rb +14 -0
  12. data/app_generators/ahn/USAGE +5 -0
  13. data/app_generators/ahn/ahn_generator.rb +76 -0
  14. data/app_generators/ahn/templates/.ahnrc +12 -0
  15. data/app_generators/ahn/templates/README +8 -0
  16. data/app_generators/ahn/templates/Rakefile +3 -0
  17. data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
  18. data/app_generators/ahn/templates/components/simon_game/lib/simon_game.rb +61 -0
  19. data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +14 -0
  20. data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +31 -0
  21. data/app_generators/ahn/templates/config/startup.rb +53 -0
  22. data/app_generators/ahn/templates/dialplan.rb +4 -0
  23. data/bin/ahn +28 -0
  24. data/bin/ahnctl +68 -0
  25. data/bin/jahn +32 -0
  26. data/lib/adhearsion/blank_slate.rb +5 -0
  27. data/lib/adhearsion/cli.rb +106 -0
  28. data/lib/adhearsion/component_manager.rb +277 -0
  29. data/lib/adhearsion/core_extensions/all.rb +9 -0
  30. data/lib/adhearsion/core_extensions/array.rb +0 -0
  31. data/lib/adhearsion/core_extensions/custom_daemonizer.rb +45 -0
  32. data/lib/adhearsion/core_extensions/global.rb +1 -0
  33. data/lib/adhearsion/core_extensions/guid.rb +5 -0
  34. data/lib/adhearsion/core_extensions/hash.rb +0 -0
  35. data/lib/adhearsion/core_extensions/metaprogramming.rb +17 -0
  36. data/lib/adhearsion/core_extensions/numeric.rb +4 -0
  37. data/lib/adhearsion/core_extensions/proc.rb +0 -0
  38. data/lib/adhearsion/core_extensions/pseudo_uuid.rb +11 -0
  39. data/lib/adhearsion/core_extensions/publishable.rb +73 -0
  40. data/lib/adhearsion/core_extensions/relationship_properties.rb +40 -0
  41. data/lib/adhearsion/core_extensions/string.rb +26 -0
  42. data/lib/adhearsion/core_extensions/thread.rb +13 -0
  43. data/lib/adhearsion/core_extensions/thread_safety.rb +7 -0
  44. data/lib/adhearsion/core_extensions/time.rb +0 -0
  45. data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
  46. data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
  47. data/lib/adhearsion/distributed/gateways/rest_gateway.rb +9 -0
  48. data/lib/adhearsion/distributed/gateways/soap_gateway.rb +9 -0
  49. data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +9 -0
  50. data/lib/adhearsion/distributed/peer_finder.rb +0 -0
  51. data/lib/adhearsion/distributed/remote_cli.rb +0 -0
  52. data/lib/adhearsion/hooks.rb +57 -0
  53. data/lib/adhearsion/host_definitions.rb +63 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +59 -0
  55. data/lib/adhearsion/initializer/configuration.rb +202 -0
  56. data/lib/adhearsion/initializer/database.rb +92 -0
  57. data/lib/adhearsion/initializer/drb.rb +25 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/paths.rb +55 -0
  60. data/lib/adhearsion/initializer/rails.rb +40 -0
  61. data/lib/adhearsion/initializer.rb +217 -0
  62. data/lib/adhearsion/logging.rb +92 -0
  63. data/lib/adhearsion/services/scheduler.rb +5 -0
  64. data/lib/adhearsion/tasks/database.rb +5 -0
  65. data/lib/adhearsion/tasks/generating.rb +20 -0
  66. data/lib/adhearsion/tasks/lint.rb +4 -0
  67. data/lib/adhearsion/tasks/testing.rb +37 -0
  68. data/lib/adhearsion/tasks.rb +15 -0
  69. data/lib/adhearsion/version.rb +9 -0
  70. data/lib/adhearsion/voip/asterisk/agi_server.rb +78 -0
  71. data/lib/adhearsion/voip/asterisk/ami/actions.rb +238 -0
  72. data/lib/adhearsion/voip/asterisk/ami/machine.rb +871 -0
  73. data/lib/adhearsion/voip/asterisk/ami/machine.rl +109 -0
  74. data/lib/adhearsion/voip/asterisk/ami/parser.rb +262 -0
  75. data/lib/adhearsion/voip/asterisk/ami.rb +147 -0
  76. data/lib/adhearsion/voip/asterisk/commands.rb +1186 -0
  77. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  78. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  79. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  80. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  81. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  82. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  83. data/lib/adhearsion/voip/asterisk.rb +4 -0
  84. data/lib/adhearsion/voip/call.rb +402 -0
  85. data/lib/adhearsion/voip/call_routing.rb +64 -0
  86. data/lib/adhearsion/voip/commands.rb +9 -0
  87. data/lib/adhearsion/voip/constants.rb +39 -0
  88. data/lib/adhearsion/voip/conveniences.rb +18 -0
  89. data/lib/adhearsion/voip/dial_plan.rb +205 -0
  90. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  91. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  92. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  93. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  94. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +75 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  96. data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
  97. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  98. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  99. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  100. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  101. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  102. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  103. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  104. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  105. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  106. data/lib/adhearsion.rb +31 -0
  107. data/script/destroy +14 -0
  108. data/script/generate +14 -0
  109. data/spec/fixtures/dialplan.rb +3 -0
  110. data/spec/initializer/test_configuration.rb +267 -0
  111. data/spec/initializer/test_loading.rb +162 -0
  112. data/spec/initializer/test_paths.rb +43 -0
  113. data/spec/silence.rb +10 -0
  114. data/spec/test_ahn_command.rb +149 -0
  115. data/spec/test_code_quality.rb +87 -0
  116. data/spec/test_component_manager.rb +97 -0
  117. data/spec/test_constants.rb +8 -0
  118. data/spec/test_drb.rb +104 -0
  119. data/spec/test_helper.rb +94 -0
  120. data/spec/test_hooks.rb +37 -0
  121. data/spec/test_host_definitions.rb +79 -0
  122. data/spec/test_initialization.rb +105 -0
  123. data/spec/test_logging.rb +80 -0
  124. data/spec/test_relationship_properties.rb +54 -0
  125. data/spec/voip/asterisk/ami_response_definitions.rb +23 -0
  126. data/spec/voip/asterisk/config_file_generators/test_agents.rb +253 -0
  127. data/spec/voip/asterisk/config_file_generators/test_queues.rb +325 -0
  128. data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +306 -0
  129. data/spec/voip/asterisk/menu_command/test_calculated_match.rb +111 -0
  130. data/spec/voip/asterisk/menu_command/test_matchers.rb +98 -0
  131. data/spec/voip/asterisk/mock_ami_server.rb +176 -0
  132. data/spec/voip/asterisk/test_agi_server.rb +453 -0
  133. data/spec/voip/asterisk/test_ami.rb +227 -0
  134. data/spec/voip/asterisk/test_commands.rb +2006 -0
  135. data/spec/voip/asterisk/test_config_manager.rb +129 -0
  136. data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
  137. data/spec/voip/dsl/test_dialing_dsl.rb +268 -0
  138. data/spec/voip/dsl/test_dispatcher.rb +82 -0
  139. data/spec/voip/dsl/test_parser.rb +87 -0
  140. data/spec/voip/freeswitch/test_basic_connection_manager.rb +39 -0
  141. data/spec/voip/freeswitch/test_inbound_connection_manager.rb +39 -0
  142. data/spec/voip/freeswitch/test_oes_server.rb +9 -0
  143. data/spec/voip/test_call_routing.rb +127 -0
  144. data/spec/voip/test_dialplan_manager.rb +372 -0
  145. data/spec/voip/test_numerical_string.rb +48 -0
  146. data/spec/voip/test_phone_number.rb +36 -0
  147. data/test/test_ahn_generator.rb +59 -0
  148. data/test/test_component_generator.rb +52 -0
  149. data/test/test_generator_helper.rb +20 -0
  150. metadata +254 -0
@@ -0,0 +1,2006 @@
1
+ require File.dirname(__FILE__) + "/../../test_helper"
2
+ require 'adhearsion/voip/menu_state_machine/menu_class'
3
+ require 'adhearsion/voip/menu_state_machine/menu_builder'
4
+
5
+ context 'Asterisk VoIP Commands' do
6
+ include DialplanCommandTestHelpers
7
+
8
+ test "a call can write back to the PBX" do
9
+ message = 'oh hai'
10
+ mock_call.write message
11
+ pbx_should_have_been_sent message
12
+ end
13
+ end
14
+ context 'hangup command' do
15
+ include DialplanCommandTestHelpers
16
+
17
+ test "hanging up a call succesfully writes HANGUP back to the PBX and a success resopnse is returned" do
18
+ pbx_should_respond_with_success
19
+ response = mock_call.hangup
20
+ pbx_should_have_been_sent 'HANGUP'
21
+ response.should.equal pbx_success_response
22
+ end
23
+ end
24
+
25
+ context 'interruptable_play command' do
26
+
27
+ include DialplanCommandTestHelpers
28
+
29
+ test 'should return a string for the digit that was pressed' do
30
+ digits = [?0, ?1, ?#, ?*, ?9]
31
+ file = "file_doesnt_matter"
32
+ digits.each { |digit| pbx_should_respond_with_success digit }
33
+ digits.map { |digit| mock_call.send(:interruptable_play, file) }.should == digits.map(&:chr)
34
+ end
35
+
36
+ test "should return nil if no digit was pressed" do
37
+ pbx_should_respond_with_success 0
38
+ mock_call.send(:interruptable_play, 'foobar').should.equal nil
39
+ end
40
+
41
+ test "should play a series of files, stopping the series when a digit is played" do
42
+ stubbed_keypad_input = [0, 0, ?3]
43
+ stubbed_keypad_input.each do |digit|
44
+ pbx_should_respond_with_success digit
45
+ end
46
+
47
+ files = (100..105).map(&:to_s)
48
+ mock_call.send(:interruptable_play, *files).should == '3'
49
+ end
50
+
51
+ end
52
+
53
+ context 'wait_for_digit command' do
54
+
55
+ include DialplanCommandTestHelpers
56
+
57
+ test 'should return a string for the digit that was pressed' do
58
+ digits = [?0, ?1, ?#, ?*, ?9]
59
+ digits.each { |digit| pbx_should_respond_with_success digit }
60
+ digits.map { |digit| mock_call.send(:wait_for_digit) }.should == digits.map(&:chr)
61
+ end
62
+
63
+ test "the timeout given must be converted to milliseconds" do
64
+ pbx_should_respond_with_success 0
65
+ mock_call.send(:wait_for_digit, 1)
66
+ output.messages.first.ends_with?('1000').should.equal true
67
+ end
68
+ end
69
+
70
+ context 'answer' do
71
+ include DialplanCommandTestHelpers
72
+
73
+ test 'should send ANSWER over the AGI socket' do
74
+ mock_call.answer
75
+ pbx_should_have_been_sent 'ANSWER'
76
+ end
77
+
78
+ end
79
+
80
+ context 'execute' do
81
+ include DialplanCommandTestHelpers
82
+
83
+ test 'execute writes exec and app name to the PBX' do
84
+ pbx_should_respond_with_success
85
+ assert_success mock_call.execute(:foo)
86
+ pbx_should_have_been_sent 'EXEC foo '
87
+ end
88
+
89
+ test 'execute returns false if the command was not executed successfully by the PBX' do
90
+ pbx_should_respond_with_failure
91
+ assert !mock_call.execute(:foo), "execute should have failed"
92
+ end
93
+
94
+ test 'execute can accept arguments after the app name which get translated into pipe-delimited arguments to the PBX' do
95
+ pbx_should_respond_with_success
96
+ mock_call.execute :foo, 'bar', 'baz', 'hi'
97
+ pbx_should_have_been_sent 'EXEC foo bar|baz|hi'
98
+ end
99
+ end
100
+
101
+ context 'play command' do
102
+ include DialplanCommandTestHelpers
103
+
104
+ test 'passing a single string to play results in the playback application being executed with that file name on the PBX' do
105
+ pbx_should_respond_with_success
106
+ audio_file = "cents-per-minute"
107
+ mock_call.play audio_file
108
+ pbx_was_asked_to_play audio_file
109
+ end
110
+
111
+ test 'multiple strings can be passed to play, causing multiple playback commands to be issued' do
112
+ 2.times do
113
+ pbx_should_respond_with_success
114
+ end
115
+ audio_files = ["cents-per-minute", 'o-hai']
116
+ mock_call.play(*audio_files)
117
+ pbx_was_asked_to_play(*audio_files)
118
+ end
119
+
120
+ test 'If a number is passed to play(), the saynumber application is executed with the number as an argument' do
121
+ pbx_should_respond_with_success
122
+ mock_call.play 123
123
+ pbx_was_asked_to_play_number(123)
124
+ end
125
+
126
+ test 'if a string representation of a number is passed to play(), the saynumber application is executed with the number as an argument' do
127
+ pbx_should_respond_with_success
128
+ mock_call.play '123'
129
+ pbx_was_asked_to_play_number(123)
130
+ end
131
+
132
+ test '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
133
+ time = Time.parse("12/5/2000")
134
+ pbx_should_respond_with_success
135
+ mock_call.play time
136
+ pbx_was_asked_to_play_time(time.to_i)
137
+ end
138
+
139
+ disabled_test 'If a string matching dollars and (optionally) cents is passed to play(), a series of command will be executed to read the dollar amount' do
140
+ #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
141
+ # currencies, it'll be completely unwieldy. I'd suggest play_currency as a separate method. - Chad
142
+ end
143
+ end
144
+
145
+ context 'input command' do
146
+
147
+ include DialplanCommandTestHelpers
148
+
149
+ # pbx_should_respond_with_successful_background_response
150
+ # pbx_should_respond_with_a_wait_for_digit_timeout
151
+
152
+ test 'should raise an error when the number of digits expected is -1 (this is deprecated behavior)' do
153
+ the_following_code {
154
+ mock_call.input(-1)
155
+ }.should.raise ArgumentError
156
+ end
157
+
158
+ test 'input() calls wait_for_digit the specified number of times (when no sound files are given)' do
159
+ # mock_call.should_receive(:interruptable_play).never
160
+ mock_call.should_receive(:wait_for_digit).times(4).and_return('1', '2', '3', '4')
161
+ mock_call.input(4).should == '1234'
162
+ end
163
+
164
+ test 'should execute wait_for_digit if no digit is pressed during interruptable_play' do
165
+ sound_files = %w[one two three]
166
+ mock_call.should_receive(:interruptable_play).once.with(*sound_files).and_return nil
167
+ mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
168
+ should_throw(:digit_request) { mock_call.input(10, :play => sound_files) }
169
+ end
170
+
171
+ test 'should default the :accept_key to "#" when unlimited digits are to be collected' do
172
+ mock_call.should_receive(:wait_for_digit).times(2).and_return '*', '#'
173
+ mock_call.input.should == '*'
174
+ end
175
+
176
+ test 'should raise an exception when unlimited digits are to be collected and :accept_key => false' do
177
+ the_following_code {
178
+ mock_call.input(:accept_key => false)
179
+ }.should.raise ArgumentError
180
+ end
181
+
182
+ test 'when :accept_key is false and input() is collecting a finite number of digits, it should allow all DTMFs' do
183
+ all_digits = %w[0 1 2 3 # * 4 5 6 7 8 9]
184
+ mock_call.should_receive(:wait_for_digit).times(all_digits.size).and_return(*all_digits)
185
+ the_following_code {
186
+ mock_call.input(all_digits.size, :accept_key => false)
187
+ }.should.not.raise ArgumentError
188
+ end
189
+
190
+ test 'passes wait_for_digit the :timeout option when one is given' do
191
+ mock_call.should_receive(:interruptable_play).never
192
+ mock_call.should_receive(:wait_for_digit).twice.and_return '1', '2'
193
+ mock_call.input(2, :timeout => 1.minute).should == '12'
194
+ end
195
+
196
+ test 'executes interruptable_play() with all of the files given to :play' do
197
+ sound_files = %w[foo bar qaz]
198
+ mock_call.should_receive(:interruptable_play).once.with(*sound_files).and_return '#'
199
+ mock_call.should_receive(:wait_for_digit).once.and_return '*'
200
+ mock_call.input(2, :play => sound_files).should == '#*'
201
+ end
202
+
203
+ test 'pressing the terminating key before any other digits returns an empty string' do
204
+ mock_call.should_receive(:wait_for_digit).once.and_return '*'
205
+ mock_call.input(:accept_key => '*').should == ''
206
+ end
207
+
208
+ test 'should execute wait_for_digit first if no sound files are given' do
209
+ mock_call.should_receive(:interruptable_play).never
210
+ mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
211
+ should_throw(:digit_request) { mock_call.input(1) }
212
+ end
213
+
214
+ test "Input timing out when digits are pressed returns only the collected digits" do
215
+ mock_call.should_receive(:wait_for_digit).twice.and_return '5', nil
216
+ mock_call.input(9, :timeout => 1.day).should == '5'
217
+ end
218
+
219
+ end
220
+
221
+ context "The variable() command" do
222
+
223
+ include DialplanCommandTestHelpers
224
+
225
+ test "should call set_variable for every Hash-key argument given" do
226
+ args = [:ohai, "ur_home_erly"]
227
+ mock_call.should_receive(:set_variable).once.with(*args)
228
+ mock_call.variable Hash[*args]
229
+ end
230
+
231
+ test "should call set_variable for every Hash-key argument given" do
232
+ many_args = { :a => :b, :c => :d, :e => :f, :g => :h}
233
+ mock_call.should_receive(:set_variable).times(many_args.size)
234
+ mock_call.variable many_args
235
+ end
236
+
237
+ test "should call get_variable for every String given" do
238
+ variables = ["foo", "bar", :qaz, :qwerty, :baz]
239
+ variables.each do |var|
240
+ mock_call.should_receive(:get_variable).once.with(var).and_return("X")
241
+ end
242
+ mock_call.variable(*variables)
243
+ end
244
+
245
+ test "should NOT return an Array when just one arg is given" do
246
+ mock_call.should_receive(:get_variable).once.and_return "lol"
247
+ mock_call.variable(:foo).should.not.be.kind_of Array
248
+ end
249
+
250
+ test "should raise an ArgumentError when a Hash and normal args are given" do
251
+ the_following_code {
252
+ mock_call.variable 5,4,3,2,1, :foo => :bar
253
+ }.should.raise ArgumentError
254
+ end
255
+
256
+ end
257
+
258
+ context "the set_variable method" do
259
+
260
+ include DialplanCommandTestHelpers
261
+
262
+ test "variables and values are properly quoted" do
263
+ mock_call.should_receive(:raw_response).once.with 'SET VARIABLE foo "i can \\" has ruby?"'
264
+ mock_call.set_variable 'foo', 'i can " has ruby?'
265
+ end
266
+
267
+ test "to_s() is effectively called on both the key and the value" do
268
+ mock_call.should_receive(:raw_response).once.with 'SET VARIABLE QAZ "QWERTY"'
269
+ mock_call.set_variable :QAZ, :QWERTY
270
+ end
271
+
272
+ end
273
+
274
+ context 'the voicemail command' do
275
+
276
+ include DialplanCommandTestHelpers
277
+
278
+ test 'should not send the context name when none is given' do
279
+ mailbox_number = 123
280
+ mock_call.should_receive(:execute).once.with('voicemail', 123, '').and_throw :sent_voicemail!
281
+ should_throw(:sent_voicemail!) { mock_call.voicemail 123 }
282
+ end
283
+
284
+ test 'should send the context name when one is given' do
285
+ mailbox_number, context_name = 333, 'doesntmatter'
286
+ mock_call.should_receive(:execute).once.with('voicemail', "#{mailbox_number}@#{context_name}", '').and_throw :sent_voicemail!
287
+ should_throw(:sent_voicemail!) { mock_call.voicemail(context_name => mailbox_number) }
288
+ end
289
+
290
+ test 'should pass in the s option if :skip => true' do
291
+ mailbox_number = '012'
292
+ mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 's').and_throw :sent_voicemail!
293
+ should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :skip => true) }
294
+ end
295
+
296
+ test 'should combine mailbox numbers with the context name given when both are given' do
297
+ context = "lolcats"
298
+ mailboxes = [1,2,3,4,5]
299
+ mailboxes_with_context = mailboxes.map { |mailbox| "#{mailbox}@#{context}"}
300
+ mock_call.should_receive(:execute).once.with('voicemail', mailboxes_with_context.join('&'), '')
301
+ mock_call.voicemail context => mailboxes
302
+ end
303
+
304
+ test 'should raise an argument error if the mailbox number is not numerical' do
305
+ the_following_code {
306
+ mock_call.voicemail :foo => "bar"
307
+ }.should.raise ArgumentError
308
+ end
309
+
310
+ test 'should raise an argument error if too many arguments are supplied' do
311
+ the_following_code {
312
+ mock_call.voicemail "wtfisthisargument", :context_name => 123, :greeting => :busy
313
+ }.should.raise ArgumentError
314
+ end
315
+
316
+ test 'should raise an ArgumentError if multiple context names are given' do
317
+ the_following_code {
318
+ mock_call.voicemail :one => [1,2,3], :two => [11,22,33]
319
+ }.should.raise ArgumentError
320
+ end
321
+
322
+ test "should raise an ArgumentError when the :greeting value isn't recognized" do
323
+ the_following_code {
324
+ mock_call.voicemail :context_name => 123, :greeting => :zomgz
325
+ }.should.raise ArgumentError
326
+ end
327
+
328
+ test 'should pass in the u option if :greeting => :unavailable' do
329
+ mailbox_number = '776'
330
+ mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'u').and_throw :sent_voicemail!
331
+ should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :unavailable) }
332
+ end
333
+
334
+ test 'should pass in both the skip and greeting options if both are supplied' do
335
+ mailbox_number = '4'
336
+ mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'u').and_throw :sent_voicemail!
337
+ should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :unavailable) }
338
+ end
339
+
340
+ test 'should raise an ArgumentError if mailbox_number is blank?()' do
341
+ the_following_code {
342
+ mock_call.voicemail ''
343
+ }.should.raise ArgumentError
344
+
345
+ the_following_code {
346
+ mock_call.voicemail nil
347
+ }.should.raise ArgumentError
348
+ end
349
+
350
+ test 'should pass in the b option if :gretting => :busy' do
351
+ mailbox_number = '1'
352
+ mock_call.should_receive(:execute).once.with('voicemail', mailbox_number, 'b').and_throw :sent_voicemail!
353
+ should_throw(:sent_voicemail!) { mock_call.voicemail(mailbox_number, :greeting => :busy) }
354
+ end
355
+
356
+ test 'should return true if VMSTATUS == "SUCCESS"' do
357
+ mock_call.should_receive(:execute).once
358
+ mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "SUCCESS"
359
+ mock_call.voicemail(3).should.equal true
360
+ end
361
+
362
+ test 'should return false if VMSTATUS == "USEREXIT"' do
363
+ mock_call.should_receive(:execute).once
364
+ mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "USEREXIT"
365
+ mock_call.voicemail(2).should.equal false
366
+ end
367
+
368
+ test 'should return nil if VMSTATUS == "FAILED"' do
369
+ mock_call.should_receive(:execute).once
370
+ mock_call.should_receive(:variable).once.with('VMSTATUS').and_return "FAILED"
371
+ mock_call.voicemail(2).should.equal nil
372
+ end
373
+
374
+ end
375
+
376
+ context 'The voicemail_main command' do
377
+
378
+ include DialplanCommandTestHelpers
379
+
380
+ test "should not pass in the context or the delimiting @ sign if you don't supply one"
381
+
382
+ test "the :folder Hash key argument should wrap the value in a()" do
383
+ folder = "foobar"
384
+ mailbox = 81
385
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}","a(#{folder})")
386
+ mock_call.voicemail_main :mailbox => mailbox, :folder => folder
387
+ end
388
+
389
+ test ':authenticate should pass in the "s" option if given false' do
390
+ mailbox = 333
391
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}","s")
392
+ mock_call.voicemail_main :mailbox => mailbox, :authenticate => false
393
+ end
394
+
395
+ test ':authenticate should pass in the s option if given false' do
396
+ mailbox = 55
397
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}")
398
+ mock_call.voicemail_main :mailbox => mailbox, :authenticate => true
399
+ end
400
+
401
+ test 'should not pass any flags only a mailbox is given' do
402
+ mailbox = "1"
403
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}")
404
+ mock_call.voicemail_main :mailbox => mailbox
405
+ end
406
+
407
+ test 'when given no mailbox or context an empty string should be passed to execute as the first argument' do
408
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "", "s")
409
+ mock_call.voicemail_main :authenticate => false
410
+ end
411
+
412
+ test 'should properly concatenate the options when given multiple ones' do
413
+ folder = "ohai"
414
+ mailbox = 9999
415
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "#{mailbox}", "sa(#{folder})")
416
+ mock_call.voicemail_main :mailbox => mailbox, :authenticate => false, :folder => folder
417
+ end
418
+
419
+ test 'should not require any arguments' do
420
+ mock_call.should_receive(:execute).once.with("VoiceMailMain")
421
+ mock_call.voicemail_main
422
+ end
423
+
424
+ test 'should pass in the "@context_name" part in if a :context is given and no mailbox is given' do
425
+ context_name = "icanhascheezburger"
426
+ mock_call.should_receive(:execute).once.with("VoiceMailMain", "@#{context_name}")
427
+ mock_call.voicemail_main :context => context_name
428
+ end
429
+
430
+ test "should raise an exception if the folder has a space or malformed characters in it" do
431
+ ["i has a space", "exclaim!", ",", ""].each do |bad_folder_name|
432
+ the_following_code {
433
+ mock_call.voicemail_main :mailbox => 123, :folder => bad_folder_name
434
+ }.should.raise ArgumentError
435
+ end
436
+ end
437
+
438
+ end
439
+
440
+ context 'the check_voicemail command' do
441
+
442
+ include DialplanCommandTestHelpers
443
+
444
+ test "should simply execute voicemail_main with no arguments after warning" do
445
+ flexmock(ahn_log.agi).should_receive(:warn).once.with(String)
446
+ mock_call.should_receive(:voicemail_main).once.and_return :mocked_out
447
+ mock_call.check_voicemail.should.equal :mocked_out
448
+ end
449
+
450
+ end
451
+
452
+
453
+ context "The queue management abstractions" do
454
+
455
+ include DialplanCommandTestHelpers
456
+
457
+ test 'should not create separate objects for queues with basically the same name' do
458
+ mock_call.queue('foo').should.equal mock_call.queue('foo')
459
+ mock_call.queue('bar').should.equal mock_call.queue(:bar)
460
+ end
461
+
462
+ test "queue() should return an instance of QueueProxy" do
463
+ mock_call.queue("foobar").should.be.kind_of Adhearsion::VoIP::Asterisk::Commands::QueueProxy
464
+ end
465
+
466
+ test "a QueueProxy should respond to join!(), members()" do
467
+ %w[join! agents].each do |method|
468
+ mock_call.queue('foobar').should.respond_to(method)
469
+ end
470
+ end
471
+
472
+ test 'a QueueProxy should return a QueueAgentsListProxy when members() is called' do
473
+ mock_call.queue('foobar').agents.should.be.kind_of(Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueAgentsListProxy)
474
+ end
475
+
476
+ test 'join! should properly join a queue' do
477
+ mock_call.should_receive(:execute).once.with("queue", "foobaz", "", '', '', '')
478
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "FULL"
479
+ mock_call.queue("foobaz").join!
480
+ end
481
+
482
+ test 'should return a symbol representing the result of joining the queue' do
483
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "TIMEOUT"
484
+ mock_call.queue('monkey').join!.should.equal :timeout
485
+ end
486
+
487
+ test 'should join a queue with a timeout properly' do
488
+ mock_call.should_receive(:execute).once.with("queue", "foobaz", "", '', '', '60')
489
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
490
+ mock_call.queue("foobaz").join! :timeout => 1.minute
491
+ end
492
+
493
+ test 'should join a queue with an announcement file properly' do
494
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "", '', '', '5')
495
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
496
+ mock_call.queue("roflcopter").join! :timeout => 5
497
+ end
498
+
499
+ test 'should join a queue with allow_transfer properly' do
500
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "Tt", '', '', '')
501
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
502
+ mock_call.queue("roflcopter").join! :allow_transfer => :everyone
503
+
504
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "T", '', '', '')
505
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
506
+ mock_call.queue("roflcopter").join! :allow_transfer => :caller
507
+
508
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "t", '', '', '')
509
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
510
+ mock_call.queue("roflcopter").join! :allow_transfer => :agent
511
+ end
512
+
513
+ test 'should join a queue with allow_hangup properly' do
514
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "Hh", '', '', '')
515
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
516
+ mock_call.queue("roflcopter").join! :allow_hangup => :everyone
517
+
518
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "H", '', '', '')
519
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
520
+ mock_call.queue("roflcopter").join! :allow_hangup => :caller
521
+
522
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "h", '', '', '')
523
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
524
+ mock_call.queue("roflcopter").join! :allow_hangup => :agent
525
+ end
526
+
527
+ test 'should join a queue properly with the :play argument' do
528
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "r", '', '', '')
529
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
530
+ mock_call.queue("roflcopter").join! :play => :ringing
531
+
532
+ mock_call.should_receive(:execute).once.with("queue", "roflcopter", "", '', '', '')
533
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
534
+ mock_call.queue("roflcopter").join! :play => :music
535
+ end
536
+
537
+ test 'joining a queue with many options specified' do
538
+ mock_call.should_receive(:execute).once.with("queue", "q", "rtHh", '', '', '120')
539
+ mock_call.should_receive(:get_variable).once.with("QUEUESTATUS").and_return "JOINEMPTY"
540
+ mock_call.queue('q').join! :allow_transfer => :agent, :timeout => 2.minutes,
541
+ :play => :ringing, :allow_hangup => :everyone
542
+ end
543
+
544
+ test 'join!() should raise an ArgumentError when unrecognized Hash key arguments are given' do
545
+ the_following_code {
546
+ mock_call.queue('iwearmysunglassesatnight').join! :misspelled => true
547
+ }.should.raise ArgumentError
548
+ end
549
+
550
+ test 'should fetch the members with the name given to queue()' do
551
+ mock_call.should_receive(:variable).once.with("QUEUE_MEMBER_COUNT(jay)").and_return 5
552
+ mock_call.queue('jay').agents.size.should.equal 5
553
+ end
554
+
555
+ test 'should not fetch a QUEUE_MEMBER_COUNT each time count() is called when caching is enabled' do
556
+ mock_call.should_receive(:variable).once.with("QUEUE_MEMBER_COUNT(sales)").and_return 0
557
+ 10.times do
558
+ mock_call.queue('sales').agents(:cache => true).size
559
+ end
560
+ end
561
+
562
+ test 'should raise an argument error if the members() method receives an unrecognized symbol' do
563
+ the_following_code {
564
+ mock_call.queue('foobarz').agents(:cached => true) # common typo
565
+ }.should.raise ArgumentError
566
+ end
567
+
568
+ test 'when fetching agents, it should properly split by the supported delimiters' do
569
+ queue_name = "doesnt_matter"
570
+ mock_call.should_receive(:get_variable).with("QUEUE_MEMBER_LIST(#{queue_name})").and_return('Agent/007,Agent/003,Zap/2')
571
+ mock_call.queue(queue_name).agents(:cache => true).to_a.size.should.equal 3
572
+ end
573
+
574
+ test 'when fetching agents, each array index should be an instance of AgentProxy' do
575
+ queue_name = 'doesnt_matter'
576
+ mock_call.should_receive(:get_variable).with("QUEUE_MEMBER_LIST(#{queue_name})").and_return('Agent/007,Agent/003,Zap/2')
577
+ agents = mock_call.queue(queue_name).agents(:cache => true).to_a
578
+ agents.size.should > 0
579
+ agents.each do |agent|
580
+ agent.should.be.kind_of Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy
581
+ end
582
+ end
583
+
584
+ test 'should properly retrieve metadata for an AgentProxy instance' do
585
+ agent_id, metadata_name = '22', 'status'
586
+ mock_env = flexmock "a mock ExecutionEnvironment"
587
+ mock_queue = flexmock "a queue that references our mock ExecutionEnvironment", :environment => mock_env, :name => "doesntmatter"
588
+ mock_env.should_receive(:variable).once.with("AGENT(#{agent_id}:#{metadata_name})")
589
+ agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new("Agent/#{agent_id}", mock_queue)
590
+ agent.send(:agent_metadata, metadata_name)
591
+ end
592
+
593
+ test 'AgentProxy#logged_in? should return true if the "state" of an agent == LOGGEDIN' do
594
+ mock_env = flexmock "a mock ExecutionEnvironment"
595
+ mock_queue = flexmock "a queue that references our mock ExecutionEnvironment", :environment => mock_env, :name => "doesntmatter"
596
+
597
+ agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new('Agent/123', mock_queue)
598
+ flexmock(agent).should_receive(:agent_metadata).once.with('status').and_return 'LOGGEDIN'
599
+ agent.should.be.logged_in
600
+
601
+ flexmock(agent).should_receive(:agent_metadata).once.with('status').and_return 'LOGGEDOUT'
602
+ agent.should.not.be.logged_in
603
+ end
604
+
605
+ test 'the AgentProxy should populate its own "id" property to the numerical ID of the "interface" with which it was constructed' do
606
+ mock_queue = flexmock :name => "doesntmatter"
607
+ id = '123'
608
+
609
+ agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new("Agent/#{id}", mock_queue)
610
+ agent.id.should.equal id
611
+
612
+ agent = Adhearsion::VoIP::Asterisk::Commands::QueueProxy::AgentProxy.new(id, mock_queue)
613
+ agent.id.should == id
614
+ end
615
+
616
+ test 'QueueAgentsListProxy#<<() should new the channel driver given as the argument to the system' do
617
+ queue_name, agent_channel = "metasyntacticvariablesftw", "Agent/123"
618
+ pbx_should_respond_with_value "ADDED"
619
+ mock_call.should_receive('execute').once.with("AddQueueMember", queue_name, agent_channel, "", "", "")
620
+ mock_call.queue(queue_name).agents.new agent_channel
621
+ end
622
+
623
+ test 'when a queue agent is dynamically added and the queue does not exist, a QueueDoesNotExistError should be raised' do
624
+ mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('NOSUCHQUEUE')
625
+ the_following_code {
626
+ mock_call.queue('this_should_not_exist').agents.new 'Agent/911'
627
+ }.should.raise Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
628
+ end
629
+
630
+ test 'when a queue agent is dynamiaclly added and the adding was successful, true should be returned' do
631
+ mock_call.should_receive(:get_variable).once.with("AQMSTATUS").and_return("ADDED")
632
+ mock_call.should_receive(:execute).once.with("AddQueueMember", "lalala", "Agent/007", "", "", "")
633
+ return_value = mock_call.queue('lalala').agents.new "Agent/007"
634
+ return_value.should.equal true
635
+ end
636
+
637
+ test 'should raise an argument when an unrecognized key is given to add()' do
638
+ the_following_code {
639
+ mock_call.queue('q').agents.new :foo => "bar"
640
+ }.should.raise ArgumentError
641
+ end
642
+
643
+ test 'should execute AddQueueMember with the penalty properly' do
644
+ queue_name = 'name_does_not_matter'
645
+ mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, '', 10, '', '')
646
+ mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
647
+ mock_call.queue(queue_name).agents.new :penalty => 10
648
+ end
649
+
650
+ test 'should execute AddQueueMember properly when the name is given' do
651
+ queue_name, agent_name = 'name_does_not_matter', 'Jay Phillips'
652
+ mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, '', '', '', agent_name)
653
+ mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
654
+ mock_call.queue(queue_name).agents.new :name => agent_name
655
+ end
656
+
657
+ test 'should execute AddQueueMember properly when the name, penalty, and interface is given' do
658
+ queue_name, agent_name, interface, penalty = 'name_does_not_matter', 'Jay Phillips', 'Agent/007', 4
659
+ mock_call.should_receive(:execute).once.with('AddQueueMember', queue_name, interface, penalty, '', agent_name)
660
+ mock_call.should_receive(:get_variable).once.with('AQMSTATUS').and_return('ADDED')
661
+ mock_call.queue(queue_name).agents.new interface, :name => agent_name, :penalty => penalty
662
+ end
663
+
664
+ test 'should return a correct boolean for exists?()' do
665
+ mock_call.should_receive(:execute).once.with("RemoveQueueMember", "kablamm", "SIP/AdhearsionQueueExistenceCheck")
666
+ mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOTINQUEUE"
667
+ mock_call.queue("kablamm").exists?.should.equal true
668
+
669
+ mock_call.should_receive(:execute).once.with("RemoveQueueMember", "monkey", "SIP/AdhearsionQueueExistenceCheck")
670
+ mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOSUCHQUEUE"
671
+ mock_call.queue("monkey").exists?.should.equal false
672
+ end
673
+
674
+ test 'should pause an agent properly from a certain queue' do
675
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(lolcats)").and_return "Agent/007,Agent/008"
676
+ mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "PAUSED"
677
+
678
+ agents = mock_call.queue('lolcats').agents :cache => true
679
+ agents.last.pause!.should.equal true
680
+ end
681
+
682
+ test 'should pause an agent properly from a certain queue and return false when the agent did not exist' do
683
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(lolcats)").and_return "Agent/007,Agent/008"
684
+ mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "NOTFOUND"
685
+ mock_call.should_receive(:execute).once.with("PauseQueueMember", 'lolcats', "Agent/008")
686
+
687
+ agents = mock_call.queue('lolcats').agents :cache => true
688
+ agents.last.pause!.should.equal false
689
+ end
690
+
691
+ test 'should pause an agent globally properly' do
692
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(family)").and_return "Agent/Jay"
693
+ mock_call.should_receive(:get_variable).once.with("PQMSTATUS").and_return "PAUSED"
694
+ mock_call.should_receive(:execute).once.with("PauseQueueMember", nil, "Agent/Jay")
695
+
696
+ mock_call.queue('family').agents.first.pause! :everywhere => true
697
+ end
698
+
699
+ test 'should unpause an agent properly' do
700
+ queue_name = "name with spaces"
701
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(#{queue_name})").and_return "Agent/Jay"
702
+ mock_call.should_receive(:get_variable).once.with("UPQMSTATUS").and_return "UNPAUSED"
703
+ mock_call.should_receive(:execute).once.with("UnpauseQueueMember", queue_name, "Agent/Jay")
704
+
705
+ mock_call.queue(queue_name).agents.first.unpause!.should.equal true
706
+ end
707
+
708
+ test 'should unpause an agent globally properly' do
709
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
710
+ mock_call.should_receive(:get_variable).once.with("UPQMSTATUS").and_return "UNPAUSED"
711
+ mock_call.should_receive(:execute).once.with("UnpauseQueueMember", nil, "Agent/Tom")
712
+
713
+ mock_call.queue('FOO').agents.first.unpause!(:everywhere => true).should.equal true
714
+ end
715
+
716
+ test 'waiting_count for a queue that does exist' do
717
+ mock_call.should_receive(:get_variable).once.with("QUEUE_WAITING_COUNT(q)").and_return "50"
718
+ flexmock(mock_call.queue('q')).should_receive(:exists?).once.and_return true
719
+ mock_call.queue('q').waiting_count.should.equal 50
720
+ end
721
+
722
+ test 'waiting_count for a queue that does not exist' do
723
+ the_following_code {
724
+ flexmock(mock_call.queue('q')).should_receive(:exists?).once.and_return false
725
+ mock_call.queue('q').waiting_count
726
+ }.should.raise Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
727
+ end
728
+
729
+ test 'empty? should call waiting_count' do
730
+ queue = mock_call.queue 'testing_empty'
731
+ flexmock(queue).should_receive(:waiting_count).once.and_return 0
732
+ queue.should.be.empty
733
+
734
+ queue = mock_call.queue 'testing_empty'
735
+ flexmock(queue).should_receive(:waiting_count).once.and_return 99
736
+ queue.should.not.be.empty
737
+ end
738
+
739
+ test 'any? should call waiting_count' do
740
+ queue = mock_call.queue 'testing_empty'
741
+ flexmock(queue).should_receive(:waiting_count).once.and_return 0
742
+ queue.any?.should.equal false
743
+
744
+ queue = mock_call.queue 'testing_empty'
745
+ flexmock(queue).should_receive(:waiting_count).once.and_return 99
746
+ queue.any?.should.equal true
747
+ end
748
+
749
+ test 'should remove an agent properly' do
750
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
751
+ mock_call.should_receive(:execute).once.with('RemoveQueueMember', 'FOO', 'Agent/Tom')
752
+ mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "REMOVED"
753
+ mock_call.queue('FOO').agents.first.remove!.should.equal true
754
+ end
755
+
756
+ test 'should remove an agent properly' do
757
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(FOO)").and_return "Agent/Tom"
758
+ mock_call.should_receive(:execute).once.with('RemoveQueueMember', 'FOO', 'Agent/Tom')
759
+ mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOTINQUEUE"
760
+ mock_call.queue('FOO').agents.first.remove!.should.equal false
761
+ end
762
+
763
+ test "should raise a QueueDoesNotExistError when removing an agent from a queue that doesn't exist" do
764
+ mock_call.should_receive(:get_variable).once.with("QUEUE_MEMBER_LIST(cool_people)").and_return "Agent/ZeroCool"
765
+ mock_call.should_receive(:execute).once.with("RemoveQueueMember", "cool_people", "Agent/ZeroCool")
766
+ mock_call.should_receive(:get_variable).once.with("RQMSTATUS").and_return "NOSUCHQUEUE"
767
+ the_following_code {
768
+ mock_call.queue("cool_people").agents.first.remove!
769
+ }.should.raise Adhearsion::VoIP::Asterisk::Commands::QueueProxy::QueueDoesNotExistError
770
+ end
771
+
772
+ test "should log an agent in properly with no agent id given" do
773
+ mock_call.should_receive(:execute).once.with('AgentLogin', nil, 's')
774
+ mock_call.queue('barrel_o_agents').agents.login!
775
+ end
776
+
777
+ test 'should remove "Agent/" before the agent ID given if necessary when logging an agent in' do
778
+ mock_call.should_receive(:execute).once.with('AgentLogin', '007', 's')
779
+ mock_call.queue('barrel_o_agents').agents.login! 'Agent/007'
780
+
781
+ mock_call.should_receive(:execute).once.with('AgentLogin', '007', 's')
782
+ mock_call.queue('barrel_o_agents').agents.login! '007'
783
+ end
784
+
785
+ test 'should add an agent silently properly' do
786
+ mock_call.should_receive(:execute).once.with('AgentLogin', '007', '')
787
+ mock_call.queue('barrel_o_agents').agents.login! 'Agent/007', :silent => false
788
+
789
+ mock_call.should_receive(:execute).once.with('AgentLogin', '008', 's')
790
+ mock_call.queue('barrel_o_agents').agents.login! 'Agent/008', :silent => true
791
+ end
792
+
793
+ test 'logging an agent in should raise an ArgumentError is unrecognized arguments are given' do
794
+ the_following_code {
795
+ mock_call.queue('ohai').agents.login! 1,2,3,4,5
796
+ }.should.raise ArgumentError
797
+
798
+ the_following_code {
799
+ mock_call.queue('lols').agents.login! 1337, :sssssilent => false
800
+ }.should.raise ArgumentError
801
+
802
+ the_following_code {
803
+ mock_call.queue('qwerty').agents.login! 777, 6,5,4,3,2,1, :wee => :wee
804
+ }.should.raise ArgumentError
805
+ end
806
+
807
+ end
808
+
809
+ context 'the menu() method' do
810
+
811
+ include DialplanCommandTestHelpers
812
+
813
+ test "should instantiate a new Menu object with only the Hash given as menu() options" do
814
+ args = [1,2,3,4,5, {:timeout => 1.year, :tries => (1.0/0.0)}]
815
+
816
+ flexmock(Adhearsion::VoIP::Menu).should_receive(:new).once.
817
+ with(args.last).and_throw(:instantiating_menu!)
818
+
819
+ should_throw(:instantiating_menu!) { mock_call.menu(*args) }
820
+ end
821
+
822
+ test "should jump to a context when a timeout is encountered and there is at least one exact match" do
823
+ pbx_should_respond_with_successful_background_response ?5
824
+ pbx_should_respond_with_successful_background_response ?4
825
+ pbx_should_respond_with_a_wait_for_digit_timeout
826
+
827
+ context_named_main = Adhearsion::DialPlan::DialplanContextProc.new(:main) { throw :inside_main! }
828
+ context_named_other = Adhearsion::DialPlan::DialplanContextProc.new(:other) { throw :inside_other! }
829
+ flexmock(mock_call).should_receive(:main).once.and_return(context_named_main)
830
+ flexmock(mock_call).should_receive(:other).never
831
+
832
+ should_pass_control_to_a_context_that_throws :inside_main! do
833
+ mock_call.menu do |link|
834
+ link.main 54
835
+ link.other 543
836
+ end
837
+ end
838
+ end
839
+
840
+ test "when the 'extension' variable is changed, it should be an instance of PhoneNumber" do
841
+ pbx_should_respond_with_successful_background_response ?5
842
+ foobar_context = Adhearsion::DialPlan::DialplanContextProc.new(:foobar) { throw :foobar! }
843
+ mock_call.should_receive(:foobar).once.and_return foobar_context
844
+ should_pass_control_to_a_context_that_throws :foobar! do
845
+ mock_call.menu do |link|
846
+ link.foobar 5
847
+ end
848
+ end
849
+ 5.should === mock_call.extension
850
+ mock_call.extension.__real_string.should == "5"
851
+ end
852
+
853
+ end
854
+
855
+ context 'the Menu class' do
856
+
857
+ include DialplanCommandTestHelpers
858
+
859
+ test "should yield a MenuBuilder when instantiated" do
860
+ lambda {
861
+ Adhearsion::VoIP::Menu.new do |block_argument|
862
+ block_argument.should.be.kind_of Adhearsion::VoIP::MenuBuilder
863
+ throw :inside_block
864
+ end
865
+ }.should.throw :inside_block
866
+ end
867
+
868
+ test "should invoke wait_for_digit instead of interruptable_play when no sound files are given" do
869
+ mock_call.should_receive(:wait_for_digit).once.with(5).and_return '#'
870
+ mock_call.menu { |link| link.does_not_match 3 }
871
+ end
872
+
873
+ test 'should invoke interruptable_play when sound files are given only for the first digit' do
874
+ sound_files = %w[i like big butts and i cannot lie]
875
+ timeout = 1337
876
+
877
+ mock_call.should_receive(:interruptable_play).once.with(*sound_files).and_return nil
878
+ mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil
879
+
880
+ mock_call.menu(sound_files, :timeout => timeout) { |link| link.qwerty 12345 }
881
+ end
882
+
883
+ test 'if the call to interruptable_play receives a timeout, it should execute wait_for_digit with the timeout given' do
884
+ sound_files = %w[i like big butts and i cannot lie]
885
+ timeout = 987
886
+
887
+ mock_call.should_receive(:interruptable_play).once.with(*sound_files).and_return nil
888
+ mock_call.should_receive(:wait_for_digit).with(timeout).and_return
889
+
890
+ mock_call.menu(sound_files, :timeout => timeout) { |link| link.foobar 911 }
891
+ end
892
+
893
+ test "should work when no files are given to be played and a timeout is reached on the first digit" do
894
+ timeout = 12
895
+ [:on_premature_timeout, :on_failure].each do |usage_case|
896
+ should_throw :got_here! do
897
+ mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil # Simulates timeout
898
+ mock_call.menu :timeout => timeout do |link|
899
+ link.foobar 0
900
+ link.__send__(usage_case) { throw :got_here! }
901
+ end
902
+ end
903
+ end
904
+ end
905
+
906
+ test "should default the timeout to five seconds" do
907
+ pbx_should_respond_with_successful_background_response ?2
908
+ pbx_should_respond_with_a_wait_for_digit_timeout
909
+
910
+ mock_call.should_receive(:wait_for_digit).once.with(5).and_return nil
911
+ mock_call.menu { |link| link.foobar 22 }
912
+ end
913
+
914
+ test "when matches fail due to timeouts, the menu should repeat :tries times" do
915
+ tries, times_timed_out = 10, 0
916
+
917
+ tries.times do
918
+ pbx_should_respond_with_successful_background_response ?4
919
+ pbx_should_respond_with_successful_background_response ?0
920
+ pbx_should_respond_with_a_wait_for_digit_timeout
921
+ end
922
+
923
+ should_throw :inside_failure_callback do
924
+ mock_call.menu :tries => tries do |link|
925
+ link.pattern_longer_than_our_test_input 400
926
+ link.on_premature_timeout { times_timed_out += 1 }
927
+ link.on_invalid { raise "should never get here!" }
928
+ link.on_failure { throw :inside_failure_callback }
929
+ end
930
+ end
931
+ times_timed_out.should.equal tries
932
+ end
933
+
934
+ test "when matches fail due to invalid input, the menu should repeat :tries times" do
935
+ tries = 10
936
+ times_invalid = 0
937
+
938
+ tries.times do
939
+ pbx_should_respond_with_successful_background_response ?0
940
+ end
941
+
942
+ should_throw :inside_failure_callback do
943
+ mock_call.menu :tries => tries do |link|
944
+ link.be_leet 1337
945
+ link.on_premature_timeout { raise "should never get here!" }
946
+ link.on_invalid { times_invalid += 1 }
947
+ link.on_failure { throw :inside_failure_callback }
948
+ end
949
+ end
950
+ times_invalid.should.equal tries
951
+ end
952
+
953
+ test "invoke on_invalid callback when an invalid extension was entered" do
954
+ pbx_should_respond_with_successful_background_response ?5
955
+ pbx_should_respond_with_successful_background_response ?5
956
+ pbx_should_respond_with_successful_background_response ?5
957
+ should_throw :inside_invalid_callback do
958
+ mock_call.menu do |link|
959
+ link.onetwothree 123
960
+ link.on_invalid { throw :inside_invalid_callback }
961
+ end
962
+ end
963
+ end
964
+
965
+ test "invoke on_premature_timeout when a timeout is encountered" do
966
+ pbx_should_respond_with_successful_background_response ?9
967
+ pbx_should_respond_with_a_wait_for_digit_timeout
968
+
969
+ should_throw :inside_timeout do
970
+ mock_call.menu :timeout => 1 do |link|
971
+ link.something 999
972
+ link.on_premature_timeout { throw :inside_timeout }
973
+ end
974
+ end
975
+ end
976
+
977
+ end
978
+
979
+ context "the Menu class's high-level judgment" do
980
+
981
+ include DialplanCommandTestHelpers
982
+
983
+ test "should match things in ambiguous ranges properly" do
984
+ pbx_should_respond_with_successful_background_response ?1
985
+ pbx_should_respond_with_successful_background_response ?1
986
+ pbx_should_respond_with_successful_background_response ?1
987
+ pbx_should_respond_with_a_wait_for_digit_timeout
988
+
989
+ main_context = Adhearsion::DialPlan::DialplanContextProc.new(:main) { throw :got_here! }
990
+ mock_call.should_receive(:main).and_return main_context
991
+
992
+ should_pass_control_to_a_context_that_throws :got_here! do
993
+ mock_call.menu do |link|
994
+ link.blah 1
995
+ link.main 11..11111
996
+ end
997
+ end
998
+ 111.should === mock_call.extension
999
+ end
1000
+
1001
+ test 'should match things in a range when there are many other non-matching patterns' do
1002
+ pbx_should_respond_with_successful_background_response ?9
1003
+ pbx_should_respond_with_successful_background_response ?9
1004
+ pbx_should_respond_with_successful_background_response ?5
1005
+
1006
+ conferences_context = Adhearsion::DialPlan::DialplanContextProc.new(:conferences) { throw :got_here! }
1007
+ mock_call.should_receive(:conferences).and_return conferences_context
1008
+
1009
+ should_pass_control_to_a_context_that_throws :got_here! do
1010
+ mock_call.menu do |link|
1011
+ link.sales 1
1012
+ link.tech_support 2
1013
+ link.finance 3
1014
+ link.conferences 900..999
1015
+ end
1016
+ end
1017
+ end
1018
+
1019
+ end
1020
+
1021
+ context 'the MenuBuilder' do
1022
+
1023
+ include MenuBuilderTestHelper
1024
+
1025
+ attr_reader :builder
1026
+ before:each do
1027
+ @builder = Adhearsion::VoIP::MenuBuilder.new
1028
+ end
1029
+
1030
+ test "should convert each pattern given to it into a MatchCalculator instance" do
1031
+ returning builder do |link|
1032
+ link.foo 1,2,3
1033
+ link.bar "4", "5", 6
1034
+ end
1035
+
1036
+ builder.weighted_match_calculators.size.should.equal 6
1037
+ builder.weighted_match_calculators.each do |match_calculator|
1038
+ match_calculator.should.be.kind_of Adhearsion::VoIP::MatchCalculator
1039
+ end
1040
+ end
1041
+
1042
+ test "conflicting ranges" do
1043
+ returning builder do |link|
1044
+ link.hundreds 100...200
1045
+ link.thousands 1_000...2_000
1046
+ link.tenthousands 10_000...20_000
1047
+ end
1048
+
1049
+ builder_should_match_with_these_quantities_of_calculated_matches \
1050
+ 1 => { :exact_match_count => 0, :potential_match_count => 11100 },
1051
+ 10 => { :exact_match_count => 0, :potential_match_count => 1110 },
1052
+ 100 => { :exact_match_count => 1, :potential_match_count => 110 },
1053
+ 1_000 => { :exact_match_count => 1, :potential_match_count => 10 },
1054
+ 10_000 => { :exact_match_count => 1, :potential_match_count => 0 },
1055
+ 100_000 => { :exact_match_count => 0, :potential_match_count => 0 }
1056
+
1057
+ end
1058
+
1059
+ test 'a String query ran against multiple Numeric patterns and a range' do
1060
+ returning builder do |link|
1061
+ link.sales 1
1062
+ link.tech_support 2
1063
+ link.finance 3
1064
+ link.conferences 900..999
1065
+ end
1066
+ match = builder.calculate_matches_for "995"
1067
+ require 'pp'
1068
+ # pp match
1069
+ match.should.not.be.potential_match
1070
+ match.should.be.exact_match
1071
+ match.actual_exact_matches.should == ["995"]
1072
+ end
1073
+
1074
+ test "multiple patterns given at once" do
1075
+ returning builder do |link|
1076
+ link.multiple_patterns 1,2,3,4,5,6,7,8,9
1077
+ link.multiple_patterns 100..199, 200..299, 300..399, 400..499, 500..599,
1078
+ 600..699, 700..799, 800..899, 900..999
1079
+ end
1080
+ 1.upto 9 do |num|
1081
+ returning builder.calculate_matches_for(num) do |matches_of_num|
1082
+ matches_of_num.potential_match_count.should.equal 100
1083
+ matches_of_num.exact_match_count.should.equal 1
1084
+ end
1085
+ returning builder.calculate_matches_for((num * 100) + 5) do |matches_of_num|
1086
+ matches_of_num.potential_match_count.should.equal 0
1087
+ matches_of_num.exact_match_count.should.equal 1
1088
+ end
1089
+ end
1090
+ end
1091
+
1092
+ test "numeric literals that don't match but ultimately would" do
1093
+ returning builder do |link|
1094
+ link.nineninenine 999
1095
+ link.shouldnt_match 4444
1096
+ end
1097
+ builder.calculate_matches_for(9).potential_match_count.should.equal 1
1098
+ end
1099
+
1100
+ test "three fixnums that obviously don't conflict" do
1101
+ returning builder do |link|
1102
+ link.one 1
1103
+ link.two 2
1104
+ link.three 3
1105
+ end
1106
+ [[1,2,3,4,'#'], [1,1,1,0,0]].transpose.each do |(input,expected_matches)|
1107
+ matches = builder.calculate_matches_for input
1108
+ matches.exact_match_count.should.equal expected_matches
1109
+ end
1110
+ end
1111
+
1112
+ test "numerical digits mixed with special digits" do
1113
+ returning builder do |link|
1114
+ link.one '5*11#3'
1115
+ link.two '5***'
1116
+ link.three '###'
1117
+ end
1118
+
1119
+ builder_should_match_with_these_quantities_of_calculated_matches \
1120
+ '5' => { :potential_match_count => 2, :exact_match_count => 0 },
1121
+ '*' => { :potential_match_count => 0, :exact_match_count => 0 },
1122
+ '5**' => { :potential_match_count => 1, :exact_match_count => 0 },
1123
+ '5*1' => { :potential_match_count => 1, :exact_match_count => 0 },
1124
+ '5*11#3' => { :potential_match_count => 0, :exact_match_count => 1 },
1125
+ '5*11#4' => { :potential_match_count => 0, :exact_match_count => 0 },
1126
+ '5***' => { :potential_match_count => 0, :exact_match_count => 1 },
1127
+ '###' => { :potential_match_count => 0, :exact_match_count => 1 },
1128
+ '##*' => { :potential_match_count => 0, :exact_match_count => 0 }
1129
+
1130
+ end
1131
+
1132
+ test 'a Fixnum exact match conflicting with a Range that would ultimately match' do
1133
+ returning builder do |link|
1134
+ link.single_digit 1
1135
+ link.range 100..200
1136
+ end
1137
+ matches = builder.calculate_matches_for 1
1138
+ matches.potential_match_count.should.equal 100
1139
+ end
1140
+
1141
+ end
1142
+
1143
+ context 'say_digits command' do
1144
+ include DialplanCommandTestHelpers
1145
+ test 'Can execute the saydigits application using say_digits' do
1146
+ digits = "12345"
1147
+ pbx_should_respond_with_success
1148
+ mock_call.say_digits digits
1149
+ pbx_was_asked_to_execute "saydigits", digits
1150
+ end
1151
+
1152
+ test 'Cannot pass non-integers into say_digits. Will raise an ArgumentError' do
1153
+ the_following_code {
1154
+ mock_call.say_digits 'abc'
1155
+ }.should.raise(ArgumentError)
1156
+
1157
+ the_following_code {
1158
+ mock_call.say_digits '1.20'
1159
+ }.should.raise(ArgumentError)
1160
+ end
1161
+
1162
+ test 'Digits that start with a 0 are considered valid and parsed properly' do
1163
+ digits = "0123"
1164
+ mock_call.should_receive(:execute).once.with("saydigits", digits)
1165
+ mock_call.say_digits digits
1166
+ end
1167
+
1168
+ end
1169
+
1170
+ context 'the enable_feature command' do
1171
+
1172
+ include DialplanCommandTestHelpers
1173
+
1174
+ test 'it should fetch the variable for DYNAMIC_FEATURES at first' do
1175
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_throw :got_variable
1176
+ should_throw :got_variable do
1177
+ mock_call.enable_feature :foobar
1178
+ end
1179
+ end
1180
+
1181
+ test 'should check Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS mapping for configuration setters' do
1182
+ feature_name = :attended_transfer
1183
+
1184
+ assertion = lambda do |arg|
1185
+ arg.should.equal :this_is_the_right_arg
1186
+ throw :inside_assertion!
1187
+ end
1188
+
1189
+ # I had to do this ugly hack because of a bug in Flexmock which prevented me from mocking out Hash#[] :(
1190
+ old_hash_feature_extension = Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name]
1191
+ begin
1192
+ Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = assertion
1193
+ # </uglyhack>
1194
+ should_throw :inside_assertion! do
1195
+ mock_call.enable_feature(feature_name, :this_is_the_right_arg)
1196
+ end
1197
+ ensure
1198
+ Adhearsion::VoIP::Asterisk::Commands::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = old_hash_feature_extension
1199
+ end
1200
+ end
1201
+
1202
+ test 'should separate enabled features with a "#"' do
1203
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return("one")
1204
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'one#bar')
1205
+ mock_call.enable_feature "bar"
1206
+ end
1207
+
1208
+ test 'should not add duplicate enabled dynamic features' do
1209
+ mock_call.should_receive(:variable).once.and_return('eins#zwei')
1210
+ mock_call.enable_feature "eins"
1211
+ end
1212
+
1213
+ test 'should raise an ArgumentError if optional options are given when DYNAMIC_FEATURE_EXTENSIONS does not have a key for the feature name' do
1214
+ the_following_code {
1215
+ mock_call.enable_feature :this_is_not_recognized, :these_features => "are not going to be recognized"
1216
+ }.should.raise ArgumentError
1217
+ end
1218
+
1219
+ test 'enabling :attended_transfer should actually enable the atxfer feature' do
1220
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return ''
1221
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
1222
+ mock_call.enable_feature :attended_transfer
1223
+ end
1224
+
1225
+ test 'the :context optional option when enabling :attended_transfer should set the TRANSFER_CONTEXT variable to the String supplied as a Hash value' do
1226
+ context_name = "direct_dial"
1227
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES").and_return ''
1228
+ mock_call.should_receive(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
1229
+ mock_call.should_receive(:variable).once.with("TRANSFER_CONTEXT" => context_name)
1230
+ mock_call.enable_feature :attended_transfer, :context => context_name
1231
+ end
1232
+
1233
+ test 'enabling :attended_transfer should not add a duplicate if atxfer has been enabled, but it should still set the TRANSFER_CONTEXT variable' do
1234
+ context_name = 'blah'
1235
+ mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'atxfer'
1236
+ mock_call.should_receive(:variable).once.with('TRANSFER_CONTEXT' => context_name)
1237
+ mock_call.enable_feature :attended_transfer, :context => context_name
1238
+ end
1239
+
1240
+ end
1241
+
1242
+ context 'the disable_feature command' do
1243
+
1244
+ include DialplanCommandTestHelpers
1245
+
1246
+ test "should properly remove the feature from the DYNAMIC_FEATURES variable" do
1247
+ mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'foobar#qaz'
1248
+ mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES' => 'qaz')
1249
+ mock_call.disable_feature "foobar"
1250
+ end
1251
+
1252
+ test "should not re-set the variable if the feature wasn't enabled in the first place" do
1253
+ mock_call.should_receive(:variable).once.with('DYNAMIC_FEATURES').and_return 'atxfer'
1254
+ mock_call.should_receive(:variable).never
1255
+ mock_call.disable_feature "jay"
1256
+ end
1257
+
1258
+ end
1259
+
1260
+ context 'jump_to command' do
1261
+
1262
+ include DialplanCommandTestHelpers
1263
+
1264
+ test 'when given a DialplanContextProc as the only argument, it should raise a ControlPassingException with that as the target' do
1265
+ # Having to do this ugly hack because I can't do anything with the exception once I set up an expectation with it normally.
1266
+ dialplan_context = Adhearsion::DialPlan::DialplanContextProc.new("my_context") {}
1267
+ should_throw :finishing_the_rescue_block do
1268
+ begin
1269
+ mock_call.jump_to dialplan_context
1270
+ rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => cpe
1271
+ cpe.target.should.equal dialplan_context
1272
+ throw :finishing_the_rescue_block
1273
+ end
1274
+ end
1275
+ end
1276
+
1277
+ test 'when given a String, it should perform a lookup of the context name' do
1278
+ context_name = 'cool_context'
1279
+ mock_call.should_receive(context_name).once.and_throw :found_context!
1280
+ should_throw :found_context! do
1281
+ mock_call.jump_to context_name
1282
+ end
1283
+ end
1284
+
1285
+ test 'when given a Symbol, it should perform a lookup of the context name' do
1286
+ context_name = :cool_context
1287
+ mock_call.should_receive(context_name).once.and_throw :found_context!
1288
+ should_throw :found_context! do
1289
+ mock_call.jump_to context_name
1290
+ end
1291
+ end
1292
+
1293
+ test "a clearly invalid context name should raise a ContextNotFoundException" do
1294
+ bad_context_name = ' ZOMGS this is A REALLY! STUPID context name . wtf were you thinking?!'
1295
+ the_following_code {
1296
+ mock_call.jump_to bad_context_name
1297
+ }.should.raise Adhearsion::VoIP::DSL::Dialplan::ContextNotFoundException
1298
+ end
1299
+
1300
+ test 'when given an :extension override, the new value should be boxed in a PhoneNumber' do
1301
+ my_context = Adhearsion::DialPlan::DialplanContextProc.new("my_context") {}
1302
+ begin
1303
+ mock_call.jump_to my_context, :extension => 1337
1304
+ rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException
1305
+ # Eating this exception
1306
+ end
1307
+ mock_call.extension.__real_num.should.equal 1337
1308
+ end
1309
+
1310
+ test 'other overrides should be simply metadef()d' do
1311
+ test_context = Adhearsion::DialPlan::DialplanContextProc.new("test_context") {}
1312
+ begin
1313
+ mock_call.jump_to test_context, :caller_id => 1_444_555_6666
1314
+ rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException
1315
+ # Eating this exception
1316
+ end
1317
+ mock_call.caller_id.should.equal 1_444_555_6666
1318
+ end
1319
+
1320
+ end
1321
+
1322
+ context "get variable command" do
1323
+ include DialplanCommandTestHelpers
1324
+
1325
+ test "Getting a variable that isn't set returns nothing" do
1326
+ pbx_should_respond_with "200 result=0"
1327
+ assert !mock_call.get_variable('OMGURFACE')
1328
+ end
1329
+
1330
+ test 'An empty variable should return an empty String' do
1331
+ pbx_should_respond_with_value ""
1332
+ mock_call.get_variable('kablamm').should == ""
1333
+ end
1334
+
1335
+ test "Getting a variable that is set returns its value" do
1336
+ unique_id = "1192470850.1"
1337
+ pbx_should_respond_with_value unique_id
1338
+ variable_value_returned = mock_call.get_variable('UNIQUEID')
1339
+ variable_value_returned.should.equal unique_id
1340
+ end
1341
+ end
1342
+
1343
+ context "duration_of command" do
1344
+ include DialplanCommandTestHelpers
1345
+
1346
+ test "Duration of must take a block" do
1347
+ the_following_code {
1348
+ mock_call.duration_of
1349
+ }.should.raise(LocalJumpError)
1350
+ end
1351
+
1352
+ test "Passed block to duration of is actually executed" do
1353
+ the_following_code {
1354
+ mock_call.duration_of {
1355
+ throw :inside_duration_of
1356
+ }
1357
+ }.should.throw :inside_duration_of
1358
+ end
1359
+
1360
+ test "Duration of block is returned" do
1361
+ start_time = Time.parse('9:25:00')
1362
+ end_time = Time.parse('9:25:05')
1363
+ expected_duration = end_time - start_time
1364
+
1365
+ flexmock(Time).should_receive(:now).twice.and_return(start_time, end_time)
1366
+ duration = mock_call.duration_of {
1367
+ # This doesn't matter
1368
+ }
1369
+
1370
+ duration.should.equal expected_duration
1371
+ end
1372
+ end
1373
+
1374
+ context "Dial command" do
1375
+ include DialplanCommandTestHelpers
1376
+
1377
+ test "should set the caller id if the caller_id option is specified" do
1378
+ mock_call.should_receive(:set_caller_id_number).once
1379
+ mock_call.dial 123, :caller_id => "1234678901"
1380
+ end
1381
+
1382
+ test 'should raise an exception when unknown hash key arguments are given to it' do
1383
+ the_following_code {
1384
+ mock_call.dial 123, :asjndfhasndfahsbdfbhasbdfhabsd => "asbdfhabshdfbajhshfbajsf"
1385
+ }.should.raise ArgumentError
1386
+ end
1387
+
1388
+ test 'should set the caller ID name when given the :name hash key argument' do
1389
+ name = "Jay Phillips"
1390
+ mock_call.should_receive(:set_caller_id_name).once.with(name)
1391
+ mock_call.dial "BlahBlahBlah", :name => name
1392
+ end
1393
+
1394
+ test "should raise an exception when a non-numerical caller_id is specified" do
1395
+ the_following_code {
1396
+ mock_call.dial 911, :caller_id => "zomgz"
1397
+ }.should.raise ArgumentError
1398
+ end
1399
+
1400
+ test 'should pass the value of the :confirm key to dial_macro_option_compiler()' do
1401
+ value_of_confirm_key = {:play => "ohai", :timeout => 30}
1402
+ mock_call.should_receive(:dial_macro_option_compiler).once.with value_of_confirm_key
1403
+ mock_call.dial 123, :confirm => value_of_confirm_key
1404
+ end
1405
+
1406
+ test "should add the return value of dial_macro_option_compiler to the :options key's value given to the dial command" do
1407
+ channel = "SIP/1337"
1408
+ macro_arg = "THISSHOULDGETPASSEDTOASTERISK"
1409
+ timeout = 10
1410
+ options = 'hH'
1411
+
1412
+ mock_call.should_receive(:dial_macro_option_compiler).once.and_return macro_arg
1413
+ mock_call.should_receive(:execute).with('Dial', channel, timeout, options + macro_arg)
1414
+ mock_call.dial channel, :for => timeout, :confirm => true, :options => options
1415
+ end
1416
+
1417
+ test 'should add the return value of dial_macro_option_compiler to the options field when NO :options are given' do
1418
+ channel = "SIP/1337"
1419
+ macro_arg = "THISSHOULDGETPASSEDTOASTERISK"
1420
+ timeout = 10
1421
+ options = 'hH'
1422
+
1423
+ mock_call.should_receive(:dial_macro_option_compiler).once.and_return macro_arg
1424
+ mock_call.should_receive(:execute).with('Dial', channel, timeout, options + macro_arg)
1425
+ mock_call.dial channel, :for => timeout, :confirm => true, :options => options
1426
+ end
1427
+
1428
+ end
1429
+
1430
+ context "The Dial command's :confirm option setting builder" do
1431
+
1432
+ include DialplanCommandTestHelpers
1433
+
1434
+ attr_reader :formatter
1435
+ before :each do
1436
+ @formatter = mock_call.method :dial_macro_option_compiler
1437
+ end
1438
+
1439
+ test 'should allow passing in the :confirm named argument with true' do
1440
+ the_following_code {
1441
+ formatter.call true
1442
+ }.should.not.raise ArgumentError
1443
+ end
1444
+
1445
+ test 'should separate :play options with "++"' do
1446
+ sound_files = *1..10
1447
+ formatter.call(:play => sound_files).should.include sound_files.join('++')
1448
+ end
1449
+
1450
+ test 'should raise an ArgumentError if an invalid Hash key is given' do
1451
+ the_following_code {
1452
+ formatter.call :this_symbol_is_not_valid => 123
1453
+ }.should.raise ArgumentError
1454
+ end
1455
+
1456
+ test "should raise an ArgumentError if the argument's class is not recognized" do
1457
+ the_following_code {
1458
+ formatter.call Time.now # Time is an example strange case
1459
+ }.should.raise ArgumentError
1460
+ end
1461
+
1462
+ test 'should return the contents within a M() Dial argument' do
1463
+ formatter.call(true).should =~ /^M\(.+\)$/
1464
+ end
1465
+
1466
+ test 'should replace the default macro name when given the :macro options' do
1467
+ macro_name = "ivegotalovelybunchofcoconuts"
1468
+ formatter.call(:macro => macro_name).starts_with?("M(#{macro_name}").should.equal true
1469
+ end
1470
+
1471
+ test 'should allow a symbol macro name' do
1472
+ the_following_code {
1473
+ formatter.call(:macro => :foo)
1474
+ }.should.not.raise ArgumentError
1475
+ end
1476
+
1477
+ test 'should only allow alphanumeric and underscores in the macro name' do
1478
+ bad_options = ["this has a space", "foo,bar", 'exists?', 'x^z', '', "!&@&*^!@"]
1479
+ bad_options.each do |bad_option|
1480
+ the_following_code {
1481
+ formatter.call(:macro => bad_option)
1482
+ }.should.raise ArgumentError
1483
+ end
1484
+ end
1485
+
1486
+ test 'should confirm :timeout => :none to 0' do
1487
+ formatter.call(:timeout => :none).should.include "timeout:0"
1488
+ end
1489
+
1490
+ test 'should separate the macro name and the arguments with a caret (^)' do
1491
+ formatter.call(:macro => "jay").should =~ /M\(jay\^.+/
1492
+ end
1493
+
1494
+ test 'should raise an ArgumentError if a caret existed anywhere in the resulting String' do
1495
+ bad_options = [{:play => "foo^bar", :key => "^", :play => ["hello-world", 'lol^cats']}]
1496
+ bad_options.each do |bad_option|
1497
+ the_following_code {
1498
+ formatter.call(bad_option)
1499
+ }.should.raise ArgumentError
1500
+ end
1501
+ end
1502
+
1503
+ test 'should raise an ArgumentError if the :key is not [0-9#*]' do
1504
+ bad_options = %w[& A $ @ . )]
1505
+ bad_options.each do |bad_option|
1506
+ the_following_code {
1507
+ formatter.call :key => bad_option
1508
+ }.should.raise ArgumentError
1509
+ end
1510
+ end
1511
+
1512
+ test 'should raise an ArgumentError if the key is longer than one digit' do
1513
+ the_following_code {
1514
+ formatter.call :key => "55"
1515
+ }.should.raise ArgumentError
1516
+ end
1517
+
1518
+ test 'should raise an ArgumentError if the timeout is not numeric and not :none' do
1519
+ bad_options = [:nonee, Time.now, method(:inspect)]
1520
+ bad_options.each do |bad_option|
1521
+ the_following_code {
1522
+ formatter.call bad_option
1523
+ }.should.raise ArgumentError
1524
+ end
1525
+ end
1526
+
1527
+ test 'should support passing a String argument as a timeout' do
1528
+ the_following_code {
1529
+ formatter.call :timeout => "123"
1530
+ }.should.not.raise ArgumentError
1531
+ end
1532
+
1533
+ test 'should raise an ArgumentError if given a Float' do
1534
+ the_following_code {
1535
+ formatter.call :timeout => 100.0012
1536
+ }.should.raise ArgumentError
1537
+ end
1538
+
1539
+ test 'should allow passing a ActiveSupport::Duration to :timeout' do
1540
+ the_following_code {
1541
+ formatter.call :timeout => 3.minutes
1542
+ }.should.not.raise ArgumentError
1543
+ end
1544
+
1545
+ end
1546
+
1547
+ context 'the dtmf command' do
1548
+
1549
+ include DialplanCommandTestHelpers
1550
+
1551
+ test 'should send the proper AGI command' do
1552
+ digits = '8404#4*'
1553
+ mock_call.dtmf digits
1554
+ pbx_should_have_been_sent "EXEC SendDTMF #{digits}"
1555
+ end
1556
+ end
1557
+
1558
+ context "the last_dial_status command and family" do
1559
+
1560
+ include DialplanCommandTestHelpers
1561
+
1562
+ test 'should convert common DIALSTATUS variables to their appropriate symbols' do
1563
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
1564
+ mock_call.last_dial_status.should.equal :answered
1565
+
1566
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CONGESTION')
1567
+ mock_call.last_dial_status.should.equal :congested
1568
+
1569
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("BUSY")
1570
+ mock_call.last_dial_status.should.equal:busy
1571
+
1572
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("CANCEL")
1573
+ mock_call.last_dial_status.should.equal :cancelled
1574
+
1575
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("NOANSWER")
1576
+ mock_call.last_dial_status.should.equal :unanswered
1577
+
1578
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("CHANUNAVAIL")
1579
+ mock_call.last_dial_status.should.equal :channel_unavailable
1580
+
1581
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return("THISISNOTVALID")
1582
+ mock_call.last_dial_status.should.equal :unknown
1583
+ end
1584
+
1585
+ test 'last_dial_successful? should return true if last_dial_status == :answered' do
1586
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
1587
+ mock_call.last_dial_successful?.should.equal true
1588
+
1589
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CHANUNAVAIL')
1590
+ mock_call.last_dial_successful?.should.equal false
1591
+ end
1592
+
1593
+ test 'last_dial_unsuccessful? should be the opposite of last_dial_successful?' do
1594
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('ANSWER')
1595
+ mock_call.last_dial_unsuccessful?.should.equal false
1596
+
1597
+ mock_call.should_receive(:variable).with("DIALSTATUS").once.and_return('CHANUNAVAIL')
1598
+ mock_call.last_dial_unsuccessful?.should.equal true
1599
+ end
1600
+
1601
+ test 'last_dial_status should not blow up if variable() returns nil. it should return :cancelled' do
1602
+ the_following_code {
1603
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return nil
1604
+ mock_call.last_dial_status.should.equal :cancelled
1605
+
1606
+ mock_call.should_receive(:variable).once.with("DIALSTATUS").and_return nil
1607
+ mock_call.last_dial_successful?.should.equal false
1608
+ }.should.not.raise
1609
+ end
1610
+
1611
+ end
1612
+
1613
+ context "set_caller_id_number command" do
1614
+ include DialplanCommandTestHelpers
1615
+
1616
+ test "should encapsulate the number with quotes" do
1617
+ caller_id = "14445556666"
1618
+ mock_call.should_receive(:raw_response).once.with(%(SET CALLERID "#{caller_id}")).and_return true
1619
+ mock_call.send(:set_caller_id_number, caller_id)
1620
+ end
1621
+ end
1622
+
1623
+ context 'set_caller_id_name command' do
1624
+ include DialplanCommandTestHelpers
1625
+
1626
+ test "should wrap the name in quotes" do
1627
+ name = "Jay Phillips"
1628
+ mock_call.should_receive(:raw_response).once.with(%(SET VARIABLE CALLERID(name) "#{name}")).and_return true
1629
+ mock_call.send(:set_caller_id_name, name)
1630
+ end
1631
+ end
1632
+
1633
+ context "record command" do
1634
+ include DialplanCommandTestHelpers
1635
+
1636
+ test "executes the command SpeechEngine gives it based on the engine name" do
1637
+ mock_speak_command = "returned command doesn't matter"
1638
+ flexmock(Adhearsion::VoIP::Asterisk::Commands::SpeechEngines).should_receive(:cepstral).
1639
+ once.and_return(mock_speak_command)
1640
+ mock_call.should_receive(:execute).once.with(mock_speak_command)
1641
+ mock_call.speak "Spoken text doesn't matter", :cepstral
1642
+ end
1643
+
1644
+ test "raises an InvalidSpeechEngine exception when the engine is 'none'" do
1645
+ the_following_code {
1646
+ mock_call.speak("o hai!", :none)
1647
+ }.should.raise Adhearsion::VoIP::Asterisk::Commands::SpeechEngines::InvalidSpeechEngine
1648
+ end
1649
+
1650
+ test "should default its engine to :none" do
1651
+ the_following_code {
1652
+ flexmock(Adhearsion::VoIP::Asterisk::Commands::SpeechEngines).should_receive(:none).once.
1653
+ and_raise(Adhearsion::VoIP::Asterisk::Commands::SpeechEngines::InvalidSpeechEngine)
1654
+ mock_call.speak "ruby ruby ruby ruby!"
1655
+ }.should.raise Adhearsion::VoIP::Asterisk::Commands::SpeechEngines::InvalidSpeechEngine
1656
+ end
1657
+
1658
+ test "Properly escapes spoken text" # TODO: What are the escaping needs?
1659
+
1660
+ end
1661
+
1662
+ context 'The join command' do
1663
+
1664
+ include DialplanCommandTestHelpers
1665
+
1666
+ test "should pass the 'd' flag when no options are given" do
1667
+ conference_id = "123"
1668
+ mock_call.should_receive(:execute).once.with("MeetMe", conference_id, "d", nil)
1669
+ mock_call.join conference_id
1670
+ end
1671
+
1672
+ test "should pass through any given flags with 'd' appended to it if necessary" do
1673
+ conference_id, flags = "1000", "zomgs"
1674
+ mock_call.should_receive(:execute).once.with("MeetMe", conference_id, flags + "d", nil)
1675
+ mock_call.join conference_id, :options => flags
1676
+ end
1677
+
1678
+ test "should raise an ArgumentError when the pin is not numerical" do
1679
+ the_following_code {
1680
+ mock_call.should_receive(:execute).never
1681
+ mock_call.join 3333, :pin => "letters are bad, mkay?!1"
1682
+ }.should.raise ArgumentError
1683
+ end
1684
+
1685
+ test "should strip out illegal characters from a conference name" do
1686
+ bizarre_conference_name = "a- bc!d&&e--`"
1687
+ normal_conference_name = "abcde"
1688
+ mock_call.should_receive(:execute).twice.with("MeetMe", normal_conference_name, "d", nil)
1689
+
1690
+ mock_call.join bizarre_conference_name
1691
+ mock_call.join normal_conference_name
1692
+ end
1693
+
1694
+ test "should allow textual conference names" do
1695
+ the_following_code {
1696
+ mock_call.should_receive(:execute).once.with_any_args
1697
+ mock_call.join "david bowie's pants"
1698
+ }.should.not.raise
1699
+ end
1700
+
1701
+ end
1702
+
1703
+
1704
+ context 'the DialPlan::ConfirmationManager' do
1705
+
1706
+ include ConfirmationManagerTestHelper
1707
+ include DialplanCommandTestHelpers
1708
+
1709
+ attr_reader :example_encoded_hash, :example_encoded_hash_without_macro_name
1710
+ before :each do
1711
+ @example_encoded_hash_without_macro_name = 'timeout:20!play:foo-bar++qaz_qwerty.gsm!key:#'
1712
+ @example_encoded_hash = 'confirm!' + @example_encoded_hash_without_macro_name
1713
+ end
1714
+
1715
+ test '::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
1716
+ Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash).should ==
1717
+ {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#'}
1718
+ end
1719
+
1720
+ test '::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
1721
+ Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash_without_macro_name).should ==
1722
+ {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#'}
1723
+ end
1724
+
1725
+ test '::decode_hash() should split the sound files in the :play key to an array by splitting by "++"' do
1726
+ decoded_sound_files = Adhearsion::DialPlan::ConfirmationManager.decode_hash(example_encoded_hash)[:play]
1727
+ decoded_sound_files.should.be.kind_of Array
1728
+ decoded_sound_files.size.should.equal 2
1729
+ end
1730
+
1731
+ test 'a call to a party which is acknowledged with the proper key during the call to interruptable_play' do
1732
+ variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
1733
+ encoded_variables = {:network_script => encode_hash(variables)}
1734
+ io_mock = StringIO.new
1735
+
1736
+ mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
1737
+ mock_call.should_receive(:variables).once.and_return encoded_variables
1738
+
1739
+ sound_files = variables[:play]
1740
+
1741
+ manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
1742
+
1743
+ flexstub(manager).should_receive(:result_digit_from).and_return ?0
1744
+ flexstub(manager).should_receive(:raw_response).and_return nil
1745
+
1746
+ flexmock(manager).should_receive(:answer).once
1747
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return '#'
1748
+
1749
+ manager.handle
1750
+ end
1751
+
1752
+ test 'when an timeout is encountered, it should set the MACRO_RESULT variable to CONTINUE' do
1753
+ variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
1754
+ encoded_variables = {:network_script => encode_hash(variables)}
1755
+ io_mock = StringIO.new
1756
+
1757
+ mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
1758
+ mock_call.should_receive(:variables).once.and_return encoded_variables
1759
+
1760
+ sound_files = variables[:play]
1761
+
1762
+ manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
1763
+
1764
+ flexstub(manager).should_receive(:result_digit_from).and_return ?0
1765
+ flexstub(manager).should_receive(:raw_response).and_return nil
1766
+
1767
+ flexmock(manager).should_receive(:answer).once
1768
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return nil
1769
+ flexmock(manager).should_receive(:wait_for_digit).once.with(20).and_return nil
1770
+
1771
+ flexmock(manager).should_receive(:variable).once.with("MACRO_RESULT" => 'CONTINUE')
1772
+
1773
+ manager.handle
1774
+ end
1775
+
1776
+ test '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
1777
+ variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '#', :macro => 'confirmer'}
1778
+ encoded_variables = {:network_script => encode_hash(variables)}
1779
+ io_mock = StringIO.new
1780
+
1781
+ mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
1782
+ mock_call.should_receive(:variables).once.and_return encoded_variables
1783
+
1784
+ sound_files = variables[:play]
1785
+
1786
+ manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
1787
+
1788
+ flexstub(manager).should_receive(:result_digit_from).and_return ?0
1789
+ flexstub(manager).should_receive(:raw_response).and_return nil
1790
+
1791
+ flexmock(manager).should_receive(:answer).once
1792
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return nil
1793
+ flexmock(manager).should_receive(:wait_for_digit).once.with(20).and_return '#'
1794
+
1795
+ manager.handle
1796
+ end
1797
+
1798
+ test 'should restart playback if the key received was not recognized' do
1799
+ variables = {:timeout => 20, :play => ['foo-bar', 'qaz_qwerty.gsm'], :key => '2', :macro => 'confirmer'}
1800
+ encoded_variables = {:network_script => encode_hash(variables)}
1801
+ io_mock = StringIO.new
1802
+
1803
+ mock_call.should_receive(:originating_voip_platform).once.and_return :asterisk
1804
+ mock_call.should_receive(:variables).once.and_return encoded_variables
1805
+
1806
+ sound_files = variables[:play]
1807
+
1808
+ manager = Adhearsion::DialPlan::ConfirmationManager.new(mock_call)
1809
+
1810
+ flexstub(manager).should_receive(:result_digit_from).and_return ?0
1811
+ flexstub(manager).should_receive(:raw_response).and_return nil
1812
+
1813
+ flexmock(manager).should_receive(:answer).once
1814
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return '3' # not :key
1815
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return '#' # not :key
1816
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return '1' # not :key
1817
+ flexmock(manager).should_receive(:interruptable_play).once.with(*sound_files).and_return '2' # matches :key
1818
+
1819
+ flexmock(manager).should_receive(:wait_for_digit).never # We never let it get to the point where it may timeout
1820
+ flexmock(manager).should_receive(:variable).never # We succeed by not setting the MACRO_RESULT variable
1821
+
1822
+ manager.handle
1823
+ end
1824
+
1825
+ end
1826
+
1827
+ BEGIN {
1828
+ module DialplanCommandTestHelpers
1829
+ def self.included(test_case)
1830
+ test_case.send(:attr_reader, :mock_call, :input, :output)
1831
+
1832
+ test_case.before do
1833
+ @input = MockSocket.new
1834
+ @output = MockSocket.new
1835
+ @mock_call = Object.new
1836
+ @mock_call.metaclass.send(:attr_reader, :call)
1837
+ mock_call.extend(Adhearsion::VoIP::Asterisk::Commands)
1838
+ flexmock(mock_call) do |call|
1839
+ call.should_receive(:from_pbx).and_return(input)
1840
+ call.should_receive(:to_pbx).and_return(output)
1841
+ end
1842
+ end
1843
+ end
1844
+
1845
+ class MockSocket
1846
+
1847
+ def print(message)
1848
+ messages << message
1849
+ end
1850
+
1851
+ def read
1852
+ messages.shift
1853
+ end
1854
+
1855
+ def gets
1856
+ read
1857
+ end
1858
+
1859
+ def messages
1860
+ @messages ||= []
1861
+ end
1862
+ end
1863
+
1864
+
1865
+ private
1866
+
1867
+ def should_pass_control_to_a_context_that_throws(symbol, &block)
1868
+ did_the_rescue_block_get_executed = false
1869
+ begin
1870
+ yield
1871
+ rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => cpe
1872
+ did_the_rescue_block_get_executed = true
1873
+ cpe.target.should.throw symbol
1874
+ rescue => e
1875
+ did_the_rescue_block_get_executed = true
1876
+ raise e
1877
+ ensure
1878
+ did_the_rescue_block_get_executed.should.be true
1879
+ end
1880
+ end
1881
+
1882
+ def should_throw(sym=nil,&block)
1883
+ block.should.throw(*[sym].compact)
1884
+ end
1885
+
1886
+ def mock_route_calculation_with(*definitions)
1887
+ flexmock(Adhearsion::VoIP::DSL::DialingDSL).should_receive(:calculate_routes_for).and_return(definitions)
1888
+ end
1889
+
1890
+ def pbx_should_have_been_sent(message)
1891
+ output.gets.should.equal message
1892
+ end
1893
+
1894
+ def pbx_should_respond_with(message)
1895
+ input.print message
1896
+ end
1897
+
1898
+ def pbx_should_respond_with_digits(string_of_digits)
1899
+ pbx_should_respond_with "200 result=#{string_of_digits}"
1900
+ end
1901
+
1902
+ def pbx_should_respond_with_digits_and_timeout(string_of_digits)
1903
+ pbx_should_respond_with "200 result=#{string_of_digits} (timeout)"
1904
+ end
1905
+
1906
+ def pbx_should_respond_to_timeout(timeout)
1907
+ pbx_should_respond_with "200 result=#{timeout}"
1908
+ end
1909
+
1910
+ def pbx_should_respond_with_value(value)
1911
+ pbx_should_respond_with "200 result=1 (#{value})"
1912
+ end
1913
+
1914
+ def pbx_should_respond_with_success(success_code = nil)
1915
+ pbx_should_respond_with pbx_success_response(success_code)
1916
+ end
1917
+
1918
+ def pbx_should_respond_with_failure(failure_code = nil)
1919
+ pbx_should_respond_with(pbx_failure_response(failure_code))
1920
+ end
1921
+
1922
+ def pbx_should_respond_with_successful_background_response(digit=0)
1923
+ pbx_should_respond_with_success digit.kind_of?(String) ? digit[0] : digit
1924
+ end
1925
+
1926
+ def pbx_should_respond_with_a_wait_for_digit_timeout
1927
+ pbx_should_respond_with_successful_background_response 0
1928
+ end
1929
+
1930
+ def pbx_success_response(success_code = nil)
1931
+ "200 result=#{success_code || default_success_code}"
1932
+ end
1933
+
1934
+ def default_success_code
1935
+ '1'
1936
+ end
1937
+
1938
+ def pbx_failure_response(failure_code = nil)
1939
+ "200 result=#{failure_code || default_failure_code}"
1940
+ end
1941
+
1942
+ def default_failure_code
1943
+ '0'
1944
+ end
1945
+
1946
+ def output_stream_matches(pattern)
1947
+ assert_match(pattern, output.gets)
1948
+ end
1949
+
1950
+ module OutputStreamMatchers
1951
+ def pbx_was_asked_to_play(*audio_files)
1952
+ audio_files.each do |audio_file|
1953
+ output_stream_matches(/playback #{audio_file}/)
1954
+ end
1955
+ end
1956
+
1957
+ def pbx_was_asked_to_play_number(number)
1958
+ output_stream_matches(/saynumber #{number}/)
1959
+ end
1960
+
1961
+ def pbx_was_asked_to_play_time(number)
1962
+ output_stream_matches(/sayunixtime #{number}/)
1963
+ end
1964
+
1965
+ def pbx_was_asked_to_execute(application, *options)
1966
+ output_stream_matches(/exec saydigits #{options.join('|')}/i)
1967
+ end
1968
+ end
1969
+ include OutputStreamMatchers
1970
+
1971
+ def assert_success(response)
1972
+ response.should.equal pbx_success_response
1973
+ end
1974
+
1975
+ end
1976
+
1977
+
1978
+ module MenuBuilderTestHelper
1979
+ def builder_should_match_with_these_quantities_of_calculated_matches(checks)
1980
+ checks.each do |check,hash|
1981
+ hash.each_pair do |method_name,intended_quantity|
1982
+ message = "There were supposed to be #{intended_quantity} #{method_name.to_s.humanize} calculated."
1983
+ builder.calculate_matches_for(check).send(method_name).
1984
+ should.messaging(message).equal(intended_quantity)
1985
+ end
1986
+ end
1987
+ end
1988
+ end
1989
+
1990
+ module MenuTestHelper
1991
+
1992
+ def pbx_should_send_digits(*digits)
1993
+ digits.each do |digit|
1994
+ digit = nil if digit == :timeout
1995
+ mock_call.should_receive(:interruptable_play).once.and_return(digit)
1996
+ end
1997
+ end
1998
+ end
1999
+
2000
+ module ConfirmationManagerTestHelper
2001
+ def encode_hash(hash)
2002
+ Adhearsion::DialPlan::ConfirmationManager.encode_hash_for_dial_macro_argument(hash)
2003
+ end
2004
+ end
2005
+
2006
+ }