# frozen_string_literal: true

RSpec.shared_examples 'LFS http 200 response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :ok }
  end
end

RSpec.shared_examples 'LFS http 200 blob response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :ok }
    let(:content_type) { ::Repositories::LfsApiController::LFS_TRANSFER_CONTENT_TYPE }
    let(:response_headers) { { 'X-Sendfile' => lfs_object.file.path } }
  end
end

RSpec.shared_examples 'LFS http 200 workhorse response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :ok }
    let(:content_type) { Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE }
  end
end

RSpec.shared_examples 'LFS http 401 response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :unauthorized }
    let(:content_type) { 'text/plain' }
  end
end

RSpec.shared_examples 'LFS http 403 response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :forbidden }
    let(:message) { 'Access forbidden. Check your access level.' }
  end
end

RSpec.shared_examples 'LFS http 501 response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :not_implemented }
    let(:message) { 'Git LFS is not enabled on this GitLab server, contact your admin.' }
  end
end

RSpec.shared_examples 'LFS http 404 response' do
  it_behaves_like 'LFS http expected response code and message' do
    let(:response_code) { :not_found }
  end
end

RSpec.shared_examples 'LFS http expected response code and message' do
  let(:response_code) {}
  let(:response_headers) { {} }
  let(:content_type) { LfsRequest::CONTENT_TYPE }
  let(:message) {}

  specify do
    expect(response).to have_gitlab_http_status(response_code)
    expect(response.headers.to_hash).to include(response_headers)
    expect(response.media_type).to match(content_type)
    expect(json_response['message']).to eq(message) if message
  end
end

RSpec.shared_examples 'LFS http requests' do
  include LfsHttpHelpers

  let(:lfs_enabled) { true }
  let(:authorize_guest) {}
  let(:authorize_download) {}
  let(:authorize_upload) {}

  let(:lfs_object) { create(:lfs_object, :with_file) }
  let(:sample_oid) { lfs_object.oid }
  let(:sample_size) { lfs_object.size }
  let(:sample_object) { { 'oid' => sample_oid, 'size' => sample_size } }
  let(:non_existing_object_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
  let(:non_existing_object_size) { 1575078 }
  let(:non_existing_object) { { 'oid' => non_existing_object_oid, 'size' => non_existing_object_size } }
  let(:multiple_objects) { [sample_object, non_existing_object] }

  let(:authorization) { authorize_user }
  let(:headers) do
    {
      'Authorization' => authorization,
      'X-Sendfile-Type' => 'X-Sendfile'
    }
  end

  let(:request_download) do
    get objects_url(container, sample_oid), params: {}, headers: headers
  end

  let(:request_upload) do
    post_lfs_json batch_url(container), upload_body(multiple_objects), headers
  end

  before do
    stub_lfs_setting(enabled: lfs_enabled)
  end

  context 'when LFS is disabled globally' do
    let(:lfs_enabled) { false }

    describe 'download request' do
      before do
        request_download
      end

      it_behaves_like 'LFS http 501 response'
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 501 response'
    end
  end

  context 'unauthenticated' do
    let(:headers) { {} }

    describe 'download request' do
      before do
        request_download
      end

      it_behaves_like 'LFS http 401 response'
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 401 response'
    end
  end

  context 'without access' do
    describe 'download request' do
      before do
        request_download
      end

      it_behaves_like 'LFS http 404 response'
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 404 response'
    end
  end

  context 'with guest access' do
    before do
      authorize_guest
    end

    describe 'download request' do
      before do
        request_download
      end

      it_behaves_like 'LFS http 404 response'
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 404 response'
    end
  end

  context 'with download permission' do
    before do
      authorize_download
      project.lfs_objects << lfs_object if defined?(project) && project
    end

    describe 'download request' do
      before do
        request_download
      end

      it_behaves_like 'LFS http 200 blob response'

      context 'when container does not exist' do
        def objects_url(*args)
          super.sub(container.full_path, 'missing/path')
        end

        it_behaves_like 'LFS http 404 response'
      end
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 403 response'
    end
  end

  context 'with upload permission' do
    before do
      authorize_upload
    end

    describe 'upload request' do
      before do
        request_upload
      end

      it_behaves_like 'LFS http 200 response'
    end
  end

  describe 'deprecated API' do
    shared_examples 'deprecated request' do
      before do
        request
      end

      it_behaves_like 'LFS http expected response code and message' do
        let(:response_code) { 501 }
        let(:message) { 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.' }
      end
    end

    context 'when fetching LFS object using deprecated API' do
      subject(:request) do
        get deprecated_objects_url(container, sample_oid), params: {}, headers: headers
      end

      it_behaves_like 'deprecated request'
    end

    context 'when handling LFS request using deprecated API' do
      subject(:request) do
        post_lfs_json deprecated_objects_url(container), nil, headers
      end

      it_behaves_like 'deprecated request'
    end

    def deprecated_objects_url(container, oid = nil)
      File.join(["#{container.http_url_to_repo}/info/lfs/objects/", oid].compact)
    end
  end
end
