# frozen_string_literal: true

require 'spec_helper'

describe Vulnerability do
  let(:state_values) { { opened: 1, closed: 2, resolved: 3 } }
  let(:severity_values) { { undefined: 0, info: 1, unknown: 2, low: 4, medium: 5, high: 6, critical: 7 } }

  let(:confidence_values) do
    { undefined: 0, ignore: 1, unknown: 2, experimental: 3, low: 4, medium: 5, high: 6, confirmed: 7 }
  end
  let(:report_types) do
    { sast: 0,
      dependency_scanning: 1,
      container_scanning: 2,
      dast: 3 }
  end

  it { is_expected.to define_enum_for(:state).with_values(state_values) }
  it { is_expected.to define_enum_for(:severity).with_values(severity_values).with_prefix(:severity) }
  it { is_expected.to define_enum_for(:confidence).with_values(confidence_values).with_prefix(:confidence) }
  it { is_expected.to define_enum_for(:report_type).with_values(report_types) }

  describe 'associations' do
    subject { build(:vulnerability) }

    it { is_expected.to belong_to(:project) }
    it { is_expected.to belong_to(:milestone) }
    it { is_expected.to belong_to(:epic) }
    it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Occurrence').inverse_of(:vulnerability) }
    it { is_expected.to have_many(:issue_links).class_name('Vulnerabilities::IssueLink').inverse_of(:vulnerability) }
    it { is_expected.to have_many(:related_issues).through(:issue_links).source(:issue) }
    it { is_expected.to belong_to(:author).class_name('User') }
    it { is_expected.to belong_to(:updated_by).class_name('User') }
    it { is_expected.to belong_to(:last_edited_by).class_name('User') }
    it { is_expected.to belong_to(:resolved_by).class_name('User') }
    it { is_expected.to belong_to(:closed_by).class_name('User') }

    it { is_expected.to have_many(:findings).class_name('Vulnerabilities::Occurrence').dependent(false) }
  end

  describe 'validations' do
    subject { build(:vulnerability) }

    it { is_expected.to validate_presence_of(:project) }
    it { is_expected.to validate_presence_of(:author) }
    it { is_expected.to validate_presence_of(:title) }
    it { is_expected.to validate_presence_of(:severity) }
    it { is_expected.to validate_presence_of(:confidence) }
    it { is_expected.to validate_presence_of(:report_type) }

    it { is_expected.to validate_length_of(:title).is_at_most(::Issuable::TITLE_LENGTH_MAX) }
    it { is_expected.to validate_length_of(:title_html).is_at_most(::Issuable::TITLE_HTML_LENGTH_MAX) }
    it { is_expected.to validate_length_of(:description).is_at_most(::Issuable::DESCRIPTION_LENGTH_MAX).allow_nil }
    it { is_expected.to validate_length_of(:description_html).is_at_most(::Issuable::DESCRIPTION_HTML_LENGTH_MAX).allow_nil }
  end

  describe 'text fields' do
    subject { create(:vulnerability, title: '_My title_ ', description: '**Hello `world`**') }

    it 'has proper markdown for title field' do
      expect(subject.title_html).to eq('_My title_') # no markdown rendering because it's a single line field
    end

    it 'has proper markdown for title field' do
      expect(subject.description_html).to(
        eq('<p data-sourcepos="1:1-1:17" dir="auto"><strong>Hello <code>world</code></strong></p>')
      )
    end

    context 'redactable fields' do
      before do
        stub_commonmark_sourcepos_disabled
      end

      it_behaves_like 'model with redactable field' do
        let(:model) { create(:vulnerability) }
        let(:field) { :description }
      end
    end
  end

  describe '#finding' do
    let_it_be(:project) { create(:project, :with_vulnerabilities) }
    let_it_be(:vulnerability) { project.vulnerabilities.first }
    let_it_be(:finding1) { create(:vulnerabilities_occurrence, vulnerability: vulnerability) }
    let_it_be(:finding2) { create(:vulnerabilities_occurrence, vulnerability: vulnerability) }

    subject { vulnerability.finding }

    context 'with multiple findings' do
      it { is_expected.to eq(finding1) }
    end
  end
end
