require 'spec_helper'

describe Ripple::Indexes do
  context "class methods" do
    subject { Indexer }
    it { should respond_to(:indexes) }
    it { should have(4).indexes }

    it "should remove the :index key from the property options" do
      subject.properties[:name].options.should_not include(:index)
      subject.properties[:age].options.should_not include(:index)
    end

    it "should not have a property for synthetic indexes" do
      subject.properties[:name_age].should == nil
      subject.properties[:name_greeting].should == nil
    end
  end
  context "inherited indexes" do
    subject { SubIndexer }
    it { should have(5).indexes }
    it "should have inherited indexes" do
      subject.indexes.keys.should include('age', 'name', 'name_age', 'name_greeting')
    end
    it "should have indexes defined on the subclass" do
      subject.indexes.keys.should include('height')
    end
  end

  context "instance methods" do
    before { subject.robject.stub!(:store).and_return(true) }
    subject { Indexer.new(:name => "Bob", :age => 28) }
    it { should respond_to(:indexes_for_persistence) }
    its(:indexes_for_persistence) { should include('age_int') }
    its(:indexes_for_persistence) { should include('name_bin') }
    its(:indexes_for_persistence) { should include('name_age_bin') }
    its(:indexes_for_persistence) { should include('name_greeting_bin') }
    its(:indexes_for_persistence) { should be_all {|_,i| i.kind_of?(Set) } }

    it "should set the indexes on the internal Riak object when saving" do
      subject.save
      subject.robject.indexes.should_not be_empty
      subject.robject.indexes["name_bin"].should == Set["Bob"]
      subject.robject.indexes["age_int"].should == Set[28]
      subject.robject.indexes["name_age_bin"].should == Set["Bob-28"]
      subject.robject.indexes["name_greeting_bin"].should == Set["Bob: Hello!"]
    end

    context "when embedded documents have indexes" do
      subject do
        Indexer.new(:name => "Bob",
                    :age => 28,
                    :addresses => [{:street => "10 Main St", :city => "Anywhere"},
                                   {:street => "100 W 10th Avenue", :city => "Springfield"}],
                    :primary_address => {
                      :street => "1 W 5th Place",
                      :city => "Independence"})
      end

      its(:indexes_for_persistence){ should include("primary_address_street_bin") }
      its(:indexes_for_persistence){ should include("primary_address_city_bin") }
      its(:indexes_for_persistence){ should include("addresses_street_bin") }
      its(:indexes_for_persistence){ should include("addresses_city_bin") }

      it "should merge indexes for many embedded associations" do
        subject.indexes_for_persistence['addresses_city_bin'].should == Set["Anywhere", "Springfield"]
        subject.indexes_for_persistence['addresses_street_bin'].should == Set["10 Main St", "100 W 10th Avenue"]
      end
    end
  end
end

describe Ripple::Index do
  it "should use a binary index when the type is a String" do
    Ripple::Index.new('foo', String).index_type.should == 'bin'
  end

  it "should use an integer index when the type is an Integer" do
    Ripple::Index.new('foo', Integer).index_type.should == 'int'
  end

  it "should use an integer index when the type is a time" do
    [Time, Date, ActiveSupport::TimeWithZone].each do |klass|
      Ripple::Index.new('foo', klass).index_type.should == 'int'
    end
  end

  it "should use an integer index when index type is Integer" do
    Ripple::Index.new('foo', String, Integer).index_type.should == 'int'
  end

  it "should use a binary index when index type is String" do
    Ripple::Index.new('foo', Integer, String).index_type.should == 'bin'
  end

  it "should raise an error when the index type cannot be determined" do
    klass = Class.new
    expect { Ripple::Index.new('foo', klass).index_type }.to raise_error(ArgumentError)
  end

  it "should use the specified index type" do
    Ripple::Index.new('foo', Time, 'bin').index_type.should == 'bin'
    Ripple::Index.new('foo', Time, String).index_type.should == 'bin'
    Ripple::Index.new('foo', Time, Integer).index_type.should == 'int'
  end

  it "should provide an index key that includes the type" do
    Ripple::Index.new('foo', Integer).index_key.should == 'foo_int'
    Ripple::Index.new('foo', String).index_key.should == 'foo_bin'
  end
end
