adhearsion-asterisk 0.1.0

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 (33) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +6 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +143 -0
  8. data/Rakefile +23 -0
  9. data/adhearsion-asterisk.gemspec +35 -0
  10. data/lib/adhearsion-asterisk.rb +1 -0
  11. data/lib/adhearsion/asterisk.rb +12 -0
  12. data/lib/adhearsion/asterisk/config_generator.rb +103 -0
  13. data/lib/adhearsion/asterisk/config_generator/agents.rb +138 -0
  14. data/lib/adhearsion/asterisk/config_generator/queues.rb +247 -0
  15. data/lib/adhearsion/asterisk/config_generator/voicemail.rb +238 -0
  16. data/lib/adhearsion/asterisk/config_manager.rb +60 -0
  17. data/lib/adhearsion/asterisk/plugin.rb +464 -0
  18. data/lib/adhearsion/asterisk/queue_proxy.rb +177 -0
  19. data/lib/adhearsion/asterisk/queue_proxy/agent_proxy.rb +81 -0
  20. data/lib/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy.rb +132 -0
  21. data/lib/adhearsion/asterisk/version.rb +5 -0
  22. data/spec/adhearsion/asterisk/config_generators/agents_spec.rb +258 -0
  23. data/spec/adhearsion/asterisk/config_generators/queues_spec.rb +322 -0
  24. data/spec/adhearsion/asterisk/config_generators/voicemail_spec.rb +306 -0
  25. data/spec/adhearsion/asterisk/config_manager_spec.rb +125 -0
  26. data/spec/adhearsion/asterisk/plugin_spec.rb +618 -0
  27. data/spec/adhearsion/asterisk/queue_proxy/agent_proxy_spec.rb +90 -0
  28. data/spec/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy_spec.rb +145 -0
  29. data/spec/adhearsion/asterisk/queue_proxy_spec.rb +156 -0
  30. data/spec/adhearsion/asterisk_spec.rb +9 -0
  31. data/spec/spec_helper.rb +23 -0
  32. data/spec/support/the_following_code.rb +3 -0
  33. metadata +229 -0
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/asterisk/config_manager'
3
+
4
+ module ConfigurationManagerTestHelper
5
+ def mock_config_manager
6
+ mock_config_manager_for sample_standard_config
7
+ end
8
+
9
+ def mock_config_manager_for(config_string)
10
+ new_config_manager_with("bogus filename").tap do |manager|
11
+ File.expects(:open).returns config_string
12
+ end
13
+ end
14
+
15
+ def new_config_manager_with(filename)
16
+ Adhearsion::Asterisk::ConfigurationManager.new(filename)
17
+ end
18
+
19
+ def sample_standard_config
20
+ <<-CONFIG
21
+ [jicksta]
22
+ foo=bar
23
+ qaz=qwerty
24
+ baz=zxcvb
25
+ [picard]
26
+ type=friend
27
+ insecure=very
28
+ host=dynamic
29
+ secret=blargh
30
+ CONFIG
31
+ end
32
+ end
33
+
34
+ describe "The configuration file parser behavior" do
35
+
36
+ include ConfigurationManagerTestHelper
37
+
38
+ it "should expose a sections array" do
39
+ manager = mock_config_manager
40
+ manager.sections.map(&:first).should == ["jicksta", "picard"]
41
+ end
42
+
43
+ it "should ignore comments" do
44
+ context_names = %w(monkey data)
45
+ tested_key_name, tested_key_value_before_comment = "bar", "baz"
46
+ manager = mock_config_manager_for <<-CONFIG
47
+ [monkey]
48
+ #{tested_key_name}=#{tested_key_value_before_comment};thiscommentshouldnotbehere
49
+ asdf=fdsa
50
+ ;[data]
51
+ ;ignored=asdf
52
+ CONFIG
53
+ manager.sections.map(&:first).should == [context_names.first]
54
+ manager[context_names.first].size.should be 2
55
+ manager[context_names.first][tested_key_name].should == tested_key_value_before_comment
56
+ end
57
+
58
+ it "should match context names with dashes and underscores" do
59
+ context_names = %w"foo-bar qaz_b-a-z"
60
+ definition_string = <<-CONFIG
61
+ [#{context_names.first}]
62
+ platypus=no
63
+ zebra=no
64
+ crappyconfig=yes
65
+
66
+ [#{context_names.last}]
67
+ callerid="Jay Phillips" <133>
68
+ CONFIG
69
+ mock_config_manager_for(definition_string).sections.map(&:first).should == context_names
70
+ end
71
+
72
+ it "should strip whitespace around keys and values" do
73
+ section_name = "hey-there-hot-momma"
74
+ tested_key_name, tested_key_value_before_comment = "bar", "i heart white space. SIKE!"
75
+ config_manager = mock_config_manager_for <<-CONFIG
76
+ \t[#{section_name}]
77
+
78
+ thereis = a lot of whitespace after these
79
+
80
+ #{tested_key_name} = \t\t\t #{tested_key_value_before_comment}
81
+
82
+ CONFIG
83
+ config_manager[section_name][tested_key_name].should == tested_key_value_before_comment
84
+ end
85
+
86
+ it "should return a Hash of properties when searching for an existing section" do
87
+ result = mock_config_manager["jicksta"]
88
+ result.should be_a_kind_of Hash
89
+ result.size.should be 3
90
+ end
91
+
92
+ it "should return nil when searching for a non-existant section" do
93
+ mock_config_manager["i-so-dont-exist-dude"].should be nil
94
+ end
95
+ end
96
+
97
+ describe "The configuration file writer" do
98
+
99
+ include ConfigurationManagerTestHelper
100
+
101
+ attr_reader :config_manager
102
+
103
+ before(:each) do
104
+ @config_manager = mock_config_manager
105
+ end
106
+
107
+ it "should remove an old section when replacing it" do
108
+ config_manager.delete_section "picard"
109
+ config_manager.sections.map(&:first).should == ["jicksta"]
110
+ end
111
+
112
+ it "should add a new section to the end" do
113
+ section_name = "wittynamehere"
114
+ config_manager.new_section(section_name, :type => "friend",
115
+ :witty => "yes",
116
+ :shaken => "yes",
117
+ :stirred => "no")
118
+ new_section = config_manager.sections.last
119
+ new_section.first.should be section_name
120
+ new_section.last.size.should be 4
121
+ end
122
+ end
123
+
124
+ describe "The configuration file generator" do
125
+ end
@@ -0,0 +1,618 @@
1
+ require 'spec_helper'
2
+
3
+ module Adhearsion::Asterisk
4
+ describe 'A DialPlan::ExecutionEnvironment with the plugin loaded' do
5
+ before(:all) { Adhearsion::Plugin.load_methods }
6
+
7
+ let(:mock_call) { stub_everything 'Call', :originating_voip_platform => :punchblock }
8
+
9
+ subject do
10
+ Adhearsion::CallController.new mock_call
11
+ end
12
+
13
+ describe '#agi' do
14
+ let :expected_agi_command do
15
+ Punchblock::Component::Asterisk::AGI::Command.new :name => 'Dial', :params => ['4044754842', 15]
16
+ end
17
+
18
+ let :complete_event do
19
+ Punchblock::Event::Complete.new.tap do |c|
20
+ c.reason = Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => 200, :result => 1, :data => 'foobar'
21
+ end
22
+ end
23
+
24
+ it 'should execute an AGI command with the specified name and parameters and return the response code, response and data' do
25
+ Punchblock::Component::Asterisk::AGI::Command.any_instance.stubs :complete_event => mock('Complete', :resource => complete_event)
26
+
27
+ subject.expects(:execute_component_and_await_completion).once.with expected_agi_command
28
+ values = subject.agi 'Dial', '4044754842', 15
29
+ values.should == [200, 1, 'foobar']
30
+ end
31
+ end
32
+
33
+ describe '#execute' do
34
+ it 'calls #agi and prefixes the command with EXEC' do
35
+ subject.expects(:agi).once.with 'EXEC Dial', '4044754842', 15
36
+ subject.execute 'Dial', '4044754842', 15
37
+ end
38
+ end
39
+
40
+ describe '#verbose' do
41
+ it 'executes the VERBOSE AGI command' do
42
+ subject.expects(:agi).once.with 'VERBOSE', 'Foo Bar!', 15
43
+ subject.verbose 'Foo Bar!', 15
44
+ end
45
+ end
46
+
47
+ describe '#enable_feature' do
48
+ it 'it should fetch the variable for DYNAMIC_FEATURES at first' do
49
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES").throws(:got_variable)
50
+ expect {
51
+ subject.enable_feature :foobar
52
+ }.to throw_symbol :got_variable
53
+ end
54
+
55
+ it 'should check Adhearsion::Asterisk::Plugin::DYNAMIC_FEATURE_EXTENSIONS mapping for configuration setters' do
56
+ feature_name = :attended_transfer
57
+
58
+ assertion = lambda do |arg|
59
+ arg.should == :this_is_the_right_arg
60
+ throw :inside_assertion!
61
+ end
62
+
63
+ # I had to do this ugly hack because of a bug in Flexmock which prevented me from mocking out Hash#[] :(
64
+ # FIXME: mock Hash
65
+ # ...DYNAMIC_FEATURE_EXTENSIONS.expects(feature_name => assertion)
66
+
67
+ old_hash_feature_extension = Adhearsion::Asterisk::Plugin::DYNAMIC_FEATURE_EXTENSIONS[feature_name]
68
+ begin
69
+ Adhearsion::Asterisk::Plugin::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = assertion
70
+
71
+ subject.expects(:enable_feature).once.with(feature_name, :this_is_the_right_arg).throws :inside_assertion!
72
+ expect { subject.enable_feature(feature_name, :this_is_the_right_arg)}.to throw_symbol :inside_assertion!
73
+ ensure
74
+ Adhearsion::Asterisk::Plugin::DYNAMIC_FEATURE_EXTENSIONS[feature_name] = old_hash_feature_extension
75
+ end
76
+ end
77
+
78
+ it 'should separate enabled features with a "#"' do
79
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES").returns("one")
80
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES" => 'one#bar')
81
+ subject.enable_feature "bar"
82
+ end
83
+
84
+ it 'should not add duplicate enabled dynamic features' do
85
+ subject.expects(:variable).once.returns('eins#zwei')
86
+ subject.enable_feature "eins"
87
+ end
88
+
89
+ it 'should raise an ArgumentError if optional options are given when DYNAMIC_FEATURE_EXTENSIONS does not have a key for the feature name' do
90
+ expect { subject.enable_feature :this_is_not_recognized,
91
+ :these_features => "are not going to be recognized"
92
+ }.to raise_error ArgumentError
93
+ end
94
+
95
+ it 'enabling :attended_transfer should actually enable the atxfer feature' do
96
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES").returns ''
97
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
98
+ subject.enable_feature :attended_transfer
99
+ end
100
+
101
+ it 'the :context optional option when enabling :attended_transfer should set the TRANSFER_CONTEXT variable to the String supplied as a Hash value' do
102
+ context_name = "direct_dial"
103
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES").returns ''
104
+ subject.expects(:variable).once.with("DYNAMIC_FEATURES" => 'atxfer')
105
+ subject.expects(:variable).once.with("TRANSFER_CONTEXT" => context_name)
106
+ subject.enable_feature :attended_transfer, :context => context_name
107
+ end
108
+
109
+ it 'enabling :attended_transfer should not add a duplicate if atxfer has been enabled, but it should still set the TRANSFER_CONTEXT variable' do
110
+ context_name = 'blah'
111
+ subject.expects(:variable).once.with('DYNAMIC_FEATURES').returns 'atxfer'
112
+ subject.expects(:variable).once.with('TRANSFER_CONTEXT' => context_name)
113
+ subject.enable_feature :attended_transfer, :context => context_name
114
+ end
115
+ end
116
+
117
+ describe '#disable_feature' do
118
+ it "should properly remove the feature from the DYNAMIC_FEATURES variable" do
119
+ subject.expects(:variable).once.with('DYNAMIC_FEATURES').returns 'foobar#qaz'
120
+ subject.expects(:variable).once.with('DYNAMIC_FEATURES' => 'qaz')
121
+ subject.disable_feature "foobar"
122
+ end
123
+
124
+ it "should not re-set the variable if the feature wasn't enabled in the first place" do
125
+ subject.expects(:variable).once.with('DYNAMIC_FEATURES').returns 'atxfer'
126
+ subject.expects(:variable).never
127
+ subject.disable_feature "jay"
128
+ end
129
+ end
130
+
131
+ describe "#variable" do
132
+ it "should call set_variable when Hash argument given" do
133
+ subject.expects(:set_variable).once.with :ohai, "ur_home_erly"
134
+ subject.variable :ohai => 'ur_home_erly'
135
+ end
136
+
137
+ it "should call set_variable for every Hash-key given" do
138
+ many_args = { :a => :b, :c => :d, :e => :f, :g => :h}
139
+ subject.expects(:set_variable).times(many_args.size)
140
+ subject.variable many_args
141
+ end
142
+
143
+ it "should call get_variable for every String given" do
144
+ variables = ["foo", "bar", :qaz, :qwerty, :baz]
145
+ variables.each do |var|
146
+ subject.expects(:get_variable).once.with(var).returns("X")
147
+ end
148
+ subject.variable(*variables).should == ["X"] * variables.size
149
+ end
150
+
151
+ it "should NOT return an Array when just one arg is given" do
152
+ subject.expects(:get_variable).once.returns "lol"
153
+ subject.variable(:foo).should_not be_a Array
154
+ end
155
+
156
+ it "should raise an ArgumentError when a Hash and normal args are given" do
157
+ lambda {
158
+ subject.variable 5, 4, 3, 2, 1, :foo => :bar
159
+ }.should raise_error ArgumentError
160
+ end
161
+ end
162
+
163
+ describe "#set_variable" do
164
+ it "uses SET VARIABLE" do
165
+ subject.expects(:agi).once.with 'SET VARIABLE', 'foo', 'i can " has ruby?'
166
+ subject.set_variable 'foo', 'i can " has ruby?'
167
+ end
168
+ end
169
+
170
+ describe '#get_variable' do
171
+ it 'uses GET VARIABLE and extracts the value from the data' do
172
+ subject.expects(:agi).once.with('GET VARIABLE', 'foo').returns [200, 1, 'bar']
173
+ subject.get_variable('foo').should == 'bar'
174
+ end
175
+ end
176
+
177
+ describe "#sip_add_header" do
178
+ it "executes SIPAddHeader" do
179
+ subject.expects(:execute).once.with 'SIPAddHeader', 'x-ahn-header: rubyrox'
180
+ subject.sip_add_header "x-ahn-header", "rubyrox"
181
+ end
182
+ end
183
+
184
+ describe "#sip_get_header" do
185
+ it "uses #get_variable to get the header value" do
186
+ value = 'jason-was-here'
187
+ subject.expects(:get_variable).once.with('SIP_HEADER(x-ahn-header)').returns value
188
+ subject.sip_get_header("x-ahn-header").should == value
189
+ end
190
+ end
191
+
192
+ describe '#join' do
193
+ it "should pass the 'd' flag when no options are given" do
194
+ conference_id = "123"
195
+ subject.expects(:execute).once.with("MeetMe", conference_id, "d", nil)
196
+ subject.meetme conference_id
197
+ end
198
+
199
+ it "should pass through any given flags with 'd' appended to it if necessary" do
200
+ conference_id, flags = "1000", "zomgs"
201
+ subject.expects(:execute).once.with("MeetMe", conference_id, flags + "d", nil)
202
+ subject.meetme conference_id, :options => flags
203
+ end
204
+
205
+ it "should NOT pass the 'd' flag when requiring static conferences" do
206
+ conference_id, options = "1000", {:use_static_conf => true}
207
+ subject.expects(:execute).once.with("MeetMe", conference_id, "", nil)
208
+ subject.meetme conference_id, options
209
+ end
210
+
211
+ it "should raise an ArgumentError when the pin is not numerical" do
212
+ lambda {
213
+ subject.expects(:execute).never
214
+ subject.meetme 3333, :pin => "letters are bad, mkay?!1"
215
+ }.should raise_error ArgumentError
216
+ end
217
+
218
+ it "should strip out illegal characters from a conference name" do
219
+ bizarre_conference_name = "a- bc!d&&e--`"
220
+ normal_conference_name = "abcde"
221
+ subject.expects(:execute).twice.with("MeetMe", normal_conference_name, "d", nil)
222
+
223
+ subject.meetme bizarre_conference_name
224
+ subject.meetme normal_conference_name
225
+ end
226
+
227
+ it "should allow textual conference names" do
228
+ lambda {
229
+ subject.expects(:execute).once
230
+ subject.meetme "david bowie's pants"
231
+ }.should_not raise_error
232
+ end
233
+ end
234
+
235
+ describe '#voicemail' do
236
+ it 'should not send the context name when none is given' do
237
+ subject.expects(:execute).once.with('voicemail', 123, '').throws :sent_voicemail!
238
+ lambda { subject.voicemail 123 }.should throw_symbol(:sent_voicemail!)
239
+ end
240
+
241
+ it 'should send the context name when one is given' do
242
+ mailbox_number, context_name = 333, 'doesntmatter'
243
+ subject.expects(:execute).once.with('voicemail', "#{mailbox_number}@#{context_name}", '').throws :sent_voicemail!
244
+ lambda { subject.voicemail(context_name => mailbox_number) }.should throw_symbol(:sent_voicemail!)
245
+ end
246
+
247
+ it 'should pass in the s option if :skip => true' do
248
+ mailbox_number = '012'
249
+ subject.expects(:execute).once.with('voicemail', mailbox_number, 's').throws :sent_voicemail!
250
+ lambda { subject.voicemail(mailbox_number, :skip => true) }.should throw_symbol(:sent_voicemail!)
251
+ end
252
+
253
+ it 'should combine mailbox numbers with the context name given when both are given' do
254
+ subject.expects(:variable).with("VMSTATUS").returns 'SUCCESS'
255
+ context = "lolcats"
256
+ mailboxes = [1,2,3,4,5]
257
+ mailboxes_with_context = mailboxes.map { |mailbox| [mailbox, context].join '@' }
258
+ subject.expects(:execute).once.with('voicemail', mailboxes_with_context.join('&'), '')
259
+ subject.voicemail context => mailboxes
260
+ end
261
+
262
+ it 'should raise an argument error if the mailbox number is not numerical' do
263
+ lambda {
264
+ subject.voicemail :foo => "bar"
265
+ }.should raise_error ArgumentError
266
+ end
267
+
268
+ it 'should raise an argument error if too many arguments are supplied' do
269
+ lambda {
270
+ subject.voicemail "wtfisthisargument", :context_name => 123, :greeting => :busy
271
+ }.should raise_error ArgumentError
272
+ end
273
+
274
+ it 'should raise an ArgumentError if multiple context names are given' do
275
+ lambda {
276
+ subject.voicemail :one => [1,2,3], :two => [11,22,33]
277
+ }.should raise_error ArgumentError
278
+ end
279
+
280
+ it "should raise an ArgumentError when the :greeting value isn't recognized" do
281
+ lambda {
282
+ subject.voicemail :context_name => 123, :greeting => :zomgz
283
+ }.should raise_error ArgumentError
284
+ end
285
+
286
+ it 'should pass in the u option if :greeting => :unavailable' do
287
+ mailbox_number = '776'
288
+ subject.expects(:execute).once.with('voicemail', mailbox_number, 'u').throws :sent_voicemail!
289
+ lambda { subject.voicemail(mailbox_number, :greeting => :unavailable) }.should throw_symbol(:sent_voicemail!)
290
+ end
291
+
292
+ it 'should pass in both the skip and greeting options if both are supplied' do
293
+ mailbox_number = '4'
294
+ subject.expects(:execute).once.with('voicemail', mailbox_number, 'u').throws :sent_voicemail!
295
+ lambda { subject.voicemail(mailbox_number, :greeting => :unavailable) }.should throw_symbol(:sent_voicemail!)
296
+ end
297
+
298
+ it 'should raise an ArgumentError if mailbox_number is blank?()' do
299
+ lambda {
300
+ subject.voicemail ''
301
+ }.should raise_error ArgumentError
302
+
303
+ lambda {
304
+ subject.voicemail nil
305
+ }.should raise_error ArgumentError
306
+ end
307
+
308
+ it 'should pass in the b option if :gretting => :busy' do
309
+ mailbox_number = '1'
310
+ subject.expects(:execute).once.with('voicemail', mailbox_number, 'b').throws :sent_voicemail!
311
+ lambda { subject.voicemail(mailbox_number, :greeting => :busy) }.should throw_symbol(:sent_voicemail!)
312
+ end
313
+
314
+ it 'should return true if VMSTATUS == "SUCCESS"' do
315
+ subject.expects(:execute).once
316
+ subject.expects(:variable).once.with('VMSTATUS').returns "SUCCESS"
317
+ subject.voicemail(3).should be true
318
+ end
319
+
320
+ it 'should return false if VMSTATUS == "USEREXIT"' do
321
+ subject.expects(:execute).once
322
+ subject.expects(:variable).once.with('VMSTATUS').returns "USEREXIT"
323
+ subject.voicemail(2).should be false
324
+ end
325
+
326
+ it 'should return nil if VMSTATUS == "FAILED"' do
327
+ subject.expects(:execute).once
328
+ subject.expects(:variable).once.with('VMSTATUS').returns "FAILED"
329
+ subject.voicemail(2).should be nil
330
+ end
331
+ end
332
+
333
+ describe '#voicemail_main' do
334
+ it "the :folder Hash key argument should wrap the value in a()" do
335
+ folder = "foobar"
336
+ mailbox = 81
337
+ subject.expects(:execute).once.with("VoiceMailMain", "#{mailbox}","a(#{folder})")
338
+ subject.voicemail_main :mailbox => mailbox, :folder => folder
339
+ end
340
+
341
+ it ':authenticate should pass in the "s" option if given false' do
342
+ mailbox = 333
343
+ subject.expects(:execute).once.with("VoiceMailMain", "#{mailbox}","s")
344
+ subject.voicemail_main :mailbox => mailbox, :authenticate => false
345
+ end
346
+
347
+ it ':authenticate should pass in the s option if given false' do
348
+ mailbox = 55
349
+ subject.expects(:execute).once.with("VoiceMailMain", "#{mailbox}")
350
+ subject.voicemail_main :mailbox => mailbox, :authenticate => true
351
+ end
352
+
353
+ it 'should not pass any flags only a mailbox is given' do
354
+ mailbox = "1"
355
+ subject.expects(:execute).once.with("VoiceMailMain", "#{mailbox}")
356
+ subject.voicemail_main :mailbox => mailbox
357
+ end
358
+
359
+ it 'when given no mailbox or context an empty string should be passed to execute as the first argument' do
360
+ subject.expects(:execute).once.with("VoiceMailMain", "", "s")
361
+ subject.voicemail_main :authenticate => false
362
+ end
363
+
364
+ it 'should properly concatenate the options when given multiple ones' do
365
+ folder = "ohai"
366
+ mailbox = 9999
367
+ subject.expects(:execute).once.with("VoiceMailMain", "#{mailbox}", "sa(#{folder})")
368
+ subject.voicemail_main :mailbox => mailbox, :authenticate => false, :folder => folder
369
+ end
370
+
371
+ it 'should not require any arguments' do
372
+ subject.expects(:execute).once.with("VoiceMailMain")
373
+ subject.voicemail_main
374
+ end
375
+
376
+ it 'should pass in the "@context_name" part in if a :context is given and no mailbox is given' do
377
+ context_name = "icanhascheezburger"
378
+ subject.expects(:execute).once.with("VoiceMailMain", "@#{context_name}")
379
+ subject.voicemail_main :context => context_name
380
+ end
381
+
382
+ it "should raise an exception if the folder has a space or malformed characters in it" do
383
+ ["i has a space", "exclaim!", ",", ""].each do |bad_folder_name|
384
+ lambda {
385
+ subject.voicemail_main :mailbox => 123, :folder => bad_folder_name
386
+ }.should raise_error ArgumentError
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "#queue" do
392
+ it 'should not create separate objects for queues with basically the same name' do
393
+ subject.queue('foo').should be subject.queue('foo')
394
+ subject.queue('bar').should be subject.queue(:bar)
395
+ end
396
+
397
+ it "should return an instance of QueueProxy" do
398
+ subject.queue("foobar").should be_a_kind_of Adhearsion::Asterisk::QueueProxy
399
+ end
400
+
401
+ it "should set the QueueProxy's name" do
402
+ subject.queue("foobar").name.should == 'foobar'
403
+ end
404
+
405
+ it "should set the QueueProxy's environment" do
406
+ subject.queue("foobar").environment.should == subject
407
+ end
408
+ end#describe #queue
409
+
410
+ describe "#play" do
411
+ let(:audiofile) { "tt-monkeys" }
412
+ let(:audiofile2) { "tt-weasels" }
413
+
414
+ it 'should return true if play proceeds correctly' do
415
+ subject.expects(:play!).with([audiofile])
416
+ subject.play(audiofile).should be true
417
+ end
418
+
419
+ it 'should return false if an audio file cannot be found' do
420
+ subject.expects(:play!).with([audiofile]).raises(Adhearsion::PlaybackError)
421
+ subject.play(audiofile).should be false
422
+
423
+ end
424
+
425
+ it 'should return false when audio files cannot be found' do
426
+ subject.expects(:play!).with([audiofile, audiofile2]).raises(Adhearsion::PlaybackError)
427
+ subject.play(audiofile, audiofile2).should be false
428
+ end
429
+ end
430
+
431
+ describe "#play!" do
432
+ let(:audiofile) { "tt-monkeys" }
433
+ let(:audiofile2) { "tt-weasels" }
434
+ let(:numeric) { 20 }
435
+ let(:numeric_string) { "42" }
436
+ let(:date) { Date.parse('2011-10-24') }
437
+ let(:time) { Time.at(875121313) }
438
+
439
+ describe "with a single argument" do
440
+ it 'passing a single string to play() results in play_soundfile being called with that file name' do
441
+ subject.expects(:play_time).with([audiofile]).returns(false)
442
+ subject.expects(:play_numeric).with(audiofile).returns(false)
443
+ subject.expects(:play_soundfile).with(audiofile).returns(true)
444
+ subject.play!(audiofile)
445
+ end
446
+
447
+ it 'If a number is passed to play(), the play_numeric method is called with that argument' do
448
+ subject.expects(:play_time).with([numeric]).returns(false)
449
+ subject.expects(:play_numeric).with(numeric).returns(true)
450
+ subject.play!(numeric)
451
+ end
452
+
453
+ it 'if a string representation of a number is passed to play(), the play_numeric method is called with that argument' do
454
+ subject.expects(:play_time).with([numeric_string]).returns(false)
455
+ subject.expects(:play_numeric).with(numeric_string).returns(true)
456
+ subject.play!(numeric_string)
457
+ end
458
+
459
+ it 'If a Time is passed to play(), the play_time method is called with that argument' do
460
+ subject.expects(:play_time).with([time]).returns(true)
461
+ subject.play!(time)
462
+ end
463
+
464
+ it 'If a Date is passed to play(), the play_time method is called with that argument' do
465
+ subject.expects(:play_time).with([date]).returns(true)
466
+ subject.play!(date)
467
+ end
468
+
469
+ it 'raises an exception if play fails' do
470
+ subject.expects(:play_time).with([audiofile]).returns(false)
471
+ subject.expects(:play_numeric).with(audiofile).returns(false)
472
+ subject.expects(:play_soundfile).with(audiofile).returns(false)
473
+ lambda { subject.play!(audiofile) }.should raise_error(Adhearsion::PlaybackError)
474
+ end
475
+ end
476
+
477
+ describe "with multiple arguments" do
478
+ it 'loops over the arguments, issuing separate play commands' do
479
+ subject.expects(:play_time).with([audiofile, audiofile2]).returns(false)
480
+ subject.expects(:play_numeric).with(audiofile).returns(false)
481
+ subject.expects(:play_soundfile).with(audiofile).returns(true)
482
+ subject.expects(:play_numeric).with(audiofile2).returns(false)
483
+ subject.expects(:play_soundfile).with(audiofile2).returns(true)
484
+ subject.play!(audiofile, audiofile2)
485
+ end
486
+
487
+ it 'raises an exception if play fails with multiple argument' do
488
+ subject.expects(:play_time).with([audiofile, audiofile2]).returns(false)
489
+ subject.expects(:play_numeric).with(audiofile).returns(false)
490
+ subject.expects(:play_soundfile).with(audiofile).returns(false)
491
+ subject.expects(:play_numeric).with(audiofile2).returns(false)
492
+ subject.expects(:play_soundfile).with(audiofile2).returns(false)
493
+ lambda { subject.play!(audiofile, audiofile2) }.should raise_error(Adhearsion::PlaybackError)
494
+ end
495
+ end
496
+
497
+ end
498
+
499
+ describe "#play_time" do
500
+ let(:date) { Date.parse('2011-10-24') }
501
+ let(:date_format) { 'ABdY' }
502
+ let(:time) { Time.at(875121313) }
503
+ let(:time_format) { 'IMp' }
504
+
505
+ it "if a Date object is passed in, SayUnixTime is sent with the argument and format" do
506
+ subject.expects(:execute).once.with("SayUnixTime", date.to_time.to_i, "", date_format)
507
+ subject.play_time(date, :format => date_format)
508
+ end
509
+
510
+ it "if a Time object is passed in, SayUnixTime is sent with the argument and format" do
511
+ subject.expects(:execute).once.with("SayUnixTime", time.to_i, "", time_format)
512
+ subject.play_time(time, :format => time_format)
513
+ end
514
+
515
+ it "if a Time object is passed in alone, SayUnixTime is sent with the argument and the default format" do
516
+ subject.expects(:execute).once.with("SayUnixTime", time.to_i, "", "")
517
+ subject.play_time(time)
518
+ end
519
+
520
+ end
521
+
522
+ describe "#play_numeric" do
523
+ let(:numeric) { 20 }
524
+ it "should send the correct command SayNumber playing a numeric argument" do
525
+ subject.expects(:execute).once.with("SayNumber", numeric)
526
+ subject.play_numeric(numeric)
527
+ end
528
+ end
529
+
530
+ describe "#play_soundfile" do
531
+ let(:audiofile) { "tt-monkeys" }
532
+ it "should send the correct command Playback playing an audio file" do
533
+ subject.expects(:execute).once.with("Playback", audiofile)
534
+ # subject.expects(:execute).once.with("Playback", audiofile).returns([200, 1, nil])
535
+ subject.expects(:get_variable).once.with("PLAYBACKSTATUS").returns(PLAYBACK_SUCCESS)
536
+ subject.play_soundfile(audiofile)
537
+ end
538
+
539
+ it "should return false if playback fails" do
540
+ subject.expects(:execute).once.with("Playback", audiofile)
541
+ subject.expects(:get_variable).once.with("PLAYBACKSTATUS").returns('FAILED')
542
+ subject.play_soundfile(audiofile).should == false
543
+ end
544
+ end
545
+
546
+ describe "#stream_file" do
547
+ let(:allowed_digits) { '35' }
548
+ let(:prompt) { 'tt-monkeys' }
549
+
550
+ let :output_params do
551
+ {
552
+ :name => 'STREAM FILE',
553
+ :params => [
554
+ 'tt-monkeys',
555
+ '35'
556
+ ]
557
+ }
558
+ end
559
+
560
+ let(:output_component) do
561
+ Punchblock::Component::Asterisk::AGI::Command.new output_params
562
+ end
563
+
564
+ let(:result) { 53 }
565
+ let(:endpos) { '5000' }
566
+
567
+ let :reason do
568
+ Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => 200,
569
+ :result => result,
570
+ :data => "endpos=#{endpos}"
571
+ end
572
+
573
+ before do
574
+ output_component
575
+ Punchblock::Component::Asterisk::AGI::Command.expects(:new).once.with(output_params).returns output_component
576
+ output_component.expects(:complete_event).at_least_once.returns mock('success', :reason => reason)
577
+ end
578
+
579
+ it "plays the correct output" do
580
+ subject.expects(:execute_component_and_await_completion).once.with(output_component).returns(output_component)
581
+ subject.stream_file prompt, allowed_digits
582
+ end
583
+
584
+ it "returns a single digit amongst the allowed when pressed" do
585
+ subject.expects(:execute_component_and_await_completion).once.with(output_component).returns(output_component)
586
+ subject.stream_file(prompt, allowed_digits).should == '5'
587
+ end
588
+
589
+ context 'when nothing was pressed' do
590
+ let(:result) { 0 }
591
+
592
+ it "returns nil" do
593
+ subject.expects(:execute_component_and_await_completion).once.with(output_component).returns(output_component)
594
+ subject.stream_file(prompt, allowed_digits).should == nil
595
+ end
596
+ end
597
+
598
+ context 'when output fails' do
599
+ let(:result) { -1 }
600
+
601
+ it "raises Adhearsion::PlaybackError" do
602
+ subject.expects(:execute_component_and_await_completion).once.with(output_component).returns(output_component)
603
+ lambda { subject.stream_file prompt, allowed_digits }.should raise_error Adhearsion::PlaybackError
604
+ end
605
+ end
606
+
607
+ context 'when output fails to open' do
608
+ let(:result) { 0 }
609
+ let(:endpos) { '0' }
610
+
611
+ it "raises Adhearsion::PlaybackError" do
612
+ subject.expects(:execute_component_and_await_completion).once.with(output_component).returns(output_component)
613
+ lambda { subject.stream_file prompt, allowed_digits }.should raise_error Adhearsion::PlaybackError
614
+ end
615
+ end
616
+ end # describe #stream_file
617
+ end#main describe
618
+ end