adhearsion 2.0.0.alpha3 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/CHANGELOG.md +13 -1
  2. data/README.markdown +1 -1
  3. data/features/cli_daemon.feature +2 -3
  4. data/features/step_definitions/cli_steps.rb +0 -1
  5. data/lib/adhearsion/call.rb +50 -32
  6. data/lib/adhearsion/call_controller.rb +53 -4
  7. data/lib/adhearsion/call_controller/dial.rb +26 -6
  8. data/lib/adhearsion/call_controller/input.rb +1 -1
  9. data/lib/adhearsion/call_controller/menu.rb +2 -2
  10. data/lib/adhearsion/call_controller/output.rb +11 -11
  11. data/lib/adhearsion/call_controller/utility.rb +3 -3
  12. data/lib/adhearsion/calls.rb +0 -4
  13. data/lib/adhearsion/cli_commands.rb +2 -3
  14. data/lib/adhearsion/console.rb +42 -14
  15. data/lib/adhearsion/foundation/thread_safety.rb +8 -2
  16. data/lib/adhearsion/generators/app/templates/Rakefile +1 -4
  17. data/lib/adhearsion/initializer.rb +12 -5
  18. data/lib/adhearsion/logging.rb +23 -4
  19. data/lib/adhearsion/process.rb +11 -2
  20. data/lib/adhearsion/punchblock_plugin.rb +8 -0
  21. data/lib/adhearsion/punchblock_plugin/initializer.rb +11 -4
  22. data/lib/adhearsion/version.rb +1 -1
  23. data/spec/adhearsion/call_controller/dial_spec.rb +123 -85
  24. data/spec/adhearsion/call_controller/output_spec.rb +10 -0
  25. data/spec/adhearsion/call_controller_spec.rb +59 -52
  26. data/spec/adhearsion/call_spec.rb +47 -3
  27. data/spec/adhearsion/console_spec.rb +10 -1
  28. data/spec/adhearsion/initializer_spec.rb +5 -5
  29. data/spec/adhearsion/logging_spec.rb +17 -1
  30. data/spec/adhearsion/process_spec.rb +18 -0
  31. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +20 -8
  32. data/spec/adhearsion/punchblock_plugin_spec.rb +42 -0
  33. data/spec/spec_helper.rb +5 -1
  34. metadata +64 -64
@@ -289,6 +289,16 @@ module Adhearsion
289
289
  subject.speak(string).should be true
290
290
  end
291
291
  end
292
+
293
+ describe "converts the argument to a string" do
294
+ it 'calls output with a string' do
295
+ expected_string = "123"
296
+ argument = 123
297
+ subject.should_receive(:play_ssml).once.with(argument, {})
298
+ subject.should_receive(:output).once.with(:text, expected_string, {}).and_return true
299
+ subject.speak(argument)
300
+ end
301
+ end
292
302
  end
293
303
 
294
304
  describe "#ssml_for" do
@@ -104,6 +104,7 @@ module Adhearsion
104
104
  before do
105
105
  flexmock subject, :execute_component_and_await_completion => nil
106
106
  flexmock call.wrapped_object, :write_and_await_response => nil
107
+ flexmock call, :register_controller! => nil
107
108
  flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
108
109
  end
109
110
 
@@ -154,22 +155,19 @@ module Adhearsion
154
155
  subject { PassController.new call }
155
156
 
156
157
  before do
157
- flexmock(call.wrapped_object).should_receive(:write_and_await_response).and_return nil
158
+ flexmock call.wrapped_object, :write_and_await_response => nil
159
+ flexmock call, :register_controller! => nil
158
160
  flexmock subject, :execute_component_and_await_completion => nil
159
161
  flexmock(SecondController).new_instances.should_receive(:md_check).once.with :foo => 'bar'
160
162
  flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
161
163
  end
162
164
 
163
- let(:latch) { CountDownLatch.new 1 }
164
-
165
165
  it "should cease execution of the current controller, and instruct the call to execute another" do
166
166
  subject.should_receive(:before).once.ordered
167
167
  call.should_receive(:answer).once.ordered
168
168
  subject.should_receive(:after).never.ordered
169
- call.wrapped_object.should_receive(:hangup).once.ordered
170
169
 
171
- call.execute_controller subject, latch
172
- latch.wait(1).should be_true
170
+ CallController.exec subject
173
171
  end
174
172
 
175
173
  it "should execute after_call callbacks before passing control" do
@@ -181,12 +179,63 @@ module Adhearsion
181
179
  end
182
180
  end
183
181
 
