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.
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +23 -0
- data/adhearsion-asterisk.gemspec +35 -0
- data/lib/adhearsion-asterisk.rb +1 -0
- data/lib/adhearsion/asterisk.rb +12 -0
- data/lib/adhearsion/asterisk/config_generator.rb +103 -0
- data/lib/adhearsion/asterisk/config_generator/agents.rb +138 -0
- data/lib/adhearsion/asterisk/config_generator/queues.rb +247 -0
- data/lib/adhearsion/asterisk/config_generator/voicemail.rb +238 -0
- data/lib/adhearsion/asterisk/config_manager.rb +60 -0
- data/lib/adhearsion/asterisk/plugin.rb +464 -0
- data/lib/adhearsion/asterisk/queue_proxy.rb +177 -0
- data/lib/adhearsion/asterisk/queue_proxy/agent_proxy.rb +81 -0
- data/lib/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy.rb +132 -0
- data/lib/adhearsion/asterisk/version.rb +5 -0
- data/spec/adhearsion/asterisk/config_generators/agents_spec.rb +258 -0
- data/spec/adhearsion/asterisk/config_generators/queues_spec.rb +322 -0
- data/spec/adhearsion/asterisk/config_generators/voicemail_spec.rb +306 -0
- data/spec/adhearsion/asterisk/config_manager_spec.rb +125 -0
- data/spec/adhearsion/asterisk/plugin_spec.rb +618 -0
- data/spec/adhearsion/asterisk/queue_proxy/agent_proxy_spec.rb +90 -0
- data/spec/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy_spec.rb +145 -0
- data/spec/adhearsion/asterisk/queue_proxy_spec.rb +156 -0
- data/spec/adhearsion/asterisk_spec.rb +9 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/the_following_code.rb +3 -0
- 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
|