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.
- data/.gitignore +10 -0
- data/CHANGELOG +8 -0
- data/README.markdown +33 -0
- data/Rakefile +28 -68
- data/adhearsion.gemspec +19 -133
- data/app_generators/ahn/templates/Gemfile +0 -4
- data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +4 -16
- data/lib/adhearsion/cli.rb +17 -0
- data/lib/adhearsion/component_manager/component_tester.rb +1 -3
- data/lib/adhearsion/component_manager/spec_framework.rb +4 -10
- data/lib/adhearsion/foundation/object.rb +10 -0
- data/lib/adhearsion/version.rb +1 -1
- data/spec/ahn_command_spec.rb +284 -0
- data/spec/component_manager_spec.rb +292 -0
- data/spec/constants_spec.rb +8 -0
- data/spec/drb_spec.rb +65 -0
- data/spec/fixtures/dialplan.rb +3 -0
- data/spec/foundation/event_socket_spec.rb +168 -0
- data/spec/host_definitions_spec.rb +79 -0
- data/spec/initialization_spec.rb +163 -0
- data/spec/initializer/configuration_spec.rb +270 -0
- data/spec/initializer/loading_spec.rb +149 -0
- data/spec/initializer/paths_spec.rb +74 -0
- data/spec/logging_spec.rb +86 -0
- data/spec/relationship_properties_spec.rb +54 -0
- data/spec/silence.rb +10 -0
- data/spec/spec_helper.rb +101 -0
- data/spec/voip/asterisk/agi_server_spec.rb +473 -0
- data/spec/voip/asterisk/ami/ami_spec.rb +549 -0
- data/spec/voip/asterisk/ami/lexer/ami_fixtures.yml +30 -0
- data/spec/voip/asterisk/ami/lexer/lexer_story +291 -0
- data/spec/voip/asterisk/ami/lexer/lexer_story.rb +241 -0
- data/spec/voip/asterisk/ami/lexer/story_helper.rb +124 -0
- data/spec/voip/asterisk/ami/old_tests.rb +204 -0
- data/spec/voip/asterisk/ami/super_manager/super_manager_story +25 -0
- data/spec/voip/asterisk/ami/super_manager/super_manager_story.rb +15 -0
- data/spec/voip/asterisk/ami/super_manager/super_manager_story_helper.rb +5 -0
- data/spec/voip/asterisk/commands_spec.rb +2179 -0
- data/spec/voip/asterisk/config_file_generators/agents_spec.rb +251 -0
- data/spec/voip/asterisk/config_file_generators/queues_spec.rb +323 -0
- data/spec/voip/asterisk/config_file_generators/voicemail_spec.rb +306 -0
- data/spec/voip/asterisk/config_manager_spec.rb +127 -0
- data/spec/voip/asterisk/menu_command/calculated_match_spec.rb +109 -0
- data/spec/voip/asterisk/menu_command/matchers_spec.rb +97 -0
- data/spec/voip/call_routing_spec.rb +125 -0
- data/spec/voip/dialplan_manager_spec.rb +468 -0
- data/spec/voip/dsl/dialing_dsl_spec.rb +270 -0
- data/spec/voip/dsl/dispatcher_spec.rb +82 -0
- data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
- data/spec/voip/dsl/parser_spec.rb +69 -0
- data/spec/voip/freeswitch/basic_connection_manager_spec.rb +39 -0
- data/spec/voip/freeswitch/inbound_connection_manager_spec.rb +39 -0
- data/spec/voip/freeswitch/oes_server_spec.rb +9 -0
- data/spec/voip/numerical_string_spec.rb +61 -0
- data/spec/voip/phone_number_spec.rb +45 -0
- data/theatre-spec/dsl_examples/dynamic_stomp.rb +7 -0
- data/theatre-spec/dsl_examples/simple_before_call.rb +7 -0
- data/theatre-spec/dsl_spec.rb +43 -0
- data/theatre-spec/invocation_spec.rb +167 -0
- data/theatre-spec/namespace_spec.rb +125 -0
- data/theatre-spec/spec_helper.rb +37 -0
- data/theatre-spec/spec_helper_spec.rb +28 -0
- data/theatre-spec/theatre_class_spec.rb +150 -0
- 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
|