# encoding: utf-8 require 'spec_helper' describe HTTPkit::Connection::EventMachine do let(:connection) { described_class.allocate } let(:callback) { double('callback', call: nil) } let(:parser) { double('parser', :<< => nil) } let(:serializer) { double('serializer', setup_body: nil, serialize: nil) } let(:message) { open_request } before do allow(connection).to receive(:close_connection_after_writing) allow(HTTP::Parser).to receive(:new) { parser } allow(HTTPkit::Serializer).to receive(:new) { serializer } allow(HTTPkit::Support::Message).to receive(:build) { message } connection.instance_variable_set(:@signature, 123) # XXX: initialize separately, EM::Connection overloads .new # this smell tells us that we should decouple from EM::Connection connection.send(:initialize, callback) end describe '.start_client' do let(:config) { { address: double, port: double } } let(:client_class) { double('client_class', new: client) } let(:client) { double } before do allow(EM).to receive(:connect) { connection } end subject! { described_class.start_client(config, client_class) } it 'starts a Client connection' do expect(EM).to have_received(:connect) .with(config[:address], config[:port], described_class) expect(client_class).to have_received(:new) .with(config, connection) expect(subject).to be(client) end end describe '.start_server' do let(:config) { { address: double, port: double } } let(:server_class) { double('server_class', new: nil) } before do allow(EM).to receive(:start_server) { |*, cb| cb.call(connection) } end subject! { described_class.start_server(config, server_class) } it 'starts a Client connection' do expect(EM).to have_received(:start_server) .with(config[:address], config[:port], described_class, kind_of(Proc)) expect(server_class).to have_received(:new) .with(config, connection) end end describe '#initialize' do subject! { connection } it 'creates parser' do expect(HTTP::Parser).to have_received(:new).with(connection) end it 'calls back' do expect(callback).to have_received(:call).with(connection) end describe 'without callback' do let(:connection) { described_class.allocate } subject! { connection.send(:initialize) } it 'is happy as well' do expect(connection).to be_a(described_class) end end end describe '#serialize' do let(:writer) { connection.method(:send_data) } subject! { connection.serialize(message) } it 'feeds a serializer' do expect(HTTPkit::Serializer).to have_received(:new).with(message, writer) expect(serializer).to have_received(:serialize) end end describe '#close' do subject! { connection.close } it 'closes the connection' do expect(connection).to have_received(:close_connection_after_writing) expect(connection.closed).to be_pending end describe 'with reason' do let(:reason) { double('reason') } subject! { connection.close(reason) } it 'rejects the promise' do expect(connection.closed).to be_rejected expect(connection.closed.reason).to be(reason) expect(connection).to have_received(:close_connection_after_writing) end end end describe '#receive_data' do let(:data) { double } subject { connection.receive_data(data) } it 'feeds the parser' do subject expect(parser).to have_received(:<<).with(data) end describe 'on error' do let(:error) { RuntimeError.new } before do allow(parser).to receive(:<<) { raise error } allow(connection).to receive(:close) end it 'closes the connection' do subject expect(connection).to have_received(:close).with(error) end end end describe '#unbind' do subject! { connection.unbind } it 'fulfills the promise' do expect(connection.closed).to be_fulfilled expect(connection.closed.value).to be(nil) end describe 'with reason' do let(:reason) { double('reason') } subject! { connection.unbind(reason) } it 'rejects the promise' do expect(connection.closed).to be_rejected expect(connection.closed.reason).to be(reason) end end end describe '#on_headers_complete' do let(:parse) { double('parse', call: nil) } before { connection.on_message = parse } subject! { connection.on_headers_complete(nil) } it 'builds the message and passes it on' do expect(HTTPkit::Support::Message).to have_received(:build).with(parser) expect(parse).to have_received(:call).with(message) end end describe '#on_body' do let(:progress) { [] } let(:chunks) { %w[body chunk] } before do connection.on_message = proc {} connection.on_headers_complete(nil) end subject! do message.body.closed.on_progress { |chunk| progress << chunk } connection.on_body(chunks[0]) connection.on_body(chunks[1]) message.close end it 'passes chunk to message body' do expect(message.body.to_s).to eq(chunks.join) expect(progress).to eq(chunks) end end describe '#on_message_complete' do before do connection.on_message = proc {} connection.on_headers_complete(nil) end subject! { connection.on_message_complete } it 'closes the body' do expect(message.body.closed).to be_fulfilled end end end