adhearsion 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +8 -0
  3. data/README.markdown +33 -0
  4. data/Rakefile +28 -68
  5. data/adhearsion.gemspec +19 -133
  6. data/app_generators/ahn/templates/Gemfile +0 -4
  7. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +4 -16
  8. data/lib/adhearsion/cli.rb +17 -0
  9. data/lib/adhearsion/component_manager/component_tester.rb +1 -3
  10. data/lib/adhearsion/component_manager/spec_framework.rb +4 -10
  11. data/lib/adhearsion/foundation/object.rb +10 -0
  12. data/lib/adhearsion/version.rb +1 -1
  13. data/spec/ahn_command_spec.rb +284 -0
  14. data/spec/component_manager_spec.rb +292 -0
  15. data/spec/constants_spec.rb +8 -0
  16. data/spec/drb_spec.rb +65 -0
  17. data/spec/fixtures/dialplan.rb +3 -0
  18. data/spec/foundation/event_socket_spec.rb +168 -0
  19. data/spec/host_definitions_spec.rb +79 -0
  20. data/spec/initialization_spec.rb +163 -0
  21. data/spec/initializer/configuration_spec.rb +270 -0
  22. data/spec/initializer/loading_spec.rb +149 -0
  23. data/spec/initializer/paths_spec.rb +74 -0
  24. data/spec/logging_spec.rb +86 -0
  25. data/spec/relationship_properties_spec.rb +54 -0
  26. data/spec/silence.rb +10 -0
  27. data/spec/spec_helper.rb +101 -0
  28. data/spec/voip/asterisk/agi_server_spec.rb +473 -0
  29. data/spec/voip/asterisk/ami/ami_spec.rb +549 -0
  30. data/spec/voip/asterisk/ami/lexer/ami_fixtures.yml +30 -0
  31. data/spec/voip/asterisk/ami/lexer/lexer_story +291 -0
  32. data/spec/voip/asterisk/ami/lexer/lexer_story.rb +241 -0
  33. data/spec/voip/asterisk/ami/lexer/story_helper.rb +124 -0
  34. data/spec/voip/asterisk/ami/old_tests.rb +204 -0
  35. data/spec/voip/asterisk/ami/super_manager/super_manager_story +25 -0
  36. data/spec/voip/asterisk/ami/super_manager/super_manager_story.rb +15 -0
  37. data/spec/voip/asterisk/ami/super_manager/super_manager_story_helper.rb +5 -0
  38. data/spec/voip/asterisk/commands_spec.rb +2179 -0
  39. data/spec/voip/asterisk/config_file_generators/agents_spec.rb +251 -0
  40. data/spec/voip/asterisk/config_file_generators/queues_spec.rb +323 -0
  41. data/spec/voip/asterisk/config_file_generators/voicemail_spec.rb +306 -0
  42. data/spec/voip/asterisk/config_manager_spec.rb +127 -0
  43. data/spec/voip/asterisk/menu_command/calculated_match_spec.rb +109 -0
  44. data/spec/voip/asterisk/menu_command/matchers_spec.rb +97 -0
  45. data/spec/voip/call_routing_spec.rb +125 -0
  46. data/spec/voip/dialplan_manager_spec.rb +468 -0
  47. data/spec/voip/dsl/dialing_dsl_spec.rb +270 -0
  48. data/spec/voip/dsl/dispatcher_spec.rb +82 -0
  49. data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
  50. data/spec/voip/dsl/parser_spec.rb +69 -0
  51. data/spec/voip/freeswitch/basic_connection_manager_spec.rb +39 -0
  52. data/spec/voip/freeswitch/inbound_connection_manager_spec.rb +39 -0
  53. data/spec/voip/freeswitch/oes_server_spec.rb +9 -0
  54. data/spec/voip/numerical_string_spec.rb +61 -0
  55. data/spec/voip/phone_number_spec.rb +45 -0
  56. data/theatre-spec/dsl_examples/dynamic_stomp.rb +7 -0
  57. data/theatre-spec/dsl_examples/simple_before_call.rb +7 -0
  58. data/theatre-spec/dsl_spec.rb +43 -0
  59. data/theatre-spec/invocation_spec.rb +167 -0
  60. data/theatre-spec/namespace_spec.rb +125 -0
  61. data/theatre-spec/spec_helper.rb +37 -0
  62. data/theatre-spec/spec_helper_spec.rb +28 -0
  63. data/theatre-spec/theatre_class_spec.rb +150 -0
  64. metadata +171 -34
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/voip/menu_state_machine/menu_builder'
3
+ require 'adhearsion/voip/menu_state_machine/matchers'
4
+
5
+ describe "MatchCalculator" do
6
+ it "the build_with_pattern() method should return an appropriate subclass instance based on the pattern's class" do
7
+ Adhearsion::VoIP::MatchCalculator.build_with_pattern(1..2, :main).should be_an_instance_of Adhearsion::VoIP::RangeMatchCalculator
8
+ end
9
+
10
+ end
11
+
12
+ describe "The RangeMatchCalculator" do
13
+
14
+ it "matching with a Range should handle the case of two potential matches in the range" do
15
+ digits_that_begin_with_eleven = [110..119, 1100..1111].map { |x| Array(x) }.flatten
16
+
17
+ calculator = Adhearsion::VoIP::RangeMatchCalculator.new 11..1111, :match_payload_doesnt_matter
18
+ match = calculator.match 11
19
+ match.exact_matches.should == [11]
20
+ match.potential_matches.should == digits_that_begin_with_eleven
21
+ end
22
+
23
+ it 'return values of #match should be an instance of CalculatedMatch' do
24
+ calculator = Adhearsion::VoIP::RangeMatchCalculator.new 1..9, :match_payload_doesnt_matter
25
+ calculator.match(0).should be_an_instance_of Adhearsion::VoIP::CalculatedMatch
26
+ calculator.match(1000).should be_an_instance_of Adhearsion::VoIP::CalculatedMatch
27
+ end
28
+
29
+ end
30
+
31
+ describe "FixnumMatchCalculator" do
32
+ attr_reader :match_payload
33
+ before(:each) do
34
+ @match_payload = :main
35
+ end
36
+
37
+ it "a potential match scenario" do
38
+ calculator = Adhearsion::VoIP::FixnumMatchCalculator.new(444, match_payload)
39
+ match = calculator.match 4
40
+ match.potential_match?.should be true
41
+ match.exact_match?.should_not be true
42
+ match.potential_matches.should == [444]
43
+ end
44
+
45
+ it 'a multi-digit exact match scenario' do
46
+ calculator = Adhearsion::VoIP::FixnumMatchCalculator.new(5555, match_payload)
47
+ calculator.match(5555).exact_match?.should be true
48
+ end
49
+
50
+ it 'a single-digit exact match scenario' do
51
+ calculator = Adhearsion::VoIP::FixnumMatchCalculator.new(1, match_payload)
52
+ calculator.match(1).exact_match?.should be true
53
+ end
54
+
55
+ it 'the context name given to the calculator should be passed on to the CalculatedMatch' do
56
+ match_payload = :icanhascheezburger
57
+ calculator = Adhearsion::VoIP::FixnumMatchCalculator.new(1337, match_payload)
58
+ calculator.match(1337).match_payload.should be match_payload
59
+ end
60
+
61
+ end
62
+
63
+ describe "StringMatchCalculator" do
64
+
65
+ attr_reader :match_payload
66
+ before(:each) do
67
+ @match_payload = :doesnt_matter
68
+ end
69
+
70
+ it "numerical digits mixed with special digits" do
71
+
72
+ %w[5*11#3 5*** ###].each do |str|
73
+ calculator = Adhearsion::VoIP::StringMatchCalculator.new(str, match_payload)
74
+
75
+ match_case = calculator.match str[0,2]
76
+ match_case.exact_match?.should_not be true
77
+ match_case.potential_match?.should be true
78
+ match_case.potential_matches.should == [str]
79
+
80
+ match_case = calculator.match str
81
+ match_case.exact_match?.should be true
82
+ match_case.potential_match?.should_not be true
83
+ match_case.exact_matches.should == [str]
84
+ end
85
+ end
86
+
87
+ it "matching the special DTMF characters such as * and #" do
88
+ %w[* #].each do |special_digit|
89
+ calculator = Adhearsion::VoIP::StringMatchCalculator.new(special_digit, match_payload)
90
+ match_case = calculator.match special_digit
91
+ match_case.potential_match?.should_not be true
92
+ match_case.exact_match?.should be true
93
+ match_case.exact_matches.first.should == special_digit
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ module CallRoutingTestHelper
4
+ private
5
+ def route(*args, &block)
6
+ Adhearsion::VoIP::CallRouting::Rule.new(*args, &block)
7
+ end
8
+
9
+ def provider_named(name)
10
+ Adhearsion::VoIP::DSL::DialingDSL::ProviderDefinition.new(name)
11
+ end
12
+
13
+ def define_rules(&block)
14
+ Adhearsion::VoIP::CallRouting::Router.define(&block)
15
+ end
16
+
17
+ def calculate_route_for(end_point)
18
+ Adhearsion::VoIP::CallRouting::Router.calculate_route_for(end_point)
19
+ end
20
+
21
+ def rules
22
+ Adhearsion::VoIP::CallRouting::Router.rules
23
+ end
24
+ end
25
+
26
+ describe "Call routing rule generation" do
27
+ include CallRoutingTestHelper
28
+
29
+ attr_reader :provider_one, :provider_two, :patterns
30
+
31
+ before(:each) do
32
+ @provider_one = provider_named(:one)
33
+ @provider_two = provider_named(:two)
34
+ @patterns = [/pattern/, /does not/, /matter/]
35
+ end
36
+
37
+ it "specifying a single pattern routed to a single provider properly stores that pattern and its provider in the generated route rule" do
38
+ pattern = %r(does not matter)
39
+ rule = route(pattern, :to => provider_one)
40
+ rule.patterns.should == [pattern]
41
+ rule.providers.should == [provider_one]
42
+ end
43
+
44
+ it "specifying multiple patterns routed to a single provider stores all of them in the generated route rule" do
45
+ first, second, third = patterns
46
+ rule = route(first, second, third, :to => provider_one)
47
+ rule.patterns.should == [*patterns]
48
+ rule.providers.should == [provider_one]
49
+ end
50
+
51
+ it "specifying multiple patterns routed to multiple providers stores all of them in the generated route rule, listing providers in the ordered specified" do
52
+ first, second, third = patterns
53
+ rule = route(first, second, third, :to => [provider_one, provider_two])
54
+ rule.patterns.should == [*patterns]
55
+ rule.providers.should == [provider_one, provider_two]
56
+ end
57
+ end
58
+
59
+ describe "Route calculation" do
60
+ include CallRoutingTestHelper
61
+ attr_reader :provider_one, :provider_two
62
+
63
+ before(:each) do
64
+ rules.clear
65
+ @provider_one = provider_named(:one)
66
+ @provider_two = provider_named(:two)
67
+ end
68
+
69
+ it "Defining a rule adds it to the router rules" do
70
+ provider = provider_one
71
+ pattern = /123/
72
+ define_rules do
73
+ route pattern, :to => provider
74
+ end
75
+ rules.size.should be 1
76
+ rule = rules.first
77
+ rule.providers.should == [provider]
78
+ rule.patterns.should == [pattern]
79
+ end
80
+
81
+ it "Definiting multiple rules adds them to the router rules" do
82
+ provider_for_rule_1 = provider_one
83
+ provider_for_rule_2 = provider_two
84
+ pattern_for_rule_1 = /123/
85
+ pattern_for_rule_2 = /987/
86
+
87
+ define_rules do
88
+ route pattern_for_rule_1, :to => provider_for_rule_1
89
+ route pattern_for_rule_2, :to => provider_for_rule_2
90
+ end
91
+
92
+ rules.size.should be 2
93
+
94
+ rule_1 = rules.first
95
+ rule_1.providers.should == [provider_for_rule_1]
96
+ rule_1.patterns.should == [pattern_for_rule_1]
97
+
98
+ rule_2 = rules.last
99
+ rule_2.providers.should == [provider_for_rule_2]
100
+ rule_2.patterns.should == [pattern_for_rule_2]
101
+ end
102
+
103
+ it "Provider is found in the simplest case of having only one pattern and one provider" do
104
+ target_provider = provider_one
105
+ define_rules do
106
+ route /123/, :to => target_provider
107
+ end
108
+ calculate_route_for(1234).should == [target_provider]
109
+ end
110
+
111
+ it "Provider is found when the specified pattern matches a rule that is not the first rule" do
112
+ provider_for_rule_1 = provider_one
113
+ provider_for_rule_2 = provider_two
114
+ pattern_for_rule_1 = /123/
115
+ pattern_for_rule_2 = /987/
116
+
117
+ define_rules do
118
+ route pattern_for_rule_1, :to => provider_for_rule_1
119
+ route pattern_for_rule_2, :to => provider_for_rule_2
120
+ end
121
+
122
+ target_provider = provider_for_rule_2
123
+ calculate_route_for(9876).should == [target_provider]
124
+ end
125
+ end
@@ -0,0 +1,468 @@
1
+ require 'spec_helper'
2
+ require 'adhearsion/voip/dsl/dialplan/parser'
3
+
4
+ module DialplanTestingHelper
5
+
6
+ def load(dial_plan_as_string)
7
+ Adhearsion::DialPlan::Loader.load(dial_plan_as_string)
8
+ end
9
+
10
+ def mock_dialplan_with(string)
11
+ string_io = StringIO.new(string)
12
+ def string_io.path
13
+ "dialplan.rb"
14
+ end
15
+ flexstub(Adhearsion::AHN_CONFIG).should_receive(:files_from_setting).with("paths", "dialplan").and_return ["dialplan.rb"]
16
+ flexstub(File).should_receive(:new).with("dialplan.rb").and_return string_io
17
+ flexstub(File).should_receive(:read).with('dialplan.rb').and_return string
18
+ end
19
+
20
+ def new_manager_with_entry_points_loaded_from_dialplan_contexts
21
+ Adhearsion::DialPlan::Manager.new.tap do |manager|
22
+ manager.dial_plan.entry_points = manager.dial_plan.loader.load_dialplans.contexts
23
+ end
24
+ end
25
+
26
+ def executing_dialplan(options)
27
+ call = options.delete(:call)
28
+ context_name = options.keys.first
29
+ dialplan = options[context_name]
30
+ call ||= new_call_for_context context_name
31
+
32
+ mock_dialplan_with dialplan
33
+
34
+ lambda do
35
+ Adhearsion::DialPlan::Manager.new.handle call
36
+ end
37
+ end
38
+
39
+ def new_call_for_context(context)
40
+ Adhearsion::Call.new(StringIO.new, :context => context)
41
+ end
42
+ end
43
+
44
+ describe "Dialplan::Manager handling" do
45
+
46
+ include DialplanTestingHelper
47
+
48
+ attr_accessor :manager, :call, :context_name, :mock_context
49
+
50
+ before :each do
51
+ @context_name = :some_context_name
52
+ @mock_context = flexmock('a context')
53
+
54
+ mock_dial_plan_lookup_for_context_name
55
+
56
+ flexmock(Adhearsion::DialPlan::Loader).should_receive(:load_dialplans).and_return {
57
+ flexmock("loaded contexts", :contexts => nil)
58
+ }
59
+ @manager = Adhearsion::DialPlan::Manager.new
60
+ @call = new_call_for_context context_name
61
+
62
+ # Sanity check context name being set
63
+ call.context.should be context_name
64
+ end
65
+
66
+ it "Given a Call, the manager finds the call's desired entry point based on the originating context" do
67
+ manager.entry_point_for(call).should be mock_context
68
+ end
69
+
70
+ it "The manager handles a call by executing the proper context" do
71
+ flexmock(Adhearsion::DialPlan::ExecutionEnvironment).new_instances.should_receive(:run).once
72
+ manager.handle(call)
73
+ end
74
+
75
+ it "should raise a NoContextError exception if the targeted context is not found" do
76
+ the_following_code {
77
+ flexmock(manager).should_receive(:entry_point_for).and_return nil
78
+ manager.handle call
79
+ }.should raise_error(Adhearsion::DialPlan::Manager::NoContextError)
80
+ end
81
+
82
+ it 'should send :answer to the execution environment if Adhearsion::AHN_CONFIG.automatically_answer_incoming_calls is set' do
83
+ flexmock(Adhearsion::DialPlan::ExecutionEnvironment).new_instances.should_receive(:answer).once.and_throw :answered_call!
84
+ Adhearsion::Configuration.configure do |config|
85
+ config.automatically_answer_incoming_calls = true
86
+ end
87
+ the_following_code {
88
+ manager.handle call
89
+ }.should throw_symbol :answered_call!
90
+ end
91
+
92
+ it 'should NOT send :answer to the execution environment if Adhearsion::AHN_CONFIG.automatically_answer_incoming_calls is NOT set' do
93
+ Adhearsion::Configuration.configure do |config|
94
+ config.automatically_answer_incoming_calls = false
95
+ end
96
+
97
+ entry_point = Adhearsion::DialPlan::DialplanContextProc.new(:does_not_matter) { "Do nothing" }
98
+ flexmock(manager).should_receive(:entry_point_for).once.with(call).and_return(entry_point)
99
+
100
+ execution_env = Adhearsion::DialPlan::ExecutionEnvironment.create(call, nil)
101
+ flexmock(execution_env).should_receive(:entry_point).and_return entry_point
102
+ flexmock(execution_env).should_receive(:answer).never
103
+
104
+ flexmock(Adhearsion::DialPlan::ExecutionEnvironment).should_receive(:new).once.and_return execution_env
105
+
106
+ Adhearsion::Configuration.configure
107
+ manager.handle call
108
+ end
109
+
110
+ private
111
+
112
+ def mock_dial_plan_lookup_for_context_name
113
+ flexstub(Adhearsion::DialPlan).new_instances.should_receive(:lookup).with(context_name).and_return(mock_context)
114
+ end
115
+
116
+ end
117
+
118
+ describe "DialPlan::Manager's handling a failed call" do
119
+
120
+ include DialplanTestingHelper
121
+
122
+ it 'should check if the call has failed and then instruct it to extract the reason from the environment' do
123
+ flexmock(Adhearsion::DialPlan::ExecutionEnvironment).new_instances.should_receive(:variable).with("REASON").once.and_return '3'
124
+ call = Adhearsion::Call.new(nil, {'extension' => "failed"})
125
+ call.failed_call?.should be true
126
+ flexmock(Adhearsion::DialPlan).should_receive(:new).once.and_return flexmock("bogus DialPlan which should never be used")
127
+ begin
128
+ Adhearsion::DialPlan::Manager.handle(call)
129
+ rescue Adhearsion::FailedExtensionCallException => error
130
+ error.call.failed_reason.should == Adhearsion::Call::ASTERISK_FRAME_STATES[3]
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "Call tagging" do
136
+
137
+ include DialplanTestingHelper
138
+
139
+ after :all do
140
+ Adhearsion.active_calls.clear!
141
+ end
142
+
143
+ it 'tagging a call with a single Symbol' do
144
+ the_following_code {
145
+ call = new_call_for_context "roflcopter"
146
+ call.tag :moderator
147
+ }.should_not raise_error
148
+ end
149
+
150
+ it 'tagging a call with multiple Symbols' do
151
+ the_following_code {
152
+ call = new_call_for_context "roflcopter"
153
+ call.tag :moderator
154
+ call.tag :female
155
+ }.should_not raise_error
156
+ end
157
+
158
+ it 'Call#tagged_with? with one tag' do
159
+ call = new_call_for_context "roflcopter"
160
+ call.tag :guest
161
+ call.tagged_with?(:guest).should be true
162
+ call.tagged_with?(:authorized).should be false
163
+ end
164
+
165
+ it "Call#remove_tag" do
166
+ call = new_call_for_context "roflcopter"
167
+ call.tag :moderator
168
+ call.tag :female
169
+ call.remove_tag :female
170
+ call.tag :male
171
+ call.tags.should == [:moderator, :male]
172
+ end
173
+
174
+ it 'Call#tagged_with? with many tags' do
175
+ call = new_call_for_context "roflcopter"
176
+ call.tag :customer
177
+ call.tag :authorized
178
+ call.tagged_with?(:customer).should be true
179
+ call.tagged_with?(:authorized).should be true
180
+ end
181
+
182
+ it 'tagging a call with a non-Symbol, non-String object' do
183
+ bad_objects = [123, Object.new, 888.88, nil, true, false, StringIO.new]
184
+ bad_objects.each do |bad_object|
185
+ the_following_code {
186
+ new_call_for_context("roflcopter").tag bad_object
187
+ }.should raise_error ArgumentError
188
+ end
189
+ end
190
+
191
+ it "finding calls by a tag" do
192
+ Adhearsion.active_calls.clear!
193
+
194
+ calls = Array.new(5) { new_call_for_context "roflcopter" }
195
+ calls.each { |call| Adhearsion.active_calls << call }
196
+
197
+ tagged_call = calls.last
198
+ tagged_call.tag :moderator
199
+
200
+ Adhearsion.active_calls.with_tag(:moderator).should == [tagged_call]
201
+ end
202
+
203
+ end
204
+
205
+ describe "DialPlan::Manager's handling a hungup call" do
206
+
207
+ include DialplanTestingHelper
208
+
209
+ it 'should check if the call was a hangup meta-AGI call and then raise a HangupExtensionCallException' do
210
+ call = Adhearsion::Call.new(nil, {'extension' => "h"})
211
+ call.hungup_call?.should be true
212
+ flexmock(Adhearsion::DialPlan).should_receive(:new).once.and_return flexmock("bogus DialPlan which should never be used")
213
+ the_following_code {
214
+ Adhearsion::DialPlan::Manager.handle(call)
215
+ }.should raise_error Adhearsion::HungupExtensionCallException
216
+ end
217
+
218
+ end
219
+
220
+ describe "DialPlan" do
221
+
222
+ attr_accessor :loader, :loader_instance, :dial_plan
223
+
224
+ before do
225
+ @loader = Adhearsion::DialPlan::Loader
226
+ @loader_instance = @loader.new
227
+ flexmock(Adhearsion::DialPlan::Loader).should_receive(:load_dialplans).once.and_return(@loader_instance)
228
+ @dial_plan = Adhearsion::DialPlan.new(@loader)
229
+ end
230
+
231
+ it "When a dial plan is instantiated, the dialplans are loaded and stored for lookup" do
232
+ dial_plan.instance_variable_get("@entry_points").should_not be nil
233
+ end
234
+
235
+ it "Can look up an entry point from a dial plan" do
236
+ context_name = 'this_context_is_better_than_your_context'
237
+ loader_instance.contexts[context_name] = lambda { puts "o hai" }
238
+ dial_plan.lookup(context_name).should_not be nil
239
+ end
240
+ end
241
+
242
+ describe "DialPlan loader" do
243
+
244
+ include DialplanTestingHelper
245
+
246
+ it "loading a single context" do
247
+ loader = load(<<-DIAL_PLAN)
248
+ one {
249
+ raise 'this block should not be evaluated'
250
+ }
251
+ DIAL_PLAN
252
+
253
+ loader.contexts.keys.size.should be 1
254
+ loader.contexts.keys.first.should be :one
255
+ end
256
+
257
+ it "loading multiple contexts loads all contexts" do
258
+ loader = load(<<-DIAL_PLAN)
259
+ one {
260
+ raise 'this block should not be evaluated'
261
+ }
262
+
263
+ two {
264
+ raise 'this other block should not be evaluated either'
265
+ }
266
+ DIAL_PLAN
267
+
268
+ loader.contexts.keys.size.should be 2
269
+ loader.contexts.keys.map(&:to_s).sort.should == %w(one two)
270
+ end
271
+
272
+ it 'loading a dialplan with a syntax error' do
273
+ the_following_code {
274
+ load "foo { &@(*!&(*@*!@^!^%@%^! }"
275
+ }.should raise_error SyntaxError
276
+ end
277
+
278
+ it "loading a dial plan from a file" do
279
+ loader = nil
280
+ Adhearsion::AHN_CONFIG.ahnrc = {"paths" => {"dialplan" => "dialplan.rb"}}
281
+ the_following_code {
282
+ AHN_ROOT.using_base_path(File.expand_path(File.dirname(__FILE__) + '/../fixtures')) do
283
+ loader = Adhearsion::DialPlan::Loader.load_dialplans
284
+ end
285
+ }.should_not raise_error
286
+
287
+ loader.contexts.keys.size.should be 1
288
+ loader.contexts.keys.first.should be :sample_context
289
+ end
290
+
291
+ end
292
+
293
+ describe "The inbox-related dialplan methods" do
294
+
295
+ include DialplanTestingHelper
296
+
297
+ it "with_next_message should execute its block with the message from the inbox" do
298
+ mock_call = new_call_for_context :entrance
299
+ [:one, :two, :three].each { |message| mock_call.inbox << message }
300
+
301
+ dialplan = %{ entrance { with_next_message { |message| throw message } } }
302
+ executing_dialplan(:entrance => dialplan, :call => mock_call).should throw_symbol :one
303
+ end
304
+
305
+ it "messages_waiting? should return false if the inbox is empty" do
306
+ mock_call = new_call_for_context :entrance
307
+ dialplan = %{ entrance { throw messages_waiting? ? :yes : :no } }
308
+ executing_dialplan(:entrance => dialplan, :call => mock_call).should throw_symbol :no
309
+ end
310
+
311
+ it "messages_waiting? should return false if the inbox is not empty" do
312
+ mock_call = new_call_for_context :entrance
313
+ mock_call.inbox << Object.new
314
+ dialplan = %{ entrance { throw messages_waiting? ? :yes : :no } }
315
+ executing_dialplan(:entrance => dialplan, :call => mock_call).should throw_symbol :yes
316
+ end
317
+
318
+ end
319
+
320
+
321
+ describe "ExecutionEnvironment" do
322
+
323
+ attr_accessor :call, :entry_point
324
+
325
+ include DialplanTestingHelper
326
+
327
+ before do
328
+ variables = { :context => "zomgzlols", :caller_id => "Ponce de Leon" }
329
+ @call = Adhearsion::Call.new(nil, variables)
330
+ @entry_point = lambda {}
331
+ end
332
+
333
+ it "On initialization, ExecutionEnvironments extend themselves with behavior specific to the voip platform which originated the call" do
334
+ Adhearsion::DialPlan::ExecutionEnvironment.included_modules.should_not include(Adhearsion::VoIP::Asterisk::Commands)
335
+ execution_environent = Adhearsion::DialPlan::ExecutionEnvironment.create(call, entry_point)
336
+ execution_environent.metaclass.included_modules.should include(Adhearsion::VoIP::Asterisk::Commands)
337
+ end
338
+
339
+ it "An executed context should raise a NameError error when a missing constant is referenced" do
340
+ the_following_code do
341
+ flexmock(Adhearsion::AHN_CONFIG).should_receive(:automatically_answer_incoming_calls).and_return false
342
+ context = :context_with_missing_constant
343
+ call = new_call_for_context context
344
+ mock_dialplan_with "#{context} { ThisConstantDoesntExist }"
345
+ Adhearsion::DialPlan::Manager.new.handle call
346
+ end.should raise_error NameError
347
+
348
+ end
349
+
350
+ it "should define variables accessors within itself" do
351
+ environment = Adhearsion::DialPlan::ExecutionEnvironment.create(@call, entry_point)
352
+ call.variables.empty?.should be false
353
+ call.variables.each do |key, value|
354
+ environment.send(key).should be value
355
+ end
356
+ end
357
+
358
+ it "should define accessors for other contexts in the dialplan" do
359
+ call = new_call_for_context :am_not_for_kokoa!
360
+ bogus_dialplan = <<-DIALPLAN
361
+ am_not_for_kokoa! {}
362
+ icanhascheezburger? {}
363
+ these_context_names_do_not_really_matter {}
364
+ DIALPLAN
365
+
366
+ mock_dialplan_with bogus_dialplan
367
+
368
+ manager = Adhearsion::DialPlan::Manager.new
369
+ manager.dial_plan.entry_points.empty?.should_not be true
370
+
371
+ manager.handle call
372
+
373
+ %w(these_context_names_do_not_really_matter icanhascheezburger? am_not_for_kokoa!).each do |context_name|
374
+ manager.context.respond_to?(context_name).should be true
375
+ end
376
+
377
+ end
378
+
379
+ end
380
+
381
+ describe "Dialplan control statements" do
382
+
383
+ include DialplanTestingHelper
384
+
385
+ it "Manager should catch ControlPassingExceptions" do
386
+ flexmock(Adhearsion::AHN_CONFIG).should_receive(:automatically_answer_incoming_calls).and_return false
387
+ dialplan = %{
388
+ foo { raise Adhearsion::VoIP::DSL::Dialplan::ControlPassingException.new(bar) }
389
+ bar {}
390
+ }
391
+ executing_dialplan(:foo => dialplan).should_not raise_error
392
+ end
393
+
394
+ it "All dialplan contexts should be available at context execution time" do
395
+ dialplan = %{
396
+ context_defined_first {
397
+ throw :i_see_it if context_defined_second
398
+ }
399
+ context_defined_second {}
400
+ }
401
+ executing_dialplan(:context_defined_first => dialplan).should throw_symbol :i_see_it
402
+ end
403
+
404
+ test_dialplan_inclusions = true
405
+ if Object.const_defined?("JRUBY_VERSION")
406
+ require 'adhearsion/version'
407
+ curver = Adhearsion::PkgVersion.new(JRUBY_VERSION)
408
+ minver = Adhearsion::PkgVersion.new("1.6.0")
409
+ if curver < minver
410
+ # JRuby contains a bug that breaks some of the menu functionality
411
+ # See: https://adhearsion.lighthouseapp.com/projects/5871/tickets/92-menu-method-under-jruby-does-not-appear-to-work
412
+ test_dialplan_inclusions = false
413
+ end
414
+ end
415
+
416
+ if test_dialplan_inclusions
417
+ it "Proc#+@ should execute the other context" do
418
+ dialplan = %{
419
+ eins {
420
+ +zwei
421
+ throw :eins
422
+ }
423
+ zwei {
424
+ throw :zwei
425
+ }
426
+ }
427
+ executing_dialplan(:eins => dialplan).should throw_symbol :zwei
428
+ end
429
+
430
+ it "Proc#+@ should not return to its originating context" do
431
+ dialplan = %{
432
+ andere {}
433
+ zuerst {
434
+ +andere
435
+ throw :after_control_statement
436
+ }
437
+ }
438
+ executing_dialplan(:zuerst => dialplan).should_not raise_error
439
+ end
440
+ end
441
+
442
+
443
+ it "new constants should still be accessible within the dialplan" do
444
+ flexmock(Adhearsion::AHN_CONFIG).should_receive(:automatically_answer_incoming_calls).and_return false
445
+ ::Jicksta = :Jicksta
446
+ dialplan = %{
447
+ constant_test {
448
+ Jicksta.should == :Jicksta
449
+ }
450
+ }
451
+ executing_dialplan(:constant_test => dialplan).should_not raise_error
452
+ end
453
+
454
+ end
455
+
456
+ describe "VoIP platform operations" do
457
+ it "can map a platform name to a module which holds its platform-specific operations" do
458
+ Adhearsion::VoIP::Commands.for(:asterisk).should == Adhearsion::VoIP::Asterisk::Commands
459
+ end
460
+ end
461
+
462
+ describe 'DialPlan::Loader' do
463
+ it '::build should raise a SyntaxError when the dialplan String contains one' do
464
+ the_following_code {
465
+ Adhearsion::DialPlan::Loader.load "foo { ((((( *@!^*@&*^!^@ }"
466
+ }.should raise_error SyntaxError
467
+ end
468
+ end