adhearsion 1.0.1 → 1.0.2

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 (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