ReadyResponder/ReadyResponder

View on GitHub
spec/models/availability_spec.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'rails_helper'

RSpec.describe Availability, type: :model do
  let(:a_person) { build(:person) }

  before(:example) do
    Timecop.freeze
  end

  after(:example) do
    Timecop.return
  end

  describe "creation" do
    it "has a valid factory" do
      availability = build(:availability, person: a_person)
      expect(availability).to be_valid
    end
  end

  context 'validation' do
    it { is_expected.to validate_presence_of(:person) }
    it { is_expected.to validate_presence_of(:status) }
    it { is_expected.to validate_presence_of(:start_time) }
    it { is_expected.to validate_presence_of(:end_time) }

    it "requires end_time to be after start_time" do # chronology
      availability = build(:availability, person: a_person, start_time: Time.now, end_time: 2.minutes.ago)
      expect(availability).not_to be_valid
    end

    describe 'the start_time and end_time' do
      let(:availability) { build(:availability, person: a_person,
        start_time: 2.hours.ago, end_time: 2.hours.from_now) }

      it 'must not overlap one of the person\'s active availabilities' do
        create(:availability, person: a_person,
          start_time: 1.hour.ago, end_time: 3.hours.from_now)

        expect(availability).not_to be_valid
      end

      it 'can overlap one of the person\'s inactive availabilities' do
        create(:availability, person: a_person,
          status: 'Cancelled', start_time: 1.hour.ago, end_time: 3.hours.from_now)

        expect(availability).to be_valid
      end

      it 'can overlap someone else\'s availabilities' do
        someone_else = create(:person)
        create(:availability, person: someone_else,
          start_time: 2.hours.ago, end_time: 3.hours.from_now)

        expect(availability).to be_valid
      end
    end
  end

  describe 'cancel! method' do
    let(:availability) { create(:availability, person: a_person, status: 'Available') }

    it 'marks the status of the availability as Cancelled' do
      expect { availability.cancel! }.to change {
        availability.reload.status }.from('Available').to('Cancelled')
    end
  end

  describe 'self.process_data' do
    it 'returns nil if there are no available availabilites' do
      expect(Availability.process_data).to eq(nil)
      create(:availability, person: a_person,
            start_time: 1.day.ago, end_time: 1.day.from_now)
      expect(Availability.process_data).to eq(nil)
    end
    it 'returns array of hashes if availabilities are found' do
      create(:availability, person: a_person,
            start_time: 1.day.ago, end_time: 1.day.from_now, status: 'Available')
      result = Availability.process_data
      expect(result).to be_a_kind_of(Array)
      expect(result[0][0]).to be_a_kind_of(Hash)
    end
  end

  context 'partially_available?' do
    it 'returns false for full availabilities' do
      start_time = Time.current
      end_time = start_time + 2.minutes
      availability = build(:availability, person: a_person,
                           start_time: start_time, end_time: end_time)

      event = create(:event, status: "Scheduled",
                     start_time: start_time, end_time: end_time)
      expect(availability.partially_available?(event)).to eq(false)
    end

    it 'returns false for full availabilities which extend past event boundaries' do
      start_time = Time.current
      end_time = start_time + 2.minutes
      availability = build(:availability, person: a_person,
                            start_time: start_time, end_time: end_time + 2.minutes)

      event = create(:event, status: "Scheduled",
                      start_time: start_time, end_time: end_time)
      expect(availability.partially_available?(event)).to eq(false)
    end
  end

  context 'overlapping scope' do
    it 'returns a chainable relation' do
      expect(described_class.overlapping([0,1])).to be_a_kind_of(ActiveRecord::Relation)
    end

    context 'given 3 contiguous availabilities' do
      let!(:first_availability)  { create(:availability, person: a_person,
                                   start_time: 5.hours.ago, end_time: 3.hours.ago) }
      let!(:second_availability) { create(:availability, person: a_person,
                                   start_time: 3.hours.ago, end_time: 1.hour.ago) }
      let!(:third_availability)  { create(:availability, person: a_person,
                                   start_time: 1.hour.ago, end_time: 1.hour.from_now) }

      it 'returns the availabilities whose duration is partially covered by the given date_range' do
        date_range = 4.hours.ago..2.hours.ago
        expect(described_class.overlapping(date_range)).to contain_exactly(
          first_availability, second_availability)
      end

      it 'returns none of them if the given range does not cover any of the availabilities' do
        date_range = 7.hours.ago..6.hours.ago
        expect(described_class.overlapping(date_range)).to be_empty
      end

      it 'returns the availabilities matching the date_range' do
        date_range = second_availability.start_time..second_availability.end_time
        expect(described_class.overlapping(date_range)).to contain_exactly(
          second_availability)
      end

      it 'does not return availabilities that are contiguous to the given date_range' do
        date_range = first_availability.end_time..third_availability.start_time
        expect(described_class.overlapping(date_range)).not_to include(
          first_availability, third_availability)
      end
    end
  end

  context 'not_overlapping scope' do
    it 'returns a chainable relation' do
      expect(described_class.not_overlapping([0,1])).to be_a_kind_of(ActiveRecord::Relation)
    end

    context 'given 3 contiguous availabilities' do
      let!(:first_availability)  { create(:availability, person: a_person,
                                   start_time: 5.hours.ago, end_time: 3.hours.ago) }
      let!(:second_availability) { create(:availability, person: a_person,
                                   start_time: 3.hours.ago, end_time: 1.hour.ago) }
      let!(:third_availability)  { create(:availability, person: a_person,
                                   start_time: 1.hour.ago, end_time: 1.hour.from_now) }

      it 'returns the availabilities whose duration does not overlap the given date_range' do
        date_range = 5.hours.ago..2.hours.ago
        expect(described_class.not_overlapping(date_range)).to contain_exactly(
          third_availability)
      end

      it 'returns an availability that begins at the end of the given date_range' do
        date_range = 7.hours.ago..1.hour.ago
        expect(described_class.not_overlapping(date_range)).to contain_exactly(
          third_availability)
      end
    end
  end

  context 'partially_overlapping scope' do
    let (:start_time){ 1.day.from_now }
    let (:end_time){2.days.from_now }
    let(:people) { create_list(:person, 7) }
    let (:a1){create(:availability, person: people[0],
                 start_time: start_time - 1.hour, end_time: end_time - 1.hour)}
    let (:a2){create(:availability, person: people[1],
                 start_time: start_time + 1.hour, end_time: end_time + 1.hour)}
    let (:a3){create(:availability, person: people[2],
                 start_time: start_time + 1.hour, end_time: end_time - 1.hour)}
    let (:a4){create(:availability, person: people[3],
                 start_time: start_time , end_time: end_time )}
    let (:a5){create(:availability, person: people[4],
                     start_time: start_time - 1.hour, end_time: end_time + 1.hour)}
    let (:a6){create(:availability, person: people[5],
                     start_time: start_time - 2.hour, end_time: start_time - 1.hour)}
    let (:a7){create(:availability, person: people[6],
                     start_time: end_time + 1.hour, end_time: end_time + 2.hour)}

    it 'returns a chainable relation' do
      expect(described_class.partially_overlapping([0,1])).to be_a_kind_of(ActiveRecord::Relation)
    end

    context 'given 3 availabilites that belong to 3 different people' do
      let!(:first_availability)  { create(:availability, person: people[0],
                                   start_time: 6.hours.ago, end_time: 3.hours.ago) }
      let!(:second_availability) { create(:availability, person: people[1],
                                   start_time: 4.hours.ago, end_time: 1.hour.ago) }
      let!(:third_availability)  { create(:availability, person: people[2],
                                   start_time: 2.hours.ago, end_time: 1.hour.from_now) }




      it 'returns the availabilities that contain a part of the given date_range' do
        date_range = 5.hours.ago..3.hours.ago
        expect(described_class.partially_overlapping(date_range)).to contain_exactly(
          second_availability)
      end
    end

    context "confirmation of date comparison" do
      let(:results){Availability.partially_overlapping((start_time..end_time))}
      it "should include partial responses that are < start_time and < end time" do
        expect(results).to include(a1)
      end

      it "should include partial responses that are > start time and < end time" do
        expect(results).to include(a3)
      end

      it "should include partial responses that are > start time and > end time" do
        expect(results).to include(a2)
      end

      it "should not include responses that are entirely before start_time" do
        expect(results).not_to include(a6)
      end
      it "should not include responses that are entirely after end_time" do
        expect(results).not_to include(a7)
      end

    end
  end

  context 'containing scope' do
    it 'returns a chainable relation' do
      expect(described_class.containing([0,1])).to be_a_kind_of(ActiveRecord::Relation)
    end

    context 'given 3 availabilites that belong to 3 different people' do
      let(:people) { create_list(:person, 3) }
      let!(:first_availability)  { create(:availability, person: people[0],
                                   start_time: 6.hours.ago, end_time: 3.hours.ago) }
      let!(:second_availability) { create(:availability, person: people[1],
                                   start_time: 4.hours.ago, end_time: 1.hour.ago) }
      let!(:third_availability)  { create(:availability, person: people[2],
                                   start_time: 2.hours.ago, end_time: 1.hour.from_now) }

      it 'returns the availabilities that contain the given date_range' do
        date_range = 4.hours.ago..3.hours.ago
        expect(described_class.containing(date_range)).to contain_exactly(
          first_availability, second_availability)
      end

      it 'returns the availabilities matching the date_range' do
        date_range = second_availability.start_time..second_availability.end_time
        expect(described_class.containing(date_range)).to contain_exactly(
          second_availability)
      end

      it 'does not return availabilities that are contiguous to the given date_range' do
        date_range = first_availability.end_time..third_availability.start_time
        expect(described_class.containing(date_range)).not_to include(
          first_availability, third_availability)
      end
    end
  end

  context 'contained_in scope' do
    it 'returns a chainable relation' do
      expect(described_class.contained_in([0,1])).to be_a_kind_of(ActiveRecord::Relation)
    end

    context 'given 3 availabilites that belong to 3 different people' do
      let(:people) { create_list(:person, 3) }
      let!(:first_availability)  { create(:availability, person: people[0],
                                   start_time: 6.hours.ago, end_time: 3.hours.ago) }
      let!(:second_availability) { create(:availability, person: people[1],
                                   start_time: 4.hours.ago, end_time: 1.hour.ago) }
      let!(:third_availability)  { create(:availability, person: people[2],
                                   start_time: 2.hours.ago, end_time: 1.hour.from_now) }

      it 'returns the availabilities that fully contained by the given date_range' do
        date_range = 7.hours.ago..Time.now
        expect(described_class.contained_in(date_range)).to contain_exactly(
          first_availability, second_availability)
      end

      it 'returns the availabilities matching the date_range' do
        date_range = second_availability.start_time..second_availability.end_time
        expect(described_class.contained_in(date_range)).to contain_exactly(
          second_availability)
      end

      it 'does not return availabilities that are contiguous to the given date_range' do
        date_range = first_availability.end_time..third_availability.start_time
        expect(described_class.contained_in(date_range)).not_to include(
          first_availability, third_availability)
      end
    end
  end
end