require 'rails_helper'

RSpec.describe User, type: :model do
  context "associations" do
    it { should belong_to(:degree) }
    it { should have_many(:employments) }
    it { should have_many(:feeds) }
    it { should have_many(:requested_contacts) }
    it { should have_many(:contact_requests) }
    it { should have_many(:wall_messages) }
    it { should have_many(:sent_wall_messages) }
    it { should have_many(:groups) }
    it { should have_and_belong_to_many(:subjects) }
    it { should have_and_belong_to_many(:contacts) }
    it { should have_and_belong_to_many(:joined_groups) }
    it { should have_attached_file(:avatar) }
  end

  context "validations" do
    before(:all) do
      FactoryGirl.create(:user)
    end

    it { should validate_presence_of(:email) }
    it { should validate_uniqueness_of(:email) }
    it { should validate_length_of(:email).is_at_most(255) }
    it { should allow_value("aarana@fi.uba.ar").for(:email) }
    it { should_not allow_value("aarana@gmail.com").for(:email) }
    it { should_not allow_value("not-an-email").for(:email) }
    it { should validate_presence_of(:password) }
    it { should validate_confirmation_of(:password) }
    it { should validate_presence_of(:name) }
    it { should validate_length_of(:name).is_at_most(100) }
    it { should validate_presence_of(:student_number) }
    it { should validate_uniqueness_of(:student_number) }
    it { should validate_numericality_of(:student_number)
      .only_integer
      .is_greater_than(0) }
    it { should validate_presence_of(:degree) }
    it { should validate_attachment_content_type(:avatar)
      .allowing('image/jpeg') }
    it { should validate_attachment_size(:avatar)
      .less_than(5.megabytes) }
    it { should allow_value("1234").for(:cellphone) }
    it { should allow_value("").for(:cellphone) }
    it { should_not allow_value("asdf").for(:cellphone) }
    it { should_not allow_value("4740-7988").for(:cellphone) }
    it { should validate_length_of(:bio).is_at_most(255) }
    it { should validate_length_of(:nationality).is_at_most(100) }
    it { should validate_length_of(:city).is_at_most(100) }
    it { should validate_length_of(:school).is_at_most(100) }
    it { should validate_inclusion_of(:gender)
      .in_array(%w(male female))
      .allow_nil }
  end

  context "when created" do
    subject { FactoryGirl.create(:user, password: "password") }

    it { should_not be_confirmed }
    it { should_not be_active_for_authentication }

    it "should send a confirmation email" do
      expect { FactoryGirl.create(:user) }.to change { ActionMailer::Base.deliveries.count }.by(1)
    end

    it "should encrypt the password before saving" do
      saved_instance = User.find(subject.id)

      expect(saved_instance.password).not_to eq("password")
    end
  end

  context "#authenticate!" do
    before(:each) do
      FactoryGirl.create(:user, email: 'unconfirmed@fi.uba.ar', password: 'password')
      FactoryGirl.create(:user, :confirmed, email: 'confirmed@fi.uba.ar', password: 'password')
    end

    it "should fail for unexisting users" do
      user = User.authenticate! 'fail', 'fail'
      expect(user).to be_nil
    end

    it "should fail for unconfirmed users" do
      user = User.authenticate! 'unconfirmed@fi.uba.ar', 'password'
      expect(user).to be_falsy
    end

    it "should fail for invalid passwords" do
      user = User.authenticate! 'confirmed@fi.uba.ar', 'fail'
      expect(user).to be_falsy
    end

    it "should succeed for valid passwords" do
      user = User.authenticate! 'confirmed@fi.uba.ar', 'password'
      expected = User.where(email: 'confirmed@fi.uba.ar').first
      expect(user).to eq(expected)
    end
  end

  context "#request_contact_to!" do
    before(:each) do
      @first = FactoryGirl.create(:user, :confirmed)
      @second = FactoryGirl.create(:user, :confirmed)
    end

    it "should build a contact request between the users" do
      request = @first.request_contact_to!(@second)

      expect(request.requester.id).to eq(@first.id)
      expect(request.owner.id).to eq(@second.id)
    end
  end

  context "#send_message_to!" do
    before(:each) do
      @first = FactoryGirl.create(:user, :confirmed)
      @second = FactoryGirl.create(:user, :confirmed)
    end

    it "should build a wall message between the users" do
      message = @first.send_message_to!(@second, "This is a message to you")

      expect(message.poster.id).to eq(@first.id)
      expect(message.owner.id).to eq(@second.id)
      expect(message.message).to eq("This is a message to you")
    end
  end

  context "#pending_request_with?" do
    before(:each) do
      @first = FactoryGirl.create(:user, :confirmed)
      @second = FactoryGirl.create(:user, :confirmed)
    end

    it "should return true when there is a pending request between the users" do
      @first.request_contact_to!(@second).save!

      expect(@first).to have_pending_request_with(@second)
      expect(@second).not_to have_pending_request_with(@first)
    end

    it "should return false when there isn't a pending request between the users" do
      expect(@first).not_to have_pending_request_with(@second)
      expect(@second).not_to have_pending_request_with(@first)
    end
  end

  context "#make_contact_with!" do
    before(:each) do
      @first = FactoryGirl.create(:user, :confirmed)
      @second = FactoryGirl.create(:user, :confirmed)
    end

    it "should make uncontacted users contacts" do
      @first.make_contact_with!(@second)

      expect(@first.contacts.count).to eq(1)
      expect(@first.contact_ids).to include(@second.id)

      expect(@second.contacts.count).to eq(1)
      expect(@second.contact_ids).to include(@first.id)
    end

    it "should leave contacted users as they are" do
      @first.make_contact_with!(@second)
      @first.make_contact_with!(@second)

      expect(@first.contacts.count).to eq(1)
      expect(@first.contact_ids).to include(@second.id)

      expect(@second.contacts.count).to eq(1)
      expect(@second.contact_ids).to include(@first.id)
    end
  end

  context "#contact_with?" do
    before(:each) do
      @first = FactoryGirl.create(:user, :confirmed)
      @second = FactoryGirl.create(:user, :confirmed)
      @first.contacts << @second
      @second.contacts << @first
      @third = FactoryGirl.create(:user, :confirmed)
    end

    it "should be true for users that are contacts" do
      expect(@first).to be_contact_with(@second)
      expect(@second).to be_contact_with(@first)
    end

    it "should be false for users that are not contacts" do
      expect(@first).not_to be_contact_with(@third)
      expect(@second).not_to be_contact_with(@third)
      expect(@third).not_to be_contact_with(@first)
      expect(@third).not_to be_contact_with(@second)
    end
  end

  context ".join!" do
    context "using valid user and group" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group)
      end

      it "should make the user a member of the group" do
        @user.join!(@group)
        expect(@user.joined_group_ids).to include(@group.id)
        expect(@group.member_ids).to include(@user.id)
      end

      it "should return true" do
        expect(@user.join!(@group)).to eq(true)
      end
    end

    context "using the user which is owner to the group" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group, owner: @user)
      end

      it "should not make the user a member of the group" do
        @user.join!(@group)
        expect(@user.joined_group_ids).not_to include(@group.id)
        expect(@group.member_ids).not_to include(@user.id)
      end

      it "should return false" do
        expect(@user.join!(@group)).to eq(false)
      end
    end

    context "using a user which already is a member" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group)
        @user.join!(@group)
      end

      it "should return false" do
        expect(@user.join!(@group)).to eq(false)
      end
    end
  end

  context ".leave!" do
    context "using valid user and group" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group)
        @user.join!(@group)
      end

      it "should make the user no longer a member of the group" do
        @user.leave!(@group)
        expect(@user.joined_group_ids).not_to include(@group.id)
        expect(@group.member_ids).not_to include(@user.id)
      end

      it "should return true" do
        expect(@user.leave!(@group)).to eq(true)
      end
    end

    context "using the user which is owner to the group" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group, owner: @user)
      end

      it "should return false" do
        expect(@user.leave!(@group)).to eq(false)
      end
    end

    context "using a user which is not a member of the group" do
      before(:each) do
        @user = FactoryGirl.create(:user)
        @group = FactoryGirl.create(:group)
      end

      it "should return false" do
        expect(@user.leave!(@group)).to eq(false)
      end
    end
  end
end
