eric-adhearsion 0.7.999 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/CHANGELOG +8 -2
  2. data/EVENTS +11 -0
  3. data/Rakefile +96 -24
  4. data/adhearsion.gemspec +148 -0
  5. data/app_generators/ahn/ahn_generator.rb +24 -9
  6. data/app_generators/ahn/templates/.ahnrc +25 -3
  7. data/app_generators/ahn/templates/Rakefile +22 -2
  8. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  9. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  10. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  11. data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
  12. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  13. data/app_generators/ahn/templates/components/simon_game/{lib/simon_game.rb → simon_game.rb} +14 -19
  14. data/app_generators/ahn/templates/config/startup.rb +3 -6
  15. data/app_generators/ahn/templates/dialplan.rb +2 -3
  16. data/app_generators/ahn/templates/events.rb +32 -0
  17. data/bin/jahn +10 -0
  18. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  19. data/lib/adhearsion.rb +17 -11
  20. data/lib/adhearsion/cli.rb +141 -24
  21. data/lib/adhearsion/component_manager.rb +169 -238
  22. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  23. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  24. data/lib/adhearsion/events_support.rb +84 -0
  25. data/lib/adhearsion/{core_extensions → foundation}/all.rb +0 -0
  26. data/lib/adhearsion/{blank_slate.rb → foundation/blank_slate.rb} +0 -0
  27. data/lib/adhearsion/{core_extensions → foundation}/custom_daemonizer.rb +0 -0
  28. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  29. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  30. data/lib/adhearsion/{core_extensions → foundation}/global.rb +0 -0
  31. data/lib/adhearsion/{core_extensions → foundation}/metaprogramming.rb +0 -0
  32. data/lib/adhearsion/foundation/numeric.rb +13 -0
  33. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  34. data/lib/adhearsion/{core_extensions → foundation}/relationship_properties.rb +2 -0
  35. data/lib/adhearsion/foundation/string.rb +26 -0
  36. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  37. data/lib/adhearsion/{core_extensions → foundation}/thread_safety.rb +0 -0
  38. data/lib/adhearsion/host_definitions.rb +5 -1
  39. data/lib/adhearsion/initializer.rb +229 -73
  40. data/lib/adhearsion/initializer/asterisk.rb +33 -11
  41. data/lib/adhearsion/initializer/configuration.rb +58 -6
  42. data/lib/adhearsion/initializer/database.rb +3 -46
  43. data/lib/adhearsion/initializer/drb.rb +9 -3
  44. data/lib/adhearsion/initializer/freeswitch.rb +3 -3
  45. data/lib/adhearsion/initializer/rails.rb +1 -1
  46. data/lib/adhearsion/tasks.rb +2 -1
  47. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  48. data/lib/adhearsion/version.rb +3 -3
  49. data/lib/adhearsion/voip/asterisk.rb +2 -2
  50. data/lib/adhearsion/voip/asterisk/agi_server.rb +9 -6
  51. data/lib/adhearsion/voip/asterisk/commands.rb +106 -4
  52. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  53. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  54. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  55. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  56. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  57. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  58. data/lib/adhearsion/voip/call.rb +51 -2
  59. data/lib/adhearsion/voip/dial_plan.rb +74 -61
  60. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +1 -1
  61. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +2 -6
  62. data/lib/adhearsion/voip/dsl/numerical_string.rb +2 -2
  63. data/lib/adhearsion/voip/freeswitch/oes_server.rb +2 -2
  64. data/lib/theatre.rb +151 -0
  65. data/lib/theatre/README.markdown +64 -0
  66. data/lib/theatre/callback_definition_loader.rb +84 -0
  67. data/lib/theatre/guid.rb +23 -0
  68. data/lib/theatre/invocation.rb +121 -0
  69. data/lib/theatre/namespace_manager.rb +153 -0
  70. data/lib/theatre/version.rb +2 -0
  71. metadata +63 -138
  72. data/Manifest.txt +0 -149
  73. data/README.txt +0 -6
  74. data/ahn_generators/component/USAGE +0 -5
  75. data/ahn_generators/component/component_generator.rb +0 -57
  76. data/ahn_generators/component/templates/configuration.rb +0 -0
  77. data/ahn_generators/component/templates/lib/lib.rb.erb +0 -3
  78. data/ahn_generators/component/templates/test/test.rb.erb +0 -12
  79. data/ahn_generators/component/templates/test/test_helper.rb +0 -14
  80. data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
  81. data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +0 -14
  82. data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +0 -31
  83. data/lib/adhearsion/core_extensions/array.rb +0 -0
  84. data/lib/adhearsion/core_extensions/guid.rb +0 -5
  85. data/lib/adhearsion/core_extensions/hash.rb +0 -0
  86. data/lib/adhearsion/core_extensions/numeric.rb +0 -4
  87. data/lib/adhearsion/core_extensions/proc.rb +0 -0
  88. data/lib/adhearsion/core_extensions/pseudo_uuid.rb +0 -11
  89. data/lib/adhearsion/core_extensions/publishable.rb +0 -73
  90. data/lib/adhearsion/core_extensions/string.rb +0 -26
  91. data/lib/adhearsion/core_extensions/thread.rb +0 -13
  92. data/lib/adhearsion/core_extensions/time.rb +0 -0
  93. data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
  94. data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
  95. data/lib/adhearsion/distributed/gateways/rest_gateway.rb +0 -9
  96. data/lib/adhearsion/distributed/gateways/soap_gateway.rb +0 -9
  97. data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +0 -9
  98. data/lib/adhearsion/distributed/peer_finder.rb +0 -0
  99. data/lib/adhearsion/distributed/remote_cli.rb +0 -0
  100. data/lib/adhearsion/hooks.rb +0 -57
  101. data/lib/adhearsion/initializer/paths.rb +0 -55
  102. data/lib/adhearsion/services/scheduler.rb +0 -5
  103. data/lib/adhearsion/voip/asterisk/ami.rb +0 -147
  104. data/lib/adhearsion/voip/asterisk/ami/actions.rb +0 -238
  105. data/lib/adhearsion/voip/asterisk/ami/machine.rb +0 -871
  106. data/lib/adhearsion/voip/asterisk/ami/machine.rl +0 -109
  107. data/lib/adhearsion/voip/asterisk/ami/parser.rb +0 -262
  108. data/script/destroy +0 -14
  109. data/script/generate +0 -14
  110. data/spec/fixtures/dialplan.rb +0 -3
  111. data/spec/initializer/test_configuration.rb +0 -267
  112. data/spec/initializer/test_loading.rb +0 -162
  113. data/spec/initializer/test_paths.rb +0 -43
  114. data/spec/silence.rb +0 -10
  115. data/spec/test_ahn_command.rb +0 -149
  116. data/spec/test_code_quality.rb +0 -87
  117. data/spec/test_component_manager.rb +0 -97
  118. data/spec/test_constants.rb +0 -8
  119. data/spec/test_drb.rb +0 -104
  120. data/spec/test_helper.rb +0 -94
  121. data/spec/test_hooks.rb +0 -37
  122. data/spec/test_host_definitions.rb +0 -79
  123. data/spec/test_initialization.rb +0 -105
  124. data/spec/test_logging.rb +0 -80
  125. data/spec/test_relationship_properties.rb +0 -54
  126. data/spec/voip/asterisk/ami_response_definitions.rb +0 -23
  127. data/spec/voip/asterisk/config_file_generators/test_agents.rb +0 -253
  128. data/spec/voip/asterisk/config_file_generators/test_queues.rb +0 -325
  129. data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +0 -306
  130. data/spec/voip/asterisk/menu_command/test_calculated_match.rb +0 -111
  131. data/spec/voip/asterisk/menu_command/test_matchers.rb +0 -98
  132. data/spec/voip/asterisk/mock_ami_server.rb +0 -176
  133. data/spec/voip/asterisk/test_agi_server.rb +0 -451
  134. data/spec/voip/asterisk/test_ami.rb +0 -227
  135. data/spec/voip/asterisk/test_commands.rb +0 -2006
  136. data/spec/voip/asterisk/test_config_manager.rb +0 -129
  137. data/spec/voip/dsl/dispatcher_spec_helper.rb +0 -45
  138. data/spec/voip/dsl/test_dialing_dsl.rb +0 -268
  139. data/spec/voip/dsl/test_dispatcher.rb +0 -82
  140. data/spec/voip/dsl/test_parser.rb +0 -87
  141. data/spec/voip/freeswitch/test_basic_connection_manager.rb +0 -39
  142. data/spec/voip/freeswitch/test_inbound_connection_manager.rb +0 -39
  143. data/spec/voip/freeswitch/test_oes_server.rb +0 -9
  144. data/spec/voip/test_call_routing.rb +0 -127
  145. data/spec/voip/test_dialplan_manager.rb +0 -372
  146. data/spec/voip/test_numerical_string.rb +0 -48
  147. data/spec/voip/test_phone_number.rb +0 -36
  148. data/test/test_ahn_generator.rb +0 -59
  149. data/test/test_component_generator.rb +0 -52
  150. data/test/test_generator_helper.rb +0 -20
