diff options
Diffstat (limited to 'spec/services')
144 files changed, 2638 insertions, 930 deletions
diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb index 87f093ee8ce..38a3f522504 100644 --- a/spec/services/access_token_validation_service_spec.rb +++ b/spec/services/access_token_validation_service_spec.rb @@ -1,41 +1,72 @@ require 'spec_helper' -describe AccessTokenValidationService, services: true do +describe AccessTokenValidationService do describe ".include_any_scope?" do + let(:request) { double("request") } + it "returns true if the required scope is present in the token's scopes" do token = double("token", scopes: [:api, :read_user]) + scopes = [:api] - expect(described_class.new(token).include_any_scope?([:api])).to be(true) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) end it "returns true if more than one of the required scopes is present in the token's scopes" do token = double("token", scopes: [:api, :read_user, :other_scope]) + scopes = [:api, :other_scope] - expect(described_class.new(token).include_any_scope?([:api, :other_scope])).to be(true) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) end it "returns true if the list of required scopes is an exact match for the token's scopes" do token = double("token", scopes: [:api, :read_user, :other_scope]) + scopes = [:api, :read_user, :other_scope] - expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).to be(true) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) end it "returns true if the list of required scopes contains all of the token's scopes, in addition to others" do token = double("token", scopes: [:api, :read_user]) + scopes = [:api, :read_user, :other_scope] - expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).to be(true) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) end it 'returns true if the list of required scopes is blank' do token = double("token", scopes: []) + scopes = [] - expect(described_class.new(token).include_any_scope?([])).to be(true) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) end it "returns false if there are no scopes in common between the required scopes and the token scopes" do token = double("token", scopes: [:api, :read_user]) + scopes = [:other_scope] + + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(false) + end + + context "conditions" do + it "ignores any scopes whose `if` condition returns false" do + token = double("token", scopes: [:api, :read_user]) + scopes = [API::Scope.new(:api, if: ->(_) { false })] + + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(false) + end + + it "does not ignore scopes whose `if` condition is not set" do + token = double("token", scopes: [:api, :read_user]) + scopes = [API::Scope.new(:api, if: ->(_) { false }), :read_user] + + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) + end + + it "does not ignore scopes whose `if` condition returns true" do + token = double("token", scopes: [:api, :read_user]) + scopes = [API::Scope.new(:api, if: ->(_) { true }), API::Scope.new(:read_user, if: ->(_) { false })] - expect(described_class.new(token).include_any_scope?([:other_scope])).to be(false) + expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true) + end end end end diff --git a/spec/services/after_branch_delete_service_spec.rb b/spec/services/after_branch_delete_service_spec.rb index 77ca17bc82c..bc9747d1413 100644 --- a/spec/services/after_branch_delete_service_spec.rb +++ b/spec/services/after_branch_delete_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AfterBranchDeleteService, services: true do +describe AfterBranchDeleteService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index e273dfe1552..d23c09d6d1d 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Auth::ContainerRegistryAuthenticationService, services: true do +describe Auth::ContainerRegistryAuthenticationService do let(:current_project) { nil } let(:current_user) { nil } let(:current_params) { {} } @@ -34,7 +34,9 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for changed configuration' do - before { stub_application_setting(container_registry_token_expire_delay: expire_delay) } + before do + stub_application_setting(container_registry_token_expire_delay: expire_delay) + end it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) } end @@ -44,7 +46,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do shared_examples 'an accessible' do let(:access) do [{ 'type' => 'repository', - 'name' => project.path_with_namespace, + 'name' => project.full_path, 'actions' => actions }] end @@ -94,8 +96,8 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end describe '#full_access_token' do - let(:project) { create(:empty_project) } - let(:token) { described_class.full_access_token(project.path_with_namespace) } + let(:project) { create(:project) } + let(:token) { described_class.full_access_token(project.full_path) } subject { { token: token } } @@ -110,17 +112,19 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do let(:current_user) { create(:user) } context 'for private project' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } context 'allow to use scope-less authentication' do it_behaves_like 'a valid token' end context 'allow developer to push images' do - before { project.team << [current_user, :developer] } + before do + project.team << [current_user, :developer] + end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a pushable' @@ -128,11 +132,13 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'allow reporter to pull images' do - before { project.team << [current_user, :reporter] } + before do + project.team << [current_user, :reporter] + end context 'when pulling from root level repository' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -141,10 +147,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'return a least of privileges' do - before { project.team << [current_user, :reporter] } + before do + project.team << [current_user, :reporter] + end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push,pull" } + { scope: "repository:#{project.full_path}:push,pull" } end it_behaves_like 'a pullable' @@ -152,10 +160,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'disallow guest to pull or push images' do - before { project.team << [current_user, :guest] } + before do + project.team << [current_user, :guest] + end let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -164,11 +174,11 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for public project' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -177,7 +187,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -195,12 +205,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for internal project' do - let(:project) { create(:empty_project, :internal) } + let(:project) { create(:project, :internal) } context 'for internal user' do context 'allow anyone to pull images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a pullable' @@ -209,7 +219,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'disallow anyone to push images' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'an inaccessible' @@ -220,7 +230,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'for external user' do let(:current_user) { create(:user, external: true) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'an inaccessible' @@ -230,7 +240,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'build authorized as user' do - let(:current_project) { create(:empty_project) } + let(:current_project) { create(:project) } let(:current_user) { create(:user) } let(:authentication_abilities) do @@ -245,7 +255,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'allow to pull and push images' do let(:current_params) do - { scope: "repository:#{current_project.path_with_namespace}:pull,push" } + { scope: "repository:#{current_project.full_path}:pull,push" } end it_behaves_like 'a pullable and pushable' do @@ -260,11 +270,11 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'for other projects' do context 'when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end context 'allow for public' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } it_behaves_like 'a pullable' it_behaves_like 'not a container repository factory' @@ -286,7 +296,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'when you are owner' do - let(:project) { create(:empty_project, namespace: current_user.namespace) } + let(:project) { create(:project, namespace: current_user.namespace) } it_behaves_like 'a pullable' it_behaves_like 'not a container repository factory' @@ -294,7 +304,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for private' do - let(:project) { create(:empty_project, :private) } + let(:project) { create(:project, :private) } it_behaves_like 'pullable for being team member' @@ -316,7 +326,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'when you are owner' do - let(:project) { create(:empty_project, namespace: current_user.namespace) } + let(:project) { create(:project, namespace: current_user.namespace) } it_behaves_like 'a pullable' it_behaves_like 'not a container repository factory' @@ -327,12 +337,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end context 'disallow for all' do context 'when you are member' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } before do project.team << [current_user, :developer] @@ -343,7 +353,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'when you are owner' do - let(:project) { create(:empty_project, :public, namespace: current_user.namespace) } + let(:project) { create(:project, :public, namespace: current_user.namespace) } it_behaves_like 'an inaccessible' it_behaves_like 'not a container repository factory' @@ -353,13 +363,15 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for project without container registry' do - let(:project) { create(:empty_project, :public, container_registry_enabled: false) } + let(:project) { create(:project, :public, container_registry_enabled: false) } - before { project.update(container_registry_enabled: false) } + before do + project.update(container_registry_enabled: false) + end context 'disallow when pulling' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'an inaccessible' @@ -384,21 +396,21 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do end context 'for private project' do - let(:project) { create(:empty_project, :private) } + let(:project) { create(:project, :private) } let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull" } + { scope: "repository:#{project.full_path}:pull" } end it_behaves_like 'a forbidden' end context 'for public project' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } context 'when pulling and pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:pull,push" } + { scope: "repository:#{project.full_path}:pull,push" } end it_behaves_like 'a pullable' @@ -407,7 +419,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do context 'when pushing' do let(:current_params) do - { scope: "repository:#{project.path_with_namespace}:push" } + { scope: "repository:#{project.full_path}:push" } end it_behaves_like 'a forbidden' diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb index a8555f5b4a0..db51a524e79 100644 --- a/spec/services/boards/create_service_spec.rb +++ b/spec/services/boards/create_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::CreateService, services: true do +describe Boards::CreateService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } subject(:service) { described_class.new(project, double) } @@ -14,8 +14,9 @@ describe Boards::CreateService, services: true do it 'creates the default lists' do board = service.execute - expect(board.lists.size).to eq 1 - expect(board.lists.first).to be_closed + expect(board.lists.size).to eq 2 + expect(board.lists.first).to be_backlog + expect(board.lists.last).to be_closed end end @@ -25,6 +26,8 @@ describe Boards::CreateService, services: true do end it 'does not create a new board' do + expect(service).to receive(:can_create_board?) { false } + expect { service.execute }.not_to change(project.boards, :count) end end diff --git a/spec/services/boards/issues/create_service_spec.rb b/spec/services/boards/issues/create_service_spec.rb index 360ee398f77..f2ddaa903da 100644 --- a/spec/services/boards/issues/create_service_spec.rb +++ b/spec/services/boards/issues/create_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::Issues::CreateService, services: true do +describe Boards::Issues::CreateService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } let(:label) { create(:label, project: project, name: 'in-progress') } diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index c982031c791..01ee3856c99 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Boards::Issues::ListService, services: true do +describe Boards::Issues::ListService do describe '#execute' do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:bug) { create(:label, project: project, name: 'Bug') } @@ -13,13 +13,14 @@ describe Boards::Issues::ListService, services: true do let(:p2) { create(:label, title: 'P2', project: project, priority: 2) } let(:p3) { create(:label, title: 'P3', project: project, priority: 3) } + let!(:backlog) { create(:backlog_list, board: board) } let!(:list1) { create(:list, board: board, label: development, position: 0) } let!(:list2) { create(:list, board: board, label: testing, position: 1) } let!(:closed) { create(:closed_list, board: board) } let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) } let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) } - let!(:reopened_issue1) { create(:issue, :reopened, project: project) } + let!(:reopened_issue1) { create(:issue, :opened, project: project) } let!(:list1_issue1) { create(:labeled_issue, project: project, labels: [p2, development]) } let!(:list1_issue2) { create(:labeled_issue, project: project, labels: [development]) } @@ -53,6 +54,14 @@ describe Boards::Issues::ListService, services: true do expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1] end + it 'returns opened issues when listing issues from Backlog' do + params = { board_id: board.id, id: backlog.id } + + issues = described_class.new(project, user, params).execute + + expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1] + end + it 'returns closed issues when listing issues from Closed' do params = { board_id: board.id, id: closed.id } diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 4ff7ac6bb2f..63dfe80d672 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Boards::Issues::MoveService, services: true do +describe Boards::Issues::MoveService do describe '#execute' do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board1) { create(:board, project: project) } let(:bug) { create(:label, project: project, name: 'Bug') } @@ -73,7 +73,7 @@ describe Boards::Issues::MoveService, services: true do issue.reload expect(issue.labels).to contain_exactly(bug, testing) - expect(issue).to be_reopened + expect(issue).to be_opened end end diff --git a/spec/services/boards/list_service_spec.rb b/spec/services/boards/list_service_spec.rb index dff33e4bcbb..1d0be99fb35 100644 --- a/spec/services/boards/list_service_spec.rb +++ b/spec/services/boards/list_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::ListService, services: true do +describe Boards::ListService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } subject(:service) { described_class.new(project, double) } diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb index ebac38e68f1..7d0b396cd06 100644 --- a/spec/services/boards/lists/create_service_spec.rb +++ b/spec/services/boards/lists/create_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::Lists::CreateService, services: true do +describe Boards::Lists::CreateService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } let(:label) { create(:label, project: project, name: 'in-progress') } diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb index af2d7c784bb..bd98625b44f 100644 --- a/spec/services/boards/lists/destroy_service_spec.rb +++ b/spec/services/boards/lists/destroy_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::Lists::DestroyService, services: true do +describe Boards::Lists::DestroyService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } diff --git a/spec/services/boards/lists/generate_service_spec.rb b/spec/services/boards/lists/generate_service_spec.rb index ed0337662af..592f25059ac 100644 --- a/spec/services/boards/lists/generate_service_spec.rb +++ b/spec/services/boards/lists/generate_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::Lists::GenerateService, services: true do +describe Boards::Lists::GenerateService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb index ab9fb1bc914..b189857e4f4 100644 --- a/spec/services/boards/lists/list_service_spec.rb +++ b/spec/services/boards/lists/list_service_spec.rb @@ -1,16 +1,33 @@ require 'spec_helper' -describe Boards::Lists::ListService, services: true do +describe Boards::Lists::ListService do + let(:project) { create(:project) } + let(:board) { create(:board, project: project) } + let(:label) { create(:label, project: project) } + let!(:list) { create(:list, board: board, label: label) } + let(:service) { described_class.new(project, double) } + describe '#execute' do - it "returns board's lists" do - project = create(:empty_project) - board = create(:board, project: project) - label = create(:label, project: project) - list = create(:list, board: board, label: label) + context 'when the board has a backlog list' do + let!(:backlog_list) { create(:backlog_list, board: board) } + + it 'does not create a backlog list' do + expect { service.execute(board) }.not_to change(board.lists, :count) + end + + it "returns board's lists" do + expect(service.execute(board)).to eq [backlog_list, list, board.closed_list] + end + end - service = described_class.new(project, double) + context 'when the board does not have a backlog list' do + it 'creates a backlog list' do + expect { service.execute(board) }.to change(board.lists, :count).by(1) + end - expect(service.execute(board)).to eq [list, board.closed_list] + it "returns board's lists" do + expect(service.execute(board)).to eq [board.backlog_list, list, board.closed_list] + end end end end diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb index 4b3bdd133f2..a9d218bad49 100644 --- a/spec/services/boards/lists/move_service_spec.rb +++ b/spec/services/boards/lists/move_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Boards::Lists::MoveService, services: true do +describe Boards::Lists::MoveService do describe '#execute' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb index d50bfb0492c..d88b2504133 100644 --- a/spec/services/chat_names/authorize_user_service_spec.rb +++ b/spec/services/chat_names/authorize_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNames::AuthorizeUserService, services: true do +describe ChatNames::AuthorizeUserService do describe '#execute' do let(:service) { create(:service) } diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb index 0dc96521fa8..79aaac3aeb6 100644 --- a/spec/services/chat_names/find_user_service_spec.rb +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNames::FindUserService, services: true do +describe ChatNames::FindUserService do describe '#execute' do let(:service) { create(:service) } diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 597c3947e71..730df4e0336 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1,21 +1,28 @@ require 'spec_helper' -describe Ci::CreatePipelineService, services: true do +describe Ci::CreatePipelineService do let(:project) { create(:project, :repository) } let(:user) { create(:admin) } + let(:ref_name) { 'refs/heads/master' } before do stub_ci_pipeline_to_return_yaml_file end describe '#execute' do - def execute_service(source: :push, after: project.commit.id, message: 'Message', ref: 'refs/heads/master') + def execute_service( + source: :push, + after: project.commit.id, + message: 'Message', + ref: ref_name, + trigger_request: nil) params = { ref: ref, before: '00000000', after: after, commits: [{ message: message }] } - described_class.new(project, user, params).execute(source) + described_class.new(project, user, params).execute( + source, trigger_request: trigger_request) end context 'valid params' do @@ -30,6 +37,7 @@ describe Ci::CreatePipelineService, services: true do it 'creates a pipeline' do expect(pipeline).to be_kind_of(Ci::Pipeline) expect(pipeline).to be_valid + expect(pipeline).to be_persisted expect(pipeline).to be_push expect(pipeline).to eq(project.pipelines.last) expect(pipeline).to have_attributes(user: user) @@ -37,6 +45,14 @@ describe Ci::CreatePipelineService, services: true do expect(pipeline.builds.first).to be_kind_of(Ci::Build) end + it 'increments the prometheus counter' do + expect(Gitlab::Metrics).to receive(:counter) + .with(:pipelines_created_total, "Counter of pipelines created") + .and_call_original + + pipeline + end + context 'when merge requests already exist for this source branch' do it 'updates head pipeline of each merge request' do merge_request_1 = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project) @@ -59,7 +75,7 @@ describe Ci::CreatePipelineService, services: true do end context 'when merge request target project is different from source project' do - let!(:target_project) { create(:project) } + let!(:target_project) { create(:project, :repository) } let!(:forked_project_link) { create(:forked_project_link, forked_to_project: project, forked_from_project: target_project) } it 'updates head pipeline for merge request' do @@ -311,5 +327,223 @@ describe Ci::CreatePipelineService, services: true do end.not_to change { Environment.count } end end + + context 'when builds with auto-retries are configured' do + before do + config = YAML.dump(rspec: { script: 'rspec', retry: 2 }) + stub_ci_pipeline_yaml_file(config) + end + + it 'correctly creates builds with auto-retry value configured' do + pipeline = execute_service + + expect(pipeline).to be_persisted + expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2 + end + end + + shared_examples 'when ref is protected' do + let(:user) { create(:user) } + + context 'when user is developer' do + before do + project.add_developer(user) + end + + it 'does not create a pipeline' do + expect(execute_service).not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when user is master' do + before do + project.add_master(user) + end + + it 'creates a pipeline' do + expect(execute_service).to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + + context 'when trigger belongs to no one' do + let(:user) {} + let(:trigger_request) { create(:ci_trigger_request) } + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when trigger belongs to a developer' do + let(:user) {} + + let(:trigger_request) do + create(:ci_trigger_request).tap do |request| + user = create(:user) + project.add_developer(user) + request.trigger.update(owner: user) + end + end + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when trigger belongs to a master' do + let(:user) {} + + let(:trigger_request) do + create(:ci_trigger_request).tap do |request| + user = create(:user) + project.add_master(user) + request.trigger.update(owner: user) + end + end + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + end + + context 'when ref is a protected branch' do + before do + create(:protected_branch, project: project, name: 'master') + end + + it_behaves_like 'when ref is protected' + end + + context 'when ref is a protected tag' do + let(:ref_name) { 'refs/tags/v1.0.0' } + + before do + create(:protected_tag, project: project, name: '*') + end + + it_behaves_like 'when ref is protected' + end + + context 'when ref is not protected' do + context 'when trigger belongs to no one' do + let(:user) {} + let(:trigger_request) { create(:ci_trigger_request) } + + it 'creates a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + end + end + + describe '#allowed_to_create?' do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:ref) { 'master' } + + subject do + described_class.new(project, user, ref: ref) + .send(:allowed_to_create?, user) + end + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to merge' do + let!(:protected_branch) do + create(:protected_branch, + :developers_can_merge, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :developers_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + end + + context 'when user is a master' do + before do + project.add_master(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_truthy } + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_truthy } + + context 'when no one can create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :no_one_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_falsey } + end + end + end + + context 'when owner cannot create pipeline' do + it { is_expected.to be_falsey } + end end end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index f2956262f4b..8295813a1ca 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -1,12 +1,15 @@ require 'spec_helper' -describe Ci::CreateTriggerRequestService, services: true do - let(:service) { described_class.new } +describe Ci::CreateTriggerRequestService do + let(:service) { described_class } let(:project) { create(:project, :repository) } - let(:trigger) { create(:ci_trigger, project: project) } + let(:trigger) { create(:ci_trigger, project: project, owner: owner) } + let(:owner) { create(:user) } before do stub_ci_pipeline_to_return_yaml_file + + project.add_developer(owner) end describe '#execute' do @@ -14,29 +17,26 @@ describe Ci::CreateTriggerRequestService, services: true do subject { service.execute(project, trigger, 'master') } context 'without owner' do - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request.builds.first).to be_kind_of(Ci::Build) } it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) } it { expect(subject.pipeline).to be_trigger } - it { expect(subject.builds.first).to be_kind_of(Ci::Build) } end context 'with owner' do - let(:owner) { create(:user) } - let(:trigger) { create(:ci_trigger, project: project, owner: owner) } - - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request.builds.first).to be_kind_of(Ci::Build) } + it { expect(subject.trigger_request.builds.first.user).to eq(owner) } it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) } it { expect(subject.pipeline).to be_trigger } it { expect(subject.pipeline.user).to eq(owner) } - it { expect(subject.builds.first).to be_kind_of(Ci::Build) } - it { expect(subject.builds.first.user).to eq(owner) } end end context 'no commit for ref' do subject { service.execute(project, trigger, 'other-branch') } - it { expect(subject).to be_nil } + it { expect(subject.pipeline).not_to be_persisted } end context 'no builds created' do @@ -46,7 +46,7 @@ describe Ci::CreateTriggerRequestService, services: true do stub_ci_pipeline_yaml_file('script: { only: [develop], script: hello World }') end - it { expect(subject).to be_nil } + it { expect(subject.pipeline).not_to be_persisted } end end end diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb new file mode 100644 index 00000000000..9a6875e448c --- /dev/null +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Ci::PipelineTriggerService do + let(:project) { create(:project, :repository) } + + before do + stub_ci_pipeline_to_return_yaml_file + end + + describe '#execute' do + let(:user) { create(:user) } + let(:trigger) { create(:ci_trigger, project: project, owner: user) } + let(:result) { described_class.new(project, user, params).execute } + + before do + project.add_developer(user) + end + + context 'when trigger belongs to a different project' do + let(:params) { { token: trigger.token, ref: 'master', variables: nil } } + let(:trigger) { create(:ci_trigger, project: create(:project), owner: user) } + + it 'does nothing' do + expect { result }.not_to change { Ci::Pipeline.count } + end + end + + context 'when params have an existsed trigger token' do + context 'when params have an existsed ref' do + let(:params) { { token: trigger.token, ref: 'master', variables: nil } } + + it 'triggers a pipeline' do + expect { result }.to change { Ci::Pipeline.count }.by(1) + expect(result[:pipeline].ref).to eq('master') + expect(result[:pipeline].project).to eq(project) + expect(result[:pipeline].user).to eq(trigger.owner) + expect(result[:status]).to eq(:success) + end + + context 'when commit message has [ci skip]' do + before do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { '[ci skip]' } + end + + it 'ignores [ci skip] and create as general' do + expect { result }.to change { Ci::Pipeline.count }.by(1) + expect(result[:status]).to eq(:success) + end + end + + context 'when params have a variable' do + let(:params) { { token: trigger.token, ref: 'master', variables: variables } } + let(:variables) { { 'AAA' => 'AAA123' } } + + it 'has a variable' do + expect { result }.to change { Ci::PipelineVariable.count }.by(1) + .and change { Ci::TriggerRequest.count }.by(1) + expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables) + expect(result[:pipeline].trigger_requests.last.variables).to be_nil + end + end + end + + context 'when params have a non-existsed ref' do + let(:params) { { token: trigger.token, ref: 'invalid-ref', variables: nil } } + + it 'does not trigger a pipeline' do + expect { result }.not_to change { Ci::Pipeline.count } + expect(result[:http_status]).to eq(400) + end + end + end + + context 'when params have a non-existsed trigger token' do + let(:params) { { token: 'invalid-token', ref: nil, variables: nil } } + + it 'does not trigger a pipeline' do + expect { result }.not_to change { Ci::Pipeline.count } + expect(result).to be_nil + end + end + end +end diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index ea211de1f82..330ec81e87d 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Ci::PlayBuildService, '#execute', :services do +describe Ci::PlayBuildService, '#execute' do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:build) { create(:ci_build, :manual, pipeline: pipeline) } @@ -11,7 +11,7 @@ describe Ci::PlayBuildService, '#execute', :services do end context 'when project does not have repository yet' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } it 'allows user to play build if protected branch rules are met' do project.add_developer(user) @@ -33,7 +33,7 @@ describe Ci::PlayBuildService, '#execute', :services do end context 'when project has repository' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } it 'allows user with developer role to play a build' do project.add_developer(user) diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb index 1557cb3c938..214adc9960f 100644 --- a/spec/services/ci/process_pipeline_service_spec.rb +++ b/spec/services/ci/process_pipeline_service_spec.rb @@ -1,14 +1,16 @@ require 'spec_helper' -describe Ci::ProcessPipelineService, '#execute', :services do +describe Ci::ProcessPipelineService, '#execute' do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:pipeline) do create(:ci_empty_pipeline, ref: 'master', project: project) end before do + stub_not_protect_default_branch + project.add_developer(user) end @@ -62,6 +64,10 @@ describe Ci::ProcessPipelineService, '#execute', :services do fail_running_or_pending expect(builds_statuses).to eq %w(failed pending) + + fail_running_or_pending + + expect(pipeline.reload).to be_success end end @@ -459,6 +465,35 @@ describe Ci::ProcessPipelineService, '#execute', :services do end end + context 'when builds with auto-retries are configured' do + before do + create_build('build:1', stage_idx: 0, user: user, options: { retry: 2 }) + create_build('test:1', stage_idx: 1, user: user, when: :on_failure) + create_build('test:2', stage_idx: 1, user: user, options: { retry: 1 }) + end + + it 'automatically retries builds in a valid order' do + expect(process_pipeline).to be_truthy + + fail_running_or_pending + + expect(builds_names).to eq %w[build:1 build:1] + expect(builds_statuses).to eq %w[failed pending] + + succeed_running_or_pending + + expect(builds_names).to eq %w[build:1 build:1 test:2] + expect(builds_statuses).to eq %w[failed success pending] + + succeed_running_or_pending + + expect(builds_names).to eq %w[build:1 build:1 test:2] + expect(builds_statuses).to eq %w[failed success success] + + expect(pipeline.reload).to be_success + end + end + def process_pipeline described_class.new(pipeline.project, user).execute(pipeline) end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 62ba0b01339..8eb0d2d10a4 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' module Ci - describe RegisterJobService, services: true do - let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false } + describe RegisterJobService do + let!(:project) { FactoryGirl.create :project, shared_runners_enabled: false } let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project } let!(:pending_build) { FactoryGirl.create :ci_build, pipeline: pipeline } let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } @@ -72,9 +72,9 @@ module Ci end context 'for multiple builds' do - let!(:project2) { create :empty_project, shared_runners_enabled: true } + let!(:project2) { create :project, shared_runners_enabled: true } let!(:pipeline2) { create :ci_pipeline, project: project2 } - let!(:project3) { create :empty_project, shared_runners_enabled: true } + let!(:project3) { create :project, shared_runners_enabled: true } let!(:pipeline3) { create :ci_pipeline, project: project3 } let!(:build1_project1) { pending_build } let!(:build2_project1) { FactoryGirl.create :ci_build, pipeline: pipeline } diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 7254e6b357a..cec667071cc 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Ci::RetryBuildService, :services do +describe Ci::RetryBuildService do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:build) { create(:ci_build, pipeline: pipeline) } @@ -25,12 +25,24 @@ describe Ci::RetryBuildService, :services do user_id auto_canceled_by_id retried].freeze shared_examples 'build duplication' do + let(:stage) do + # TODO, we still do not have factory for new stages, we will need to + # switch existing factory to persist stages, instead of using LegacyStage + # + Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test') + end + let(:build) do create(:ci_build, :failed, :artifacts_expired, :erased, :queued, :coverage, :tags, :allowed_to_fail, :on_tag, - :teardown_environment, :triggered, :trace, - description: 'some build', pipeline: pipeline, - auto_canceled_by: create(:ci_empty_pipeline)) + :triggered, :trace, :teardown_environment, + description: 'my-job', stage: 'test', pipeline: pipeline, + auto_canceled_by: create(:ci_empty_pipeline)) do |build| + ## + # TODO, workaround for FactoryGirl limitation when having both + # stage (text) and stage_id (integer) columns in the table. + build.stage_id = stage.id + end end describe 'clone accessors' do @@ -73,6 +85,8 @@ describe Ci::RetryBuildService, :services do context 'when user has ability to execute build' do before do + stub_not_protect_default_branch + project.add_developer(user) end @@ -119,6 +133,8 @@ describe Ci::RetryBuildService, :services do context 'when user has ability to execute build' do before do + stub_not_protect_default_branch + project.add_developer(user) end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 3e860203063..6ce75c65c8c 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Ci::RetryPipelineService, '#execute', :services do +describe Ci::RetryPipelineService, '#execute' do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:service) { described_class.new(project, user) } @@ -244,13 +244,9 @@ describe Ci::RetryPipelineService, '#execute', :services do create_build('verify', :canceled, 1) end - it 'does not reprocess manual action' do - service.execute(pipeline) - - expect(build('test')).to be_pending - expect(build('deploy')).to be_failed - expect(build('verify')).to be_created - expect(pipeline.reload).to be_running + it 'raises an error' do + expect { service.execute(pipeline) } + .to raise_error Gitlab::Access::AccessDeniedError end end @@ -261,13 +257,9 @@ describe Ci::RetryPipelineService, '#execute', :services do create_build('verify', :canceled, 2) end - it 'does not reprocess manual action' do - service.execute(pipeline) - - expect(build('test')).to be_pending - expect(build('deploy')).to be_failed - expect(build('verify')).to be_created - expect(pipeline.reload).to be_running + it 'raises an error' do + expect { service.execute(pipeline) } + .to raise_error Gitlab::Access::AccessDeniedError end end end diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb index 98044ad232e..e2a9ed27e87 100644 --- a/spec/services/ci/stop_environments_service_spec.rb +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::StopEnvironmentsService, services: true do +describe Ci::StopEnvironmentsService do let(:project) { create(:project, :private, :repository) } let(:user) { create(:user) } diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index c44e6b2a48b..0da0e57dbcd 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::UpdateBuildQueueService, :services do +describe Ci::UpdateBuildQueueService do let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) { create(:ci_pipeline, project: project) } @@ -9,7 +9,9 @@ describe Ci::UpdateBuildQueueService, :services do let(:runner) { create(:ci_runner) } context 'when there are runner that can pick build' do - before { build.project.runners << runner } + before do + build.project.runners << runner + end it 'ticks runner queue value' do expect { subject.execute(build) } @@ -36,7 +38,9 @@ describe Ci::UpdateBuildQueueService, :services do end context 'when there are no runners that can pick build' do - before { build.tag_list = [:docker] } + before do + build.tag_list = [:docker] + end it 'does not tick runner queue value' do expect { subject.execute(build) } diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb index e429fcfc72f..7cc04c92d27 100644 --- a/spec/services/ci/update_runner_service_spec.rb +++ b/spec/services/ci/update_runner_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::UpdateRunnerService, :services do +describe Ci::UpdateRunnerService do let(:runner) { create(:ci_runner) } describe '#update' do diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb index bea7c965233..9e15eae8c13 100644 --- a/spec/services/compare_service_spec.rb +++ b/spec/services/compare_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CompareService, services: true do +describe CompareService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, 'feature') } diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb index 3f548688c20..38096a080a7 100644 --- a/spec/services/create_branch_service_spec.rb +++ b/spec/services/create_branch_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateBranchService, services: true do +describe CreateBranchService do let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index 5398b5c3f7e..049b082277a 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateDeploymentService, services: true do +describe CreateDeploymentService do let(:user) { create(:user) } let(:options) { nil } @@ -122,6 +122,61 @@ describe CreateDeploymentService, services: true do end end + describe '#expanded_environment_url' do + subject { service.send(:expanded_environment_url) } + + context 'when yaml environment uses $CI_COMMIT_REF_NAME' do + let(:job) do + create(:ci_build, + ref: 'master', + options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } }) + end + + it { is_expected.to eq('http://review/master') } + end + + context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do + let(:job) do + create(:ci_build, + ref: 'master', + environment: 'production', + options: { environment: { url: 'http://review/$CI_ENVIRONMENT_SLUG' } }) + end + + let!(:environment) do + create(:environment, + project: job.project, + name: 'production', + slug: 'prod-slug', + external_url: 'http://review/old') + end + + it { is_expected.to eq('http://review/prod-slug') } + end + + context 'when yaml environment uses yaml_variables containing symbol keys' do + let(:job) do + create(:ci_build, + yaml_variables: [{ key: :APP_HOST, value: 'host' }], + options: { environment: { url: 'http://review/$APP_HOST' } }) + end + + it { is_expected.to eq('http://review/host') } + end + + context 'when yaml environment does not have url' do + let(:job) { create(:ci_build, environment: 'staging') } + + let!(:environment) do + create(:environment, project: job.project, name: job.environment) + end + + it 'returns the external_url from persisted environment' do + is_expected.to be_nil + end + end + end + describe 'processing of builds' do shared_examples 'does not create deployment' do it 'does not create a new deployment' do @@ -189,6 +244,8 @@ describe CreateDeploymentService, services: true do context 'when job is retried' do it_behaves_like 'creates deployment' do before do + stub_not_protect_default_branch + project.add_developer(user) end @@ -204,7 +261,9 @@ describe CreateDeploymentService, services: true do let(:merge_request) { create(:merge_request, target_branch: 'master', source_branch: 'feature', source_project: project) } context "while updating the 'first_deployed_to_production_at' time" do - before { merge_request.mark_as_merged } + before do + merge_request.mark_as_merged + end context "for merge requests merged before the current deploy" do it "sets the time if the deploy's environment is 'production'" do diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb index 271ccfe7968..ac0a0458f56 100644 --- a/spec/services/create_release_service_spec.rb +++ b/spec/services/create_release_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe CreateReleaseService, services: true do +describe CreateReleaseService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } - let(:service) { CreateReleaseService.new(project, user) } + let(:service) { described_class.new(project, user) } it 'creates a new release' do result = service.execute(tag_name, description) diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb index d81d0fd76c9..b6ab6b8271c 100644 --- a/spec/services/create_snippet_service_spec.rb +++ b/spec/services/create_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateSnippetService, services: true do +describe CreateSnippetService do before do @user = create :user @admin = create :user, admin: true diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index c4685c9aa31..19855c9bee2 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeleteBranchService, services: true do +describe DeleteBranchService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } diff --git a/spec/services/delete_merged_branches_service_spec.rb b/spec/services/delete_merged_branches_service_spec.rb index cae74df9c90..03c682ae0d7 100644 --- a/spec/services/delete_merged_branches_service_spec.rb +++ b/spec/services/delete_merged_branches_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeleteMergedBranchesService, services: true do +describe DeleteMergedBranchesService do subject(:service) { described_class.new(project, project.owner) } let(:project) { create(:project, :repository) } @@ -24,6 +24,22 @@ describe DeleteMergedBranchesService, services: true do expect(project.repository.branch_names).to include('master') end + it 'keeps protected branches' do + create(:protected_branch, project: project, name: 'improve/awesome') + + service.execute + + expect(project.repository.branch_names).to include('improve/awesome') + end + + it 'keeps wildcard protected branches' do + create(:protected_branch, project: project, name: 'improve/*') + + service.execute + + expect(project.repository.branch_names).to include('improve/awesome') + end + context 'user without rights' do let(:user) { create(:user) } @@ -35,7 +51,7 @@ describe DeleteMergedBranchesService, services: true do context 'open merge requests' do it 'does not delete branches from open merge requests' do fork_link = create(:forked_project_link, forked_from_project: project) - create(:merge_request, :reopened, source_project: project, target_project: project, source_branch: 'branch-merged', target_branch: 'master') + create(:merge_request, :opened, source_project: project, target_project: project, source_branch: 'branch-merged', target_branch: 'master') create(:merge_request, :opened, source_project: fork_link.forked_to_project, target_project: project, target_branch: 'improve/awesome', source_branch: 'master') service.execute diff --git a/spec/services/discussions/update_diff_position_service_spec.rb b/spec/services/discussions/update_diff_position_service_spec.rb index 177e32e13bd..c239494298b 100644 --- a/spec/services/discussions/update_diff_position_service_spec.rb +++ b/spec/services/discussions/update_diff_position_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Discussions::UpdateDiffPositionService, services: true do +describe Discussions::UpdateDiffPositionService do let(:project) { create(:project, :repository) } let(:current_user) { project.owner } let(:create_commit) { project.commit("913c66a37b4a45b9769037c55c2d238bd0942d2e") } diff --git a/spec/services/emails/create_service_spec.rb b/spec/services/emails/create_service_spec.rb new file mode 100644 index 00000000000..641d5538de8 --- /dev/null +++ b/spec/services/emails/create_service_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Emails::CreateService do + let(:user) { create(:user) } + let(:opts) { { email: 'new@email.com' } } + + subject(:service) { described_class.new(user, opts) } + + describe '#execute' do + it 'creates an email with valid attributes' do + expect { service.execute }.to change { Email.count }.by(1) + expect(Email.where(opts)).not_to be_empty + end + + it 'has the right user association' do + service.execute + + expect(user.emails).to eq(Email.where(opts)) + end + end +end diff --git a/spec/services/emails/destroy_service_spec.rb b/spec/services/emails/destroy_service_spec.rb new file mode 100644 index 00000000000..1f4294dd905 --- /dev/null +++ b/spec/services/emails/destroy_service_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe Emails::DestroyService do + let!(:user) { create(:user) } + let!(:email) { create(:email, user: user) } + + subject(:service) { described_class.new(user, email: email.email) } + + describe '#execute' do + it 'removes an email' do + expect { service.execute }.to change { user.emails.count }.by(-1) + end + end +end diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index b06cefe071d..42adb044190 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe EventCreateService, services: true do +describe EventCreateService do include UserActivitiesHelpers - let(:service) { EventCreateService.new } + let(:service) { described_class.new } describe 'Issues' do describe '#open_issue' do @@ -113,8 +113,8 @@ describe EventCreateService, services: true do end end - describe '#push', :redis do - let(:project) { create(:empty_project) } + describe '#push', :clean_gitlab_redis_shared_state do + let(:project) { create(:project) } let(:user) { create(:user) } it 'creates a new event' do @@ -128,7 +128,7 @@ describe EventCreateService, services: true do describe 'Project' do let(:user) { create :user } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } describe '#join_project' do subject { service.join_project(project, user) } diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb index 16bca66766a..cc950ae6bb3 100644 --- a/spec/services/files/update_service_spec.rb +++ b/spec/services/files/update_service_spec.rb @@ -32,8 +32,8 @@ describe Files::UpdateService do let(:last_commit_sha) { "foo" } it "returns a hash with the correct error message and a :error status " do - expect { subject.execute }. - to raise_error(Files::UpdateService::FileChangedError, + expect { subject.execute } + .to raise_error(Files::UpdateService::FileChangedError, "You are attempting to update a file that has changed since you started editing it.") end end diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index ac7ccfbaab0..3ce01a995b4 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -1,18 +1,17 @@ require 'spec_helper' -describe GitHooksService, services: true do +describe GitHooksService do include RepoHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } - let(:service) { GitHooksService.new } + let(:service) { described_class.new } before do @blankrev = Gitlab::Git::BLANK_SHA @oldrev = sample_commit.parent_id @newrev = sample_commit.id @ref = 'refs/heads/feature' - @repo_path = project.repository.path_to_repo end describe '#execute' do @@ -21,7 +20,7 @@ describe GitHooksService, services: true do hook = double(trigger: [true, nil]) expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) - service.execute(user, @repo_path, @blankrev, @newrev, @ref) { } + service.execute(user, project, @blankrev, @newrev, @ref) { } end end @@ -31,7 +30,7 @@ describe GitHooksService, services: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + service.execute(user, project, @blankrev, @newrev, @ref) end.to raise_error(GitHooksService::PreReceiveError) end end @@ -43,7 +42,7 @@ describe GitHooksService, services: true do expect(service).not_to receive(:run_hook).with('post-receive') expect do - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + service.execute(user, project, @blankrev, @newrev, @ref) end.to raise_error(GitHooksService::PreReceiveError) end end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index bcd1fb64ab9..82724ccd281 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -3,27 +3,24 @@ require 'spec_helper' describe GitPushService, services: true do include RepoHelpers - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:blankrev) { Gitlab::Git::BLANK_SHA } + let(:oldrev) { sample_commit.parent_id } + let(:newrev) { sample_commit.id } + let(:ref) { 'refs/heads/master' } before do project.team << [user, :master] - @blankrev = Gitlab::Git::BLANK_SHA - @oldrev = sample_commit.parent_id - @newrev = sample_commit.id - @ref = 'refs/heads/master' end describe 'Push branches' do - let(:oldrev) { @oldrev } - let(:newrev) { @newrev } - subject do - execute_service(project, user, oldrev, newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end context 'new branch' do - let(:oldrev) { @blankrev } + let(:oldrev) { blankrev } it { is_expected.to be_truthy } @@ -51,7 +48,7 @@ describe GitPushService, services: true do end context 'rm branch' do - let(:newrev) { @blankrev } + let(:newrev) { blankrev } it { is_expected.to be_truthy } @@ -70,24 +67,20 @@ describe GitPushService, services: true do end describe "Git Push Data" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @push_data = service.push_data - @commit = project.commit(@newrev) - end + let(:commit) { project.commit(newrev) } - subject { @push_data } + subject { push_data_from_service(project, user, oldrev, newrev, ref) } it { is_expected.to include(object_kind: 'push') } - it { is_expected.to include(before: @oldrev) } - it { is_expected.to include(after: @newrev) } - it { is_expected.to include(ref: @ref) } + it { is_expected.to include(before: oldrev) } + it { is_expected.to include(after: newrev) } + it { is_expected.to include(ref: ref) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } it { is_expected.to include(project_id: project.id) } context "with repository data" do - subject { @push_data[:repository] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:repository] } it { is_expected.to include(name: project.name) } it { is_expected.to include(url: project.url_to_repo) } @@ -96,7 +89,7 @@ describe GitPushService, services: true do end context "with commits" do - subject { @push_data[:commits] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits] } it { is_expected.to be_an(Array) } it 'has 1 element' do @@ -104,11 +97,11 @@ describe GitPushService, services: true do end context "the commit" do - subject { @push_data[:commits].first } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first } - it { is_expected.to include(id: @commit.id) } - it { is_expected.to include(message: @commit.safe_message) } - it { is_expected.to include(timestamp: @commit.date.xmlschema) } + it { is_expected.to include(id: commit.id) } + it { is_expected.to include(message: commit.safe_message) } + it { expect(subject[:timestamp].in_time_zone).to eq(commit.date.in_time_zone) } it do is_expected.to include( url: [ @@ -116,23 +109,23 @@ describe GitPushService, services: true do project.namespace.to_param, project.to_param, 'commit', - @commit.id + commit.id ].join('/') ) end context "with a author" do - subject { @push_data[:commits].first[:author] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first[:author] } - it { is_expected.to include(name: @commit.author_name) } - it { is_expected.to include(email: @commit.author_email) } + it { is_expected.to include(name: commit.author_name) } + it { is_expected.to include(email: commit.author_email) } end end end end describe "Pipelines" do - subject { execute_service(project, user, @oldrev, @newrev, @ref) } + subject { execute_service(project, user, oldrev, newrev, ref) } before do stub_ci_pipeline_to_return_yaml_file @@ -145,29 +138,26 @@ describe GitPushService, services: true do end describe "Push Event" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @event = Event.find_by_action(Event::PUSHED) - @push_data = service.push_data - end + let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } + let(:event) { Event.find_by_action(Event::PUSHED) } - it { expect(@event).not_to be_nil } - it { expect(@event.project).to eq(project) } - it { expect(@event.action).to eq(Event::PUSHED) } - it { expect(@event.data).to eq(@push_data) } + it { expect(event).not_to be_nil } + it { expect(event.project).to eq(project) } + it { expect(event.action).to eq(Event::PUSHED) } + it { expect(event.data).to eq(push_data) } context "Updates merge requests" do it "when pushing a new branch for the first time" do - expect(UpdateMergeRequestsWorker).to receive(:perform_async). - with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + expect(UpdateMergeRequestsWorker).to receive(:perform_async) + .with(project.id, user.id, blankrev, 'newrev', ref) + execute_service(project, user, blankrev, 'newrev', ref ) end end - + context "Sends System Push data" do it "when pushing on a branch" do - expect(SystemHookPushWorker).to receive(:perform_async).with(@push_data, :push_hooks) - execute_service(project, user, @oldrev, @newrev, @ref ) + expect(SystemHookPushWorker).to receive(:perform_async).with(push_data, :push_hooks) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -177,13 +167,13 @@ describe GitPushService, services: true do it "calls the copy attributes method for the first push to the default branch" do expect(project.repository).to receive(:copy_gitattributes).with('master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master') + execute_service(project, user, blankrev, 'newrev', ref) end it "calls the copy attributes method for changes to the default branch" do - expect(project.repository).to receive(:copy_gitattributes).with('refs/heads/master') + expect(project.repository).to receive(:copy_gitattributes).with(ref) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master') + execute_service(project, user, 'oldrev', 'newrev', ref) end end @@ -196,7 +186,7 @@ describe GitPushService, services: true do it "does not call copy attributes method" do expect(project.repository).not_to receive(:copy_gitattributes) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -206,7 +196,7 @@ describe GitPushService, services: true do it "when pushing a branch for the first time" do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) @@ -217,7 +207,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).to be_empty end @@ -227,7 +217,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -242,7 +232,7 @@ describe GitPushService, services: true do expect(project.default_branch).to eq("master") expect_any_instance_of(ProtectedBranches::CreateService).not_to receive(:execute) - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS]) @@ -254,7 +244,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -262,7 +252,7 @@ describe GitPushService, services: true do it "when pushing new commits to existing branch" do expect(project).to receive(:execute_hooks) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master' ) + execute_service(project, user, 'oldrev', 'newrev', ref) end end end @@ -283,8 +273,8 @@ describe GitPushService, services: true do author_email: commit_author.email ) - allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit). - and_return(commit) + allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit) + .and_return(commit) allow(project.repository).to receive(:commits_between).and_return([commit]) end @@ -292,7 +282,7 @@ describe GitPushService, services: true do it "creates a note if a pushed commit mentions an issue" do expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "only creates a cross-reference note if one doesn't already exist" do @@ -300,7 +290,7 @@ describe GitPushService, services: true do expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "defaults to the pushing user if the commit's author is not known" do @@ -310,16 +300,16 @@ describe GitPushService, services: true do ) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "finds references in the first push to a non-default branch" do - allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([]) - allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit]) + allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([]) + allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit]) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @blankrev, @newrev, 'refs/heads/other' ) + execute_service(project, user, blankrev, newrev, 'refs/heads/other') end end @@ -341,22 +331,22 @@ describe GitPushService, services: true do committed_date: commit_time ) - allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit). - and_return(commit) + allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit) + .and_return(commit) allow(project.repository).to receive(:commits_between).and_return([commit]) end context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do it 'sets the metric for referenced issues' do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time) end it 'does not set the metric for non-referenced issues' do non_referenced_issue = create(:issue, project: project) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil end @@ -377,41 +367,29 @@ describe GitPushService, services: true do author_email: commit_author.email ) - allow(project.repository).to receive(:commits_between). - and_return([closing_commit]) + allow(project.repository).to receive(:commits_between) + .and_return([closing_commit]) - allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit). - and_return(closing_commit) + allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit) + .and_return(closing_commit) project.team << [commit_author, :master] end context "to default branches" do it "closes issues" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_closed end it "adds a note indicating that the issue is now closed" do expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) end it "doesn't create additional cross-reference notes" do expect(SystemNoteService).not_to receive(:cross_reference) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) - end - - it "doesn't close issues when external issue tracker is in use" do - allow_any_instance_of(Project).to receive(:default_issues_tracker?). - and_return(false) - external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern) - allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker) - - # The push still shouldn't create cross-reference notes. - expect do - execute_service(project, commit_author, @oldrev, @newrev, 'refs/heads/hurf' ) - end.not_to change { Note.where(project_id: project.id, system: true).count } + execute_service(project, commit_author, oldrev, newrev, ref) end end @@ -423,11 +401,11 @@ describe GitPushService, services: true do it "creates cross-reference notes" do expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "doesn't close issues" do - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_opened end end @@ -444,11 +422,12 @@ describe GitPushService, services: true do stub_jira_urls("JIRA-1") allow(closing_commit).to receive_messages({ - issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), - safe_message: message, - author_name: commit_author.name, - author_email: commit_author.email - }) + issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), + safe_message: message, + author_name: commit_author.name, + author_email: commit_author.email + }) + allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) allow(project.repository).to receive_messages(commits_between: [closing_commit]) @@ -462,7 +441,7 @@ describe GitPushService, services: true do let(:message) { "this is some work.\n\nrelated to JIRA-1" } it "initiates one api call to jira server to mention the issue" do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: /mentioned this issue in/ @@ -472,7 +451,11 @@ describe GitPushService, services: true do context "closing an issue" do let(:message) { "this is some work.\n\ncloses JIRA-1" } - let(:comment_body) { { body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json } + let(:comment_body) do + { + body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/commit/#{closing_commit.id}]." + }.to_json + end before do open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" }) @@ -486,13 +469,13 @@ describe GitPushService, services: true do context "using right markdown" do it "initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once end it "initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: comment_body @@ -500,21 +483,57 @@ describe GitPushService, services: true do end end - context "using wrong markdown" do - let(:message) { "this is some work.\n\ncloses #1" } + context "using internal issue reference" do + context 'when internal issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + let(:message) { "this is some work.\n\ncloses #1" } + + it "does not initiates one api call to jira server to close the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) - it "does not initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + end - expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + it "does not initiates one api call to jira server to comment on the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end end - it "does not initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + context 'when internal issues are enabled' do + let(:issue) { create(:issue, project: project) } + let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" } - expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( - body: comment_body - ).once + it "initiates one api call to jira server to close the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once + end + + it "initiates one api call to jira server to comment on the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end + + it "closes the internal issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + expect(issue.reload).to be_closed + end + + it "adds a note indicating that the issue is now closed" do + expect(SystemNoteService).to receive(:change_status) + .with(issue, project, commit_author, "closed", closing_commit) + execute_service(project, commit_author, oldrev, newrev, ref) + end end end end @@ -523,7 +542,7 @@ describe GitPushService, services: true do describe "empty project" do let(:project) { create(:project_empty_repo) } - let(:new_ref) { 'refs/heads/feature'} + let(:new_ref) { 'refs/heads/feature' } before do allow(project).to receive(:default_branch).and_return('feature') @@ -531,7 +550,7 @@ describe GitPushService, services: true do end it 'push to first branch updates HEAD' do - execute_service(project, user, @blankrev, @newrev, new_ref ) + execute_service(project, user, blankrev, newrev, new_ref) end end @@ -539,20 +558,24 @@ describe GitPushService, services: true do let(:housekeeping) { Projects::HousekeepingService.new(project) } before do - # Flush any raw Redis data stored by the housekeeping code. - Gitlab::Redis.with { |conn| conn.flushall } + # Flush any raw key-value data stored by the housekeeping code. + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } allow(Projects::HousekeepingService).to receive(:new).and_return(housekeeping) end after do - Gitlab::Redis.with { |conn| conn.flushall } + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } end it 'does not perform housekeeping when not needed' do expect(housekeeping).not_to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end context 'when housekeeping is needed' do @@ -563,20 +586,20 @@ describe GitPushService, services: true do it 'performs housekeeping' do expect(housekeeping).to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end it 'does not raise an exception' do allow(housekeeping).to receive(:try_obtain_lease).and_return(false) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end it 'increments the push counter' do expect(housekeeping).to receive(:increment!) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end @@ -584,9 +607,9 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end context 'on the default branch' do @@ -598,13 +621,13 @@ describe GitPushService, services: true do commit = double(:commit) diff = double(:diff, new_path: 'README.md') - expect(commit).to receive(:raw_deltas). - and_return([diff]) + expect(commit).to receive(:raw_deltas) + .and_return([diff]) service.push_commits = [commit] - expect(ProjectCacheWorker).to receive(:perform_async). - with(project.id, %i(readme), %i(commit_count repository_size)) + expect(ProjectCacheWorker).to receive(:perform_async) + .with(project.id, %i(readme), %i(commit_count repository_size)) service.update_caches end @@ -616,9 +639,9 @@ describe GitPushService, services: true do end it 'does not flush any conditional caches' do - expect(ProjectCacheWorker).to receive(:perform_async). - with(project.id, [], %i(commit_count repository_size)). - and_call_original + expect(ProjectCacheWorker).to receive(:perform_async) + .with(project.id, [], %i(commit_count repository_size)) + .and_call_original service.update_caches end @@ -629,14 +652,13 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end it 'only schedules a limited number of commits' do - allow(service).to receive(:push_commits). - and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true))) + service.push_commits = Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)) expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times @@ -644,8 +666,7 @@ describe GitPushService, services: true do end it "skips commits which don't include cross-references" do - allow(service).to receive(:push_commits). - and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)]) + service.push_commits = [double(:commit, to_hash: {}, matches_cross_reference_regex?: false)] expect(ProcessCommitWorker).not_to receive(:perform_async) @@ -653,9 +674,31 @@ describe GitPushService, services: true do end end + describe '#update_signatures' do + let(:service) do + described_class.new( + project, + user, + oldrev: oldrev, + newrev: newrev, + ref: 'refs/heads/master' + ) + end + + it 'calls CreateGpgSignatureWorker.perform_async for each commit' do + expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id) + + execute_service(project, user, oldrev, newrev, ref) + end + end + def execute_service(project, user, oldrev, newrev, ref) - service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref ) + service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) service.execute service end + + def push_data_from_service(project, user, oldrev, newrev, ref) + execute_service(project, user, oldrev, newrev, ref).push_data + end end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index 1fdcb420a8b..f877c145390 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe GitTagPushService, services: true do +describe GitTagPushService do include RepoHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } - let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } + let(:service) { described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } let(:oldrev) { Gitlab::Git::BLANK_SHA } let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0 @@ -184,7 +184,7 @@ describe GitTagPushService, services: true do describe "Webhooks" do context "execute webhooks" do - let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } + let(:service) { described_class.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } it "when pushing tags" do expect(project).to receive(:execute_hooks) diff --git a/spec/services/gravatar_service_spec.rb b/spec/services/gravatar_service_spec.rb index 8c4ad8c7a3e..d2cc53fe0ee 100644 --- a/spec/services/gravatar_service_spec.rb +++ b/spec/services/gravatar_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GravatarService, service: true do +describe GravatarService do describe '#execute' do let(:url) { 'http://example.com/avatar?hash=%{hash}&size=%{size}&email=%{email}&username=%{username}' } diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index bcb62429275..b2175717a70 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Groups::CreateService, '#execute', services: true do +describe Groups::CreateService, '#execute' do let!(:user) { create(:user) } let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } @@ -14,7 +14,9 @@ describe Groups::CreateService, '#execute', services: true do end context "cannot create group with restricted visibility level" do - before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } + before do + allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) + end it { is_expected.not_to be_persisted } end @@ -25,7 +27,9 @@ describe Groups::CreateService, '#execute', services: true do let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) } context 'as group owner' do - before { group.add_owner(user) } + before do + group.add_owner(user) + end it { is_expected.to be_persisted } end diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index a37257d1bf4..1b2ce3cd03e 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Groups::DestroyService, services: true do +describe Groups::DestroyService do include DatabaseConnectionHelpers let!(:user) { create(:user) } let!(:group) { create(:group) } let!(:nested_group) { create(:group, parent: group) } - let!(:project) { create(:empty_project, namespace: group) } + let!(:project) { create(:project, namespace: group) } let!(:notification_setting) { create(:notification_setting, source: group)} let!(:gitlab_shell) { Gitlab::Shell.new } let!(:remove_path) { group.path + "+#{group.id}+deleted" } @@ -15,6 +15,14 @@ describe Groups::DestroyService, services: true do group.add_user(user, Gitlab::Access::OWNER) end + def destroy_group(group, user, async) + if async + Groups::DestroyService.new(group, user).async_execute + else + Groups::DestroyService.new(group, user).execute + end + end + shared_examples 'group destruction' do |async| context 'database records' do before do @@ -27,33 +35,27 @@ describe Groups::DestroyService, services: true do it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) } end + context 'mattermost team' do + let!(:chat_team) { create(:chat_team, namespace: group) } + + it 'destroys the team too' do + expect_any_instance_of(Mattermost::Team).to receive(:destroy) + + destroy_group(group, user, async) + end + end + context 'file system' do context 'Sidekiq inline' do before do - # Run sidekiq immediatly to check that renamed dir will be removed + # Run sidekiq immediately to check that renamed dir will be removed Sidekiq::Testing.inline! { destroy_group(group, user, async) } end - it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey } - it { expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey } - end - - context 'Sidekiq fake' do - before do - # Don't run sidekiq to check if renamed repository exists - Sidekiq::Testing.fake! { destroy_group(group, user, async) } + it 'verifies that paths have been deleted' do + expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey + expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey end - - it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey } - it { expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_truthy } - end - end - - def destroy_group(group, user, async) - if async - Groups::DestroyService.new(group, user).async_execute - else - Groups::DestroyService.new(group, user).execute end end end @@ -61,6 +63,26 @@ describe Groups::DestroyService, services: true do describe 'asynchronous delete' do it_behaves_like 'group destruction', true + context 'Sidekiq fake' do + before do + # Don't run Sidekiq to verify that group and projects are not actually destroyed + Sidekiq::Testing.fake! { destroy_group(group, user, true) } + end + + after do + # Clean up stale directories + gitlab_shell.rm_namespace(project.repository_storage_path, group.path) + gitlab_shell.rm_namespace(project.repository_storage_path, remove_path) + end + + it 'verifies original paths and projects still exist' do + expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_truthy + expect(gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey + expect(Project.unscoped.count).to eq(1) + expect(Group.unscoped.count).to eq(2) + end + end + context 'potential race conditions' do context "when the `GroupDestroyWorker` task runs immediately" do it "deletes the group" do @@ -88,7 +110,7 @@ describe Groups::DestroyService, services: true do # Kick off the initial group destroy in a new thread, so that # it doesn't share this spec's database transaction. - Thread.new { Groups::DestroyService.new(group, user).async_execute }.join(5) + Thread.new { described_class.new(group, user).async_execute }.join(5) group_record = run_with_new_database_connection do |conn| conn.execute("SELECT * FROM namespaces WHERE id = #{group.id}").first diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index f6ad5cebd2c..44f22a3b37b 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Groups::UpdateService, services: true do +describe Groups::UpdateService do let!(:user) { create(:user) } let!(:private_group) { create(:group, :private) } let!(:internal_group) { create(:group, :internal) } @@ -13,7 +13,7 @@ describe Groups::UpdateService, services: true do before do public_group.add_user(user, Gitlab::Access::MASTER) - create(:empty_project, :public, group: public_group) + create(:project, :public, group: public_group) end it "does not change permission level" do @@ -27,7 +27,7 @@ describe Groups::UpdateService, services: true do before do internal_group.add_user(user, Gitlab::Access::MASTER) - create(:empty_project, :internal, group: internal_group) + create(:project, :internal, group: internal_group) end it "does not change permission level" do @@ -69,7 +69,7 @@ describe Groups::UpdateService, services: true do before do internal_group.add_user(user, Gitlab::Access::MASTER) - create(:empty_project, :internal, group: internal_group) + create(:project, :internal, group: internal_group) end it 'returns true' do diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb index 81b1d327696..1875d0448cd 100644 --- a/spec/services/import_export_clean_up_service_spec.rb +++ b/spec/services/import_export_clean_up_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ImportExportCleanUpService, services: true do +describe ImportExportCleanUpService do describe '#execute' do let(:service) { described_class.new } diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb index 6437d00e451..befa65ec0f8 100644 --- a/spec/services/issuable/bulk_update_service_spec.rb +++ b/spec/services/issuable/bulk_update_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Issuable::BulkUpdateService, services: true do +describe Issuable::BulkUpdateService do let(:user) { create(:user) } - let(:project) { create(:empty_project, namespace: user.namespace) } + let(:project) { create(:project, namespace: user.namespace) } def bulk_update(issuables, extra_params = {}) bulk_update_params = extra_params @@ -72,7 +72,7 @@ describe Issuable::BulkUpdateService, services: true do end context "when the new assignee ID is #{IssuableFinder::NONE}" do - it "unassigns the issues" do + it 'unassigns the issues' do expect { bulk_update(merge_request, assignee_id: IssuableFinder::NONE) } .to change { merge_request.reload.assignee }.to(nil) end diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb index bed25fe7ccf..03f76bd428d 100644 --- a/spec/services/issues/build_service_spec.rb +++ b/spec/services/issues/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper.rb' -describe Issues::BuildService, services: true do +describe Issues::BuildService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index be0e829880e..a03f68434de 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::CloseService, services: true do +describe Issues::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } @@ -18,26 +18,26 @@ describe Issues::CloseService, services: true do let(:service) { described_class.new(project, user) } it 'checks if the user is authorized to update the issue' do - expect(service).to receive(:can?).with(user, :update_issue, issue). - and_call_original + expect(service).to receive(:can?).with(user, :update_issue, issue) + .and_call_original service.execute(issue) end it 'does not close the issue when the user is not authorized to do so' do - allow(service).to receive(:can?).with(user, :update_issue, issue). - and_return(false) + allow(service).to receive(:can?).with(user, :update_issue, issue) + .and_return(false) expect(service).not_to receive(:close_issue) expect(service.execute(issue)).to eq(issue) end it 'closes the issue when the user is authorized to do so' do - allow(service).to receive(:can?).with(user, :update_issue, issue). - and_return(true) + allow(service).to receive(:can?).with(user, :update_issue, issue) + .and_return(true) - expect(service).to receive(:close_issue). - with(issue, commit: nil, notifications: true, system_note: true) + expect(service).to receive(:close_issue) + .with(issue, commit: nil, notifications: true, system_note: true) service.execute(issue) end @@ -98,13 +98,13 @@ describe Issues::CloseService, services: true do end end - context 'external issue tracker' do + context 'internal issues disabled' do before do - allow(project).to receive(:default_issues_tracker?).and_return(false) - described_class.new(project, user).close_issue(issue) + project.issues_enabled = false + project.save! end - it 'closes the issue' do + it 'does not close the issue' do expect(issue).to be_valid expect(issue).to be_opened expect(todo.reload).to be_pending diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index dab1a3469f7..fcbd69fd58b 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Issues::CreateService, services: true do - let(:project) { create(:empty_project) } +describe Issues::CreateService do + let(:project) { create(:project) } let(:user) { create(:user) } describe '#execute' do @@ -155,7 +155,9 @@ describe Issues::CreateService, services: true do context 'issue create service' do context 'assignees' do - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'removes assignee when user id is invalid' do opts = { title: 'Title', description: 'Description', assignee_ids: [-1] } @@ -204,9 +206,9 @@ describe Issues::CreateService, services: true do end end - it_behaves_like 'new issuable record that supports slash commands' + it_behaves_like 'new issuable record that supports quick actions' - context 'Slash commands' do + context 'Quick actions' do context 'with assignee and milestone in params and command' do let(:opts) do { diff --git a/spec/services/issues/duplicate_service_spec.rb b/spec/services/issues/duplicate_service_spec.rb new file mode 100644 index 00000000000..089e77cc88b --- /dev/null +++ b/spec/services/issues/duplicate_service_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Issues::DuplicateService do + let(:user) { create(:user) } + let(:canonical_project) { create(:project) } + let(:duplicate_project) { create(:project) } + + let(:canonical_issue) { create(:issue, project: canonical_project) } + let(:duplicate_issue) { create(:issue, project: duplicate_project) } + + subject { described_class.new(duplicate_project, user, {}) } + + describe '#execute' do + context 'when the issues passed are the same' do + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, duplicate_issue) + end + end + + context 'when the user cannot update the duplicate issue' do + before do + canonical_project.add_reporter(user) + end + + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, canonical_issue) + end + end + + context 'when the user cannot comment on the canonical issue' do + before do + duplicate_project.add_reporter(user) + end + + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, canonical_issue) + end + end + + context 'when the user can mark the issue as a duplicate' do + before do + canonical_project.add_reporter(user) + duplicate_project.add_reporter(user) + end + + it 'closes the duplicate issue' do + subject.execute(duplicate_issue, canonical_issue) + + expect(duplicate_issue.reload).to be_closed + expect(canonical_issue.reload).to be_open + end + + it 'adds a system note to the duplicate issue' do + expect(SystemNoteService) + .to receive(:mark_duplicate_issue).with(duplicate_issue, duplicate_project, user, canonical_issue) + + subject.execute(duplicate_issue, canonical_issue) + end + + it 'adds a system note to the canonical issue' do + expect(SystemNoteService) + .to receive(:mark_canonical_issue_of_duplicate).with(canonical_issue, canonical_project, user, duplicate_issue) + + subject.execute(duplicate_issue, canonical_issue) + end + end + end +end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 9f8346d52bb..f2b35a8fadf 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Issues::MoveService, services: true do +describe Issues::MoveService do let(:user) { create(:user) } let(:author) { create(:user) } let(:title) { 'Some issue' } let(:description) { 'Some issue description' } - let(:old_project) { create(:empty_project) } - let(:new_project) { create(:empty_project) } + let(:old_project) { create(:project) } + let(:new_project) { create(:project) } let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do @@ -37,9 +37,6 @@ describe Issues::MoveService, services: true do describe '#execute' do shared_context 'issue move executed' do - let!(:milestone2) do - create(:milestone, project_id: new_project.id, title: 'v9.0') - end let!(:award_emoji) { create(:award_emoji, awardable: old_issue) } let!(:new_issue) { move_service.execute(old_issue, new_project) } @@ -48,6 +45,63 @@ describe Issues::MoveService, services: true do context 'issue movable' do include_context 'user can move issue' + context 'move to new milestone' do + let(:new_issue) { move_service.execute(old_issue, new_project) } + + context 'project milestone' do + let!(:milestone2) do + create(:milestone, project_id: new_project.id, title: 'v9.0') + end + + it 'assigns milestone to new issue' do + expect(new_issue.reload.milestone.title).to eq 'v9.0' + expect(new_issue.reload.milestone).to eq(milestone2) + end + end + + context 'group milestones' do + let!(:group) { create(:group, :private) } + let!(:group_milestone_1) do + create(:milestone, group_id: group.id, title: 'v9.0_group') + end + + before do + old_issue.update(milestone: group_milestone_1) + old_project.update(namespace: group) + new_project.update(namespace: group) + + group.add_users([user], GroupMember::DEVELOPER) + end + + context 'when moving to a project of the same group' do + it 'keeps the same group milestone' do + expect(new_issue.reload.project).to eq(new_project) + expect(new_issue.reload.milestone).to eq(group_milestone_1) + end + end + + context 'when moving to a project of a different group' do + let!(:group_2) { create(:group, :private) } + + let!(:group_milestone_2) do + create(:milestone, group_id: group_2.id, title: 'v9.0_group') + end + + before do + old_issue.update(milestone: group_milestone_1) + new_project.update(namespace: group_2) + + group_2.add_users([user], GroupMember::DEVELOPER) + end + + it 'assigns to new group milestone of same title' do + expect(new_issue.reload.project).to eq(new_project) + expect(new_issue.reload.milestone).to eq(group_milestone_2) + end + end + end + end + context 'generic issue' do include_context 'issue move executed' @@ -55,11 +109,6 @@ describe Issues::MoveService, services: true do expect(new_issue.project).to eq new_project end - it 'assigns milestone to new issue' do - expect(new_issue.reload.milestone.title).to eq 'v9.0' - expect(new_issue.reload.milestone).to eq(milestone2) - end - it 'assign labels to new issue' do expected_label_titles = new_issue.reload.labels.map(&:title) expect(expected_label_titles).to include 'label1' @@ -251,12 +300,18 @@ describe Issues::MoveService, services: true do end context 'user is reporter only in new project' do - before { new_project.team << [user, :reporter] } + before do + new_project.team << [user, :reporter] + end + it { expect { move }.to raise_error(StandardError, /permissions/) } end context 'user is reporter only in old project' do - before { old_project.team << [user, :reporter] } + before do + old_project.team << [user, :reporter] + end + it { expect { move }.to raise_error(StandardError, /permissions/) } end diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb index 391ecad303a..205e9ebd237 100644 --- a/spec/services/issues/reopen_service_spec.rb +++ b/spec/services/issues/reopen_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Issues::ReopenService, services: true do - let(:project) { create(:empty_project) } +describe Issues::ReopenService do + let(:project) { create(:project) } let(:issue) { create(:issue, :closed, project: project) } describe '#execute' do diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 86f218dec12..fac66791ffb 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper.rb' -describe Issues::ResolveDiscussions, services: true do +describe Issues::ResolveDiscussions do class DummyService < Issues::BaseService include ::Issues::ResolveDiscussions diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 5184c1d5f19..ff0d876e6da 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1,13 +1,13 @@ # coding: utf-8 require 'spec_helper' -describe Issues::UpdateService, services: true do +describe Issues::UpdateService do include EmailHelpers let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:label) { create(:label, project: project) } let(:label2) { create(:label) } @@ -31,6 +31,13 @@ describe Issues::UpdateService, services: true do end end + def find_notes(action) + issue + .notes + .joins(:system_note_metadata) + .where(system_note_metadata: { action: action }) + end + def update_issue(opts) described_class.new(project, user, opts).execute(issue) end @@ -246,13 +253,13 @@ describe Issues::UpdateService, services: true do end context 'when the milestone change' do - before do + it 'marks todos as done' do update_issue(milestone: create(:milestone)) - end - it 'marks todos as done' do expect(todo.reload.done?).to eq true end + + it_behaves_like 'system notes for milestones' end context 'when the labels change' do @@ -288,7 +295,9 @@ describe Issues::UpdateService, services: true do end context 'when issue has the `label` label' do - before { issue.labels << label } + before do + issue.labels << label + end it 'does not send notifications for existing labels' do opts = { label_ids: [label.id, label2.id] } @@ -322,7 +331,9 @@ describe Issues::UpdateService, services: true do it { expect(issue.tasks?).to eq(true) } context 'when tasks are marked as completed' do - before { update_issue(description: "- [x] Task 1\n- [X] Task 2") } + before do + update_issue(description: "- [x] Task 1\n- [X] Task 2") + end it 'creates system note about task status change' do note1 = find_note('marked the task **Task 1** as completed') @@ -330,6 +341,9 @@ describe Issues::UpdateService, services: true do expect(note1).not_to be_nil expect(note2).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) end end @@ -345,6 +359,9 @@ describe Issues::UpdateService, services: true do expect(note1).not_to be_nil expect(note2).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) end end @@ -354,10 +371,12 @@ describe Issues::UpdateService, services: true do update_issue(description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2") end - it 'does not create a system note' do - note = find_note('marked the task **Task 2** as incomplete') + it 'does not create a system note for the task' do + task_note = find_note('marked the task **Task 2** as incomplete') + description_notes = find_notes('description') - expect(note).to be_nil + expect(task_note).to be_nil + expect(description_notes.length).to eq(2) end end @@ -368,9 +387,11 @@ describe Issues::UpdateService, services: true do end it 'does not create a system note referencing the position the old item' do - note = find_note('marked the task **Two** as incomplete') + task_note = find_note('marked the task **Two** as incomplete') + description_notes = find_notes('description') - expect(note).to be_nil + expect(task_note).to be_nil + expect(description_notes.length).to eq(2) end it 'does not generate a new note at all' do @@ -400,7 +421,9 @@ describe Issues::UpdateService, services: true do context 'when remove_label_ids and label_ids are passed' do let(:params) { { label_ids: [], remove_label_ids: [label.id] } } - before { issue.update_attributes(labels: [label, label3]) } + before do + issue.update_attributes(labels: [label, label3]) + end it 'ignores the label_ids parameter' do expect(result.label_ids).not_to be_empty @@ -414,7 +437,9 @@ describe Issues::UpdateService, services: true do context 'when add_label_ids and remove_label_ids are passed' do let(:params) { { add_label_ids: [label3.id], remove_label_ids: [label.id] } } - before { issue.update_attributes(labels: [label]) } + before do + issue.update_attributes(labels: [label]) + end it 'adds the passed labels' do expect(result.label_ids).to include(label3.id) @@ -463,7 +488,28 @@ describe Issues::UpdateService, services: true do context 'updating mentions' do let(:mentionable) { issue } - include_examples 'updating mentions', Issues::UpdateService + include_examples 'updating mentions', described_class + end + + context 'duplicate issue' do + let(:canonical_issue) { create(:issue, project: project) } + + context 'invalid canonical_issue_id' do + it 'does not call the duplicate service' do + expect(Issues::DuplicateService).not_to receive(:new) + + update_issue(canonical_issue_id: 123456789) + end + end + + context 'valid canonical_issue_id' do + it 'calls the duplicate service with both issues' do + expect_any_instance_of(Issues::DuplicateService) + .to receive(:execute).with(issue, canonical_issue) + + update_issue(canonical_issue_id: canonical_issue.id) + end + end end include_examples 'issuable update service' do diff --git a/spec/services/labels/create_service_spec.rb b/spec/services/labels/create_service_spec.rb index 0ecab0c3475..438e6dbc628 100644 --- a/spec/services/labels/create_service_spec.rb +++ b/spec/services/labels/create_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Labels::CreateService, services: true do +describe Labels::CreateService do describe '#execute' do let(:project) { create(:project) } let(:group) { create(:group) } - + let(:hex_color) { '#FF0000' } let(:named_color) { 'red' } let(:upcase_color) { 'RED' } @@ -17,7 +17,7 @@ describe Labels::CreateService, services: true do context 'in a project' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(project: project) + label = described_class.new(params_with(hex_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -26,7 +26,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(project: project) + label = described_class.new(params_with(named_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -35,7 +35,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(project: project) + label = described_class.new(params_with(upcase_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -44,7 +44,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(project: project) + label = described_class.new(params_with(spaced_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -53,7 +53,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(project: project) + label = described_class.new(params_with(unknown_color)).execute(project: project) expect(label).not_to be_persisted end @@ -61,7 +61,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(project: project) + label = described_class.new(params_with(no_color)).execute(project: project) expect(label).not_to be_persisted end @@ -71,7 +71,7 @@ describe Labels::CreateService, services: true do context 'in a group' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(group: group) + label = described_class.new(params_with(hex_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -80,7 +80,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(group: group) + label = described_class.new(params_with(named_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -89,7 +89,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(group: group) + label = described_class.new(params_with(upcase_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -98,7 +98,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(group: group) + label = described_class.new(params_with(spaced_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -107,7 +107,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(group: group) + label = described_class.new(params_with(unknown_color)).execute(group: group) expect(label).not_to be_persisted end @@ -115,7 +115,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(group: group) + label = described_class.new(params_with(no_color)).execute(group: group) expect(label).not_to be_persisted end @@ -125,7 +125,7 @@ describe Labels::CreateService, services: true do context 'in admin area' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(template: true) + label = described_class.new(params_with(hex_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -134,7 +134,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(template: true) + label = described_class.new(params_with(named_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -143,7 +143,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(template: true) + label = described_class.new(params_with(upcase_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -152,7 +152,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(template: true) + label = described_class.new(params_with(spaced_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -161,7 +161,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(template: true) + label = described_class.new(params_with(unknown_color)).execute(template: true) expect(label).not_to be_persisted end @@ -169,7 +169,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(template: true) + label = described_class.new(params_with(no_color)).execute(template: true) expect(label).not_to be_persisted end diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb index de8f1827cce..a781fbc7f7d 100644 --- a/spec/services/labels/find_or_create_service_spec.rb +++ b/spec/services/labels/find_or_create_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Labels::FindOrCreateService, services: true do +describe Labels::FindOrCreateService do describe '#execute' do let(:group) { create(:group) } - let(:project) { create(:empty_project, namespace: group) } + let(:project) { create(:project, namespace: group) } let(:params) do { diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb index 4b90ad19640..8809b282127 100644 --- a/spec/services/labels/promote_service_spec.rb +++ b/spec/services/labels/promote_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe Labels::PromoteService, services: true do +describe Labels::PromoteService do describe '#execute' do let!(:user) { create(:user) } context 'project without group' do - let!(:project_1) { create(:empty_project) } + let!(:project_1) { create(:project) } let!(:project_label_1_1) { create(:label, project: project_1) } @@ -27,10 +27,10 @@ describe Labels::PromoteService, services: true do let!(:group_1) { create(:group) } let!(:group_2) { create(:group) } - let!(:project_1) { create(:empty_project, namespace: group_1) } - let!(:project_2) { create(:empty_project, namespace: group_1) } - let!(:project_3) { create(:empty_project, namespace: group_1) } - let!(:project_4) { create(:empty_project, namespace: group_2) } + let!(:project_1) { create(:project, namespace: group_1) } + let!(:project_2) { create(:project, namespace: group_1) } + let!(:project_3) { create(:project, namespace: group_1) } + let!(:project_4) { create(:project, namespace: group_2) } # Labels/issues can't be lazily created so we might as well eager initialize # all other objects too since we use them inside @@ -66,9 +66,9 @@ describe Labels::PromoteService, services: true do end it 'recreates the label as a group label' do - expect { service.execute(project_label_1_1) }. - to change(project_1.labels, :count).by(-1). - and change(group_1.labels, :count).by(1) + expect { service.execute(project_label_1_1) } + .to change(project_1.labels, :count).by(-1) + .and change(group_1.labels, :count).by(1) expect(new_label).not_to be_nil end diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb index 11d5f1cad5e..ae819c011de 100644 --- a/spec/services/labels/transfer_service_spec.rb +++ b/spec/services/labels/transfer_service_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Labels::TransferService, services: true do +describe Labels::TransferService do describe '#execute' do let(:user) { create(:admin) } let(:group_1) { create(:group) } let(:group_2) { create(:group) } let(:group_3) { create(:group) } - let(:project_1) { create(:empty_project, namespace: group_2) } - let(:project_2) { create(:empty_project, namespace: group_3) } + let(:project_1) { create(:project, namespace: group_2) } + let(:project_2) { create(:project, namespace: group_3) } let(:group_label_1) { create(:group_label, group: group_1, name: 'Group Label 1') } let(:group_label_2) { create(:group_label, group: group_1, name: 'Group Label 2') } diff --git a/spec/services/labels/update_service_spec.rb b/spec/services/labels/update_service_spec.rb index f2498ea6990..bb95fe20fbf 100644 --- a/spec/services/labels/update_service_spec.rb +++ b/spec/services/labels/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::UpdateService, services: true do +describe Labels::UpdateService do describe '#execute' do let(:project) { create(:project) } @@ -20,7 +20,7 @@ describe Labels::UpdateService, services: true do context 'with color in hex-code' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(hex_color)).execute(@label) + label = described_class.new(params_with(hex_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -29,7 +29,7 @@ describe Labels::UpdateService, services: true do context 'with color in allowed name' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(named_color)).execute(@label) + label = described_class.new(params_with(named_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -38,7 +38,7 @@ describe Labels::UpdateService, services: true do context 'with color in up-case allowed name' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(upcase_color)).execute(@label) + label = described_class.new(params_with(upcase_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -47,7 +47,7 @@ describe Labels::UpdateService, services: true do context 'with color surrounded by spaces' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(spaced_color)).execute(@label) + label = described_class.new(params_with(spaced_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -56,7 +56,7 @@ describe Labels::UpdateService, services: true do context 'with unknown color' do it 'doesn\'t update the label' do - label = Labels::UpdateService.new(params_with(unknown_color)).execute(@label) + label = described_class.new(params_with(unknown_color)).execute(@label) expect(label).not_to be_valid end @@ -64,7 +64,7 @@ describe Labels::UpdateService, services: true do context 'with no color' do it 'doesn\'t update the label' do - label = Labels::UpdateService.new(params_with(no_color)).execute(@label) + label = described_class.new(params_with(no_color)).execute(@label) expect(label).not_to be_valid end diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb index 7d5a66801db..302c488d6c6 100644 --- a/spec/services/members/approve_access_request_service_spec.rb +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Members::ApproveAccessRequestService, services: true do +describe Members::ApproveAccessRequestService do let(:user) { create(:user) } let(:access_requester) { create(:user) } - let(:project) { create(:empty_project, :public, :access_requestable) } + let(:project) { create(:project, :public, :access_requestable) } let(:group) { create(:group, :public, :access_requestable) } let(:opts) { {} } diff --git a/spec/services/members/authorized_destroy_service_spec.rb b/spec/services/members/authorized_destroy_service_spec.rb index f99b11f208c..2d04d824180 100644 --- a/spec/services/members/authorized_destroy_service_spec.rb +++ b/spec/services/members/authorized_destroy_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Members::AuthorizedDestroyService, services: true do +describe Members::AuthorizedDestroyService do let(:member_user) { create(:user) } - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:group) { create(:group, :public) } - let(:group_project) { create(:empty_project, :public, group: group) } + let(:group_project) { create(:project, :public, group: group) } def number_of_assigned_issuables(user) Issue.assigned_to(user).count + MergeRequest.assigned_to(user).count diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb index 5ce8e17976b..2a793e0eb4d 100644 --- a/spec/services/members/create_service_spec.rb +++ b/spec/services/members/create_service_spec.rb @@ -1,11 +1,13 @@ require 'spec_helper' -describe Members::CreateService, services: true do - let(:project) { create(:empty_project) } +describe Members::CreateService do + let(:project) { create(:project) } let(:user) { create(:user) } let(:project_user) { create(:user) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'adds user to members' do params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST } diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 41450c67d7e..72f5e27180d 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Members::DestroyService, services: true do +describe Members::DestroyService do let(:user) { create(:user) } let(:member_user) { create(:user) } - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:group) { create(:group, :public) } shared_examples 'a service raising ActiveRecord::RecordNotFound' do @@ -104,8 +104,8 @@ describe Members::DestroyService, services: true do let(:params) { { id: project.members.find_by!(user_id: user.id).id } } it 'destroys the member' do - expect { described_class.new(project, user, params).execute }. - to change { project.members.count }.by(-1) + expect { described_class.new(project, user, params).execute } + .to change { project.members.count }.by(-1) end end end diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 814c17b9ad0..0a704bba521 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::RequestAccessService, services: true do +describe Members::RequestAccessService do let(:user) { create(:user) } shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do @@ -29,7 +29,7 @@ describe Members::RequestAccessService, services: true do end context 'when current user cannot request access to the project' do - %i[empty_project group].each do |source_type| + %i[project group].each do |source_type| it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { create(source_type, :private) } end @@ -37,7 +37,7 @@ describe Members::RequestAccessService, services: true do end context 'when access requests are disabled' do - %i[empty_project group].each do |source_type| + %i[project group].each do |source_type| it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do let(:source) { create(source_type, :public) } end @@ -45,7 +45,7 @@ describe Members::RequestAccessService, services: true do end context 'when current user can request access to the project' do - %i[empty_project group].each do |source_type| + %i[project group].each do |source_type| it_behaves_like 'a service creating a access request' do let(:source) { create(source_type, :public, :access_requestable) } end diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb index d3556020d4d..fcbe0e5985f 100644 --- a/spec/services/merge_requests/assign_issues_service_spec.rb +++ b/spec/services/merge_requests/assign_issues_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::AssignIssuesService, services: true do +describe MergeRequests::AssignIssuesService do let(:user) { create(:user) } let(:project) { create(:project, :public, :repository) } let(:issue) { create(:issue, project: project) } diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 6f9d1208b1d..b46c419de14 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::BuildService, services: true do +describe MergeRequests::BuildService do include RepoHelpers let(:project) { create(:project, :repository) } @@ -19,7 +19,7 @@ describe MergeRequests::BuildService, services: true do let(:commits) { nil } let(:service) do - MergeRequests::BuildService.new(project, user, + described_class.new(project, user, description: description, source_branch: source_branch, target_branch: target_branch, @@ -206,7 +206,9 @@ describe MergeRequests::BuildService, services: true do context 'branch starts with external issue IID followed by a hyphen' do let(:source_branch) { '12345-fix-issue' } - before { allow(project).to receive(:default_issues_tracker?).and_return(false) } + before do + allow(project).to receive(:external_issue_tracker).and_return(true) + end it 'sets the title to: Resolves External Issue $issue-iid' do expect(merge_request.title).to eq('Resolve External Issue 12345') @@ -262,8 +264,8 @@ describe MergeRequests::BuildService, services: true do end context 'upstream project has disabled merge requests' do - let(:upstream_project) { create(:empty_project, :merge_requests_disabled) } - let(:project) { create(:empty_project, forked_from_project: upstream_project) } + let(:upstream_project) { create(:project, :merge_requests_disabled) } + let(:project) { create(:project, forked_from_project: upstream_project) } let(:commits) { Commit.decorate([commit_1], project) } it 'sets target project correctly' do diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 154f30aac3b..04bf267d167 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CloseService, services: true do +describe MergeRequests::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } @@ -32,8 +32,8 @@ describe MergeRequests::CloseService, services: true do it { expect(@merge_request).to be_closed } it 'executes hooks with close action' do - expect(service).to have_received(:execute_hooks). - with(@merge_request, 'close') + expect(service).to have_received(:execute_hooks) + .with(@merge_request, 'close') end it 'sends email to user2 about assign of new merge_request' do diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb index c77e6e9cd50..6f49a65d795 100644 --- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb +++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb @@ -64,9 +64,9 @@ describe MergeRequests::Conflicts::ResolveService do end it 'creates a commit with the correct parents' do - expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 - 824be604a34828eb682305f0d963056cfac87b2d)) + expect(merge_request.source_branch_head.parents.map(&:id)) + .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end end @@ -129,9 +129,8 @@ describe MergeRequests::Conflicts::ResolveService do it 'creates a commit with the correct parents' do resolve_conflicts - expect(merge_request_from_fork.source_branch_head.parents.map(&:id)). - to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', - target_head]) + expect(merge_request_from_fork.source_branch_head.parents.map(&:id)) + .to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head]) end end end @@ -169,9 +168,9 @@ describe MergeRequests::Conflicts::ResolveService do end it 'creates a commit with the correct parents' do - expect(merge_request.source_branch_head.parents.map(&:id)). - to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 - 824be604a34828eb682305f0d963056cfac87b2d)) + expect(merge_request.source_branch_head.parents.map(&:id)) + .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06 + 824be604a34828eb682305f0d963056cfac87b2d)) end it 'sets the content to the content given' do @@ -204,8 +203,8 @@ describe MergeRequests::Conflicts::ResolveService do end it 'raises a MissingResolution error' do - expect { service.execute(user, invalid_params) }. - to raise_error(Gitlab::Conflict::File::MissingResolution) + expect { service.execute(user, invalid_params) } + .to raise_error(Gitlab::Conflict::File::MissingResolution) end end @@ -230,8 +229,8 @@ describe MergeRequests::Conflicts::ResolveService do end it 'raises a MissingResolution error' do - expect { service.execute(user, invalid_params) }. - to raise_error(Gitlab::Conflict::File::MissingResolution) + expect { service.execute(user, invalid_params) } + .to raise_error(Gitlab::Conflict::File::MissingResolution) end end @@ -250,8 +249,8 @@ describe MergeRequests::Conflicts::ResolveService do end it 'raises a MissingFiles error' do - expect { service.execute(user, invalid_params) }. - to raise_error(described_class::MissingFiles) + expect { service.execute(user, invalid_params) } + .to raise_error(described_class::MissingFiles) end end end diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb index 1588d30c394..492b55cdece 100644 --- a/spec/services/merge_requests/create_from_issue_service_spec.rb +++ b/spec/services/merge_requests/create_from_issue_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CreateFromIssueService, services: true do +describe MergeRequests::CreateFromIssueService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:issue) { create(:issue, project: project) } diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 2963f62cc7d..8fef480274d 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CreateService, services: true do +describe MergeRequests::CreateService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:assignee) { create(:user) } @@ -83,9 +83,9 @@ describe MergeRequests::CreateService, services: true do let!(:pipeline_3) { create(:ci_pipeline, project: project, ref: "other_branch", project_id: project.id) } before do - project.merge_requests. - where(source_branch: opts[:source_branch], target_branch: opts[:target_branch]). - destroy_all + project.merge_requests + .where(source_branch: opts[:source_branch], target_branch: opts[:target_branch]) + .destroy_all end it 'sets head pipeline' do @@ -108,7 +108,7 @@ describe MergeRequests::CreateService, services: true do end end - it_behaves_like 'new issuable record that supports slash commands' do + it_behaves_like 'new issuable record that supports quick actions' do let(:default_params) do { source_branch: 'feature', @@ -117,7 +117,7 @@ describe MergeRequests::CreateService, services: true do end end - context 'Slash commands' do + context 'Quick actions' do context 'with assignee and milestone in params and command' do let(:merge_request) { described_class.new(project, user, opts).execute } let(:milestone) { create(:milestone, project: project) } @@ -150,7 +150,9 @@ describe MergeRequests::CreateService, services: true do context 'asssignee_id' do let(:assignee) { create(:user) } - before { project.team << [user, :master] } + before do + project.team << [user, :master] + end it 'removes assignee_id when user id is invalid' do opts = { title: 'Title', description: 'Description', assignee_id: -1 } diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index 4a7d8ab4c6c..672d86e4028 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -78,7 +78,7 @@ describe MergeRequests::GetUrlsService do end context 'pushing to existing branch and merge request is reopened' do - let!(:merge_request) { create(:merge_request, :reopened, source_project: project, source_branch: source_branch) } + let!(:merge_request) { create(:merge_request, :opened, source_project: project, source_branch: source_branch) } let(:changes) { existing_branch_changes } it_behaves_like 'show_merge_request_url' end diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb index 935f4710851..bb46e1dd9ab 100644 --- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb +++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb @@ -10,8 +10,8 @@ describe MergeRequests::MergeRequestDiffCacheService do expect(Rails.cache).to receive(:read).with(cache_key).and_return({}) expect(Rails.cache).to receive(:write).with(cache_key, anything) - allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => true)) - allow_any_instance_of(Repository).to receive(:diffable?).and_return(true) + allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(true) + allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(true) subject.execute(merge_request) end diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index d96f819e66a..e593bfeeaf7 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe MergeRequests::MergeService, services: true do +describe MergeRequests::MergeService do let(:user) { create(:user) } let(:user2) { create(:user) } - let(:merge_request) { create(:merge_request, assignee: user2) } + let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) } let(:project) { merge_request.project } before do @@ -13,7 +13,7 @@ describe MergeRequests::MergeService, services: true do describe '#execute' do context 'valid params' do - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } before do allow(service).to receive(:execute_hooks) @@ -81,7 +81,9 @@ describe MergeRequests::MergeService, services: true do end context "when jira_issue_transition_id is not present" do - before { allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) } + before do + allow_any_instance_of(JIRA::Resource::Issue).to receive(:resolution).and_return(nil) + end it "does not close issue" do allow(jira_tracker).to receive_messages(jira_issue_transition_id: nil) @@ -110,7 +112,7 @@ describe MergeRequests::MergeService, services: true do context 'closes related todos' do let(:merge_request) { create(:merge_request, assignee: user, author: user) } let(:project) { merge_request.project } - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } let!(:todo) do create(:todo, :assigned, project: project, @@ -131,23 +133,70 @@ describe MergeRequests::MergeService, services: true do it { expect(todo).to be_done } end - context 'remove source branch by author' do - let(:service) do - merge_request.merge_params['force_remove_source_branch'] = '1' - merge_request.save! - MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') + context 'source branch removal' do + context 'when the source branch is protected' do + let(:service) do + described_class.new(project, user, should_remove_source_branch: '1') + end + + before do + create(:protected_branch, project: project, name: merge_request.source_branch) + end + + it 'does not delete the source branch' do + expect(DeleteBranchService).not_to receive(:new) + service.execute(merge_request) + end end - it 'removes the source branch' do - expect(DeleteBranchService).to receive(:new). - with(merge_request.source_project, merge_request.author). - and_call_original - service.execute(merge_request) + context 'when the source branch is the default branch' do + let(:service) do + described_class.new(project, user, should_remove_source_branch: '1') + end + + before do + allow(project).to receive(:root_ref?).with(merge_request.source_branch).and_return(true) + end + + it 'does not delete the source branch' do + expect(DeleteBranchService).not_to receive(:new) + service.execute(merge_request) + end + end + + context 'when the source branch can be removed' do + context 'when MR author set the source branch to be removed' do + let(:service) do + merge_request.merge_params['force_remove_source_branch'] = '1' + merge_request.save! + described_class.new(project, user, commit_message: 'Awesome message') + end + + it 'removes the source branch using the author user' do + expect(DeleteBranchService).to receive(:new) + .with(merge_request.source_project, merge_request.author) + .and_call_original + service.execute(merge_request) + end + end + + context 'when MR merger set the source branch to be removed' do + let(:service) do + described_class.new(project, user, commit_message: 'Awesome message', should_remove_source_branch: '1') + end + + it 'removes the source branch using the current user' do + expect(DeleteBranchService).to receive(:new) + .with(merge_request.source_project, user) + .and_call_original + service.execute(merge_request) + end + end end end context "error handling" do - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } before do allow(Rails.logger).to receive(:error) diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb index a20b32eda5f..a37cdab8928 100644 --- a/spec/services/merge_requests/post_merge_service_spec.rb +++ b/spec/services/merge_requests/post_merge_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::PostMergeService, services: true do +describe MergeRequests::PostMergeService do let(:user) { create(:user) } let(:merge_request) { create(:merge_request, assignee: user) } let(:project) { merge_request.project } diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 1f109eab268..2af2485eeed 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe MergeRequests::RefreshService, services: true do +describe MergeRequests::RefreshService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } - let(:service) { MergeRequests::RefreshService } + let(:service) { described_class } describe '#execute' do before do @@ -57,8 +57,8 @@ describe MergeRequests::RefreshService, services: true do end it 'executes hooks with update action' do - expect(refresh_service).to have_received(:execute_hooks). - with(@merge_request, 'update', @oldrev) + expect(refresh_service).to have_received(:execute_hooks) + .with(@merge_request, 'update', @oldrev) expect(@merge_request.notes).not_to be_empty expect(@merge_request).to be_open @@ -83,8 +83,8 @@ describe MergeRequests::RefreshService, services: true do end it 'executes hooks with update action' do - expect(refresh_service).to have_received(:execute_hooks). - with(@merge_request, 'update', @oldrev) + expect(refresh_service).to have_received(:execute_hooks) + .with(@merge_request, 'update', @oldrev) expect(@merge_request.notes).not_to be_empty expect(@merge_request).to be_open @@ -98,18 +98,52 @@ describe MergeRequests::RefreshService, services: true do end context 'push to origin repo target branch' do - before do - service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') - reload_mrs + context 'when all MRs to the target branch had diffs' do + before do + service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') + reload_mrs + end + + it 'updates the merge state' do + expect(@merge_request.notes.last.note).to include('merged') + expect(@merge_request).to be_merged + expect(@fork_merge_request).to be_merged + expect(@fork_merge_request.notes.last.note).to include('merged') + expect(@build_failed_todo).to be_done + expect(@fork_build_failed_todo).to be_done + end end - it 'updates the merge state' do - expect(@merge_request.notes.last.note).to include('merged') - expect(@merge_request).to be_merged - expect(@fork_merge_request).to be_merged - expect(@fork_merge_request.notes.last.note).to include('merged') - expect(@build_failed_todo).to be_done - expect(@fork_build_failed_todo).to be_done + context 'when an MR to be closed was empty already' do + let!(:empty_fork_merge_request) do + create(:merge_request, + source_project: @fork_project, + source_branch: 'master', + target_branch: 'master', + target_project: @project) + end + + before do + # This spec already has a fake push, so pretend that we were targeting + # feature all along. + empty_fork_merge_request.update_columns(target_branch: 'feature') + + service.new(@project, @user).execute(@oldrev, @newrev, 'refs/heads/feature') + reload_mrs + empty_fork_merge_request.reload + end + + it 'only updates the non-empty MRs' do + expect(@merge_request).to be_merged + expect(@merge_request.notes.last.note).to include('merged') + + expect(@fork_merge_request).to be_merged + expect(@fork_merge_request.notes.last.note).to include('merged') + + expect(empty_fork_merge_request).to be_open + expect(empty_fork_merge_request.merge_request_diff.state).to eq('empty') + expect(empty_fork_merge_request.notes).to be_empty + end end end @@ -146,8 +180,8 @@ describe MergeRequests::RefreshService, services: true do end it 'executes hooks with update action' do - expect(refresh_service).to have_received(:execute_hooks). - with(@fork_merge_request, 'update', @oldrev) + expect(refresh_service).to have_received(:execute_hooks) + .with(@fork_merge_request, 'update', @oldrev) expect(@merge_request.notes).to be_empty expect(@merge_request).to be_open @@ -228,8 +262,8 @@ describe MergeRequests::RefreshService, services: true do let(:refresh_service) { service.new(@fork_project, @user) } it 'refreshes the merge request' do - expect(refresh_service).to receive(:execute_hooks). - with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA) + expect(refresh_service).to receive(:execute_hooks) + .with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA) allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev) refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master') diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index b6d4db2f922..f02af0c582e 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::ReopenService, services: true do +describe MergeRequests::ReopenService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } @@ -28,11 +28,11 @@ describe MergeRequests::ReopenService, services: true do end it { expect(merge_request).to be_valid } - it { expect(merge_request).to be_reopened } + it { expect(merge_request).to be_opened } it 'executes hooks with reopen action' do - expect(service).to have_received(:execute_hooks). - with(merge_request, 'reopen') + expect(service).to have_received(:execute_hooks) + .with(merge_request, 'reopen') end it 'sends email to user2 about reopen of merge_request' do diff --git a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb index 7ddd812e513..e3fd906fe7b 100644 --- a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb +++ b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::ResolvedDiscussionNotificationService, services: true do +describe MergeRequests::ResolvedDiscussionNotificationService do let(:merge_request) { create(:merge_request) } let(:user) { create(:user) } let(:project) { merge_request.project } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index d371fc68312..dd3ac9c4ac6 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::UpdateService, services: true do +describe MergeRequests::UpdateService do include EmailHelpers let(:project) { create(:project, :repository) } @@ -30,6 +30,13 @@ describe MergeRequests::UpdateService, services: true do end end + def find_notes(action) + @merge_request + .notes + .joins(:system_note_metadata) + .where(system_note_metadata: { action: action }) + end + def update_merge_request(opts) @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) @merge_request.reload @@ -48,7 +55,7 @@ describe MergeRequests::UpdateService, services: true do } end - let(:service) { MergeRequests::UpdateService.new(project, user, opts) } + let(:service) { described_class.new(project, user, opts) } before do allow(service).to receive(:execute_hooks) @@ -71,8 +78,8 @@ describe MergeRequests::UpdateService, services: true do end it 'executes hooks with update action' do - expect(service).to have_received(:execute_hooks). - with(@merge_request, 'update') + expect(service).to have_received(:execute_hooks) + .with(@merge_request, 'update') end it 'sends email to user2 about assign of new merge request and email to user3 about merge request unassignment' do @@ -138,7 +145,7 @@ describe MergeRequests::UpdateService, services: true do } end - let(:service) { MergeRequests::UpdateService.new(project, user, opts) } + let(:service) { described_class.new(project, user, opts) } context 'without pipeline' do before do @@ -188,8 +195,8 @@ describe MergeRequests::UpdateService, services: true do head_pipeline_of: merge_request ) - expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user). - and_return(service_mock) + expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user) + .and_return(service_mock) expect(service_mock).to receive(:execute).with(merge_request) end @@ -198,7 +205,7 @@ describe MergeRequests::UpdateService, services: true do context 'with a non-authorised user' do let(:visitor) { create(:user) } - let(:service) { MergeRequests::UpdateService.new(project, visitor, opts) } + let(:service) { described_class.new(project, visitor, opts) } before do merge_request.update_attribute(:merge_error, 'Error') @@ -289,13 +296,13 @@ describe MergeRequests::UpdateService, services: true do end context 'when the milestone change' do - before do + it 'marks pending todos as done' do update_merge_request({ milestone: create(:milestone) }) - end - it 'marks pending todos as done' do expect(pending_todo.reload).to be_done end + + it_behaves_like 'system notes for milestones' end context 'when the labels change' do @@ -341,7 +348,7 @@ describe MergeRequests::UpdateService, services: true do opts = { label_ids: [label.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_email(subscriber) @@ -349,13 +356,15 @@ describe MergeRequests::UpdateService, services: true do end context 'when issue has the `label` label' do - before { merge_request.labels << label } + before do + merge_request.labels << label + end it 'does not send notifications for existing labels' do opts = { label_ids: [label.id, label2.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_not_email(subscriber) @@ -366,7 +375,7 @@ describe MergeRequests::UpdateService, services: true do opts = { label_ids: [label2.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_not_email(subscriber) @@ -377,16 +386,20 @@ describe MergeRequests::UpdateService, services: true do context 'updating mentions' do let(:mentionable) { merge_request } - include_examples 'updating mentions', MergeRequests::UpdateService + include_examples 'updating mentions', described_class end context 'when MergeRequest has tasks' do - before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + before do + update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end it { expect(@merge_request.tasks?).to eq(true) } context 'when tasks are marked as completed' do - before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) } + before do + update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) + end it 'creates system note about task status change' do note1 = find_note('marked the task **Task 1** as completed') @@ -394,6 +407,9 @@ describe MergeRequests::UpdateService, services: true do expect(note1).not_to be_nil expect(note2).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) end end @@ -409,6 +425,9 @@ describe MergeRequests::UpdateService, services: true do expect(note1).not_to be_nil expect(note2).not_to be_nil + + description_notes = find_notes('description') + expect(description_notes.length).to eq(1) end end end diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb index d581b94ff43..2bdf224804d 100644 --- a/spec/services/milestones/close_service_spec.rb +++ b/spec/services/milestones/close_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Milestones::CloseService, services: true do +describe Milestones::CloseService do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) } before do @@ -11,7 +11,7 @@ describe Milestones::CloseService, services: true do describe '#execute' do before do - Milestones::CloseService.new(project, user, {}).execute(milestone) + described_class.new(project, user, {}).execute(milestone) end it { expect(milestone).to be_valid } diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb index 6d29edb449a..8837b91051d 100644 --- a/spec/services/milestones/create_service_spec.rb +++ b/spec/services/milestones/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Milestones::CreateService, services: true do - let(:project) { create(:empty_project) } +describe Milestones::CreateService do + let(:project) { create(:project) } let(:user) { create(:user) } describe '#execute' do @@ -14,7 +14,7 @@ describe Milestones::CreateService, services: true do description: 'Patch release to fix security issue' } - @milestone = Milestones::CreateService.new(project, user, opts).execute + @milestone = described_class.new(project, user, opts).execute end it { expect(@milestone).to be_valid } diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb new file mode 100644 index 00000000000..5739386dd0d --- /dev/null +++ b/spec/services/milestones/destroy_service_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Milestones::DestroyService do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) } + let(:issue) { create(:issue, project: project, milestone: milestone) } + let(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) } + + before do + project.team << [user, :master] + end + + def service + described_class.new(project, user, {}) + end + + describe '#execute' do + it 'deletes milestone' do + service.execute(milestone) + + expect { milestone.reload }.to raise_error ActiveRecord::RecordNotFound + end + + it 'deletes milestone id from issuables' do + service.execute(milestone) + + expect(issue.reload.milestone).to be_nil + expect(merge_request.reload.milestone).to be_nil + end + + context 'group milestones' do + let(:group) { create(:group) } + let(:group_milestone) { create(:milestone, group: group) } + + before do + project.update(namespace: group) + group.add_developer(user) + end + + it { expect(service.execute(group_milestone)).to be_nil } + + it 'does not update milestone issuables' do + expect(MergeRequests::UpdateService).not_to receive(:new) + expect(Issues::UpdateService).not_to receive(:new) + + service.execute(group_milestone) + end + end + end +end diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb index 39f06f8f8e7..a6cc2251e48 100644 --- a/spec/services/note_summary_spec.rb +++ b/spec/services/note_summary_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe NoteSummary, services: true do - let(:project) { build(:empty_project) } +describe NoteSummary do + let(:project) { build(:project) } let(:noteable) { build(:issue) } let(:user) { build(:user) } diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb index 133175769ca..6e1c1fe6c02 100644 --- a/spec/services/notes/build_service_spec.rb +++ b/spec/services/notes/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::BuildService, services: true do +describe Notes::BuildService do let(:note) { create(:discussion_note_on_issue) } let(:project) { note.project } let(:author) { note.author } diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 152c6d20daa..661d26946e7 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Notes::CreateService, services: true do - let(:project) { create(:empty_project) } +describe Notes::CreateService do + let(:project) { create(:project) } let(:issue) { create(:issue, project: project) } let(:user) { create(:user) } let(:opts) do diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb index f53f96e0c2b..c9a99a43edb 100644 --- a/spec/services/notes/destroy_service_spec.rb +++ b/spec/services/notes/destroy_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Notes::DestroyService, services: true do +describe Notes::DestroyService do describe '#execute' do it 'deletes a note' do - project = create(:empty_project) + project = create(:project) issue = create(:issue, project: project) note = create(:note, project: project, noteable: issue) diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb index e33a611929b..a2b3638059f 100644 --- a/spec/services/notes/post_process_service_spec.rb +++ b/spec/services/notes/post_process_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Notes::PostProcessService, services: true do - let(:project) { create(:empty_project) } +describe Notes::PostProcessService do + let(:project) { create(:project) } let(:issue) { create(:issue, project: project) } let(:user) { create(:user) } @@ -21,7 +21,7 @@ describe Notes::PostProcessService, services: true do expect(project).to receive(:execute_hooks) expect(project).to receive(:execute_services) - Notes::PostProcessService.new(@note).execute + described_class.new(@note).execute end end end diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb index c9954dc3603..0280a19098b 100644 --- a/spec/services/notes/slash_commands_service_spec.rb +++ b/spec/services/notes/quick_actions_service_spec.rb @@ -1,15 +1,17 @@ require 'spec_helper' -describe Notes::SlashCommandsService, services: true do +describe Notes::QuickActionsService do shared_context 'note on noteable' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:assignee) { create(:user) } - before { project.team << [assignee, :master] } + before do + project.team << [assignee, :master] + end end - shared_examples 'note on noteable that does not support slash commands' do + shared_examples 'note on noteable that does not support quick actions' do include_context 'note on noteable' before do @@ -43,7 +45,7 @@ describe Notes::SlashCommandsService, services: true do end end - shared_examples 'note on noteable that supports slash commands' do + shared_examples 'note on noteable that supports quick actions' do include_context 'note on noteable' before do @@ -208,22 +210,22 @@ describe Notes::SlashCommandsService, services: true do describe '#execute' do let(:service) { described_class.new(project, master) } - it_behaves_like 'note on noteable that supports slash commands' do + it_behaves_like 'note on noteable that supports quick actions' do let(:note) { build(:note_on_issue, project: project) } end - it_behaves_like 'note on noteable that supports slash commands' do + it_behaves_like 'note on noteable that supports quick actions' do let(:note) { build(:note_on_merge_request, project: project) } end - it_behaves_like 'note on noteable that does not support slash commands' do + it_behaves_like 'note on noteable that does not support quick actions' do let(:note) { build(:note_on_commit, project: project) } end end context 'CE restriction for issue assignees' do describe '/assign' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } let(:assignee) { create(:user) } let(:master) { create(:user) } diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb index 905e2f46bde..3210539f3ee 100644 --- a/spec/services/notes/update_service_spec.rb +++ b/spec/services/notes/update_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Notes::UpdateService, services: true do - let(:project) { create(:empty_project) } +describe Notes::UpdateService do + let(:project) { create(:project) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb new file mode 100644 index 00000000000..0eb0771fd29 --- /dev/null +++ b/spec/services/notification_recipient_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe NotificationRecipientService do + set(:user) { create(:user) } + set(:project) { create(:project, :public) } + set(:issue) { create(:issue, project: project) } + + set(:watcher) do + watcher = create(:user) + setting = watcher.notification_settings_for(project) + setting.level = :watch + setting.save + + watcher + end + + subject { described_class.new(project) } + + describe '#build_recipients' do + it 'does not modify the participants of the target' do + expect { subject.build_recipients(issue, user, action: :new_issue) } + .not_to change { issue.participants(user) } + end + end + + describe '#build_new_note_recipients' do + set(:note) { create(:note_on_issue, noteable: issue, project: project) } + + it 'does not modify the participants of the target' do + expect { subject.build_new_note_recipients(note) } + .not_to change { note.noteable.participants(note.author) } + end + end +end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index de3bbc6b6a1..882ee7751b5 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe NotificationService, services: true do +describe NotificationService do include EmailHelpers - let(:notification) { NotificationService.new } + let(:notification) { described_class.new } let(:assignee) { create(:user) } around(:each) do |example| @@ -93,6 +93,18 @@ describe NotificationService, services: true do end end + describe 'GpgKeys' do + describe '#new_gpg_key' do + let!(:key) { create(:gpg_key) } + + it { expect(notification.new_gpg_key(key)).to be_truthy } + + it 'sends email to key owner' do + expect{ notification.new_gpg_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + end + end + end + describe 'Email' do describe '#new_email' do let!(:email) { create(:email) } @@ -107,7 +119,7 @@ describe NotificationService, services: true do describe 'Notes' do context 'issue note' do - let(:project) { create(:empty_project, :private) } + let(:project) { create(:project, :private) } let(:issue) { create(:issue, project: project, assignees: [assignee]) } let(:mentioned_issue) { create(:issue, assignees: issue.assignees) } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @outsider also') } @@ -216,7 +228,7 @@ describe NotificationService, services: true do end context 'confidential issue note' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:author) { create(:user) } let(:assignee) { create(:user) } let(:non_member) { create(:user) } @@ -248,7 +260,7 @@ describe NotificationService, services: true do end context 'issue note mention' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project, assignees: [assignee]) } let(:mentioned_issue) { create(:issue, assignees: issue.assignees) } let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@all mentioned') } @@ -291,7 +303,7 @@ describe NotificationService, services: true do end context 'project snippet note' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') } @@ -383,7 +395,7 @@ describe NotificationService, services: true do before do build_team(note.project) reset_delivered_emails! - allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) + allow(note.noteable).to receive(:author).and_return(@u_committer) update_custom_notification(:new_note, @u_guest_custom, resource: project) update_custom_notification(:new_note, @u_custom_global) end @@ -452,8 +464,8 @@ describe NotificationService, services: true do describe 'Issues' do let(:group) { create(:group) } - let(:project) { create(:empty_project, :public, namespace: group) } - let(:another_project) { create(:empty_project, :public, namespace: group) } + let(:project) { create(:project, :public, namespace: group) } + let(:another_project) { create(:project, :public, namespace: group) } let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant' } before do @@ -682,17 +694,6 @@ describe NotificationService, services: true do let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } } let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } } - it "emails subscribers of the issue's added labels only" do - notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) - - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) - end - it "emails the current user if they've opted into notifications about their activity" do subscriber_to_label_2.notified_of_own_activity = true notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) @@ -709,6 +710,12 @@ describe NotificationService, services: true do it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) + should_not_email(subscriber_to_label_1) + should_not_email(subscriber_to_group_label_1) + should_not_email(subscriber_to_group_label_2_on_another_project) + should_email(subscriber_1_to_group_label_2) + should_email(subscriber_2_to_group_label_2) + should_email(subscriber_to_label_2) should_not_email(issue.assignees.first) should_not_email(issue.author) should_not_email(@u_watcher) @@ -718,12 +725,6 @@ describe NotificationService, services: true do should_not_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) end context 'confidential issues' do @@ -854,7 +855,7 @@ describe NotificationService, services: true do describe 'Merge Requests' do let(:group) { create(:group) } let(:project) { create(:project, :public, :repository, namespace: group) } - let(:another_project) { create(:empty_project, :public, namespace: group) } + let(:another_project) { create(:project, :public, namespace: group) } let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' } before do @@ -866,11 +867,6 @@ describe NotificationService, services: true do end describe '#new_merge_request' do - before do - update_custom_notification(:new_merge_request, @u_guest_custom, resource: project) - update_custom_notification(:new_merge_request, @u_custom_global) - end - it do notification.new_merge_request(merge_request, @u_disabled) @@ -996,7 +992,7 @@ describe NotificationService, services: true do let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } } let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } } - it "emails subscribers of the merge request's added labels only" do + it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled) should_not_email(subscriber_to_label_1) @@ -1005,11 +1001,6 @@ describe NotificationService, services: true do should_email(subscriber_1_to_group_label_2) should_email(subscriber_2_to_group_label_2) should_email(subscriber_to_label_2) - end - - it "doesn't send email to anyone but subscribers of the given labels" do - notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled) - should_not_email(merge_request.assignee) should_not_email(merge_request.author) should_not_email(@u_watcher) @@ -1019,12 +1010,6 @@ describe NotificationService, services: true do should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_lazy_participant) - should_not_email(subscriber_to_label_1) - should_not_email(subscriber_to_group_label_1) - should_not_email(subscriber_to_group_label_2_on_another_project) - should_email(subscriber_1_to_group_label_2) - should_email(subscriber_2_to_group_label_2) - should_email(subscriber_to_label_2) end end @@ -1069,12 +1054,12 @@ describe NotificationService, services: true do should_email(merge_request.assignee) should_email(@u_watcher) + should_email(@u_guest_watcher) + should_email(@u_guest_custom) + should_email(@u_custom_global) should_email(@u_participant_mentioned) should_email(@subscriber) should_email(@watcher_and_subscriber) - should_email(@u_guest_watcher) - should_email(@u_custom_global) - should_email(@u_guest_custom) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -1165,7 +1150,7 @@ describe NotificationService, services: true do end describe 'Projects' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } before do build_team(project) @@ -1226,7 +1211,7 @@ describe NotificationService, services: true do describe 'ProjectMember' do describe '#decline_group_invite' do - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:member) { create(:user) } before(:each) do @@ -1244,7 +1229,7 @@ describe NotificationService, services: true do end context 'guest user in private project' do - let(:private_project) { create(:empty_project, :private) } + let(:private_project) { create(:project, :private) } let(:guest) { create(:user) } let(:developer) { create(:user) } let(:assignee) { create(:user) } @@ -1539,8 +1524,7 @@ describe NotificationService, services: true do # When resource is nil it means global notification def update_custom_notification(event, user, resource: nil, value: true) setting = user.notification_settings_for(resource) - setting.events[event] = value - setting.save + setting.update!(event => value) end def add_users_with_subscription(project, issuable) diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb index aa63fe3a5c1..f8db6900a0a 100644 --- a/spec/services/pages_service_spec.rb +++ b/spec/services/pages_service_spec.rb @@ -1,19 +1,23 @@ require 'spec_helper' -describe PagesService, services: true do +describe PagesService do let(:build) { create(:ci_build) } let(:data) { Gitlab::DataBuilder::Build.build(build) } - let(:service) { PagesService.new(data) } + let(:service) { described_class.new(data) } before do allow(Gitlab.config.pages).to receive(:enabled).and_return(true) end context 'execute asynchronously for pages job' do - before { build.name = 'pages' } + before do + build.name = 'pages' + end context 'on success' do - before { build.success } + before do + build.success + end it 'executes worker' do expect(PagesWorker).to receive(:perform_async) @@ -23,7 +27,9 @@ describe PagesService, services: true do %w(pending running failed canceled).each do |status| context "on #{status}" do - before { build.status = status } + before do + build.status = status + end it 'does not execute worker' do expect(PagesWorker).not_to receive(:perform_async) diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb index b2fb5c91313..64a9559791f 100644 --- a/spec/services/preview_markdown_service_spec.rb +++ b/spec/services/preview_markdown_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe PreviewMarkdownService do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } before do project.add_developer(user) @@ -19,24 +19,24 @@ describe PreviewMarkdownService do end end - context 'new note with slash commands' do + context 'new note with quick actions' do let(:issue) { create(:issue, project: project) } let(:params) do { text: "Please do it\n/assign #{user.to_reference}", - slash_commands_target_type: 'Issue', - slash_commands_target_id: issue.id + quick_actions_target_type: 'Issue', + quick_actions_target_id: issue.id } end let(:service) { described_class.new(project, user, params) } - it 'removes slash commands from text' do + it 'removes quick actions from text' do result = service.execute expect(result[:text]).to eq 'Please do it' end - it 'explains slash commands effect' do + it 'explains quick actions effect' do result = service.execute expect(result[:commands]).to eq "Assigns #{user.to_reference}." @@ -47,21 +47,21 @@ describe PreviewMarkdownService do let(:params) do { text: "My work\n/estimate 2y", - slash_commands_target_type: 'MergeRequest' + quick_actions_target_type: 'MergeRequest' } end let(:service) { described_class.new(project, user, params) } - it 'removes slash commands from text' do + it 'removes quick actions from text' do result = service.execute expect(result[:text]).to eq 'My work' end - it 'explains slash commands effect' do + it 'explains quick actions effect' do result = service.execute expect(result[:commands]).to eq 'Sets time estimate to 2y.' - end + end end end diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb index c198c3eedfc..c1f098530bf 100644 --- a/spec/services/projects/autocomplete_service_spec.rb +++ b/spec/services/projects/autocomplete_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::AutocompleteService, services: true do +describe Projects::AutocompleteService do describe '#issues' do describe 'confidential issues' do let(:author) { create(:user) } @@ -8,7 +8,7 @@ describe Projects::AutocompleteService, services: true do let(:non_member) { create(:user) } let(:member) { create(:user) } let(:admin) { create(:admin) } - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let!(:issue) { create(:issue, project: project, title: 'Issue 1') } let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) } let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignees: [assignee]) } diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 3c566c04d6b..b0dc7488b5f 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::CreateService, '#execute', services: true do +describe Projects::CreateService, '#execute' do let(:user) { create :user } let(:opts) do { @@ -115,7 +115,7 @@ describe Projects::CreateService, '#execute', services: true do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) opts.merge!( - visibility_level: Gitlab::VisibilityLevel.options['Public'] + visibility_level: Gitlab::VisibilityLevel::PUBLIC ) end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 0d6dd28e332..85b05ef6d05 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::DestroyService, services: true do +describe Projects::DestroyService do let!(:user) { create(:user) } let!(:project) { create(:project, :repository, namespace: user.namespace) } let!(:path) { project.repository.path_to_repo } @@ -15,8 +15,9 @@ describe Projects::DestroyService, services: true do shared_examples 'deleting the project' do it 'deletes the project' do expect(Project.unscoped.all).not_to include(project) - expect(Dir.exist?(path)).to be_falsey - expect(Dir.exist?(remove_path)).to be_falsey + + expect(project.gitlab_shell.exists?(project.repository_storage_path, path + '.git')).to be_falsey + expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path + '.git')).to be_falsey end end @@ -35,6 +36,27 @@ describe Projects::DestroyService, services: true do end end + shared_examples 'handles errors thrown during async destroy' do |error_message| + it 'does not allow the error to bubble up' do + expect do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + end.not_to raise_error + end + + it 'unmarks the project as "pending deletion"' do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + + expect(project.reload.pending_delete).to be(false) + end + + it 'stores an error message in `projects.delete_error`' do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + + expect(project.reload.delete_error).to be_present + expect(project.delete_error).to include(error_message) + end + end + context 'Sidekiq inline' do before do # Run sidekiq immediatly to check that renamed repository will be removed @@ -59,14 +81,14 @@ describe Projects::DestroyService, services: true do before do new_user = create(:user) project.team.add_user(new_user, Gitlab::Access::DEVELOPER) - allow_any_instance_of(Projects::DestroyService).to receive(:flush_caches).and_raise(Redis::CannotConnectError) + allow_any_instance_of(described_class).to receive(:flush_caches).and_raise(::Redis::CannotConnectError) end it 'keeps project team intact upon an error' do Sidekiq::Testing.inline! do begin destroy_project(project, user, {}) - rescue Redis::CannotConnectError + rescue ::Redis::CannotConnectError end end @@ -88,10 +110,51 @@ describe Projects::DestroyService, services: true do end it_behaves_like 'deleting the project with pipeline and build' - end - context 'with execute' do - it_behaves_like 'deleting the project with pipeline and build' + context 'errors' do + context 'when `remove_legacy_registry_tags` fails' do + before do + expect_any_instance_of(described_class) + .to receive(:remove_legacy_registry_tags).and_return(false) + end + + it_behaves_like 'handles errors thrown during async destroy', "Failed to remove some tags" + end + + context 'when `remove_repository` fails' do + before do + expect_any_instance_of(described_class) + .to receive(:remove_repository).and_return(false) + end + + it_behaves_like 'handles errors thrown during async destroy', "Failed to remove project repository" + end + + context 'when `execute` raises expected error' do + before do + expect_any_instance_of(Project) + .to receive(:destroy!).and_raise(StandardError.new("Other error message")) + end + + it_behaves_like 'handles errors thrown during async destroy', "Other error message" + end + + context 'when `execute` raises unexpected error' do + before do + expect_any_instance_of(Project) + .to receive(:destroy!).and_raise(Exception.new("Other error message")) + end + + it 'allows error to bubble up and rolls back project deletion' do + expect do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + end.to raise_error + + expect(project.reload.pending_delete).to be(false) + expect(project.delete_error).to include("Other error message") + end + end + end end describe 'container registry' do @@ -118,8 +181,7 @@ describe Projects::DestroyService, services: true do expect_any_instance_of(ContainerRepository) .to receive(:delete_tags!).and_return(false) - expect{ destroy_project(project, user) } - .to raise_error(ActiveRecord::RecordNotDestroyed) + expect(destroy_project(project, user)).to be false end end end @@ -144,8 +206,7 @@ describe Projects::DestroyService, services: true do expect_any_instance_of(ContainerRepository) .to receive(:delete_tags!).and_return(false) - expect { destroy_project(project, user) } - .to raise_error(Projects::DestroyService::DestroyError) + expect(destroy_project(project, user)).to be false end end end diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index 33b267c069c..da236052ebf 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Projects::DownloadService, services: true do +describe Projects::DownloadService do describe 'File service' do before do @user = create(:user) - @project = create(:empty_project, creator_id: @user.id, namespace: @user.namespace) + @project = create(:project, creator_id: @user.id, namespace: @user.namespace) end context 'for a URL that is not on whitelist' do diff --git a/spec/services/projects/enable_deploy_key_service_spec.rb b/spec/services/projects/enable_deploy_key_service_spec.rb index 78626fbad4b..835dae68fcd 100644 --- a/spec/services/projects/enable_deploy_key_service_spec.rb +++ b/spec/services/projects/enable_deploy_key_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Projects::EnableDeployKeyService, services: true do +describe Projects::EnableDeployKeyService do let(:deploy_key) { create(:deploy_key, public: true) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:user) { project.creator} let!(:params) { { key_id: deploy_key.id } } diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index f8eb34f2ef4..c90536ba346 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Projects::ForkService, services: true do +describe Projects::ForkService do describe 'fork by user' do before do - @from_namespace = create(:namespace) - @from_user = create(:user, namespace: @from_namespace ) + @from_user = create(:user) + @from_namespace = @from_user.namespace avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") @from_project = create(:project, :repository, @@ -13,8 +13,8 @@ describe Projects::ForkService, services: true do star_count: 107, avatar: avatar, description: 'wow such project') - @to_namespace = create(:namespace) - @to_user = create(:user, namespace: @to_namespace) + @to_user = create(:user) + @to_namespace = @to_user.namespace @from_project.add_user(@to_user, :developer) end diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index fff12beed71..ebed802708d 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -66,14 +66,14 @@ describe Projects::HousekeepingService do allow(subject).to receive(:lease_key).and_return(:the_lease_key) # At push 200 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid). - exactly(1).times + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid) + .exactly(1).times # At push 50, 100, 150 - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid). - exactly(3).times + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid) + .exactly(3).times # At push 10, 20, ... (except those above) - expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid). - exactly(16).times + expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid) + .exactly(16).times 201.times do subject.increment! diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 44db299812f..c0ab1ea704d 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Projects::ImportService, services: true do - let!(:project) { create(:empty_project) } +describe Projects::ImportService do + let!(:project) { create(:project) } let(:user) { project.creator } subject { described_class.new(project, user) } @@ -26,7 +26,7 @@ describe Projects::ImportService, services: true do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The repository could not be created." + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created." end end @@ -52,7 +52,7 @@ describe Projects::ImportService, services: true do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" end it 'does not remove the GitHub remote' do @@ -86,7 +86,7 @@ describe Projects::ImportService, services: true do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Failed to import the repository" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository" end end end @@ -111,11 +111,11 @@ describe Projects::ImportService, services: true do end it 'flushes various caches' do - allow_any_instance_of(Repository).to receive(:fetch_remote). - and_return(true) + allow_any_instance_of(Repository).to receive(:fetch_remote) + .and_return(true) - allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute). - and_return(true) + allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute) + .and_return(true) expect_any_instance_of(Repository).to receive(:expire_content_cache) @@ -129,7 +129,7 @@ describe Projects::ImportService, services: true do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The remote data could not be imported." + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The remote data could not be imported." end it 'fails if importer raise an error' do @@ -139,7 +139,7 @@ describe Projects::ImportService, services: true do result = subject.execute expect(result[:status]).to eq :error - expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - Github: failed to connect API" + expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Github: failed to connect API" end it 'expires content cache after error' do diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb index 0657b7e93fe..0d18ceb8ff8 100644 --- a/spec/services/projects/participants_service_spec.rb +++ b/spec/services/projects/participants_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Projects::ParticipantsService, services: true do +describe Projects::ParticipantsService do describe '#groups' do describe 'avatar_url' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/dk.png')) } let(:user) { create(:user) } let!(:group_member) { create(:group_member, group: group, user: user) } @@ -13,7 +13,7 @@ describe Projects::ParticipantsService, services: true do groups = participants.groups expect(groups.size).to eq 1 - expect(groups.first[:avatar_url]).to eq("/uploads/group/avatar/#{group.id}/dk.png") + expect(groups.first[:avatar_url]).to eq("/uploads/-/system/group/avatar/#{group.id}/dk.png") end it 'should return an url for the avatar with relative url' do @@ -24,7 +24,7 @@ describe Projects::ParticipantsService, services: true do groups = participants.groups expect(groups.size).to eq 1 - expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/group/avatar/#{group.id}/dk.png") + expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/-/system/group/avatar/#{group.id}/dk.png") end end end diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb index 8a6a9f09f74..f4c59735c43 100644 --- a/spec/services/projects/propagate_service_template_spec.rb +++ b/spec/services/projects/propagate_service_template_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::PropagateServiceTemplate, services: true do +describe Projects::PropagateServiceTemplate do describe '.propagate' do let!(:service_template) do PushoverService.create( @@ -15,7 +15,7 @@ describe Projects::PropagateServiceTemplate, services: true do }) end - let!(:project) { create(:empty_project) } + let!(:project) { create(:project) } it 'creates services for projects' do expect(project.pushover_service).to be_nil @@ -60,8 +60,8 @@ describe Projects::PropagateServiceTemplate, services: true do Service.build_from_template(project.id, service_template).save! Service.build_from_template(project.id, other_service).save! - expect { described_class.propagate(service_template) }. - not_to change { Service.count } + expect { described_class.propagate(service_template) } + .not_to change { Service.count } end it 'creates the service containing the template attributes' do @@ -76,7 +76,7 @@ describe Projects::PropagateServiceTemplate, services: true do before do stub_const 'Projects::PropagateServiceTemplate::BATCH_SIZE', 3 - project_total.times { create(:empty_project) } + project_total.times { create(:project) } described_class.propagate(service_template) end @@ -90,8 +90,8 @@ describe Projects::PropagateServiceTemplate, services: true do it 'updates the project external tracker' do service_template.update!(category: 'issue_tracker', default: false) - expect { described_class.propagate(service_template) }. - to change { project.reload.has_external_issue_tracker }.to(true) + expect { described_class.propagate(service_template) } + .to change { project.reload.has_external_issue_tracker }.to(true) end end @@ -99,8 +99,8 @@ describe Projects::PropagateServiceTemplate, services: true do it 'updates the project external tracker' do service_template.update!(type: 'ExternalWikiService') - expect { described_class.propagate(service_template) }. - to change { project.reload.has_external_wiki }.to(true) + expect { described_class.propagate(service_template) } + .to change { project.reload.has_external_wiki }.to(true) end end end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index b957517c715..2cb60cbcfc4 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -1,16 +1,16 @@ require 'spec_helper' -describe Projects::TransferService, services: true do +describe Projects::TransferService do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, :repository, namespace: user.namespace) } context 'namespace -> namespace' do before do - allow_any_instance_of(Gitlab::UploadsTransfer). - to receive(:move_project).and_return(true) - allow_any_instance_of(Gitlab::PagesTransfer). - to receive(:move_project).and_return(true) + allow_any_instance_of(Gitlab::UploadsTransfer) + .to receive(:move_project).and_return(true) + allow_any_instance_of(Gitlab::PagesTransfer) + .to receive(:move_project).and_return(true) group.add_owner(user) @result = transfer_project(project, user, group) end @@ -19,6 +19,73 @@ describe Projects::TransferService, services: true do it { expect(project.namespace).to eq(group) } end + context 'when transfer succeeds' do + before do + group.add_owner(user) + end + + it 'sends notifications' do + expect_any_instance_of(NotificationService).to receive(:project_was_moved) + + transfer_project(project, user, group) + end + + it 'expires full_path cache' do + expect(project).to receive(:expires_full_path_cache) + + transfer_project(project, user, group) + end + + it 'executes system hooks' do + transfer_project(project, user, group) do |service| + expect(service).to receive(:execute_system_hooks) + end + end + end + + context 'when transfer fails' do + let!(:original_path) { project_path(project) } + + def attempt_project_transfer(&block) + expect do + transfer_project(project, user, group, &block) + end.to raise_error(ActiveRecord::ActiveRecordError) + end + + before do + group.add_owner(user) + + expect_any_instance_of(Labels::TransferService).to receive(:execute).and_raise(ActiveRecord::StatementInvalid, "PG ERROR") + end + + def project_path(project) + File.join(project.repository_storage_path, "#{project.disk_path}.git") + end + + def current_path + project_path(project) + end + + it 'rolls back repo location' do + attempt_project_transfer + + expect(Dir.exist?(original_path)).to be_truthy + expect(original_path).to eq current_path + end + + it "doesn't send move notifications" do + expect_any_instance_of(NotificationService).not_to receive(:project_was_moved) + + attempt_project_transfer + end + + it "doesn't run system hooks" do + attempt_project_transfer do |service| + expect(service).not_to receive(:execute_system_hooks) + end + end + end + context 'namespace -> no namespace' do before do @result = transfer_project(project, user, nil) @@ -53,18 +120,26 @@ describe Projects::TransferService, services: true do end def transfer_project(project, user, new_namespace) - Projects::TransferService.new(project, user).execute(new_namespace) + service = Projects::TransferService.new(project, user) + + yield(service) if block_given? + + service.execute(new_namespace) end context 'visibility level' do let(:internal_group) { create(:group, :internal) } - before { internal_group.add_owner(user) } + before do + internal_group.add_owner(user) + end context 'when namespace visibility level < project visibility level' do let(:public_project) { create(:project, :public, :repository, namespace: user.namespace) } - before { transfer_project(public_project, user, internal_group) } + before do + transfer_project(public_project, user, internal_group) + end it { expect(public_project.visibility_level).to eq(internal_group.visibility_level) } end @@ -72,7 +147,9 @@ describe Projects::TransferService, services: true do context 'when namespace visibility level > project visibility level' do let(:private_project) { create(:project, :private, :repository, namespace: user.namespace) } - before { transfer_project(private_project, user, internal_group) } + before do + transfer_project(private_project, user, internal_group) + end it { expect(private_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) } end @@ -106,9 +183,9 @@ describe Projects::TransferService, services: true do end it 'only schedules a single job for every user' do - expect(UserProjectAccessChangedService).to receive(:new). - with([owner.id, group_member.id]). - and_call_original + expect(UserProjectAccessChangedService).to receive(:new) + .with([owner.id, group_member.id]) + .and_call_original transfer_project(project, owner, group) end diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb index 23f5555d3e0..2ae8d5f7c54 100644 --- a/spec/services/projects/unlink_fork_service_spec.rb +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Projects::UnlinkForkService, services: true do - subject { Projects::UnlinkForkService.new(fork_project, user) } +describe Projects::UnlinkForkService do + subject { described_class.new(fork_project, user) } let(:fork_link) { create(:forked_project_link) } let(:fork_project) { fork_link.forked_to_project } @@ -12,9 +12,9 @@ describe Projects::UnlinkForkService, services: true do let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) } before do - allow(MergeRequests::CloseService).to receive(:new). - with(fork_project, user). - and_return(mr_close_service) + allow(MergeRequests::CloseService).to receive(:new) + .with(fork_project, user) + .and_return(mr_close_service) end it 'close all pending merge requests' do diff --git a/spec/services/projects/update_pages_configuration_service_spec.rb b/spec/services/projects/update_pages_configuration_service_spec.rb index 8b329bc21c3..e4d4e6ff3dd 100644 --- a/spec/services/projects/update_pages_configuration_service_spec.rb +++ b/spec/services/projects/update_pages_configuration_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Projects::UpdatePagesConfigurationService, services: true do - let(:project) { create(:empty_project) } +describe Projects::UpdatePagesConfigurationService do + let(:project) { create(:project) } subject { described_class.new(project) } describe "#update" do diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index fc0a17296f3..aa6ad6340f5 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -96,6 +96,78 @@ describe Projects::UpdatePagesService do expect(execute).not_to eq(:success) end + describe 'maximum pages artifacts size' do + let(:metadata) { spy('metadata') } + + before do + file = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip') + metafile = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') + + build.update_attributes(artifacts_file: file) + build.update_attributes(artifacts_metadata: metafile) + + allow(build).to receive(:artifacts_metadata_entry) + .and_return(metadata) + end + + shared_examples 'pages size limit exceeded' do + it 'limits the maximum size of gitlab pages' do + subject.execute + + expect(deploy_status.description) + .to match(/artifacts for pages are too large/) + end + end + + context 'when maximum pages size is set to zero' do + before do + stub_application_setting(max_pages_size: 0) + end + + context 'when page size does not exceed internal maximum' do + before do + allow(metadata).to receive(:total_size).and_return(200.megabytes) + end + + it 'updates pages correctly' do + subject.execute + + expect(deploy_status.description).not_to be_present + end + end + + context 'when pages size does exceed internal maximum' do + before do + allow(metadata).to receive(:total_size).and_return(2.terabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + end + + context 'when pages size is greater than max size setting' do + before do + stub_application_setting(max_pages_size: 200) + allow(metadata).to receive(:total_size).and_return(201.megabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + + context 'when max size setting is greater than internal max size' do + before do + stub_application_setting(max_pages_size: 3.terabytes / 1.megabyte) + allow(metadata).to receive(:total_size).and_return(2.terabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + end + + def deploy_status + GenericCommitStatus.find_by(name: 'pages:deploy') + end + def execute subject.execute[:status] end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 05b18fef061..d945e0aa1f0 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -1,11 +1,14 @@ require 'spec_helper' -describe Projects::UpdateService, services: true do +describe Projects::UpdateService, '#execute' do let(:user) { create(:user) } let(:admin) { create(:admin) } - let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) } - describe 'update_by_user' do + let(:project) do + create(:project, creator: user, namespace: user.namespace) + end + + context 'when changing visibility level' do context 'when visibility_level is INTERNAL' do it 'updates the project to internal' do result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) @@ -40,7 +43,7 @@ describe Projects::UpdateService, services: true do it 'does not update the project to public' do result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC) - expect(result).to eq({ status: :error, message: 'Visibility level unallowed' }) + expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' }) expect(project).to be_private end @@ -55,12 +58,13 @@ describe Projects::UpdateService, services: true do end end - describe 'visibility_level' do - let(:project) { create(:empty_project, :internal) } + describe 'when updating project that has forks' do + let(:project) { create(:project, :internal) } let(:forked_project) { create(:forked_project_with_submodules, :internal) } before do - forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id) + forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, + forked_from_project_id: project.id) forked_project.save end @@ -89,10 +93,45 @@ describe Projects::UpdateService, services: true do end end - it 'returns an error result when record cannot be updated' do - result = update_project(project, admin, { name: 'foo&bar' }) + context 'when updating a default branch' do + let(:project) { create(:project, :repository) } + + it 'changes a default branch' do + update_project(project, admin, default_branch: 'feature') + + expect(Project.find(project.id).default_branch).to eq 'feature' + end + end + + context 'when updating a project that contains container images' do + before do + stub_container_registry_config(enabled: true) + stub_container_registry_tags(repository: /image/, tags: %w[rc1]) + create(:container_repository, project: project, name: :image) + end + + it 'does not allow to rename the project' do + result = update_project(project, admin, path: 'renamed') + + expect(result).to include(status: :error) + expect(result[:message]).to match(/contains container registry tags/) + end + + it 'allows to update other settings' do + result = update_project(project, admin, public_builds: true) + + expect(result[:status]).to eq :success + expect(project.reload.public_builds).to be true + end + end + + context 'when passing invalid parameters' do + it 'returns an error result when record cannot be updated' do + result = update_project(project, admin, { name: 'foo&bar' }) - expect(result).to eq({ status: :error, message: 'Project could not be updated' }) + expect(result).to eq({ status: :error, + message: 'Project could not be updated!' }) + end end def update_project(project, user, opts) diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb index 6ea8f309981..835e83d6dba 100644 --- a/spec/services/protected_branches/create_service_spec.rb +++ b/spec/services/protected_branches/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe ProtectedBranches::CreateService, services: true do - let(:project) { create(:empty_project) } +describe ProtectedBranches::CreateService do + let(:project) { create(:project) } let(:user) { project.owner } let(:params) do { diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb index 62bdd49a4d7..5698101af54 100644 --- a/spec/services/protected_branches/update_service_spec.rb +++ b/spec/services/protected_branches/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedBranches::UpdateService, services: true do +describe ProtectedBranches::UpdateService do let(:protected_branch) { create(:protected_branch) } let(:project) { protected_branch.project } let(:user) { project.owner } diff --git a/spec/services/protected_tags/create_service_spec.rb b/spec/services/protected_tags/create_service_spec.rb index d91a58e8de5..c3ed95aaebf 100644 --- a/spec/services/protected_tags/create_service_spec.rb +++ b/spec/services/protected_tags/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe ProtectedTags::CreateService, services: true do - let(:project) { create(:empty_project) } +describe ProtectedTags::CreateService do + let(:project) { create(:project) } let(:user) { project.owner } let(:params) do { diff --git a/spec/services/protected_tags/update_service_spec.rb b/spec/services/protected_tags/update_service_spec.rb index e78fde4c48d..d333430088d 100644 --- a/spec/services/protected_tags/update_service_spec.rb +++ b/spec/services/protected_tags/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedTags::UpdateService, services: true do +describe ProtectedTags::UpdateService do let(:protected_tag) { create(:protected_tag) } let(:project) { protected_tag.project } let(:user) { project.owner } diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index e5e400ee281..b78ecfb61c4 100644 --- a/spec/services/slash_commands/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe SlashCommands::InterpretService, services: true do - let(:project) { create(:empty_project, :public) } +describe QuickActions::InterpretService do + let(:project) { create(:project, :public) } let(:developer) { create(:user) } let(:developer2) { create(:user) } let(:issue) { create(:issue, project: project) } @@ -9,13 +9,13 @@ describe SlashCommands::InterpretService, services: true do let(:inprogress) { create(:label, project: project, title: 'In Progress') } let(:bug) { create(:label, project: project, title: 'Bug') } let(:note) { build(:note, commit_id: merge_request.diff_head_sha) } + let(:service) { described_class.new(project, developer) } before do project.team << [developer, :developer] end describe '#execute' do - let(:service) { described_class.new(project, developer) } let(:merge_request) { create(:merge_request, source_project: project) } shared_examples 'reopen command' do @@ -261,6 +261,31 @@ describe SlashCommands::InterpretService, services: true do end end + shared_examples 'duplicate command' do + it 'fetches issue and populates canonical_issue_id if content contains /duplicate issue_reference' do + issue_duplicate # populate the issue + _, updates = service.execute(content, issuable) + + expect(updates).to eq(canonical_issue_id: issue_duplicate.id) + end + end + + shared_examples 'shrug command' do + it 'appends ¯\_(ツ)_/¯ to the comment' do + new_content, _ = service.execute(content, issuable) + + expect(new_content).to end_with(described_class::SHRUG) + end + end + + shared_examples 'tableflip command' do + it 'appends (╯°□°)╯︵ ┻━┻ to the comment' do + new_content, _ = service.execute(content, issuable) + + expect(new_content).to end_with(described_class::TABLEFLIP) + end + end + it_behaves_like 'reopen command' do let(:content) { '/reopen' } let(:issuable) { issue } @@ -359,18 +384,18 @@ describe SlashCommands::InterpretService, services: true do let(:content) { "/assign @#{developer.username}" } context 'Issue' do - it 'fetches assignee and populates assignee_id if content contains /assign' do + it 'fetches assignee and populates assignee_ids if content contains /assign' do _, updates = service.execute(content, issue) - expect(updates).to eq(assignee_ids: [developer.id]) + expect(updates[:assignee_ids]).to match_array([developer.id]) end end context 'Merge Request' do - it 'fetches assignee and populates assignee_id if content contains /assign' do + it 'fetches assignee and populates assignee_ids if content contains /assign' do _, updates = service.execute(content, merge_request) - expect(updates).to eq(assignee_id: developer.id) + expect(updates).to eq(assignee_ids: [developer.id]) end end end @@ -378,21 +403,23 @@ describe SlashCommands::InterpretService, services: true do context 'assign command with multiple assignees' do let(:content) { "/assign @#{developer.username} @#{developer2.username}" } - before{ project.team << [developer2, :developer] } + before do + project.team << [developer2, :developer] + end context 'Issue' do - it 'fetches assignee and populates assignee_id if content contains /assign' do + it 'fetches assignee and populates assignee_ids if content contains /assign' do _, updates = service.execute(content, issue) - expect(updates[:assignee_ids]).to match_array([developer.id, developer2.id]) + expect(updates[:assignee_ids]).to match_array([developer.id]) end end context 'Merge Request' do - it 'fetches assignee and populates assignee_id if content contains /assign' do + it 'fetches assignee and populates assignee_ids if content contains /assign' do _, updates = service.execute(content, merge_request) - expect(updates).to eq(assignee_id: developer.id) + expect(updates).to eq(assignee_ids: [developer.id]) end end end @@ -420,11 +447,11 @@ describe SlashCommands::InterpretService, services: true do end context 'Merge Request' do - it 'populates assignee_id: nil if content contains /unassign' do - merge_request.update(assignee_id: developer.id) + it 'populates assignee_ids: [] if content contains /unassign' do + merge_request.update(assignee_ids: [developer.id]) _, updates = service.execute(content, merge_request) - expect(updates).to eq(assignee_id: nil) + expect(updates).to eq(assignee_ids: []) end end end @@ -642,6 +669,41 @@ describe SlashCommands::InterpretService, services: true do let(:issuable) { issue } end + context '/duplicate command' do + it_behaves_like 'duplicate command' do + let(:issue_duplicate) { create(:issue, project: project) } + let(:content) { "/duplicate #{issue_duplicate.to_reference}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { '/duplicate' } + let(:issuable) { issue } + end + + context 'cross project references' do + it_behaves_like 'duplicate command' do + let(:other_project) { create(:project, :public) } + let(:issue_duplicate) { create(:issue, project: other_project) } + let(:content) { "/duplicate #{issue_duplicate.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { "/duplicate imaginary#1234" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:other_project) { create(:project, :private) } + let(:issue_duplicate) { create(:issue, project: other_project) } + + let(:content) { "/duplicate #{issue_duplicate.to_reference(project)}" } + let(:issuable) { issue } + end + end + end + context 'when current_user cannot :admin_issue' do let(:visitor) { create(:user) } let(:issue) { create(:issue, project: project, author: visitor) } @@ -691,6 +753,11 @@ describe SlashCommands::InterpretService, services: true do let(:content) { '/remove_due_date' } let(:issuable) { issue } end + + it_behaves_like 'empty command' do + let(:content) { '/duplicate #{issue.to_reference}' } + let(:issuable) { issue } + end end context '/award command' do @@ -724,6 +791,30 @@ describe SlashCommands::InterpretService, services: true do end end + context '/shrug command' do + it_behaves_like 'shrug command' do + let(:content) { '/shrug people are people' } + let(:issuable) { issue } + end + + it_behaves_like 'shrug command' do + let(:content) { '/shrug' } + let(:issuable) { issue } + end + end + + context '/tableflip command' do + it_behaves_like 'tableflip command' do + let(:content) { '/tableflip curse your sudden but enviable betrayal' } + let(:issuable) { issue } + end + + it_behaves_like 'tableflip command' do + let(:content) { '/tableflip' } + let(:issuable) { issue } + end + end + context '/target_branch command' do let(:non_empty_project) { create(:project, :repository) } let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) } @@ -798,7 +889,11 @@ describe SlashCommands::InterpretService, services: true do context 'if the project has multiple boards' do let(:issuable) { issue } - before { create(:board, project: project) } + + before do + create(:board, project: project) + end + it_behaves_like 'empty command' end diff --git a/spec/services/repair_ldap_blocked_user_service_spec.rb b/spec/services/repair_ldap_blocked_user_service_spec.rb index 87192457298..bf79cfe74b7 100644 --- a/spec/services/repair_ldap_blocked_user_service_spec.rb +++ b/spec/services/repair_ldap_blocked_user_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe RepairLdapBlockedUserService, services: true do +describe RepairLdapBlockedUserService do let(:user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:identity) { user.ldap_identity } - subject(:service) { RepairLdapBlockedUserService.new(user) } + subject(:service) { described_class.new(user) } describe '#execute' do it 'changes to normal block after destroying last ldap identity' do diff --git a/spec/services/repository_archive_clean_up_service_spec.rb b/spec/services/repository_archive_clean_up_service_spec.rb index 842585f9e54..2d7fa3f80f7 100644 --- a/spec/services/repository_archive_clean_up_service_spec.rb +++ b/spec/services/repository_archive_clean_up_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe RepositoryArchiveCleanUpService, services: true do +describe RepositoryArchiveCleanUpService do describe '#execute' do subject(:service) { described_class.new } diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb index cbf4f56213d..1309240b430 100644 --- a/spec/services/search/global_service_spec.rb +++ b/spec/services/search/global_service_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Search::GlobalService, services: true do +describe Search::GlobalService do let(:user) { create(:user) } let(:internal_user) { create(:user) } - let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') } - let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') } - let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') } - let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') } + let!(:found_project) { create(:project, :private, name: 'searchable_project') } + let!(:unfound_project) { create(:project, :private, name: 'unfound_project') } + let!(:internal_project) { create(:project, :internal, name: 'searchable_internal_project') } + let!(:public_project) { create(:project, :public, name: 'searchable_public_project') } before do found_project.add_master(user) @@ -16,7 +16,7 @@ describe Search::GlobalService, services: true do describe '#execute' do context 'unauthenticated' do it 'returns public projects only' do - results = Search::GlobalService.new(nil, search: "searchable").execute + results = described_class.new(nil, search: "searchable").execute expect(results.objects('projects')).to match_array [public_project] end @@ -24,19 +24,19 @@ describe Search::GlobalService, services: true do context 'authenticated' do it 'returns public, internal and private projects' do - results = Search::GlobalService.new(user, search: "searchable").execute + results = described_class.new(user, search: "searchable").execute expect(results.objects('projects')).to match_array [public_project, found_project, internal_project] end it 'returns only public & internal projects' do - results = Search::GlobalService.new(internal_user, search: "searchable").execute + results = described_class.new(internal_user, search: "searchable").execute expect(results.objects('projects')).to match_array [internal_project, public_project] end it 'namespace name is searchable' do - results = Search::GlobalService.new(user, search: found_project.namespace.path).execute + results = described_class.new(user, search: found_project.namespace.path).execute expect(results.objects('projects')).to match_array [found_project] end diff --git a/spec/services/search/group_service_spec.rb b/spec/services/search/group_service_spec.rb index 38f264f6e7b..cbc553a60cf 100644 --- a/spec/services/search/group_service_spec.rb +++ b/spec/services/search/group_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Search::GroupService, services: true do +describe Search::GroupService do shared_examples_for 'group search' do context 'finding projects by name' do let(:user) { create(:user) } @@ -8,16 +8,16 @@ describe Search::GroupService, services: true do let(:nested_group) { create(:group, :nested) } # These projects shouldn't be found - let!(:outside_project) { create(:empty_project, :public, name: "Outside #{term}") } - let!(:private_project) { create(:empty_project, :private, namespace: nested_group, name: "Private #{term}" )} - let!(:other_project) { create(:empty_project, :public, namespace: nested_group, name: term.reverse) } + let!(:outside_project) { create(:project, :public, name: "Outside #{term}") } + let!(:private_project) { create(:project, :private, namespace: nested_group, name: "Private #{term}" )} + let!(:other_project) { create(:project, :public, namespace: nested_group, name: term.reverse) } # These projects should be found - let!(:project1) { create(:empty_project, :internal, namespace: nested_group, name: "Inner #{term} 1") } - let!(:project2) { create(:empty_project, :internal, namespace: nested_group, name: "Inner #{term} 2") } - let!(:project3) { create(:empty_project, :internal, namespace: nested_group.parent, name: "Outer #{term}") } + let!(:project1) { create(:project, :internal, namespace: nested_group, name: "Inner #{term} 1") } + let!(:project2) { create(:project, :internal, namespace: nested_group, name: "Inner #{term} 2") } + let!(:project3) { create(:project, :internal, namespace: nested_group.parent, name: "Outer #{term}") } - let(:results) { Search::GroupService.new(user, search_group, search: term).execute } + let(:results) { described_class.new(user, search_group, search: term).execute } subject { results.objects('projects') } context 'in parent group' do diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb index 14f3301d9f4..eae9bd4f5cf 100644 --- a/spec/services/search/snippet_service_spec.rb +++ b/spec/services/search/snippet_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Search::SnippetService, services: true do +describe Search::SnippetService do let(:author) { create(:author) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let!(:public_snippet) { create(:snippet, :public, content: 'password: XXX') } let!(:internal_snippet) { create(:snippet, :internal, content: 'password: XXX') } diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 5cf989105d0..02de83a2df8 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' -describe SearchService, services: true do +describe SearchService do let(:user) { create(:user) } let(:accessible_group) { create(:group, :private) } let(:inaccessible_group) { create(:group, :private) } let!(:group_member) { create(:group_member, group: accessible_group, user: user) } - let!(:accessible_project) { create(:empty_project, :private, name: 'accessible_project') } - let!(:inaccessible_project) { create(:empty_project, :private, name: 'inaccessible_project') } + let!(:accessible_project) { create(:project, :private, name: 'accessible_project') } + let!(:inaccessible_project) { create(:project, :private, name: 'inaccessible_project') } let(:note) { create(:note_on_issue, project: accessible_project) } let(:snippet) { create(:snippet, author: user) } - let(:group_project) { create(:empty_project, group: accessible_group, name: 'group_project') } - let(:public_project) { create(:empty_project, :public, name: 'public_project') } + let(:group_project) { create(:project, group: accessible_group, name: 'group_project') } + let(:public_project) { create(:project, :public, name: 'public_project') } before do accessible_project.add_master(user) @@ -22,16 +22,16 @@ describe SearchService, services: true do describe '#project' do context 'when the project is accessible' do it 'returns the project' do - project = SearchService.new(user, project_id: accessible_project.id).project + project = described_class.new(user, project_id: accessible_project.id).project expect(project).to eq accessible_project end it 'returns the project for guests' do - search_project = create :empty_project + search_project = create :project search_project.add_guest(user) - project = SearchService.new(user, project_id: search_project.id).project + project = described_class.new(user, project_id: search_project.id).project expect(project).to eq search_project end @@ -39,7 +39,7 @@ describe SearchService, services: true do context 'when the project is not accessible' do it 'returns nil' do - project = SearchService.new(user, project_id: inaccessible_project.id).project + project = described_class.new(user, project_id: inaccessible_project.id).project expect(project).to be_nil end @@ -47,7 +47,7 @@ describe SearchService, services: true do context 'when there is no project_id' do it 'returns nil' do - project = SearchService.new(user).project + project = described_class.new(user).project expect(project).to be_nil end @@ -57,7 +57,7 @@ describe SearchService, services: true do describe '#group' do context 'when the group is accessible' do it 'returns the group' do - group = SearchService.new(user, group_id: accessible_group.id).group + group = described_class.new(user, group_id: accessible_group.id).group expect(group).to eq accessible_group end @@ -65,7 +65,7 @@ describe SearchService, services: true do context 'when the group is not accessible' do it 'returns nil' do - group = SearchService.new(user, group_id: inaccessible_group.id).group + group = described_class.new(user, group_id: inaccessible_group.id).group expect(group).to be_nil end @@ -73,7 +73,7 @@ describe SearchService, services: true do context 'when there is no group_id' do it 'returns nil' do - group = SearchService.new(user).group + group = described_class.new(user).group expect(group).to be_nil end @@ -83,7 +83,7 @@ describe SearchService, services: true do describe '#show_snippets?' do context 'when :snippets is \'true\'' do it 'returns true' do - show_snippets = SearchService.new(user, snippets: 'true').show_snippets? + show_snippets = described_class.new(user, snippets: 'true').show_snippets? expect(show_snippets).to be_truthy end @@ -91,7 +91,7 @@ describe SearchService, services: true do context 'when :snippets is not \'true\'' do it 'returns false' do - show_snippets = SearchService.new(user, snippets: 'tru').show_snippets? + show_snippets = described_class.new(user, snippets: 'tru').show_snippets? expect(show_snippets).to be_falsey end @@ -99,7 +99,7 @@ describe SearchService, services: true do context 'when :snippets is missing' do it 'returns false' do - show_snippets = SearchService.new(user).show_snippets? + show_snippets = described_class.new(user).show_snippets? expect(show_snippets).to be_falsey end @@ -110,7 +110,7 @@ describe SearchService, services: true do context 'with accessible project_id' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, project_id: accessible_project.id, scope: 'notes').scope + scope = described_class.new(user, project_id: accessible_project.id, scope: 'notes').scope expect(scope).to eq 'notes' end @@ -118,7 +118,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, project_id: accessible_project.id, scope: 'projects').scope + scope = described_class.new(user, project_id: accessible_project.id, scope: 'projects').scope expect(scope).to eq 'blobs' end @@ -126,7 +126,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user, project_id: accessible_project.id).scope + scope = described_class.new(user, project_id: accessible_project.id).scope expect(scope).to eq 'blobs' end @@ -136,7 +136,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, snippets: 'true', scope: 'snippet_titles').scope + scope = described_class.new(user, snippets: 'true', scope: 'snippet_titles').scope expect(scope).to eq 'snippet_titles' end @@ -144,7 +144,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, snippets: 'true', scope: 'projects').scope + scope = described_class.new(user, snippets: 'true', scope: 'projects').scope expect(scope).to eq 'snippet_blobs' end @@ -152,7 +152,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user, snippets: 'true').scope + scope = described_class.new(user, snippets: 'true').scope expect(scope).to eq 'snippet_blobs' end @@ -162,7 +162,7 @@ describe SearchService, services: true do context 'with no project_id, no snippets' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, scope: 'issues').scope + scope = described_class.new(user, scope: 'issues').scope expect(scope).to eq 'issues' end @@ -170,7 +170,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, scope: 'blobs').scope + scope = described_class.new(user, scope: 'blobs').scope expect(scope).to eq 'projects' end @@ -178,7 +178,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user).scope + scope = described_class.new(user).scope expect(scope).to eq 'projects' end @@ -189,7 +189,7 @@ describe SearchService, services: true do describe '#search_results' do context 'with accessible project_id' do it 'returns an instance of Gitlab::ProjectSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, project_id: accessible_project.id, scope: 'notes', @@ -201,7 +201,7 @@ describe SearchService, services: true do context 'with accessible project_id and \'true\' snippets' do it 'returns an instance of Gitlab::ProjectSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, project_id: accessible_project.id, snippets: 'true', @@ -214,7 +214,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do it 'returns an instance of Gitlab::SnippetSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, snippets: 'true', search: snippet.content).search_results @@ -225,7 +225,7 @@ describe SearchService, services: true do context 'with no project_id and no snippets' do it 'returns an instance of Gitlab::SearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, search: public_project.name).search_results @@ -237,7 +237,7 @@ describe SearchService, services: true do describe '#search_objects' do context 'with accessible project_id' do it 'returns objects in the project' do - search_objects = SearchService.new( + search_objects = described_class.new( user, project_id: accessible_project.id, scope: 'notes', @@ -249,7 +249,7 @@ describe SearchService, services: true do context 'with accessible project_id and \'true\' snippets' do it 'returns objects in the project' do - search_objects = SearchService.new( + search_objects = described_class.new( user, project_id: accessible_project.id, snippets: 'true', @@ -262,7 +262,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do it 'returns objects in snippets' do - search_objects = SearchService.new( + search_objects = described_class.new( user, snippets: 'true', search: snippet.content).search_objects @@ -273,7 +273,7 @@ describe SearchService, services: true do context 'with accessible group_id' do it 'returns objects in the group' do - search_objects = SearchService.new( + search_objects = described_class.new( user, group_id: accessible_group.id, search: group_project.name).search_objects @@ -284,7 +284,7 @@ describe SearchService, services: true do context 'with no project_id, group_id or snippets' do it 'returns objects in global' do - search_objects = SearchService.new( + search_objects = described_class.new( user, search: public_project.name).search_objects diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb index 74cba8c014b..a14dfa3f01f 100644 --- a/spec/services/spam_service_spec.rb +++ b/spec/services/spam_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SpamService, services: true do +describe SpamService do describe '#when_recaptcha_verified' do def check_spam(issue, request, recaptcha_verified) described_class.new(issue, request).when_recaptcha_verified(recaptcha_verified) do @@ -15,7 +15,7 @@ describe SpamService, services: true do end context 'when recaptcha was not verified' do - let(:project) { create(:empty_project, :public) } + let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project) } let(:request) { double(:request, env: {}) } @@ -70,7 +70,9 @@ describe SpamService, services: true do end context 'when not indicated as spam by akismet' do - before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) } + before do + allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) + end it 'returns false' do expect(check_spam(issue, request, false)).to be_falsey diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb index 63a1e78f274..817fa4262d5 100644 --- a/spec/services/submit_usage_ping_service_spec.rb +++ b/spec/services/submit_usage_ping_service_spec.rb @@ -92,8 +92,8 @@ describe SubmitUsagePingService do end def stub_response(body) - stub_request(:post, 'https://version.gitlab.com/usage_data'). - to_return( + stub_request(:post, 'https://version.gitlab.com/usage_data') + .to_return( headers: { 'Content-Type' => 'application/json' }, body: body.to_json ) diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 667059f230c..8b5d9187785 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe SystemHooksService, services: true do +describe SystemHooksService do let(:user) { create(:user) } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:project_member) { create(:project_member) } let(:key) { create(:key, user: user) } let(:deploy_key) { create(:key) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index c499b1bb343..e3805160b04 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe SystemNoteService, services: true do - include Gitlab::Routing.url_helpers +describe SystemNoteService do + include Gitlab::Routing - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:author) { create(:user) } let(:noteable) { create(:issue, project: project) } let(:issue) { noteable } @@ -333,8 +333,8 @@ describe SystemNoteService, services: true do end it 'sets the note text' do - expect(subject.note). - to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**" + expect(subject.note) + .to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**" end end end @@ -521,8 +521,8 @@ describe SystemNoteService, services: true do context 'when mentioner is not a MergeRequest' do it 'is falsey' do mentioner = noteable.dup - expect(described_class.cross_reference_disallowed?(noteable, mentioner)). - to be_falsey + expect(described_class.cross_reference_disallowed?(noteable, mentioner)) + .to be_falsey end end @@ -533,14 +533,14 @@ describe SystemNoteService, services: true do it 'is truthy when noteable is in commits' do expect(mentioner).to receive(:commits).and_return([noteable]) - expect(described_class.cross_reference_disallowed?(noteable, mentioner)). - to be_truthy + expect(described_class.cross_reference_disallowed?(noteable, mentioner)) + .to be_truthy end it 'is falsey when noteable is not in commits' do expect(mentioner).to receive(:commits).and_return([]) - expect(described_class.cross_reference_disallowed?(noteable, mentioner)). - to be_falsey + expect(described_class.cross_reference_disallowed?(noteable, mentioner)) + .to be_falsey end end @@ -548,8 +548,8 @@ describe SystemNoteService, services: true do let(:noteable) { ExternalIssue.new('EXT-1234', project) } it 'is truthy' do mentioner = noteable.dup - expect(described_class.cross_reference_disallowed?(noteable, mentioner)). - to be_truthy + expect(described_class.cross_reference_disallowed?(noteable, mentioner)) + .to be_truthy end end end @@ -566,13 +566,13 @@ describe SystemNoteService, services: true do end it 'is truthy when already mentioned' do - expect(described_class.cross_reference_exists?(noteable, commit0)). - to be_truthy + expect(described_class.cross_reference_exists?(noteable, commit0)) + .to be_truthy end it 'is falsey when not already mentioned' do - expect(described_class.cross_reference_exists?(noteable, commit1)). - to be_falsey + expect(described_class.cross_reference_exists?(noteable, commit1)) + .to be_falsey end context 'legacy capitalized cross reference' do @@ -583,8 +583,8 @@ describe SystemNoteService, services: true do end it 'is truthy when already mentioned' do - expect(described_class.cross_reference_exists?(noteable, commit0)). - to be_truthy + expect(described_class.cross_reference_exists?(noteable, commit0)) + .to be_truthy end end end @@ -596,13 +596,13 @@ describe SystemNoteService, services: true do end it 'is truthy when already mentioned' do - expect(described_class.cross_reference_exists?(commit0, commit1)). - to be_truthy + expect(described_class.cross_reference_exists?(commit0, commit1)) + .to be_truthy end it 'is falsey when not already mentioned' do - expect(described_class.cross_reference_exists?(commit1, commit0)). - to be_falsey + expect(described_class.cross_reference_exists?(commit1, commit0)) + .to be_falsey end context 'legacy capitalized cross reference' do @@ -613,8 +613,8 @@ describe SystemNoteService, services: true do end it 'is truthy when already mentioned' do - expect(described_class.cross_reference_exists?(commit0, commit1)). - to be_truthy + expect(described_class.cross_reference_exists?(commit0, commit1)) + .to be_truthy end end end @@ -629,8 +629,8 @@ describe SystemNoteService, services: true do end it 'is true when a fork mentions an external issue' do - expect(described_class.cross_reference_exists?(noteable, commit2)). - to be true + expect(described_class.cross_reference_exists?(noteable, commit2)) + .to be true end context 'legacy capitalized cross reference' do @@ -640,15 +640,15 @@ describe SystemNoteService, services: true do end it 'is true when a fork mentions an external issue' do - expect(described_class.cross_reference_exists?(noteable, commit2)). - to be true + expect(described_class.cross_reference_exists?(noteable, commit2)) + .to be true end end end end describe '.noteable_moved' do - let(:new_project) { create(:empty_project) } + let(:new_project) { create(:project) } let(:new_noteable) { create(:issue, project: new_project) } subject do @@ -667,7 +667,7 @@ describe SystemNoteService, services: true do end it 'mentions referenced project' do - expect(subject.note).to include new_project.path_with_namespace + expect(subject.note).to include new_project.full_path end end @@ -718,7 +718,7 @@ describe SystemNoteService, services: true do describe 'JIRA integration' do include JiraServiceHelper - let(:project) { create(:jira_project) } + let(:project) { create(:jira_project, :repository) } let(:author) { create(:user) } let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, :simple, target_project: project, source_project: project) } @@ -807,7 +807,7 @@ describe SystemNoteService, services: true do body: hash_including( GlobalID: "GitLab", object: { - url: namespace_project_commit_url(project.namespace, project, commit), + url: project_commit_url(project, commit), title: "GitLab: Mentioned on commit - #{commit.title}", icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, status: { resolved: false } @@ -833,7 +833,7 @@ describe SystemNoteService, services: true do body: hash_including( GlobalID: "GitLab", object: { - url: namespace_project_issue_url(project.namespace, project, issue), + url: project_issue_url(project, issue), title: "GitLab: Mentioned on issue - #{issue.title}", icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, status: { resolved: false } @@ -859,7 +859,7 @@ describe SystemNoteService, services: true do body: hash_including( GlobalID: "GitLab", object: { - url: namespace_project_snippet_url(project.namespace, project, snippet), + url: project_snippet_url(project, snippet), title: "GitLab: Mentioned on snippet - #{snippet.title}", icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" }, status: { resolved: false } @@ -873,7 +873,7 @@ describe SystemNoteService, services: true do describe "existing reference" do before do allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) - message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.path_with_namespace}|http://localhost/#{project.path_with_namespace}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" + message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.full_path}/commit/#{commit.id}]:\n'#{commit.title.chomp}'" allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)]) end @@ -1052,7 +1052,7 @@ describe SystemNoteService, services: true do let(:action) { 'task' } end - it "posts the 'marked as a Work In Progress from commit' system note" do + it "posts the 'marked the task as complete' system note" do expect(subject.note).to eq("marked the task **task** as completed") end end @@ -1098,7 +1098,57 @@ describe SystemNoteService, services: true do diff_id = merge_request.merge_request_diff.id line_code = change_position.line_code(project.repository) - expect(subject.note).to include(diffs_namespace_project_merge_request_url(project.namespace, project, merge_request, diff_id: diff_id, anchor: line_code)) + expect(subject.note).to include(diffs_project_merge_request_url(project, merge_request, diff_id: diff_id, anchor: line_code)) + end + end + + describe '.mark_duplicate_issue' do + subject { described_class.mark_duplicate_issue(noteable, project, author, canonical_issue) } + + context 'within the same project' do + let(:canonical_issue) { create(:issue, project: project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked this issue as a duplicate of #{canonical_issue.to_reference}" } + end + + context 'across different projects' do + let(:other_project) { create(:project) } + let(:canonical_issue) { create(:issue, project: other_project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}" } + end + end + + describe '.mark_canonical_issue_of_duplicate' do + subject { described_class.mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue) } + + context 'within the same project' do + let(:duplicate_issue) { create(:issue, project: project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference} as a duplicate of this issue" } + end + + context 'across different projects' do + let(:other_project) { create(:project) } + let(:duplicate_issue) { create(:issue, project: other_project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue" } end end end diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb index b9121b1de49..1b31ce29f7a 100644 --- a/spec/services/tags/create_service_spec.rb +++ b/spec/services/tags/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Tags::CreateService, services: true do +describe Tags::CreateService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } @@ -26,9 +26,9 @@ describe Tags::CreateService, services: true do context 'when tag already exists' do it 'returns an error' do - expect(repository).to receive(:add_tag). - with(user, 'v1.1.0', 'master', 'Foo'). - and_raise(Rugged::TagError) + expect(repository).to receive(:add_tag) + .with(user, 'v1.1.0', 'master', 'Foo') + .and_raise(Rugged::TagError) response = service.execute('v1.1.0', 'master', 'Foo') @@ -39,9 +39,9 @@ describe Tags::CreateService, services: true do context 'when pre-receive hook fails' do it 'returns an error' do - expect(repository).to receive(:add_tag). - with(user, 'v1.1.0', 'master', 'Foo'). - and_raise(GitHooksService::PreReceiveError, 'something went wrong') + expect(repository).to receive(:add_tag) + .with(user, 'v1.1.0', 'master', 'Foo') + .and_raise(GitHooksService::PreReceiveError, 'something went wrong') response = service.execute('v1.1.0', 'master', 'Foo') diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb index 28396fc3658..7c8c1dd0d3a 100644 --- a/spec/services/tags/destroy_service_spec.rb +++ b/spec/services/tags/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Tags::DestroyService, services: true do +describe Tags::DestroyService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb deleted file mode 100644 index f99fd8434c2..00000000000 --- a/spec/services/test_hook_service_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'spec_helper' - -describe TestHookService, services: true do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - let(:hook) { create(:project_hook, project: project) } - - describe '#execute' do - it "executes successfully" do - stub_request(:post, hook.url).to_return(status: 200) - expect(TestHookService.new.execute(hook, user)).to be_truthy - end - end -end diff --git a/spec/services/test_hooks/project_service_spec.rb b/spec/services/test_hooks/project_service_spec.rb new file mode 100644 index 00000000000..4218c15a3ce --- /dev/null +++ b/spec/services/test_hooks/project_service_spec.rb @@ -0,0 +1,188 @@ +require 'spec_helper' + +describe TestHooks::ProjectService do + let(:current_user) { create(:user) } + + describe '#execute' do + let(:project) { create(:project, :repository) } + let(:hook) { create(:project_hook, project: project) } + let(:service) { described_class.new(hook, current_user, trigger) } + let(:sample_data) { { data: 'sample' } } + let(:success_result) { { status: :success, http_status: 200, message: 'ok' } } + + context 'hook with not implemented test' do + let(:trigger) { 'not_implemented_events' } + + it 'returns error message' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Testing not available for this hook' }) + end + end + + context 'push_events' do + let(:trigger) { 'push_events' } + + it 'returns error message if not enough data' do + allow(project).to receive(:empty_repo?).and_return(true) + + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has at least one commit.' }) + end + + it 'executes hook' do + allow(project).to receive(:empty_repo?).and_return(false) + allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'tag_push_events' do + let(:trigger) { 'tag_push_events' } + + it 'returns error message if not enough data' do + allow(project).to receive(:empty_repo?).and_return(true) + + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has at least one commit.' }) + end + + it 'executes hook' do + allow(project).to receive(:empty_repo?).and_return(false) + allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'note_events' do + let(:trigger) { 'note_events' } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has notes.' }) + end + + it 'executes hook' do + allow(project).to receive(:notes).and_return([Note.new]) + allow(Gitlab::DataBuilder::Note).to receive(:build).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'issues_events' do + let(:trigger) { 'issues_events' } + let(:issue) { build(:issue) } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has issues.' }) + end + + it 'executes hook' do + allow(project).to receive(:issues).and_return([issue]) + allow(issue).to receive(:to_hook_data).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'confidential_issues_events' do + let(:trigger) { 'confidential_issues_events' } + let(:issue) { build(:issue) } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has issues.' }) + end + + it 'executes hook' do + allow(project).to receive(:issues).and_return([issue]) + allow(issue).to receive(:to_hook_data).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'merge_requests_events' do + let(:trigger) { 'merge_requests_events' } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has merge requests.' }) + end + + it 'executes hook' do + create(:merge_request, source_project: project) + allow_any_instance_of(MergeRequest).to receive(:to_hook_data).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'job_events' do + let(:trigger) { 'job_events' } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has CI jobs.' }) + end + + it 'executes hook' do + create(:ci_build, project: project) + allow(Gitlab::DataBuilder::Build).to receive(:build).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'pipeline_events' do + let(:trigger) { 'pipeline_events' } + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the project has CI pipelines.' }) + end + + it 'executes hook' do + create(:ci_empty_pipeline, project: project) + allow(Gitlab::DataBuilder::Pipeline).to receive(:build).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'wiki_page_events' do + let(:trigger) { 'wiki_page_events' } + + it 'returns error message if wiki disabled' do + allow(project).to receive(:wiki_enabled?).and_return(false) + + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the wiki is enabled and has pages.' }) + end + + it 'returns error message if not enough data' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Ensure the wiki is enabled and has pages.' }) + end + + it 'executes hook' do + create(:wiki_page, wiki: project.wiki) + allow(Gitlab::DataBuilder::WikiPage).to receive(:build).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + end +end diff --git a/spec/services/test_hooks/system_service_spec.rb b/spec/services/test_hooks/system_service_spec.rb new file mode 100644 index 00000000000..00d89924766 --- /dev/null +++ b/spec/services/test_hooks/system_service_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe TestHooks::SystemService do + let(:current_user) { create(:user) } + + describe '#execute' do + let(:project) { create(:project, :repository) } + let(:hook) { create(:system_hook) } + let(:service) { described_class.new(hook, current_user, trigger) } + let(:sample_data) { { data: 'sample' }} + let(:success_result) { { status: :success, http_status: 200, message: 'ok' } } + + before do + allow(Project).to receive(:first).and_return(project) + end + + context 'hook with not implemented test' do + let(:trigger) { 'not_implemented_events' } + + it 'returns error message' do + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: 'Testing not available for this hook' }) + end + end + + context 'push_events' do + let(:trigger) { 'push_events' } + + it 'returns error message if not enough data' do + allow(project).to receive(:empty_repo?).and_return(true) + + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has commits." }) + end + + it 'executes hook' do + allow(project).to receive(:empty_repo?).and_return(false) + allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'tag_push_events' do + let(:trigger) { 'tag_push_events' } + + it 'returns error message if not enough data' do + allow(project.repository).to receive(:tags).and_return([]) + + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has tags." }) + end + + it 'executes hook' do + allow(project.repository).to receive(:tags).and_return(['tag']) + allow(Gitlab::DataBuilder::Push).to receive(:build_sample).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + + context 'repository_update_events' do + let(:trigger) { 'repository_update_events' } + + it 'returns error message if not enough data' do + allow(project).to receive(:commit).and_return(nil) + expect(hook).not_to receive(:execute) + expect(service.execute).to include({ status: :error, message: "Ensure project \"#{project.human_name}\" has commits." }) + end + + it 'executes hook' do + allow(project).to receive(:empty_repo?).and_return(false) + allow(Gitlab::DataBuilder::Repository).to receive(:update).and_return(sample_data) + + expect(hook).to receive(:execute).with(sample_data, trigger).and_return(success_result) + expect(service.execute).to include(success_result) + end + end + end +end diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 175a42a32d9..534d3e65be2 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe TodoService, services: true do +describe TodoService do let(:author) { create(:user) } let(:assignee) { create(:user) } let(:non_member) { create(:user) } @@ -10,7 +10,7 @@ describe TodoService, services: true do let(:john_doe) { create(:user) } let(:skipped) { create(:user) } let(:skip_users) { [skipped] } - let(:project) { create(:empty_project) } + let(:project) { create(:project) } let(:mentions) { 'FYI: ' + [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') } let(:directly_addressed) { [author, assignee, john_doe, member, guest, non_member, admin, skipped].map(&:to_reference).join(' ') } let(:directly_addressed_and_mentioned) { member.to_reference + ", what do you think? cc: " + [guest, admin, skipped].map(&:to_reference).join(' ') } @@ -103,7 +103,7 @@ describe TodoService, services: true do context 'when a private group is mentioned' do let(:group) { create(:group, :private) } - let(:project) { create(:empty_project, :private, group: group) } + let(:project) { create(:project, :private, group: group) } let(:issue) { create(:issue, author: author, project: project, description: group.to_reference) } before do @@ -873,21 +873,21 @@ describe TodoService, services: true do create(:todo, :mentioned, user: john_doe, target: issue, project: project) todos = TodosFinder.new(john_doe, {}).execute - expect { TodoService.new.mark_todos_as_done(todos, john_doe) } + expect { described_class.new.mark_todos_as_done(todos, john_doe) } .to change { john_doe.todos.done.count }.from(0).to(1) end it 'marks an array of todos as done' do todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - expect { TodoService.new.mark_todos_as_done([todo], john_doe) } + expect { described_class.new.mark_todos_as_done([todo], john_doe) } .to change { todo.reload.state }.from('pending').to('done') end it 'returns the ids of updated todos' do # Needed on API todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - expect(TodoService.new.mark_todos_as_done([todo], john_doe)).to eq([todo.id]) + expect(described_class.new.mark_todos_as_done([todo], john_doe)).to eq([todo.id]) end context 'when some of the todos are done already' do @@ -895,23 +895,23 @@ describe TodoService, services: true do let!(:second_todo) { create(:todo, :mentioned, user: john_doe, target: another_issue, project: project) } it 'returns the ids of those still pending' do - TodoService.new.mark_pending_todos_as_done(issue, john_doe) + described_class.new.mark_pending_todos_as_done(issue, john_doe) - expect(TodoService.new.mark_todos_as_done(Todo.all, john_doe)).to eq([second_todo.id]) + expect(described_class.new.mark_todos_as_done(Todo.all, john_doe)).to eq([second_todo.id]) end it 'returns an empty array if all are done' do - TodoService.new.mark_pending_todos_as_done(issue, john_doe) - TodoService.new.mark_pending_todos_as_done(another_issue, john_doe) + described_class.new.mark_pending_todos_as_done(issue, john_doe) + described_class.new.mark_pending_todos_as_done(another_issue, john_doe) - expect(TodoService.new.mark_todos_as_done(Todo.all, john_doe)).to eq([]) + expect(described_class.new.mark_todos_as_done(Todo.all, john_doe)).to eq([]) end end - it 'caches the number of todos of a user', :caching do + it 'caches the number of todos of a user', :use_clean_rails_memory_store_caching do create(:todo, :mentioned, user: john_doe, target: issue, project: project) todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - TodoService.new.mark_todos_as_done([todo], john_doe) + described_class.new.mark_todos_as_done([todo], john_doe) expect_any_instance_of(TodosFinder).not_to receive(:execute) diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb index 69ed8de9c31..dc2d0e2d47a 100644 --- a/spec/services/update_release_service_spec.rb +++ b/spec/services/update_release_service_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe UpdateReleaseService, services: true do +describe UpdateReleaseService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } let(:new_description) { 'The best release!' } - let(:service) { UpdateReleaseService.new(project, user) } + let(:service) { described_class.new(project, user) } context 'with an existing release' do let(:create_service) { CreateReleaseService.new(project, user) } diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index 37c2e861362..ef535c5cf1f 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UpdateSnippetService, services: true do +describe UpdateSnippetService do before do @user = create :user @admin = create :user, admin: true diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb index 95ba28dbecd..24f3a5c5ff0 100644 --- a/spec/services/upload_service_spec.rb +++ b/spec/services/upload_service_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe UploadService, services: true do +describe UploadService do describe 'File service' do before do @user = create(:user) - @project = create(:empty_project, creator_id: @user.id, namespace: @user.namespace) + @project = create(:project, creator_id: @user.id, namespace: @user.namespace) end context 'for valid gif file' do diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb index b4efe7de431..14a5e40350a 100644 --- a/spec/services/user_project_access_changed_service_spec.rb +++ b/spec/services/user_project_access_changed_service_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe UserProjectAccessChangedService do describe '#execute' do it 'schedules the user IDs' do - expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait). - with([[1], [2]]) + expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait) + .with([[1], [2]]) described_class.new([1, 2]).execute end diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb index 8d67ebe3231..fef4da0c76e 100644 --- a/spec/services/users/activity_service_spec.rb +++ b/spec/services/users/activity_service_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Users::ActivityService, services: true do +describe Users::ActivityService do include UserActivitiesHelpers let(:user) { create(:user) } subject(:service) { described_class.new(user, 'type') } - describe '#execute', :redis do + describe '#execute', :clean_gitlab_redis_shared_state do context 'when last activity is nil' do before do service.execute @@ -41,8 +41,8 @@ describe Users::ActivityService, services: true do end def last_hour_user_ids - Gitlab::UserActivities.new. - select { |k, v| v >= 1.hour.ago.to_i.to_s }. - map { |k, _| k.to_i } + Gitlab::UserActivities.new + .select { |k, v| v >= 1.hour.ago.to_i.to_s } + .map { |k, _| k.to_i } end end diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb index 2a6bfc1b3a0..677d4a622e1 100644 --- a/spec/services/users/build_service_spec.rb +++ b/spec/services/users/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::BuildService, services: true do +describe Users::BuildService do describe '#execute' do let(:params) do { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' } diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index 75746278573..24dac569678 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::CreateService, services: true do +describe Users::CreateService do describe '#execute' do let(:admin_user) { create(:admin) } diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 5409f67c091..a82567f6f43 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe Users::DestroyService, services: true do +describe Users::DestroyService do describe "Deletes a user and all their personal projects" do let!(:user) { create(:user) } let!(:admin) { create(:admin) } let!(:namespace) { create(:namespace, owner: user) } - let!(:project) { create(:empty_project, namespace: namespace) } + let!(:project) { create(:project, namespace: namespace) } let(:service) { described_class.new(admin) } context 'no options are given' do @@ -66,7 +66,7 @@ describe Users::DestroyService, services: true do end context "a deleted user's merge_requests" do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.add_developer(user) diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb index 9e1edf1ac30..ac3a8738cac 100644 --- a/spec/services/users/migrate_to_ghost_user_service_spec.rb +++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb @@ -1,22 +1,38 @@ require 'spec_helper' -describe Users::MigrateToGhostUserService, services: true do +describe Users::MigrateToGhostUserService do let!(:user) { create(:user) } - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:service) { described_class.new(user) } context "migrating a user's associated records to the ghost user" do context 'issues' do - include_examples "migrating a deleted user's associated records to the ghost user", Issue do - let(:created_record) { create(:issue, project: project, author: user) } - let(:assigned_record) { create(:issue, project: project, assignee: user) } + context 'deleted user is present as both author and edited_user' do + include_examples "migrating a deleted user's associated records to the ghost user", Issue, [:author, :last_edited_by] do + let(:created_record) do + create(:issue, project: project, author: user, last_edited_by: user) + end + end + end + + context 'deleted user is present only as edited_user' do + include_examples "migrating a deleted user's associated records to the ghost user", Issue, [:last_edited_by] do + let(:created_record) { create(:issue, project: project, author: create(:user), last_edited_by: user) } + end end end context 'merge requests' do - include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest do - let(:created_record) { create(:merge_request, source_project: project, author: user, target_branch: "first") } - let(:assigned_record) { create(:merge_request, source_project: project, assignee: user, target_branch: 'second') } + context 'deleted user is present as both author and merge_user' do + include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest, [:author, :merge_user] do + let(:created_record) { create(:merge_request, source_project: project, author: user, merge_user: user, target_branch: "first") } + end + end + + context 'deleted user is present only as both merge_user' do + include_examples "migrating a deleted user's associated records to the ghost user", MergeRequest, [:merge_user] do + let(:created_record) { create(:merge_request, source_project: project, merge_user: user, target_branch: "first") } + end end end @@ -33,9 +49,8 @@ describe Users::MigrateToGhostUserService, services: true do end context 'award emoji' do - include_examples "migrating a deleted user's associated records to the ghost user", AwardEmoji do + include_examples "migrating a deleted user's associated records to the ghost user", AwardEmoji, [:user] do let(:created_record) { create(:award_emoji, user: user) } - let(:author_alias) { :user } context "when the awardable already has an award emoji of the same name assigned to the ghost user" do let(:awardable) { create(:issue) } diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index 8c40d25e00c..08fd26d67fd 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -3,18 +3,18 @@ require 'spec_helper' describe Users::RefreshAuthorizedProjectsService do # We're using let! here so that any expectations for the service class are not # triggered twice. - let!(:project) { create(:empty_project) } + let!(:project) { create(:project) } let(:user) { project.namespace.owner } let(:service) { described_class.new(user) } - describe '#execute', :redis do + describe '#execute', :clean_gitlab_redis_shared_state do it 'refreshes the authorizations using a lease' do - expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain). - and_return('foo') + expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) + .and_return('foo') - expect(Gitlab::ExclusiveLease).to receive(:cancel). - with(an_instance_of(String), 'foo') + expect(Gitlab::ExclusiveLease).to receive(:cancel) + .with(an_instance_of(String), 'foo') expect(service).to receive(:execute_without_lease) @@ -28,12 +28,12 @@ describe Users::RefreshAuthorizedProjectsService do end it 'updates the authorized projects of the user' do - project2 = create(:empty_project) - to_remove = user.project_authorizations. - create!(project: project2, access_level: Gitlab::Access::MASTER) + project2 = create(:project) + to_remove = user.project_authorizations + .create!(project: project2, access_level: Gitlab::Access::MASTER) - expect(service).to receive(:update_authorizations). - with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]]) + expect(service).to receive(:update_authorizations) + .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]]) service.execute_without_lease end @@ -41,11 +41,11 @@ describe Users::RefreshAuthorizedProjectsService do it 'sets the access level of a project to the highest available level' do user.project_authorizations.delete_all - to_remove = user.project_authorizations. - create!(project: project, access_level: Gitlab::Access::DEVELOPER) + to_remove = user.project_authorizations + .create!(project: project, access_level: Gitlab::Access::DEVELOPER) - expect(service).to receive(:update_authorizations). - with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]]) + expect(service).to receive(:update_authorizations) + .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]]) service.execute_without_lease end @@ -109,7 +109,7 @@ describe Users::RefreshAuthorizedProjectsService do end context 'projects the user is a member of' do - let!(:other_project) { create(:empty_project) } + let!(:other_project) { create(:project) } before do other_project.team.add_reporter(user) @@ -122,7 +122,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects of groups the user is a member of' do let(:group) { create(:group) } - let!(:other_project) { create(:empty_project, group: group) } + let!(:other_project) { create(:project, group: group) } before do group.add_owner(user) @@ -136,7 +136,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects of subgroups of groups the user is a member of', :nested_groups do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } - let!(:other_project) { create(:empty_project, group: nested_group) } + let!(:other_project) { create(:project, group: nested_group) } before do group.add_master(user) @@ -149,7 +149,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects shared with groups the user is a member of' do let(:group) { create(:group) } - let(:other_project) { create(:empty_project) } + let(:other_project) { create(:project) } let!(:project_group_link) { create(:project_group_link, project: other_project, group: group, group_access: Gitlab::Access::GUEST) } before do @@ -164,7 +164,7 @@ describe Users::RefreshAuthorizedProjectsService do context 'projects shared with subgroups of groups the user is a member of', :nested_groups do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } - let(:other_project) { create(:empty_project) } + let(:other_project) { create(:project) } let!(:project_group_link) { create(:project_group_link, project: other_project, group: nested_group, group_access: Gitlab::Access::DEVELOPER) } before do diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb new file mode 100644 index 00000000000..343804e3de0 --- /dev/null +++ b/spec/services/users/update_service_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Users::UpdateService do + let(:user) { create(:user) } + + describe '#execute' do + it 'updates the name' do + result = update_user(user, name: 'New Name') + + expect(result).to eq(status: :success) + expect(user.name).to eq('New Name') + end + + it 'returns an error result when record cannot be updated' do + expect do + update_user(user, { email: 'invalid' }) + end.not_to change { user.reload.email } + end + + def update_user(user, opts) + described_class.new(user, opts).execute + end + end + + describe '#execute!' do + it 'updates the name' do + result = update_user(user, name: 'New Name') + + expect(result).to be true + expect(user.name).to eq('New Name') + end + + it 'raises an error when record cannot be updated' do + expect do + update_user(user, email: 'invalid') + end.to raise_error(ActiveRecord::RecordInvalid) + end + + def update_user(user, opts) + described_class.new(user, opts).execute! + end + end +end diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index b5abc46e80c..79d90defd78 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe WebHookService, services: true do - let(:project) { create(:empty_project) } +describe WebHookService do + let(:project) { create(:project) } let(:project_hook) { create(:project_hook) } let(:headers) do { @@ -12,7 +12,7 @@ describe WebHookService, services: true do let(:data) do { before: 'oldrev', after: 'newrev', ref: 'ref' } end - let(:service_instance) { WebHookService.new(project_hook, data, 'push_hooks') } + let(:service_instance) { described_class.new(project_hook, data, 'push_hooks') } describe '#execute' do before(:each) do @@ -53,12 +53,12 @@ describe WebHookService, services: true do end it 'handles exceptions' do - exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout] + exceptions = [SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout, Net::ReadTimeout] exceptions.each do |exception_class| exception = exception_class.new('Exception message') WebMock.stub_request(:post, project_hook.url).to_raise(exception) - expect(service_instance.execute).to eq([nil, exception.message]) + expect(service_instance.execute).to eq({ status: :error, message: exception.message }) expect { service_instance.execute }.not_to raise_error end end @@ -66,13 +66,13 @@ describe WebHookService, services: true do it 'handles 200 status code' do WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: 'Success') - expect(service_instance.execute).to eq([200, 'Success']) + expect(service_instance.execute).to include({ status: :success, http_status: 200, message: 'Success' }) end it 'handles 2xx status codes' do WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: 'Success') - expect(service_instance.execute).to eq([201, 'Success']) + expect(service_instance.execute).to include({ status: :success, http_status: 201, message: 'Success' }) end context 'execution logging' do @@ -112,9 +112,26 @@ describe WebHookService, services: true do end end + context 'with unsafe response body' do + before do + WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "\xBB") + service_instance.execute + end + + it 'log successful execution' do + expect(hook_log.trigger).to eq('push_hooks') + expect(hook_log.url).to eq(project_hook.url) + expect(hook_log.request_headers).to eq(headers) + expect(hook_log.response_body).to eq('') + expect(hook_log.response_status).to eq('200') + expect(hook_log.execution_duration).to be > 0 + expect(hook_log.internal_error_message).to be_nil + end + end + context 'should not log ServiceHooks' do let(:service_hook) { create(:service_hook) } - let(:service_instance) { WebHookService.new(service_hook, data, 'service_hook') } + let(:service_instance) { described_class.new(service_hook, data, 'service_hook') } before do WebMock.stub_request(:post, service_hook.url).to_return(status: 200, body: 'Success') @@ -131,7 +148,7 @@ describe WebHookService, services: true do it 'enqueue WebHookWorker' do expect(Sidekiq::Client).to receive(:enqueue).with(WebHookWorker, project_hook.id, data, 'push_hooks') - WebHookService.new(project_hook, data, 'push_hooks').async_execute + described_class.new(project_hook, data, 'push_hooks').async_execute end end end diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb index 054e28ae7b0..b270194d9b8 100644 --- a/spec/services/wiki_pages/create_service_spec.rb +++ b/spec/services/wiki_pages/create_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe WikiPages::CreateService, services: true do - let(:project) { create(:empty_project) } +describe WikiPages::CreateService do + let(:project) { create(:project) } let(:user) { create(:user) } let(:opts) do diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb index 920be4d4c8a..2938126914b 100644 --- a/spec/services/wiki_pages/destroy_service_spec.rb +++ b/spec/services/wiki_pages/destroy_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe WikiPages::DestroyService, services: true do - let(:project) { create(:empty_project) } +describe WikiPages::DestroyService do + let(:project) { create(:project) } let(:user) { create(:user) } let(:page) { create(:wiki_page) } diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb index 5e36ea4cf94..a242bf5a5cc 100644 --- a/spec/services/wiki_pages/update_service_spec.rb +++ b/spec/services/wiki_pages/update_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe WikiPages::UpdateService, services: true do - let(:project) { create(:empty_project) } +describe WikiPages::UpdateService do + let(:project) { create(:project) } let(:user) { create(:user) } let(:page) { create(:wiki_page) } |