spec/adhearsion/call_controller/dial_spec.rb in adhearsion-2.3.5 vs spec/adhearsion/call_controller/dial_spec.rb in adhearsion-2.4.0.beta1

- old
+ new

@@ -18,24 +18,29 @@ let(:mock_answered) { Punchblock::Event::Answered.new } let(:latch) { CountDownLatch.new 1 } before do - other_mock_call.stub id: other_call_id, write_command: true - second_other_mock_call.stub id: second_other_call_id, write_command: true + other_mock_call.wrapped_object.stub id: other_call_id, write_command: true + second_other_mock_call.wrapped_object.stub id: second_other_call_id, write_command: true end - def mock_end(reason = :hangup) + def mock_end(reason = :hangup_command) Punchblock::Event::End.new.tap { |event| event.stub reason: reason } end describe "#dial" do it "should dial the call to the correct endpoint and return a dial status object" do OutboundCall.should_receive(:new).and_return other_mock_call other_mock_call.should_receive(:dial).with(to, :from => 'foo').once dial_thread = Thread.new do - subject.dial(to, :from => 'foo').should be_a Dial::DialStatus + status = subject.dial(to, :from => 'foo') + + status.should be_a Dial::DialStatus + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :no_answer end sleep 0.1 other_mock_call << mock_end dial_thread.join.should be_true end @@ -63,11 +68,10 @@ end describe "without a block" do before do other_mock_call.should_receive(:dial).once.with(to, options) - other_mock_call.should_receive(:hangup).once OutboundCall.should_receive(:new).and_return other_mock_call end it "blocks the original controller until the new call ends" do dial_in_thread @@ -78,10 +82,11 @@ latch.wait(1).should be_true end it "unblocks the original controller if the original call ends" do + other_mock_call.should_receive(:hangup).once dial_in_thread latch.wait(1).should be_false call << mock_end @@ -104,10 +109,11 @@ end it "hangs up the new call when the dial unblocks" do call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) + other_mock_call.should_receive(:hangup).once dial_in_thread latch.wait(1).should be_false @@ -144,10 +150,14 @@ latch.wait(2).should be_true t.join status = t.value status.result.should be == :error + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :error end end context "when the call is answered and joined" do it "has an overall dial status of :answer" do @@ -164,11 +174,44 @@ latch.wait(1).should be_true t.join status = t.value status.result.should be == :answer + status.joined_call.should eq(other_mock_call) + + joined_status = status.joins[status.calls.first] + joined_status.result.should == :joined end + + it "records the duration of the join" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + other_mock_call.stub hangup: true + + t = dial_in_thread + + sleep 0.5 + + base_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze base_time + + other_mock_call << mock_answered + + base_time = Time.local(2008, 9, 1, 12, 0, 37) + Timecop.freeze base_time + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + other_mock_call << mock_end + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :answer + status.joined_call.should eq(other_mock_call) + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 37.0 + end end end describe "when the caller has already hung up" do before do @@ -192,15 +235,13 @@ before do OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call other_mock_call.should_receive(:dial).once.with(to, other_options) - other_mock_call.should_receive(:hangup).once second_other_mock_call.should_receive(:dial).once.with(second_to, second_other_options) second_other_mock_call.should_receive(:join).never - second_other_mock_call.should_receive(:hangup).once end def dial_in_thread Thread.new do status = subject.dial [to, second_to], options @@ -210,23 +251,21 @@ end it "dials all parties and joins the first one to answer, hanging up the rest" do call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) - second_other_mock_call.should_receive(:hangup).once + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end + end t = dial_in_thread latch.wait(1).should be_false other_mock_call << mock_answered other_mock_call << mock_end - latch.wait(1).should be_false - - second_other_mock_call << mock_end - latch.wait(2).should be_true t.join status = t.value status.should be_a Dial::DialStatus @@ -235,23 +274,22 @@ end it "unblocks when the joined call unjoins, allowing it to proceed further" do call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) - second_other_mock_call.should_receive(:hangup).once + other_mock_call.should_receive(:hangup).once + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end + end t = dial_in_thread latch.wait(1).should be_false other_mock_call << mock_answered - other_mock_call << Punchblock::Event::Unjoined.new(:call_id => call.id) + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) - latch.wait(1).should be_false - - second_other_mock_call << mock_end - latch.wait(2).should be_true t.join status = t.value status.should be_a Dial::DialStatus @@ -348,21 +386,21 @@ context "when a call is answered and joined, and the other ends with an error" do it "has an overall dial status of :answer" do call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) - second_other_mock_call.should_receive(:hangup).once + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end(:error) + end t = dial_in_thread sleep 0.5 other_mock_call << mock_answered other_mock_call << mock_end - second_other_mock_call << mock_end(:error) - latch.wait(1).should be_true t.join status = t.value status.result.should be == :answer @@ -386,22 +424,21 @@ status end latch.wait time = Time.now - time - time.to_i.should be == timeout + time.round.should be == timeout t.join status = t.value status.result.should be == :timeout end describe "if someone answers before the timeout elapses" do it "should not abort until the far end hangs up" do other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout)) call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) - other_mock_call.should_receive(:hangup).once OutboundCall.should_receive(:new).and_return other_mock_call time = Time.now t = Thread.new do @@ -447,20 +484,640 @@ let(:confirmation_latch) { CountDownLatch.new 1 } let(:options) { {:confirm => confirmation_controller} } + context "with confirmation controller metadata specified" do + let(:options) { {:confirm => confirmation_controller, :confirm_metadata => {:foo => 'bar'}} } + + before do + other_mock_call.should_receive(:dial).once + OutboundCall.should_receive(:new).and_return other_mock_call + end + + it "should set the metadata on the controller" do + other_mock_call.should_receive(:hangup).once.and_return do + other_mock_call << mock_end + end + other_mock_call['confirm'] = false + + dial_in_thread + + latch.wait(0.1).should be_false + + other_mock_call << mock_answered + + confirmation_latch.wait(1).should be_true + latch.wait(2).should be_true + + other_mock_call[:foo].should == 'bar' + end + end + + context "when an outbound call is answered" do + before do + other_mock_call.should_receive(:dial).once + OutboundCall.should_receive(:new).and_return other_mock_call + end + + it "should execute the specified confirmation controller" do + other_mock_call.should_receive(:hangup).once.and_return do + other_mock_call << mock_end + end + other_mock_call['confirm'] = false + + dial_in_thread + + latch.wait(0.1).should be_false + + other_mock_call << mock_answered + + confirmation_latch.wait(1).should be_true + latch.wait(2).should be_true + end + + it "should join the calls if the call is still active after execution of the call controller" do + other_mock_call.should_receive(:hangup).once + other_mock_call['confirm'] = true + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + + t = dial_in_thread + + latch.wait(1).should be_false + + base_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze base_time + + other_mock_call << mock_answered + + base_time = Time.local(2008, 9, 1, 12, 0, 42) + Timecop.freeze base_time + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + other_mock_call << mock_end + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :answer + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 42.0 + joined_status.result.should == :joined + end + + it "should not join the calls if the call is not active after execution of the call controller" do + other_mock_call.should_receive(:hangup).once.and_return do + other_mock_call << mock_end + end + other_mock_call['confirm'] = false + call.should_receive(:answer).never + other_mock_call.should_receive(:join).never.with(call) + + t = dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :unconfirmed + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :unconfirmed + end + end + + context "when multiple calls are made" do + before do + OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call + end + + def dial_in_thread + Thread.new do + status = subject.dial [to, second_to], options + latch.countdown! + status + end + end + + context "when one answers" do + it "should only execute the confirmation controller on the first call to answer, immediately hanging up all others" do + other_mock_call['confirm'] = true + call.should_receive(:answer).once + + other_mock_call.should_receive(:dial).once.with(to, from: nil) + other_mock_call.should_receive(:join).once.with(call) + other_mock_call.should_receive(:hangup).once + + second_other_mock_call.should_receive(:dial).once.with(second_to, from: nil) + second_other_mock_call.should_receive(:join).never + second_other_mock_call.should_receive(:execute_controller).never + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end(:foo) + end + + t = dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + confirmation_latch.wait(1).should be_true + + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + other_mock_call << mock_end + + latch.wait(2).should be_true + + t.join + status = t.value + status.should be_a Dial::DialStatus + status.should have(2).calls + status.calls.each { |c| c.should be_a OutboundCall } + status.result.should be == :answer + end + end + end + end + end + + describe "#dial_and_confirm" do + it "should dial the call to the correct endpoint and return a dial status object" do + OutboundCall.should_receive(:new).and_return other_mock_call + other_mock_call.should_receive(:dial).with(to, :from => 'foo').once + dial_thread = Thread.new do + status = subject.dial_and_confirm(to, :from => 'foo') + + status.should be_a Dial::DialStatus + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :no_answer + end + sleep 0.1 + other_mock_call << mock_end + dial_thread.join.should be_true + end + + it "should default the caller ID to that of the original call" do + call.stub :from => 'sip:foo@bar.com' + OutboundCall.should_receive(:new).and_return other_mock_call + other_mock_call.should_receive(:dial).with(to, :from => 'sip:foo@bar.com').once + dial_thread = Thread.new do + subject.dial_and_confirm to + end + sleep 0.1 + other_mock_call << mock_end + dial_thread.join.should be_true + end + + let(:options) { { :foo => :bar } } + + def dial_in_thread + Thread.new do + status = subject.dial_and_confirm to, options + latch.countdown! + status + end + end + + describe "without a block" do before do + other_mock_call.should_receive(:dial).once.with(to, options) + OutboundCall.should_receive(:new).and_return other_mock_call + end + + it "blocks the original controller until the new call ends" do + dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_end + + latch.wait(1).should be_true + end + + it "unblocks the original controller if the original call ends" do + other_mock_call.should_receive(:hangup).once + dial_in_thread + + latch.wait(1).should be_false + + call << mock_end + + latch.wait(1).should be_true + end + + it "joins the new call to the existing one on answer" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + + dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + other_mock_call << mock_end + + latch.wait(1).should be_true + end + + it "hangs up the new call when the dial unblocks" do + other_mock_call.should_receive(:hangup).once + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + + dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + call << mock_end + + latch.wait(1).should be_true + end + + context "when the call is rejected" do + it "has an overall dial status of :no_answer" do + t = dial_in_thread + + sleep 0.5 + + other_mock_call << mock_end(:reject) + + latch.wait(2).should be_true + + t.join + status = t.value + status.result.should be == :no_answer + end + end + + context "when the call ends with an error" do + it "has an overall dial status of :error" do + t = dial_in_thread + + sleep 0.5 + + other_mock_call << mock_end(:error) + + latch.wait(2).should be_true + + t.join + status = t.value + status.result.should be == :error + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :error + end + end + + context "when the call is answered and joined" do + it "has an overall dial status of :answer" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + + t = dial_in_thread + + sleep 0.5 + + other_mock_call << mock_answered + other_mock_call << mock_end + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :answer + status.joined_call.should eq(other_mock_call) + + joined_status = status.joins[status.calls.first] + joined_status.result.should == :joined + end + + it "records the duration of the join" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + other_mock_call.stub hangup: true + + t = dial_in_thread + + sleep 0.5 + + base_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze base_time + + other_mock_call << mock_answered + + base_time = Time.local(2008, 9, 1, 12, 0, 37) + Timecop.freeze base_time + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + other_mock_call << mock_end + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :answer + status.joined_call.should eq(other_mock_call) + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 37.0 + end + end + end + + describe "when the caller has already hung up" do + before do + call << mock_end + end + + it "should raise Call::Hangup" do + expect { subject.dial_and_confirm to, options }.to raise_error(Call::Hangup) + end + + it "should not make any outbound calls" do + OutboundCall.should_receive(:new).never + expect { subject.dial_and_confirm to, options }.to raise_error + end + end + + describe "with multiple third parties specified" do + let(:options) { {} } + let(:other_options) { options } + let(:second_other_options) { options } + + before do + OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call + + other_mock_call.should_receive(:dial).once.with(to, other_options) + + second_other_mock_call.should_receive(:dial).once.with(second_to, second_other_options) + second_other_mock_call.should_receive(:join).never + end + + def dial_in_thread + Thread.new do + status = subject.dial_and_confirm [to, second_to], options + latch.countdown! + status + end + end + + it "dials all parties and joins the first one to answer, hanging up the rest" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end + end + + t = dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + other_mock_call << mock_end + + latch.wait(2).should be_true + + t.join + status = t.value + status.should be_a Dial::DialStatus + status.should have(2).calls + status.calls.each { |c| c.should be_a OutboundCall } + end + + it "unblocks when the joined call unjoins, allowing it to proceed further" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + other_mock_call.should_receive(:hangup).once + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end + end + + t = dial_in_thread + + latch.wait(1).should be_false + + other_mock_call << mock_answered + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + + latch.wait(2).should be_true + + t.join + status = t.value + status.should be_a Dial::DialStatus + status.should have(2).calls + status.calls.each { |c| c.should be_a OutboundCall } + end + + describe "with options overrides" do + let(:options) do + { + :from => 'foo', + :timeout => 3000, + :headers => { + :x_foo => 'bar' + } + } + end + + let(:dial_other_options) do + { + :foo => 'bar', + :headers => { + :x_foo => 'buzz' + } + } + end + + let(:other_options) do + { + :from => 'foo', + :timeout => 3000, + :foo => 'bar', + :headers => { + :x_foo => 'buzz' + } + + } + end + + let(:dial_second_other_options) do + { + :timeout => 5000, + :headers => { + :x_bar => 'barbuzz' + } + } + end + + let(:second_other_options) do + { + :from => 'foo', + :timeout => 5000, + :headers => { + :x_foo => 'bar', + :x_bar => 'barbuzz' + } + } + end + + it "with multiple destinations as an hash, with overrides for each, and an options hash, it dials each call with specified options" do + t = Thread.new do + subject.dial_and_confirm({ + to => dial_other_options, + second_to => dial_second_other_options + }, options) + latch.countdown! + end + + latch.wait(1).should be_false + other_mock_call << mock_end + latch.wait(1).should be_false + second_other_mock_call << mock_end + latch.wait(2).should be_true + t.join + end + end + + context "when all calls are rejected" do + it "has an overall dial status of :no_answer" do + t = dial_in_thread + + sleep 0.5 + + other_mock_call << mock_end(:reject) + second_other_mock_call << mock_end(:reject) + + latch.wait(2).should be_true + + t.join + status = t.value + status.result.should be == :no_answer + end + end + + context "when a call is answered and joined, and the other ends with an error" do + it "has an overall dial status of :answer" do + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call << mock_end(:error) + end + + t = dial_in_thread + + sleep 0.5 + + other_mock_call << mock_answered + other_mock_call << mock_end + + latch.wait(1).should be_true + + t.join + status = t.value + status.result.should be == :answer + end + end + end + + describe "with a timeout specified" do + let(:timeout) { 3 } + + it "should abort the dial after the specified timeout" do other_mock_call.should_receive(:dial).once + other_mock_call.should_receive(:hangup).once OutboundCall.should_receive(:new).and_return other_mock_call + + time = Time.now + + t = Thread.new do + status = subject.dial_and_confirm to, :timeout => timeout + latch.countdown! + status + end + + latch.wait + time = Time.now - time + time.round.should be == timeout + t.join + status = t.value + status.result.should be == :timeout end + describe "if someone answers before the timeout elapses" do + it "should not abort until the far end hangs up" do + other_mock_call.should_receive(:dial).once.with(to, hash_including(:timeout => timeout)) + call.should_receive(:answer).once + other_mock_call.should_receive(:join).once.with(call) + OutboundCall.should_receive(:new).and_return other_mock_call + + time = Time.now + + t = Thread.new do + status = subject.dial_and_confirm to, :timeout => timeout + latch.countdown! + status + end + + latch.wait(2).should be_false + + other_mock_call << mock_answered + + latch.wait(2).should be_false + + other_mock_call << mock_end + + latch.wait(0.1).should be_true + time = Time.now - time + time.to_i.should be > timeout + t.join + status = t.value + status.result.should be == :answer + end + end + end + + describe "with a confirmation controller" do + let(:confirmation_controller) do + latch = confirmation_latch + Class.new(Adhearsion::CallController) do + @@confirmation_latch = latch + + def run + # Copy metadata onto call variables so we can assert it later. Ugly hack + metadata.each_pair do |key, value| + call[key] = value + end + @@confirmation_latch.countdown! + if delay = call['confirmation_delay'] + sleep delay + end + call['confirm'] || hangup + end + end + end + + let(:confirmation_latch) { CountDownLatch.new 1 } + + let(:options) { {:confirm => confirmation_controller} } + context "with confirmation controller metadata specified" do let(:options) { {:confirm => confirmation_controller, :confirm_metadata => {:foo => 'bar'}} } + before do + other_mock_call.should_receive(:dial).once + OutboundCall.should_receive(:new).and_return other_mock_call + end + it "should set the metadata on the controller" do - other_mock_call.should_receive(:hangup).twice.and_return do + other_mock_call.should_receive(:hangup).once.and_return do other_mock_call << mock_end end other_mock_call['confirm'] = false dial_in_thread @@ -475,12 +1132,17 @@ other_mock_call[:foo].should == 'bar' end end context "when an outbound call is answered" do + before do + other_mock_call.should_receive(:dial).once + OutboundCall.should_receive(:new).and_return other_mock_call + end + it "should execute the specified confirmation controller" do - other_mock_call.should_receive(:hangup).twice.and_return do + other_mock_call.should_receive(:hangup).once.and_return do other_mock_call << mock_end end other_mock_call['confirm'] = false dial_in_thread @@ -492,31 +1154,43 @@ confirmation_latch.wait(1).should be_true latch.wait(2).should be_true end it "should join the calls if the call is still active after execution of the call controller" do - other_mock_call.should_receive(:hangup).once + other_mock_call.should_receive(:hangup).once.and_return do + other_mock_call << mock_end + end other_mock_call['confirm'] = true call.should_receive(:answer).once other_mock_call.should_receive(:join).once.with(call) t = dial_in_thread latch.wait(1).should be_false + base_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze base_time + other_mock_call << mock_answered - other_mock_call << mock_end + base_time = Time.local(2008, 9, 1, 12, 0, 42) + Timecop.freeze base_time + other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id) + latch.wait(1).should be_true t.join status = t.value status.result.should be == :answer + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 42.0 + joined_status.result.should == :joined end it "should not join the calls if the call is not active after execution of the call controller" do - other_mock_call.should_receive(:hangup).twice.and_return do + other_mock_call.should_receive(:hangup).once.and_return do other_mock_call << mock_end end other_mock_call['confirm'] = false call.should_receive(:answer).never other_mock_call.should_receive(:join).never.with(call) @@ -530,12 +1204,91 @@ latch.wait(1).should be_true t.join status = t.value status.result.should be == :unconfirmed + + joined_status = status.joins[status.calls.first] + joined_status.duration.should == 0.0 + joined_status.result.should == :unconfirmed end end + + context "when multiple calls are made" do + let(:confirmation_latch) { CountDownLatch.new 2 } + let(:apology_controller) do + Class.new(Adhearsion::CallController) do + def run + logger.info "Apologising..." + call['apology_metadata'] = metadata + call['apology_done'] = true + end + end + end + let(:options) { {confirm: confirmation_controller, confirm_metadata: {'foo' => 'bar'}, apology: apology_controller} } + + before do + OutboundCall.should_receive(:new).and_return other_mock_call, second_other_mock_call + end + + def dial_in_thread + Thread.new do + status = subject.dial_and_confirm [to, second_to], options + latch.countdown! + status + end + end + + context "when two answer" do + it "should execute the confirmation controller on both, joining the first call to confirm" do + other_mock_call['confirm'] = true + other_mock_call['confirmation_delay'] = 1 + second_other_mock_call['confirm'] = true + second_other_mock_call['confirmation_delay'] = 1.3 + + call.should_receive(:answer).once + + other_mock_call.should_receive(:dial).once.with(to, from: nil) + other_mock_call.should_receive(:join).once.with(call) + other_mock_call.should_receive(:hangup).once.and_return do + other_mock_call.async.deliver_message mock_end + end + + second_other_mock_call.should_receive(:dial).once.with(second_to, from: nil) + second_other_mock_call.should_receive(:join).never + second_other_mock_call.should_receive(:hangup).once.and_return do + second_other_mock_call.async.deliver_message mock_end + end + + t = dial_in_thread + + latch.wait(1).should be_false + + other_mock_call.async.deliver_message mock_answered + second_other_mock_call.async.deliver_message mock_answered + confirmation_latch.wait(1).should be_true + + sleep 2 + + other_mock_call.async.deliver_message Punchblock::Event::Unjoined.new(call_uri: call.id) + + latch.wait(2).should be_true + + second_other_mock_call['apology_done'].should be_true + second_other_mock_call['apology_metadata'].should == {'foo' => 'bar'} + + t.join + status = t.value + status.should be_a Dial::DialStatus + status.should have(2).calls + status.calls.each { |c| c.should be_a OutboundCall } + status.result.should be == :answer + status.joins[other_mock_call].result.should == :joined + status.joins[second_other_mock_call].result.should == :lost_confirmation + end + end + end end - end#describe #dial + end end end end