adhearsion 2.0.0.alpha3 → 2.0.0.beta1

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