# frozen_string_literal: true

require 'spec_helper'

describe Projects::VulnerabilityFeedbackController do
  let(:group)   { create(:group) }
  let(:project) { create(:project, :public, namespace: group) }
  let(:user)    { create(:user) }
  let(:guest)   { create(:user) }

  before do
    group.add_developer(user)
  end

  describe 'GET #index' do
    let(:pipeline_1) { create(:ci_pipeline, project: project) }
    let(:pipeline_2) { create(:ci_pipeline, project: project) }

    let(:merge_request) { create(:merge_request, source_project: project) }

    let!(:vuln_feedback_1) { create(:vulnerability_feedback, :dismissal, :sast, project: project, author: user, pipeline: pipeline_1) }
    let!(:vuln_feedback_2) { create(:vulnerability_feedback, :issue, :sast, project: project, author: user, pipeline: pipeline_1) }
    let!(:vuln_feedback_3) { create(:vulnerability_feedback, :dismissal, :sast, project: project, author: user, pipeline: pipeline_2) }
    let!(:vuln_feedback_4) { create(:vulnerability_feedback, :dismissal, :dependency_scanning, project: project, author: user, pipeline: pipeline_2) }
    let!(:vuln_feedback_5) { create(:vulnerability_feedback, :merge_request, :dependency_scanning, project: project, author: user, pipeline: pipeline_1, merge_request: merge_request) }

    context '@vulnerability_feedback' do
      it 'returns a successful 200 response' do
        list_feedbacks

        expect(response).to have_gitlab_http_status(:ok)
      end

      it 'returns project feedbacks list' do
        list_feedbacks

        expect(response).to match_response_schema('vulnerability_feedback_list', dir: 'ee')
        expect(json_response.length).to eq 5
      end

      context 'with filter params' do
        it 'returns project feedbacks list filtered on category' do
          list_feedbacks({ category: 'sast' })

          expect(response).to match_response_schema('vulnerability_feedback_list', dir: 'ee')
          expect(json_response.length).to eq 3
        end

        it 'returns project feedbacks list filtered on feedback_type' do
          list_feedbacks({ feedback_type: 'issue' })

          expect(response).to match_response_schema('vulnerability_feedback_list', dir: 'ee')
          expect(json_response.length).to eq 1
        end

        it 'returns project feedbacks list filtered on category and feedback_type' do
          list_feedbacks({ category: 'sast', feedback_type: 'dismissal' })

          expect(response).to match_response_schema('vulnerability_feedback_list', dir: 'ee')
          expect(json_response.length).to eq 2
        end
      end

      context 'with unauthorized user for given project' do
        let(:unauthorized_user) { create(:user) }
        let(:project) { create(:project, :private, namespace: group) }

        before do
          sign_in(unauthorized_user)
        end

        it 'returns a 404 response' do
          list_feedbacks

          expect(response).to have_gitlab_http_status(:not_found)
        end
      end
    end

    def list_feedbacks(params = {})
      get :index, params: { namespace_id: project.namespace.to_param, project_id: project }.merge(params)
    end
  end

  describe 'POST #create' do
    let(:pipeline) { create(:ci_pipeline, project: project) }
    let(:create_params) do
      {
        feedback_type: 'dismissal', pipeline_id: pipeline.id, category: 'sast',
        comment: 'a dismissal comment',
        project_fingerprint: '418291a26024a1445b23fe64de9380cdcdfd1fa8',
        vulnerability_data: {
          category: 'sast',
          severity: 'Low',
          confidence: 'Medium',
          cve: '818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM',
          title: 'Predictable pseudorandom number generator',
          description: 'Description of Predictable pseudorandom number generator',
          tool: 'find_sec_bugs',
          location: {
            file: 'subdir/src/main/java/com/gitlab/security_products/tests/App.java',
            start_line: '41'
          },
          identifiers: [{
            type: 'CVE',
            name: 'CVE-2018-1234',
            value: 'CVE-2018-1234',
            url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1234'
          }],
          links: [{
            name: 'Awesome-security blog post',
            url: 'https;//example.com/blog-post'
          }]
        }
      }
    end

    context 'with valid params' do
      it 'returns the created feedback' do
        allow(VulnerabilityFeedback::CreateService)
          .to receive(:new).with(project, user, ActionController::Parameters.new(create_params).permit!)
          .and_call_original

        create_feedback user: user, project: project, params: create_params

        expect(response).to match_response_schema('vulnerability_feedback', dir: 'ee')
      end
    end

    context 'with invalid params' do
      it 'returns a forbidden 403 response when feedbback_type is nil' do
        create_feedback user: user, project: project, params: create_params.except(:feedback_type)

        expect(response).to have_gitlab_http_status(:forbidden)
      end

      it 'returns a forbidden 403 response when feedbback_type is invalid' do
        create_feedback user: user, project: project, params: create_params.merge(feedback_type: 'foo')

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    context 'with unauthorized user for feedback creation' do
      context 'for issue feedback' do
        it 'returns a forbidden 403 response' do
          group.add_guest(guest)

          create_feedback user: guest, project: project, params: create_params.merge(feedback_type: 'issue')

          expect(response).to have_gitlab_http_status(:forbidden)
        end
      end

      context 'for merge_request feedback' do
        it 'returns a forbidden 403 response' do
          group.add_guest(guest)

          create_feedback user: guest, project: project, params: create_params.merge(feedback_type: 'merge_request')

          expect(response).to have_gitlab_http_status(:forbidden)
        end
      end

      context 'for dismissal feedback' do
        it 'returns a forbidden 403 response' do
          group.add_guest(guest)

          create_feedback user: guest, project: project, params: create_params.merge(feedback_type: 'dismissal')

          expect(response).to have_gitlab_http_status(:forbidden)
        end
      end
    end

    context 'with unauthorized user for given project' do
      let(:unauthorized_user) { create(:user) }
      let(:project) { create(:project, :private, namespace: group) }

      it 'returns a 404 response' do
        create_feedback user: unauthorized_user, project: project, params: create_params

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    def create_feedback(user:, project:, params:)
      sign_in(user)
      post_params = {
        namespace_id: project.namespace.to_param,
        project_id: project,
        vulnerability_feedback: params,
        format: :json
      }

      post :create, params: post_params, as: :json
    end
  end

  describe 'PATCH #update' do
    let(:vuln_feedback) do
      create(:vulnerability_feedback, :dismissal, :sast, :comment,
             project: project,
             author: user
      )
    end

    context 'with valid params' do
      before do
        vuln_feedback.comment = 'new dismissal comment'
        update_feedback user: user, params: vuln_feedback
      end

      it 'returns the updated feedback' do
        expect(response).to match_response_schema('vulnerability_feedback', dir: 'ee')
      end

      it 'returns a successful 200 response' do
        expect(response).to have_gitlab_http_status(:ok)
      end

      it 'updates the comment attributes' do
        expect(vuln_feedback.reload.comment).to eq('new dismissal comment')
      end
    end

    context 'with invalid params' do
      it 'returns a not found 404 response for invalid vulnerability feedback id' do
        vuln_feedback.id = 123
        update_feedback user: user, params: vuln_feedback

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    context 'with unauthorized user for feedback update' do
      it 'returns a forbidden 403 response' do
        group.add_guest(guest)

        update_feedback user: guest, params: vuln_feedback

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    context 'with unauthorized user for given project' do
      let(:unauthorized_user) { create(:user) }
      let(:project) { create(:project, :private, namespace: group) }

      it 'returns a 404 response' do
        update_feedback user: unauthorized_user, params: vuln_feedback

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    def update_feedback(user:, params:)
      sign_in(user)

      patch :update, params: {
                       namespace_id: project.namespace.to_param,
                       project_id: project,
                       id: params[:id],
                       vulnerability_feedback: params
                     }, as: :json
    end
  end

  describe 'DELETE #destroy' do
    let(:pipeline) { create(:ci_pipeline, project: project) }
    let!(:vuln_feedback) { create(:vulnerability_feedback, :dismissal, :sast, project: project, author: user, pipeline: pipeline) }

    context 'with valid params' do
      it 'returns a successful 204 response' do
        destroy_feedback user: user, project: project, id: vuln_feedback.id

        expect(response).to have_gitlab_http_status(:no_content)
      end
    end

    context 'with invalid params' do
      it 'returns a not found 404 response for invalid vulnerability feedback id' do
        destroy_feedback user: user, project: project, id: 123

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    context 'with unauthorized user for feedback deletion' do
      it 'returns a forbidden 403 response' do
        group.add_guest(guest)

        destroy_feedback user: guest, project: project, id: vuln_feedback.id

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    context 'with unauthorized user for given project' do
      let(:unauthorized_user) { create(:user) }
      let(:project) { create(:project, :private, namespace: group) }

      it 'returns a 404 response' do
        destroy_feedback user: unauthorized_user, project: project, id: vuln_feedback.id

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    context 'for issue feedback' do
      let!(:vuln_feedback) { create(:vulnerability_feedback, :issue, :sast, project: project, author: user, pipeline: pipeline) }

      it 'returns a forbidden 403 response' do
        destroy_feedback user: user, project: project, id: vuln_feedback.id

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    context 'for merge_request feedback' do
      let!(:vuln_feedback) { create(:vulnerability_feedback, :merge_request, :sast, project: project, author: user, pipeline: pipeline) }

      it 'returns a forbidden 403 response' do
        destroy_feedback user: user, project: project, id: vuln_feedback.id

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end

    def destroy_feedback(user:, project:, id:)
      sign_in(user)

      delete :destroy, params: { namespace_id: project.namespace.to_param, project_id: project, id: id }
    end
  end
end
