# frozen_string_literal: true

require 'spec_helper'

describe Security::VulnerabilityFindingsFinder do
  describe '#execute' do
    let_it_be(:project1) { create(:project, :private, :repository) }
    let_it_be(:project2) { create(:project, :private, :repository) }
    let_it_be(:pipeline1) { create(:ci_pipeline, :success, project: project1) }
    let_it_be(:pipeline2) { create(:ci_pipeline, :success, project: project2) }
    let(:pipelines) { [pipeline1, pipeline2] }

    let_it_be(:finding1) { create(:vulnerabilities_occurrence, report_type: :sast, severity: :high, confidence: :high, pipelines: [pipeline1], project: project1) }
    let_it_be(:finding2) { create(:vulnerabilities_occurrence, report_type: :dependency_scanning, severity: :medium, confidence: :low, pipelines: [pipeline2], project: project2) }
    let_it_be(:finding3) { create(:vulnerabilities_occurrence, report_type: :sast, severity: :low, pipelines: [pipeline2], project: project2) }
    let_it_be(:finding4) { create(:vulnerabilities_occurrence, report_type: :dast, severity: :medium, pipelines: [pipeline1], project: project1) }

    let(:params) { {} }
    let(:include_sha) { false }

    subject { described_class.new(pipelines, params: params, include_sha: include_sha).execute }

    context 'when given a pipeline' do
      let(:pipelines) { [pipeline1] }

      it "returns vulnerability findings found in the pipeline" do
        is_expected.to contain_exactly(finding1, finding4)
      end
    end

    context 'include_sha' do
      context 'when false' do
        it 'does not include a sha' do
          expect(subject.map(&:sha)).to be_all(&:nil?)
        end
      end

      context 'when true' do
        let(:include_sha) { true }

        it 'includes the sha for the latest pipeline associated with the finding' do
          subject.each do |finding|
            expect(finding.sha).to eq(finding.pipelines.last.sha)
          end
        end
      end
    end

    context 'by report type' do
      context 'when sast' do
        let(:params) { { report_type: %w[sast] } }

        it 'includes only sast' do
          is_expected.to contain_exactly(finding1, finding3)
        end
      end

      context 'when dependency_scanning' do
        let(:params) { { report_type: %w[dependency_scanning] } }

        it 'includes only depscan' do
          is_expected.to contain_exactly(finding2)
        end
      end
    end

    context 'by severity' do
      context 'when high' do
        let(:params) { { severity: %w[high] } }

        it 'includes only high' do
          is_expected.to contain_exactly(finding1)
        end
      end

      context 'when medium' do
        let(:params) { { severity: %w[medium] } }

        it 'includes only medium' do
          is_expected.to contain_exactly(finding2, finding4)
        end
      end
    end

    context 'by confidence' do
      context 'when high' do
        let(:params) { { confidence: %w[high] } }

        it 'includes only high confidence vulnerabilities' do
          is_expected.to contain_exactly(finding1)
        end
      end

      context 'when low' do
        let(:params) { { confidence: %w[low] } }

        it 'includes only low confidence vulnerabilities' do
          is_expected.to contain_exactly(finding2)
        end
      end
    end

    context 'by project' do
      let(:params) { { project_id: [project2.id] } }

      it 'includes only vulnerabilities for one project' do
        is_expected.to contain_exactly(finding2, finding3)
      end
    end

    context 'by all filters' do
      context 'with found entity' do
        let(:params) { { severity: %w[high medium low], project_id: [project1.id, project2.id], report_type: %w[sast dast], scope: 'all' } }

        before do
          create(:vulnerability_feedback, :sast, :dismissal,
             pipeline: pipeline1,
             project: project1,
             project_fingerprint: finding1.project_fingerprint)
        end

        it 'filters by all params' do
          is_expected.to contain_exactly(finding1, finding3, finding4)
        end
      end

      context 'without found entity' do
        let(:params) { { severity: %w[low], project_id: [project1.id], report_type: %w[sast] } }

        it 'did not find anything' do
          is_expected.to be_empty
        end
      end
    end

    context 'by scope' do
      let!(:dismissal) do
        create(:vulnerability_feedback, :sast, :dismissal,
               pipeline: pipeline1,
               project: project1,
               project_fingerprint: finding1.project_fingerprint)
      end
      let!(:issue) do
        create(:vulnerability_feedback, :sast, :issue,
               pipeline: pipeline1,
               project: project1,
               project_fingerprint: finding4.project_fingerprint)
      end

      context 'when all' do
        let(:params) { { scope: 'all' } }

        it 'includes all vulnerabilities' do
          is_expected.to contain_exactly(finding1, finding2, finding3, finding4)
        end
      end

      context 'when not specified' do
        let(:params) { {} }

        it 'excludes dismissed vulnerabilities' do
          is_expected.to contain_exactly(finding2, finding3, finding4)
        end
      end

      context 'when dismissed' do
        let(:params) { { scope: 'dismissed' } }

        it 'excludes dismissed vulnerabilities' do
          is_expected.to contain_exactly(finding2, finding3, finding4)
        end
      end
    end

    context 'by some filters' do
      context 'with found entity' do
        let(:params) { { project_id: [project2.id], severity: %w[medium low] } }

        it 'filters by all params' do
          is_expected.to contain_exactly(finding2, finding3)
        end
      end

      context 'without found entity' do
        let(:params) { { project_id: project1.id, severity: %w[low] } }

        it 'did not find anything' do
          is_expected.to be_empty
        end
      end
    end
  end
end
