# frozen_string_literal: true require 'spec_helper' RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failures do let(:fake_file_acquisition_strategy) do Class.new do attr_reader :errors def initialize(...) @errors = ActiveModel::Errors.new(self) end def valid? true end def project_params {} end end end let(:params) do { path: 'path', namespace: user.namespace, name: 'name' } end let_it_be(:user) { create(:user) } subject { described_class.new(user, params: params, file_acquisition_strategy: FakeStrategy) } before do stub_const('FakeStrategy', fake_file_acquisition_strategy) end describe 'validation' do it { expect(subject).to be_valid } it 'validates presence of path' do params[:path] = nil invalid = described_class.new(user, params: params, file_acquisition_strategy: FakeStrategy) expect(invalid).not_to be_valid expect(invalid.errors.full_messages).to include("Path can't be blank") end it 'validates presence of name' do params[:namespace] = nil invalid = described_class.new(user, params: params, file_acquisition_strategy: FakeStrategy) expect(invalid).not_to be_valid expect(invalid.errors.full_messages).to include("Namespace can't be blank") end it 'is invalid if the strategy is invalid' do expect_next_instance_of(FakeStrategy) do |strategy| allow(strategy).to receive(:valid?).and_return(false) allow(strategy).to receive(:errors).and_wrap_original do |original| original.call.tap do |errors| errors.add(:base, "some error") end end end invalid = described_class.new(user, params: params, file_acquisition_strategy: FakeStrategy) expect(invalid).not_to be_valid expect(invalid.errors.full_messages).to include("some error") expect(invalid.errors.full_messages).to include("some error") end end describe '#execute' do it 'creates a project successfully' do response = nil expect { response = subject.execute } .to change(Project, :count).by(1) expect(response).to be_success expect(response.http_status).to eq(:ok) expect(response.payload).to be_instance_of(Project) expect(response.payload.name).to eq('name') expect(response.payload.path).to eq('path') expect(response.payload.namespace).to eq(user.namespace) project = Project.last expect(project.name).to eq('name') expect(project.path).to eq('path') expect(project.namespace_id).to eq(user.namespace.id) expect(project.import_type).to eq('gitlab_project') end context 'when the project creation raises an error' do it 'fails to create a project' do expect_next_instance_of(Projects::GitlabProjectsImportService) do |service| expect(service).to receive(:execute).and_raise(StandardError, "failed to create project") end response = nil expect { response = subject.execute } .to change(Project, :count).by(0) expect(response).to be_error expect(response.http_status).to eq(:bad_request) expect(response.message).to eq("failed to create project") expect(response.payload).to eq(other_errors: []) end end context 'when the validation fail' do it 'fails to create a project' do params.delete(:path) response = nil expect { response = subject.execute } .to change(Project, :count).by(0) expect(response).to be_error expect(response.http_status).to eq(:bad_request) expect(response.message).to eq("Path can't be blank") expect(response.payload).to eq(other_errors: []) end context 'when the project contains multilple errors' do it 'fails to create a project' do params.merge!(name: '_ an invalid name _', path: '_ an invalid path _') response = nil expect { response = subject.execute } .to change(Project, :count).by(0) expect(response).to be_error expect(response.http_status).to eq(:bad_request) expect(response.message) .to eq(%{Project namespace path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'}) expect(response.payload).to eq(other_errors: [ %{Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'}, %{Path must not start or end with a special character and must not contain consecutive special characters.} ]) end end end context 'when the strategy adds project parameters' do before do expect_next_instance_of(FakeStrategy) do |strategy| expect(strategy).to receive(:project_params).and_return(name: 'the strategy name') end subject.valid? end it 'merges the strategy project parameters' do response = nil expect { response = subject.execute } .to change(Project, :count).by(1) expect(response).to be_success expect(response.http_status).to eq(:ok) expect(response.payload).to be_instance_of(Project) expect(response.payload.name).to eq('the strategy name') expect(response.payload.path).to eq('path') expect(response.payload.namespace).to eq(user.namespace) project = Project.last expect(project.name).to eq('the strategy name') expect(project.path).to eq('path') expect(project.namespace_id).to eq(user.namespace.id) expect(project.import_type).to eq('gitlab_project') end end end end