# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Dast::Profile, type: :model do
  let_it_be(:project) { create(:project) }

  subject { create(:dast_profile, project: project) }

  describe 'associations' do
    it { is_expected.to belong_to(:project) }
    it { is_expected.to belong_to(:dast_site_profile) }
    it { is_expected.to belong_to(:dast_scanner_profile) }
    it { is_expected.to have_many(:secret_variables).through(:dast_site_profile).class_name('Dast::SiteProfileSecretVariable') }
    it { is_expected.to have_many(:dast_profiles_pipelines).class_name('Dast::ProfilesPipeline').with_foreign_key(:dast_profile_id).inverse_of(:dast_profile) }
    it { is_expected.to have_many(:ci_pipelines).through(:dast_profiles_pipelines).class_name('Ci::Pipeline') }
    it { is_expected.to have_many(:dast_profile_schedules).class_name('Dast::ProfileSchedule').with_foreign_key(:dast_profile_id).inverse_of(:dast_profile) }
  end

  describe 'validations' do
    it { is_expected.to be_valid }
    it { is_expected.to validate_length_of(:name).is_at_most(255) }
    it { is_expected.to validate_length_of(:description).is_at_most(255) }
    it { is_expected.to validate_length_of(:branch_name).is_at_most(255) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
    it { is_expected.to validate_presence_of(:project_id) }
    it { is_expected.to validate_presence_of(:dast_site_profile_id) }
    it { is_expected.to validate_presence_of(:dast_scanner_profile_id) }
    it { is_expected.to validate_presence_of(:name) }

    shared_examples 'the project_id does not match' do
      let(:association_name) { association.class.underscore }

      subject { build(:dast_profile, project: project, association_name => association) }

      before do
        association.project_id = non_existing_record_id
      end

      it 'is not valid', :aggregate_failures do
        expect(subject).not_to be_valid
        expect(subject.errors.full_messages).to include("Project must match #{association_name}.project_id")
      end
    end

    context 'when the project_id and dast_site_profile.project_id do not match' do
      let(:association) { build(:dast_site_profile) }

      it_behaves_like 'the project_id does not match'
    end

    context 'when the project_id and dast_scanner_profile.project_id do not match' do
      let(:association) { build(:dast_scanner_profile) }

      it_behaves_like 'the project_id does not match'
    end

    context 'when the description is nil' do
      subject { build(:dast_profile, description: nil) }

      it 'is not valid' do
        aggregate_failures do
          expect(subject).not_to be_valid
          expect(subject.errors.full_messages).to include('Description can\'t be nil')
        end
      end
    end

    context 'when a branch_name is specified but the project does not have a respository' do
      subject { build(:dast_profile, branch_name: SecureRandom.hex) }

      it 'is not valid', :aggregate_failures do
        expect(subject).not_to be_valid
        expect(subject.errors.full_messages).to include('Project must have a repository')
        expect(subject.errors.full_messages).not_to include('Branch name can\'t reference a branch that does not exist')
      end
    end

    context 'when a branch_name is specified but the project does not have a respository' do
      let_it_be(:project) { create(:project, :repository) }

      subject { build(:dast_profile, project: project, branch_name: SecureRandom.hex) }

      it 'is not valid', :aggregate_failures do
        expect(subject).not_to be_valid
        expect(subject.errors.full_messages).not_to include('Project must have a repository')
        expect(subject.errors.full_messages).to include('Branch name can\'t reference a branch that does not exist')
      end
    end
  end

  describe 'scopes' do
    describe 'by_project_id' do
      it 'includes the correct records' do
        another_dast_profile = create(:dast_profile)

        result = described_class.by_project_id(subject.project_id)

        aggregate_failures do
          expect(result).to include(subject)
          expect(result).not_to include(another_dast_profile)
        end
      end
    end
  end

  describe 'instance methods' do
    describe '#branch' do
      context 'when the associated project does not have a repository' do
        it 'returns nil' do
          expect(subject.branch).to be_nil
        end
      end

      context 'when the associated project has a repository' do
        let_it_be(:project) { create(:project, :repository) }

        subject { create(:dast_profile, project: project) }

        it 'returns a Dast::Branch' do
          expect(subject.branch).to be_a(Dast::Branch)
        end
      end
    end

    describe '#secret_ci_variables' do
      it { is_expected.to delegate_method(:secret_ci_variables).to(:dast_site_profile) }
    end
  end
end