require 'spec_helper'

describe "ManyPolymorphicProxy" do
  before do
    Room.collection.drop
    Message.collection.drop
  end

  it "should default reader to empty array" do
    Room.new.messages.should == []
  end

  it "should add type key to polymorphic class base" do
    Message.keys.keys.should include('_type')
  end

  it "should allow adding to assiciation like it was an array" do
    room = Room.new
    room.messages << Enter.new
    room.messages.push Exit.new
    room.messages.concat Exit.new
    room.messages.size.should == 3
  end

  it "should be able to replace the association" do
    room = Room.create(:name => 'Lounge')

    lambda {
      room.messages = [
        Enter.new(:body => 'John entered room', :position => 1),
        Chat.new(:body => 'Heyyyoooo!',         :position => 2),
        Exit.new(:body => 'John exited room',   :position => 3)
      ]
    }.should change { Message.count }.by(3)

    room = room.reload
    messages = room.messages.all :order => "position"
    messages.size.should == 3
    messages[0].body.should == 'John entered room'
    messages[1].body.should == 'Heyyyoooo!'
    messages[2].body.should == 'John exited room'
  end

  it "should correctly store type when using <<, push and concat" do
    room = Room.new
    room.messages <<      Enter.new(:body => 'John entered the room', :position => 1)
    room.messages.push    Exit.new(:body => 'John entered the room', :position => 2)
    room.messages.concat  Chat.new(:body => 'Holla!'             , :position => 3)

    room = room.reload
    messages = room.messages.all :order => "position"
    messages[0]._type.should == 'Enter'
    messages[1]._type.should == 'Exit'
    messages[2]._type.should == 'Chat'
  end

  context "build" do
    it "should assign foreign key" do
      room = Room.create
      message = room.messages.build
      message.room_id.should == room._id
    end

    it "should assign _type" do
      room = Room.create
      message = room.messages.build
      message._type.should == 'Message'
    end

    it "should allow assigning attributes" do
      room = Room.create
      message = room.messages.build(:body => 'Foo!')
      message.body.should == 'Foo!'
    end
  end

  context "create" do
    it "should assign foreign key" do
      room = Room.create
      message = room.messages.create
      message.room_id.should == room._id
    end

    it "should assign _type" do
      room = Room.create
      message = room.messages.create
      message._type.should == 'Message'
    end

    it "should save record" do
      room = Room.create
      lambda {
        room.messages.create
      }.should change { Message.count }
    end

    it "should allow passing attributes" do
      room = Room.create
      message = room.messages.create(:body => 'Foo!')
      message.body.should == 'Foo!'
    end
  end

  context "count" do
    it "should work scoped to association" do
      room = Room.create
      3.times { room.messages.create }

      other_room = Room.create
      2.times { other_room.messages.create }

      room.messages.count.should == 3
      other_room.messages.count.should == 2
    end

    it "should work with conditions" do
      room = Room.create
      room.messages.create(:body => 'Foo')
      room.messages.create(:body => 'Other 1')
      room.messages.create(:body => 'Other 2')

      room.messages.count(:body => 'Foo').should == 1
    end
  end

  context "Finding scoped to association" do
    before do
      @lounge = Room.create(:name => 'Lounge')
      @lm1 = Message.create(:body => 'Loungin!', :position => 1)
      @lm2 = Message.create(:body => 'I love loungin!', :position => 2)
      @lounge.messages = [@lm1, @lm2]
      @lounge.save

      @hall = Room.create(:name => 'Hall')
      @hm1 = Message.create(:body => 'Do not fall in the hall', :position => 1)
      @hm3 = Message.create(:body => 'Loungin!', :position => 3)
      @hm2 = Message.create(:body => 'Hall the king!', :position => 2)
      @hall.messages = [@hm1, @hm2, @hm3]
      @hall.save
      @hall.reload
    end

    context "dynamic finders" do
      it "should work with single key" do
        @lounge.messages.find_by_position(1).should == @lm1
        @hall.messages.find_by_position(2).should == @hm2
      end

      it "should work with multiple keys" do
        @lounge.messages.find_by_body_and_position('Loungin!', 1).should == @lm1
        @lounge.messages.find_by_body_and_position('Loungin!', 2).should be_nil
      end

      it "should raise error when using !" do
        lambda {
          @lounge.messages.find_by_position!(222)
        }.should raise_error(MongoMapper::DocumentNotFound)
      end

      context "find_or_create_by" do
        it "should not create document if found" do
          lambda {
            message = @lounge.messages.find_or_create_by_body('Loungin!')
            message.room.should == @lounge
            message.should == @lm1
          }.should_not change { Message.count }
        end

        it "should create document if not found" do
          lambda {
            message = @lounge.messages.find_or_create_by_body('Yo dawg!')
            message.room.should == @lounge
            message._type.should == 'Message'
          }.should change { Message.count }.by(1)
        end
      end
    end

    context "with #all" do
      it "should work" do
        @lounge.messages.all(:order => "position").should == [@lm1, @lm2]
      end

      it "should work with conditions" do
        messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
        messages.should == [@lm1]
      end

      it "should work with order" do
        messages = @lounge.messages.all(:order => 'position desc')
        messages.should == [@lm2, @lm1]
      end
    end

    context "with #first" do
      it "should work" do
        @lounge.messages.first(:order => "position asc").should == @lm1
      end

      it "should work with conditions" do
        message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
        message.should == @lm2
      end
    end

    context "with #last" do
      it "should work" do
        @lounge.messages.last(:order => "position asc").should == @lm2
      end

      it "should work with conditions" do
        message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
        message.should == @lm1
      end
    end

    context "with one id" do
      it "should work for id in association" do
        @lounge.messages.find(@lm2._id).should == @lm2
      end

      it "should not work for id not in association" do
        lambda {
          @lounge.messages.find!(@hm2._id)
        }.should raise_error(MongoMapper::DocumentNotFound)
      end
    end

    context "with query options/criteria" do
      it "should work with order on association" do
        @lounge.messages.should == [@lm1, @lm2]
      end

      it "should allow overriding the order provided to the association" do
        @lounge.messages.all(:order => 'position').should == [@lm1, @lm2]
      end

      it "should allow using conditions on association" do
        @hall.latest_messages.should == [@hm3, @hm2]
      end
    end

    context "with multiple ids" do
      it "should work for ids in association" do
        messages = @lounge.messages.find(@lm1._id, @lm2._id)
        messages.should == [@lm1, @lm2]
      end

      it "should not work for ids not in association" do
        lambda {
          @lounge.messages.find!(@lm1._id, @lm2._id, @hm2._id)
        }.should raise_error(MongoMapper::DocumentNotFound)
      end
    end

    context "with #paginate" do
      before do
        @messages = @hall.messages.paginate(:per_page => 2, :page => 1, :order => 'position asc')
      end

      it "should return total pages" do
        @messages.total_pages.should == 2
      end

      it "should return total entries" do
        @messages.total_entries.should == 3
      end

      it "should return the subject" do
        @messages.should == [@hm1, @hm2]
      end
    end
  end

  context "extending the association" do
    it "should work using a block passed to many" do
      room = Room.new(:name => "Amazing Room")
      messages = room.messages = [
        Enter.new(:body => 'John entered room',  :position => 3),
        Chat.new(:body => 'Heyyyoooo!',          :position => 4),
        Exit.new(:body => 'John exited room',    :position => 5),
        Enter.new(:body => 'Steve entered room', :position => 6),
        Chat.new(:body => 'Anyone there?',       :position => 7),
        Exit.new(:body => 'Steve exited room',   :position => 8)
      ]
      room.save
      room.messages.older.should == messages[3..5]
    end

    it "should work using many's :extend option" do

      room = Room.new(:name => "Amazing Room")
      accounts = room.accounts = [
        Bot.new(:last_logged_in => 3.weeks.ago),
        AccountUser.new(:last_logged_in => nil),
        Bot.new(:last_logged_in => 1.week.ago)
      ]
      room.save
      room.accounts.inactive.should == [accounts[1]]
    end
  end
end