184
- describe '#write_and_await_response' do
182
+ describe "#write_and_await_response" do
185
183
  let(:message) { Punchblock::Command::Accept.new }
186
184
 
187
- it "delegates to the call" do
188
- flexmock(subject.call).should_receive(:write_and_await_response).once.with(message, 20)
189
- subject.write_and_await_response message, 20
185
+ it "delegates to the call, blocking first until it is allowed to execute" do
186
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
187
+ flexmock(subject.call).should_receive(:write_and_await_response).once.ordered.with(message)
188
+ subject.write_and_await_response message
189
+ end
190
+ end
191
+
192
+ [ :answer,
193
+ :reject,
194
+ :hangup,
195
+ :mute,
196
+ :unmute,
197
+ :join].each do |method_name|
198
+ describe "##{method_name}" do
199
+ it "delegates to the call, blocking first until it is allowed to execute" do
200
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
201
+ flexmock(subject.call).should_receive(method_name).once.ordered
202
+ subject.send method_name
203
+ end
204
+ end
205
+ end
206
+
207
+ describe "#block_until_resumed" do
208
+ context "when the controller has not been paused" do
209
+ it "should not block" do
210
+ t1 = Time.now
211
+ subject.block_until_resumed
212
+ t2 = Time.now
213
+
214
+ (t2 - t1).should < 0.2
215
+ end
216
+ end
217
+
218
+ context "when the controller is paused" do
219
+ before { subject.pause! }
220
+
221
+ it "should unblock when the controller is unpaused" do
222
+ t1 = t2 = nil
223
+ latch = CountDownLatch.new 1
224
+ t = Thread.new do
225
+ t1 = Time.now
226
+ subject.block_until_resumed
227
+ t2 = Time.now
228
+ latch.countdown!
229
+ end
230
+
231
+ sleep 0.5
232
+
233
+ subject.resume!
234
+
235
+ latch.wait(1).should be_true
236
+
237
+ (t2 - t1).should >= 0.5
238
+ end
190
239
  end
191
240
  end
192
241
 
@@ -247,48 +296,6 @@ module Adhearsion
247
296
  (Time.now - starting_time).should > 0.5
248
297
  end
249
298
  end
250
-
251
- describe '#answer' do
252
- it "should delegate to the call" do
253
- flexmock(call).should_receive(:answer).once.with(:foo)
254
- subject.answer :foo
255
- end
256
- end
257
-
258
- describe '#reject' do
259
- it "should delegate to the call" do
260
- flexmock(call).should_receive(:reject).once.with(:foo, :bar)
261
- subject.reject :foo, :bar
262
- end
263
- end
264
-
265
- describe '#hangup' do
266
- it "should delegate to the call" do
267
- flexmock(call).should_receive(:hangup).once.with(:foo)
268
- subject.hangup :foo
269
- end
270
- end
271
-
272
- describe '#mute' do
273
- it 'should delegate to the call' do
274
- flexmock(call).should_receive(:mute).once
275
- subject.mute
276
- end
277
- end
278
-
279
- describe '#unmute' do
280
- it 'should delegate to the call' do
281
- flexmock(call).should_receive(:unmute).once
282
- subject.unmute
283
- end
284
- end
285
-
286
- describe '#join' do
287
- it 'should delegate to the call' do
288
- flexmock(call).should_receive(:join).once.with(:foo)
289
- subject.join :foo
290
- end
291
- end
292
299
  end
293
300
  end
294
301
 
@@ -39,6 +39,8 @@ module Adhearsion
39
39
  its(:offer) { should be offer }
40
40
  its(:client) { should be mock_client }
41
41
 
42
+ its(:after_end_hold_time) { should == 30 }
43
+
42
44
  describe "its variables" do
43
45
  context "with an offer with headers" do
44
46
  let(:headers) { {:x_foo => 'bar'} }
@@ -150,9 +152,11 @@ module Adhearsion
150
152
  end
151
153
 
152
154
  it "shuts down the actor" do
155
+ flexmock subject.wrapped_object, :after_end_hold_time => 2
153
156
  subject << end_event
154
- sleep 5.1
157
+ sleep 2.1
155
158
  subject.should_not be_alive
159
+ lambda { subject.id }.should raise_error Call::ExpiredError, /expired and is no longer accessible/
156
160
  end
157
161
  end
158
162
  end
@@ -270,10 +274,12 @@ module Adhearsion
270
274
  end
271
275
 
272
276
  describe "with an error response" do
273
- let(:response) { Exception.new }
277
+ let(:new_exception) { Class.new Exception }
278
+ let(:response) { new_exception.new }
274
279
 
