summaryrefslogtreecommitdiff
path: root/spec/services
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2016-12-18 23:39:51 +0100
committerKamil Trzcinski <ayufan@ayufan.eu>2016-12-18 23:39:51 +0100
commit14d47884dff6844625c2e65b247fd773d78f5ea2 (patch)
tree10fba027e61521df236fd6eec7ba829c5fe2c8ec /spec/services
parent9fd775def2d89500cf291fe675458b68ead7cd2c (diff)
parent546fa165ff728bc2d25ed9b55b95dd1d48139d4a (diff)
downloadgitlab-ce-dockerfile-templates.tar.gz
Merge remote-tracking branch 'origin/master' into dockerfile-templatesdockerfile-templates
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/access_token_validation_service_spec.rb41
-rw-r--r--spec/services/after_branch_delete_service_spec.rb15
-rw-r--r--spec/services/chat_names/authorize_user_service_spec.rb25
-rw-r--r--spec/services/chat_names/find_user_service_spec.rb36
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb17
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb137
-rw-r--r--spec/services/ci/send_pipeline_notification_service_spec.rb48
-rw-r--r--spec/services/ci/stop_environments_service_spec.rb105
-rw-r--r--spec/services/delete_branch_service_spec.rb41
-rw-r--r--spec/services/delete_merged_branches_service_spec.rb54
-rw-r--r--spec/services/destroy_group_service_spec.rb40
-rw-r--r--spec/services/discussions/resolve_service_spec.rb52
-rw-r--r--spec/services/git_push_service_spec.rb122
-rw-r--r--spec/services/git_tag_push_service_spec.rb8
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb6
-rw-r--r--spec/services/issues/build_service_spec.rb130
-rw-r--r--spec/services/issues/close_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb43
-rw-r--r--spec/services/issues/move_service_spec.rb10
-rw-r--r--spec/services/issues/update_service_spec.rb37
-rw-r--r--spec/services/labels/transfer_service_spec.rb2
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb4
-rw-r--r--spec/services/members/destroy_service_spec.rb1
-rw-r--r--spec/services/members/request_access_service_spec.rb31
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb45
-rw-r--r--spec/services/merge_requests/build_service_spec.rb12
-rw-r--r--spec/services/merge_requests/close_service_spec.rb2
-rw-r--r--spec/services/merge_requests/get_urls_service_spec.rb6
-rw-r--r--spec/services/merge_requests/merge_request_diff_cache_service_spec.rb1
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb22
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb (renamed from spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb)40
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb44
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb2
-rw-r--r--spec/services/merge_requests/update_service_spec.rb33
-rw-r--r--spec/services/notes/create_service_spec.rb37
-rw-r--r--spec/services/notification_service_spec.rb246
-rw-r--r--spec/services/projects/create_service_spec.rb207
-rw-r--r--spec/services/projects/destroy_service_spec.rb30
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb4
-rw-r--r--spec/services/system_note_service_spec.rb250
40 files changed, 1458 insertions, 530 deletions
diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb
new file mode 100644
index 00000000000..87f093ee8ce
--- /dev/null
+++ b/spec/services/access_token_validation_service_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe AccessTokenValidationService, services: true do
+ describe ".include_any_scope?" do
+ it "returns true if the required scope is present in the token's scopes" do
+ token = double("token", scopes: [:api, :read_user])
+
+ expect(described_class.new(token).include_any_scope?([:api])).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])
+
+ expect(described_class.new(token).include_any_scope?([:api, :other_scope])).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])
+
+ expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).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])
+
+ expect(described_class.new(token).include_any_scope?([:api, :read_user, :other_scope])).to be(true)
+ end
+
+ it 'returns true if the list of required scopes is blank' do
+ token = double("token", scopes: [])
+
+ expect(described_class.new(token).include_any_scope?([])).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])
+
+ expect(described_class.new(token).include_any_scope?([:other_scope])).to be(false)
+ end
+ end
+end
diff --git a/spec/services/after_branch_delete_service_spec.rb b/spec/services/after_branch_delete_service_spec.rb
new file mode 100644
index 00000000000..d29e0addb53
--- /dev/null
+++ b/spec/services/after_branch_delete_service_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe AfterBranchDeleteService, services: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ it 'stops environments attached to branch' do
+ expect(service).to receive(:stop_environments)
+
+ service.execute('feature')
+ end
+ end
+end
diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb
new file mode 100644
index 00000000000..d50bfb0492c
--- /dev/null
+++ b/spec/services/chat_names/authorize_user_service_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe ChatNames::AuthorizeUserService, services: true do
+ describe '#execute' do
+ let(:service) { create(:service) }
+
+ subject { described_class.new(service, params).execute }
+
+ context 'when all parameters are valid' do
+ let(:params) { { team_id: 'T0001', team_domain: 'myteam', user_id: 'U0001', user_name: 'user' } }
+
+ it 'requests a new token' do
+ is_expected.to be_url
+ end
+ end
+
+ context 'when there are missing parameters' do
+ let(:params) { {} }
+
+ it 'does not request a new token' do
+ is_expected.to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb
new file mode 100644
index 00000000000..51441e8f3be
--- /dev/null
+++ b/spec/services/chat_names/find_user_service_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe ChatNames::FindUserService, services: true do
+ describe '#execute' do
+ let(:service) { create(:service) }
+
+ subject { described_class.new(service, params).execute }
+
+ context 'find user mapping' do
+ let(:user) { create(:user) }
+ let!(:chat_name) { create(:chat_name, user: user, service: service) }
+
+ context 'when existing user is requested' do
+ let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } }
+
+ it 'returns the existing user' do
+ is_expected.to eq(user)
+ end
+
+ it 'updates when last time chat name was used' do
+ subject
+
+ expect(chat_name.reload.last_used_at).to be_like_time(Time.now)
+ end
+ end
+
+ context 'when different user is requested' do
+ let(:params) { { team_id: chat_name.team_id, user_id: 'non-existing-user' } }
+
+ it 'returns existing user' do
+ is_expected.to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 4aadd009f3e..ceaca96e25b 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -210,5 +210,22 @@ describe Ci::CreatePipelineService, services: true do
expect(result.manual_actions).not_to be_empty
end
end
+
+ context 'with environment' do
+ before do
+ config = YAML.dump(deploy: { environment: { name: "review/$CI_BUILD_REF_NAME" }, script: 'ls' })
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'creates the environment' do
+ result = execute(ref: 'refs/heads/master',
+ before: '00000000',
+ after: project.commit.id,
+ commits: [{ message: 'some msg' }])
+
+ expect(result).to be_persisted
+ expect(Environment.find_by(name: "review/master")).not_to be_nil
+ end
+ end
end
end
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index ff113efd916..ebb11166964 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -1,31 +1,10 @@
require 'spec_helper'
describe Ci::ProcessPipelineService, services: true do
- let(:pipeline) { create(:ci_pipeline, ref: 'master') }
+ let(:pipeline) { create(:ci_empty_pipeline, ref: 'master') }
let(:user) { create(:user) }
- let(:config) { nil }
-
- before do
- allow(pipeline).to receive(:ci_yaml_file).and_return(config)
- end
describe '#execute' do
- def all_builds
- pipeline.builds
- end
-
- def builds
- all_builds.where.not(status: [:created, :skipped])
- end
-
- def process_pipeline
- described_class.new(pipeline.project, user).execute(pipeline)
- end
-
- def succeed_pending
- builds.pending.update_all(status: 'success')
- end
-
context 'start queuing next builds' do
before do
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage_idx: 0)
@@ -223,10 +202,6 @@ describe Ci::ProcessPipelineService, services: true do
pipeline.builds.running_or_pending.each(&:success)
expect(manual_actions).to be_many # production and clear cache
end
-
- def manual_actions
- pipeline.manual_actions
- end
end
end
@@ -282,15 +257,6 @@ describe Ci::ProcessPipelineService, services: true do
expect(builds.map(&:status)).to eq(%w[success skipped pending])
end
end
-
- def create_build(name, stage_idx, when_value = nil)
- create(:ci_build,
- :created,
- pipeline: pipeline,
- name: name,
- stage_idx: stage_idx,
- when: when_value)
- end
end
context 'when failed build in the middle stage is retried' do
@@ -327,65 +293,92 @@ describe Ci::ProcessPipelineService, services: true do
end
end
- context 'creates a builds from .gitlab-ci.yml' do
- let(:config) do
- YAML.dump({
- rspec: {
- stage: 'test',
- script: 'rspec'
- },
- rubocop: {
- stage: 'test',
- script: 'rubocop'
- },
- deploy: {
- stage: 'deploy',
- script: 'deploy'
- }
- })
+ context 'when there are builds that are not created yet' do
+ let(:pipeline) do
+ create(:ci_pipeline, config: config)
end
- # Using stubbed .gitlab-ci.yml created in commit factory
- #
+ let(:config) do
+ { rspec: { stage: 'test', script: 'rspec' },
+ deploy: { stage: 'deploy', script: 'rsync' } }
+ end
before do
- stub_ci_pipeline_yaml_file(config)
create(:ci_build, :created, pipeline: pipeline, name: 'linux', stage: 'build', stage_idx: 0)
create(:ci_build, :created, pipeline: pipeline, name: 'mac', stage: 'build', stage_idx: 0)
end
- it 'when processing a pipeline' do
- # Currently we have two builds with state created
+ it 'processes the pipeline' do
+ # Currently we have five builds with state created
+ #
expect(builds.count).to eq(0)
expect(all_builds.count).to eq(2)
- # Create builds will mark the created as pending
- expect(process_pipeline).to be_truthy
+ # Process builds service will enqueue builds from the first stage.
+ #
+ process_pipeline
+
expect(builds.count).to eq(2)
expect(all_builds.count).to eq(2)
- # When we builds succeed we will create a rest of pipeline from .gitlab-ci.yml
- # We will have 2 succeeded, 2 pending (from stage test), total 5 (one more build from deploy)
+ # When builds succeed we will enqueue remaining builds.
+ #
+ # We will have 2 succeeded, 1 pending (from stage test), total 4 (two
+ # additional build from `.gitlab-ci.yml`).
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.success.count).to eq(2)
- expect(builds.pending.count).to eq(2)
- expect(all_builds.count).to eq(5)
+ expect(builds.pending.count).to eq(1)
+ expect(all_builds.count).to eq(4)
- # When we succeed the 2 pending from stage test,
- # We will queue a deploy stage, no new builds will be created
+ # When pending build succeeds in stage test, we enqueue deploy stage.
+ #
succeed_pending
- expect(process_pipeline).to be_truthy
+ process_pipeline
+
expect(builds.pending.count).to eq(1)
- expect(builds.success.count).to eq(4)
- expect(all_builds.count).to eq(5)
+ expect(builds.success.count).to eq(3)
+ expect(all_builds.count).to eq(4)
- # When we succeed last pending build, we will have a total of 5 succeeded builds, no new builds will be created
+ # When the last one succeeds we have 4 successful builds.
+ #
succeed_pending
- expect(process_pipeline).to be_falsey
- expect(builds.success.count).to eq(5)
- expect(all_builds.count).to eq(5)
+ process_pipeline
+
+ expect(builds.success.count).to eq(4)
+ expect(all_builds.count).to eq(4)
end
end
end
+
+ def all_builds
+ pipeline.builds
+ end
+
+ def builds
+ all_builds.where.not(status: [:created, :skipped])
+ end
+
+ def process_pipeline
+ described_class.new(pipeline.project, user).execute(pipeline)
+ end
+
+ def succeed_pending
+ builds.pending.update_all(status: 'success')
+ end
+
+ def manual_actions
+ pipeline.manual_actions
+ end
+
+ def create_build(name, stage_idx, when_value = nil)
+ create(:ci_build,
+ :created,
+ pipeline: pipeline,
+ name: name,
+ stage_idx: stage_idx,
+ when: when_value)
+ end
end
diff --git a/spec/services/ci/send_pipeline_notification_service_spec.rb b/spec/services/ci/send_pipeline_notification_service_spec.rb
deleted file mode 100644
index 288302cc94f..00000000000
--- a/spec/services/ci/send_pipeline_notification_service_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'spec_helper'
-
-describe Ci::SendPipelineNotificationService, services: true do
- let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- sha: project.commit('master').sha,
- user: user,
- status: status)
- end
-
- let(:project) { create(:project) }
- let(:user) { create(:user) }
-
- subject{ described_class.new(pipeline) }
-
- describe '#execute' do
- before do
- reset_delivered_emails!
- end
-
- shared_examples 'sending emails' do
- it 'sends an email to pipeline user' do
- perform_enqueued_jobs do
- subject.execute([user.email])
- end
-
- email = ActionMailer::Base.deliveries.last
- expect(email.subject).to include(email_subject)
- expect(email.to).to eq([user.email])
- end
- end
-
- context 'with success pipeline' do
- let(:status) { 'success' }
- let(:email_subject) { "Pipeline ##{pipeline.id} has succeeded" }
-
- it_behaves_like 'sending emails'
- end
-
- context 'with failed pipeline' do
- let(:status) { 'failed' }
- let(:email_subject) { "Pipeline ##{pipeline.id} has failed" }
-
- it_behaves_like 'sending emails'
- end
- end
-end
diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb
new file mode 100644
index 00000000000..6f7d1a5d28d
--- /dev/null
+++ b/spec/services/ci/stop_environments_service_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Ci::StopEnvironmentsService, services: true do
+ let(:project) { create(:project, :private) }
+ let(:user) { create(:user) }
+
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ context 'when environment with review app exists' do
+ before do
+ create(:environment, :with_review_app, project: project,
+ ref: 'feature')
+ end
+
+ context 'when user has permission to stop environment' do
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when environment is associated with removed branch' do
+ it 'stops environment' do
+ expect_environment_stopped_on('feature')
+ end
+ end
+
+ context 'when environment is associated with different branch' do
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on('master')
+ end
+ end
+
+ context 'when specified branch does not exist' do
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on('non/existent/branch')
+ end
+ end
+
+ context 'when no branch not specified' do
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on(nil)
+ end
+ end
+
+ context 'when environment is not stoppable' do
+ before do
+ allow_any_instance_of(Environment)
+ .to receive(:stoppable?).and_return(false)
+ end
+
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on('feature')
+ end
+ end
+ end
+
+ context 'when user does not have permission to stop environment' do
+ before do
+ project.team << [user, :guest]
+ end
+
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on('master')
+ end
+ end
+ end
+
+ context 'when there is no environment associated with review app' do
+ before do
+ create(:environment, project: project)
+ end
+
+ context 'when user has permission to stop environments' do
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'does not stop environment' do
+ expect_environment_not_stopped_on('master')
+ end
+ end
+ end
+
+ context 'when environment does not exist' do
+ it 'does not raise error' do
+ expect { service.execute('master') }
+ .not_to raise_error
+ end
+ end
+ end
+
+ def expect_environment_stopped_on(branch)
+ expect_any_instance_of(Environment)
+ .to receive(:stop!)
+
+ service.execute(branch)
+ end
+
+ def expect_environment_not_stopped_on(branch)
+ expect_any_instance_of(Environment)
+ .not_to receive(:stop!)
+
+ service.execute(branch)
+ end
+end
diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb
new file mode 100644
index 00000000000..336f5dafb5b
--- /dev/null
+++ b/spec/services/delete_branch_service_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe DeleteBranchService, services: true do
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ context 'when user has access to push to repository' do
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'removes the branch' do
+ expect(branch_exists?('feature')).to be true
+
+ result = service.execute('feature')
+
+ expect(result[:status]).to eq :success
+ expect(branch_exists?('feature')).to be false
+ end
+ end
+
+ context 'when user does not have access to push to repository' do
+ it 'does not remove branch' do
+ expect(branch_exists?('feature')).to be true
+
+ result = service.execute('feature')
+
+ expect(result[:status]).to eq :error
+ expect(result[:message]).to eq 'You dont have push access to repo'
+ expect(branch_exists?('feature')).to be true
+ end
+ end
+ end
+
+ def branch_exists?(branch_name)
+ repository.ref_exists?("refs/heads/#{branch_name}")
+ end
+end
diff --git a/spec/services/delete_merged_branches_service_spec.rb b/spec/services/delete_merged_branches_service_spec.rb
new file mode 100644
index 00000000000..181488e89c7
--- /dev/null
+++ b/spec/services/delete_merged_branches_service_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe DeleteMergedBranchesService, services: true do
+ subject(:service) { described_class.new(project, project.owner) }
+
+ let(:project) { create(:project) }
+
+ context '#execute' do
+ context 'unprotected branches' do
+ before do
+ service.execute
+ end
+
+ it 'deletes a branch that was merged' do
+ expect(project.repository.branch_names).not_to include('improve/awesome')
+ end
+
+ it 'keeps branch that is unmerged' do
+ expect(project.repository.branch_names).to include('feature')
+ end
+
+ it 'keeps "master"' do
+ expect(project.repository.branch_names).to include('master')
+ end
+ end
+
+ context 'protected branches' do
+ before do
+ create(:protected_branch, name: 'improve/awesome', project: project)
+ service.execute
+ end
+
+ it 'keeps protected branch' do
+ expect(project.repository.branch_names).to include('improve/awesome')
+ end
+ end
+
+ context 'user without rights' do
+ let(:user) { create(:user) }
+
+ it 'cannot execute' do
+ expect { described_class.new(project, user).execute }.to raise_error(Gitlab::Access::AccessDeniedError)
+ end
+ end
+ end
+
+ context '#async_execute' do
+ it 'calls DeleteMergedBranchesWorker async' do
+ expect(DeleteMergedBranchesWorker).to receive(:perform_async)
+
+ service.async_execute
+ end
+ end
+end
diff --git a/spec/services/destroy_group_service_spec.rb b/spec/services/destroy_group_service_spec.rb
index da724643604..538e85cdc89 100644
--- a/spec/services/destroy_group_service_spec.rb
+++ b/spec/services/destroy_group_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe DestroyGroupService, services: true do
+ include DatabaseConnectionHelpers
+
let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project, namespace: group) }
@@ -50,6 +52,44 @@ describe DestroyGroupService, services: true do
describe 'asynchronous delete' do
it_behaves_like 'group destruction', true
+
+ context 'potential race conditions' do
+ context "when the `GroupDestroyWorker` task runs immediately" do
+ it "deletes the group" do
+ # Commit the contents of this spec's transaction so far
+ # so subsequent db connections can see it.
+ #
+ # DO NOT REMOVE THIS LINE, even if you see a WARNING with "No
+ # transaction is currently in progress". Without this, this
+ # spec will always be green, since the group created in setup
+ # cannot be seen by any other connections / threads in this spec.
+ Group.connection.commit_db_transaction
+
+ group_record = run_with_new_database_connection do |conn|
+ conn.execute("SELECT * FROM namespaces WHERE id = #{group.id}").first
+ end
+
+ expect(group_record).not_to be_nil
+
+ # Execute the contents of `GroupDestroyWorker` in a separate thread, to
+ # simulate data manipulation by the Sidekiq worker (different database
+ # connection / transaction).
+ expect(GroupDestroyWorker).to receive(:perform_async).and_wrap_original do |m, group_id, user_id|
+ Thread.new { m[group_id, user_id] }.join(5)
+ end
+
+ # Kick off the initial group destroy in a new thread, so that
+ # it doesn't share this spec's database transaction.
+ Thread.new { DestroyGroupService.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
+ end
+
+ expect(group_record).to be_nil
+ end
+ end
+ end
end
describe 'synchronous delete' do
diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb
new file mode 100644
index 00000000000..12c3cdf28c6
--- /dev/null
+++ b/spec/services/discussions/resolve_service_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Discussions::ResolveService do
+ describe '#execute' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:project) { merge_request.project }
+ let(:merge_request) { discussion.noteable }
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ it "doesn't resolve discussions the user can't resolve" do
+ expect(discussion).to receive(:can_resolve?).with(user).and_return(false)
+
+ service.execute(discussion)
+
+ expect(discussion.resolved?).to be(false)
+ end
+
+ it 'resolves the discussion' do
+ service.execute(discussion)
+
+ expect(discussion.resolved?).to be(true)
+ end
+
+ it 'executes the notification service' do
+ expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(discussion.noteable)
+
+ service.execute(discussion)
+ end
+
+ it 'adds a system note to the discussion' do
+ issue = create(:issue, project: project)
+
+ expect(SystemNoteService).to receive(:discussion_continued_in_issue).with(discussion, project, user, issue)
+ service = described_class.new(project, user, merge_request: merge_request, follow_up_issue: issue)
+ service.execute(discussion)
+ end
+
+ it 'can resolve multiple discussions at once' do
+ other_discussion = Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: discussion.noteable, project: discussion.noteable.source_project)]).first
+
+ service.execute([discussion, other_discussion])
+
+ expect(discussion.resolved?).to be(true)
+ expect(other_discussion.resolved?).to be(true)
+ end
+ end
+end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index cea7e6429f9..e7624e70725 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -27,27 +27,14 @@ describe GitPushService, services: true do
it { is_expected.to be_truthy }
- it 'flushes general cached data' do
- expect(project.repository).to receive(:expire_cache).
- with('master', newrev)
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
subject
end
- it 'flushes the visible content cache' do
- expect(project.repository).to receive(:expire_has_visible_content_cache)
-
- subject
- end
-
- it 'flushes the branches cache' do
- expect(project.repository).to receive(:expire_branches_cache)
-
- subject
- end
-
- it 'flushes the branch count cache' do
- expect(project.repository).to receive(:expire_branch_count_cache)
+ it 'calls the after_create_branch hook' do
+ expect(project.repository).to receive(:after_create_branch)
subject
end
@@ -56,21 +43,8 @@ describe GitPushService, services: true do
context 'existing branch' do
it { is_expected.to be_truthy }
- it 'flushes general cached data' do
- expect(project.repository).to receive(:expire_cache).
- with('master', newrev)
-
- subject
- end
-
- it 'does not flush the branches cache' do
- expect(project.repository).not_to receive(:expire_branches_cache)
-
- subject
- end
-
- it 'does not flush the branch count cache' do
- expect(project.repository).not_to receive(:expire_branch_count_cache)
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
subject
end
@@ -81,27 +55,14 @@ describe GitPushService, services: true do
it { is_expected.to be_truthy }
- it 'flushes the visible content cache' do
- expect(project.repository).to receive(:expire_has_visible_content_cache)
-
- subject
- end
-
- it 'flushes the branches cache' do
- expect(project.repository).to receive(:expire_branches_cache)
-
- subject
- end
-
- it 'flushes the branch count cache' do
- expect(project.repository).to receive(:expire_branch_count_cache)
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
subject
end
- it 'flushes general cached data' do
- expect(project.repository).to receive(:expire_cache).
- with('master', newrev)
+ it 'calls the after_remove_branch hook' do
+ expect(project.repository).to receive(:after_remove_branch)
subject
end
@@ -302,7 +263,7 @@ describe GitPushService, services: true do
author_email: commit_author.email
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -360,7 +321,7 @@ describe GitPushService, services: true do
committed_date: commit_time
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -399,7 +360,7 @@ describe GitPushService, services: true do
allow(project.repository).to receive(:commits_between).
and_return([closing_commit])
- allow_any_instance_of(ProcessCommitWorker).to receive(:find_commit).
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
and_return(closing_commit)
project.team << [commit_author, :master]
@@ -490,7 +451,17 @@ 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://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json }
+ 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 }
+
+ before do
+ open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" })
+ closed_issue = open_issue.dup
+ allow(open_issue).to receive(:resolution).and_return(false)
+ allow(closed_issue).to receive(:resolution).and_return(true)
+ allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
+
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return("JIRA-1")
+ end
context "using right markdown" do
it "initiates one api call to jira server to close the issue" do
@@ -588,6 +559,51 @@ describe GitPushService, services: true do
end
end
+ describe '#update_caches' do
+ let(:service) do
+ described_class.new(project,
+ user,
+ oldrev: sample_commit.parent_id,
+ newrev: sample_commit.id,
+ ref: 'refs/heads/master')
+ end
+
+ context 'on the default branch' do
+ before do
+ allow(service).to receive(:is_default_branch?).and_return(true)
+ end
+
+ it 'flushes the caches of any special files that have been changed' do
+ commit = double(:commit)
+ diff = double(:diff, new_path: 'README.md')
+
+ expect(commit).to receive(:raw_diffs).with(deltas_only: true).
+ and_return([diff])
+
+ service.push_commits = [commit]
+
+ expect(ProjectCacheWorker).to receive(:perform_async).
+ with(project.id, %i(readme))
+
+ service.update_caches
+ end
+ end
+
+ context 'on a non-default branch' do
+ before do
+ allow(service).to receive(:is_default_branch?).and_return(false)
+ end
+
+ it 'does not flush any conditional caches' do
+ expect(ProjectCacheWorker).to receive(:perform_async).
+ with(project.id, []).
+ and_call_original
+
+ service.update_caches
+ end
+ end
+ end
+
def execute_service(project, user, oldrev, newrev, ref)
service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref )
service.execute
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index 0879e3ab4c8..bd074b9bd71 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -18,7 +18,7 @@ describe GitTagPushService, services: true do
end
it 'flushes general cached data' do
- expect(project.repository).to receive(:expire_cache)
+ expect(project.repository).to receive(:before_push_tag)
subject
end
@@ -28,12 +28,6 @@ describe GitTagPushService, services: true do
subject
end
-
- it 'flushes the tag count cache' do
- expect(project.repository).to receive(:expire_tag_count_cache)
-
- subject
- end
end
describe "Git Tag Push Data" do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 6f7ce8ca992..5f3020b6525 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -260,14 +260,14 @@ describe Issuable::BulkUpdateService, services: true do
it 'subscribes the given user' do
bulk_update(issues, subscription_event: 'subscribe')
- expect(issues).to all(be_subscribed(user))
+ expect(issues).to all(be_subscribed(user, project))
end
end
describe 'unsubscribe from issues' do
let(:issues) do
create_list(:closed_issue, 2, project: project) do |issue|
- issue.subscriptions.create(user: user, subscribed: true)
+ issue.subscriptions.create(user: user, project: project, subscribed: true)
end
end
@@ -275,7 +275,7 @@ describe Issuable::BulkUpdateService, services: true do
bulk_update(issues, subscription_event: 'unsubscribe')
issues.each do |issue|
- expect(issue).not_to be_subscribed(user)
+ expect(issue).not_to be_subscribed(user, project)
end
end
end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
new file mode 100644
index 00000000000..4cfba35c830
--- /dev/null
+++ b/spec/services/issues/build_service_spec.rb
@@ -0,0 +1,130 @@
+require 'spec_helper.rb'
+
+describe Issues::BuildService, services: true do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'for discussions in a merge request' do
+ let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
+ let(:issue) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute }
+
+ def position_on_line(line_number)
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: line_number,
+ diff_refs: merge_request.diff_refs
+ )
+ end
+
+ describe '#items_for_discussions' do
+ it 'has an item for each discussion' do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.source_project, position: position_on_line(13))
+ service = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request)
+
+ service.execute
+
+ expect(service.items_for_discussions.size).to eq(2)
+ end
+ end
+
+ describe '#item_for_discussion' do
+ let(:service) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request) }
+
+ it 'mentions the author of the note' do
+ discussion = Discussion.new([create(:diff_note_on_merge_request, author: create(:user, username: 'author'))])
+ expect(service.item_for_discussion(discussion)).to include('@author')
+ end
+
+ it 'wraps the note in a blockquote' do
+ note_text = "This is a string\n"\
+ ">>>\n"\
+ "with a blockquote\n"\
+ "> That has a quote\n"\
+ ">>>\n"
+ note_result = "This is a string\n"\
+ "> with a blockquote\n"\
+ "> > That has a quote\n"
+ discussion = Discussion.new([create(:diff_note_on_merge_request, note: note_text)])
+ expect(service.item_for_discussion(discussion)).to include(">>>\n#{note_result}\n>>>")
+ end
+ end
+
+ describe '#execute' do
+ it 'has the merge request reference in the title' do
+ expect(issue.title).to include(merge_request.title)
+ end
+
+ it 'has the reference of the merge request in the description' do
+ expect(issue.description).to include(merge_request.to_reference)
+ end
+
+ it 'does not assign title when a title was given' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ title: 'What an issue').execute
+
+ expect(issue.title).to eq('What an issue')
+ end
+
+ it 'does not assign description when a description was given' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ description: 'Fix at your earliest conveignance').execute
+
+ expect(issue.description).to eq('Fix at your earliest conveignance')
+ end
+
+ describe 'with multiple discussions' do
+ before do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15))
+ end
+
+ it 'mentions all the authors in the description' do
+ authors = merge_request.diff_discussions.map(&:author)
+
+ expect(issue.description).to include(*authors.map(&:to_reference))
+ end
+
+ it 'has a link for each unresolved discussion in the description' do
+ notes = merge_request.diff_discussions.map(&:first_note)
+ links = notes.map { |note| Gitlab::UrlBuilder.build(note) }
+
+ expect(issue.description).to include(*links)
+ end
+
+ it 'mentions additional notes' do
+ create_list(:diff_note_on_merge_request, 2, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15))
+
+ expect(issue.description).to include('(+2 comments)')
+ end
+ end
+ end
+ end
+
+ context 'For a merge request without discussions' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ describe '#execute' do
+ it 'mentions the merge request in the description' do
+ issue = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute
+
+ expect(issue.description).to include("Review the conversation in #{merge_request.to_reference}")
+ end
+ end
+ end
+
+ describe '#execute' do
+ it 'builds a new issues with given params' do
+ issue = described_class.new(project, user, title: 'Issue #1', description: 'Issue description').execute
+
+ expect(issue.title).to eq('Issue #1')
+ expect(issue.description).to eq('Issue description')
+ end
+ end
+end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 4465f22a001..7a54373963e 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -62,7 +62,7 @@ describe Issues::CloseService, services: true do
it 'creates system note about issue reassign' do
note = issue.notes.last
- expect(note.note).to include "Status changed to closed"
+ expect(note.note).to include "closed"
end
it 'marks todos as done' do
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 5c0331ebe66..8bde61ee336 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -136,5 +136,48 @@ describe Issues::CreateService, services: true do
end
it_behaves_like 'new issuable record that supports slash commands'
+
+ context 'for a merge request' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ let(:opts) { { merge_request_for_resolving_discussions: merge_request } }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'resolves the discussion for the merge request' do
+ described_class.new(project, user, opts).execute
+ discussion.first_note.reload
+
+ expect(discussion.resolved?).to be(true)
+ end
+
+ it 'added a system note to the discussion' do
+ described_class.new(project, user, opts).execute
+
+ reloaded_discussion = MergeRequest.find(merge_request.id).discussions.first
+
+ expect(reloaded_discussion.last_note.system).to eq(true)
+ end
+
+ it 'assigns the title and description for the issue' do
+ issue = described_class.new(project, user, opts).execute
+
+ expect(issue.title).not_to be_nil
+ expect(issue.description).not_to be_nil
+ end
+
+ it 'can set nil explicityly to the title and description' do
+ issue = described_class.new(project, user,
+ merge_request_for_resolving_discussions: merge_request,
+ description: nil,
+ title: nil).execute
+
+ expect(issue.description).to be_nil
+ expect(issue.title).to be_nil
+ end
+ end
end
end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index f0ded06b785..db196ed5751 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -81,11 +81,11 @@ describe Issues::MoveService, services: true do
end
it 'adds system note to old issue at the end' do
- expect(old_issue.notes.last.note).to match /^Moved to/
+ expect(old_issue.notes.last.note).to start_with 'moved to'
end
it 'adds system note to new issue at the end' do
- expect(new_issue.notes.last.note).to match /^Moved from/
+ expect(new_issue.notes.last.note).to start_with 'moved from'
end
it 'closes old issue' do
@@ -151,7 +151,7 @@ describe Issues::MoveService, services: true do
end
it 'adds a system note about move after rewritten notes' do
- expect(system_notes.last.note).to match /^Moved from/
+ expect(system_notes.last.note).to match /^moved from/
end
it 'preserves orignal author of comment' do
@@ -189,7 +189,7 @@ describe Issues::MoveService, services: true do
it 'rewrites references using a cross reference to old project' do
expect(new_note.note)
- .to eq "Note with reference to merge request #{old_project.to_reference}!1"
+ .to eq "Note with reference to merge request #{old_project.to_reference(new_project)}!1"
end
end
@@ -217,7 +217,7 @@ describe Issues::MoveService, services: true do
it 'rewrites referenced issues creating cross project reference' do
expect(new_issue.description)
- .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
+ .to eq "Some description #{another_issue.to_reference(new_project)}"
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 1638a46ed51..eafbea46905 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -2,6 +2,8 @@
require 'spec_helper'
describe Issues::UpdateService, services: true do
+ include EmailHelpers
+
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
@@ -91,24 +93,24 @@ describe Issues::UpdateService, services: true do
end
it 'creates system note about issue reassign' do
- note = find_note('Reassigned to')
+ note = find_note('assigned to')
expect(note).not_to be_nil
- expect(note.note).to include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "assigned to #{user2.to_reference}"
end
it 'creates system note about issue label edit' do
- note = find_note('Added ~')
+ note = find_note('added ~')
expect(note).not_to be_nil
- expect(note.note).to include "Added ~#{label.id} label"
+ expect(note.note).to include "added #{label.to_reference} label"
end
it 'creates system note about title change' do
- note = find_note('Changed title:')
+ note = find_note('changed title')
expect(note).not_to be_nil
- expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
+ expect(note.note).to eq 'changed title from **{-Old-} title** to **{+New+} title**'
end
end
end
@@ -128,10 +130,10 @@ describe Issues::UpdateService, services: true do
it 'creates system note about confidentiality change' do
update_issue(confidential: true)
- note = find_note('Made the issue confidential')
+ note = find_note('made the issue confidential')
expect(note).not_to be_nil
- expect(note.note).to eq 'Made the issue confidential'
+ expect(note.note).to eq 'made the issue confidential'
end
it 'executes confidential issue hooks' do
@@ -215,7 +217,7 @@ describe Issues::UpdateService, services: true do
let!(:subscriber) do
create(:user).tap do |u|
- label.toggle_subscription(u)
+ label.toggle_subscription(u, project)
project.team << [u, :developer]
end
end
@@ -269,8 +271,8 @@ describe Issues::UpdateService, services: true do
before { update_issue(description: "- [x] Task 1\n- [X] Task 2") }
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as completed')
- note2 = find_note('Marked the task **Task 2** as completed')
+ note1 = find_note('marked the task **Task 1** as completed')
+ note2 = find_note('marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -284,8 +286,8 @@ describe Issues::UpdateService, services: true do
end
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as incomplete')
- note2 = find_note('Marked the task **Task 2** as incomplete')
+ note1 = find_note('marked the task **Task 1** as incomplete')
+ note2 = find_note('marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -299,7 +301,7 @@ describe Issues::UpdateService, services: true do
end
it 'does not create a system note' do
- note = find_note('Marked the task **Task 2** as incomplete')
+ note = find_note('marked the task **Task 2** as incomplete')
expect(note).to be_nil
end
@@ -312,7 +314,7 @@ 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')
+ note = find_note('marked the task **Two** as incomplete')
expect(note).to be_nil
end
@@ -374,5 +376,10 @@ describe Issues::UpdateService, services: true do
let(:mentionable) { issue }
include_examples 'updating mentions', Issues::UpdateService
end
+
+ include_examples 'issuable update service' do
+ let(:open_issuable) { issue }
+ let(:closed_issuable) { create(:closed_issue, project: project) }
+ end
end
end
diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb
index ddf3527dc0f..13654a0881c 100644
--- a/spec/services/labels/transfer_service_spec.rb
+++ b/spec/services/labels/transfer_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Labels::TransferService, services: true do
describe '#execute' do
- let(:user) { create(:user) }
+ let(:user) { create(:admin) }
let(:group_1) { create(:group) }
let(:group_2) { create(:group) }
let(:group_3) { create(:group) }
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index 7b090343a3e..7d5a66801db 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
describe Members::ApproveAccessRequestService, services: true do
let(:user) { create(:user) }
let(:access_requester) { create(:user) }
- let(:project) { create(:project, :public) }
- let(:group) { create(:group, :public) }
+ let(:project) { create(:empty_project, :public, :access_requestable) }
+ let(:group) { create(:group, :public, :access_requestable) }
let(:opts) { {} }
shared_examples 'a service raising ActiveRecord::RecordNotFound' do
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 9995f3488af..574df6e0f42 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -26,6 +26,7 @@ describe Members::DestroyService, services: true do
context 'when the given member is an access requester' do
before do
source.members.find_by(user_id: member_user).destroy
+ source.update_attributes(request_access_enabled: true)
source.request_access(member_user)
end
let(:access_requester) { source.requesters.find_by(user_id: member_user) }
diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb
index 0d2d5f03199..853c125dadb 100644
--- a/spec/services/members/request_access_service_spec.rb
+++ b/spec/services/members/request_access_service_spec.rb
@@ -2,8 +2,6 @@ require 'spec_helper'
describe Members::RequestAccessService, services: true do
let(:user) { create(:user) }
- let(:project) { create(:project, :private) }
- let(:group) { create(:group, :private) }
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
it 'raises Gitlab::Access::AccessDeniedError' do
@@ -31,27 +29,26 @@ describe Members::RequestAccessService, services: true do
end
context 'when current user cannot request access to the project' do
- it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
- let(:source) { project }
+ %i[project group].each do |source_type|
+ it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
+ let(:source) { create(source_type, :private) }
+ end
end
+ end
- it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
- let(:source) { group }
+ context 'when access requests are disabled' do
+ %i[project group].each do |source_type|
+ it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
+ let(:source) { create(source_type, :public) }
+ end
end
end
context 'when current user can request access to the project' do
- before do
- project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- end
-
- it_behaves_like 'a service creating a access request' do
- let(:source) { project }
- end
-
- it_behaves_like 'a service creating a access request' do
- let(:source) { group }
+ %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
end
end
end
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
index dd656c3bbb7..bb7830c7eea 100644
--- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -1,29 +1,46 @@
require 'spec_helper'
-# Write specs in this file.
describe MergeRequests::AddTodoWhenBuildFailsService do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { create(:project) }
let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
- let(:pipeline) { create(:ci_pipeline_with_one_job, ref: merge_request.source_branch, project: project, sha: sha) }
- let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user, commit_message: 'Awesome message') }
+ let(:ref) { merge_request.source_branch }
+
+ let(:pipeline) do
+ create(:ci_pipeline_with_one_job, ref: ref,
+ project: project,
+ sha: sha)
+ end
+
+ let(:service) do
+ described_class.new(project, user, commit_message: 'Awesome message')
+ end
+
let(:todo_service) { TodoService.new }
let(:merge_request) do
- create(:merge_request, merge_user: user, source_branch: 'master',
- target_branch: 'feature', source_project: project, target_project: project,
+ create(:merge_request, merge_user: user,
+ source_branch: 'master',
+ target_branch: 'feature',
+ source_project: project,
+ target_project: project,
state: 'opened')
end
before do
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_return(pipeline)
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
allow(service).to receive(:todo_service).and_return(todo_service)
end
describe '#execute' do
context 'commit status with ref' do
- let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, pipeline: pipeline) }
+ let(:commit_status) do
+ create(:generic_commit_status, ref: ref, pipeline: pipeline)
+ end
it 'notifies the todo service' do
expect(todo_service).to receive(:merge_request_build_failed).with(merge_request)
@@ -32,7 +49,7 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
end
context 'commit status with non-HEAD ref' do
- let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch) }
+ let(:commit_status) { create(:generic_commit_status, ref: ref) }
it 'does not notify the todo service' do
expect(todo_service).not_to receive(:merge_request_build_failed)
@@ -48,6 +65,18 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
service.execute(commit_status)
end
end
+
+ context 'when commit status is a build allowed to fail' do
+ let(:commit_status) do
+ create(:ci_build, :allowed_to_fail, ref: ref, pipeline: pipeline)
+ end
+
+ it 'does not create todo' do
+ expect(todo_service).not_to receive(:merge_request_build_failed)
+
+ service.execute(commit_status)
+ end
+ end
end
describe '#close' do
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 3f5df049ea2..dc945ca4868 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -24,6 +24,8 @@ describe MergeRequests::BuildService, services: true do
end
before do
+ project.team << [user, :guest]
+
allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare)
allow(project).to receive(:commit).and_return(commit_1)
allow(project).to receive(:commit).and_return(commit_2)
@@ -168,6 +170,16 @@ describe MergeRequests::BuildService, services: true do
expect(merge_request.title).to eq("Resolve \"#{issue.title}\"")
end
+ context 'when issue is not accessible to user' do
+ before do
+ project.team.truncate
+ end
+
+ it 'uses branch title as the merge request title' do
+ expect(merge_request.title).to eq("#{issue.iid} fix issue")
+ end
+ end
+
context 'issue does not exist' do
let(:source_branch) { "#{issue.iid.succ}-fix-issue" }
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 24c25e4350f..5f6a7716beb 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -42,7 +42,7 @@ describe MergeRequests::CloseService, services: true do
it 'creates system note about merge_request reassign' do
note = @merge_request.notes.last
- expect(note.note).to include 'Status changed to closed'
+ expect(note.note).to include 'closed'
end
it 'marks todos as done' do
diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb
index 3a71776e81f..08829e4be70 100644
--- a/spec/services/merge_requests/get_urls_service_spec.rb
+++ b/spec/services/merge_requests/get_urls_service_spec.rb
@@ -4,8 +4,8 @@ describe MergeRequests::GetUrlsService do
let(:project) { create(:project, :public) }
let(:service) { MergeRequests::GetUrlsService.new(project) }
let(:source_branch) { "my_branch" }
- let(:new_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" }
- let(:show_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" }
+ let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" }
+ let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/#{merge_request.iid}" }
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
let(:deleted_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 #{Gitlab::Git::BLANK_SHA} refs/heads/#{source_branch}" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
@@ -115,7 +115,7 @@ describe MergeRequests::GetUrlsService do
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/existing_branch" }
let(:changes) { "#{new_branch_changes}\n#{existing_branch_changes}" }
- let(:new_merge_request_url) { "http://localhost/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" }
+ let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" }
it 'returns 2 urls for both creating new and showing merge request' do
result = service.execute(changes)
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 807f89e80b7..05cdbe5287a 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,6 +10,7 @@ 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))
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 f93d7732a9a..5a89acc96a4 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -34,7 +34,7 @@ describe MergeRequests::MergeService, services: true do
it 'creates system note about merge_request merge' do
note = merge_request.notes.last
- expect(note.note).to include 'Status changed to merged'
+ expect(note.note).to include 'merged'
end
end
@@ -59,14 +59,19 @@ describe MergeRequests::MergeService, services: true do
include JiraServiceHelper
let(:jira_tracker) { project.create_jira_service }
+ let(:jira_issue) { ExternalIssue.new('JIRA-123', project) }
+ let(:commit) { double('commit', safe_message: "Fixes #{jira_issue.to_reference}") }
before do
project.update_attributes!(has_external_issue_tracker: true)
jira_service_settings
+ stub_jira_urls(jira_issue.id)
+ allow(merge_request).to receive(:commits).and_return([commit])
end
it 'closes issues on JIRA issue tracker' do
jira_issue = ExternalIssue.new('JIRA-123', project)
+ stub_jira_urls(jira_issue)
commit = double('commit', safe_message: "Fixes #{jira_issue.to_reference}")
allow(merge_request).to receive(:commits).and_return([commit])
@@ -75,9 +80,22 @@ describe MergeRequests::MergeService, services: true do
service.execute(merge_request)
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) }
+
+ it "does not close issue" do
+ allow(jira_tracker).to receive_messages(jira_issue_transition_id: nil)
+
+ expect_any_instance_of(JiraService).not_to receive(:transition_issue)
+
+ service.execute(merge_request)
+ end
+ end
+
context "wrong issue markdown" do
it 'does not close issues on JIRA issue tracker' do
- jira_issue = ExternalIssue.new('#123', project)
+ jira_issue = ExternalIssue.new('#JIRA-123', project)
+ stub_jira_urls(jira_issue)
commit = double('commit', safe_message: "Fixes #{jira_issue.to_reference}")
allow(merge_request).to receive(:commits).and_return([commit])
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index 1f90efdbd6a..f92978a33a3 100644
--- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe MergeRequests::MergeWhenBuildSucceedsService do
+describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:user) { create(:user) }
let(:project) { create(:project) }
@@ -10,8 +10,14 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
source_project: project, target_project: project, state: "opened")
end
- let(:pipeline) { create(:ci_pipeline_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) }
- let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') }
+ let(:pipeline) do
+ create(:ci_pipeline_with_one_job, ref: mr_merge_if_green_enabled.source_branch,
+ project: project)
+ end
+
+ let(:service) do
+ described_class.new(project, user, commit_message: 'Awesome message')
+ end
describe "#execute" do
let(:merge_request) do
@@ -21,7 +27,10 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
context 'first time enabling' do
before do
- allow(merge_request).to receive(:pipeline).and_return(pipeline)
+ allow(merge_request)
+ .to receive(:head_pipeline)
+ .and_return(pipeline)
+
service.execute(merge_request)
end
@@ -34,17 +43,21 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
it 'creates a system note' do
note = merge_request.notes.last
- expect(note.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-z]{8}/
+ expect(note.note).to match /enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{8}/
end
end
context 'already approved' do
- let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, new_key: true) }
+ let(:service) { described_class.new(project, user, new_key: true) }
let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) }
before do
- allow(mr_merge_if_green_enabled).to receive(:pipeline).and_return(pipeline)
- allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true)
+ allow(mr_merge_if_green_enabled).to receive(:head_pipeline)
+ .and_return(pipeline)
+
+ allow(mr_merge_if_green_enabled).to receive(:mergeable?)
+ .and_return(true)
+
allow(pipeline).to receive(:success?).and_return(true)
end
@@ -113,7 +126,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
it 'Posts a system note' do
note = mr_merge_if_green_enabled.notes.last
- expect(note.note).to include 'Canceled the automatic merge'
+ expect(note.note).to include 'canceled the automatic merge'
end
end
@@ -138,9 +151,12 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
before do
# This behavior of MergeRequest: we instantiate a new object
- allow_any_instance_of(MergeRequest).to receive(:pipeline).and_wrap_original do
- Ci::Pipeline.find(pipeline.id)
- end
+ #
+ allow_any_instance_of(MergeRequest)
+ .to receive(:head_pipeline)
+ .and_wrap_original do
+ Ci::Pipeline.find(pipeline.id)
+ end
end
it "doesn't merge if any of stages failed" do
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index e515bc9f89c..7e3705983fb 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -76,10 +76,10 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_merged }
- it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@fork_merge_request.notes.last.note).to include('merged') }
it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_done }
end
@@ -95,11 +95,11 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@merge_request.diffs.size).to be > 0 }
it { expect(@fork_merge_request).to be_merged }
- it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@fork_merge_request.notes.last.note).to include('merged') }
it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_done }
end
@@ -119,24 +119,34 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@fork_merge_request.notes.last.note).to include('Added 28 commits') }
+ it { expect(@fork_merge_request.notes.last.note).to include('added 28 commits') }
it { expect(@fork_merge_request).to be_open }
it { expect(@build_failed_todo).to be_pending }
it { expect(@fork_build_failed_todo).to be_pending }
end
context 'push to fork repo target branch' do
- before do
- service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature')
- reload_mrs
+ describe 'changes to merge requests' do
+ before do
+ service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature')
+ reload_mrs
+ end
+
+ it { expect(@merge_request.notes).to be_empty }
+ it { expect(@merge_request).to be_open }
+ it { expect(@fork_merge_request.notes).to be_empty }
+ it { expect(@fork_merge_request).to be_open }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
- it { expect(@merge_request.notes).to be_empty }
- it { expect(@merge_request).to be_open }
- it { expect(@fork_merge_request.notes).to be_empty }
- it { expect(@fork_merge_request).to be_open }
- it { expect(@build_failed_todo).to be_pending }
- it { expect(@fork_build_failed_todo).to be_pending }
+ describe 'merge request diff' do
+ it 'does not reload the diff of the merge request made from fork' do
+ expect do
+ service.new(@fork_project, @user).execute(@oldrev, @newrev, 'refs/heads/feature')
+ end.not_to change { @fork_merge_request.reload.merge_request_diff }
+ end
+ end
end
context 'push to origin repo target branch after fork project was removed' do
@@ -146,7 +156,7 @@ describe MergeRequests::RefreshService, services: true do
reload_mrs
end
- it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@merge_request.notes.last.note).to include('merged') }
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
@@ -169,8 +179,8 @@ describe MergeRequests::RefreshService, services: true do
expect(@merge_request).to be_open
notes = @fork_merge_request.notes.reorder(:created_at).map(&:note)
- expect(notes[0]).to include('Restored source branch `master`')
- expect(notes[1]).to include('Added 28 commits')
+ expect(notes[0]).to include('restored source branch `master`')
+ expect(notes[1]).to include('added 28 commits')
expect(@fork_merge_request).to be_open
end
end
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index af7424a76a9..a99d4eac9bd 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -41,7 +41,7 @@ describe MergeRequests::ReopenService, services: true do
it 'creates system note about merge_request reopen' do
note = merge_request.notes.last
- expect(note.note).to include 'Status changed to reopened'
+ expect(note.note).to include 'reopened'
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 2433a7dad06..88c786947d3 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe MergeRequests::UpdateService, services: true do
+ include EmailHelpers
+
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
@@ -79,31 +81,31 @@ describe MergeRequests::UpdateService, services: true do
end
it 'creates system note about merge_request reassign' do
- note = find_note('Reassigned to')
+ note = find_note('assigned to')
expect(note).not_to be_nil
- expect(note.note).to include "Reassigned to \@#{user2.username}"
+ expect(note.note).to include "assigned to #{user2.to_reference}"
end
it 'creates system note about merge_request label edit' do
- note = find_note('Added ~')
+ note = find_note('added ~')
expect(note).not_to be_nil
- expect(note.note).to include "Added ~#{label.id} label"
+ expect(note.note).to include "added #{label.to_reference} label"
end
it 'creates system note about title change' do
- note = find_note('Changed title:')
+ note = find_note('changed title')
expect(note).not_to be_nil
- expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
+ expect(note.note).to eq 'changed title from **{-Old-} title** to **{+New+} title**'
end
it 'creates system note about branch change' do
- note = find_note('Target')
+ note = find_note('changed target')
expect(note).not_to be_nil
- expect(note.note).to eq 'Target branch changed from `master` to `target`'
+ expect(note.note).to eq 'changed target branch from `master` to `target`'
end
context 'when not including source branch removal options' do
@@ -199,7 +201,7 @@ describe MergeRequests::UpdateService, services: true do
context 'when the issue is relabeled' do
let!(:non_subscriber) { create(:user) }
- let!(:subscriber) { create(:user).tap { |u| label.toggle_subscription(u) } }
+ let!(:subscriber) { create(:user) { |u| label.toggle_subscription(u, project) } }
before do
project.team << [non_subscriber, :developer]
@@ -258,8 +260,8 @@ describe MergeRequests::UpdateService, services: true do
before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) }
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as completed')
- note2 = find_note('Marked the task **Task 2** as completed')
+ note1 = find_note('marked the task **Task 1** as completed')
+ note2 = find_note('marked the task **Task 2** as completed')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -273,8 +275,8 @@ describe MergeRequests::UpdateService, services: true do
end
it 'creates system note about task status change' do
- note1 = find_note('Marked the task **Task 1** as incomplete')
- note2 = find_note('Marked the task **Task 2** as incomplete')
+ note1 = find_note('marked the task **Task 1** as incomplete')
+ note2 = find_note('marked the task **Task 2** as incomplete')
expect(note1).not_to be_nil
expect(note2).not_to be_nil
@@ -318,5 +320,10 @@ describe MergeRequests::UpdateService, services: true do
expect(issue_ids).to be_empty
end
end
+
+ include_examples 'issuable update service' do
+ let(:open_issuable) { merge_request }
+ let(:closed_issuable) { create(:closed_merge_request, source_project: project) }
+ end
end
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 93885c84dc3..25804696d2e 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -14,12 +14,41 @@ describe Notes::CreateService, services: true do
end
context "valid params" do
- before do
- @note = Notes::CreateService.new(project, user, opts).execute
+ it 'returns a valid note' do
+ note = Notes::CreateService.new(project, user, opts).execute
+
+ expect(note).to be_valid
+ end
+
+ it 'returns a persisted note' do
+ note = Notes::CreateService.new(project, user, opts).execute
+
+ expect(note).to be_persisted
+ end
+
+ it 'note has valid content' do
+ note = Notes::CreateService.new(project, user, opts).execute
+
+ expect(note.note).to eq(opts[:note])
end
- it { expect(@note).to be_valid }
- it { expect(@note.note).to eq(opts[:note]) }
+ it 'TodoService#new_note is called' do
+ note = build(:note)
+ allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
+
+ expect_any_instance_of(TodoService).to receive(:new_note).with(note, user)
+
+ Notes::CreateService.new(project, user, opts).execute
+ end
+
+ it 'enqueues NewNoteWorker' do
+ note = build(:note, id: 999)
+ allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
+
+ expect(NewNoteWorker).to receive(:perform_async).with(note.id)
+
+ Notes::CreateService.new(project, user, opts).execute
+ end
end
describe 'note with commands' do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 699b9925b4e..f3e80ac22a0 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe NotificationService, services: true do
+ include EmailHelpers
+
let(:notification) { NotificationService.new }
around(:each) do |example|
@@ -17,7 +19,7 @@ describe NotificationService, services: true do
it 'sends no emails when no new mentions are present' do
send_notifications
- expect(ActionMailer::Base.deliveries).to be_empty
+ should_not_email_anyone
end
it 'emails new mentions with a watch level higher than participant' do
@@ -27,7 +29,7 @@ describe NotificationService, services: true do
it 'does not email new mentions with a watch level equal to or less than participant' do
send_notifications(@u_participating, @u_mentioned)
- expect(ActionMailer::Base.deliveries).to be_empty
+ should_not_email_anyone
end
end
@@ -64,9 +66,9 @@ describe NotificationService, services: true do
before do
build_team(note.project)
- project.team << [issue.author, :master]
- project.team << [issue.assignee, :master]
- project.team << [note.author, :master]
+ project.add_master(issue.author)
+ project.add_master(issue.assignee)
+ project.add_master(note.author)
create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@subscribed_participant cc this guy')
update_custom_notification(:new_note, @u_guest_custom, project)
update_custom_notification(:new_note, @u_custom_global)
@@ -79,7 +81,7 @@ describe NotificationService, services: true do
# Ensure create SentNotification by noteable = issue 6 times, not noteable = note
expect(SentNotification).to receive(:record).with(issue, any_args).exactly(8).times
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
notification.new_note(note)
@@ -111,7 +113,7 @@ describe NotificationService, services: true do
context 'participating' do
context 'by note' do
before do
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
note.author = @u_lazy_participant
note.save
notification.new_note(note)
@@ -134,7 +136,7 @@ describe NotificationService, services: true do
@u_watcher.notification_settings_for(note.project).participating!
@u_watcher.notification_settings_for(note.project.group).global!
update_custom_notification(:new_note, @u_custom_global)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
end
it do
@@ -168,12 +170,12 @@ describe NotificationService, services: true do
let(:guest_watcher) { create_user_with_notification(:watch, "guest-watcher-confidential") }
it 'filters out users that can not read the issue' do
- project.team << [member, :developer]
- project.team << [guest, :guest]
+ project.add_developer(member)
+ project.add_guest(guest)
expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
notification.new_note(note)
@@ -195,8 +197,8 @@ describe NotificationService, services: true do
before do
build_team(note.project)
- note.project.team << [note.author, :master]
- ActionMailer::Base.deliveries.clear
+ note.project.add_master(note.author)
+ reset_delivered_emails!
end
describe '#new_note' do
@@ -237,8 +239,8 @@ describe NotificationService, services: true do
before do
build_team(note.project)
- note.project.team << [note.author, :master]
- ActionMailer::Base.deliveries.clear
+ note.project.add_master(note.author)
+ reset_delivered_emails!
end
describe '#new_note' do
@@ -273,7 +275,7 @@ describe NotificationService, services: true do
before do
build_team(note.project)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer)
update_custom_notification(:new_note, @u_guest_custom, project)
update_custom_notification(:new_note, @u_custom_global)
@@ -324,8 +326,8 @@ describe NotificationService, services: true do
before do
build_team(note.project)
- project.team << [merge_request.author, :master]
- project.team << [merge_request.assignee, :master]
+ project.add_master(merge_request.author)
+ project.add_master(merge_request.assignee)
end
describe '#new_note' do
@@ -342,13 +344,15 @@ describe NotificationService, services: true do
end
describe 'Issues' do
- let(:project) { create(:empty_project, :public) }
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_project) { create(:empty_project, :public, namespace: group) }
let(:issue) { create :issue, project: project, assignee: create(:user), description: 'cc @participant' }
before do
build_team(issue.project)
add_users_with_subscription(issue.project, issue)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
update_custom_notification(:new_issue, @u_guest_custom, project)
update_custom_notification(:new_issue, @u_custom_global)
end
@@ -377,13 +381,24 @@ describe NotificationService, services: true do
end
it "emails subscribers of the issue's labels" do
- subscriber = create(:user)
- label = create(:label, issues: [issue])
+ user_1 = create(:user)
+ user_2 = create(:user)
+ user_3 = create(:user)
+ user_4 = create(:user)
+ label = create(:label, project: project, issues: [issue])
+ group_label = create(:group_label, group: group, issues: [issue])
issue.reload
- label.toggle_subscription(subscriber)
+ label.toggle_subscription(user_1, project)
+ group_label.toggle_subscription(user_2, project)
+ group_label.toggle_subscription(user_3, another_project)
+ group_label.toggle_subscription(user_4)
+
notification.new_issue(issue, @u_disabled)
- should_email(subscriber)
+ should_email(user_1)
+ should_email(user_2)
+ should_not_email(user_3)
+ should_email(user_4)
end
context 'confidential issues' do
@@ -396,19 +411,19 @@ describe NotificationService, services: true do
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
it "emails subscribers of the issue's labels that can read the issue" do
- project.team << [member, :developer]
- project.team << [guest, :guest]
+ project.add_developer(member)
+ project.add_guest(guest)
- label = create(:label, issues: [confidential_issue])
+ label = create(:label, project: project, issues: [confidential_issue])
confidential_issue.reload
- label.toggle_subscription(non_member)
- label.toggle_subscription(author)
- label.toggle_subscription(assignee)
- label.toggle_subscription(member)
- label.toggle_subscription(guest)
- label.toggle_subscription(admin)
+ label.toggle_subscription(non_member, project)
+ label.toggle_subscription(author, project)
+ label.toggle_subscription(assignee, project)
+ label.toggle_subscription(member, project)
+ label.toggle_subscription(guest, project)
+ label.toggle_subscription(admin, project)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
notification.new_issue(confidential_issue, @u_disabled)
@@ -554,20 +569,30 @@ describe NotificationService, services: true do
end
describe '#relabeled_issue' do
- let(:label) { create(:label, issues: [issue]) }
- let(:label2) { create(:label) }
- let!(:subscriber_to_label) { create(:user).tap { |u| label.toggle_subscription(u) } }
- let!(:subscriber_to_label2) { create(:user).tap { |u| label2.toggle_subscription(u) } }
+ let(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1', issues: [issue]) }
+ let(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
+ let(:label_1) { create(:label, project: project, title: 'Label 1', issues: [issue]) }
+ let(:label_2) { create(:label, project: project, title: 'Label 2') }
+ let!(:subscriber_to_group_label_1) { create(:user) { |u| group_label_1.toggle_subscription(u, project) } }
+ let!(:subscriber_1_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u, project) } }
+ let!(:subscriber_2_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u) } }
+ let!(:subscriber_to_group_label_2_on_another_project) { create(:user) { |u| group_label_2.toggle_subscription(u, another_project) } }
+ 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, [label2], @u_disabled)
-
- should_not_email(subscriber_to_label)
- should_email(subscriber_to_label2)
+ 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 "doesn't send email to anyone but subscribers of the given labels" do
- notification.relabeled_issue(issue, [label2], @u_disabled)
+ notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
should_not_email(issue.assignee)
should_not_email(issue.author)
@@ -578,8 +603,12 @@ 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)
- should_email(subscriber_to_label2)
+ 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
@@ -590,21 +619,21 @@ describe NotificationService, services: true do
let(:guest) { create(:user) }
let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignee: assignee) }
- let!(:label_1) { create(:label, issues: [confidential_issue]) }
- let!(:label_2) { create(:label) }
+ let!(:label_1) { create(:label, project: project, issues: [confidential_issue]) }
+ let!(:label_2) { create(:label, project: project) }
it "emails subscribers of the issue's labels that can read the issue" do
- project.team << [member, :developer]
- project.team << [guest, :guest]
+ project.add_developer(member)
+ project.add_guest(guest)
- label_2.toggle_subscription(non_member)
- label_2.toggle_subscription(author)
- label_2.toggle_subscription(assignee)
- label_2.toggle_subscription(member)
- label_2.toggle_subscription(guest)
- label_2.toggle_subscription(admin)
+ label_2.toggle_subscription(non_member, project)
+ label_2.toggle_subscription(author, project)
+ label_2.toggle_subscription(assignee, project)
+ label_2.toggle_subscription(member, project)
+ label_2.toggle_subscription(guest, project)
+ label_2.toggle_subscription(admin, project)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
@@ -725,7 +754,9 @@ describe NotificationService, services: true do
end
describe 'Merge Requests' do
- let(:project) { create(:project, :public) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :public, namespace: group) }
+ let(:another_project) { create(:empty_project, :public, namespace: group) }
let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' }
before do
@@ -733,7 +764,7 @@ describe NotificationService, services: true do
add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, project)
update_custom_notification(:new_merge_request, @u_custom_global)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
end
describe '#new_merge_request' do
@@ -758,12 +789,23 @@ describe NotificationService, services: true do
end
it "emails subscribers of the merge request's labels" do
- subscriber = create(:user)
- label = create(:label, merge_requests: [merge_request])
- label.toggle_subscription(subscriber)
+ user_1 = create(:user)
+ user_2 = create(:user)
+ user_3 = create(:user)
+ user_4 = create(:user)
+ label = create(:label, project: project, merge_requests: [merge_request])
+ group_label = create(:group_label, group: group, merge_requests: [merge_request])
+ label.toggle_subscription(user_1, project)
+ group_label.toggle_subscription(user_2, project)
+ group_label.toggle_subscription(user_3, another_project)
+ group_label.toggle_subscription(user_4)
+
notification.new_merge_request(merge_request, @u_disabled)
- should_email(subscriber)
+ should_email(user_1)
+ should_email(user_2)
+ should_not_email(user_3)
+ should_email(user_4)
end
context 'participating' do
@@ -857,20 +899,30 @@ describe NotificationService, services: true do
end
describe '#relabel_merge_request' do
- let(:label) { create(:label, merge_requests: [merge_request]) }
- let(:label2) { create(:label) }
- let!(:subscriber_to_label) { create(:user).tap { |u| label.toggle_subscription(u) } }
- let!(:subscriber_to_label2) { create(:user).tap { |u| label2.toggle_subscription(u) } }
+ let(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1', merge_requests: [merge_request]) }
+ let(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
+ let(:label_1) { create(:label, project: project, title: 'Label 1', merge_requests: [merge_request]) }
+ let(:label_2) { create(:label, project: project, title: 'Label 2') }
+ let!(:subscriber_to_group_label_1) { create(:user) { |u| group_label_1.toggle_subscription(u, project) } }
+ let!(:subscriber_1_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u, project) } }
+ let!(:subscriber_2_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u) } }
+ let!(:subscriber_to_group_label_2_on_another_project) { create(:user) { |u| group_label_2.toggle_subscription(u, another_project) } }
+ 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
- notification.relabeled_merge_request(merge_request, [label2], @u_disabled)
-
- should_not_email(subscriber_to_label)
- should_email(subscriber_to_label2)
+ notification.relabeled_merge_request(merge_request, [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 "doesn't send email to anyone but subscribers of the given labels" do
- notification.relabeled_merge_request(merge_request, [label2], @u_disabled)
+ 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)
@@ -881,8 +933,12 @@ 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)
- should_email(subscriber_to_label2)
+ 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
@@ -1111,7 +1167,7 @@ describe NotificationService, services: true do
before do
build_team(project)
- ActionMailer::Base.deliveries.clear
+ reset_delivered_emails!
end
describe '#project_was_moved' do
@@ -1156,7 +1212,7 @@ describe NotificationService, services: true do
let(:member) { create(:user) }
before(:each) do
- project.team << [member, :developer, project.owner]
+ project.add_developer(member, current_user: project.owner)
end
it do
@@ -1179,9 +1235,9 @@ describe NotificationService, services: true do
let(:note) { create(:note, noteable: merge_request, project: private_project) }
before do
- private_project.team << [assignee, :developer]
- private_project.team << [developer, :developer]
- private_project.team << [guest, :guest]
+ private_project.add_developer(assignee)
+ private_project.add_developer(developer)
+ private_project.add_guest(guest)
ActionMailer::Base.deliveries.clear
end
@@ -1243,15 +1299,15 @@ describe NotificationService, services: true do
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
- project.team << [@u_watcher, :master]
- project.team << [@u_participating, :master]
- project.team << [@u_participant_mentioned, :master]
- project.team << [@u_disabled, :master]
- project.team << [@u_mentioned, :master]
- project.team << [@u_committer, :master]
- project.team << [@u_not_mentioned, :master]
- project.team << [@u_lazy_participant, :master]
- project.team << [@u_custom_global, :master]
+ project.add_master(@u_watcher)
+ project.add_master(@u_participating)
+ project.add_master(@u_participant_mentioned)
+ project.add_master(@u_disabled)
+ project.add_master(@u_mentioned)
+ project.add_master(@u_committer)
+ project.add_master(@u_not_mentioned)
+ project.add_master(@u_lazy_participant)
+ project.add_master(@u_custom_global)
end
def create_global_setting_for(user, level)
@@ -1285,15 +1341,15 @@ describe NotificationService, services: true do
@subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
- project.team << [@subscribed_participant, :master]
- project.team << [@subscriber, :master]
- project.team << [@unsubscriber, :master]
- project.team << [@watcher_and_subscriber, :master]
+ project.add_master(@subscribed_participant)
+ project.add_master(@subscriber)
+ project.add_master(@unsubscriber)
+ project.add_master(@watcher_and_subscriber)
- issuable.subscriptions.create(user: @subscriber, subscribed: true)
- issuable.subscriptions.create(user: @subscribed_participant, subscribed: true)
- issuable.subscriptions.create(user: @unsubscriber, subscribed: false)
+ issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true)
+ issuable.subscriptions.create(user: @subscribed_participant, project: project, subscribed: true)
+ issuable.subscriptions.create(user: @unsubscriber, project: project, subscribed: false)
# Make the watcher a subscriber to detect dupes
- issuable.subscriptions.create(user: @watcher_and_subscriber, subscribed: true)
+ issuable.subscriptions.create(user: @watcher_and_subscriber, project: project, subscribed: true)
end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 876bfaf085c..a1539b69401 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -1,141 +1,150 @@
require 'spec_helper'
-describe Projects::CreateService, services: true do
- describe :create_by_user do
- before do
- @user = create :user
- @opts = {
- name: "GitLab",
- namespace: @user.namespace
- }
- end
+describe Projects::CreateService, '#execute', services: true do
+ let(:user) { create :user }
+ let(:opts) do
+ {
+ name: "GitLab",
+ namespace: user.namespace
+ }
+ end
- it 'creates services on Project creation' do
- project = create_project(@user, @opts)
- project.reload
+ it 'creates labels on Project creation if there are templates' do
+ Label.create(title: "bug", template: true)
+ project = create_project(user, opts)
- expect(project.services).not_to be_empty
- end
+ expect(project.labels).not_to be_empty
+ end
- it 'creates labels on Project creation if there are templates' do
- Label.create(title: "bug", template: true)
- project = create_project(@user, @opts)
- project.reload
+ context 'user namespace' do
+ it do
+ project = create_project(user, opts)
- expect(project.labels).not_to be_empty
+ expect(project).to be_valid
+ expect(project.owner).to eq(user)
+ expect(project.team.masters).to include(user)
+ expect(project.namespace).to eq(user.namespace)
end
+ end
- context 'user namespace' do
- before do
- @project = create_project(@user, @opts)
+ context 'group namespace' do
+ let(:group) do
+ create(:group).tap do |group|
+ group.add_owner(user)
end
-
- it { expect(@project).to be_valid }
- it { expect(@project.owner).to eq(@user) }
- it { expect(@project.team.masters).to include(@user) }
- it { expect(@project.namespace).to eq(@user.namespace) }
end
- context 'group namespace' do
- before do
- @group = create :group
- @group.add_owner(@user)
+ before do
+ user.refresh_authorized_projects # Ensure cache is warm
+ end
- @opts.merge!(namespace_id: @group.id)
- @project = create_project(@user, @opts)
- end
+ it do
+ project = create_project(user, opts.merge!(namespace_id: group.id))
- it { expect(@project).to be_valid }
- it { expect(@project.owner).to eq(@group) }
- it { expect(@project.namespace).to eq(@group) }
+ expect(project).to be_valid
+ expect(project.owner).to eq(group)
+ expect(project.namespace).to eq(group)
+ expect(user.authorized_projects).to include(project)
end
+ end
- context 'error handling' do
- it 'handles invalid options' do
- @opts.merge!({ default_branch: 'master' } )
- expect(create_project(@user, @opts)).to eq(nil)
- end
+ context 'error handling' do
+ it 'handles invalid options' do
+ opts.merge!({ default_branch: 'master' } )
+ expect(create_project(user, opts)).to eq(nil)
end
+ end
- context 'wiki_enabled creates repository directory' do
- context 'wiki_enabled true creates wiki repository directory' do
- before do
- @project = create_project(@user, @opts)
- @path = ProjectWiki.new(@project, @user).send(:path_to_repo)
- end
+ context 'wiki_enabled creates repository directory' do
+ context 'wiki_enabled true creates wiki repository directory' do
+ it do
+ project = create_project(user, opts)
+ path = ProjectWiki.new(project, user).send(:path_to_repo)
- it { expect(File.exist?(@path)).to be_truthy }
+ expect(File.exist?(path)).to be_truthy
end
+ end
- context 'wiki_enabled false does not create wiki repository directory' do
- before do
- @opts.merge!(wiki_enabled: false)
- @project = create_project(@user, @opts)
- @path = ProjectWiki.new(@project, @user).send(:path_to_repo)
- end
+ context 'wiki_enabled false does not create wiki repository directory' do
+ it do
+ opts.merge!(wiki_enabled: false)
+ project = create_project(user, opts)
+ path = ProjectWiki.new(project, user).send(:path_to_repo)
- it { expect(File.exist?(@path)).to be_falsey }
+ expect(File.exist?(path)).to be_falsey
end
end
+ end
- context 'builds_enabled global setting' do
- let(:project) { create_project(@user, @opts) }
-
- subject { project.builds_enabled? }
+ context 'builds_enabled global setting' do
+ let(:project) { create_project(user, opts) }
- context 'global builds_enabled false does not enable CI by default' do
- before do
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
- end
+ subject { project.builds_enabled? }
- it { is_expected.to be_falsey }
+ context 'global builds_enabled false does not enable CI by default' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
end
- context 'global builds_enabled true does enable CI by default' do
- before do
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
- end
+ it { is_expected.to be_falsey }
+ end
- it { is_expected.to be_truthy }
+ context 'global builds_enabled true does enable CI by default' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
end
+
+ it { is_expected.to be_truthy }
end
+ end
- context 'restricted visibility level' do
- before do
- stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ context 'restricted visibility level' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
- @opts.merge!(
- visibility_level: Gitlab::VisibilityLevel.options['Public']
- )
- end
+ opts.merge!(
+ visibility_level: Gitlab::VisibilityLevel.options['Public']
+ )
+ end
- it 'does not allow a restricted visibility level for non-admins' do
- project = create_project(@user, @opts)
- expect(project).to respond_to(:errors)
- expect(project.errors.messages).to have_key(:visibility_level)
- expect(project.errors.messages[:visibility_level].first).to(
- match('restricted by your GitLab administrator')
- )
- end
+ it 'does not allow a restricted visibility level for non-admins' do
+ project = create_project(user, opts)
+ expect(project).to respond_to(:errors)
+ expect(project.errors.messages).to have_key(:visibility_level)
+ expect(project.errors.messages[:visibility_level].first).to(
+ match('restricted by your GitLab administrator')
+ )
+ end
- it 'allows a restricted visibility level for admins' do
- admin = create(:admin)
- project = create_project(admin, @opts)
+ it 'allows a restricted visibility level for admins' do
+ admin = create(:admin)
+ project = create_project(admin, opts)
- expect(project.errors.any?).to be(false)
- expect(project.saved?).to be(true)
- end
+ expect(project.errors.any?).to be(false)
+ expect(project.saved?).to be(true)
end
+ end
- context 'repository creation' do
- it 'synchronously creates the repository' do
- expect_any_instance_of(Project).to receive(:create_repository)
+ context 'repository creation' do
+ it 'synchronously creates the repository' do
+ expect_any_instance_of(Project).to receive(:create_repository)
- project = create_project(@user, @opts)
- expect(project).to be_valid
- expect(project.owner).to eq(@user)
- expect(project.namespace).to eq(@user.namespace)
- end
+ project = create_project(user, opts)
+ expect(project).to be_valid
+ expect(project.owner).to eq(user)
+ expect(project.namespace).to eq(user.namespace)
+ end
+ end
+
+ context 'when there is an active service template' do
+ before do
+ create(:service, project: nil, template: true, active: true)
+ end
+
+ it 'creates a service from this template' do
+ project = create_project(user, opts)
+
+ expect(project.services.count).to eq 1
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 7dcd03496bb..90771825f5c 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -7,15 +7,21 @@ describe Projects::DestroyService, services: true do
let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
let!(:async) { false } # execute or async_execute
+ shared_examples 'deleting the project' do
+ it 'deletes the project' do
+ expect(Project.all).not_to include(project)
+ expect(Dir.exist?(path)).to be_falsey
+ expect(Dir.exist?(remove_path)).to be_falsey
+ end
+ end
+
context 'Sidekiq inline' do
before do
# Run sidekiq immediatly to check that renamed repository will be removed
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
- it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exist?(path)).to be_falsey }
- it { expect(Dir.exist?(remove_path)).to be_falsey }
+ it_behaves_like 'deleting the project'
end
context 'Sidekiq fake' do
@@ -38,11 +44,21 @@ describe Projects::DestroyService, services: true do
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
- it 'deletes the project' do
- expect(Project.all).not_to include(project)
- expect(Dir.exist?(path)).to be_falsey
- expect(Dir.exist?(remove_path)).to be_falsey
+ it_behaves_like 'deleting the project'
+ end
+
+ context 'delete with pipeline' do # which has optimistic locking
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+ before do
+ expect(project).to receive(:destroy!).and_call_original
+
+ perform_enqueued_jobs do
+ destroy_project(project, user, {})
+ end
end
+
+ it_behaves_like 'deleting the project'
end
context 'container registry' do
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index b57e338b782..becf627a4f5 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -169,7 +169,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unsubscribe command' do
it 'populates subscription_event: "unsubscribe" if content contains /unsubscribe' do
- issuable.subscribe(developer)
+ issuable.subscribe(developer, project)
_, updates = service.execute(content, issuable)
expect(updates).to eq(subscription_event: 'unsubscribe')
@@ -321,7 +321,7 @@ describe SlashCommands::InterpretService, services: true do
it_behaves_like 'multiple label with same argument' do
let(:content) { %(/label ~"#{inprogress.title}" \n/label ~#{inprogress.title}) }
let(:issuable) { issue }
- end
+ end
it_behaves_like 'unlabel command' do
let(:content) { %(/unlabel ~"#{inprogress.title}") }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 5bb107fdd85..0e8adb68721 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe SystemNoteService, services: true do
+ include Gitlab::Routing.url_helpers
+
let(:project) { create(:project) }
let(:author) { create(:user) }
let(:noteable) { create(:issue, project: project) }
@@ -48,7 +50,7 @@ describe SystemNoteService, services: true do
context 'without existing commits' do
it 'adds a message header' do
- expect(note_lines[0]).to eq "Added #{new_commits.size} commits:"
+ expect(note_lines[0]).to eq "added #{new_commits.size} commits"
end
it 'adds a message line for each commit' do
@@ -118,7 +120,7 @@ describe SystemNoteService, services: true do
context 'when assignee added' do
it 'sets the note text' do
- expect(subject.note).to eq "Reassigned to @#{assignee.username}"
+ expect(subject.note).to eq "assigned to @#{assignee.username}"
end
end
@@ -126,7 +128,7 @@ describe SystemNoteService, services: true do
let(:assignee) { nil }
it 'sets the note text' do
- expect(subject.note).to eq 'Assignee removed'
+ expect(subject.note).to eq 'removed assignee'
end
end
end
@@ -145,7 +147,7 @@ describe SystemNoteService, services: true do
let(:removed) { [] }
it 'sets the note text' do
- expect(subject.note).to eq "Added ~#{labels[0].id} ~#{labels[1].id} labels"
+ expect(subject.note).to eq "added ~#{labels[0].id} ~#{labels[1].id} labels"
end
end
@@ -154,7 +156,7 @@ describe SystemNoteService, services: true do
let(:removed) { labels }
it 'sets the note text' do
- expect(subject.note).to eq "Removed ~#{labels[0].id} ~#{labels[1].id} labels"
+ expect(subject.note).to eq "removed ~#{labels[0].id} ~#{labels[1].id} labels"
end
end
@@ -163,7 +165,7 @@ describe SystemNoteService, services: true do
let(:removed) { [labels[1]] }
it 'sets the note text' do
- expect(subject.note).to eq "Added ~#{labels[0].id} and removed ~#{labels[1].id} labels"
+ expect(subject.note).to eq "added ~#{labels[0].id} and removed ~#{labels[1].id} labels"
end
end
end
@@ -177,7 +179,7 @@ describe SystemNoteService, services: true do
context 'when milestone added' do
it 'sets the note text' do
- expect(subject.note).to eq "Milestone changed to #{milestone.to_reference}"
+ expect(subject.note).to eq "changed milestone to #{milestone.to_reference}"
end
end
@@ -185,7 +187,7 @@ describe SystemNoteService, services: true do
let(:milestone) { nil }
it 'sets the note text' do
- expect(subject.note).to eq 'Milestone removed'
+ expect(subject.note).to eq 'removed milestone'
end
end
end
@@ -202,13 +204,13 @@ describe SystemNoteService, services: true do
let(:source) { double('commit', gfm_reference: 'commit 123456') }
it 'sets the note text' do
- expect(subject.note).to eq "Status changed to #{status} by commit 123456"
+ expect(subject.note).to eq "#{status} via commit 123456"
end
end
context 'without a source' do
it 'sets the note text' do
- expect(subject.note).to eq "Status changed to #{status}"
+ expect(subject.note).to eq status
end
end
end
@@ -223,8 +225,8 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
- it "posts the Merge When Build Succeeds system note" do
- expect(subject.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-f]{40} succeeds/
+ it "posts the 'merge when pipeline succeeds' system note" do
+ expect(subject.note).to match /enabled an automatic merge when the pipeline for (\w+\/\w+@)?\h{40} succeeds/
end
end
@@ -237,8 +239,8 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
- it "posts the Merge When Build Succeeds system note" do
- expect(subject.note).to eq "Canceled the automatic merge"
+ it "posts the 'merge when pipeline succeeds' system note" do
+ expect(subject.note).to eq "canceled the automatic merge"
end
end
@@ -250,7 +252,7 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
expect(subject.note).
- to eq "Changed title: **{-Old title-}** → **{+#{noteable.title}+}**"
+ to eq "changed title from **{-Old title-}** to **{+#{noteable.title}+}**"
end
end
end
@@ -262,7 +264,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'a system note'
it 'sets the note text' do
- expect(subject.note).to eq 'Made the issue visible'
+ expect(subject.note).to eq 'made the issue visible to everyone'
end
end
end
@@ -276,7 +278,7 @@ describe SystemNoteService, services: true do
context 'when target branch name changed' do
it 'sets the note text' do
- expect(subject.note).to eq "Target branch changed from `#{old_branch}` to `#{new_branch}`"
+ expect(subject.note).to eq "changed target branch from `#{old_branch}` to `#{new_branch}`"
end
end
end
@@ -288,7 +290,7 @@ describe SystemNoteService, services: true do
context 'when source branch deleted' do
it 'sets the note text' do
- expect(subject.note).to eq "Deleted source branch `feature`"
+ expect(subject.note).to eq "deleted source branch `feature`"
end
end
end
@@ -300,7 +302,7 @@ describe SystemNoteService, services: true do
context 'when a branch is created from the new branch button' do
it 'sets the note text' do
- expect(subject.note).to match /\AStarted branch [`1-mepmep`]/
+ expect(subject.note).to match /\Acreated branch [`1-mepmep`]/
end
end
end
@@ -336,13 +338,13 @@ describe SystemNoteService, services: true do
let(:mentioner) { project2.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "Mentioned in commit #{mentioner.to_reference(project)}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference(project)}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "Mentioned in issue #{mentioner.to_reference(project)}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference(project)}"
end
end
end
@@ -352,13 +354,13 @@ describe SystemNoteService, services: true do
let(:mentioner) { project.repository.commit }
it 'references the mentioning commit' do
- expect(subject.note).to eq "Mentioned in commit #{mentioner.to_reference}"
+ expect(subject.note).to eq "mentioned in commit #{mentioner.to_reference}"
end
end
context 'from non-Commit' do
it 'references the mentioning object' do
- expect(subject.note).to eq "Mentioned in issue #{mentioner.to_reference}"
+ expect(subject.note).to eq "mentioned in issue #{mentioner.to_reference}"
end
end
end
@@ -368,7 +370,11 @@ describe SystemNoteService, services: true do
describe '.cross_reference?' do
it 'is truthy when text begins with expected text' do
- expect(described_class.cross_reference?('Mentioned in something')).to be_truthy
+ expect(described_class.cross_reference?('mentioned in something')).to be_truthy
+ end
+
+ it 'is truthy when text begins with legacy capitalized expected text' do
+ expect(described_class.cross_reference?('mentioned in something')).to be_truthy
end
it 'is falsey when text does not begin with expected text' do
@@ -431,6 +437,19 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(noteable, commit1)).
to be_falsey
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ # Mention issue (noteable) from commit0
+ system_note = described_class.cross_reference(noteable, commit0, author)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is truthy when already mentioned' do
+ expect(described_class.cross_reference_exists?(noteable, commit0)).
+ to be_truthy
+ end
+ end
end
context 'commit from commit' do
@@ -448,6 +467,19 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(commit1, commit0)).
to be_falsey
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ # Mention commit1 from commit0
+ system_note = described_class.cross_reference(commit0, commit1, author)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is truthy when already mentioned' do
+ expect(described_class.cross_reference_exists?(commit0, commit1)).
+ to be_truthy
+ end
+ end
end
context 'commit with cross-reference from fork' do
@@ -463,6 +495,18 @@ describe SystemNoteService, services: true do
expect(described_class.cross_reference_exists?(noteable, commit2)).
to be true
end
+
+ context 'legacy capitalized cross reference' do
+ before do
+ system_note = described_class.cross_reference(noteable, commit0, author2)
+ system_note.update(note: system_note.note.capitalize)
+ end
+
+ it 'is true when a fork mentions an external issue' do
+ expect(described_class.cross_reference_exists?(noteable, commit2)).
+ to be true
+ end
+ end
end
end
@@ -486,7 +530,7 @@ describe SystemNoteService, services: true do
end
it 'mentions referenced project' do
- expect(subject.note).to include new_project.to_reference
+ expect(subject.note).to include new_project.path_with_namespace
end
end
@@ -496,7 +540,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'cross project mentionable'
it 'notifies about noteable being moved to' do
- expect(subject.note).to match /Moved to/
+ expect(subject.note).to match /moved to/
end
end
@@ -506,7 +550,7 @@ describe SystemNoteService, services: true do
it_behaves_like 'cross project mentionable'
it 'notifies about noteable being moved from' do
- expect(subject.note).to match /Moved from/
+ expect(subject.note).to match /moved from/
end
end
@@ -534,44 +578,166 @@ describe SystemNoteService, services: true do
let(:project) { create(:jira_project) }
let(:author) { create(:user) }
let(:issue) { create(:issue, project: project) }
- let(:mergereq) { create(:merge_request, :simple, target_project: project, source_project: project) }
+ let(:merge_request) { create(:merge_request, :simple, target_project: project, source_project: project) }
let(:jira_issue) { ExternalIssue.new("JIRA-1", project)}
let(:jira_tracker) { project.jira_service }
let(:commit) { project.commit }
let(:comment_url) { jira_api_comment_url(jira_issue.id) }
let(:success_message) { "JiraService SUCCESS: Successfully posted to http://jira.example.net." }
- before { stub_jira_urls(jira_issue.id) }
+ before do
+ stub_jira_urls(jira_issue.id)
+ jira_service_settings
+ end
+
+ noteable_types = ["merge_requests", "commit"]
+
+ noteable_types.each do |type|
+ context "when noteable is a #{type}" do
+ it "blocks cross reference when #{type.underscore}_events is false" do
+ jira_tracker.update("#{type}_events" => false)
- context 'in JIRA issue tracker' do
- before { jira_service_settings }
+ noteable = type == "commit" ? commit : merge_request
+ result = described_class.cross_reference(jira_issue, noteable, author)
- describe "new reference" do
- subject { described_class.cross_reference(jira_issue, commit, author) }
+ expect(result).to eq("Events for #{noteable.class.to_s.underscore.humanize.pluralize.downcase} are disabled.")
+ end
+
+ it "blocks cross reference when #{type.underscore}_events is true" do
+ jira_tracker.update("#{type}_events" => true)
- it { is_expected.to eq(success_message) }
+ noteable = type == "commit" ? commit : merge_request
+ result = described_class.cross_reference(jira_issue, noteable, author)
+
+ expect(result).to eq(success_message)
+ end
end
end
- context 'issue from an issue' do
- context 'in JIRA issue tracker' do
- before { jira_service_settings }
+ describe "new reference" do
+ context 'for commits' do
+ it "creates comment" do
+ result = described_class.cross_reference(jira_issue, commit, author)
+
+ expect(result).to eq(success_message)
+ end
+
+ it "creates remote link" do
+ described_class.cross_reference(jira_issue, commit, author)
+
+ expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
+ body: hash_including(
+ GlobalID: "GitLab",
+ object: {
+ url: namespace_project_commit_url(project.namespace, project, commit),
+ title: "GitLab: Mentioned on commit - #{commit.title}",
+ icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" },
+ status: { resolved: false }
+ }
+ )
+ ).once
+ end
+ end
+
+ context 'for issues' do
+ let(:issue) { create(:issue, project: project) }
+
+ it "creates comment" do
+ result = described_class.cross_reference(jira_issue, issue, author)
+
+ expect(result).to eq(success_message)
+ end
+
+ it "creates remote link" do
+ described_class.cross_reference(jira_issue, issue, author)
+
+ expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
+ body: hash_including(
+ GlobalID: "GitLab",
+ object: {
+ url: namespace_project_issue_url(project.namespace, project, issue),
+ title: "GitLab: Mentioned on issue - #{issue.title}",
+ icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" },
+ status: { resolved: false }
+ }
+ )
+ ).once
+ end
+ end
- subject { described_class.cross_reference(jira_issue, issue, author) }
+ context 'for snippets' do
+ let(:snippet) { create(:snippet, project: project) }
- it { is_expected.to eq(success_message) }
+ it "creates comment" do
+ result = described_class.cross_reference(jira_issue, snippet, author)
+
+ expect(result).to eq(success_message)
+ end
+
+ it "creates remote link" do
+ described_class.cross_reference(jira_issue, snippet, author)
+
+ expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
+ body: hash_including(
+ GlobalID: "GitLab",
+ object: {
+ url: namespace_project_snippet_url(project.namespace, project, snippet),
+ title: "GitLab: Mentioned on snippet - #{snippet.title}",
+ icon: { title: "GitLab", url16x16: "https://gitlab.com/favicon.ico" },
+ status: { resolved: false }
+ }
+ )
+ ).once
+ end
end
end
describe "existing reference" do
before do
- 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}'"
+ 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}'"
allow_any_instance_of(JIRA::Resource::Issue).to receive(:comments).and_return([OpenStruct.new(body: message)])
end
- subject { described_class.cross_reference(jira_issue, commit, author) }
+ it "does not return success message" do
+ result = described_class.cross_reference(jira_issue, commit, author)
+
+ expect(result).not_to eq(success_message)
+ end
+
+ it 'does not try to create comment and remote link' do
+ subject
+
+ expect(WebMock).not_to have_requested(:post, jira_api_comment_url(jira_issue))
+ expect(WebMock).not_to have_requested(:post, jira_api_remote_link_url(jira_issue))
+ end
+ end
+ end
+
+ describe '.discussion_continued_in_issue' do
+ let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first }
+ let(:merge_request) { discussion.noteable }
+ let(:project) { merge_request.source_project }
+ let(:issue) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+
+ def reloaded_merge_request
+ MergeRequest.find(merge_request.id)
+ end
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'creates a new note in the discussion' do
+ # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded.
+ expect { SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) }.
+ to change { reloaded_merge_request.discussions.first.notes.size }.by(1)
+ end
+
+ it 'mentions the created issue in the system note' do
+ note = SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue)
- it { is_expected.not_to eq(success_message) }
+ expect(note.note).to include(issue.to_reference)
end
end
end