require 'spec_helper'

RSpec.describe Clique::DataStore do
  subject { described_class.new(data_files) }

  let(:user) { 'foo' }
  let(:user_file) { "data/#{user}.json" }
  let(:user_model) { double(find_by: user) }
  let(:data_files) { { user => user_file } }

  before do
    allow(Clique::DataArray).to receive(:new).with(user) { user_model }
  end

  describe '#initialize' do
    it 'set data files into instance variable' do
      expect(subject.instance_variable_get(:@files))
        .to include(user => user_file)
    end
  end

  describe '#empty?' do
    context 'when data_files has item' do
      it 'returns false' do
        expect(subject.empty?).to be false
      end
    end

    context 'when data_files is empty' do
      let(:data_files) { {} }

      it 'returns true' do
        expect(subject.empty?).to be true
      end
    end
  end

  describe '#models' do
    before do
      allow(IO).to receive(:read) { user }
    end

    it 'returns models hash' do
      expect(subject.models).to eq(user => user_model)
    end
  end

  describe '#indices' do
    let(:user_index) { double }

    before do
      allow(subject).to receive(:models) { { user => user_model } }
      allow(Clique::DataIndex).to receive(:new) { double(build: user_index) }
    end

    it 'returns data indices hash' do
      expect(subject.indices).to eq(user => user_index)
    end
  end

  describe '#model_names' do
    let(:models) { { 'foo' => double, bar: double } }

    before do
      allow(IO).to receive(:read).with(user_file) { user }
      allow(subject).to receive(:models) { models }
    end

    it 'returns keys in models' do
      expect(subject.model_names).to eq models.keys
    end
  end

  describe '#fields_for' do
    let(:fields) { [double, double] }
    let(:model) { double(fields: fields) }
    let(:model_name) { 'foo' }

    before do
      allow(subject).to receive(:fetch).with(model_name) { model }
    end

    it 'returns fields list for given model' do
      expect(subject.fields_for(model_name)).to eq fields
    end
  end

  describe '#fetch' do
    before do
      allow(subject).to receive(:models) { { user => user_model } }
    end
    it 'fetches model for given name' do
      expect(subject.fetch(user)).to eq user_model
    end
  end

  describe '#search' do
    let(:filter) { double }
    let(:queries) { { 'foo' => filter } }
    let(:user_index) { double(find_by: [user]) }

    before do
      allow(subject).to receive(:valid_queries) { queries }
      allow(subject).to receive(:indices) { { user => user_index } }
      subject.search(queries)
    end

    it 'filters queries with valid_queries' do
      expect(subject).to have_received(:valid_queries)
    end

    it 'fetches result by calling find_by on corresponding model' do
      expect(user_index).to have_received(:find_by).with(filter)
    end

    it 'returns array of find_by result' do
      expect(subject.search(queries)).to eq [user]
    end
  end

  describe '#valid_queries' do
    let(:queries) do
      {
        user => double,
        bar: double
      }
    end

    before do
      allow(subject).to receive(:fetch)
      allow(subject).to receive(:fetch).with(user) { double }
    end

    it 'returns query matches model in data store' do
      expect(subject.valid_queries(queries)).to include(user)
    end

    it 'does not return unmatched model' do
      expect(subject.valid_queries(queries)).not_to include(:bar)
    end
  end
end