@@ -1,176 +0,0 @@
1
- class AmiServer
2
- class AMIResponseHelper
3
- class << self
4
- def bad_login_response
5
- { "Response" => "Error", "Message" => "Authentication failed" }
6
- end
7
-
8
- def good_login_response
9
- { "Response" => "Success", "Message" => "Authentication accepted" }
10
- end
11
-
12
- def follows_response
13
- { "Response" => "Follows", "Privilege" => "Command" }
14
- end
15
-
16
- def bad_dbget_response
17
- { "Response" => "Error", "Message" => "Database entry not found" }
18
- end
19
-
20
- def good_dbget_response
21
- { "Response" => "Success", "Message" => "Result will follow" }
22
- end
23
-
24
- def dbput_response
25
- { "Response" => "Success", "Message" => "Updated database successfully" }
26
- end
27
-
28
- def dbget_event_response
29
- { "Event" => "DBGetResponse" }
30
- end
31
-
32
- def queue_status_response
33
- { "Response" => "Success", "Message" => "Queue status will follow" }
34
- end
35
-
36
- def queue_status_event_complete_response
37
- { "Event" => "QueueStatusComplete" }
38
- end
39
-
40
- def queue_status_event_params_response
41
- { "Event" => "QueueParams", "Queue" => "default", "Max" => 0, "Min" => 0, "Holdtime" => 0,
42
- "Completed" => 0, "Abandoned" => 0, "ServiceLevel" => 0, "ServiceLevelPerf" => 0 }
43
- end
44
-
45
- def events_on_response
46
- { "Response" => "Events On" }
47
- end
48
-
49
- def events_off_response
50
- { "Response" => "Events Off" }
51
- end
52
-
53
- def ping_response
54
- { "Response" => "Pong" }
55
- end
56
-
57
- def unrecognized_action
58
- { "Response" => "Error", "Message" => "Invalid/unknown command" }
59
- end
60
- end
61
- end
62
-
63
- def initialize(*args)
64
- @closed = false
65
- @buffer = ""
66
- @db = {}
67
- extend MonitorMixin
68
- fill(prompt, 1)
69
- end
70
-
71
- def closed?
72
- @closed
73
- end
74
-
75
- def close
76
- @closed = true
77
- end
78
-
79
- def read_nonblock(count)
80
- str = @buffer.slice!(0..count-1)
81
- raise Errno::EAGAIN if str == ""
82
- str
83
- end
84
-
85
- def gets
86
- @buffer.slice!(0..-1)
87
- end
88
-
89
- def write(str)
90
- raise ArgumentError, "malformed request" if not str.ends_with?("\r\n\r\n")
91
- args = str.split("\r\n").inject({}) { |accum, str| accum.merge!(YAML.load(str)) }
92
- actionid = args['actionid']
93
- case args['action']
94
- when "login"
95
- if args['username'] == username and args['secret'] == password
96
- fill(response(actionid, AMIResponseHelper.good_login_response))
97
- else
98
- fill(response(actionid, AMIResponseHelper.bad_login_response))
99
- end
100
- when "queues"
101
- fill("No Members\nNo Callers\r\n")
102
- when "command"
103
- case args['command']
104
- when "show channels"
105
- fill(response(actionid, AMIResponseHelper.follows_response), 1)
106
- fill("Channel (Context Extension Pri ) State Appl. Data\n0 active channel(s)\n--END COMMAND--")
107
- when "meetme list"
108
- fill(response(actionid, AMIResponseHelper.follows_response), 1)
109
- str = []
110
- str << "Usage: meetme (un)lock|(un)mute|kick|list [concise] "
111
- str << " Executes a command for the conference or on a conferee"
112
- str << "--END COMMAND--"
113
- fill(str.join("\n"))
114
- else
115
- fill(response(actionid, AMIResponseHelper.follows_response), 1)
116
- fill("No such command '#{args['command']}' (type 'help' for help)\n--END COMMAND--")
117
- end
118
- when "dbget"
119
- if not @db.has_key?(args['family']) or not @db[args['family']].has_key?(args['key'])
120
- fill(response(actionid, AMIResponseHelper.bad_dbget_response))
121
- else
122
- val = @db[args['family']][args['key']]
123
- fill(response(actionid, AMIResponseHelper.good_dbget_response))
124
- fill(response(actionid, {
125
- :Family => args['family'],
126
- :Key => args['key'],
127
- :Val => val }.merge!(AMIResponseHelper.dbget_event_response)))
128
- end
129
- when "dbput"
130
- @db[args['family']] ||= {}
131
- @db[args['family']][args['key']] = args['val']
132
- fill(response(actionid, AMIResponseHelper.dbput_response))
133
- when "queuestatus"
134
- fill(response(actionid, AMIResponseHelper.queue_status_response))
135
- fill(response(actionid, AMIResponseHelper.queue_status_event_params_response))
136
- fill(response(actionid, AMIResponseHelper.queue_status_event_complete_response))
137
- when "events"
138
- if args['eventmask'] !~ /^on$/i
139
- if args['eventmask'] =~ /^off$/i
140
- fill(response(actionid, AMIResponseHelper.events_off_response))
141
- else
142
- fill(response(actionid, AMIResponseHelper.events_on_response))
143
- end
144
- end
145
- when "ping"
146
- fill(response(actionid, AMIResponseHelper.ping_response))
147
- else
148
- # raise ArgumentError, "Unknown action #{args['action']}"
149
- fill(response(actionid, AMIResponseHelper.unrecognized_action))
150
- end
151
- end
152
-
153
- private
154
-
155
- def response(action_id, args = {})
156
- resp = []
157
- resp << "Response: #{args.delete('Response')}" if args['Response']
158
- resp << "Privilege: #{args.delete('Privilege')}" if args['Privilege']
159
- resp << "Event: #{args.delete('Event')}" if args['Event']
160
- resp << "ActionID: #{action_id}"
161
- resp << "Message: #{args.delete('Message')}" if args['Message']
162
- args.inject(resp) do |accum, pair|
163
- accum << "#{pair[0]}: #{pair[1]}"
164
- end
165
- resp.join("\r\n")
166
- end
167
-
168
- def fill(line, blanks=2)
169
- @buffer += line
170
- @buffer += "\r\n" * blanks
171
- end
172
-
173
- def prompt; "Asterisk Call Manager/1.0" end
174
- def username; "admin" end
175
- def password; "password" end
176
- end
@@ -1,451 +0,0 @@
1
- require File.dirname(__FILE__) + "/../../test_helper"
2
- require 'adhearsion/voip/asterisk'
3
-
4
- context "The AGI server's serve() method" do
5
-
6
- include AgiServerTestHelper
7
-
8
- attr_reader :server_class, :server
9
- before :each do
10
- @server_class = Adhearsion::VoIP::Asterisk::AGI::Server::RubyServer
11
- @server = @server_class.new(:port,:host)
12
- end
13
-
14
- test 'should instantiate a new Call with the IO object it receives' do
15
- stub_before_call_hooks!
16
- io_mock = flexmock "Mock IO object that's passed to the serve() method"
17
- call_mock = flexmock "A Call mock that's returned by Adhearsion#receive_call_from", :variable => {}
18
- flexstub(server_class).should_receive(:ahn_log)
19
- the_following_code {
20
- flexmock(Adhearsion).should_receive(:receive_call_from).once.with(io_mock).and_throw :created_call!
21
- server.serve(io_mock)
22
- }.should.throw :created_call!
23
- end
24
-
25
- test 'should hand the call off to a new Manager if the request is agi://IP_ADDRESS_HERE' do
26
- stub_before_call_hooks!
27
- call_mock = flexmock 'A new mock call that will be passed to the manager', :variables => {}
28
-
29
- flexmock(Adhearsion).should_receive(:receive_call_from).once.and_return call_mock
30
- manager_mock = flexmock 'a mock dialplan manager'
31
- manager_mock.should_receive(:handle).once.with(call_mock)
32
- flexmock(Adhearsion::DialPlan::Manager).should_receive(:new).once.and_return manager_mock
33
- server.serve(nil)
34
- end
35
-
36
- test 'should hand off a call to a ConfirmationManager if the request begins with confirm!' do
37
- confirm_options = Adhearsion::DialPlan::ConfirmationManager.encode_hash_for_dial_macro_argument :timeout => 20, :key => "#"
38
- call_mock = flexmock "a call that has network_script as a variable", :variables => {:network_script => "confirm!#{confirm_options[/^M\(\^?(.+)\)$/,1]}"}
39
- manager_mock = flexmock 'a mock ConfirmationManager'
40
-
41
- the_following_code {
42
- flexstub(Adhearsion).should_receive(:receive_call_from).once.and_return(call_mock)
43
- flexmock(Adhearsion::DialPlan::ConfirmationManager).should_receive(:confirmation_call?).once.with(call_mock).and_return true
44
- flexmock(Adhearsion::DialPlan::ConfirmationManager).should_receive(:handle).once.with(call_mock).and_throw :handled_call!
45
- server.serve(nil)
46
- }.should.throw :handled_call!
47
- end
48
-
49
- test 'calling the serve() method invokes any BeforeCall hooks' do
50
- flexmock(Adhearsion::Hooks::BeforeCall).should_receive(:trigger_hooks).once.and_throw :before_call_hooks_executed
51
- assert_throws :before_call_hooks_executed do
52
- server.serve nil
53
- end
54
- end
55
-
56
- test 'should execute the OnHungupCall hooks when a HungupExtensionCallException is raised' do
57
- call_mock = flexmock 'a bogus call', :hungup_call? => true, :variables => {:extension => "h"}
58
- mock_env = flexmock "A mock execution environment which gets passed along in the HungupExtensionCallException"
59
-
60
- stub_confirmation_manager!
61
- flexstub(Adhearsion).should_receive(:receive_call_from).once.and_return(call_mock)
62
- flexmock(Adhearsion::DialPlan::Manager).should_receive(:handle).once.and_raise Adhearsion::HungupExtensionCallException.new(mock_env)
63
- flexmock(Adhearsion::Hooks::OnHungupCall).should_receive(:trigger_hooks).once.with(mock_env).and_throw :hungup_call
64
-
65
- the_following_code { server.serve nil }.should.throw :hungup_call
66
- end
67
-
68
- test 'should execute the OnFailedCall hooks when a FailedExtensionCallException is raised' do
69
- call_mock = flexmock 'a bogus call', :failed_call? => true, :variables => {:extension => "failed"}
70
- mock_env = flexmock "A mock execution environment which gets passed along in the HungupExtensionCallException", :failed_reason => "does not matter"
71
-
72
- server = Adhearsion::VoIP::Asterisk::AGI::Server::RubyServer.new :port, :host
73
-
74
- flexmock(Adhearsion).should_receive(:receive_call_from).once.and_return(call_mock)
75
- flexmock(Adhearsion::DialPlan::Manager).should_receive(:handle).once.and_raise Adhearsion::FailedExtensionCallException.new(mock_env)
76
- flexmock(Adhearsion::Hooks::OnFailedCall).should_receive(:trigger_hooks).once.with(mock_env).and_throw :failed_call
77
- the_following_code { server.serve nil }.should.throw :failed_call
78
- end
79
-
80
- end
81
-
82
- context "Active Calls" do
83
- include CallVariableTestHelper
84
- attr_accessor :typical_call
85
-
86
- before do
87
- @mock_io = StringIO.new
88
- @typical_call = Adhearsion::Call.new(@mock_io, typical_call_variables_hash)
89
- end
90
-
91
- after do
92
- Adhearsion::active_calls.clear!
93
- end
94
-
95
- test 'A Call object can be instantiated with a hash of attributes' do
96
- the_following_code {
97
- Adhearsion::Call.new(@mock_io, {})
98
- }.should.not.raise
99
- end
100
-
101
- test 'Attributes passed into initialization of call object are accessible as attributes on the object' do
102
- Adhearsion::Call.new(@mock_io, {:channel => typical_call_variables_hash[:channel]}).channel.should == typical_call_variables_hash[:channel]
103
- end
104
-
105
- test 'Attributes passed into initialization of call object are accessible in the variables() Hash' do
106
- Adhearsion::Call.new(@mock_io, typical_call_variables_hash).variables.should.equal typical_call_variables_hash
107
- end
108
-
109
- test 'Can add a call to the active calls list' do
110
- Adhearsion.active_calls.should.not.be.any
111
- Adhearsion.active_calls << typical_call
112
- Adhearsion.active_calls.size.should == 1
113
- end
114
-
115
- test 'A hungup call removes itself from the active calls' do
116
- mock_io = flexmock typical_call_variable_io
117
- mock_io.should_receive(:close).once
118
-
119
- size_before = Adhearsion.active_calls.size
120
-
121
- call = Adhearsion.receive_call_from mock_io
122
- Adhearsion.active_calls.size.should.be > size_before
123
- call.hangup!
124
- Adhearsion.active_calls.size.should == size_before
125
- end
126
-
127
- test 'Can find active call by unique ID' do
128
- Adhearsion.active_calls << typical_call
129
- assert_not_nil Adhearsion.active_calls.find(typical_call_variables_hash[:channel])
130
- end
131
-
132
- test 'A call can store the IO associated with the PBX/switch connection' do
133
- Adhearsion::Call.new(@mock_io, {}).should.respond_to(:io)
134
- end
135
-
136
- test 'A call can be instantiated given a PBX/switch IO' do
137
- call = Adhearsion::Call.receive_from(typical_call_variable_io)
138
- call.should.be.kind_of(Adhearsion::Call)
139
- call.channel.should == typical_call_variables_hash[:channel]
140
- end
141
-
142
- test 'A call with an extension of "t" raises a UselessCallException' do
143
- the_following_code {
144
- Adhearsion::Call.new(@mock_io, typical_call_variables_hash.merge(:extension => 't'))
145
- }.should.raise(Adhearsion::UselessCallException)
146
- end
147
-
148
- test 'Can create a call and add it via a top-level method on the Adhearsion module' do
149
- assert !Adhearsion.active_calls.any?
150
- call = Adhearsion.receive_call_from(typical_call_variable_io)
151
- call.should.be.kind_of(Adhearsion::Call)
152
- Adhearsion.active_calls.size.should == 1
153
- end
154
-
155
- test 'A call can identify its originating voip platform' do
156
- call = Adhearsion::receive_call_from(typical_call_variable_io)
157
- call.originating_voip_platform.should.equal(:asterisk)
158
- end
159
-
160
- end
161
-
162
- context 'A new Call object' do
163
-
164
- include CallVariableTestHelper
165
-
166
- test "it should have an @inbox object that's a synchronized Queue" do
167
- new_call = Adhearsion::Call.new(nil, {})
168
- new_call.inbox.should.be.kind_of Queue
169
- new_call.should.respond_to :<<
170
- end
171
-
172
- test 'the unique_identifier() method should return the :channel variable for :asterisk calls' do
173
- variables = typical_call_variables_hash
174
- new_call = Adhearsion::Call.new(nil, typical_call_variables_hash)
175
- flexmock(new_call).should_receive(:originating_voip_platform).and_return :asterisk
176
- new_call.unique_identifier.should == variables[:channel]
177
- end
178
-
179
- test "Call#define_singleton_accessor_with_pair should define a singleton method, not a class method" do
180
- control = Adhearsion::Call.new(nil, {})
181
- experiment = Adhearsion::Call.new(nil, {})
182
-
183
- experiment.send(:define_singleton_accessor_with_pair, "ohai", 123)
184
- experiment.should.respond_to "ohai"
185
- control.should.not.respond_to "ohai"
186
- end
187
-
188
- end
189
-
190
- context 'the Calls collection' do
191
-
192
- include CallVariableTestHelper
193
-
194
- test 'the #<< method should add a Call to the Hash with its unique_id' do
195
- id = rand
196
- collection = Adhearsion::Calls.new
197
- call = Adhearsion::Call.new(nil, {})
198
- flexmock(call).should_receive(:unique_identifier).and_return id
199
- collection << call
200
- hash = collection.instance_variable_get("@calls")
201
- hash.should.not.be.empty
202
- hash[id].should.equal call
203
- end
204
-
205
- test '#size should return the size of the Hash' do
206
- collection = Adhearsion::Calls.new
207
- collection.size.should.equal 0
208
- collection << Adhearsion::Call.new(nil, {})
209
- collection.size.should.equal 1
210
- end
211
-
212
- test '#remove_inactive_call should delete the call in the Hash' do
213
- collection = Adhearsion::Calls.new
214
-
215
- number_of_calls = 10
216
- unique_ids = Array.new(number_of_calls) { rand }
217
- calls = unique_ids.map { |id| Adhearsion::Call.new(nil, {:uniqueid => id}) }
218
- calls.each { |call| collection << call }
219
-
220
- deleted_call = calls[number_of_calls / 2]
221
- collection.remove_inactive_call deleted_call
222
- collection.size.should.equal number_of_calls - 1
223
- end
224
-
225
- test '#find_by_unique_id should pull the Call from the Hash using the unique_id' do
226
- id = rand
227
- call_database = flexmock "a mock Hash in which calls are stored"
228
- call_database.should_receive(:[]).once.with(id)
229
- collection = Adhearsion::Calls.new
230
- flexmock(collection).should_receive(:calls).once.and_return(call_database)
231
- collection.find(id)
232
- end
233
-
234
- end
235
-
236
- context 'Typical call variable parsing with typical data that has no special treatment' do
237
- include CallVariableTestHelper
238
- attr_reader :variables, :io
239
-
240
- setup do
241
- @io = typical_call_variable_io
242
- @variables = parsed_call_variables_from(io)
243
- end
244
-
245
- # test "listing all variable names" do
246
- # parser.variable_names.map(&:to_s).sort.should == typical_call_variables_hash.keys.map(&:to_s).sort
247
- # end
248
-
249
- test "extracts call variables into a Hash" do
250
- assert variables.kind_of?(Hash)
251
- end
252
-
253
- test "extracting and converting variables and their values to a hash" do
254
- variables.should == typical_call_variables_hash
255
- end
256
- end
257
-
258
- context 'Call variable parsing with data that is treated specially' do
259
- include CallVariableTestHelper
260
- test "callington is renamed to type_of_calling_number" do
261
- variables = parsed_call_variables_from typical_call_variable_io
262
- variables[:type_of_calling_number].should == :unknown
263
- variables.has_key?(:callington).should == false
264
- end
265
- test "normalizes the context to be a valid Ruby method name"
266
- test "value of 'no' converts to false" do
267
- merged_hash_with_call_variables(:foo => "no")[:foo].should.equal(false)
268
- end
269
- test "value of 'yes' converts to true"
270
- test "value of 'unknown' converts to nil"
271
- test 'separate_line_into_key_value_pair parses values with spaces in them' do
272
- key, value = Adhearsion::Call::Variables::Parser.separate_line_into_key_value_pair("foo: My Name")
273
- value.should == 'My Name'
274
- end
275
- end
276
-
277
- context 'Typical call variable line parsing with a typical line that is not treated specially' do
278
- include CallVariableTestHelper
279
- attr_reader :parser, :line
280
-
281
- setup do
282
- @line = 'agi_channel: SIP/marcel-b58046e0'
283
- @key, @value = Adhearsion::Call::Variables::Parser.separate_line_into_key_value_pair line
284
- end
285
-
286
- test "raw name is extracted correctly" do
287
- @key.should == 'agi_channel'
288
- end
289
-
290
- test "raw value is extracted correctly" do
291
- @value.should == 'SIP/marcel-b58046e0'
292
- end
293
- end
294
-
295
- context 'Call variable line parsing with a line that is treated specially' do
296
- include CallVariableTestHelper
297
- attr_reader :key, :value, :line
298
-
299
- setup do
300
- @line = 'agi_request: agi://10.0.0.152'
301
- @key, @value = Adhearsion::Call::Variables::Parser.separate_line_into_key_value_pair line
302
- end
303
-
304
- test "splits out name and value correctly even if the value contains a semicolon (i.e. the same character that is used as the name/value separators)" do
305
- @key.should == 'agi_request'
306
- @value.should == 'agi://10.0.0.152'
307
- end
308
-
309
- end
310
-
311
- context "Extracting the query from the request URI" do
312
- include CallVariableTestHelper
313
- attr_reader :parser
314
-
315
- setup do
316
- # We don't want this Call::Variables::Parser to call parse because we only care about handling the request
317
- # variable which we mock here.
318
- @parser = Adhearsion::Call::Variables::Parser.new(StringIO.new('This argument does not matter'))
319
- end
320
-
321
- test "an empty hash is returned if there is no query string" do
322
- parsed_call_variables_with_query('').should == {}
323
- end
324
-
325
- test "a key and value for parameter is returned when there is one query string parameter" do
326
- parsed_call_variables_with_query('?foo=bar').should == {'foo' => 'bar'}
327
- end
328
-
329
- test "both key/value pairs are returned when there are a pair of query string parameters" do
330
- parsed_call_variables_with_query('?foo=bar&baz=quux').should == {'foo' => 'bar', 'baz' => 'quux'}
331
- end
332
-
333
- test "all key/value pairs are returned when there are more than a pair of query string parameters" do
334
- parsed_call_variables_with_query('?foo=bar&baz=quux&name=marcel').should == {'foo' => 'bar', 'baz' => 'quux', 'name' => 'marcel'}
335
- end
336
-
337
- end
338
-
339
- BEGIN {
340
- module CallVariableTestHelper
341
- def parsed_call_variables_from(io)
342
- Adhearsion::Call::Variables::Parser.parse(io).variables
343
- end
344
-
345
- def coerce_call_variables(variables)
346
- Adhearsion::Call::Variables::Parser.coerce_variables(variables)
347
- end
348
-
349
- def merged_hash_with_call_variables(new_hash)
350
- call_variables_with_new_query = typical_call_variables_hash.merge new_hash
351
- coerce_call_variables call_variables_with_new_query
352
- end
353
-
354
- def parsed_call_variables_with_query(query_string)
355
- request_string = "agi://10.0.0.0/#{query_string}"
356
- merged_hash_with_call_variables({:request => request_string})[:query]
357
- end
358
-
359
- def typical_call_variable_io
360
- StringIO.new(typical_call_variable_section)
361
- end
362
-
363
- def typical_call_variable_section
364
- <<-VARIABLES
365
- agi_network: yes
366
- agi_request: agi://10.0.0.152/monkey?foo=bar&qaz=qwerty
367
- agi_channel: SIP/marcel-b58046e0
368
- agi_language: en
369
- agi_type: SIP
370
- agi_uniqueid: 1191245124.16
371
- agi_callerid: 011441234567899
372
- agi_calleridname: unknown
373
- agi_callingpres: 0
374
- agi_callingani2: 0
375
- agi_callington: 0
376
- agi_callingtns: 0
377
- agi_dnid: 911
378
- agi_rdnis: unknown
379
- agi_context: adhearsion
380
- agi_extension: 911
381
- agi_priority: 1
382
- agi_enhanced: 0.0
383
- agi_accountcode:
384
-
385
- VARIABLES
386
- end
387
-
388
- # TODO:
389
- # - "unknown" should be converted to nil
390
- # - "yes" or "no" should be converted to true or false
391
- # - numbers beginning with a 0 MUST be converted to a NumericalString
392
- # - Look up why there are so many zeroes. They're likely reprentative of some PRI definition.
393
-
394
- def typical_call_variables_hash
395
- uncoerced_variable_map = expected_uncoerced_variable_map
396
-
397
- uncoerced_variable_map[:request] = URI.parse(uncoerced_variable_map[:request])
398
- uncoerced_variable_map[:extension] = 911 # Adhearsion::VoIP::DSL::PhoneNumber.new(uncoerced_variable_map[:extension]) #
399
- uncoerced_variable_map[:callerid] = Adhearsion::VoIP::DSL::NumericalString.new(uncoerced_variable_map[:callerid])
400
-
401
- uncoerced_variable_map[:network] = true
402
- uncoerced_variable_map[:calleridname] = nil
403
- uncoerced_variable_map[:callingpres] = 0
404
- uncoerced_variable_map[:callingani2] = 0
405
- uncoerced_variable_map[:callingtns] = 0
406
- uncoerced_variable_map[:dnid] = 911
407
- uncoerced_variable_map[:rdnis] = nil
408
- uncoerced_variable_map[:priority] = 1
409
- uncoerced_variable_map[:enhanced] = 0.0
410
- uncoerced_variable_map[:uniqueid] = 1191245124.16
411
-
412
- uncoerced_variable_map[:type_of_calling_number] = Adhearsion::VoIP::Constants::Q931_TYPE_OF_NUMBER[uncoerced_variable_map.delete(:callington).to_i]
413
-
414
- coerced_variable_map = uncoerced_variable_map
415
- coerced_variable_map[:query] = {"foo" => "bar", "qaz" => "qwerty"}
416
- coerced_variable_map
417
- end
418
-
419
- def expected_uncoerced_variable_map
420
- {:network => 'yes',
421
- :request => 'agi://10.0.0.152/monkey?foo=bar&qaz=qwerty',
422
- :channel => 'SIP/marcel-b58046e0',
423
- :language => 'en',
424
- :type => 'SIP',
425
- :uniqueid => '1191245124.16',
426
- :callerid => '011441234567899',
427
- :calleridname => 'unknown',
428
- :callingpres => '0',
429
- :callingani2 => '0',
430
- :callington => '0',
431
- :callingtns => '0',
432
- :dnid => '911',
433
- :rdnis => 'unknown',
434
- :context => 'adhearsion',
435
- :extension => '911',
436
- :priority => '1',
437
- :enhanced => '0.0',
438
- :accountcode => ''}
439
- end
440
- end
441
-
442
- module AgiServerTestHelper
443
- def stub_before_call_hooks!
444
- flexstub(Adhearsion::Hooks::BeforeCall).should_receive :trigger_hooks
445
- end
446
-
447
- def stub_confirmation_manager!
448
- flexstub(Adhearsion::DialPlan::ConfirmationManager).should_receive(:confirmation_call?).and_return false
449
- end
450
- end
451
- }