# frozen_string_literal: true

require 'spec_helper'

describe ZoomMeeting do
  let(:project) { build(:project) }

  describe 'Factory' do
    subject { build(:zoom_meeting) }

    it { is_expected.to be_valid }
  end

  describe 'Associations' do
    it { is_expected.to belong_to(:project).required }
    it { is_expected.to belong_to(:issue).required }
  end

  describe 'scopes' do
    let(:issue) { create(:issue, project: project) }
    let!(:added_meeting) { create(:zoom_meeting, :added_to_issue, issue: issue) }
    let!(:removed_meeting) { create(:zoom_meeting, :removed_from_issue, issue: issue) }

    describe '.added_to_issue' do
      it 'gets only added meetings' do
        meetings_added = described_class.added_to_issue.pluck(:id)

        expect(meetings_added).to include(added_meeting.id)
        expect(meetings_added).not_to include(removed_meeting.id)
      end
    end
    describe '.removed_from_issue' do
      it 'gets only removed meetings' do
        meetings_removed = described_class.removed_from_issue.pluck(:id)

        expect(meetings_removed).to include(removed_meeting.id)
        expect(meetings_removed).not_to include(added_meeting.id)
      end
    end
  end

  describe 'Validations' do
    describe 'url' do
      it { is_expected.to validate_presence_of(:url) }
      it { is_expected.to validate_length_of(:url).is_at_most(255) }

      shared_examples 'invalid Zoom URL' do
        it do
          expect(subject).to be_invalid
          expect(subject.errors[:url])
            .to contain_exactly('must contain one valid Zoom URL')
        end
      end

      context 'with non-Zoom URL' do
        before do
          subject.url = %{https://non-zoom.url}
        end

        include_examples 'invalid Zoom URL'
      end

      context 'with multiple Zoom-URLs' do
        before do
          subject.url = %{https://zoom.us/j/123 https://zoom.us/j/456}
        end

        include_examples 'invalid Zoom URL'
      end
    end

    describe 'issue association' do
      let(:issue) { build(:issue, project: project) }

      subject { build(:zoom_meeting, project: project, issue: issue) }

      context 'for the same project' do
        it { is_expected.to be_valid }
      end

      context 'for a different project' do
        let(:issue) { build(:issue) }

        it do
          expect(subject).to be_invalid
          expect(subject.errors[:issue])
            .to contain_exactly('must associate the same project')
        end
      end
    end
  end

  describe 'limit number of meetings per issue' do
    shared_examples 'can add meetings' do
      it 'can add new Zoom meetings' do
        create(:zoom_meeting, :added_to_issue, issue: issue)
      end
    end

    shared_examples 'can remove meetings' do
      it 'can remove Zoom meetings' do
        create(:zoom_meeting, :removed_from_issue, issue: issue)
      end
    end

    shared_examples 'cannot add meetings' do
      it 'fails to add a new meeting' do
        expect do
          create(:zoom_meeting, :added_to_issue, issue: issue)
        end.to raise_error ActiveRecord::RecordNotUnique
      end
    end

    let(:issue) { create(:issue, project: project) }

    context 'without meetings' do
      it_behaves_like 'can add meetings'
    end

    context 'when no other meeting is added' do
      before do
        create(:zoom_meeting, :removed_from_issue, issue: issue)
      end

      it_behaves_like 'can add meetings'
    end

    context 'when meeting is added' do
      before do
        create(:zoom_meeting, :added_to_issue, issue: issue)
      end

      it_behaves_like 'cannot add meetings'
    end

    context 'when meeting is added to another issue' do
      let(:another_issue) { create(:issue, project: project) }

      before do
        create(:zoom_meeting, :added_to_issue, issue: another_issue)
      end

      it_behaves_like 'can add meetings'
    end

    context 'when second meeting is removed' do
      before do
        create(:zoom_meeting, :removed_from_issue, issue: issue)
      end

      it_behaves_like 'can remove meetings'
    end
  end
end
