require 'spec_helper' require 'flydata-core/postgresql/pg_client' module FlydataCore module Postgresql describe PGClient do let(:dbconf) do { host: 'localhost', port: 5555, username: 'testuser', password: 'testpassword', dbname: 'testdb' } end let(:subject_object) { described_class.new(dbconf) } let(:pg_conn) { double('pg_conn') } let(:socket) { double('socket') } before do allow(PG::Connection).to receive(:connect_start).and_return(pg_conn) allow(pg_conn).to receive(:connect_poll).and_return(PG::PGRES_POLLING_OK) allow(pg_conn).to receive(:status).and_return(PG::CONNECTION_OK) allow(pg_conn).to receive(:socket_io).and_return(socket) allow(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(true) end describe '#establish_connection' do subject { subject_object.establish_connection } context 'without errors' do it { is_expected.to eq(pg_conn) } it 'sets @conn' do subject expect(subject_object.instance_variable_get(:@conn)).to eq(pg_conn) end it 'reuses @conn until closes' do subject_object.establish_connection subject expect(subject_object.instance_variable_get(:@conn)).to eq(pg_conn) end end context 'when getting an async writing timeout error' do before do expect(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(nil) end it { expect{subject}.to raise_error(PG::Error, /Asynchronous.+\(WRITING\)/) } end context 'when getting an async reading timeout error' do before do expect(pg_conn).to receive(:connect_poll).and_return(PG::PGRES_POLLING_READING) expect(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(true) expect(IO).to receive(:select).with([socket], nil, nil, PGClient::PG_CONNECT_TIMEOUT).and_return(false) end it { expect{subject}.to raise_error(PG::Error, /Asynchronous.+\(READING\)/) } end context 'when getting socket error' do before do expect(IPSocket).to receive(:getaddress).with('localhost').and_raise(SocketError, 'getaddrinfo: nodename nor servname provided, or not known') end it { expect{subject}.to raise_error(PG::Error, /Connection failed: FATAL: unknown host\(localhost\)\./) } end end describe '#query' do subject { subject_object.query(query) } context 'when query is a string' do let(:query) { 'test query;' } let(:result) { double('result') } it do expect(pg_conn).to receive(:query).with(query, []). and_return(result) expect(subject).to eq(result) end end context 'when query has overriders' do let(:query) { PGQuery.new(query_text, value_overriders:{"value" => -> (v) { "#{v}!" } }) } let(:query_text) { "test_query" } let(:result) { [{"id" => 1, "value" => "hi"}, {"id" => 2, "value" => "ho"}] } let(:expected_values) { ["hi!", "ho!"] } it do expect(pg_conn).to receive(:query).with(query_text, []). and_return(result) subject.each_with_index do |row, i| expect(row["value"]).to eq expected_values[i] end end end context 'when query has binding_params' do let(:binding_params) { ['1000', '2000'] } let(:query) { PGQuery.new(query_text, binding_params: binding_params) } let(:query_text) { "test_query" } let(:result) { double('result') } it do expect(pg_conn).to receive(:query).with(query_text, binding_params). and_return(result) expect(subject).to eq(result) end end end describe '#close' do subject { subject_object.close } context 'when conn exists' do before { subject_object.establish_connection } it do expect(pg_conn).to receive(:finish).once subject expect(subject_object.instance_variable_get(:@conn)).to be_nil end end context 'when conn does not exist' do it do expect(pg_conn).to receive(:finish).never expect{subject}.not_to raise_error expect(subject_object.instance_variable_get(:@conn)).to be_nil end end end end describe PGClient::HashValueOverrider do let(:subject_object) { described_class.new(delegate, overriders) } let(:delegate) { { "salute" => saluteval, "id" => idval } } let(:idval) { 1 } let(:saluteval) { "hello" } let(:overriders) { {"salute" => ->(v) { "#{v}!" } } } describe '#[]' do subject { subject_object[key] } context 'when no overrider for the key' do let(:key) { "id" } it { is_expected.to eq idval } end context 'when key has an overrider' do let(:key) { "salute" } it { is_expected.to eq "#{saluteval}!" } end end describe '#values' do subject { subject_object.values } it { is_expected.to eq [ "#{saluteval}!", idval ] } end describe '#first' do subject { subject_object.first } it { is_expected.to eq [ "salute", "#{saluteval}!" ] } end describe '#kind_of?' do subject { subject_object.kind_of?(klass) } context 'when klass is of delegate' do let(:klass) { delegate.class } it { is_expected.to be_truthy } end context 'when klass is of self' do let(:klass) { described_class } it { is_expected.to be_falsey } end end describe '#has_key?' do subject { subject_object.has_key?(key) } context 'when delegate has the key' do let(:key) { "id" } it { is_expected.to be_truthy } end context 'when delegate does not have the key' do let(:key) { "random_stuff" } it { is_expected.to be_falsey } end end end end end