Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb71
-rw-r--r--spec/services/ci/image_for_build_service_spec.rb50
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb20
-rw-r--r--spec/services/ci/register_build_service_spec.rb45
-rw-r--r--spec/services/ci/retry_build_service_spec.rb142
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb196
-rw-r--r--spec/services/ci/update_runner_service_spec.rb41
-rw-r--r--spec/services/create_deployment_service_spec.rb17
-rw-r--r--spec/services/groups/destroy_service_spec.rb21
-rw-r--r--spec/services/issues/build_service_spec.rb11
-rw-r--r--spec/services/issues/create_service_spec.rb11
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb25
-rw-r--r--spec/services/merge_requests/build_service_spec.rb13
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb29
-rw-r--r--spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb37
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb45
-rw-r--r--spec/services/merge_requests/resolve_service_spec.rb13
-rw-r--r--spec/services/notes/create_service_spec.rb50
-rw-r--r--spec/services/notification_service_spec.rb67
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/destroy_service_spec.rb19
-rw-r--r--spec/services/protected_branches/create_service_spec.rb4
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb39
-rw-r--r--spec/services/spam_service_spec.rb71
-rw-r--r--spec/services/system_note_service_spec.rb49
-rw-r--r--spec/services/tags/create_service_spec.rb (renamed from spec/services/create_tag_service_spec.rb)2
-rw-r--r--spec/services/tags/destroy_service_spec.rb (renamed from spec/services/delete_tag_service_spec.rb)2
-rw-r--r--spec/services/todo_service_spec.rb246
-rw-r--r--spec/services/users/destroy_spec.rb79
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb74
-rw-r--r--spec/services/wiki_pages/create_service_spec.rb36
-rw-r--r--spec/services/wiki_pages/destroy_service_spec.rb21
-rw-r--r--spec/services/wiki_pages/update_service_spec.rb37
34 files changed, 1288 insertions, 301 deletions
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index bb26513103d..b91234ddb1e 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -72,7 +72,7 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
shared_examples 'a pullable and pushable' do
it_behaves_like 'a accessible' do
- let(:actions) { ['pull', 'push'] }
+ let(:actions) { %w(pull push) }
end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index ceaca96e25b..8459a3d8cfb 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -79,66 +79,53 @@ describe Ci::CreatePipelineService, services: true do
context 'when commit contains a [ci skip] directive' do
let(:message) { "some message[ci skip]" }
- let(:messageFlip) { "some message[skip ci]" }
- let(:capMessage) { "some message[CI SKIP]" }
- let(:capMessageFlip) { "some message[SKIP CI]" }
+
+ ci_messages = [
+ "some message[ci skip]",
+ "some message[skip ci]",
+ "some message[CI SKIP]",
+ "some message[SKIP CI]",
+ "some message[ci_skip]",
+ "some message[skip_ci]",
+ "some message[ci-skip]",
+ "some message[skip-ci]"
+ ]
before do
allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message }
end
- it "skips builds creation if there is [ci skip] tag in commit message" do
- commits = [{ message: message }]
- pipeline = execute(ref: 'refs/heads/master',
- before: '00000000',
- after: project.commit.id,
- commits: commits)
+ ci_messages.each do |ci_message|
+ it "skips builds creation if the commit message is #{ci_message}" do
+ commits = [{ message: ci_message }]
+ pipeline = execute(ref: 'refs/heads/master',
+ before: '00000000',
+ after: project.commit.id,
+ commits: commits)
- expect(pipeline).to be_persisted
- expect(pipeline.builds.any?).to be false
- expect(pipeline.status).to eq("skipped")
- end
-
- it "skips builds creation if there is [skip ci] tag in commit message" do
- commits = [{ message: messageFlip }]
- pipeline = execute(ref: 'refs/heads/master',
- before: '00000000',
- after: project.commit.id,
- commits: commits)
-
- expect(pipeline).to be_persisted
- expect(pipeline.builds.any?).to be false
- expect(pipeline.status).to eq("skipped")
+ expect(pipeline).to be_persisted
+ expect(pipeline.builds.any?).to be false
+ expect(pipeline.status).to eq("skipped")
+ end
end
- it "skips builds creation if there is [CI SKIP] tag in commit message" do
- commits = [{ message: capMessage }]
- pipeline = execute(ref: 'refs/heads/master',
- before: '00000000',
- after: project.commit.id,
- commits: commits)
-
- expect(pipeline).to be_persisted
- expect(pipeline.builds.any?).to be false
- expect(pipeline.status).to eq("skipped")
- end
+ it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do
+ allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" }
- it "skips builds creation if there is [SKIP CI] tag in commit message" do
- commits = [{ message: capMessageFlip }]
+ commits = [{ message: "some message" }]
pipeline = execute(ref: 'refs/heads/master',
before: '00000000',
after: project.commit.id,
commits: commits)
expect(pipeline).to be_persisted
- expect(pipeline.builds.any?).to be false
- expect(pipeline.status).to eq("skipped")
+ expect(pipeline.builds.first.name).to eq("rspec")
end
- it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do
- allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" }
+ it "does not skip builds creation if the commit message is nil" do
+ allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { nil }
- commits = [{ message: "some message" }]
+ commits = [{ message: nil }]
pipeline = execute(ref: 'refs/heads/master',
before: '00000000',
after: project.commit.id,
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
deleted file mode 100644
index b3e0a7b9b58..00000000000
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require 'spec_helper'
-
-module Ci
- describe ImageForBuildService, services: true do
- let(:service) { ImageForBuildService.new }
- let(:project) { FactoryGirl.create(:empty_project) }
- let(:commit_sha) { '01234567890123456789' }
- let(:pipeline) { project.ensure_pipeline('master', commit_sha) }
- let(:build) { FactoryGirl.create(:ci_build, pipeline: pipeline) }
-
- describe '#execute' do
- before { build }
-
- context 'branch name' do
- before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) }
- before { build.run! }
- let(:image) { service.execute(project, ref: 'master') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown branch name' do
- let(:image) { service.execute(project, ref: 'feature') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- end
-
- context 'commit sha' do
- before { build.run! }
- let(:image) { service.execute(project, sha: build.sha) }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-running.svg') }
- it { expect(image.name).to eq('build-running.svg') }
- end
-
- context 'unknown commit sha' do
- let(:image) { service.execute(project, sha: '0000000') }
-
- it { expect(image).to be_kind_of(OpenStruct) }
- it { expect(image.path.to_s).to include('public/ci/build-unknown.svg') }
- it { expect(image.name).to eq('build-unknown.svg') }
- 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 ebb11166964..de68fb64726 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -1,8 +1,16 @@
require 'spec_helper'
-describe Ci::ProcessPipelineService, services: true do
- let(:pipeline) { create(:ci_empty_pipeline, ref: 'master') }
+describe Ci::ProcessPipelineService, :services do
let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+
+ let(:pipeline) do
+ create(:ci_empty_pipeline, ref: 'master', project: project)
+ end
+
+ before do
+ project.add_developer(user)
+ end
describe '#execute' do
context 'start queuing next builds' do
@@ -285,7 +293,7 @@ describe Ci::ProcessPipelineService, services: true do
expect(builds.pluck(:name))
.to contain_exactly('build:1', 'build:2', 'test:1', 'test:2')
- Ci::Build.retry(pipeline.builds.find_by(name: 'test:2')).success
+ Ci::Build.retry(pipeline.builds.find_by(name: 'test:2'), user).success
expect(builds.pluck(:name)).to contain_exactly(
'build:1', 'build:2', 'test:1', 'test:2', 'test:2', 'deploy:1', 'deploy:2')
@@ -333,7 +341,7 @@ describe Ci::ProcessPipelineService, services: true do
expect(builds.pending.count).to eq(1)
expect(all_builds.count).to eq(4)
- # When pending build succeeds in stage test, we enqueue deploy stage.
+ # When pending merge_when_pipeline_succeeds in stage test, we enqueue deploy stage.
#
succeed_pending
process_pipeline
@@ -369,9 +377,7 @@ describe Ci::ProcessPipelineService, services: true do
builds.pending.update_all(status: 'success')
end
- def manual_actions
- pipeline.manual_actions
- end
+ delegate :manual_actions, to: :pipeline
def create_build(name, stage_idx, when_value = nil)
create(:ci_build,
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index d9f774a1095..cd7dd53025c 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -170,6 +170,51 @@ module Ci
end
end
+ context 'when first build is stalled' do
+ before do
+ pending_build.lock_version = 10
+ end
+
+ subject { described_class.new(specific_runner).execute }
+
+ context 'with multiple builds are in queue' do
+ let!(:other_build) { create :ci_build, pipeline: pipeline }
+
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build, other_build])
+ end
+
+ it "receives second build from the queue" do
+ expect(subject).to be_valid
+ expect(subject.build).to eq(other_build)
+ end
+ end
+
+ context 'when single build is in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([pending_build])
+ end
+
+ it "does not receive any valid result" do
+ expect(subject).not_to be_valid
+ end
+ end
+
+ context 'when there is no build in queue' do
+ before do
+ allow_any_instance_of(Ci::RegisterBuildService).to receive(:builds_for_specific_runner)
+ .and_return([])
+ end
+
+ it "does not receive builds but result is valid" do
+ expect(subject).to be_valid
+ expect(subject.build).to be_nil
+ end
+ end
+ end
+
def execute(runner)
described_class.new(runner).execute.build
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
new file mode 100644
index 00000000000..65af4e13118
--- /dev/null
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -0,0 +1,142 @@
+require 'spec_helper'
+
+describe Ci::RetryBuildService, :services do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ let(:service) do
+ described_class.new(project, user)
+ end
+
+ CLONE_ACCESSORS = described_class::CLONE_ACCESSORS
+
+ REJECT_ACCESSORS =
+ %i[id status user token coverage trace runner artifacts_expire_at
+ artifacts_file artifacts_metadata artifacts_size created_at
+ updated_at started_at finished_at queued_at erased_by
+ erased_at].freeze
+
+ IGNORE_ACCESSORS =
+ %i[type lock_version target_url gl_project_id deploy job_id base_tags
+ commit_id deployments erased_by_id last_deployment project_id
+ runner_id tag_taggings taggings tags trigger_request_id
+ user_id].freeze
+
+ shared_examples 'build duplication' do
+ let(:build) do
+ create(:ci_build, :failed, :artifacts_expired, :erased,
+ :queued, :coverage, :tags, :allowed_to_fail, :on_tag,
+ :teardown_environment, :triggered, :trace,
+ description: 'some build', pipeline: pipeline)
+ end
+
+ describe 'clone accessors' do
+ CLONE_ACCESSORS.each do |attribute|
+ it "clones #{attribute} build attribute" do
+ expect(new_build.send(attribute)).to be_present
+ expect(new_build.send(attribute)).to eq build.send(attribute)
+ end
+ end
+ end
+
+ describe 'reject acessors' do
+ REJECT_ACCESSORS.each do |attribute|
+ it "does not clone #{attribute} build attribute" do
+ expect(new_build.send(attribute)).not_to eq build.send(attribute)
+ end
+ end
+ end
+
+ it 'has correct number of known attributes' do
+ known_accessors = CLONE_ACCESSORS + REJECT_ACCESSORS + IGNORE_ACCESSORS
+
+ # :tag_list is a special case, this accessor does not exist
+ # in reflected associations, comes from `act_as_taggable` and
+ # we use it to copy tags, instead of reusing tags.
+ #
+ current_accessors =
+ Ci::Build.attribute_names.map(&:to_sym) +
+ Ci::Build.reflect_on_all_associations.map(&:name) +
+ [:tag_list]
+
+ current_accessors.uniq!
+
+ expect(known_accessors).to contain_exactly(*current_accessors)
+ end
+ end
+
+ describe '#execute' do
+ let(:new_build) { service.execute(build) }
+
+ context 'when user has ability to execute build' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'build duplication'
+
+ it 'creates a new build that represents the old one' do
+ expect(new_build.name).to eq build.name
+ end
+
+ it 'enqueues the new build' do
+ expect(new_build).to be_pending
+ end
+
+ it 'resolves todos for old build that failed' do
+ expect(MergeRequests::AddTodoWhenBuildFailsService)
+ .to receive_message_chain(:new, :close)
+
+ service.execute(build)
+ end
+
+ context 'when there are subsequent builds that are skipped' do
+ let!(:subsequent_build) do
+ create(:ci_build, :skipped, stage_idx: 1, pipeline: pipeline)
+ end
+
+ it 'resumes pipeline processing in subsequent stages' do
+ service.execute(build)
+
+ expect(subsequent_build.reload).to be_created
+ end
+ end
+ end
+
+ context 'when user does not have ability to execute build' do
+ it 'raises an error' do
+ expect { service.execute(build) }
+ .to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+ end
+
+ describe '#reprocess' do
+ let(:new_build) { service.reprocess(build) }
+
+ context 'when user has ability to execute build' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'build duplication'
+
+ it 'creates a new build that represents the old one' do
+ expect(new_build.name).to eq build.name
+ end
+
+ it 'does not enqueue the new build' do
+ expect(new_build).to be_created
+ end
+ end
+
+ context 'when user does not have ability to execute build' do
+ it 'raises an error' do
+ expect { service.reprocess(build) }
+ .to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
new file mode 100644
index 00000000000..8b1ed6470e4
--- /dev/null
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -0,0 +1,196 @@
+require 'spec_helper'
+
+describe Ci::RetryPipelineService, '#execute', :services do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:service) { described_class.new(project, user) }
+
+ context 'when user has ability to modify pipeline' do
+ let(:user) { create(:admin) }
+
+ context 'when there are failed builds in the last stage' do
+ before do
+ create_build('rspec 1', :success, 0)
+ create_build('rspec 2', :failed, 1)
+ create_build('rspec 3', :canceled, 1)
+ end
+
+ it 'enqueues all builds in the last stage' do
+ service.execute(pipeline)
+
+ expect(build('rspec 2')).to be_pending
+ expect(build('rspec 3')).to be_pending
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when there are failed or canceled builds in the first stage' do
+ before do
+ create_build('rspec 1', :failed, 0)
+ create_build('rspec 2', :canceled, 0)
+ create_build('rspec 3', :canceled, 1)
+ create_build('spinach 1', :canceled, 2)
+ end
+
+ it 'retries builds failed builds and marks subsequent for processing' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('rspec 2')).to be_pending
+ expect(build('rspec 3')).to be_created
+ expect(build('spinach 1')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when there is failed build present which was run on failure' do
+ before do
+ create_build('rspec 1', :failed, 0)
+ create_build('rspec 2', :canceled, 0)
+ create_build('rspec 3', :canceled, 1)
+ create_build('report 1', :failed, 2)
+ end
+
+ it 'retries builds only in the first stage' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('rspec 2')).to be_pending
+ expect(build('rspec 3')).to be_created
+ expect(build('report 1')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+
+ it 'creates a new job for report job in this case' do
+ service.execute(pipeline)
+
+ expect(statuses.where(name: 'report 1').first).to be_retried
+ end
+ end
+
+ context 'when the last stage was skipepd' do
+ before do
+ create_build('build 1', :success, 0)
+ create_build('test 2', :failed, 1)
+ create_build('report 3', :skipped, 2)
+ create_build('report 4', :skipped, 2)
+ end
+
+ it 'retries builds only in the first stage' do
+ service.execute(pipeline)
+
+ expect(build('build 1')).to be_success
+ expect(build('test 2')).to be_pending
+ expect(build('report 3')).to be_created
+ expect(build('report 4')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when pipeline contains manual actions' do
+ context 'when there is a canceled manual action in first stage' do
+ before do
+ create_build('rspec 1', :failed, 0)
+ create_build('staging', :canceled, 0, :manual)
+ create_build('rspec 2', :canceled, 1)
+ end
+
+ it 'retries builds failed builds and marks subsequent for processing' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('staging')).to be_skipped
+ expect(build('rspec 2')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when there is a skipped manual action in last stage' do
+ before do
+ create_build('rspec 1', :canceled, 0)
+ create_build('rspec 2', :skipped, 0, :manual)
+ create_build('staging', :skipped, 1, :manual)
+ end
+
+ it 'retries canceled job and reprocesses manual actions' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('rspec 2')).to be_skipped
+ expect(build('staging')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when there is a created manual action in the last stage' do
+ before do
+ create_build('rspec 1', :canceled, 0)
+ create_build('staging', :created, 1, :manual)
+ end
+
+ it 'retries canceled job and does not update the manual action' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('staging')).to be_created
+ expect(pipeline.reload).to be_running
+ end
+ end
+
+ context 'when there is a created manual action in the first stage' do
+ before do
+ create_build('rspec 1', :canceled, 0)
+ create_build('staging', :created, 0, :manual)
+ end
+
+ it 'retries canceled job and skipps the manual action' do
+ service.execute(pipeline)
+
+ expect(build('rspec 1')).to be_pending
+ expect(build('staging')).to be_skipped
+ expect(pipeline.reload).to be_running
+ end
+ end
+ end
+
+ it 'closes all todos about failed jobs for pipeline' do
+ expect(MergeRequests::AddTodoWhenBuildFailsService)
+ .to receive_message_chain(:new, :close_all)
+
+ service.execute(pipeline)
+ end
+
+ it 'reprocesses the pipeline' do
+ expect(pipeline).to receive(:process!)
+
+ service.execute(pipeline)
+ end
+ end
+
+ context 'when user is not allowed to retry pipeline' do
+ it 'raises an error' do
+ expect { service.execute(pipeline) }
+ .to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+
+ def statuses
+ pipeline.reload.statuses
+ end
+
+ def build(name)
+ statuses.latest.find_by(name: name)
+ end
+
+ def create_build(name, status, stage_num, on = 'on_success')
+ create(:ci_build, name: name,
+ status: status,
+ stage: "stage_#{stage_num}",
+ stage_idx: stage_num,
+ when: on,
+ pipeline: pipeline) do |build|
+ pipeline.update_status
+ end
+ end
+end
diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb
new file mode 100644
index 00000000000..e429fcfc72f
--- /dev/null
+++ b/spec/services/ci/update_runner_service_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Ci::UpdateRunnerService, :services do
+ let(:runner) { create(:ci_runner) }
+
+ describe '#update' do
+ before do
+ allow(runner).to receive(:tick_runner_queue)
+ end
+
+ context 'with description params' do
+ let(:params) { { description: 'new runner' } }
+
+ it 'updates the runner and ticking the queue' do
+ expect(update).to be_truthy
+
+ runner.reload
+
+ expect(runner).to have_received(:tick_runner_queue)
+ expect(runner.description).to eq('new runner')
+ end
+ end
+
+ context 'when params are not valid' do
+ let(:params) { { run_untagged: false } }
+
+ it 'does not update and give false because it is not valid' do
+ expect(update).to be_falsey
+
+ runner.reload
+
+ expect(runner).not_to have_received(:tick_runner_queue)
+ expect(runner.run_untagged).to be_truthy
+ end
+ end
+
+ def update
+ described_class.new(runner).update(params)
+ end
+ end
+end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index cf0a18aacec..18b964e2453 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -9,7 +9,8 @@ describe CreateDeploymentService, services: true do
describe '#execute' do
let(:options) { nil }
let(:params) do
- { environment: 'production',
+ {
+ environment: 'production',
ref: 'master',
tag: false,
sha: '97de212e80737a608d939f648d959671fb0a0142',
@@ -83,10 +84,11 @@ describe CreateDeploymentService, services: true do
context 'for environment with invalid name' do
let(:params) do
- { environment: 'name,with,commas',
+ {
+ environment: 'name,with,commas',
ref: 'master',
tag: false,
- sha: '97de212e80737a608d939f648d959671fb0a0142',
+ sha: '97de212e80737a608d939f648d959671fb0a0142'
}
end
@@ -101,7 +103,8 @@ describe CreateDeploymentService, services: true do
context 'when variables are used' do
let(:params) do
- { environment: 'review-apps/$CI_BUILD_REF_NAME',
+ {
+ environment: 'review-apps/$CI_BUILD_REF_NAME',
ref: 'master',
tag: false,
sha: '97de212e80737a608d939f648d959671fb0a0142',
@@ -234,7 +237,11 @@ describe CreateDeploymentService, services: true do
context 'when build is retried' do
it_behaves_like 'does create environment and deployment' do
- let(:deployable) { Ci::Build.retry(build) }
+ before do
+ project.add_developer(user)
+ end
+
+ let(:deployable) { Ci::Build.retry(build, user) }
subject { deployable.success }
end
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index f86189b68e9..98c560ffb26 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -5,18 +5,24 @@ describe Groups::DestroyService, services: true do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
let!(:project) { create(:project, namespace: group) }
let!(:gitlab_shell) { Gitlab::Shell.new }
let!(:remove_path) { group.path + "+#{group.id}+deleted" }
+ before do
+ group.add_user(user, Gitlab::Access::OWNER)
+ end
+
shared_examples 'group destruction' do |async|
context 'database records' do
before do
destroy_group(group, user, async)
end
- it { expect(Group.all).not_to include(group) }
- it { expect(Project.all).not_to include(project) }
+ it { expect(Group.unscoped.all).not_to include(group) }
+ it { expect(Group.unscoped.all).not_to include(nested_group) }
+ it { expect(Project.unscoped.all).not_to include(project) }
end
context 'file system' do
@@ -32,7 +38,7 @@ describe Groups::DestroyService, services: true do
context 'Sidekiq fake' do
before do
- # Dont run sidekiq to check if renamed repository exists
+ # Don't run sidekiq to check if renamed repository exists
Sidekiq::Testing.fake! { destroy_group(group, user, async) }
end
@@ -95,4 +101,13 @@ describe Groups::DestroyService, services: true do
describe 'synchronous delete' do
it_behaves_like 'group destruction', false
end
+
+ context 'projects in pending_delete' do
+ before do
+ project.pending_delete = true
+ project.save
+ end
+
+ it_behaves_like 'group destruction', false
+ end
end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index 4cfba35c830..09807e5d35b 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -120,11 +120,20 @@ describe Issues::BuildService, services: true do
end
describe '#execute' do
+ let(:milestone) { create(:milestone, project: project) }
+
it 'builds a new issues with given params' do
- issue = described_class.new(project, user, title: 'Issue #1', description: 'Issue description').execute
+ issue = described_class.new(
+ project,
+ user,
+ title: 'Issue #1',
+ description: 'Issue description',
+ milestone_id: milestone.id,
+ ).execute
expect(issue.title).to eq('Issue #1')
expect(issue.description).to eq('Issue description')
+ expect(issue.milestone).to eq(milestone)
end
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 30578ee4c7d..6045d00ff09 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -46,6 +46,7 @@ describe Issues::CreateService, services: true do
expect(issue).to be_persisted
expect(issue.title).to eq('Awesome issue')
+ expect(issue.description).to eq('please fix')
expect(issue.assignee).to be_nil
expect(issue.labels).to be_empty
expect(issue.milestone).to be_nil
@@ -229,16 +230,6 @@ describe Issues::CreateService, services: true do
expect { issue }.not_to change{SpamLog.last.recaptcha_verified}
end
end
-
- context 'when spam log title does not match the issue title' do
- before do
- opts[:title] = 'Another issue'
- end
-
- it 'does not mark spam_log as recaptcha_verified' do
- expect { issue }.not_to change{SpamLog.last.recaptcha_verified}
- end
- end
end
context 'when recaptcha was not verified' do
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 bb7830c7eea..d80fb8a1af1 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
@@ -17,7 +17,7 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
described_class.new(project, user, commit_message: 'Awesome message')
end
- let(:todo_service) { TodoService.new }
+ let(:todo_service) { spy('todo service') }
let(:merge_request) do
create(:merge_request, merge_user: user,
@@ -107,4 +107,27 @@ describe MergeRequests::AddTodoWhenBuildFailsService do
end
end
end
+
+ describe '#close_all' do
+ context 'when using pipeline that belongs to merge request' do
+ it 'resolves todos about failed builds for pipeline' do
+ service.close_all(pipeline)
+
+ expect(todo_service)
+ .to have_received(:merge_request_build_retried)
+ .with(merge_request)
+ end
+ end
+
+ context 'when pipeline is not related to merge request' do
+ let(:pipeline) { create(:ci_empty_pipeline) }
+
+ it 'does not resolve any todos about failed builds' do
+ service.close_all(pipeline)
+
+ expect(todo_service)
+ .not_to have_received(:merge_request_build_retried)
+ end
+ end
+ end
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index dc945ca4868..0768f644036 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -44,15 +44,14 @@ describe MergeRequests::BuildService, services: true do
end
end
- context 'missing target branch' do
- let(:target_branch) { '' }
+ context 'when target branch is missing' do
+ let(:target_branch) { nil }
+ let(:commits) { Commit.decorate([commit_1], project) }
- it 'forbids the merge request from being created' do
+ it 'creates compare object with target branch as default branch' do
expect(merge_request.can_be_created).to eq(false)
- end
-
- it 'adds an error message to the merge request' do
- expect(merge_request.errors).to contain_exactly('You must select source and target branch')
+ expect(merge_request.compare).to be_present
+ expect(merge_request.target_branch).to eq(project.default_branch)
end
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 5a89acc96a4..d96f819e66a 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -149,35 +149,46 @@ describe MergeRequests::MergeService, services: true do
context "error handling" do
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
- it 'saves error if there is an exception' do
- allow(service).to receive(:repository).and_raise("error message")
+ before do
+ allow(Rails.logger).to receive(:error)
+ end
+ it 'logs and saves error if there is an exception' do
+ error_message = 'error message'
+
+ allow(service).to receive(:repository).and_raise("error message")
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
- expect(merge_request.merge_error).to eq("Something went wrong during merge: error message")
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
- it 'saves error if there is an PreReceiveError exception' do
- allow(service).to receive(:repository).and_raise(GitHooksService::PreReceiveError, "error")
+ it 'logs and saves error if there is an PreReceiveError exception' do
+ error_message = 'error message'
+ allow(service).to receive(:repository).and_raise(GitHooksService::PreReceiveError, error_message)
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
- expect(merge_request.merge_error).to eq("error")
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
- it 'aborts if there is a merge conflict' do
+ it 'logs and saves error if there is a merge conflict' do
+ error_message = 'Conflicts detected during merge'
+
allow_any_instance_of(Repository).to receive(:merge).and_return(false)
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
- expect(merge_request.open?).to be_truthy
+ expect(merge_request).to be_open
expect(merge_request.merge_commit_sha).to be_nil
- expect(merge_request.merge_error).to eq("Conflicts detected during merge")
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
end
end
diff --git a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
index f92978a33a3..c2f205c389d 100644
--- a/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb
@@ -5,7 +5,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
let(:project) { create(:project) }
let(:mr_merge_if_green_enabled) do
- create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
source_branch: "master", target_branch: 'feature',
source_project: project, target_project: project, state: "opened")
end
@@ -36,7 +36,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
it 'sets the params, merge_user, and flag' do
expect(merge_request).to be_valid
- expect(merge_request.merge_when_build_succeeds).to be_truthy
+ expect(merge_request.merge_when_pipeline_succeeds).to be_truthy
expect(merge_request.merge_params).to eq commit_message: 'Awesome message'
expect(merge_request.merge_user).to be user
end
@@ -62,7 +62,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
end
it 'updates the merge params' do
- expect(SystemNoteService).not_to receive(:merge_when_build_succeeds)
+ expect(SystemNoteService).not_to receive(:merge_when_pipeline_succeeds)
service.execute(mr_merge_if_green_enabled)
expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key)
@@ -82,7 +82,7 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
sha: merge_request_head, status: 'success')
end
- it "merges all merge requests with merge when build succeeds enabled" do
+ it "merges all merge requests with merge when the pipeline succeeds enabled" do
expect(MergeWorker).to receive(:perform_async)
service.trigger(triggering_pipeline)
end
@@ -111,6 +111,31 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
service.trigger(unrelated_pipeline)
end
end
+
+ context 'when the merge request is not mergeable' do
+ let(:mr_conflict) do
+ create(:merge_request, merge_when_pipeline_succeeds: true, merge_user: user,
+ source_branch: 'master', target_branch: 'feature-conflict',
+ source_project: project, target_project: project)
+ end
+
+ let(:conflict_pipeline) do
+ create(:ci_pipeline, project: project, ref: mr_conflict.source_branch,
+ sha: mr_conflict.diff_head_sha, status: 'success')
+ end
+
+ it 'does not merge the merge request' do
+ expect(MergeWorker).not_to receive(:perform_async)
+
+ service.trigger(conflict_pipeline)
+ end
+
+ it 'creates todos for unmergeability' do
+ expect_any_instance_of(TodoService).to receive(:merge_request_became_unmergeable).with(mr_conflict)
+
+ service.trigger(conflict_pipeline)
+ end
+ end
end
describe "#cancel" do
@@ -118,8 +143,8 @@ describe MergeRequests::MergeWhenPipelineSucceedsService do
service.cancel(mr_merge_if_green_enabled)
end
- it "resets all the merge_when_build_succeeds params" do
- expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey
+ it "resets all the pipeline succeeds params" do
+ expect(mr_merge_if_green_enabled.merge_when_pipeline_succeeds).to be_falsey
expect(mr_merge_if_green_enabled.merge_params).to eq({})
expect(mr_merge_if_green_enabled.merge_user).to be nil
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 2cc21acab7b..ff367f54d2a 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -18,7 +18,7 @@ describe MergeRequests::RefreshService, services: true do
source_branch: 'master',
target_branch: 'feature',
target_project: @project,
- merge_when_build_succeeds: true,
+ merge_when_pipeline_succeeds: true,
merge_user: @user)
@fork_merge_request = create(:merge_request,
@@ -62,7 +62,7 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.notes).not_to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@merge_request.merge_when_build_succeeds).to be_falsey }
+ it { expect(@merge_request.merge_when_pipeline_succeeds).to be_falsey }
it { expect(@merge_request.diff_head_sha).to eq(@newrev) }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
@@ -287,41 +287,64 @@ describe MergeRequests::RefreshService, services: true do
it 'references the commit that caused the Work in Progress status' do
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
-
allow(refresh_service).to receive(:find_new_commits)
refresh_service.instance_variable_set("@commits", [
- instance_double(
- Commit,
+ double(
id: 'aaaaaaa',
+ sha: '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e',
short_id: 'aaaaaaa',
title: 'Fix issue',
work_in_progress?: false
),
- instance_double(
- Commit,
+ double(
id: 'bbbbbbb',
+ sha: '498214de67004b1da3d820901307bed2a68a8ef6',
short_id: 'bbbbbbb',
title: 'fixup! Fix issue',
work_in_progress?: true,
to_reference: 'bbbbbbb'
),
- instance_double(
- Commit,
+ double(
id: 'ccccccc',
+ sha: '1b12f15a11fc6e62177bef08f47bc7b5ce50b141',
short_id: 'ccccccc',
title: 'fixup! Fix issue',
work_in_progress?: true,
to_reference: 'ccccccc'
),
])
-
refresh_service.execute(@oldrev, @newrev, 'refs/heads/wip')
reload_mrs
-
expect(@merge_request.notes.last.note).to eq(
"marked as a **Work In Progress** from bbbbbbb"
)
end
+
+ it 'does not mark as WIP based on commits that do not belong to an MR' do
+ allow(refresh_service).to receive(:find_new_commits)
+ refresh_service.instance_variable_set("@commits", [
+ double(
+ id: 'aaaaaaa',
+ sha: 'aaaaaaa',
+ short_id: 'aaaaaaa',
+ title: 'Fix issue',
+ work_in_progress?: false
+ ),
+ double(
+ id: 'bbbbbbb',
+ sha: 'bbbbbbbb',
+ short_id: 'bbbbbbb',
+ title: 'fixup! Fix issue',
+ work_in_progress?: true,
+ to_reference: 'bbbbbbb'
+ )
+ ])
+
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ reload_mrs
+
+ expect(@merge_request.work_in_progress?).to be_falsey
+ end
end
def reload_mrs
diff --git a/spec/services/merge_requests/resolve_service_spec.rb b/spec/services/merge_requests/resolve_service_spec.rb
index a0e51681725..d33535d22af 100644
--- a/spec/services/merge_requests/resolve_service_spec.rb
+++ b/spec/services/merge_requests/resolve_service_spec.rb
@@ -59,20 +59,19 @@ describe MergeRequests::ResolveService do
it 'creates a commit with the correct parents' do
expect(merge_request.source_branch_head.parents.map(&:id)).
- to eq(['1450cd639e0bc6721eb02800169e464f212cde06',
- '824be604a34828eb682305f0d963056cfac87b2d'])
+ to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+ 824be604a34828eb682305f0d963056cfac87b2d))
end
end
context 'when the source project is a fork and does not contain the HEAD of the target branch' do
let!(:target_head) do
- project.repository.commit_file(
+ project.repository.create_file(
user,
'new-file-in-target',
'',
message: 'Add new file in target',
- branch_name: 'conflict-start',
- update: false)
+ branch_name: 'conflict-start')
end
before do
@@ -125,8 +124,8 @@ describe MergeRequests::ResolveService do
it 'creates a commit with the correct parents' do
expect(merge_request.source_branch_head.parents.map(&:id)).
- to eq(['1450cd639e0bc6721eb02800169e464f212cde06',
- '824be604a34828eb682305f0d963056cfac87b2d'])
+ to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+ 824be604a34828eb682305f0d963056cfac87b2d))
end
it 'sets the content to the content given' do
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 9c92a5080c6..152c6d20daa 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -102,47 +102,19 @@ describe Notes::CreateService, services: true do
expect(subject.note).to eq(params[:note])
end
end
- end
-
- describe "award emoji" do
- before do
- project.team << [user, :master]
- end
-
- it "creates an award emoji" do
- opts = {
- note: ':smile: ',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.name).to eq('smile')
- end
- it "creates regular note if emoji name is invalid" do
- opts = {
- note: ':smile: moretext:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
- note = described_class.new(project, user, opts).execute
-
- expect(note).to be_valid
- expect(note.note).to eq(opts[:note])
- end
-
- it "normalizes the emoji name" do
- opts = {
- note: ':+1:',
- noteable_type: 'Issue',
- noteable_id: issue.id
- }
-
- expect_any_instance_of(TodoService).to receive(:new_award_emoji).with(issue, user)
+ describe 'note with emoji only' do
+ it 'creates regular note' do
+ opts = {
+ note: ':smile: ',
+ noteable_type: 'Issue',
+ noteable_id: issue.id
+ }
+ note = described_class.new(project, user, opts).execute
- described_class.new(project, user, opts).execute
+ expect(note).to be_valid
+ expect(note.note).to eq(':smile:')
+ end
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 7cf2cd9968f..ebbaea4e59a 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -146,6 +146,16 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
+ it "emails the note author if they've opted into notifications about their activity" do
+ add_users_with_subscription(note.project, issue)
+ note.author.notified_of_own_activity = true
+ reset_delivered_emails!
+
+ notification.new_note(note)
+
+ should_email(note.author)
+ end
+
it 'filters out "mentioned in" notes' do
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
@@ -476,6 +486,20 @@ describe NotificationService, services: true do
should_not_email(issue.assignee)
end
+ it "emails the author if they've opted into notifications about their activity" do
+ issue.author.notified_of_own_activity = true
+
+ notification.new_issue(issue, issue.author)
+
+ should_email(issue.author)
+ end
+
+ it "doesn't email the author if they haven't opted into notifications about their activity" do
+ notification.new_issue(issue, issue.author)
+
+ should_not_email(issue.author)
+ end
+
it "emails subscribers of the issue's labels" do
user_1 = create(:user)
user_2 = create(:user)
@@ -665,6 +689,19 @@ describe NotificationService, services: true do
should_email(subscriber_to_label_2)
end
+ it "emails the current user if they've opted into notifications about their activity" do
+ subscriber_to_label_2.notified_of_own_activity = true
+ notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2)
+
+ should_email(subscriber_to_label_2)
+ end
+
+ it "doesn't email the current user if they haven't opted into notifications about their activity" do
+ notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2)
+
+ should_not_email(subscriber_to_label_2)
+ end
+
it "doesn't send email to anyone but subscribers of the given labels" do
notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
@@ -818,6 +855,20 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
+ it "emails the author if they've opted into notifications about their activity" do
+ merge_request.author.notified_of_own_activity = true
+
+ notification.new_merge_request(merge_request, merge_request.author)
+
+ should_email(merge_request.author)
+ end
+
+ it "doesn't email the author if they haven't opted into notifications about their activity" do
+ notification.new_merge_request(merge_request, merge_request.author)
+
+ should_not_email(merge_request.author)
+ end
+
it "emails subscribers of the merge request's labels" do
user_1 = create(:user)
user_2 = create(:user)
@@ -999,20 +1050,28 @@ describe NotificationService, services: true do
should_not_email(@u_lazy_participant)
end
- it "notifies the merger when merge_when_build_succeeds is true" do
- merge_request.merge_when_build_succeeds = true
+ it "notifies the merger when the pipeline succeeds is true" do
+ merge_request.merge_when_pipeline_succeeds = true
notification.merge_mr(merge_request, @u_watcher)
should_email(@u_watcher)
end
- it "does not notify the merger when merge_when_build_succeeds is false" do
- merge_request.merge_when_build_succeeds = false
+ it "does not notify the merger when the pipeline succeeds is false" do
+ merge_request.merge_when_pipeline_succeeds = false
notification.merge_mr(merge_request, @u_watcher)
should_not_email(@u_watcher)
end
+ it "notifies the merger when the pipeline succeeds is false but they've opted into notifications about their activity" do
+ merge_request.merge_when_pipeline_succeeds = false
+ @u_watcher.notified_of_own_activity = true
+ notification.merge_mr(merge_request, @u_watcher)
+
+ should_email(@u_watcher)
+ end
+
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index af515ad2e0e..62f21049b0b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -50,7 +50,7 @@ describe Projects::CreateService, '#execute', services: true do
context 'error handling' do
it 'handles invalid options' do
- opts.merge!({ default_branch: 'master' } )
+ opts[:default_branch] = 'master'
expect(create_project(user, opts)).to eq(nil)
end
end
@@ -67,7 +67,7 @@ describe Projects::CreateService, '#execute', services: true do
context 'wiki_enabled false does not create wiki repository directory' do
it do
- opts.merge!(wiki_enabled: false)
+ opts[:wiki_enabled] = false
project = create_project(user, opts)
path = ProjectWiki.new(project, user).send(:path_to_repo)
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 3faa88c00a1..74bfba44dfd 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -50,6 +50,25 @@ describe Projects::DestroyService, services: true do
it { expect(Dir.exist?(remove_path)).to be_truthy }
end
+ context 'when flushing caches fail' do
+ before do
+ new_user = create(:user)
+ project.team.add_user(new_user, Gitlab::Access::DEVELOPER)
+ allow_any_instance_of(Projects::DestroyService).to receive(:flush_caches).and_raise(Redis::CannotConnectError)
+ end
+
+ it 'keeps project team intact upon an error' do
+ Sidekiq::Testing.inline! do
+ begin
+ destroy_project(project, user, {})
+ rescue Redis::CannotConnectError
+ end
+ end
+
+ expect(project.team.members.count).to eq 1
+ end
+ end
+
context 'with async_execute' do
let(:async) { true }
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index 7d4eff3b6ef..6ea8f309981 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -6,8 +6,8 @@ describe ProtectedBranches::CreateService, services: true do
let(:params) do
{
name: 'master',
- merge_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ],
- push_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ]
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }],
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]
}
end
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index 0b0925983eb..52e8678cb9d 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -267,6 +267,14 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ shared_examples 'award command' do
+ it 'toggle award 100 emoji if content containts /award :100:' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(emoji_award: "100")
+ end
+ end
+
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@@ -654,6 +662,37 @@ describe SlashCommands::InterpretService, services: true do
end
end
+ context '/award command' do
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'award command' do
+ let(:content) { '/award :100:' }
+ let(:issuable) { merge_request }
+ end
+
+ context 'ignores command with no argument' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award' }
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'ignores non-existing / invalid emojis' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/award noop' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/award :lorem_ipsum:' }
+ let(:issuable) { issue }
+ end
+ end
+ end
+
context '/target_branch command' do
let(:non_empty_project) { create(:project) }
let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) }
diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb
index 271c17dd8c0..4ce3b95aa87 100644
--- a/spec/services/spam_service_spec.rb
+++ b/spec/services/spam_service_spec.rb
@@ -1,46 +1,61 @@
require 'spec_helper'
describe SpamService, services: true do
- describe '#check' do
- let(:project) { create(:project, :public) }
- let(:issue) { create(:issue, project: project) }
- let(:request) { double(:request, env: {}) }
+ describe '#when_recaptcha_verified' do
+ def check_spam(issue, request, recaptcha_verified)
+ described_class.new(issue, request).when_recaptcha_verified(recaptcha_verified) do
+ 'yielded'
+ end
+ end
+
+ it 'yields block when recaptcha was already verified' do
+ issue = build_stubbed(:issue)
- def check_spam(issue, request)
- described_class.new(issue, request).check
+ expect(check_spam(issue, nil, true)).to eql('yielded')
end
- context 'when indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
+ context 'when recaptcha was not verified' do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:request) { double(:request, env: {}) }
- it 'returns false when request is missing' do
- expect(check_spam(issue, nil)).to be_falsey
- end
+ context 'when indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
- it 'returns false when issue is not public' do
- issue = create(:issue, project: create(:project, :private))
+ it 'doesnt check as spam when request is missing' do
+ check_spam(issue, nil, false)
- expect(check_spam(issue, request)).to be_falsey
- end
+ expect(issue.spam).to be_falsey
+ end
- it 'returns true' do
- expect(check_spam(issue, request)).to be_truthy
- end
+ it 'checks as spam' do
+ check_spam(issue, request, false)
- it 'creates a spam log' do
- expect { check_spam(issue, request) }.to change { SpamLog.count }.from(0).to(1)
- end
- end
+ expect(issue.spam).to be_truthy
+ end
- context 'when not indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+ it 'creates a spam log' do
+ expect { check_spam(issue, request, false) }
+ .to change { SpamLog.count }.from(0).to(1)
+ end
- it 'returns false' do
- expect(check_spam(issue, request)).to be_falsey
+ it 'doesnt yield block' do
+ expect(check_spam(issue, request, false))
+ .to eql(SpamLog.last)
+ end
end
- it 'does not create a spam log' do
- expect { check_spam(issue, request) }.not_to change { SpamLog.count }
+ context 'when not indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+
+ it 'returns false' do
+ expect(check_spam(issue, request, false)).to be_falsey
+ end
+
+ it 'does not create a spam log' do
+ expect { check_spam(issue, request, false) }
+ .not_to change { SpamLog.count }
+ end
end
end
end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 7f027ae02a2..36a17a3bf2e 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -215,13 +215,13 @@ describe SystemNoteService, services: true do
end
end
- describe '.merge_when_build_succeeds' do
+ describe '.merge_when_pipeline_succeeds' do
let(:pipeline) { build(:ci_pipeline_without_jobs )}
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.diff_head_commit) }
+ subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) }
it_behaves_like 'a system note'
@@ -230,12 +230,12 @@ describe SystemNoteService, services: true do
end
end
- describe '.cancel_merge_when_build_succeeds' do
+ describe '.cancel_merge_when_pipeline_succeeds' do
let(:noteable) do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) }
+ subject { described_class.cancel_merge_when_pipeline_succeeds(noteable, project, author) }
it_behaves_like 'a system note'
@@ -418,45 +418,6 @@ describe SystemNoteService, services: true do
to be_truthy
end
end
-
- context 'when noteable is an Issue' do
- let(:issue) { create(:issue, project: project) }
-
- it 'is truthy when issue is closed' do
- issue.close
-
- expect(described_class.cross_reference_disallowed?(issue, project.commit)).
- to be_truthy
- end
-
- it 'is falsey when issue is open' do
- expect(described_class.cross_reference_disallowed?(issue, project.commit)).
- to be_falsy
- end
- end
-
- context 'when noteable is a Merge Request' do
- let(:merge_request) { create(:merge_request, :simple, source_project: project) }
-
- it 'is truthy when merge request is closed' do
- allow(merge_request).to receive(:closed?).and_return(:true)
-
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_truthy
- end
-
- it 'is truthy when merge request is merged' do
- allow(merge_request).to receive(:closed?).and_return(:true)
-
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_truthy
- end
-
- it 'is falsey when merge request is open' do
- expect(described_class.cross_reference_disallowed?(merge_request, project.commit)).
- to be_falsy
- end
- end
end
describe '.cross_reference_exists?' do
@@ -631,7 +592,7 @@ describe SystemNoteService, services: true do
jira_service_settings
end
- noteable_types = ["merge_requests", "commit"]
+ noteable_types = %w(merge_requests commit)
noteable_types.each do |type|
context "when noteable is a #{type}" do
diff --git a/spec/services/create_tag_service_spec.rb b/spec/services/tags/create_service_spec.rb
index 7dc43c50b0d..5478b8c9ec0 100644
--- a/spec/services/create_tag_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe CreateTagService, services: true do
+describe Tags::CreateService, services: true do
let(:project) { create(:project) }
let(:repository) { project.repository }
let(:user) { create(:user) }
diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/tags/destroy_service_spec.rb
index 477551f5036..a388c93379a 100644
--- a/spec/services/delete_tag_service_spec.rb
+++ b/spec/services/tags/destroy_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DeleteTagService, services: true do
+describe Tags::DestroyService, services: true do
let(:project) { create(:project) }
let(:repository) { project.repository }
let(:user) { create(:user) }
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 13d584a8975..fb9a8462f84 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -9,7 +9,9 @@ describe TodoService, services: true do
let(:admin) { create(:admin) }
let(:john_doe) { create(:user) }
let(:project) { create(:project) }
- let(:mentions) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
+ let(:mentions) { 'FYI: ' + [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
+ let(:directly_addressed) { [author, assignee, john_doe, member, guest, non_member, admin].map(&:to_reference).join(' ') }
+ let(:directly_addressed_and_mentioned) { member.to_reference + ", what do you think? cc: " + [guest, admin].map(&:to_reference).join(' ') }
let(:service) { described_class.new }
before do
@@ -21,8 +23,10 @@ describe TodoService, services: true do
describe 'Issues' do
let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
+ let(:addressed_issue) { create(:issue, project: project, assignee: john_doe, author: author, description: "#{directly_addressed}\n- [ ] Task 1\n- [ ] Task 2") }
let(:unassigned_issue) { create(:issue, project: project, assignee: nil) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) }
+ let(:addressed_confident_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: directly_addressed) }
describe '#new_issue' do
it 'creates a todo if assigned' do
@@ -52,6 +56,26 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
+ it 'creates a directly addressed todo for each valid addressed user' do
+ service.new_issue(addressed_issue, author)
+
+ should_create_todo(user: member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: guest, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: author, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: john_doe, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
+ it 'creates correct todos for each valid user based on the type of mention' do
+ issue.update(description: directly_addressed_and_mentioned)
+
+ service.new_issue(issue, author)
+
+ should_create_todo(user: member, target: issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
+ end
+
it 'does not create todo if user can not see the issue when issue is confidential' do
service.new_issue(confidential_issue, john_doe)
@@ -63,6 +87,17 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
+ it 'does not create directly addressed todo if user cannot see the issue when issue is confidential' do
+ service.new_issue(addressed_confident_issue, john_doe)
+
+ should_create_todo(user: assignee, target: addressed_confident_issue, author: john_doe, action: Todo::ASSIGNED)
+ should_create_todo(user: author, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: member, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: guest, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: john_doe, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
context 'when a private group is mentioned' do
let(:group) { create :group, :private }
let(:project) { create :project, :private, group: group }
@@ -94,12 +129,38 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
+ it 'creates a todo for each valid user based on the type of mention' do
+ issue.update(description: directly_addressed_and_mentioned)
+
+ service.update_issue(issue, author)
+
+ should_create_todo(user: member, target: issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
+ should_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
+ end
+
+ it 'creates a directly addressed todo for each valid addressed user' do
+ service.update_issue(addressed_issue, author)
+
+ should_create_todo(user: member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: guest, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: john_doe, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: author, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
it 'does not create a todo if user was already mentioned' do
create(:todo, :mentioned, user: member, project: project, target: issue, author: author)
expect { service.update_issue(issue, author) }.not_to change(member.todos, :count)
end
+ it 'does not create a directly addressed todo if user was already mentioned or addressed' do
+ create(:todo, :directly_addressed, user: member, project: project, target: addressed_issue, author: author)
+
+ expect { service.update_issue(addressed_issue, author) }.not_to change(member.todos, :count)
+ end
+
it 'does not create todo if user can not see the issue when issue is confidential' do
service.update_issue(confidential_issue, john_doe)
@@ -111,6 +172,17 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
end
+ it 'does not create a directly addressed todo if user can not see the issue when issue is confidential' do
+ service.update_issue(addressed_confident_issue, john_doe)
+
+ should_create_todo(user: author, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: assignee, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: member, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: guest, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: john_doe, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
context 'issues with a task list' do
it 'does not create todo when tasks are marked as completed' do
issue.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
@@ -125,6 +197,19 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
end
+ it 'does not create directly addressed todo when tasks are marked as completed' do
+ addressed_issue.update(description: "#{directly_addressed}\n- [x] Task 1\n- [x] Task 2\n")
+
+ service.update_issue(addressed_issue, author)
+
+ should_not_create_todo(user: admin, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: assignee, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: author, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: john_doe, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_issue, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
it 'does not raise an error when description not change' do
issue.update(title: 'Sample')
@@ -202,39 +287,51 @@ describe TodoService, services: true do
end
end
- shared_examples 'marking todos as done' do |meth|
- let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
- let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
+ shared_examples 'updating todos state' do |meth, state, new_state|
+ let!(:first_todo) { create(:todo, state, user: john_doe, project: project, target: issue, author: author) }
+ let!(:second_todo) { create(:todo, state, user: john_doe, project: project, target: issue, author: author) }
- it 'marks related todos for the user as done' do
+ it 'updates related todos for the user with the new_state' do
service.send(meth, collection, john_doe)
- expect(first_todo.reload).to be_done
- expect(second_todo.reload).to be_done
+ expect(first_todo.reload.state?(new_state)).to be true
+ expect(second_todo.reload.state?(new_state)).to be true
end
describe 'cached counts' do
it 'updates when todos change' do
- expect(john_doe.todos_done_count).to eq(0)
- expect(john_doe.todos_pending_count).to eq(2)
+ expect(john_doe.todos.where(state: new_state).count).to eq(0)
+ expect(john_doe.todos.where(state: state).count).to eq(2)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.send(meth, collection, john_doe)
- expect(john_doe.todos_done_count).to eq(2)
- expect(john_doe.todos_pending_count).to eq(0)
+ expect(john_doe.todos.where(state: new_state).count).to eq(2)
+ expect(john_doe.todos.where(state: state).count).to eq(0)
end
end
end
describe '#mark_todos_as_done' do
- it_behaves_like 'marking todos as done', :mark_todos_as_done do
+ it_behaves_like 'updating todos state', :mark_todos_as_done, :pending, :done do
let(:collection) { [first_todo, second_todo] }
end
end
describe '#mark_todos_as_done_by_ids' do
- it_behaves_like 'marking todos as done', :mark_todos_as_done_by_ids do
+ it_behaves_like 'updating todos state', :mark_todos_as_done_by_ids, :pending, :done do
+ let(:collection) { [first_todo, second_todo].map(&:id) }
+ end
+ end
+
+ describe '#mark_todos_as_pending' do
+ it_behaves_like 'updating todos state', :mark_todos_as_pending, :done, :pending do
+ let(:collection) { [first_todo, second_todo] }
+ end
+ end
+
+ describe '#mark_todos_as_pending_by_ids' do
+ it_behaves_like 'updating todos state', :mark_todos_as_pending_by_ids, :done, :pending do
let(:collection) { [first_todo, second_todo].map(&:id) }
end
end
@@ -244,8 +341,11 @@ describe TodoService, services: true do
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) }
let(:note) { create(:note, project: project, noteable: issue, author: john_doe, note: mentions) }
+ let(:addressed_note) { create(:note, project: project, noteable: issue, author: john_doe, note: directly_addressed) }
let(:note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: mentions) }
+ let(:addressed_note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: directly_addressed) }
let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: mentions) }
+ let(:addressed_note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: directly_addressed) }
let(:note_on_project_snippet) { create(:note_on_project_snippet, project: project, author: john_doe, note: mentions) }
let(:system_note) { create(:system_note, project: project, noteable: issue) }
@@ -276,6 +376,26 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
end
+ it 'creates a todo for each valid user based on the type of mention' do
+ note.update(note: directly_addressed_and_mentioned)
+
+ service.new_note(note, john_doe)
+
+ should_create_todo(user: member, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: note)
+ should_create_todo(user: admin, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
+ end
+
+ it 'creates a directly addressed todo for each valid addressed user' do
+ service.new_note(addressed_note, john_doe)
+
+ should_create_todo(user: member, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note)
+ should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note)
+ should_create_todo(user: author, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note)
+ should_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note)
+ should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note)
+ end
+
it 'does not create todo if user can not see the issue when leaving a note on a confidential issue' do
service.new_note(note_on_confidential_issue, john_doe)
@@ -287,6 +407,17 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
end
+ it 'does not create a directly addressed todo if user can not see the issue when leaving a note on a confidential issue' do
+ service.new_note(addressed_note_on_confidential_issue, john_doe)
+
+ should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
+ end
+
it 'creates a todo for each valid mentioned user when leaving a note on commit' do
service.new_note(note_on_commit, john_doe)
@@ -296,6 +427,15 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit)
end
+ it 'creates a directly addressed todo for each valid mentioned user when leaving a note on commit' do
+ service.new_note(addressed_note_on_commit, john_doe)
+
+ should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit)
+ should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit)
+ should_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit)
+ should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: addressed_note_on_commit.commit_id, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_commit)
+ end
+
it 'does not create todo when leaving a note on snippet' do
should_not_create_any_todo { service.new_note(note_on_project_snippet, john_doe) }
end
@@ -324,6 +464,7 @@ describe TodoService, services: true do
describe 'Merge Requests' do
let(:mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
+ let(:addressed_mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: "#{directly_addressed}\n- [ ] Task 1\n- [ ] Task 2") }
let(:mr_unassigned) { create(:merge_request, source_project: project, author: author, assignee: nil) }
describe '#new_merge_request' do
@@ -350,6 +491,25 @@ describe TodoService, services: true do
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
+
+ it 'creates a todo for each valid user based on the type of mention' do
+ mr_assigned.update(description: directly_addressed_and_mentioned)
+
+ service.new_merge_request(mr_assigned, author)
+
+ should_create_todo(user: member, target: mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
+ end
+
+ it 'creates a directly addressed todo for each valid addressed user' do
+ service.new_merge_request(addressed_mr_assigned, author)
+
+ should_create_todo(user: member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: author, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: john_doe, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ end
end
describe '#update_merge_request' do
@@ -363,12 +523,37 @@ describe TodoService, services: true do
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
end
+ it 'creates a todo for each valid user based on the type of mention' do
+ mr_assigned.update(description: directly_addressed_and_mentioned)
+
+ service.update_merge_request(mr_assigned, author)
+
+ should_create_todo(user: member, target: mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
+ end
+
+ it 'creates a directly addressed todo for each valid addressed user' do
+ service.update_merge_request(addressed_mr_assigned, author)
+
+ should_create_todo(user: member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: john_doe, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_create_todo(user: author, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
it 'does not create a todo if user was already mentioned' do
create(:todo, :mentioned, user: member, project: project, target: mr_assigned, author: author)
expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count)
end
+ it 'does not create a directly addressed todo if user was already mentioned or addressed' do
+ create(:todo, :directly_addressed, user: member, project: project, target: addressed_mr_assigned, author: author)
+
+ expect{ service.update_merge_request(addressed_mr_assigned, author) }.not_to change(member.todos, :count)
+ end
+
context 'with a task list' do
it 'does not create todo when tasks are marked as completed' do
mr_assigned.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
@@ -384,6 +569,20 @@ describe TodoService, services: true do
should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
end
+ it 'does not create directly addressed todo when tasks are marked as completed' do
+ addressed_mr_assigned.update(description: "#{directly_addressed}\n- [x] Task 1\n- [X] Task 2")
+
+ service.update_merge_request(addressed_mr_assigned, author)
+
+ should_not_create_todo(user: admin, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: assignee, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: author, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: john_doe, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: non_member, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ end
+
it 'does not raise an error when description not change' do
mr_assigned.update(title: 'Sample')
@@ -436,6 +635,11 @@ describe TodoService, services: true do
service.reassigned_merge_request(mr_assigned, author)
should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
end
+
+ it 'does not create a directly addressed todo for guests' do
+ service.reassigned_merge_request(addressed_mr_assigned, author)
+ should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ end
end
describe '#merge_merge_request' do
@@ -452,6 +656,11 @@ describe TodoService, services: true do
service.merge_merge_request(mr_assigned, john_doe)
should_not_create_todo(user: guest, target: mr_assigned, action: Todo::MENTIONED)
end
+
+ it 'does not create directly addressed todo for guests' do
+ service.merge_merge_request(addressed_mr_assigned, john_doe)
+ should_not_create_todo(user: guest, target: addressed_mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
+ end
end
describe '#new_award_emoji' do
@@ -471,7 +680,7 @@ describe TodoService, services: true do
end
it 'creates a pending todo for merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_build_failed(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::BUILD_FAILED)
@@ -491,7 +700,7 @@ describe TodoService, services: true do
describe '#merge_request_became_unmergeable' do
it 'creates a pending todo for a merge_user' do
- mr_unassigned.update(merge_when_build_succeeds: true, merge_user: admin)
+ mr_unassigned.update(merge_when_pipeline_succeeds: true, merge_user: admin)
service.merge_request_became_unmergeable(mr_unassigned)
should_create_todo(user: admin, author: admin, target: mr_unassigned, action: Todo::UNMERGEABLE)
@@ -509,6 +718,7 @@ describe TodoService, services: true do
describe '#new_note' do
let(:mention) { john_doe.to_reference }
let(:diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
+ let(:addressed_diff_note_on_merge_request) { create(:diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "#{mention}, hey!") }
let(:legacy_diff_note_on_merge_request) { create(:legacy_diff_note_on_merge_request, project: project, noteable: mr_unassigned, author: author, note: "Hey #{mention}") }
it 'creates a todo for mentioned user on new diff note' do
@@ -517,6 +727,12 @@ describe TodoService, services: true do
should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::MENTIONED, note: diff_note_on_merge_request)
end
+ it 'creates a directly addressed todo for addressed user on new diff note' do
+ service.new_note(addressed_diff_note_on_merge_request, author)
+
+ should_create_todo(user: john_doe, target: mr_unassigned, author: author, action: Todo::DIRECTLY_ADDRESSED, note: addressed_diff_note_on_merge_request)
+ end
+
it 'creates a todo for mentioned user on legacy diff note' do
service.new_note(legacy_diff_note_on_merge_request, author)
diff --git a/spec/services/users/destroy_spec.rb b/spec/services/users/destroy_spec.rb
index 46e58393218..922e82445d0 100644
--- a/spec/services/users/destroy_spec.rb
+++ b/spec/services/users/destroy_spec.rb
@@ -2,11 +2,11 @@ require 'spec_helper'
describe Users::DestroyService, services: true do
describe "Deletes a user and all their personal projects" do
- let!(:user) { create(:user) }
- let!(:current_user) { create(:user) }
- let!(:namespace) { create(:namespace, owner: user) }
- let!(:project) { create(:project, namespace: namespace) }
- let(:service) { described_class.new(current_user) }
+ let!(:user) { create(:user) }
+ let!(:admin) { create(:admin) }
+ let!(:namespace) { create(:namespace, owner: user) }
+ let!(:project) { create(:project, namespace: namespace) }
+ let(:service) { described_class.new(admin) }
context 'no options are given' do
it 'deletes the user' do
@@ -24,6 +24,54 @@ describe Users::DestroyService, services: true do
end
end
+ context "a deleted user's issues" do
+ let(:project) { create :project }
+
+ before do
+ project.add_developer(user)
+ end
+
+ context "for an issue the user has created" do
+ let!(:issue) { create(:issue, project: project, author: user) }
+
+ before do
+ service.execute(user)
+ end
+
+ it 'does not delete the issue' do
+ expect(Issue.find_by_id(issue.id)).to be_present
+ end
+
+ it 'migrates the issue so that the "Ghost User" is the issue owner' do
+ migrated_issue = Issue.find_by_id(issue.id)
+
+ expect(migrated_issue.author).to eq(User.ghost)
+ end
+
+ it 'blocks the user before migrating issues to the "Ghost User' do
+ expect(user).to be_blocked
+ end
+ end
+
+ context "for an issue the user was assigned to" do
+ let!(:issue) { create(:issue, project: project, assignee: user) }
+
+ before do
+ service.execute(user)
+ end
+
+ it 'does not delete issues the user is assigned to' do
+ expect(Issue.find_by_id(issue.id)).to be_present
+ end
+
+ it 'migrates the issue so that it is "Unassigned"' do
+ migrated_issue = Issue.find_by_id(issue.id)
+
+ expect(migrated_issue.assignee).to be_nil
+ end
+ end
+ end
+
context "solo owned groups present" do
let(:solo_owned) { create(:group) }
let(:member) { create(:group_member) }
@@ -57,5 +105,26 @@ describe Users::DestroyService, services: true do
expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
+
+ context "deletion permission checks" do
+ it 'does not delete the user when user is not an admin' do
+ other_user = create(:user)
+
+ expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect(User.exists?(user.id)).to be(true)
+ end
+
+ it 'allows admins to delete anyone' do
+ described_class.new(admin).execute(user)
+
+ expect(User.exists?(user.id)).to be(false)
+ end
+
+ it 'allows users to delete their own account' do
+ described_class.new(user).execute(user)
+
+ expect(User.exists?(user.id)).to be(false)
+ end
+ end
end
end
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index 690fe979492..08733d6dcf1 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -131,6 +131,80 @@ describe Users::RefreshAuthorizedProjectsService do
it 'sets the values to the access levels' do
expect(hash.values).to eq([Gitlab::Access::MASTER])
end
+
+ context 'personal projects' do
+ it 'includes the project with the right access level' do
+ expect(hash[project.id]).to eq(Gitlab::Access::MASTER)
+ end
+ end
+
+ context 'projects the user is a member of' do
+ let!(:other_project) { create(:empty_project) }
+
+ before do
+ other_project.team.add_reporter(user)
+ end
+
+ it 'includes the project with the right access level' do
+ expect(hash[other_project.id]).to eq(Gitlab::Access::REPORTER)
+ end
+ end
+
+ context 'projects of groups the user is a member of' do
+ let(:group) { create(:group) }
+ let!(:other_project) { create(:project, group: group) }
+
+ before do
+ group.add_owner(user)
+ end
+
+ it 'includes the project with the right access level' do
+ expect(hash[other_project.id]).to eq(Gitlab::Access::OWNER)
+ end
+ end
+
+ context 'projects of subgroups of groups the user is a member of' do
+ let(:group) { create(:group) }
+ let(:nested_group) { create(:group, parent: group) }
+ let!(:other_project) { create(:project, group: nested_group) }
+
+ before do
+ group.add_master(user)
+ end
+
+ it 'includes the project with the right access level' do
+ expect(hash[other_project.id]).to eq(Gitlab::Access::MASTER)
+ end
+ end
+
+ context 'projects shared with groups the user is a member of' do
+ let(:group) { create(:group) }
+ let(:other_project) { create(:empty_project) }
+ let!(:project_group_link) { create(:project_group_link, project: other_project, group: group, group_access: Gitlab::Access::GUEST) }
+
+ before do
+ group.add_master(user)
+ end
+
+ it 'includes the project with the right access level' do
+ expect(hash[other_project.id]).to eq(Gitlab::Access::GUEST)
+ end
+ end
+
+ context 'projects shared with subgroups of groups the user is a member of' do
+ let(:group) { create(:group) }
+ let(:nested_group) { create(:group, parent: group) }
+ let(:other_project) { create(:empty_project) }
+ let!(:project_group_link) { create(:project_group_link, project: other_project, group: nested_group, group_access: Gitlab::Access::DEVELOPER) }
+
+ before do
+ group.add_master(user)
+ end
+
+ it 'includes the project with the right access level' do
+ expect(hash[other_project.id]).to eq(Gitlab::Access::DEVELOPER)
+ end
+ end
end
describe '#current_authorizations_per_project' do
diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb
new file mode 100644
index 00000000000..5341ba3d261
--- /dev/null
+++ b/spec/services/wiki_pages/create_service_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe WikiPages::CreateService, services: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:opts) do
+ {
+ title: 'Title',
+ content: 'Content for wiki page',
+ format: 'markdown'
+ }
+ end
+ let(:service) { described_class.new(project, user, opts) }
+
+ describe '#execute' do
+ context "valid params" do
+ before do
+ allow(service).to receive(:execute_hooks)
+ project.add_master(user)
+ end
+
+ subject { service.execute }
+
+ it 'creates a valid wiki page' do
+ is_expected.to be_valid
+ expect(subject.title).to eq(opts[:title])
+ expect(subject.content).to eq(opts[:content])
+ expect(subject.format).to eq(opts[:format].to_sym)
+ end
+
+ it 'executes webhooks' do
+ expect(service).to have_received(:execute_hooks).once.with(subject, 'create')
+ end
+ end
+ end
+end
diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb
new file mode 100644
index 00000000000..a4b9a390fe2
--- /dev/null
+++ b/spec/services/wiki_pages/destroy_service_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe WikiPages::DestroyService, services: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:wiki_page) { create(:wiki_page) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ before do
+ allow(service).to receive(:execute_hooks)
+ project.add_master(user)
+ end
+
+ it 'executes webhooks' do
+ service.execute(wiki_page)
+
+ expect(service).to have_received(:execute_hooks).once.with(wiki_page, 'delete')
+ end
+ end
+end
diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb
new file mode 100644
index 00000000000..2bccca764d7
--- /dev/null
+++ b/spec/services/wiki_pages/update_service_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe WikiPages::UpdateService, services: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:wiki_page) { create(:wiki_page) }
+ let(:opts) do
+ {
+ content: 'New content for wiki page',
+ format: 'markdown',
+ message: 'New wiki message'
+ }
+ end
+ let(:service) { described_class.new(project, user, opts) }
+
+ describe '#execute' do
+ context "valid params" do
+ before do
+ allow(service).to receive(:execute_hooks)
+ project.add_master(user)
+ end
+
+ subject { service.execute(wiki_page) }
+
+ it 'updates the wiki page' do
+ is_expected.to be_valid
+ expect(subject.content).to eq(opts[:content])
+ expect(subject.format).to eq(opts[:format].to_sym)
+ expect(subject.message).to eq(opts[:message])
+ end
+
+ it 'executes webhooks' do
+ expect(service).to have_received(:execute_hooks).once.with(subject, 'update')
+ end
+ end
+ end
+end