require "spec_helper" describe G5Updatable::LocationsUpdater do let(:name) { properties['name'] } let(:uid) { properties['uid'] } let(:client_uid) { properties['client_uid'] } let(:client_urn) { properties['client_uid'].split('/').last } let(:urn) { properties['urn'] } let(:latitude) { properties['latitude'] } let(:longitude) { properties['longitude'] } let(:properties) do JSON.parse(fixture('location-g5-cl-1soj9pe2-541-apartments.json'))['location'] end let(:options) { {} } let(:updater) { described_class.new(properties, options) } shared_examples 'does not destroy existing clients and locations' do let!(:existing_client) { FactoryGirl.create(:client) } let!(:existing_location) { FactoryGirl.create(:location, client_uid: existing_client.uid) } it 'does not destroy the existing client or location' do expect(G5Updatable::Client.find(existing_client.id)).to_not be_nil expect(G5Updatable::Location.find(existing_location.id)).to_not be_nil end end describe "#update" do subject { G5Updatable::Location.last } context "with no existing Location records" do before { updater.update } it "creates a Location" do expect(G5Updatable::Location.count).to eq(1) end it 'redacts amenities key' do expect(subject.properties.keys.collect(&:to_sym)).to eq(properties.keys.collect(&:to_sym) - [:amenities]) end its(:uid) { is_expected.to eq(uid) } its(:urn) { is_expected.to eq(urn) } its(:client_uid) { is_expected.to eq(client_uid) } its(:client_urn) { is_expected.to eq(client_urn ) } its(:name) { is_expected.to eq(name) } its(:latitude) { is_expected.to eq(latitude) } its(:longitude) { is_expected.to eq(longitude) } it 'sets name field so that consumers can easily sort by name' do subject expect(G5Updatable::Location.find_by_name(name)).to eq(subject) end it 'creates amenities' do subject expect(subject.hub_amenities.count).to eq(2) expect(subject.hub_amenities.collect(&:name)).to match_array(['Power', 'WIFI']) expect(subject.hub_amenities.collect(&:icon)).to match_array(%w(fa-electric-outlet fa-wifi)) expect(subject.hub_amenities.first.external_updated_at).to be_present expect(subject.hub_amenities.first.external_created_at).to be_present end it_behaves_like 'does not destroy existing clients and locations' end context 'with an existing Location record' do let(:old_amenity) { create(:hub_amenity) } before do location = FactoryGirl.create(:location, uid: 'old', urn: urn, client_uid: client_urn) location.hub_amenities << old_amenity updater.update end it 'does not create a new Location' do expect(G5Updatable::Location.count).to eq(1) end its(:urn) { is_expected.to eq(urn) } its(:name) { is_expected.to eq name } it 'updates amenities' do expect(subject.hub_amenities.collect(&:name)).to match_array(['Power', 'WIFI']) end it 'sets flat amenities for easier exclusive querying' do expect(subject.flat_amenity_names).to eq('|Power|WIFI|') end it_behaves_like 'does not destroy existing clients and locations' context 'destroy_orphaned_locations set to false' do before do expect(updater).to_not receive :destroy_orphaned_locations! end it "does not create a new Location" do expect(G5Updatable::Location.count).to eq(1) end its(:urn) { is_expected.to eq(urn) } its(:name) { is_expected.to eq name } it_behaves_like 'does not destroy existing clients and locations' end end context "with an existing identical Location record" do before do FactoryGirl.create(:location, uid: uid, urn: urn, client_uid: "client_uid", name: name, properties: properties, updated_at: 1.hour.ago) @original_updated_at = subject.updated_at updater.update end it "updates its timestamp" do expect(subject.reload.updated_at).to be > @original_updated_at end it_behaves_like 'does not destroy existing clients and locations' end context "with no locations" do let(:g5_locations) { [] } let(:options) { {destroy_orphaned_locations: true} } before do FactoryGirl.create(:location, uid: uid, urn: "another_urn", client_urn: client_urn, client_uid: "client_uid", name: name, properties: properties) FactoryGirl.create(:location, uid: uid, urn: "some_urn", client_uid: "another_client_uid", name: name, properties: properties) updater.update end it "deletes locations" do expect(G5Updatable::Location.by_client_uid("client_uid").count).to eq(0) expect(G5Updatable::Location.by_client_uid("another_client_uid").count).to eq(1) end end context "with an orphaned location" do let(:options) { {destroy_orphaned_locations: true} } before do FactoryGirl.create(:location, uid: "another_uid", urn: "dead_urn", client_urn: client_urn) updater.update end it "deletes locations" do expect(G5Updatable::Location.by_urn(urn).by_client_urn(client_urn).count).to eq(1) expect(G5Updatable::Location.by_urn("dead_urn").by_client_urn(client_urn).count).to eq(0) end end context "callbacks" do before do FactoryGirl.create(:location, uid: 'old', urn: urn) @update_callback_called = false @create_callback_called = false described_class.on_update { |_location| @update_callback_called = true } described_class.on_create { |_location| @create_callback_called = true } end describe "update callbacks" do it "calls update callbacks passing the location that was updated" do expect(@update_callback_called).to be false updater.update expect(@update_callback_called).to be true end end describe "create callbacks" do it "skips create callbacks when the location already exists" do updater.update expect(@create_callback_called).to be false end context 'with some new locations' do let(:new_properties) do JSON.parse(fixture('client-g5-c-1soj8m6e-g5-multifamily.json'))['client']['locations'] end it "calls create callbacks for a new location" do described_class.new(new_properties, options).update expect(@create_callback_called).to be true end end end end end describe ".on_update and .on_update_callbacks" do before do described_class.on_update_callbacks = [] end it "adds to the #update callbacks" do callback_1 = -> (location, anything) {} callback_2 = -> (location, anything) {} described_class.on_update(&callback_1) described_class.on_update(&callback_2) expect(described_class.on_update_callbacks). to eq([callback_1, callback_2]) end it "passes the location into each callback" do accumulator = [] callback = -> (location, anything) { accumulator << location.id } described_class.on_update(&callback) updater.update location = G5Updatable::Location.first expect(accumulator).to include(location.id) end end describe ".on_update_callbacks" do it "defaults to an empty array" do described_class.on_update_callbacks = nil expect(described_class.on_update_callbacks).to be_empty end end describe ".on_create and .on_create_callbacks" do before do described_class.on_create_callbacks = [] end it "adds to the #update callbacks" do callback_1 = -> (location) {} callback_2 = -> (location) {} described_class.on_create(&callback_1) described_class.on_create(&callback_2) expect(described_class.on_create_callbacks). to eq([callback_1, callback_2]) end it "passes the location into each callback" do accumulator = [] callback = -> (location) { accumulator << location.id } described_class.on_create(&callback) updater.update location = G5Updatable::Location.first expect(accumulator).to include(location.id) end end describe ".on_create_callbacks" do it "defaults to an empty array" do described_class.on_create_callbacks = nil expect(described_class.on_create_callbacks).to be_empty end end end