adhearsion 2.3.5 → 2.4.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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile +2 -0
- data/README.markdown +21 -2
- data/adhearsion.gemspec +5 -4
- data/features/cli_plugin.feature +41 -0
- data/features/cli_start.feature +12 -4
- data/features/step_definitions/cli_steps.rb +12 -0
- data/features/support/env.rb +1 -1
- data/features/support/utils.rb +0 -1
- data/lib/adhearsion.rb +4 -1
- data/lib/adhearsion/call.rb +92 -22
- data/lib/adhearsion/call_controller.rb +19 -15
- data/lib/adhearsion/call_controller/dial.rb +157 -25
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +8 -0
- data/lib/adhearsion/call_controller/output/async_player.rb +1 -1
- data/lib/adhearsion/call_controller/output/formatter.rb +1 -1
- data/lib/adhearsion/call_controller/output/player.rb +1 -1
- data/lib/adhearsion/calls.rb +2 -0
- data/lib/adhearsion/cli_commands.rb +3 -163
- data/lib/adhearsion/cli_commands/ahn_command.rb +141 -0
- data/lib/adhearsion/cli_commands/plugin_command.rb +74 -0
- data/lib/adhearsion/cli_commands/thor_errors.rb +36 -0
- data/lib/adhearsion/console.rb +14 -6
- data/lib/adhearsion/generators/app/templates/spec/call_controllers/simon_game_spec.rb +36 -36
- data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +1 -1
- data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +0 -1
- data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +1 -1
- data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +0 -1
- data/lib/adhearsion/logging.rb +5 -1
- data/lib/adhearsion/outbound_call.rb +16 -0
- data/lib/adhearsion/punchblock_plugin.rb +0 -2
- data/lib/adhearsion/punchblock_plugin/initializer.rb +7 -12
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +785 -32
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +10 -0
- data/spec/adhearsion/call_controller/output/async_player_spec.rb +1 -1
- data/spec/adhearsion/call_controller/output/player_spec.rb +1 -1
- data/spec/adhearsion/call_controller/output_spec.rb +3 -3
- data/spec/adhearsion/call_controller/record_spec.rb +1 -1
- data/spec/adhearsion/call_controller_spec.rb +13 -9
- data/spec/adhearsion/call_spec.rb +216 -51
- data/spec/adhearsion/calls_spec.rb +1 -1
- data/spec/adhearsion/console_spec.rb +20 -9
- data/spec/adhearsion/outbound_call_spec.rb +40 -6
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +9 -21
- data/spec/adhearsion/punchblock_plugin_spec.rb +1 -1
- data/spec/adhearsion/router_spec.rb +1 -1
- data/spec/spec_helper.rb +11 -15
- data/spec/support/call_controller_test_helpers.rb +2 -2
- data/spec/support/punchblock_mocks.rb +2 -2
- metadata +41 -16
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe <%= @controller_name.camelcase %> do
|
6
6
|
|
7
|
-
let(:mock_call) {
|
7
|
+
let(:mock_call) { double 'Call', :to => '1112223333', :from => "2223334444" }
|
8
8
|
let(:metadata) { {} }
|
9
9
|
subject { <%= @controller_name.camelcase %>.new(mock_call, metadata) }
|
10
10
|
|
@@ -26,6 +26,5 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_development_dependency %q<bundler>, ["~> 1.0"]
|
27
27
|
s.add_development_dependency %q<rspec>, ["~> 2.5"]
|
28
28
|
s.add_development_dependency %q<rake>, [">= 0"]
|
29
|
-
s.add_development_dependency %q<mocha>, [">= 0"]
|
30
29
|
s.add_development_dependency %q<guard-rspec>
|
31
30
|
end
|
data/lib/adhearsion/logging.rb
CHANGED
@@ -7,6 +7,10 @@ module Adhearsion
|
|
7
7
|
|
8
8
|
LOG_LEVELS = %w(TRACE DEBUG INFO WARN ERROR FATAL)
|
9
9
|
|
10
|
+
class ::Logging::Repository
|
11
|
+
def delete( key ) @h.delete(to_key(key)) end
|
12
|
+
end
|
13
|
+
|
10
14
|
module HasLogger
|
11
15
|
def logger
|
12
16
|
::Logging.logger[logger_id]
|
@@ -76,7 +80,7 @@ module Adhearsion
|
|
76
80
|
|
77
81
|
::Logging.logger.root.level = level
|
78
82
|
|
79
|
-
formatter = formatter if formatter
|
83
|
+
self.formatter = formatter if formatter
|
80
84
|
end
|
81
85
|
|
82
86
|
def default_appenders
|
@@ -36,6 +36,10 @@ module Adhearsion
|
|
36
36
|
dial_command.target_call_id if dial_command
|
37
37
|
end
|
38
38
|
|
39
|
+
def domain
|
40
|
+
dial_command.domain if dial_command
|
41
|
+
end
|
42
|
+
|
39
43
|
def client
|
40
44
|
PunchblockPlugin::Initializer.client
|
41
45
|
end
|
@@ -76,6 +80,12 @@ module Adhearsion
|
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
83
|
+
# @private
|
84
|
+
def register_initial_handlers
|
85
|
+
super
|
86
|
+
on_answer { |event| @start_time = Time.now }
|
87
|
+
end
|
88
|
+
|
79
89
|
def run_router
|
80
90
|
catching_standard_errors do
|
81
91
|
Adhearsion.router.handle current_actor
|
@@ -105,5 +115,11 @@ module Adhearsion
|
|
105
115
|
run_router_on_answer
|
106
116
|
end
|
107
117
|
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def transport
|
122
|
+
dial_command.transport if dial_command
|
123
|
+
end
|
108
124
|
end
|
109
125
|
end
|
@@ -20,8 +20,6 @@ module Adhearsion
|
|
20
20
|
port Proc.new { PunchblockPlugin.default_port_for_platform platform }, :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "Port punchblock needs to connect"
|
21
21
|
certs_directory nil , :desc => "Directory containing certificates for securing the connection."
|
22
22
|
root_domain nil , :desc => "The root domain at which to address the server"
|
23
|
-
calls_domain nil , :desc => "The domain at which to address calls"
|
24
|
-
mixers_domain nil , :desc => "The domain at which to address mixers"
|
25
23
|
connection_timeout 60 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "The amount of time to wait for a connection"
|
26
24
|
reconnect_attempts 1.0/0.0 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "The number of times to (re)attempt connection to the server"
|
27
25
|
reconnect_timer 5 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "Delay between connection attempts"
|
@@ -5,7 +5,7 @@ require 'blather'
|
|
5
5
|
module Adhearsion
|
6
6
|
class PunchblockPlugin
|
7
7
|
class Initializer
|
8
|
-
cattr_accessor :config, :client, :
|
8
|
+
cattr_accessor :config, :client, :dispatcher, :attempts
|
9
9
|
|
10
10
|
self.attempts = 0
|
11
11
|
|
@@ -14,16 +14,10 @@ module Adhearsion
|
|
14
14
|
self.config = Adhearsion.config[:punchblock]
|
15
15
|
|
16
16
|
username = self.config.username
|
17
|
-
|
18
|
-
when :xmpp
|
17
|
+
if (self.config.platform || :xmpp) == :xmpp
|
19
18
|
username = Blather::JID.new username
|
20
19
|
username = Blather::JID.new username.node, username.domain, resource unless username.resource
|
21
20
|
username = username.to_s
|
22
|
-
Punchblock::Connection::XMPP
|
23
|
-
when :asterisk
|
24
|
-
Punchblock::Connection::Asterisk
|
25
|
-
when :freeswitch
|
26
|
-
Punchblock::Connection::Freeswitch
|
27
21
|
end
|
28
22
|
|
29
23
|
connection_options = {
|
@@ -34,14 +28,11 @@ module Adhearsion
|
|
34
28
|
:port => self.config.port,
|
35
29
|
:certs => self.config.certs_directory,
|
36
30
|
:root_domain => self.config.root_domain,
|
37
|
-
:calls_domain => self.config.calls_domain,
|
38
|
-
:mixers_domain => self.config.mixers_domain,
|
39
31
|
:media_engine => self.config.media_engine,
|
40
32
|
:default_voice => self.config.default_voice
|
41
33
|
}
|
42
34
|
|
43
|
-
self.
|
44
|
-
self.client = Punchblock::Client.new :connection => connection
|
35
|
+
self.client = Punchblock.client_with_connection self.config.platform, connection_options
|
45
36
|
|
46
37
|
# Tell the Punchblock connection that we are ready to process calls.
|
47
38
|
Events.register_callback :after_initialized do
|
@@ -170,6 +161,10 @@ module Adhearsion
|
|
170
161
|
def resource
|
171
162
|
[Adhearsion::Process.fqdn, ::Process.pid].join '-'
|
172
163
|
end
|
164
|
+
|
165
|
+
def connection
|
166
|
+
client.connection
|
167
|
+
end
|
173
168
|
end
|
174
169
|
end # Punchblock
|
175
170
|
end # Plugin
|
data/lib/adhearsion/version.rb
CHANGED
@@ -20,11 +20,11 @@ module Adhearsion
|
|
20
20
|
let(:latch) { CountDownLatch.new 1 }
|
21
21
|
|
22
22
|
before do
|
23
|
-
other_mock_call.stub id: other_call_id, write_command: true
|
24
|
-
second_other_mock_call.stub id: second_other_call_id, write_command: true
|
23
|
+
other_mock_call.wrapped_object.stub id: other_call_id, write_command: true
|
24
|
+
second_other_mock_call.wrapped_object.stub id: second_other_call_id, write_command: true
|
25
25
|
end
|
26
26
|
|
27
|
-
def mock_end(reason = :
|
27
|
+
def mock_end(reason = :hangup_command)
|
28
28
|
Punchblock::Event::End.new.tap { |event| event.stub reason: reason }
|
29
29
|
end
|
30
30
|
|
@@ -33,7 +33,12 @@ module Adhearsion
|
|
33
33
|
OutboundCall.should_receive(:new).and_return other_mock_call
|
34
34
|
other_mock_call.should_receive(:dial).with(to, :from => 'foo').once
|
35
35
|
dial_thread = Thread.new do
|
36
|
-
subject.dial(to, :from => 'foo')
|
36
|
+
status = subject.dial(to, :from => 'foo')
|
37
|
+
|
38
|
+
status.should be_a Dial::DialStatus
|
39
|
+
joined_status = status.joins[status.calls.first]
|
40
|
+
joined_status.duration.should == 0.0
|
41
|
+
joined_status.result.should == :no_answer
|
37
42
|
end
|
38
43
|
sleep 0.1
|
39
44
|
other_mock_call << mock_end
|
@@ -65,7 +70,6 @@ module Adhearsion
|
|
65
70
|
describe "without a block" do
|
66
71
|
before do
|
67
72
|
other_mock_call.should_receive(:dial).once.with(to, options)
|
68
|
-
other_mock_call.should_receive(:hangup).once
|
69
73
|
OutboundCall.should_receive(:new).and_return other_mock_call
|
70
74
|
end
|
71
75
|
|
@@ -80,6 +84,7 @@ module Adhearsion
|
|
80
84
|
end
|
81
85
|
|
82
86
|
it "unblocks the original controller if the original call ends" do
|
87
|
+
other_mock_call.should_receive(:hangup).once
|
83
88
|
dial_in_thread
|
84
89
|
|
85
90
|
latch.wait(1).should be_false
|
@@ -106,6 +111,7 @@ module Adhearsion
|
|
106
111
|
it "hangs up the new call when the dial unblocks" do
|
107
112
|
call.should_receive(:answer).once
|
108
113
|
other_mock_call.should_receive(:join).once.with(call)
|
114
|
+
other_mock_call.should_receive(:hangup).once
|
109
115
|
|
110
116
|
dial_in_thread
|
111
117
|
|
@@ -146,6 +152,10 @@ module Adhearsion
|
|
146
152
|
t.join
|
147
153
|
status = t.value
|
148
154
|
status.result.should be == :error
|
155
|
+
|
156
|
+
joined_status = status.joins[status.calls.first]
|
157
|
+
joined_status.duration.should == 0.0
|
158
|
+
joined_status.result.should == :error
|
149
159
|
end
|
150
160
|
end
|
151
161
|
|
@@ -166,6 +176,39 @@ module Adhearsion
|
|
166
176
|
t.join
|
167
177
|
status = t.value
|
168
178
|
status.result.should be == :answer
|
179
|
+
status.joined_call.should eq(other_mock_call)
|
180
|
+
|
181
|
+
joined_status = status.joins[status.calls.first]
|
182
|
+
joined_status.result.should == :joined
|
183
|
+
end
|
184
|
+
|
185
|
+
it "records the duration of the join" do
|
186
|
+
call.should_receive(:answer).once
|
187
|
+
other_mock_call.should_receive(:join).once.with(call)
|
188
|
+
other_mock_call.stub hangup: true
|
189
|
+
|
190
|
+
t = dial_in_thread
|
191
|
+
|
192
|
+
sleep 0.5
|
193
|
+
|
194
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
195
|
+
Timecop.freeze base_time
|
196
|
+
|
197
|
+
other_mock_call << mock_answered
|
198
|
+
|
199
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
200
|
+
Timecop.freeze base_time
|
201
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
202
|
+
other_mock_call << mock_end
|
203
|
+
|
204
|
+
latch.wait(1).should be_true
|
205
|
+
|
206
|
+
t.join
|
207
|
+
status = t.value
|
208
|
+
status.result.should be == :answer
|
209
|
+
status.joined_call.should eq(other_mock_call)
|
210
|
+
joined_status = status.joins[status.calls.first]
|
211
|
+
joined_status.duration.should == 37.0
|
169
212
|
end
|
170
213
|
end
|
171
214
|
end
|
@@ -194,11 +237,9 @@ module Adhearsion
|
|
194
237
|
OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call
|
195
238
|
|
196
239
|
other_mock_call.should_receive(:dial).once.with(to, other_options)
|
197
|
-
other_mock_call.should_receive(:hangup).once
|
198
240
|
|
199
241
|
second_other_mock_call.should_receive(:dial).once.with(second_to, second_other_options)
|
200
242
|
second_other_mock_call.should_receive(:join).never
|
201
|
-
second_other_mock_call.should_receive(:hangup).once
|
202
243
|
end
|
203
244
|
|
204
245
|
def dial_in_thread
|
@@ -212,7 +253,9 @@ module Adhearsion
|
|
212
253
|
it "dials all parties and joins the first one to answer, hanging up the rest" do
|
213
254
|
call.should_receive(:answer).once
|
214
255
|
other_mock_call.should_receive(:join).once.with(call)
|
215
|
-
second_other_mock_call.should_receive(:hangup).once
|
256
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
257
|
+
second_other_mock_call << mock_end
|
258
|
+
end
|
216
259
|
|
217
260
|
t = dial_in_thread
|
218
261
|
|
@@ -221,10 +264,6 @@ module Adhearsion
|
|
221
264
|
other_mock_call << mock_answered
|
222
265
|
other_mock_call << mock_end
|
223
266
|
|
224
|
-
latch.wait(1).should be_false
|
225
|
-
|
226
|
-
second_other_mock_call << mock_end
|
227
|
-
|
228
267
|
latch.wait(2).should be_true
|
229
268
|
|
230
269
|
t.join
|
@@ -237,18 +276,17 @@ module Adhearsion
|
|
237
276
|
it "unblocks when the joined call unjoins, allowing it to proceed further" do
|
238
277
|
call.should_receive(:answer).once
|
239
278
|
other_mock_call.should_receive(:join).once.with(call)
|
240
|
-
|
279
|
+
other_mock_call.should_receive(:hangup).once
|
280
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
281
|
+
second_other_mock_call << mock_end
|
282
|
+
end
|
241
283
|
|
242
284
|
t = dial_in_thread
|
243
285
|
|
244
286
|
latch.wait(1).should be_false
|
245
287
|
|
246
288
|
other_mock_call << mock_answered
|
247
|
-
other_mock_call << Punchblock::Event::Unjoined.new(:
|
248
|
-
|
249
|
-
latch.wait(1).should be_false
|
250
|
-
|
251
|
-
second_other_mock_call << mock_end
|
289
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
252
290
|
|
253
291
|
latch.wait(2).should be_true
|
254
292
|
|
@@ -350,7 +388,9 @@ module Adhearsion
|
|
350
388
|
it "has an overall dial status of :answer" do
|
351
389
|
call.should_receive(:answer).once
|
352
390
|
other_mock_call.should_receive(:join).once.with(call)
|
353
|
-
second_other_mock_call.should_receive(:hangup).once
|
391
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
392
|
+
second_other_mock_call << mock_end(:error)
|
393
|
+
end
|
354
394
|
|
355
395
|
t = dial_in_thread
|
356
396
|
|
@@ -359,8 +399,6 @@ module Adhearsion
|
|
359
399
|
other_mock_call << mock_answered
|
360
400
|
other_mock_call << mock_end
|
361
401
|
|
362
|
-
second_other_mock_call << mock_end(:error)
|
363
|
-
|
364
402
|
latch.wait(1).should be_true
|
365
403
|
|
366
404
|
t.join
|
@@ -388,7 +426,7 @@ module Adhearsion
|
|
388
426
|
|
389
427
|
latch.wait
|
390
428
|
time = Time.now - time
|
391
|
-
time.
|
429
|
+
time.round.should be == timeout
|
392
430
|
t.join
|
393
431
|
status = t.value
|
394
432
|
status.result.should be == :timeout
|
@@ -399,7 +437,6 @@ module Adhearsion
|
|
399
437
|
other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout))
|
400
438
|
call.should_receive(:answer).once
|
401
439
|
other_mock_call.should_receive(:join).once.with(call)
|
402
|
-
other_mock_call.should_receive(:hangup).once
|
403
440
|
OutboundCall.should_receive(:new).and_return other_mock_call
|
404
441
|
|
405
442
|
time = Time.now
|
@@ -449,16 +486,16 @@ module Adhearsion
|
|
449
486
|
|
450
487
|
let(:options) { {:confirm => confirmation_controller} }
|
451
488
|
|
452
|
-
before do
|
453
|
-
other_mock_call.should_receive(:dial).once
|
454
|
-
OutboundCall.should_receive(:new).and_return other_mock_call
|
455
|
-
end
|
456
|
-
|
457
489
|
context "with confirmation controller metadata specified" do
|
458
490
|
let(:options) { {:confirm => confirmation_controller, :confirm_metadata => {:foo => 'bar'}} }
|
459
491
|
|
492
|
+
before do
|
493
|
+
other_mock_call.should_receive(:dial).once
|
494
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
495
|
+
end
|
496
|
+
|
460
497
|
it "should set the metadata on the controller" do
|
461
|
-
other_mock_call.should_receive(:hangup).
|
498
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
462
499
|
other_mock_call << mock_end
|
463
500
|
end
|
464
501
|
other_mock_call['confirm'] = false
|
@@ -477,8 +514,13 @@ module Adhearsion
|
|
477
514
|
end
|
478
515
|
|
479
516
|
context "when an outbound call is answered" do
|
517
|
+
before do
|
518
|
+
other_mock_call.should_receive(:dial).once
|
519
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
520
|
+
end
|
521
|
+
|
480
522
|
it "should execute the specified confirmation controller" do
|
481
|
-
other_mock_call.should_receive(:hangup).
|
523
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
482
524
|
other_mock_call << mock_end
|
483
525
|
end
|
484
526
|
other_mock_call['confirm'] = false
|
@@ -503,7 +545,14 @@ module Adhearsion
|
|
503
545
|
|
504
546
|
latch.wait(1).should be_false
|
505
547
|
|
548
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
549
|
+
Timecop.freeze base_time
|
550
|
+
|
506
551
|
other_mock_call << mock_answered
|
552
|
+
|
553
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 42)
|
554
|
+
Timecop.freeze base_time
|
555
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
507
556
|
other_mock_call << mock_end
|
508
557
|
|
509
558
|
latch.wait(1).should be_true
|
@@ -511,10 +560,14 @@ module Adhearsion
|
|
511
560
|
t.join
|
512
561
|
status = t.value
|
513
562
|
status.result.should be == :answer
|
563
|
+
|
564
|
+
joined_status = status.joins[status.calls.first]
|
565
|
+
joined_status.duration.should == 42.0
|
566
|
+
joined_status.result.should == :joined
|
514
567
|
end
|
515
568
|
|
516
569
|
it "should not join the calls if the call is not active after execution of the call controller" do
|
517
|
-
other_mock_call.should_receive(:hangup).
|
570
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
518
571
|
other_mock_call << mock_end
|
519
572
|
end
|
520
573
|
other_mock_call['confirm'] = false
|
@@ -532,10 +585,710 @@ module Adhearsion
|
|
532
585
|
t.join
|
533
586
|
status = t.value
|
534
587
|
status.result.should be == :unconfirmed
|
588
|
+
|
589
|
+
joined_status = status.joins[status.calls.first]
|
590
|
+
joined_status.duration.should == 0.0
|
591
|
+
joined_status.result.should == :unconfirmed
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
context "when multiple calls are made" do
|
596
|
+
before do
|
597
|
+
OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call
|
598
|
+
end
|
599
|
+
|
600
|
+
def dial_in_thread
|
601
|
+
Thread.new do
|
602
|
+
status = subject.dial [to, second_to], options
|
603
|
+
latch.countdown!
|
604
|
+
status
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
context "when one answers" do
|
609
|
+
it "should only execute the confirmation controller on the first call to answer, immediately hanging up all others" do
|
610
|
+
other_mock_call['confirm'] = true
|
611
|
+
call.should_receive(:answer).once
|
612
|
+
|
613
|
+
other_mock_call.should_receive(:dial).once.with(to, from: nil)
|
614
|
+
other_mock_call.should_receive(:join).once.with(call)
|
615
|
+
other_mock_call.should_receive(:hangup).once
|
616
|
+
|
617
|
+
second_other_mock_call.should_receive(:dial).once.with(second_to, from: nil)
|
618
|
+
second_other_mock_call.should_receive(:join).never
|
619
|
+
second_other_mock_call.should_receive(:execute_controller).never
|
620
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
621
|
+
second_other_mock_call << mock_end(:foo)
|
622
|
+
end
|
623
|
+
|
624
|
+
t = dial_in_thread
|
625
|
+
|
626
|
+
latch.wait(1).should be_false
|
627
|
+
|
628
|
+
other_mock_call << mock_answered
|
629
|
+
confirmation_latch.wait(1).should be_true
|
630
|
+
|
631
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
632
|
+
other_mock_call << mock_end
|
633
|
+
|
634
|
+
latch.wait(2).should be_true
|
635
|
+
|
636
|
+
t.join
|
637
|
+
status = t.value
|
638
|
+
status.should be_a Dial::DialStatus
|
639
|
+
status.should have(2).calls
|
640
|
+
status.calls.each { |c| c.should be_a OutboundCall }
|
641
|
+
status.result.should be == :answer
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
describe "#dial_and_confirm" do
|
649
|
+
it "should dial the call to the correct endpoint and return a dial status object" do
|
650
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
651
|
+
other_mock_call.should_receive(:dial).with(to, :from => 'foo').once
|
652
|
+
dial_thread = Thread.new do
|
653
|
+
status = subject.dial_and_confirm(to, :from => 'foo')
|
654
|
+
|
655
|
+
status.should be_a Dial::DialStatus
|
656
|
+
joined_status = status.joins[status.calls.first]
|
657
|
+
joined_status.duration.should == 0.0
|
658
|
+
joined_status.result.should == :no_answer
|
659
|
+
end
|
660
|
+
sleep 0.1
|
661
|
+
other_mock_call << mock_end
|
662
|
+
dial_thread.join.should be_true
|
663
|
+
end
|
664
|
+
|
665
|
+
it "should default the caller ID to that of the original call" do
|
666
|
+
call.stub :from => 'sip:foo@bar.com'
|
667
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
668
|
+
other_mock_call.should_receive(:dial).with(to, :from => 'sip:foo@bar.com').once
|
669
|
+
dial_thread = Thread.new do
|
670
|
+
subject.dial_and_confirm to
|
671
|
+
end
|
672
|
+
sleep 0.1
|
673
|
+
other_mock_call << mock_end
|
674
|
+
dial_thread.join.should be_true
|
675
|
+
end
|
676
|
+
|
677
|
+
let(:options) { { :foo => :bar } }
|
678
|
+
|
679
|
+
def dial_in_thread
|
680
|
+
Thread.new do
|
681
|
+
status = subject.dial_and_confirm to, options
|
682
|
+
latch.countdown!
|
683
|
+
status
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
describe "without a block" do
|
688
|
+
before do
|
689
|
+
other_mock_call.should_receive(:dial).once.with(to, options)
|
690
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
691
|
+
end
|
692
|
+
|
693
|
+
it "blocks the original controller until the new call ends" do
|
694
|
+
dial_in_thread
|
695
|
+
|
696
|
+
latch.wait(1).should be_false
|
697
|
+
|
698
|
+
other_mock_call << mock_end
|
699
|
+
|
700
|
+
latch.wait(1).should be_true
|
701
|
+
end
|
702
|
+
|
703
|
+
it "unblocks the original controller if the original call ends" do
|
704
|
+
other_mock_call.should_receive(:hangup).once
|
705
|
+
dial_in_thread
|
706
|
+
|
707
|
+
latch.wait(1).should be_false
|
708
|
+
|
709
|
+
call << mock_end
|
710
|
+
|
711
|
+
latch.wait(1).should be_true
|
712
|
+
end
|
713
|
+
|
714
|
+
it "joins the new call to the existing one on answer" do
|
715
|
+
call.should_receive(:answer).once
|
716
|
+
other_mock_call.should_receive(:join).once.with(call)
|
717
|
+
|
718
|
+
dial_in_thread
|
719
|
+
|
720
|
+
latch.wait(1).should be_false
|
721
|
+
|
722
|
+
other_mock_call << mock_answered
|
723
|
+
other_mock_call << mock_end
|
724
|
+
|
725
|
+
latch.wait(1).should be_true
|
726
|
+
end
|
727
|
+
|
728
|
+
it "hangs up the new call when the dial unblocks" do
|
729
|
+
other_mock_call.should_receive(:hangup).once
|
730
|
+
call.should_receive(:answer).once
|
731
|
+
other_mock_call.should_receive(:join).once.with(call)
|
732
|
+
|
733
|
+
dial_in_thread
|
734
|
+
|
735
|
+
latch.wait(1).should be_false
|
736
|
+
|
737
|
+
other_mock_call << mock_answered
|
738
|
+
call << mock_end
|
739
|
+
|
740
|
+
latch.wait(1).should be_true
|
741
|
+
end
|
742
|
+
|
743
|
+
context "when the call is rejected" do
|
744
|
+
it "has an overall dial status of :no_answer" do
|
745
|
+
t = dial_in_thread
|
746
|
+
|
747
|
+
sleep 0.5
|
748
|
+
|
749
|
+
other_mock_call << mock_end(:reject)
|
750
|
+
|
751
|
+
latch.wait(2).should be_true
|
752
|
+
|
753
|
+
t.join
|
754
|
+
status = t.value
|
755
|
+
status.result.should be == :no_answer
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
context "when the call ends with an error" do
|
760
|
+
it "has an overall dial status of :error" do
|
761
|
+
t = dial_in_thread
|
762
|
+
|
763
|
+
sleep 0.5
|
764
|
+
|
765
|
+
other_mock_call << mock_end(:error)
|
766
|
+
|
767
|
+
latch.wait(2).should be_true
|
768
|
+
|
769
|
+
t.join
|
770
|
+
status = t.value
|
771
|
+
status.result.should be == :error
|
772
|
+
|
773
|
+
joined_status = status.joins[status.calls.first]
|
774
|
+
joined_status.duration.should == 0.0
|
775
|
+
joined_status.result.should == :error
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
context "when the call is answered and joined" do
|
780
|
+
it "has an overall dial status of :answer" do
|
781
|
+
call.should_receive(:answer).once
|
782
|
+
other_mock_call.should_receive(:join).once.with(call)
|
783
|
+
|
784
|
+
t = dial_in_thread
|
785
|
+
|
786
|
+
sleep 0.5
|
787
|
+
|
788
|
+
other_mock_call << mock_answered
|
789
|
+
other_mock_call << mock_end
|
790
|
+
|
791
|
+
latch.wait(1).should be_true
|
792
|
+
|
793
|
+
t.join
|
794
|
+
status = t.value
|
795
|
+
status.result.should be == :answer
|
796
|
+
status.joined_call.should eq(other_mock_call)
|
797
|
+
|
798
|
+
joined_status = status.joins[status.calls.first]
|
799
|
+
joined_status.result.should == :joined
|
800
|
+
end
|
801
|
+
|
802
|
+
it "records the duration of the join" do
|
803
|
+
call.should_receive(:answer).once
|
804
|
+
other_mock_call.should_receive(:join).once.with(call)
|
805
|
+
other_mock_call.stub hangup: true
|
806
|
+
|
807
|
+
t = dial_in_thread
|
808
|
+
|
809
|
+
sleep 0.5
|
810
|
+
|
811
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
812
|
+
Timecop.freeze base_time
|
813
|
+
|
814
|
+
other_mock_call << mock_answered
|
815
|
+
|
816
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
817
|
+
Timecop.freeze base_time
|
818
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
819
|
+
other_mock_call << mock_end
|
820
|
+
|
821
|
+
latch.wait(1).should be_true
|
822
|
+
|
823
|
+
t.join
|
824
|
+
status = t.value
|
825
|
+
status.result.should be == :answer
|
826
|
+
status.joined_call.should eq(other_mock_call)
|
827
|
+
joined_status = status.joins[status.calls.first]
|
828
|
+
joined_status.duration.should == 37.0
|
535
829
|
end
|
536
830
|
end
|
537
831
|
end
|
538
|
-
|
832
|
+
|
833
|
+
describe "when the caller has already hung up" do
|
834
|
+
before do
|
835
|
+
call << mock_end
|
836
|
+
end
|
837
|
+
|
838
|
+
it "should raise Call::Hangup" do
|
839
|
+
expect { subject.dial_and_confirm to, options }.to raise_error(Call::Hangup)
|
840
|
+
end
|
841
|
+
|
842
|
+
it "should not make any outbound calls" do
|
843
|
+
OutboundCall.should_receive(:new).never
|
844
|
+
expect { subject.dial_and_confirm to, options }.to raise_error
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
describe "with multiple third parties specified" do
|
849
|
+
let(:options) { {} }
|
850
|
+
let(:other_options) { options }
|
851
|
+
let(:second_other_options) { options }
|
852
|
+
|
853
|
+
before do
|
854
|
+
OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call
|
855
|
+
|
856
|
+
other_mock_call.should_receive(:dial).once.with(to, other_options)
|
857
|
+
|
858
|
+
second_other_mock_call.should_receive(:dial).once.with(second_to, second_other_options)
|
859
|
+
second_other_mock_call.should_receive(:join).never
|
860
|
+
end
|
861
|
+
|
862
|
+
def dial_in_thread
|
863
|
+
Thread.new do
|
864
|
+
status = subject.dial_and_confirm [to, second_to], options
|
865
|
+
latch.countdown!
|
866
|
+
status
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
it "dials all parties and joins the first one to answer, hanging up the rest" do
|
871
|
+
call.should_receive(:answer).once
|
872
|
+
other_mock_call.should_receive(:join).once.with(call)
|
873
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
874
|
+
second_other_mock_call << mock_end
|
875
|
+
end
|
876
|
+
|
877
|
+
t = dial_in_thread
|
878
|
+
|
879
|
+
latch.wait(1).should be_false
|
880
|
+
|
881
|
+
other_mock_call << mock_answered
|
882
|
+
other_mock_call << mock_end
|
883
|
+
|
884
|
+
latch.wait(2).should be_true
|
885
|
+
|
886
|
+
t.join
|
887
|
+
status = t.value
|
888
|
+
status.should be_a Dial::DialStatus
|
889
|
+
status.should have(2).calls
|
890
|
+
status.calls.each { |c| c.should be_a OutboundCall }
|
891
|
+
end
|
892
|
+
|
893
|
+
it "unblocks when the joined call unjoins, allowing it to proceed further" do
|
894
|
+
call.should_receive(:answer).once
|
895
|
+
other_mock_call.should_receive(:join).once.with(call)
|
896
|
+
other_mock_call.should_receive(:hangup).once
|
897
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
898
|
+
second_other_mock_call << mock_end
|
899
|
+
end
|
900
|
+
|
901
|
+
t = dial_in_thread
|
902
|
+
|
903
|
+
latch.wait(1).should be_false
|
904
|
+
|
905
|
+
other_mock_call << mock_answered
|
906
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
907
|
+
|
908
|
+
latch.wait(2).should be_true
|
909
|
+
|
910
|
+
t.join
|
911
|
+
status = t.value
|
912
|
+
status.should be_a Dial::DialStatus
|
913
|
+
status.should have(2).calls
|
914
|
+
status.calls.each { |c| c.should be_a OutboundCall }
|
915
|
+
end
|
916
|
+
|
917
|
+
describe "with options overrides" do
|
918
|
+
let(:options) do
|
919
|
+
{
|
920
|
+
:from => 'foo',
|
921
|
+
:timeout => 3000,
|
922
|
+
:headers => {
|
923
|
+
:x_foo => 'bar'
|
924
|
+
}
|
925
|
+
}
|
926
|
+
end
|
927
|
+
|
928
|
+
let(:dial_other_options) do
|
929
|
+
{
|
930
|
+
:foo => 'bar',
|
931
|
+
:headers => {
|
932
|
+
:x_foo => 'buzz'
|
933
|
+
}
|
934
|
+
}
|
935
|
+
end
|
936
|
+
|
937
|
+
let(:other_options) do
|
938
|
+
{
|
939
|
+
:from => 'foo',
|
940
|
+
:timeout => 3000,
|
941
|
+
:foo => 'bar',
|
942
|
+
:headers => {
|
943
|
+
:x_foo => 'buzz'
|
944
|
+
}
|
945
|
+
|
946
|
+
}
|
947
|
+
end
|
948
|
+
|
949
|
+
let(:dial_second_other_options) do
|
950
|
+
{
|
951
|
+
:timeout => 5000,
|
952
|
+
:headers => {
|
953
|
+
:x_bar => 'barbuzz'
|
954
|
+
}
|
955
|
+
}
|
956
|
+
end
|
957
|
+
|
958
|
+
let(:second_other_options) do
|
959
|
+
{
|
960
|
+
:from => 'foo',
|
961
|
+
:timeout => 5000,
|
962
|
+
:headers => {
|
963
|
+
:x_foo => 'bar',
|
964
|
+
:x_bar => 'barbuzz'
|
965
|
+
}
|
966
|
+
}
|
967
|
+
end
|
968
|
+
|
969
|
+
it "with multiple destinations as an hash, with overrides for each, and an options hash, it dials each call with specified options" do
|
970
|
+
t = Thread.new do
|
971
|
+
subject.dial_and_confirm({
|
972
|
+
to => dial_other_options,
|
973
|
+
second_to => dial_second_other_options
|
974
|
+
}, options)
|
975
|
+
latch.countdown!
|
976
|
+
end
|
977
|
+
|
978
|
+
latch.wait(1).should be_false
|
979
|
+
other_mock_call << mock_end
|
980
|
+
latch.wait(1).should be_false
|
981
|
+
second_other_mock_call << mock_end
|
982
|
+
latch.wait(2).should be_true
|
983
|
+
t.join
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
context "when all calls are rejected" do
|
988
|
+
it "has an overall dial status of :no_answer" do
|
989
|
+
t = dial_in_thread
|
990
|
+
|
991
|
+
sleep 0.5
|
992
|
+
|
993
|
+
other_mock_call << mock_end(:reject)
|
994
|
+
second_other_mock_call << mock_end(:reject)
|
995
|
+
|
996
|
+
latch.wait(2).should be_true
|
997
|
+
|
998
|
+
t.join
|
999
|
+
status = t.value
|
1000
|
+
status.result.should be == :no_answer
|
1001
|
+
end
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
context "when a call is answered and joined, and the other ends with an error" do
|
1005
|
+
it "has an overall dial status of :answer" do
|
1006
|
+
call.should_receive(:answer).once
|
1007
|
+
other_mock_call.should_receive(:join).once.with(call)
|
1008
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
1009
|
+
second_other_mock_call << mock_end(:error)
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
t = dial_in_thread
|
1013
|
+
|
1014
|
+
sleep 0.5
|
1015
|
+
|
1016
|
+
other_mock_call << mock_answered
|
1017
|
+
other_mock_call << mock_end
|
1018
|
+
|
1019
|
+
latch.wait(1).should be_true
|
1020
|
+
|
1021
|
+
t.join
|
1022
|
+
status = t.value
|
1023
|
+
status.result.should be == :answer
|
1024
|
+
end
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
describe "with a timeout specified" do
|
1029
|
+
let(:timeout) { 3 }
|
1030
|
+
|
1031
|
+
it "should abort the dial after the specified timeout" do
|
1032
|
+
other_mock_call.should_receive(:dial).once
|
1033
|
+
other_mock_call.should_receive(:hangup).once
|
1034
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
1035
|
+
|
1036
|
+
time = Time.now
|
1037
|
+
|
1038
|
+
t = Thread.new do
|
1039
|
+
status = subject.dial_and_confirm to, :timeout => timeout
|
1040
|
+
latch.countdown!
|
1041
|
+
status
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
latch.wait
|
1045
|
+
time = Time.now - time
|
1046
|
+
time.round.should be == timeout
|
1047
|
+
t.join
|
1048
|
+
status = t.value
|
1049
|
+
status.result.should be == :timeout
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
describe "if someone answers before the timeout elapses" do
|
1053
|
+
it "should not abort until the far end hangs up" do
|
1054
|
+
other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout))
|
1055
|
+
call.should_receive(:answer).once
|
1056
|
+
other_mock_call.should_receive(:join).once.with(call)
|
1057
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
1058
|
+
|
1059
|
+
time = Time.now
|
1060
|
+
|
1061
|
+
t = Thread.new do
|
1062
|
+
status = subject.dial_and_confirm to, :timeout => timeout
|
1063
|
+
latch.countdown!
|
1064
|
+
status
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
latch.wait(2).should be_false
|
1068
|
+
|
1069
|
+
other_mock_call << mock_answered
|
1070
|
+
|
1071
|
+
latch.wait(2).should be_false
|
1072
|
+
|
1073
|
+
other_mock_call << mock_end
|
1074
|
+
|
1075
|
+
latch.wait(0.1).should be_true
|
1076
|
+
time = Time.now - time
|
1077
|
+
time.to_i.should be > timeout
|
1078
|
+
t.join
|
1079
|
+
status = t.value
|
1080
|
+
status.result.should be == :answer
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
describe "with a confirmation controller" do
|
1086
|
+
let(:confirmation_controller) do
|
1087
|
+
latch = confirmation_latch
|
1088
|
+
Class.new(Adhearsion::CallController) do
|
1089
|
+
@@confirmation_latch = latch
|
1090
|
+
|
1091
|
+
def run
|
1092
|
+
# Copy metadata onto call variables so we can assert it later. Ugly hack
|
1093
|
+
metadata.each_pair do |key, value|
|
1094
|
+
call[key] = value
|
1095
|
+
end
|
1096
|
+
@@confirmation_latch.countdown!
|
1097
|
+
if delay = call['confirmation_delay']
|
1098
|
+
sleep delay
|
1099
|
+
end
|
1100
|
+
call['confirm'] || hangup
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
let(:confirmation_latch) { CountDownLatch.new 1 }
|
1106
|
+
|
1107
|
+
let(:options) { {:confirm => confirmation_controller} }
|
1108
|
+
|
1109
|
+
context "with confirmation controller metadata specified" do
|
1110
|
+
let(:options) { {:confirm => confirmation_controller, :confirm_metadata => {:foo => 'bar'}} }
|
1111
|
+
|
1112
|
+
before do
|
1113
|
+
other_mock_call.should_receive(:dial).once
|
1114
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
it "should set the metadata on the controller" do
|
1118
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
1119
|
+
other_mock_call << mock_end
|
1120
|
+
end
|
1121
|
+
other_mock_call['confirm'] = false
|
1122
|
+
|
1123
|
+
dial_in_thread
|
1124
|
+
|
1125
|
+
latch.wait(0.1).should be_false
|
1126
|
+
|
1127
|
+
other_mock_call << mock_answered
|
1128
|
+
|
1129
|
+
confirmation_latch.wait(1).should be_true
|
1130
|
+
latch.wait(2).should be_true
|
1131
|
+
|
1132
|
+
other_mock_call[:foo].should == 'bar'
|
1133
|
+
end
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
context "when an outbound call is answered" do
|
1137
|
+
before do
|
1138
|
+
other_mock_call.should_receive(:dial).once
|
1139
|
+
OutboundCall.should_receive(:new).and_return other_mock_call
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
it "should execute the specified confirmation controller" do
|
1143
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
1144
|
+
other_mock_call << mock_end
|
1145
|
+
end
|
1146
|
+
other_mock_call['confirm'] = false
|
1147
|
+
|
1148
|
+
dial_in_thread
|
1149
|
+
|
1150
|
+
latch.wait(0.1).should be_false
|
1151
|
+
|
1152
|
+
other_mock_call << mock_answered
|
1153
|
+
|
1154
|
+
confirmation_latch.wait(1).should be_true
|
1155
|
+
latch.wait(2).should be_true
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
it "should join the calls if the call is still active after execution of the call controller" do
|
1159
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
1160
|
+
other_mock_call << mock_end
|
1161
|
+
end
|
1162
|
+
other_mock_call['confirm'] = true
|
1163
|
+
call.should_receive(:answer).once
|
1164
|
+
other_mock_call.should_receive(:join).once.with(call)
|
1165
|
+
|
1166
|
+
t = dial_in_thread
|
1167
|
+
|
1168
|
+
latch.wait(1).should be_false
|
1169
|
+
|
1170
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
1171
|
+
Timecop.freeze base_time
|
1172
|
+
|
1173
|
+
other_mock_call << mock_answered
|
1174
|
+
|
1175
|
+
base_time = Time.local(2008, 9, 1, 12, 0, 42)
|
1176
|
+
Timecop.freeze base_time
|
1177
|
+
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1178
|
+
|
1179
|
+
latch.wait(1).should be_true
|
1180
|
+
|
1181
|
+
t.join
|
1182
|
+
status = t.value
|
1183
|
+
status.result.should be == :answer
|
1184
|
+
|
1185
|
+
joined_status = status.joins[status.calls.first]
|
1186
|
+
joined_status.duration.should == 42.0
|
1187
|
+
joined_status.result.should == :joined
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
it "should not join the calls if the call is not active after execution of the call controller" do
|
1191
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
1192
|
+
other_mock_call << mock_end
|
1193
|
+
end
|
1194
|
+
other_mock_call['confirm'] = false
|
1195
|
+
call.should_receive(:answer).never
|
1196
|
+
other_mock_call.should_receive(:join).never.with(call)
|
1197
|
+
|
1198
|
+
t = dial_in_thread
|
1199
|
+
|
1200
|
+
latch.wait(1).should be_false
|
1201
|
+
|
1202
|
+
other_mock_call << mock_answered
|
1203
|
+
|
1204
|
+
latch.wait(1).should be_true
|
1205
|
+
|
1206
|
+
t.join
|
1207
|
+
status = t.value
|
1208
|
+
status.result.should be == :unconfirmed
|
1209
|
+
|
1210
|
+
joined_status = status.joins[status.calls.first]
|
1211
|
+
joined_status.duration.should == 0.0
|
1212
|
+
joined_status.result.should == :unconfirmed
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
context "when multiple calls are made" do
|
1217
|
+
let(:confirmation_latch) { CountDownLatch.new 2 }
|
1218
|
+
let(:apology_controller) do
|
1219
|
+
Class.new(Adhearsion::CallController) do
|
1220
|
+
def run
|
1221
|
+
logger.info "Apologising..."
|
1222
|
+
call['apology_metadata'] = metadata
|
1223
|
+
call['apology_done'] = true
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
let(:options) { {confirm: confirmation_controller, confirm_metadata: {'foo' => 'bar'}, apology: apology_controller} }
|
1228
|
+
|
1229
|
+
before do
|
1230
|
+
OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
def dial_in_thread
|
1234
|
+
Thread.new do
|
1235
|
+
status = subject.dial_and_confirm [to, second_to], options
|
1236
|
+
latch.countdown!
|
1237
|
+
status
|
1238
|
+
end
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
context "when two answer" do
|
1242
|
+
it "should execute the confirmation controller on both, joining the first call to confirm" do
|
1243
|
+
other_mock_call['confirm'] = true
|
1244
|
+
other_mock_call['confirmation_delay'] = 1
|
1245
|
+
second_other_mock_call['confirm'] = true
|
1246
|
+
second_other_mock_call['confirmation_delay'] = 1.3
|
1247
|
+
|
1248
|
+
call.should_receive(:answer).once
|
1249
|
+
|
1250
|
+
other_mock_call.should_receive(:dial).once.with(to, from: nil)
|
1251
|
+
other_mock_call.should_receive(:join).once.with(call)
|
1252
|
+
other_mock_call.should_receive(:hangup).once.and_return do
|
1253
|
+
other_mock_call.async.deliver_message mock_end
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
second_other_mock_call.should_receive(:dial).once.with(second_to, from: nil)
|
1257
|
+
second_other_mock_call.should_receive(:join).never
|
1258
|
+
second_other_mock_call.should_receive(:hangup).once.and_return do
|
1259
|
+
second_other_mock_call.async.deliver_message mock_end
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
t = dial_in_thread
|
1263
|
+
|
1264
|
+
latch.wait(1).should be_false
|
1265
|
+
|
1266
|
+
other_mock_call.async.deliver_message mock_answered
|
1267
|
+
second_other_mock_call.async.deliver_message mock_answered
|
1268
|
+
confirmation_latch.wait(1).should be_true
|
1269
|
+
|
1270
|
+
sleep 2
|
1271
|
+
|
1272
|
+
other_mock_call.async.deliver_message Punchblock::Event::Unjoined.new(call_uri: call.id)
|
1273
|
+
|
1274
|
+
latch.wait(2).should be_true
|
1275
|
+
|
1276
|
+
second_other_mock_call['apology_done'].should be_true
|
1277
|
+
second_other_mock_call['apology_metadata'].should == {'foo' => 'bar'}
|
1278
|
+
|
1279
|
+
t.join
|
1280
|
+
status = t.value
|
1281
|
+
status.should be_a Dial::DialStatus
|
1282
|
+
status.should have(2).calls
|
1283
|
+
status.calls.each { |c| c.should be_a OutboundCall }
|
1284
|
+
status.result.should be == :answer
|
1285
|
+
status.joins[other_mock_call].result.should == :joined
|
1286
|
+
status.joins[second_other_mock_call].result.should == :lost_confirmation
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
end
|
1291
|
+
end
|
539
1292
|
end
|
540
1293
|
end
|
541
1294
|
end
|