275
280
  it "raises the error" do
276
- lambda { subject.write_and_await_response message }.should raise_error Exception
281
+ flexmock(Events).should_receive(:trigger).never
282
+ lambda { subject.write_and_await_response message }.should raise_error new_exception
277
283
  end
278
284
  end
279
285
 
@@ -525,6 +531,44 @@ module Adhearsion
525
531
  subject.execute_controller mock_controller, latch
526
532
  latch.wait(3).should be_true
527
533
  end
534
+
535
+ it "should add the controller thread to the important threads" do
536
+ flexmock(CallController).should_receive(:exec)
537
+ controller_thread = subject.execute_controller mock_controller, latch
538
+ Adhearsion::Process.important_threads.should include controller_thread
539
+ end
540
+ end
541
+
542
+ describe "#register_controller" do
543
+ it "should add the controller to a list on the call" do
544
+ subject.register_controller :foo
545
+ subject.controllers.should include :foo
546
+ end
547
+ end
548
+
549
+ context "with two controllers registered" do
550
+ let(:controller1) { flexmock 'CallController1' }
551
+ let(:controller2) { flexmock 'CallController2' }
552
+
553
+ before { subject.controllers << controller1 << controller2 }
554
+
555
+ describe "#pause_controllers" do
556
+ it "should pause each of the registered controllers" do
557
+ controller1.should_receive(:pause!).once
558
+ controller2.should_receive(:pause!).once
559
+
560
+ subject.pause_controllers
561
+ end
562
+ end
563
+
564
+ describe "#resume_controllers" do
565
+ it "should resume each of the registered controllers" do
566
+ controller1.should_receive(:resume!).once
567
+ controller2.should_receive(:resume!).once
568
+
569
+ subject.resume_controllers
570
+ end
571
+ end
528
572
  end
529
573
  end
530
574
 
@@ -2,6 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  module Adhearsion
4
4
  describe Console do
5
+ before do
6
+ flexmock Console.instance, :pry => nil
7
+ end
8
+
5
9
  include FlexMock::ArgumentTypes
6
10
  describe "providing hooks to include console functionality" do
7
11
  it "should allow mixing in a module globally on all CallController classes" do
@@ -130,7 +134,12 @@ module Adhearsion
130
134
  describe "#interact_with_call" do
131
135
  let(:call) { Call.new }
132
136
 
133
- it "should suspend the call's controller"
137
+ it "should pause the call's controllers, and unpause even if the interactive controller raises" do
138
+ flexmock(call).should_receive(:pause_controllers).once.ordered
139
+ flexmock(CallController).should_receive(:exec).once.ordered.and_raise StandardError
140
+ flexmock(call).should_receive(:resume_controllers).once.ordered
141
+ lambda { Console.interact_with_call call }.should raise_error StandardError
142
+ end
134
143
 
135
144
  it "should execute an interactive call controller on the call" do
