spec/adhearsion/call_spec.rb in adhearsion-2.3.5 vs spec/adhearsion/call_spec.rb in adhearsion-2.4.0.beta1

- old
+ new

@@ -8,21 +8,25 @@ end end module Adhearsion describe Call do - let(:mock_client) { mock('Client').as_null_object } + let(:mock_client) { double('Client').as_null_object } - let(:call_id) { rand } + let(:call_id) { rand.to_s } + let(:domain) { 'rayo.net' } let(:headers) { nil } let(:to) { 'sip:you@there.com' } let(:from) { 'sip:me@here.com' } + let(:transport) { 'footransport' } let :offer do - Punchblock::Event::Offer.new :target_call_id => call_id, - :to => to, - :from => from, - :headers => headers + Punchblock::Event::Offer.new target_call_id: call_id, + domain: domain, + transport: transport, + to: to, + from: from, + headers: headers end subject { Adhearsion::Call.new offer } before do @@ -39,18 +43,50 @@ it { should be_active } its(:commands) { should be_empty } its(:id) { should be == call_id } + its(:domain) { should be == domain } + its(:uri) { should be == "footransport:#{call_id}@#{domain}" } its(:to) { should be == to } its(:from) { should be == from } + context "when the ID is nil" do + let(:call_id) { nil } + + its(:uri) { should be == nil } + end + + context "when the domain is nil" do + let(:domain) { nil } + + its(:uri) { should be == "footransport:#{call_id}" } + end + + context "when the transport is nil" do + let(:transport) { nil } + + its(:uri) { should be == "#{call_id}@#{domain}" } + end + + it "should mark its start time" do + base_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze base_time + subject.start_time.should == base_time + end + + describe "#commands" do + it "should use a duplicating accessor for the command registry" do + subject.commands.should_not be subject.commands + end + end + describe "its variables" do context "with an offer" do context "with headers" do - let(:headers) { {:x_foo => 'bar'} } - its(:variables) { should be == headers } + let(:headers) { {'X-foo' => 'bar'} } + its(:variables) { should be == {'x_foo' => 'bar'} } it "should be made available via []" do subject[:x_foo].should be == 'bar' end @@ -58,24 +94,33 @@ subject[:x_foo] = 'baz' subject[:x_foo].should be == 'baz' end context "when receiving an event with headers" do - let(:event) { Punchblock::Event::End.new :headers => {:x_bar => 'foo'} } + let(:event) { Punchblock::Event::End.new :headers => {'X-bar' => 'foo'} } it "should merge later headers" do subject << event - subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'} + subject.variables.should be == {'x_foo' => 'bar', 'x_bar' => 'foo'} end + + context "with have symbol names" do + let(:event) { Punchblock::Event::End.new :headers => {:x_bar => 'foo'} } + + it "should merge later headers" do + subject << event + subject.variables.should be == {'x_foo' => 'bar', 'x_bar' => 'foo'} + end + end end context "when sending a command with headers" do - let(:command) { Punchblock::Command::Accept.new :headers => {:x_bar => 'foo'} } + let(:command) { Punchblock::Command::Accept.new :headers => {'X-bar' => 'foo'} } it "should merge later headers" do subject.write_command command - subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'} + subject.variables.should be == {'x_foo' => 'bar', 'x_bar' => 'foo'} end end end context "without headers" do @@ -95,33 +140,48 @@ lambda { Adhearsion::Call.new }.should_not raise_error end end it 'allows the registration of event handlers which are called when messages are delivered' do - event = mock 'Event' + event = double 'Event' event.should_receive(:foo?).and_return true - response = mock 'Response' + response = double 'Response' response.should_receive(:call).once subject.register_event_handler(:foo?) { response.call } subject << event end describe "event handlers" do - let(:response) { mock 'Response' } + before { pending } + let(:response) { double 'Response' } describe "for joined events" do context "joined to another call" do let :event do - Punchblock::Event::Joined.new :call_id => 'foobar' + Punchblock::Event::Joined.new call_uri: 'xmpp:foobar@rayo.net' end it "should trigger any on_joined callbacks set for the matching call ID" do response.should_receive(:call).once.with(event) - subject.on_joined(:call_id => 'foobar') { |event| response.call event } + subject.on_joined(:call_uri => 'xmpp:foobar@rayo.net') { |event| response.call event } subject << event end + it "should trigger any on_joined callbacks set for the matching call ID as a string" do + response.should_receive(:call).once.with(event) + subject.on_joined('foobar') { |event| response.call event } + subject << event + end + + it "should trigger any on_joined callbacks set for the matching call" do + response.should_receive(:call).once.with(event) + call = Call.new + call.wrapped_object.stub id: 'foobar', domain: 'rayo.net' + subject.on_joined(call) { |event| response.call event } + subject << event + end + it "should not trigger on_joined callbacks for other call IDs" do response.should_receive(:call).never subject.on_joined(:call_id => 'barfoo') { |event| response.call event } subject << event end @@ -153,22 +213,36 @@ it "should not trigger any on_joined callbacks set for calls" do response.should_receive(:call).never subject.on_joined(:call_id => 'foobar') { |event| response.call event } subject << event end + + it "should not trigger any on_joined callbacks set for the matching call ID as a string" do + response.should_receive(:call).never + subject.on_joined('foobar') { |event| response.call event } + subject << event + end + + it "should not trigger any on_joined callbacks set for the matching call" do + response.should_receive(:call).never + call = Call.new + call.stub :id => 'foobar' + subject.on_joined(call) { |event| response.call event } + subject << event + end end end describe "for unjoined events" do context "unjoined from another call" do let :event do - Punchblock::Event::Unjoined.new :call_id => 'foobar' + Punchblock::Event::Unjoined.new call_uri: 'xmpp:foobar@rayo.net' end it "should trigger any on_unjoined callbacks set for the matching call ID" do response.should_receive(:call).once.with(event) - subject.on_unjoined(:call_id => 'foobar') { |event| response.call event } + subject.on_unjoined(:call_uri => 'xmpp:foobar@rayo.net') { |event| response.call event } subject << event end it "should trigger any on_unjoined callbacks set for the matching call ID as a string" do response.should_receive(:call).once.with(event) @@ -177,11 +251,11 @@ end it "should trigger any on_unjoined callbacks set for the matching call" do response.should_receive(:call).once.with(event) call = Call.new - call.stub :id => 'foobar' + call.wrapped_object.stub id: 'foobar', domain: 'rayo.net' subject.on_unjoined(call) { |event| response.call event } subject << event end it "should not trigger on_unjoined callbacks for other call IDs" do @@ -276,15 +350,15 @@ let(:other_call) { Call.new } before { other_call.stub :id => other_call_id } let :joined_event do - Punchblock::Event::Joined.new :call_id => other_call_id + Punchblock::Event::Joined.new call_uri: other_call_id end let :unjoined_event do - Punchblock::Event::Unjoined.new :call_id => other_call_id + Punchblock::Event::Unjoined.new call_uri: other_call_id end context "when we know about the joined call" do before { Adhearsion.active_calls << other_call } @@ -330,13 +404,45 @@ it "should set the end reason" do subject << end_event subject.end_reason.should be == :hangup end + it "should set the end time" do + finish_time = Time.local(2008, 9, 1, 12, 1, 3) + Timecop.freeze finish_time + subject.end_time.should == nil + subject << end_event + subject.end_time.should == finish_time + end + + it "should set the call duration" do + start_time = Time.local(2008, 9, 1, 12, 0, 0) + Timecop.freeze start_time + subject + + mid_point_time = Time.local(2008, 9, 1, 12, 0, 37) + Timecop.freeze mid_point_time + + subject.duration.should == 37.0 + + finish_time = Time.local(2008, 9, 1, 12, 1, 3) + Timecop.freeze finish_time + + subject << end_event + + future_time = Time.local(2008, 9, 1, 12, 2, 3) + Timecop.freeze finish_time + + subject.duration.should == 63.0 + end + it "should instruct the command registry to terminate" do - subject.commands.should_receive(:terminate).once + command = Punchblock::Command::Answer.new + command.request! + subject.future.write_and_await_response command subject << end_event + command.response(1).should be_a Call::Hangup end it "removes itself from the active calls" do size_before = Adhearsion.active_calls.size @@ -355,10 +461,37 @@ lambda { subject.id }.should raise_error Call::ExpiredError, /expired and is no longer accessible/ end end end + describe "#wait_for_end" do + let :end_event do + Punchblock::Event::End.new reason: :hangup + end + + context "when the call has already ended" do + before { subject << end_event } + + it "should return the end reason" do + subject.wait_for_end.should == :hangup + end + end + + context "when the call has not yet ended" do + it "should block until the call ends and return the end reason" do + fut = subject.future.wait_for_end + + sleep 0.5 + fut.should_not be_ready + + subject << end_event + + fut.value.should == :hangup + end + end + end + describe "tagging a call" do it 'with a single Symbol' do lambda { subject.tag :moderator }.should_not raise_error @@ -403,15 +536,15 @@ subject.tagged_with?(:authorized).should be true end end describe "#write_command" do - let(:mock_command) { mock('Command') } + let(:mock_command) { double('Command') } it "should asynchronously write the command to the Punchblock connection" do subject.wrapped_object.should_receive(:client).once.and_return mock_client - mock_client.should_receive(:execute_command).once.with(mock_command, :call_id => subject.id, :async => true).and_return true + mock_client.should_receive(:execute_command).once.with(mock_command, call_id: call_id, domain: domain, async: true).and_return true subject.write_command mock_command end describe "with a hungup call" do before do @@ -444,13 +577,13 @@ it "writes a command to the call" do subject.wrapped_object.should_receive(:write_command).once.with(message) subject.write_and_await_response message end - it "adds the command to the registry" do + it "removes the command from the registry after execution" do subject.write_and_await_response message - subject.commands.should_not be_empty + subject.commands.should be_empty end it "blocks until a response is received" do slow_command = Punchblock::Command::Dial.new slow_command.request! @@ -461,10 +594,30 @@ starting_time = Time.now subject.write_and_await_response slow_command (Time.now - starting_time).should >= 0.5 end + context "while waiting for a response" do + let(:slow_command) { Punchblock::Command::Dial.new } + + before { slow_command.request! } + + it "does not block the whole actor while waiting for a response" do + fut = subject.future.write_and_await_response slow_command + subject.id.should == call_id + slow_command.response = response + fut.value + end + + it "adds the command to the registry" do + subject.future.write_and_await_response slow_command + sleep 0.2 + subject.commands.should_not be_empty + subject.commands.first.should be slow_command + end + end + describe "with a successful response" do it "returns the executed command" do subject.write_and_await_response(message).should be message end end @@ -570,21 +723,21 @@ end describe "with no headers" do it 'should send a Reject message' do expect_message_waiting_for_response do |c| - c.is_a?(Punchblock::Command::Reject) && c.headers_hash == {} + c.is_a?(Punchblock::Command::Reject) && c.headers == {} end subject.reject end end describe "with headers set" do it 'should send a Hangup message with the correct headers' do headers = {:foo => 'bar'} expect_message_waiting_for_response do |c| - c.is_a?(Punchblock::Command::Reject) && c.headers_hash == headers + c.is_a?(Punchblock::Command::Reject) && c.headers == headers end subject.reject nil, headers end end @@ -638,55 +791,57 @@ end end context "with a call" do let(:call_id) { rand.to_s } + let(:domain) { 'rayo.net' } + let(:uri) { "footransport:#{call_id}@#{domain}" } let(:target) { described_class.new } - before { target.stub id: call_id } + before { target.wrapped_object.stub uri: uri } it "should send a join command joining to the provided call ID" do - expect_join_with_options :call_id => call_id + expect_join_with_options call_uri: uri subject.join target end context "and direction/media options" do it "should send a join command with the correct options" do - expect_join_with_options :call_id => call_id, :media => :bridge, :direction => :recv + expect_join_with_options :call_id => uri, :media => :bridge, :direction => :recv subject.join target, :media => :bridge, :direction => :recv end end end context "with a call ID" do let(:target) { rand.to_s } it "should send a join command joining to the provided call ID" do - expect_join_with_options :call_id => target + expect_join_with_options call_uri: "footransport:#{target}@#{subject.domain}" subject.join target end context "and direction/media options" do it "should send a join command with the correct options" do - expect_join_with_options :call_id => target, :media => :bridge, :direction => :recv + expect_join_with_options :call_uri => "footransport:#{target}@#{subject.domain}", :media => :bridge, :direction => :recv subject.join target, :media => :bridge, :direction => :recv end end end - context "with a call ID as a hash key" do + context "with a call URI as a hash key" do let(:call_id) { rand.to_s } - let(:target) { { :call_id => call_id } } + let(:target) { { :call_uri => call_id } } it "should send a join command joining to the provided call ID" do - expect_join_with_options :call_id => call_id + expect_join_with_options :call_uri => call_id subject.join target end context "and direction/media options" do it "should send a join command with the correct options" do - expect_join_with_options :call_id => call_id, :media => :bridge, :direction => :recv + expect_join_with_options :call_uri => call_id, :media => :bridge, :direction => :recv subject.join target.merge({:media => :bridge, :direction => :recv}) end end end @@ -708,14 +863,14 @@ end context "with a call ID and a mixer name as hash keys" do let(:call_id) { rand.to_s } let(:mixer_name) { rand.to_s } - let(:target) { { :call_id => call_id, :mixer_name => mixer_name } } + let(:target) { { :call_uri => call_id, :mixer_name => mixer_name } } it "should raise an ArgumentError" do - lambda { subject.join target }.should raise_error ArgumentError, /call ID and mixer name/ + lambda { subject.join target }.should raise_error ArgumentError, /call URI and mixer name/ end end end describe "#unjoin" do @@ -725,35 +880,37 @@ end end context "with a call" do let(:call_id) { rand.to_s } + let(:domain) { 'rayo.net' } + let(:uri) { "footransport:#{call_id}@#{domain}" } let(:target) { described_class.new } - before { target.stub id: call_id } + before { target.wrapped_object.stub uri: uri } it "should send an unjoin command unjoining from the provided call ID" do - expect_unjoin_with_options :call_id => call_id + expect_unjoin_with_options call_uri: "footransport:#{call_id}@#{domain}" subject.unjoin target end end context "with a call ID" do let(:target) { rand.to_s } it "should send an unjoin command unjoining from the provided call ID" do - expect_unjoin_with_options :call_id => target + expect_unjoin_with_options call_uri: "footransport:#{target}@#{subject.domain}" subject.unjoin target end end - context "with a call ID as a hash key" do + context "with a call URI as a hash key" do let(:call_id) { rand.to_s } - let(:target) { { :call_id => call_id } } + let(:target) { { call_uri: call_id } } it "should send an unjoin command unjoining from the provided call ID" do - expect_unjoin_with_options :call_id => call_id + expect_unjoin_with_options call_uri: call_id subject.unjoin target end end context "with a mixer name as a hash key" do @@ -764,17 +921,17 @@ expect_unjoin_with_options :mixer_name => mixer_name subject.unjoin target end end - context "with a call ID and a mixer name as hash keys" do + context "with a call URI and a mixer name as hash keys" do let(:call_id) { rand.to_s } let(:mixer_name) { rand.to_s } - let(:target) { { :call_id => call_id, :mixer_name => mixer_name } } + let(:target) { { call_uri: call_id, mixer_name: mixer_name } } it "should raise an ArgumentError" do - lambda { subject.unjoin target }.should raise_error ArgumentError, /call ID and mixer name/ + lambda { subject.unjoin target }.should raise_error ArgumentError, /call URI and mixer name/ end end end describe "#mute" do @@ -844,12 +1001,12 @@ subject.controllers.should include :foo end end context "with two controllers registered" do - let(:controller1) { mock 'CallController1' } - let(:controller2) { mock 'CallController2' } + let(:controller1) { double 'CallController1' } + let(:controller2) { double 'CallController2' } before { subject.controllers << controller1 << controller2 } describe "#pause_controllers" do it "should pause each of the registered controllers" do @@ -865,9 +1022,17 @@ controller1.should_receive(:resume!).once controller2.should_receive(:resume!).once subject.resume_controllers end + end + end + + describe "after termination" do + it "should delete its logger" do + logger = subject.logger + subject.terminate + ::Logging::Repository.instance[logger.name].should be_nil end end end describe Call::CommandRegistry do