# encoding: utf-8
require 'spec_helper'
module Punchblock
module Connection
describe XMPP do
let(:options) { { :root_domain => 'rayo.net' } }
let(:connection) { XMPP.new({:username => '1@app.rayo.net', :password => 1}.merge(options)) }
let(:mock_event_handler) { double('Event Handler').as_null_object }
before do
connection.event_handler = mock_event_handler
end
subject { connection }
describe "rayo domains" do
before { stub_uuids 'randomcallid' }
context "with no domains specified, and a JID of 1@app.rayo.net" do
let(:options) { { :username => '1@app.rayo.net' } }
describe '#root_domain' do
subject { super().root_domain }
it { should be == 'app.rayo.net' }
end
describe '#new_call_uri' do
it "should return an appropriate random call URI" do
expect(subject.new_call_uri).to eq('xmpp:randomcallid@app.rayo.net')
end
end
end
context "with only a rayo domain set" do
let(:options) { { :rayo_domain => 'rayo.org' } }
describe '#root_domain' do
subject { super().root_domain }
it { should be == 'rayo.org' }
end
describe '#new_call_uri' do
it "should return an appropriate random call URI" do
expect(subject.new_call_uri).to eq('xmpp:randomcallid@rayo.org')
end
end
end
context "with only a root domain set" do
let(:options) { { :root_domain => 'rayo.org' } }
describe '#root_domain' do
subject { super().root_domain }
it { should be == 'rayo.org' }
end
describe '#new_call_uri' do
it "should return an appropriate random call URI" do
expect(subject.new_call_uri).to eq('xmpp:randomcallid@rayo.org')
end
end
end
end
it 'should require a username and password to be passed in the options' do
expect { XMPP.new :password => 1 }.to raise_error ArgumentError
expect { XMPP.new :username => 1 }.to raise_error ArgumentError
end
it 'should properly set the Blather logger' do
old_logger = Punchblock.logger
Punchblock.logger = :foo
XMPP.new :username => '1@call.rayo.net', :password => 1
expect(Blather.logger).to be :foo
Punchblock.logger = old_logger
end
it "looking up original command by command ID" do
pending
offer = Event::Offer.new
offer.call_id = '9f00061'
offer.to = 'sip:whatever@127.0.0.1'
output = <<-MSG
MSG
output = RayoNode.import parse_stanza(output).root
expect(connection).to receive(:write_to_stream).once.and_return true
iq = Blather::Stanza::Iq.new :set, '9f00061@call.rayo.net'
expect(connection).to receive(:create_iq).and_return iq
write_thread = Thread.new do
connection.write offer.call_id, output
end
result = import_stanza <<-MSG
MSG
sleep 0.5 # Block so there's enough time for the write thread to get to the point where it's waiting on an IQ
connection.__send__ :handle_iq_result, result
write_thread.join
expect(output.state_name).to eq(:executing)
expect(connection.original_component_from_id('fgh4590')).to eq(output)
example_complete = import_stanza <<-MSG
MSG
connection.__send__ :handle_presence, example_complete
expect(output.complete_event(0.5).source).to eq(output)
expect(output.component_id).to eq('fgh4590')
end
let(:client) { connection.send :client }
before { allow(client).to receive :write }
describe "sending a command" do
let(:command) { Punchblock::Command::Answer.new request_id: 'fooobarrr', target_call_id: 'foo', domain: 'bar.com' }
it "should write an IQ containing the command to the socket" do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza).to be_a Blather::Stanza::Iq
expect(stanza.to).to eq('foo@bar.com')
expect(stanza.type).to eq(:set)
}
connection.write command
end
it "should put the command in a requested state" do
connection.write command
expect(command).to be_requested
end
it "should use the command's request_id as the ID id" do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.id).to eq('fooobarrr')
}
connection.write command
end
end
it 'should send a "Chat" presence when ready' do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.to).to eq('rayo.net')
expect(stanza).to be_a Blather::Stanza::Presence::Status
expect(stanza.chat?).to be true
}
connection.ready!
end
it 'should send a "Do Not Disturb" presence when not_ready' do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.to).to eq('rayo.net')
expect(stanza).to be_a Blather::Stanza::Presence::Status
expect(stanza.dnd?).to be true
}
connection.not_ready!
end
describe '#send_message' do
it 'should send a "normal" message to the given user and domain' do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.to).to eq('someone@example.org')
expect(stanza).to be_a Blather::Stanza::Message
expect(stanza.type).to eq(:normal)
expect(stanza.body).to eq('Hello World!')
expect(stanza.subject).to be_nil
}
connection.send_message 'someone', 'example.org', 'Hello World!'
end
it 'should default to the root domain' do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.to).to eq('someone@rayo.net')
}
connection.send_message "someone", nil, nil
end
it 'should send a message with the given subject' do
expect(client).to receive(:write).once.with { |stanza|
expect(stanza.subject).to eq("Important Message")
}
connection.send_message nil, nil, nil, :subject => "Important Message"
end
end
describe '#handle_presence' do
let :complete_xml do
<<-MSG
MSG
end
let(:example_complete) { import_stanza complete_xml }
it { expect(example_complete).to be_a Blather::Stanza::Presence }
describe "accessing the rayo node for a presence stanza" do
it "should import the rayo node" do
expect(example_complete.rayo_node).to be_a Punchblock::Event::Complete
end
it "should be memoized" do
expect(example_complete.rayo_node).to be example_complete.rayo_node
end
end
describe "presence received" do
let(:handle_presence) { connection.__send__ :handle_presence, example_event }
describe "from an offer" do
let :offer_xml do
<<-MSG
MSG
end
before do
@now = DateTime.now
DateTime.stub now: @now
end
let(:example_event) { import_stanza offer_xml }
it { expect(example_event).to be_a Blather::Stanza::Presence }
it 'should call the event handler with the event' do
expect(mock_event_handler).to receive(:call).once.with { |event|
expect(event).to be_instance_of Event::Offer
expect(event.target_call_id).to eq('9f00061')
expect(event.source_uri).to eq('xmpp:9f00061@call.rayo.net')
expect(event.domain).to eq('call.rayo.net')
expect(event.transport).to eq('xmpp')
expect(event.timestamp).to eq(@now)
}
handle_presence
end
context "with a delayed delivery timestamp" do
let :offer_xml do
<<-MSG
MSG
end
it 'should stamp that time on the rayo event' do
expect(mock_event_handler).to receive(:call).once.with { |event|
expect(event.timestamp).to eq(DateTime.new(2002, 9, 10, 23, 8, 25, 0))
}
handle_presence
end
end
end
describe "from something that's not a real event" do
let :irrelevant_xml do
<<-MSG
MSG
end
let(:example_event) { import_stanza irrelevant_xml }
it 'should not be considered to be a rayo event' do
expect(example_event.rayo_event?).to be_false
end
it 'should have a nil rayo_node' do
expect(example_event.rayo_node).to be_nil
end
it 'should not handle the event' do
expect(mock_event_handler).to receive(:call).never
expect { handle_presence }.to throw_symbol(:pass)
end
end
end
end
describe "#handle_error" do
let(:call_id) { "f6d437f4-1e18-457b-99f8-b5d853f50347" }
let(:component_id) { 'abc123' }
let :error_xml do
<<-MSG
Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]
MSG
end
let(:example_error) { import_stanza error_xml }
let(:cmd) { Component::Output.new }
before do
cmd.request!
connection.__send__ :handle_error, example_error, cmd
end
subject { cmd.response }
it "should have the correct call ID" do
expect(subject.call_id).to eq(call_id)
end
it "should have the correct component ID" do
expect(subject.component_id).to eq(component_id)
end
it "should have the correct name" do
expect(subject.name).to eq(:item_not_found)
end
it "should have the correct text" do
expect(subject.text).to eq('Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]')
end
end
describe "#prep_command_for_execution" do
let(:stanza) { subject.prep_command_for_execution command }
context "with a dial command" do
let(:command) { Command::Dial.new }
let(:expected_jid) { 'rayo.net' }
it "should use the correct JID" do
stanza = subject.prep_command_for_execution command
expect(stanza.to).to eq(expected_jid)
end
end
context "with a call command" do
let(:command) { Command::Answer.new target_call_id: 'abc123' }
let(:expected_jid) { 'abc123@rayo.net' }
it "should use the correct JID" do
expect(stanza.to).to eq(expected_jid)
end
context "with a domain specified" do
let(:expected_jid) { 'abc123@calls.rayo.net' }
it "should use the specified domain in the JID" do
stanza = subject.prep_command_for_execution command, domain: 'calls.rayo.net'
expect(stanza.to).to eq(expected_jid)
end
end
end
context "with a call component" do
let(:command) { Component::Output.new :target_call_id => 'abc123' }
let(:expected_jid) { 'abc123@rayo.net' }
it "should use the correct JID" do
expect(stanza.to).to eq(expected_jid)
end
end
context "with a call component command" do
let(:command) { Component::Stop.new :target_call_id => 'abc123', :component_id => 'foobar' }
let(:expected_jid) { 'abc123@rayo.net/foobar' }
it "should use the correct JID" do
expect(stanza.to).to eq(expected_jid)
end
end
context "with a mixer component" do
let(:command) { Component::Output.new :target_mixer_name => 'abc123' }
let(:expected_jid) { 'abc123@rayo.net' }
it "should use the correct JID" do
expect(stanza.to).to eq(expected_jid)
end
end
context "with a mixer component command" do
let(:command) { Component::Stop.new :target_mixer_name => 'abc123', :component_id => 'foobar' }
let(:expected_jid) { 'abc123@rayo.net/foobar' }
it "should use the correct JID" do
expect(stanza.to).to eq(expected_jid)
end
end
end
describe "receiving events from a mixer" do
context "after joining the mixer" do
before do
expect(client).to receive :write_with_handler
subject.write Command::Join.new(:mixer_name => 'foomixer')
end
let :active_speaker_xml do
<<-MSG
MSG
end
let(:active_speaker_event) { import_stanza active_speaker_xml }
it "should tag those events with a mixer name, rather than a call ID" do
expect(mock_event_handler).to receive(:call).once.with { |event|
expect(event).to be_instance_of Event::StartedSpeaking
expect(event.target_mixer_name).to eq('foomixer')
expect(event.target_call_id).to be nil
expect(event.domain).to eq('rayo.net')
}
connection.__send__ :handle_presence, active_speaker_event
end
end
end
end # describe XMPP
end # XMPP
end # Punchblock