136
145
  flexmock(CallController).should_receive(:exec).once.with(on do |c|
@@ -9,7 +9,7 @@ describe Adhearsion::Initializer do
9
9
 
10
10
  describe "#start" do
11
11
  before do
12
- Adhearsion::Logging.reset
12
+ ::Logging.reset
13
13
  flexmock(Adhearsion::Logging).should_receive(:start).once.and_return('')
14
14
  flexmock(::Logging::Appenders::File).should_receive(:assert_valid_logfile).and_return(true)
15
15
  flexmock(::Logging::Appenders).should_receive(:file).and_return(nil)
@@ -21,7 +21,7 @@ describe Adhearsion::Initializer do
21
21
  end
22
22
 
23
23
  after :all do
24
- Adhearsion::Logging.reset
24
+ ::Logging.reset
25
25
  Adhearsion::Logging.start
26
26
  Adhearsion::Logging.silence!
27
27
  end
@@ -137,14 +137,14 @@ describe Adhearsion::Initializer do
137
137
 
138
138
  describe "Initializing logger" do
139
139
  before do
140
- Adhearsion::Logging.reset
140
+ ::Logging.reset
141
141
  flexmock(::Logging::Appenders::File).should_receive(:assert_valid_logfile).and_return(true)
142
142
  flexmock(::Logging::Appenders).should_receive(:file).and_return(nil)
143
143
  Adhearsion.config = nil
144
144
  end
145
145
 
146
146
  after :all do
147
- Adhearsion::Logging.reset
147
+ ::Logging.reset
148
148
  Adhearsion::Logging.start
149
149
  Adhearsion::Logging.silence!
150
150
  Adhearsion::Events.reinitialize_queue!
@@ -191,7 +191,7 @@ describe "Updating RAILS_ENV variable" do
191
191
  include InitializerStubs
192
192
 
193
193
  before do
194
- Adhearsion::Logging.reset
194
+ ::Logging.reset
195
195
  flexmock(Adhearsion::Logging).should_receive(:start).once.and_return('')
196
196
  flexmock(::Logging::Appenders::File).should_receive(:assert_valid_logfile).and_return(true)
197
197
  flexmock(::Logging::Appenders).should_receive(:file).and_return(nil)
@@ -4,7 +4,7 @@ describe Adhearsion::Logging do
4
4
 
5
5
  before :all do
6
6
  ::Logging.shutdown
7
- Adhearsion::Logging.reset
7
+ ::Logging.reset
8
8
  Adhearsion::Logging.init
9
9
  end
10
10
 
@@ -83,6 +83,22 @@ describe Adhearsion::Logging do
83
83
  _foo_logger.object_id.should_not eql(_bar_logger)
84
84
  end
85
85
 
86
+ it 'should reopen logfiles' do
87
+ flexmock(::Logging).should_receive(:reopen).once
88
+ Adhearsion::Logging.reopen_logs
89
+ end
90
+
91
+ it 'should toggle between :trace and the configured log level' do
92
+ orig_level = Adhearsion.config.platform.logging['level']
93
+ Adhearsion.config.platform.logging['level'] = :warn
94
+ Adhearsion::Logging.level = :warn
95
+ Adhearsion::Logging.toggle_trace!
96
+ Adhearsion::Logging.level.should == 0
97
+ Adhearsion::Logging.toggle_trace!
98
+ Adhearsion::Logging.level.should == 3
99
+ Adhearsion.config.platform.logging['level'] = orig_level
100
+ end
101
+
86
102
  describe 'level changing' do
87
103
 
88
104
  before { Adhearsion::Logging.unsilence! }
@@ -72,5 +72,23 @@ module Adhearsion
72
72
  Adhearsion::Process.final_shutdown
73
73
  end
74
74
  end
75
+
76
+ it 'should handle subsequent :shutdown events in the correct order' do
77
+ Adhearsion::Process.booted
78
+ Adhearsion::Process.state_name.should be :running
79
+ Adhearsion::Process.shutdown
80
+ Adhearsion::Process.state_name.should be :stopping
81
+ Adhearsion::Process.shutdown
82
+ Adhearsion::Process.state_name.should be :rejecting
83
+ Adhearsion::Process.shutdown
84
+ Adhearsion::Process.state_name.should be :stopped
85
+ flexmock(Adhearsion::Process.instance).should_receive(:die_now!).once
86
+ Adhearsion::Process.shutdown
87
+ end
88
+
89
+ it 'should forcibly kill the Adhearsion process on :force_stop' do
90
+ flexmock(::Process).should_receive(:exit).once.with(1)
91
+ Adhearsion::Process.force_stop
92
+ end
75
93
  end
76
94
  end
@@ -17,6 +17,7 @@ module Adhearsion
17
17
  config.connection_timeout = 60
18
18
  config.reconnect_attempts = 1.0/0.0
19
19
  config.reconnect_timer = 5
20
+ config.media_engine = nil
20
21
  end
21
22
  end
22
23
 
@@ -35,6 +36,7 @@ module Adhearsion
35
36
  config.connection_timeout = options[:connection_timeout] if options.has_key?(:connection_timeout)
36
37
  config.reconnect_attempts = options[:reconnect_attempts] if options.has_key?(:reconnect_attempts)
37
38
  config.reconnect_timer = options[:reconnect_timer] if options.has_key?(:reconnect_timer)
39
+ config.media_engine = options[:media_engine] if options.has_key?(:media_engine)
38
40
  end
39
41
 
40
42
  Initializer.init
@@ -43,6 +45,8 @@ module Adhearsion
43
45
 
44
46
  before do
45
47
  Events.refresh!
48
+ flexmock Adhearsion::Process, :fqdn => 'hostname'
49
+ flexmock ::Process, :pid => 1234
46
50
  end
47
51
 
48
52
  let(:call_id) { rand }
@@ -87,12 +91,25 @@ module Adhearsion
87
91
  it "should properly set the reconnect_timer value" do
88
92
  subject.reconnect_timer.should == 5
89
93
  end
94
+
95
+ it "should properly set the media_engine value" do
96
+ subject.media_engine.should == nil
97
+ end
98
+ end
99
+
100
+ it "starts the client with the correct resource" do
101
+ username = "usera@127.0.0.1/hostname-1234"
102
+
103
+ flexmock(::Punchblock::Connection::XMPP).should_receive(:new).once.with(FlexMock.hsh :username => username).and_return do
104
+ flexmock 'Client', :event_handler= => true
105
+ end
106
+ initialize_punchblock
90
107
  end
91
108
 
92
109
  it "starts the client with any overridden settings" do
93
- overrides = {:username => 'userb@127.0.0.1', :password => '123', :host => 'foo.bar.com', :port => 200, :connection_timeout => 20, :root_domain => 'foo.com', :calls_domain => 'call.foo.com', :mixers_domain => 'mixer.foo.com'}
110
+ overrides = {:username => 'userb@127.0.0.1', :password => '123', :host => 'foo.bar.com', :port => 200, :connection_timeout => 20, :root_domain => 'foo.com', :calls_domain => 'call.foo.com', :mixers_domain => 'mixer.foo.com', :media_engine => :swift}
94
111
 
95
- flexmock(::Punchblock::Connection::XMPP).should_receive(:new).once.with(overrides).and_return do
112
+ flexmock(::Punchblock::Connection::XMPP).should_receive(:new).once.with(overrides.merge({:username => 'userb@127.0.0.1/hostname-1234'})).and_return do
96
113
  flexmock 'Client', :event_handler= => true
97
114
  end
98
115
  initialize_punchblock overrides
@@ -157,7 +174,7 @@ module Adhearsion
157
174
  end
158
175
 
159
176
  describe 'using Asterisk' do
160
- let(:overrides) { {:username => 'test', :password => '123', :host => 'foo.bar.com', :port => 200, :connection_timeout => 20, :root_domain => 'foo.com', :calls_domain => 'call.foo.com', :mixers_domain => 'mixer.foo.com'} }
177
+ let(:overrides) { {:username => 'test', :password => '123', :host => 'foo.bar.com', :port => 200, :connection_timeout => 20, :root_domain => 'foo.com', :calls_domain => 'call.foo.com', :mixers_domain => 'mixer.foo.com', :media_engine => :swift} }
161
178
 
162
179
  it 'should start an Asterisk PB connection' do
163
180
  flexmock(::Punchblock::Connection::Asterisk).should_receive(:new).once.with(overrides).and_return do
@@ -247,11 +264,6 @@ module Adhearsion
247
264
  Adhearsion.active_calls << mock_call
248
265
  end
249
266
 
250
- it "should log an error" do
251
- flexmock(Adhearsion::Logging.get_logger(Initializer)).should_receive(:debug).once.with("Event received for call #{call_id}: #{mock_event.inspect}")
252
- Events.trigger_immediately :punchblock, mock_event
253
- end
254
-
255
267
  it "should place the event in the call's inbox" do
256
268
  mock_call.should_receive(:deliver_message!).once.with(mock_event)
257
269
  Initializer.dispatch_call_event mock_event
@@ -7,5 +7,47 @@ module Adhearsion
7
7
  PunchblockPlugin.client.should be :foo
8
8
  PunchblockPlugin::Initializer.client = nil
9
9
  end
10
+
11
+ describe '#execute_component' do
12
+ let(:message) { Punchblock::Command::Accept.new }
13
+ let(:response) { :foo }
14
+ let(:mock_client) { flexmock 'Client', :execute_command => true }
15
+
16
+ before do
17
+ PunchblockPlugin::Initializer.client = mock_client
18
+ flexmock message, :execute! => true
19
+ message.response = response
20
+ end
21
+
22
+ it "writes a command to the client" do
23
+ flexmock(PunchblockPlugin.client).should_receive(:execute_command).once.with(message)
24
+ PunchblockPlugin.execute_component message
25
+ end
26
+
27
+ it "blocks until a response is received" do
28
+ slow_command = Punchblock::Command::Dial.new
29
+ Thread.new do
30
+ sleep 0.5
31
+ slow_command.response = response
32
+ end
33
+ starting_time = Time.now
34
+ PunchblockPlugin.execute_component slow_command
35
+ (Time.now - starting_time).should >= 0.5
36
+ end
37
+
38
+ describe "with a successful response" do
39
+ it "returns the executed command" do
40
+ PunchblockPlugin.execute_component(message).should be message
41
+ end
42
+ end
43
+
44
+ describe "with an error response" do
45
+ let(:response) { Exception.new }
46
+
47
+ it "raises the error" do
48
+ lambda { PunchblockPlugin.execute_component message }.should raise_error Exception
49
+ end
50
+ end
51
+ end
10
52
  end
11
53
  end