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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-05 03:07:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-05 03:07:49 +0300
commit77237c5a6b9044f58beabc54d3589e5fa09cbfba (patch)
treef43188047fe8955f6cf78e05ae9c2e8f6a019e0b /spec
parent2fd92f2dc784ade9cb4e1c33dd60cbfad7b86818 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/repositories/git_http_controller_spec.rb44
-rw-r--r--spec/features/broadcast_messages_spec.rb56
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb6
-rw-r--r--spec/lib/backup/manager_spec.rb53
-rw-r--r--spec/lib/gitlab/asset_proxy_spec.rb8
-rw-r--r--spec/lib/gitlab/git_access_spec.rb10
-rw-r--r--spec/lib/gitlab/gl_repository/repo_type_spec.rb78
-rw-r--r--spec/lib/gitlab/gl_repository_spec.rb9
-rw-r--r--spec/lib/gitlab/path_regex_spec.rb33
-rw-r--r--spec/lib/gitlab/repo_path_spec.rb80
-rw-r--r--spec/lib/gitlab/repository_cache_spec.rb43
-rw-r--r--spec/lib/gitlab/repository_set_cache_spec.rb39
-rw-r--r--spec/models/concerns/milestoneish_spec.rb71
-rw-r--r--spec/requests/api/broadcast_messages_spec.rb22
-rw-r--r--spec/requests/api/internal/base_spec.rb206
-rw-r--r--spec/requests/groups/milestones_controller_spec.rb46
-rw-r--r--spec/services/issues/close_service_spec.rb22
-rw-r--r--spec/services/issues/create_service_spec.rb8
-rw-r--r--spec/services/issues/reopen_service_spec.rb10
-rw-r--r--spec/services/issues/update_service_spec.rb54
-rw-r--r--spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb8
-rw-r--r--spec/services/metrics/dashboard/default_embed_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/dynamic_embed_service_spec.rb8
-rw-r--r--spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb8
-rw-r--r--spec/services/milestones/closed_issues_count_service_spec.rb24
-rw-r--r--spec/services/milestones/issues_count_service_spec.rb24
-rw-r--r--spec/services/milestones/transfer_service_spec.rb19
-rw-r--r--spec/services/post_receive_service_spec.rb240
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb67
-rw-r--r--spec/views/shared/milestones/_issuable.html.haml_spec.rb33
-rw-r--r--spec/workers/post_receive_spec.rb111
31 files changed, 1188 insertions, 254 deletions
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb
index 10a7b72ca89..2573fdf16ff 100644
--- a/spec/controllers/repositories/git_http_controller_spec.rb
+++ b/spec/controllers/repositories/git_http_controller_spec.rb
@@ -6,16 +6,18 @@ describe Repositories::GitHttpController do
include GitHttpHelpers
let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository) }
+ let_it_be(:project_snippet) { create(:project_snippet, :public, :repository, project: project) }
let(:namespace_id) { project.namespace.to_param }
let(:repository_id) { project.path + '.git' }
- let(:project_params) do
+ let(:container_params) do
{
namespace_id: namespace_id,
repository_id: repository_id
}
end
- let(:params) { project_params }
+ let(:params) { container_params }
describe 'HEAD #info_refs' do
it 'returns 403' do
@@ -27,7 +29,7 @@ describe Repositories::GitHttpController do
shared_examples 'info_refs behavior' do
describe 'GET #info_refs' do
- let(:params) { project_params.merge(service: 'git-upload-pack') }
+ let(:params) { container_params.merge(service: 'git-upload-pack') }
it 'returns 401 for unauthenticated requests to public repositories when http protocol is disabled' do
stub_application_setting(enabled_git_access_protocol: 'ssh')
@@ -41,8 +43,6 @@ describe Repositories::GitHttpController do
end
context 'with authorized user' do
- let(:user) { project.owner }
-
before do
request.headers.merge! auth_env(user.username, user.password, nil)
end
@@ -122,7 +122,7 @@ describe Repositories::GitHttpController do
end
shared_examples 'access checker class' do
- let(:params) { project_params.merge(service: 'git-upload-pack') }
+ let(:params) { container_params.merge(service: 'git-upload-pack') }
it 'calls the right access class checker with the right object' do
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
@@ -136,11 +136,41 @@ describe Repositories::GitHttpController do
end
context 'when repository container is a project' do
- it_behaves_like 'info_refs behavior'
+ it_behaves_like 'info_refs behavior' do
+ let(:user) { project.owner }
+ end
it_behaves_like 'git_upload_pack behavior', true
it_behaves_like 'access checker class' do
let(:expected_class) { Gitlab::GitAccess }
let(:expected_object) { project }
end
end
+
+ context 'when repository container is a personal snippet' do
+ let(:namespace_id) { 'snippets' }
+ let(:repository_id) { personal_snippet.to_param + '.git' }
+
+ it_behaves_like 'info_refs behavior' do
+ let(:user) { personal_snippet.author }
+ end
+ it_behaves_like 'git_upload_pack behavior', false
+ it_behaves_like 'access checker class' do
+ let(:expected_class) { Gitlab::GitAccessSnippet }
+ let(:expected_object) { personal_snippet }
+ end
+ end
+
+ context 'when repository container is a project snippet' do
+ let(:namespace_id) { project.full_path + '/snippets' }
+ let(:repository_id) { project_snippet.to_param + '.git' }
+
+ it_behaves_like 'info_refs behavior' do
+ let(:user) { project_snippet.author }
+ end
+ it_behaves_like 'git_upload_pack behavior', false
+ it_behaves_like 'access checker class' do
+ let(:expected_class) { Gitlab::GitAccessSnippet }
+ let(:expected_object) { project_snippet }
+ end
+ end
end
diff --git a/spec/features/broadcast_messages_spec.rb b/spec/features/broadcast_messages_spec.rb
index 43fbf1010c9..d7c84b8085b 100644
--- a/spec/features/broadcast_messages_spec.rb
+++ b/spec/features/broadcast_messages_spec.rb
@@ -3,28 +3,58 @@
require 'spec_helper'
describe 'Broadcast Messages' do
- let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') }
+ shared_examples 'a Broadcast Messages' do
+ it 'shows broadcast message' do
+ visit root_path
- it 'shows broadcast message' do
- visit root_path
+ expect(page).to have_content 'SampleMessage'
+ end
+ end
+
+ shared_examples 'a dismissable Broadcast Messages' do
+ it 'hides broadcast message after dismiss', :js do
+ visit root_path
+
+ find('.js-dismiss-current-broadcast-notification').click
+
+ expect(page).not_to have_content 'SampleMessage'
+ end
+
+ it 'broadcast message is still hidden after refresh', :js do
+ visit root_path
+
+ find('.js-dismiss-current-broadcast-notification').click
+ visit root_path
+
+ expect(page).not_to have_content 'SampleMessage'
+ end
+ end
+
+ describe 'banner type' do
+ let!(:broadcast_message) { create(:broadcast_message, message: 'SampleMessage') }
+
+ it_behaves_like 'a Broadcast Messages'
+
+ it 'shows broadcast message' do
+ visit root_path
- expect(page).to have_content 'SampleMessage'
+ expect(page).not_to have_selector('.js-dismiss-current-broadcast-notification')
+ end
end
- it 'hides broadcast message after dismiss', :js do
- visit root_path
+ describe 'dismissable banner type' do
+ let!(:broadcast_message) { create(:broadcast_message, dismissable: true, message: 'SampleMessage') }
- find('.js-dismiss-current-broadcast-notification').click
+ it_behaves_like 'a Broadcast Messages'
- expect(page).not_to have_content 'SampleMessage'
+ it_behaves_like 'a dismissable Broadcast Messages'
end
- it 'broadcast message is still hidden after refresh', :js do
- visit root_path
+ describe 'notification type' do
+ let!(:broadcast_message) { create(:broadcast_message, broadcast_type: 'notification', message: 'SampleMessage') }
- find('.js-dismiss-current-broadcast-notification').click
- visit root_path
+ it_behaves_like 'a Broadcast Messages'
- expect(page).not_to have_content 'SampleMessage'
+ it_behaves_like 'a dismissable Broadcast Messages'
end
end
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index 7e181e429d7..69ff5f16ec1 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -14,7 +14,7 @@ describe BroadcastMessagesHelper do
context 'when last broadcast message is hidden' do
before do
- helper.request.cookies["hide_broadcast_notification_message_#{broadcast_message_2.id}"] = 'true'
+ helper.request.cookies["hide_broadcast_message_#{broadcast_message_2.id}"] = 'true'
end
it { is_expected.to eq broadcast_message_1 }
@@ -29,6 +29,10 @@ describe BroadcastMessagesHelper do
describe 'broadcast_message' do
let(:current_broadcast_message) { BroadcastMessage.new(message: 'Current Message') }
+ before do
+ allow(helper).to receive(:current_user).and_return(create(:user))
+ end
+
it 'returns nil when no current message' do
expect(helper.broadcast_message(nil)).to be_nil
end
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 06ad0557e37..cee299522ce 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -214,6 +214,30 @@ describe Backup::Manager do
end
end
+ describe 'verify_backup_version' do
+ context 'on version mismatch' do
+ let(:gitlab_version) { Gitlab::VERSION }
+
+ it 'stops the process' do
+ allow(YAML).to receive(:load_file)
+ .and_return({ gitlab_version: "not #{gitlab_version}" })
+
+ expect { subject.verify_backup_version }.to raise_error SystemExit
+ end
+ end
+
+ context 'on version match' do
+ let(:gitlab_version) { Gitlab::VERSION }
+
+ it 'does nothing' do
+ allow(YAML).to receive(:load_file)
+ .and_return({ gitlab_version: "#{gitlab_version}" })
+
+ expect { subject.verify_backup_version }.not_to raise_error
+ end
+ end
+ end
+
describe '#unpack' do
context 'when there are no backup files in the directory' do
before do
@@ -292,6 +316,23 @@ describe Backup::Manager do
expect(progress).to have_received(:puts).with(a_string_matching('done'))
end
end
+
+ context 'when there is a non-tarred backup in the directory' do
+ before do
+ allow(Dir).to receieve(:glob).and_return(
+ [
+ 'backup_information.yml'
+ ]
+ )
+
+ it 'selects the non-tarred backup to restore from' do
+ expect { subject.unpack }.to output.to_stdout
+ expect(progress).to have_received(:puts)
+ .with(a_string_matching('Non tarred backup found '))
+ expect(Kernel).not_to receive(:system)
+ end
+ end
+ end
end
describe '#upload' do
@@ -329,9 +370,7 @@ describe Backup::Manager do
.with(hash_including(key: backup_filename, public: false))
.and_return(true)
- Dir.chdir(Gitlab.config.backup.path) do
- subject.upload
- end
+ subject.upload
end
it 'adds the DIRECTORY environment variable if present' do
@@ -341,9 +380,7 @@ describe Backup::Manager do
.with(hash_including(key: "daily/#{backup_filename}", public: false))
.and_return(true)
- Dir.chdir(Gitlab.config.backup.path) do
- subject.upload
- end
+ subject.upload
end
end
@@ -373,9 +410,7 @@ describe Backup::Manager do
.with(hash_excluding(public: false))
.and_return(true)
- Dir.chdir(Gitlab.config.backup.path) do
- subject.upload
- end
+ subject.upload
end
end
end
diff --git a/spec/lib/gitlab/asset_proxy_spec.rb b/spec/lib/gitlab/asset_proxy_spec.rb
index f5aa1819982..e406917a5a4 100644
--- a/spec/lib/gitlab/asset_proxy_spec.rb
+++ b/spec/lib/gitlab/asset_proxy_spec.rb
@@ -33,9 +33,15 @@ describe Gitlab::AssetProxy do
expect(described_class.proxy_url(url)).to eq(proxied_url)
end
+ it 'returns original URL for invalid domains' do
+ url = 'foo_bar://'
+
+ expect(described_class.proxy_url(url)).to eq(url)
+ end
+
context 'whitelisted domain' do
it 'returns original URL for single domain whitelist' do
- url = 'http://gitlab.com/test.png'
+ url = 'http://gitlab.com/${default_branch}/test.png'
expect(described_class.proxy_url(url)).to eq(url)
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f95349a2125..a29c56c598f 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -240,7 +240,7 @@ describe Gitlab::GitAccess do
let(:access) do
described_class.new(actor, nil,
protocol, authentication_abilities: authentication_abilities,
- project_path: project_path, namespace_path: namespace_path,
+ repository_path: project_path, namespace_path: namespace_path,
redirected_path: redirected_path)
end
@@ -259,7 +259,7 @@ describe Gitlab::GitAccess do
let(:access) do
described_class.new(actor, nil,
protocol, authentication_abilities: authentication_abilities,
- project_path: project_path, namespace_path: namespace_path,
+ repository_path: project_path, namespace_path: namespace_path,
redirected_path: redirected_path)
end
@@ -453,7 +453,7 @@ describe Gitlab::GitAccess do
let(:access) do
described_class.new(actor, project,
protocol, authentication_abilities: authentication_abilities,
- project_path: project_path, namespace_path: namespace_path,
+ repository_path: project_path, namespace_path: namespace_path,
redirected_path: redirected_path)
end
@@ -598,7 +598,7 @@ describe Gitlab::GitAccess do
let(:public_project) { create(:project, :public, :repository) }
let(:project_path) { public_project.path }
let(:namespace_path) { public_project.namespace.path }
- let(:access) { described_class.new(nil, public_project, 'web', authentication_abilities: [:download_code], project_path: project_path, namespace_path: namespace_path) }
+ let(:access) { described_class.new(nil, public_project, 'web', authentication_abilities: [:download_code], repository_path: project_path, namespace_path: namespace_path) }
context 'when repository is enabled' do
it 'give access to download code' do
@@ -1203,7 +1203,7 @@ describe Gitlab::GitAccess do
def access
described_class.new(actor, project, protocol,
authentication_abilities: authentication_abilities,
- namespace_path: namespace_path, project_path: project_path,
+ namespace_path: namespace_path, repository_path: project_path,
redirected_path: redirected_path, auth_result_type: auth_result_type)
end
diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
index 7cf0442fbe1..6185b068d4c 100644
--- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb
+++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
@@ -5,46 +5,62 @@ describe Gitlab::GlRepository::RepoType do
let_it_be(:project) { create(:project) }
let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let(:project_path) { project.repository.full_path }
+ let(:wiki_path) { project.wiki.repository.full_path }
+ let(:personal_snippet_path) { "snippets/#{personal_snippet.id}" }
+ let(:project_snippet_path) { "#{project.full_path}/snippets/#{project_snippet.id}" }
describe Gitlab::GlRepository::PROJECT do
it_behaves_like 'a repo type' do
- let(:expected_identifier) { "project-#{project.id}" }
let(:expected_id) { project.id.to_s }
+ let(:expected_identifier) { "project-#{expected_id}" }
let(:expected_suffix) { '' }
- let(:expected_repository) { project.repository }
let(:expected_container) { project }
+ let(:expected_repository) { expected_container.repository }
end
it 'knows its type' do
- expect(described_class).not_to be_wiki
- expect(described_class).to be_project
- expect(described_class).not_to be_snippet
+ aggregate_failures do
+ expect(described_class).not_to be_wiki
+ expect(described_class).to be_project
+ expect(described_class).not_to be_snippet
+ end
end
it 'checks if repository path is valid' do
- expect(described_class.valid?(project.repository.full_path)).to be_truthy
- expect(described_class.valid?(project.wiki.repository.full_path)).to be_truthy
+ aggregate_failures do
+ expect(described_class.valid?(project_path)).to be_truthy
+ expect(described_class.valid?(wiki_path)).to be_truthy
+ expect(described_class.valid?(personal_snippet_path)).to be_truthy
+ expect(described_class.valid?(project_snippet_path)).to be_truthy
+ end
end
end
describe Gitlab::GlRepository::WIKI do
it_behaves_like 'a repo type' do
- let(:expected_identifier) { "wiki-#{project.id}" }
let(:expected_id) { project.id.to_s }
+ let(:expected_identifier) { "wiki-#{expected_id}" }
let(:expected_suffix) { '.wiki' }
- let(:expected_repository) { project.wiki.repository }
let(:expected_container) { project }
+ let(:expected_repository) { expected_container.wiki.repository }
end
it 'knows its type' do
- expect(described_class).to be_wiki
- expect(described_class).not_to be_project
- expect(described_class).not_to be_snippet
+ aggregate_failures do
+ expect(described_class).to be_wiki
+ expect(described_class).not_to be_project
+ expect(described_class).not_to be_snippet
+ end
end
it 'checks if repository path is valid' do
- expect(described_class.valid?(project.repository.full_path)).to be_falsey
- expect(described_class.valid?(project.wiki.repository.full_path)).to be_truthy
+ aggregate_failures do
+ expect(described_class.valid?(project_path)).to be_falsey
+ expect(described_class.valid?(wiki_path)).to be_truthy
+ expect(described_class.valid?(personal_snippet_path)).to be_falsey
+ expect(described_class.valid?(project_snippet_path)).to be_falsey
+ end
end
end
@@ -59,9 +75,20 @@ describe Gitlab::GlRepository::RepoType do
end
it 'knows its type' do
- expect(described_class).to be_snippet
- expect(described_class).not_to be_wiki
- expect(described_class).not_to be_project
+ aggregate_failures do
+ expect(described_class).to be_snippet
+ expect(described_class).not_to be_wiki
+ expect(described_class).not_to be_project
+ end
+ end
+
+ it 'checks if repository path is valid' do
+ aggregate_failures do
+ expect(described_class.valid?(project_path)).to be_falsey
+ expect(described_class.valid?(wiki_path)).to be_falsey
+ expect(described_class.valid?(personal_snippet_path)).to be_truthy
+ expect(described_class.valid?(project_snippet_path)).to be_truthy
+ end
end
end
@@ -75,9 +102,20 @@ describe Gitlab::GlRepository::RepoType do
end
it 'knows its type' do
- expect(described_class).to be_snippet
- expect(described_class).not_to be_wiki
- expect(described_class).not_to be_project
+ aggregate_failures do
+ expect(described_class).to be_snippet
+ expect(described_class).not_to be_wiki
+ expect(described_class).not_to be_project
+ end
+ end
+
+ it 'checks if repository path is valid' do
+ aggregate_failures do
+ expect(described_class.valid?(project_path)).to be_falsey
+ expect(described_class.valid?(wiki_path)).to be_falsey
+ expect(described_class.valid?(personal_snippet_path)).to be_truthy
+ expect(described_class.valid?(project_snippet_path)).to be_truthy
+ end
end
end
end
diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb
index 3cfc4c2a132..858f436047e 100644
--- a/spec/lib/gitlab/gl_repository_spec.rb
+++ b/spec/lib/gitlab/gl_repository_spec.rb
@@ -5,13 +5,18 @@ require 'spec_helper'
describe ::Gitlab::GlRepository do
describe '.parse' do
let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:snippet) { create(:personal_snippet) }
it 'parses a project gl_repository' do
- expect(described_class.parse("project-#{project.id}")).to eq([project, Gitlab::GlRepository::PROJECT])
+ expect(described_class.parse("project-#{project.id}")).to eq([project, project, Gitlab::GlRepository::PROJECT])
end
it 'parses a wiki gl_repository' do
- expect(described_class.parse("wiki-#{project.id}")).to eq([project, Gitlab::GlRepository::WIKI])
+ expect(described_class.parse("wiki-#{project.id}")).to eq([project, project, Gitlab::GlRepository::WIKI])
+ end
+
+ it 'parses a snippet gl_repository' do
+ expect(described_class.parse("snippet-#{snippet.id}")).to eq([snippet, nil, Gitlab::GlRepository::SNIPPET])
end
it 'throws an argument error on an invalid gl_repository type' do
diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb
index 3cbcae4cdeb..8dabe5a756b 100644
--- a/spec/lib/gitlab/path_regex_spec.rb
+++ b/spec/lib/gitlab/path_regex_spec.rb
@@ -411,4 +411,37 @@ describe Gitlab::PathRegex do
it { is_expected.not_to match('git lab') }
it { is_expected.not_to match('gitlab.git') }
end
+
+ shared_examples 'invalid snippet routes' do
+ it { is_expected.not_to match('gitlab-org/gitlab/snippets/1.git') }
+ it { is_expected.not_to match('snippets/1.git') }
+ it { is_expected.not_to match('gitlab-org/gitlab/snippets/') }
+ it { is_expected.not_to match('/gitlab-org/gitlab/snippets/1') }
+ it { is_expected.not_to match('gitlab-org/gitlab/snippets/foo') }
+ it { is_expected.not_to match('root/snippets/1') }
+ it { is_expected.not_to match('/snippets/1') }
+ it { is_expected.not_to match('snippets/') }
+ it { is_expected.not_to match('snippets/foo') }
+ end
+
+ describe '.full_snippets_repository_path_regex' do
+ subject { described_class.full_snippets_repository_path_regex }
+
+ it { is_expected.to match('gitlab-org/gitlab/snippets/1') }
+ it { is_expected.to match('snippets/1') }
+
+ it_behaves_like 'invalid snippet routes'
+ end
+
+ describe '.personal_and_project_snippets_path_regex' do
+ subject { %r{\A#{described_class.personal_and_project_snippets_path_regex}\z} }
+
+ it { is_expected.to match('gitlab-org/gitlab/snippets') }
+ it { is_expected.to match('snippets') }
+
+ it { is_expected.not_to match('gitlab-org/gitlab/snippets/1') }
+ it { is_expected.not_to match('snippets/1') }
+
+ it_behaves_like 'invalid snippet routes'
+ end
end
diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb
index 2aeb69db2cb..e72bdc01940 100644
--- a/spec/lib/gitlab/repo_path_spec.rb
+++ b/spec/lib/gitlab/repo_path_spec.rb
@@ -3,60 +3,72 @@
require 'spec_helper'
describe ::Gitlab::RepoPath do
- describe '.parse' do
- let_it_be(:project) { create(:project, :repository) }
+ include Gitlab::Routing
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:personal_snippet) { create(:personal_snippet) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project) }
+ let_it_be(:redirect) { project.route.create_redirect('foo/bar/baz') }
+ describe '.parse' do
context 'a repository storage path' do
- it 'parses a full repository path' do
- expect(described_class.parse(project.repository.full_path)).to eq([project, Gitlab::GlRepository::PROJECT, nil])
+ it 'parses a full repository project path' do
+ expect(described_class.parse(project.repository.full_path)).to eq([project, project, Gitlab::GlRepository::PROJECT, nil])
+ end
+
+ it 'parses a full wiki project path' do
+ expect(described_class.parse(project.wiki.repository.full_path)).to eq([project, project, Gitlab::GlRepository::WIKI, nil])
+ end
+
+ it 'parses a personal snippet repository path' do
+ expect(described_class.parse("snippets/#{personal_snippet.id}")).to eq([personal_snippet, nil, Gitlab::GlRepository::SNIPPET, nil])
end
- it 'parses a full wiki path' do
- expect(described_class.parse(project.wiki.repository.full_path)).to eq([project, Gitlab::GlRepository::WIKI, nil])
+ it 'parses a project snippet repository path' do
+ expect(described_class.parse("#{project.full_path}/snippets/#{project_snippet.id}")).to eq([project_snippet, project, Gitlab::GlRepository::SNIPPET, nil])
end
end
context 'a relative path' do
it 'parses a relative repository path' do
- expect(described_class.parse(project.full_path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, nil])
+ expect(described_class.parse(project.full_path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, nil])
end
it 'parses a relative wiki path' do
- expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, Gitlab::GlRepository::WIKI, nil])
+ expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, project, Gitlab::GlRepository::WIKI, nil])
end
it 'parses a relative path starting with /' do
- expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, nil])
+ expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, nil])
end
context 'of a redirected project' do
let(:redirect) { project.route.create_redirect('foo/bar') }
it 'parses a relative repository path' do
- expect(described_class.parse(redirect.path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
+ expect(described_class.parse(redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
end
it 'parses a relative wiki path' do
- expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, Gitlab::GlRepository::WIKI, 'foo/bar.wiki'])
+ expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, project, Gitlab::GlRepository::WIKI, 'foo/bar.wiki'])
end
it 'parses a relative path starting with /' do
- expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
+ expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
+ end
+
+ it 'parses a redirected project snippet repository path' do
+ expect(described_class.parse(redirect.path + "/snippets/#{project_snippet.id}.git")).to eq([project_snippet, project, Gitlab::GlRepository::SNIPPET, "foo/bar/snippets/#{project_snippet.id}"])
end
end
end
- it "returns the default type for non existent paths" do
- _project, type, _redirected = described_class.parse("path/non-existent.git")
-
- expect(type).to eq(Gitlab::GlRepository.default_type)
+ it 'returns the default type for non existent paths' do
+ expect(described_class.parse('path/non-existent.git')).to eq([nil, nil, Gitlab::GlRepository.default_type, nil])
end
end
describe '.find_project' do
- let(:project) { create(:project) }
- let(:redirect) { project.route.create_redirect('foo/bar/baz') }
-
context 'when finding a project by its canonical path' do
context 'when the cases match' do
it 'returns the project and false' do
@@ -81,4 +93,34 @@ describe ::Gitlab::RepoPath do
end
end
end
+
+ describe '.find_snippet' do
+ it 'extracts path and id from personal snippet route' do
+ expect(described_class.find_snippet("snippets/#{personal_snippet.id}")).to eq([personal_snippet, false])
+ end
+
+ it 'extracts path and id from project snippet route' do
+ expect(described_class.find_snippet("#{project.full_path}/snippets/#{project_snippet.id}")).to eq([project_snippet, false])
+ end
+
+ it 'returns nil for invalid snippet paths' do
+ aggregate_failures do
+ expect(described_class.find_snippet("snippets/#{project_snippet.id}")).to eq([nil, false])
+ expect(described_class.find_snippet("#{project.full_path}/snippets/#{personal_snippet.id}")).to eq([nil, false])
+ expect(described_class.find_snippet('')).to eq([nil, false])
+ end
+ end
+
+ it 'returns nil for snippets not associated with the project' do
+ snippet = create(:project_snippet)
+
+ expect(described_class.find_snippet("#{project.full_path}/snippets/#{snippet.id}")).to eq([nil, false])
+ end
+
+ context 'when finding a project snippet via a redirect' do
+ it 'returns the project and true' do
+ expect(described_class.find_snippet("#{redirect.path}/snippets/#{project_snippet.id}")).to eq([project_snippet, true])
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/repository_cache_spec.rb b/spec/lib/gitlab/repository_cache_spec.rb
index e787288fc51..be31be761ad 100644
--- a/spec/lib/gitlab/repository_cache_spec.rb
+++ b/spec/lib/gitlab/repository_cache_spec.rb
@@ -12,19 +12,44 @@ describe Gitlab::RepositoryCache do
describe '#cache_key' do
subject { cache.cache_key(:foo) }
- it 'includes the namespace' do
- expect(subject).to eq "foo:#{namespace}"
+ shared_examples 'cache_key examples' do
+ it 'includes the namespace' do
+ expect(subject).to eq "foo:#{namespace}"
+ end
+
+ context 'with a given namespace' do
+ let(:extra_namespace) { 'my:data' }
+ let(:cache) do
+ described_class.new(repository, extra_namespace: extra_namespace,
+ backend: backend)
+ end
+
+ it 'includes the full namespace' do
+ expect(subject).to eq "foo:#{namespace}:#{extra_namespace}"
+ end
+ end
end
- context 'with a given namespace' do
- let(:extra_namespace) { 'my:data' }
- let(:cache) do
- described_class.new(repository, extra_namespace: extra_namespace,
- backend: backend)
+ describe 'project repository' do
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { project.repository }
end
+ end
+
+ describe 'personal snippet repository' do
+ let_it_be(:personal_snippet) { create(:personal_snippet) }
+ let(:namespace) { repository.full_path }
+
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { personal_snippet.repository }
+ end
+ end
+
+ describe 'project snippet repository' do
+ let_it_be(:project_snippet) { create(:project_snippet, project: project) }
- it 'includes the full namespace' do
- expect(subject).to eq "foo:#{namespace}:#{extra_namespace}"
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { project_snippet.repository }
end
end
end
diff --git a/spec/lib/gitlab/repository_set_cache_spec.rb b/spec/lib/gitlab/repository_set_cache_spec.rb
index de0f3602346..bcf27b338f6 100644
--- a/spec/lib/gitlab/repository_set_cache_spec.rb
+++ b/spec/lib/gitlab/repository_set_cache_spec.rb
@@ -11,16 +11,41 @@ describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do
describe '#cache_key' do
subject { cache.cache_key(:foo) }
- it 'includes the namespace' do
- is_expected.to eq("foo:#{namespace}:set")
+ shared_examples 'cache_key examples' do
+ it 'includes the namespace' do
+ is_expected.to eq("foo:#{namespace}:set")
+ end
+
+ context 'with a given namespace' do
+ let(:extra_namespace) { 'my:data' }
+ let(:cache) { described_class.new(repository, extra_namespace: extra_namespace) }
+
+ it 'includes the full namespace' do
+ is_expected.to eq("foo:#{namespace}:#{extra_namespace}:set")
+ end
+ end
+ end
+
+ describe 'project repository' do
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { project.repository }
+ end
+ end
+
+ describe 'personal snippet repository' do
+ let_it_be(:personal_snippet) { create(:personal_snippet) }
+ let(:namespace) { repository.full_path }
+
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { personal_snippet.repository }
+ end
end
- context 'with a given namespace' do
- let(:extra_namespace) { 'my:data' }
- let(:cache) { described_class.new(repository, extra_namespace: extra_namespace) }
+ describe 'project snippet repository' do
+ let_it_be(:project_snippet) { create(:project_snippet, project: project) }
- it 'includes the full namespace' do
- is_expected.to eq("foo:#{namespace}:#{extra_namespace}:set")
+ it_behaves_like 'cache_key examples' do
+ let(:repository) { project_snippet.repository }
end
end
end
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index d46c9747845..71a18e2fbec 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -211,20 +211,21 @@ describe Milestone, 'Milestoneish' do
end
end
- describe '#complete?' do
+ describe '#complete?', :use_clean_rails_memory_store_caching do
it 'returns false when has items opened' do
expect(milestone.complete?(non_member)).to eq false
end
it 'returns true when all items are closed' do
issue.close
- merge_request.close
+ security_issue_1.close
+ security_issue_2.close
expect(milestone.complete?(non_member)).to eq true
end
end
- describe '#percent_complete' do
+ describe '#percent_complete', :use_clean_rails_memory_store_caching do
context 'division by zero' do
let(:new_milestone) { build_stubbed(:milestone) }
@@ -233,34 +234,58 @@ describe Milestone, 'Milestoneish' do
end
describe '#count_issues_by_state' do
- it 'does not count confidential issues for non project members' do
- expect(milestone.closed_issues_count(non_member)).to eq 2
- expect(milestone.total_issues_count(non_member)).to eq 3
+ describe '#total_issues_count', :use_clean_rails_memory_store_caching do
+ it 'counts all issues including confidential' do
+ expect(milestone.total_issues_count(guest)).to eq 9
+ end
end
- it 'does not count confidential issues for project members with guest role' do
- expect(milestone.closed_issues_count(guest)).to eq 2
- expect(milestone.total_issues_count(guest)).to eq 3
+ describe '#opened_issues_count', :use_clean_rails_memory_store_caching do
+ it 'counts all open issues including confidential' do
+ expect(milestone.opened_issues_count(guest)).to eq 3
+ end
end
- it 'counts confidential issues for author' do
- expect(milestone.closed_issues_count(author)).to eq 4
- expect(milestone.total_issues_count(author)).to eq 6
+ describe '#closed_issues_count', :use_clean_rails_memory_store_caching do
+ it 'counts all closed issues including confidential' do
+ expect(milestone.closed_issues_count(guest)).to eq 6
+ end
end
- it 'counts confidential issues for assignee' do
- expect(milestone.closed_issues_count(assignee)).to eq 4
- expect(milestone.total_issues_count(assignee)).to eq 6
- end
+ context 'when cached_milestone_issue_counters are disabled' do
+ before do
+ stub_feature_flags(cached_milestone_issue_counters: false)
+ end
- it 'counts confidential issues for project members' do
- expect(milestone.closed_issues_count(member)).to eq 6
- expect(milestone.total_issues_count(member)).to eq 9
- end
+ it 'does not count confidential issues for non project members' do
+ expect(milestone.closed_issues_count(non_member)).to eq 2
+ expect(milestone.total_issues_count(non_member)).to eq 3
+ end
- it 'counts confidential issues for admin' do
- expect(milestone.closed_issues_count(admin)).to eq 6
- expect(milestone.total_issues_count(admin)).to eq 9
+ it 'does not count confidential issues for project members with guest role' do
+ expect(milestone.closed_issues_count(guest)).to eq 2
+ expect(milestone.total_issues_count(guest)).to eq 3
+ end
+
+ it 'counts confidential issues for author' do
+ expect(milestone.closed_issues_count(author)).to eq 4
+ expect(milestone.total_issues_count(author)).to eq 6
+ end
+
+ it 'counts confidential issues for assignee' do
+ expect(milestone.closed_issues_count(assignee)).to eq 4
+ expect(milestone.total_issues_count(assignee)).to eq 6
+ end
+
+ it 'counts confidential issues for project members' do
+ expect(milestone.closed_issues_count(member)).to eq 6
+ expect(milestone.total_issues_count(member)).to eq 9
+ end
+
+ it 'counts confidential issues for admin' do
+ expect(milestone.closed_issues_count(admin)).to eq 6
+ expect(milestone.total_issues_count(admin)).to eq 9
+ end
end
end
diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb
index 17b25d5c7cc..9bfbbe0daab 100644
--- a/spec/requests/api/broadcast_messages_spec.rb
+++ b/spec/requests/api/broadcast_messages_spec.rb
@@ -17,7 +17,7 @@ describe API::BroadcastMessages do
expect(response).to include_pagination_headers
expect(json_response).to be_kind_of(Array)
expect(json_response.first.keys)
- .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type))
+ .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
end
end
@@ -28,7 +28,7 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq message.id
expect(json_response.keys)
- .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type))
+ .to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
end
end
@@ -111,6 +111,15 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request)
end
+
+ it 'accepts an active dismissable value ' do
+ attrs = { message: 'new message', dismissable: true }
+
+ post api('/broadcast_messages', admin), params: attrs
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['dismissable']).to eq true
+ end
end
end
@@ -187,6 +196,15 @@ describe API::BroadcastMessages do
expect(response).to have_gitlab_http_status(:bad_request)
end
+
+ it 'accepts a new dismissable value ' do
+ attrs = { message: 'new message', dismissable: true }
+
+ put api("/broadcast_messages/#{message.id}", admin), params: attrs
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['dismissable']).to eq true
+ end
end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index fe0a3ffebd3..1f20a088e4f 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -10,6 +10,9 @@ describe API::Internal::Base do
let(:gl_repository) { "project-#{project.id}" }
let(:reference_counter) { double('ReferenceCounter') }
+ let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
+ let_it_be(:project_snippet) { create(:project_snippet, :repository, author: user, project: project) }
+
describe "GET /internal/check" do
it do
expect_any_instance_of(Redis).to receive(:ping).and_return('PONG')
@@ -312,6 +315,54 @@ describe API::Internal::Base do
end
end
+ context 'git push with personal snippet' do
+ it 'responds with success' do
+ push(key, personal_snippet)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["gl_project_path"]).to eq(personal_snippet.repository.full_path)
+ expect(json_response["gl_repository"]).to eq("snippet-#{personal_snippet.id}")
+ expect(user.reload.last_activity_on).to be_nil
+ end
+ end
+
+ context 'git pull with personal snippet' do
+ it 'responds with success' do
+ pull(key, personal_snippet)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["gl_project_path"]).to eq(personal_snippet.repository.full_path)
+ expect(json_response["gl_repository"]).to eq("snippet-#{personal_snippet.id}")
+ expect(user.reload.last_activity_on).to eql(Date.today)
+ end
+ end
+
+ context 'git push with project snippet' do
+ it 'responds with success' do
+ push(key, project_snippet)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["gl_project_path"]).to eq(project_snippet.repository.full_path)
+ expect(json_response["gl_repository"]).to eq("snippet-#{project_snippet.id}")
+ expect(user.reload.last_activity_on).to be_nil
+ end
+ end
+
+ context 'git pull with project snippet' do
+ it 'responds with success' do
+ pull(key, project_snippet)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response["status"]).to be_truthy
+ expect(json_response["gl_project_path"]).to eq(project_snippet.repository.full_path)
+ expect(json_response["gl_repository"]).to eq("snippet-#{project_snippet.id}")
+ expect(user.reload.last_activity_on).to eql(Date.today)
+ end
+ end
+
context "git pull" do
before do
allow(Feature).to receive(:persisted_names).and_return(%w[gitaly_mep_mep])
@@ -393,10 +444,28 @@ describe API::Internal::Base do
end
end
- it_behaves_like 'storing arguments in the application context' do
- let(:expected_params) { { user: key.user.username, project: project.full_path } }
+ context 'with Project' do
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: key.user.username, project: project.full_path } }
+
+ subject { push(key, project) }
+ end
+ end
+
+ context 'with PersonalSnippet' do
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: key.user.username } }
+
+ subject { push(key, personal_snippet) }
+ end
+ end
- subject { push(key, project) }
+ context 'with ProjectSnippet' do
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: key.user.username, project: project_snippet.project.full_path } }
+
+ subject { push(key, project_snippet) }
+ end
end
end
@@ -450,7 +519,7 @@ describe API::Internal::Base do
{
authentication_abilities: [:read_project, :download_code, :push_code],
namespace_path: project.namespace.path,
- project_path: project.path,
+ repository_path: project.path,
redirected_path: nil
}
).and_return(access_checker)
@@ -835,22 +904,60 @@ describe API::Internal::Base do
allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(user)
end
- it 'executes PostReceiveService' do
- message = <<~MESSAGE.strip
- To create a merge request for #{branch_name}, visit:
- http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
- MESSAGE
+ context 'with Project' do
+ it 'executes PostReceiveService' do
+ message = <<~MESSAGE.strip
+ To create a merge request for #{branch_name}, visit:
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
+ MESSAGE
+
+ subject
- subject
+ expect(json_response).to eq({
+ 'messages' => [{ 'message' => message, 'type' => 'basic' }],
+ 'reference_counter_decreased' => true
+ })
+ end
- expect(json_response).to eq({
- 'messages' => [{ 'message' => message, 'type' => 'basic' }],
- 'reference_counter_decreased' => true
- })
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: user.username, project: project.full_path } }
+ end
end
- it_behaves_like 'storing arguments in the application context' do
- let(:expected_params) { { user: user.username, project: project.full_path } }
+ context 'with PersonalSnippet' do
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+
+ it 'executes PostReceiveService' do
+ subject
+
+ expect(json_response).to eq({
+ 'messages' => [],
+ 'reference_counter_decreased' => true
+ })
+ end
+
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: key.user.username } }
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+ end
+ end
+
+ context 'with ProjectSnippet' do
+ let(:gl_repository) { "snippet-#{project_snippet.id}" }
+
+ it 'executes PostReceiveService' do
+ subject
+
+ expect(json_response).to eq({
+ 'messages' => [],
+ 'reference_counter_decreased' => true
+ })
+ end
+
+ it_behaves_like 'storing arguments in the application context' do
+ let(:expected_params) { { user: key.user.username, project: project_snippet.project.full_path } }
+ let(:gl_repository) { "snippet-#{project_snippet.id}" }
+ end
end
context 'with an orphaned write deploy key' do
@@ -866,16 +973,32 @@ describe API::Internal::Base do
end
context 'when project is nil' do
- let(:gl_repository) { 'project-foo' }
+ context 'with Project' do
+ let(:gl_repository) { 'project-foo' }
- it 'does not try to notify that project moved' do
- allow(Gitlab::GlRepository).to receive(:parse).and_return([nil, Gitlab::GlRepository::PROJECT])
+ it 'does not try to notify that project moved' do
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([nil, nil, Gitlab::GlRepository::PROJECT])
- expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message)
+ expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message)
- subject
+ subject
- expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with PersonalSnippet' do
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+
+ it 'does not try to notify that project moved' do
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([personal_snippet, nil, Gitlab::GlRepository::PROJECT])
+
+ expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
end
end
@@ -896,24 +1019,37 @@ describe API::Internal::Base do
end
end
- def gl_repository_for(project_or_wiki)
- case project_or_wiki
+ def gl_repository_for(container)
+ case container
when ProjectWiki
- Gitlab::GlRepository::WIKI.identifier_for_container(project_or_wiki.project)
+ Gitlab::GlRepository::WIKI.identifier_for_container(container.project)
when Project
- Gitlab::GlRepository::PROJECT.identifier_for_container(project_or_wiki)
+ Gitlab::GlRepository::PROJECT.identifier_for_container(container)
+ when Snippet
+ Gitlab::GlRepository::SNIPPET.identifier_for_container(container)
else
nil
end
end
- def pull(key, project, protocol = 'ssh')
+ def full_path_for(container)
+ case container
+ when PersonalSnippet
+ "snippets/#{container.id}"
+ when ProjectSnippet
+ "#{container.project.full_path}/snippets/#{container.id}"
+ else
+ container.full_path
+ end
+ end
+
+ def pull(key, container, protocol = 'ssh')
post(
api("/internal/allowed"),
params: {
key_id: key.id,
- project: project.full_path,
- gl_repository: gl_repository_for(project),
+ project: full_path_for(container),
+ gl_repository: gl_repository_for(container),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: protocol
@@ -921,12 +1057,12 @@ describe API::Internal::Base do
)
end
- def push(key, project, protocol = 'ssh', env: nil)
+ def push(key, container, protocol = 'ssh', env: nil)
params = {
changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
key_id: key.id,
- project: project.full_path,
- gl_repository: gl_repository_for(project),
+ project: full_path_for(container),
+ gl_repository: gl_repository_for(container),
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
@@ -939,14 +1075,14 @@ describe API::Internal::Base do
)
end
- def archive(key, project)
+ def archive(key, container)
post(
api("/internal/allowed"),
params: {
ref: 'master',
key_id: key.id,
- project: project.full_path,
- gl_repository: gl_repository_for(project),
+ project: full_path_for(container),
+ gl_repository: gl_repository_for(container),
action: 'git-upload-archive',
secret_token: secret_token,
protocol: 'ssh'
diff --git a/spec/requests/groups/milestones_controller_spec.rb b/spec/requests/groups/milestones_controller_spec.rb
index 4d15aa43cd2..1c6743dc678 100644
--- a/spec/requests/groups/milestones_controller_spec.rb
+++ b/spec/requests/groups/milestones_controller_spec.rb
@@ -12,23 +12,45 @@ describe Groups::MilestonesController do
end
let!(:private_milestone) { create(:milestone, project: public_project_with_private_issues_and_mrs, title: 'project milestone') }
- it 'avoids N+1 database queries' do
- public_project = create(:project, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
- create(:milestone, project: public_project)
+ describe 'GET #index' do
+ it 'avoids N+1 database queries' do
+ public_project = create(:project, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
+ create(:milestone, project: public_project)
- control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { get "/groups/#{public_group.to_param}/-/milestones.json" }.count
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { get group_milestones_path(public_group, format: :json) }.count
- projects = create_list(:project, 2, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
- projects.each do |project|
- create(:milestone, project: project)
+ projects = create_list(:project, 2, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
+ projects.each do |project|
+ create(:milestone, project: project)
+ end
+
+ expect { get group_milestones_path(public_group, format: :json) }.not_to exceed_all_query_limit(control_count)
+ expect(response).to have_gitlab_http_status(:ok)
+ milestones = json_response
+
+ expect(milestones.count).to eq(3)
+ expect(milestones.map {|x| x['title']}).not_to include(private_milestone.title)
end
+ end
- expect { get "/groups/#{public_group.to_param}/-/milestones.json" }.not_to exceed_all_query_limit(control_count)
- expect(response).to have_gitlab_http_status(:ok)
- milestones = json_response
+ describe 'GET #show' do
+ let(:milestone) { create(:milestone, group: public_group) }
+ let(:show_path) { group_milestone_path(public_group, milestone) }
- expect(milestones.count).to eq(3)
- expect(milestones.map {|x| x['title']}).not_to include(private_milestone.title)
+ it 'avoids N+1 database queries' do
+ projects = create_list(:project, 3, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
+ projects.each do |project|
+ create_list(:issue, 2, milestone: milestone, project: project)
+ end
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) { get show_path }
+
+ projects = create_list(:project, 3, :public, :merge_requests_enabled, :issues_enabled, group: public_group)
+ projects.each do |project|
+ create_list(:issue, 2, milestone: milestone, project: project)
+ end
+
+ expect { get show_path }.not_to exceed_all_query_limit(control)
+ end
end
end
end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index eb3b1d7c754..141567045a2 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -178,35 +178,55 @@ describe Issues::CloseService do
end
context "valid params" do
- before do
+ def close_issue
perform_enqueued_jobs do
described_class.new(project, user).close_issue(issue)
end
end
it 'closes the issue' do
+ close_issue
+
expect(issue).to be_valid
expect(issue).to be_closed
end
it 'records closed user' do
+ close_issue
+
expect(issue.closed_by_id).to be(user.id)
end
it 'sends email to user2 about assign of new issue', :sidekiq_might_not_need_inline do
+ close_issue
+
email = ActionMailer::Base.deliveries.last
expect(email.to.first).to eq(user2.email)
expect(email.subject).to include(issue.title)
end
it 'creates system note about issue reassign' do
+ close_issue
+
note = issue.notes.last
expect(note.note).to include "closed"
end
it 'marks todos as done' do
+ close_issue
+
expect(todo.reload).to be_done
end
+
+ it 'deletes milestone issue counters cache' do
+ issue.update(milestone: create(:milestone, project: project))
+
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, issue.milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ close_issue
+ end
end
context 'when issue is not confidential' do
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index c9701e5d194..09fff389cec 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -196,6 +196,14 @@ describe Issues::CreateService do
end
end
end
+
+ it 'deletes milestone issues count cache' do
+ expect_next_instance_of(Milestones::IssuesCountService, milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ issue
+ end
end
context 'issue create service' do
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index f04029e64aa..ca878ee947a 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -43,6 +43,16 @@ describe Issues::ReopenService do
.to change { project.open_issues_count }.from(0).to(1)
end
+ it 'deletes milestone issue counters cache' do
+ issue.update(milestone: create(:milestone, project: project))
+
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, issue.milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ described_class.new(project, user).execute(issue)
+ end
+
context 'when issue is not confidential' do
it 'executes issue hooks' do
expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 5da77dd914c..69e47d890a5 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -412,9 +412,24 @@ describe Issues::UpdateService, :mailer do
should_email(subscriber)
should_not_email(non_subscriber)
end
+
+ it 'clears milestone issue counters cache' do
+ issue.milestone = create(:milestone, project: project)
+
+ issue.save
+
+ expect_next_instance_of(Milestones::IssuesCountService, issue.milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, issue.milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ update_issue(milestone_id: "")
+ end
end
- context 'when the milestone is changed' do
+ context 'when the milestone is assigned' do
before do
stub_feature_flags(track_resource_milestone_change_events: false)
end
@@ -444,6 +459,43 @@ describe Issues::UpdateService, :mailer do
should_email(subscriber)
should_not_email(non_subscriber)
end
+
+ it 'deletes issue counters cache for the milestone' do
+ milestone = create(:milestone, project: project)
+
+ expect_next_instance_of(Milestones::IssuesCountService, milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ update_issue(milestone: milestone)
+ end
+ end
+
+ context 'when the milestone is changed' do
+ it 'deletes issue counters cache for both milestones' do
+ old_milestone = create(:milestone, project: project)
+ new_milestone = create(:milestone, project: project)
+
+ issue.update!(milestone: old_milestone)
+
+ expect_next_instance_of(Milestones::IssuesCountService, old_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, old_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::IssuesCountService, new_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, new_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ update_issue(milestone: new_milestone)
+ end
end
context 'when the labels change' do
diff --git a/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
index 744693dad15..2f03d18cd1f 100644
--- a/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
@@ -35,12 +35,18 @@ describe Metrics::Dashboard::CustomMetricEmbedService do
it { is_expected.to be_truthy }
- context 'not embedded' do
+ context 'missing embedded' do
let(:params) { valid_params.except(:embedded) }
it { is_expected.to be_falsey }
end
+ context 'not embedded' do
+ let(:params) { valid_params.merge(embedded: 'false') }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'non-system dashboard' do
let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
diff --git a/spec/services/metrics/dashboard/default_embed_service_spec.rb b/spec/services/metrics/dashboard/default_embed_service_spec.rb
index 1b88276368c..8e32316433d 100644
--- a/spec/services/metrics/dashboard/default_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/default_embed_service_spec.rb
@@ -27,7 +27,7 @@ describe Metrics::Dashboard::DefaultEmbedService, :use_clean_rails_memory_store_
end
context 'not embedded' do
- let(:params) { { embedded: false } }
+ let(:params) { { embedded: 'false' } }
it { is_expected.to be_falsey }
end
diff --git a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
index c1ce9818f21..ee75284b4ce 100644
--- a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
@@ -35,12 +35,18 @@ describe Metrics::Dashboard::DynamicEmbedService, :use_clean_rails_memory_store_
it { is_expected.to be_truthy }
- context 'not embedded' do
+ context 'missing embedded' do
let(:params) { valid_params.except(:embedded) }
it { is_expected.to be_falsey }
end
+ context 'not embedded' do
+ let(:params) { valid_params.merge(embedded: 'false') }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'undefined dashboard' do
let(:params) { valid_params.except(:dashboard_path) }
diff --git a/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
index a772b911d8a..034d6aba5d6 100644
--- a/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
@@ -28,12 +28,18 @@ describe Metrics::Dashboard::GrafanaMetricEmbedService do
it { is_expected.to be_truthy }
- context 'not embedded' do
+ context 'missing embedded' do
let(:params) { valid_params.except(:embedded) }
it { is_expected.to be_falsey }
end
+ context 'not embedded' do
+ let(:params) { valid_params.merge(embedded: 'false') }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'undefined grafana_url' do
let(:params) { valid_params.except(:grafana_url) }
diff --git a/spec/services/milestones/closed_issues_count_service_spec.rb b/spec/services/milestones/closed_issues_count_service_spec.rb
new file mode 100644
index 00000000000..b86eede2e22
--- /dev/null
+++ b/spec/services/milestones/closed_issues_count_service_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Milestones::ClosedIssuesCountService, :use_clean_rails_memory_store_caching do
+ let(:project) { create(:project) }
+ let(:milestone) { create(:milestone, project: project) }
+
+ before do
+ create(:issue, milestone: milestone, project: project)
+ create(:issue, :confidential, milestone: milestone, project: project)
+
+ create(:issue, :closed, milestone: milestone, project: project)
+ create(:issue, :closed, :confidential, milestone: milestone, project: project)
+ end
+
+ subject { described_class.new(milestone) }
+
+ it_behaves_like 'a counter caching service'
+
+ it 'counts closed issues including confidential' do
+ expect(subject.count).to eq(2)
+ end
+end
diff --git a/spec/services/milestones/issues_count_service_spec.rb b/spec/services/milestones/issues_count_service_spec.rb
new file mode 100644
index 00000000000..22aea884424
--- /dev/null
+++ b/spec/services/milestones/issues_count_service_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Milestones::IssuesCountService, :use_clean_rails_memory_store_caching do
+ let(:project) { create(:project) }
+ let(:milestone) { create(:milestone, project: project) }
+
+ before do
+ create(:issue, milestone: milestone, project: project)
+ create(:issue, :confidential, milestone: milestone, project: project)
+
+ create(:issue, :closed, milestone: milestone, project: project)
+ create(:issue, :closed, milestone: milestone, project: project)
+ end
+
+ subject { described_class.new(milestone) }
+
+ it_behaves_like 'a counter caching service'
+
+ it 'counts all issues including confidential' do
+ expect(subject.count).to eq(4)
+ end
+end
diff --git a/spec/services/milestones/transfer_service_spec.rb b/spec/services/milestones/transfer_service_spec.rb
index 711969ce504..9b087b07cea 100644
--- a/spec/services/milestones/transfer_service_spec.rb
+++ b/spec/services/milestones/transfer_service_spec.rb
@@ -40,6 +40,25 @@ describe Milestones::TransferService do
expect(new_milestone.project_milestone?).to be_truthy
end
+ it 'deletes milestone issue counters cache for both milestones' do
+ new_milestone = create(:milestone, project: project, title: group_milestone.title)
+
+ expect_next_instance_of(Milestones::IssuesCountService, group_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, group_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::IssuesCountService, new_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+ expect_next_instance_of(Milestones::ClosedIssuesCountService, new_milestone) do |service|
+ expect(service).to receive(:delete_cache).and_call_original
+ end
+
+ service.execute
+ end
+
it 'does not apply new project milestone to issues with project milestone' do
service.execute
diff --git a/spec/services/post_receive_service_spec.rb b/spec/services/post_receive_service_spec.rb
index 64b4a1125e8..f3631ff922f 100644
--- a/spec/services/post_receive_service_spec.rb
+++ b/spec/services/post_receive_service_spec.rb
@@ -5,8 +5,10 @@ require 'spec_helper'
describe PostReceiveService do
include Gitlab::Routing
- let_it_be(:project) { create(:project, :repository, :wiki_repo) }
let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) }
+ let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: user) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
let(:identifier) { 'key-123' }
let(:gl_repository) { "project-#{project.id}" }
@@ -14,6 +16,7 @@ describe PostReceiveService do
let(:secret_token) { Gitlab::Shell.secret_token }
let(:reference_counter) { double('ReferenceCounter') }
let(:push_options) { ['ci.skip', 'another push option'] }
+ let(:repository) { project.repository }
let(:changes) do
"#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{branch_name}"
@@ -29,104 +32,173 @@ describe PostReceiveService do
}
end
- let(:response) { PostReceiveService.new(user, project, params).execute }
+ let(:service) { described_class.new(user, repository, project, params) }
+ let(:response) { service.execute }
subject { response.messages.as_json }
- it 'enqueues a PostReceive worker job' do
- expect(PostReceive).to receive(:perform_async)
- .with(gl_repository, identifier, changes, { ci: { skip: true } })
+ context 'when project is nil' do
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+ let(:project) { nil }
+ let(:repository) { personal_snippet.repository }
- subject
+ it 'does not return error' do
+ expect(subject).to be_empty
+ end
end
- it 'decreases the reference counter and returns the result' do
- expect(Gitlab::ReferenceCounter).to receive(:new).with(gl_repository)
- .and_return(reference_counter)
- expect(reference_counter).to receive(:decrease).and_return(true)
+ context 'when repository is nil' do
+ let(:repository) { nil }
- expect(response.reference_counter_decreased).to be(true)
+ it 'does not return error' do
+ expect(subject).to be_empty
+ end
end
- it 'returns link to create new merge request' do
- message = <<~MESSAGE.strip
- To create a merge request for #{branch_name}, visit:
- http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
- MESSAGE
+ context 'when both repository and project are nil' do
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+ let(:project) { nil }
+ let(:repository) { nil }
- expect(subject).to include(build_basic_message(message))
+ it 'does not return error' do
+ expect(subject).to be_empty
+ end
end
- it 'returns the link to an existing merge request when it exists' do
- merge_request = create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
- message = <<~MESSAGE.strip
- View merge request for feature:
- #{project_merge_request_url(project, merge_request)}
- MESSAGE
+ shared_examples 'post_receive_service actions' do
+ it 'enqueues a PostReceive worker job' do
+ expect(PostReceive).to receive(:perform_async)
+ .with(gl_repository, identifier, changes, { ci: { skip: true } })
- expect(subject).to include(build_basic_message(message))
- end
+ subject
+ end
- context 'when printing_merge_request_link_enabled is false' do
- let(:project) { create(:project, printing_merge_request_link_enabled: false) }
+ it 'decreases the reference counter and returns the result' do
+ expect(Gitlab::ReferenceCounter).to receive(:new).with(gl_repository)
+ .and_return(reference_counter)
+ expect(reference_counter).to receive(:decrease).and_return(true)
- it 'returns no merge request messages' do
- expect(subject).to be_blank
+ expect(response.reference_counter_decreased).to be(true)
end
end
- it 'does not invoke MergeRequests::PushOptionsHandlerService' do
- expect(MergeRequests::PushOptionsHandlerService).not_to receive(:new)
+ context 'with Project' do
+ it_behaves_like 'post_receive_service actions'
- subject
- end
+ it 'returns link to create new merge request' do
+ message = <<~MESSAGE.strip
+ To create a merge request for #{branch_name}, visit:
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
+ MESSAGE
- context 'when there are merge_request push options' do
- let(:params) { super().merge(push_options: ['merge_request.create']) }
+ expect(subject).to include(build_basic_message(message))
+ end
+
+ it 'returns the link to an existing merge request when it exists' do
+ merge_request = create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
+ message = <<~MESSAGE.strip
+ View merge request for feature:
+ #{project_merge_request_url(project, merge_request)}
+ MESSAGE
- before do
- project.add_developer(user)
+ expect(subject).to include(build_basic_message(message))
end
- it 'invokes MergeRequests::PushOptionsHandlerService' do
- expect(MergeRequests::PushOptionsHandlerService).to receive(:new).and_call_original
+ context 'when printing_merge_request_link_enabled is false' do
+ let(:project) { create(:project, printing_merge_request_link_enabled: false) }
- subject
+ it 'returns no merge request messages' do
+ expect(subject).to be_blank
+ end
end
- it 'creates a new merge request' do
- expect { Sidekiq::Testing.fake! { subject } }.to change(MergeRequest, :count).by(1)
+ it 'does not invoke MergeRequests::PushOptionsHandlerService' do
+ expect(MergeRequests::PushOptionsHandlerService).not_to receive(:new)
+
+ subject
end
- it 'links to the newly created merge request' do
- message = <<~MESSAGE.strip
- View merge request for #{branch_name}:
- http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/1
- MESSAGE
+ context 'when there are merge_request push options' do
+ let(:params) { super().merge(push_options: ['merge_request.create']) }
- expect(subject).to include(build_basic_message(message))
+ before do
+ project.add_developer(user)
+ end
+
+ it 'invokes MergeRequests::PushOptionsHandlerService' do
+ expect(MergeRequests::PushOptionsHandlerService).to receive(:new).and_call_original
+
+ subject
+ end
+
+ it 'creates a new merge request' do
+ expect { Sidekiq::Testing.fake! { subject } }.to change(MergeRequest, :count).by(1)
+ end
+
+ it 'links to the newly created merge request' do
+ message = <<~MESSAGE.strip
+ View merge request for #{branch_name}:
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/1
+ MESSAGE
+
+ expect(subject).to include(build_basic_message(message))
+ end
+
+ it 'adds errors on the service instance to warnings' do
+ expect_any_instance_of(
+ MergeRequests::PushOptionsHandlerService
+ ).to receive(:errors).at_least(:once).and_return(['my error'])
+
+ message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+
+ expect(subject).to include(build_alert_message(message))
+ end
+
+ it 'adds ActiveRecord errors on invalid MergeRequest records to warnings' do
+ invalid_merge_request = MergeRequest.new
+ invalid_merge_request.errors.add(:base, 'my error')
+ message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+
+ expect_any_instance_of(
+ MergeRequests::CreateService
+ ).to receive(:execute).and_return(invalid_merge_request)
+
+ expect(subject).to include(build_alert_message(message))
+ end
end
+ end
+
+ context 'with PersonalSnippet' do
+ let(:gl_repository) { "snippet-#{personal_snippet.id}" }
+ let(:repository) { personal_snippet.repository }
- it 'adds errors on the service instance to warnings' do
- expect_any_instance_of(
- MergeRequests::PushOptionsHandlerService
- ).to receive(:errors).at_least(:once).and_return(['my error'])
+ it_behaves_like 'post_receive_service actions'
- message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+ it 'does not return link to create new merge request' do
+ expect(subject).to be_empty
+ end
+
+ it 'does not return the link to an existing merge request when it exists' do
+ create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
- expect(subject).to include(build_alert_message(message))
+ expect(subject).to be_empty
end
+ end
- it 'adds ActiveRecord errors on invalid MergeRequest records to warnings' do
- invalid_merge_request = MergeRequest.new
- invalid_merge_request.errors.add(:base, 'my error')
- message = "WARNINGS:\nError encountered with push options 'merge_request.create': my error"
+ context 'with ProjectSnippet' do
+ let(:gl_repository) { "snippet-#{project_snippet.id}" }
+ let(:repository) { project_snippet.repository }
- expect_any_instance_of(
- MergeRequests::CreateService
- ).to receive(:execute).and_return(invalid_merge_request)
+ it_behaves_like 'post_receive_service actions'
- expect(subject).to include(build_alert_message(message))
+ it 'does not return link to create new merge request' do
+ expect(subject).to be_empty
+ end
+
+ it 'does not return the link to an existing merge request when it exists' do
+ create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
+
+ expect(subject).to be_empty
end
end
@@ -178,6 +250,50 @@ describe PostReceiveService do
end
end
+ describe '#process_mr_push_options' do
+ context 'when repository belongs to a snippet' do
+ context 'with PersonalSnippet' do
+ let(:repository) { personal_snippet.repository }
+
+ it 'returns an error message' do
+ result = service.process_mr_push_options(push_options, changes)
+
+ expect(result).to match('Push options are only supported for projects')
+ end
+ end
+
+ context 'with ProjectSnippet' do
+ let(:repository) { project_snippet.repository }
+
+ it 'returns an error message' do
+ result = service.process_mr_push_options(push_options, changes)
+
+ expect(result).to match('Push options are only supported for projects')
+ end
+ end
+ end
+ end
+
+ describe '#merge_request_urls' do
+ context 'when repository belongs to a snippet' do
+ context 'with PersonalSnippet' do
+ let(:repository) { personal_snippet.repository }
+
+ it 'returns an empty array' do
+ expect(service.merge_request_urls).to be_empty
+ end
+ end
+
+ context 'with ProjectSnippet' do
+ let(:repository) { project_snippet.repository }
+
+ it 'returns an empty array' do
+ expect(service.merge_request_urls).to be_empty
+ end
+ end
+ end
+ end
+
def build_alert_message(message)
{ 'type' => 'alert', 'message' => message }
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index e58919c8688..5e15ade492b 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -14,6 +14,14 @@ describe 'gitlab:app namespace rake task' do
tars_glob.first
end
+ def backup_files
+ %w(backup_information.yml artifacts.tar.gz builds.tar.gz lfs.tar.gz pages.tar.gz)
+ end
+
+ def backup_directories
+ %w(db repositories)
+ end
+
before(:all) do
Rake.application.rake_require 'tasks/gitlab/helpers'
Rake.application.rake_require 'tasks/gitlab/backup'
@@ -28,12 +36,16 @@ describe 'gitlab:app namespace rake task' do
before do
stub_env('force', 'yes')
FileUtils.rm(tars_glob, force: true)
+ FileUtils.rm(backup_files, force: true)
+ FileUtils.rm_rf(backup_directories, secure: true)
reenable_backup_sub_tasks
stub_container_registry_config(enabled: enable_registry)
end
after do
FileUtils.rm(tars_glob, force: true)
+ FileUtils.rm(backup_files, force: true)
+ FileUtils.rm_rf(backup_directories, secure: true)
end
def run_rake_task(task_name)
@@ -62,15 +74,6 @@ describe 'gitlab:app namespace rake task' do
let(:gitlab_version) { Gitlab::VERSION }
- it 'fails on mismatch' do
- allow(YAML).to receive(:load_file)
- .and_return({ gitlab_version: "not #{gitlab_version}" })
-
- expect do
- expect { run_rake_task('gitlab:backup:restore') }.to output.to_stdout
- end.to raise_error(SystemExit)
- end
-
context 'restore with matching gitlab version' do
before do
allow(YAML).to receive(:load_file)
@@ -241,7 +244,7 @@ describe 'gitlab:app namespace rake task' do
)
expect(exit_status).to eq(0)
- expect(tar_contents).to match('db/')
+ expect(tar_contents).to match('db')
expect(tar_contents).to match('uploads.tar.gz')
expect(tar_contents).to match('repositories/')
expect(tar_contents).to match('builds.tar.gz')
@@ -379,6 +382,50 @@ describe 'gitlab:app namespace rake task' do
end
end
+ describe 'skipping tar archive creation' do
+ before do
+ stub_env('SKIP', 'tar')
+ end
+
+ it 'created files with backup content and no tar archive' do
+ expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout
+
+ dir_contents = Dir.children(Gitlab.config.backup.path)
+
+ expect(dir_contents).to contain_exactly(
+ 'backup_information.yml',
+ 'db',
+ 'uploads.tar.gz',
+ 'builds.tar.gz',
+ 'artifacts.tar.gz',
+ 'lfs.tar.gz',
+ 'pages.tar.gz',
+ 'registry.tar.gz',
+ 'repositories',
+ 'tmp'
+ )
+ end
+
+ it 'those component files can be restored from' do
+ expect { run_rake_task("gitlab:backup:create") }.to output.to_stdout
+
+ allow(Rake::Task['gitlab:shell:setup'])
+ .to receive(:invoke).and_return(true)
+
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:db:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:repo:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:uploads:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:builds:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:pages:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:lfs:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:registry:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:shell:setup']).to receive :invoke
+ expect { run_rake_task("gitlab:backup:restore") }.to output.to_stdout
+ end
+ end
+
describe "Human Readable Backup Name" do
it 'name has human readable time' do
expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout
diff --git a/spec/views/shared/milestones/_issuable.html.haml_spec.rb b/spec/views/shared/milestones/_issuable.html.haml_spec.rb
index 3c2b7c6305a..6e81fec79d4 100644
--- a/spec/views/shared/milestones/_issuable.html.haml_spec.rb
+++ b/spec/views/shared/milestones/_issuable.html.haml_spec.rb
@@ -3,19 +3,38 @@
require 'spec_helper'
describe 'shared/milestones/_issuable.html.haml' do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
- let(:milestone) { create(:milestone, project: project) }
- let(:issuable) { create(:issue, project: project, assignees: [user]) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:milestone) { create(:milestone, project: project) }
before do
assign(:project, project)
assign(:milestone, milestone)
end
- it 'avatar links to issues page' do
- render 'shared/milestones/issuable', issuable: issuable, show_project_name: true
+ subject(:rendered) { render 'shared/milestones/issuable', issuable: issuable, show_project_name: true }
- expect(rendered).to have_css("a[href='#{project_issues_path(project, milestone_title: milestone.title, assignee_id: user.id, state: 'all')}']")
+ context 'issue' do
+ let(:issuable) { create(:issue, project: project, assignees: [user]) }
+
+ it 'links to the page for the issue' do
+ expect(rendered).to have_css("a[href='#{project_issue_path(project, issuable)}']", class: 'issue-link')
+ end
+
+ it 'links to issues page for user' do
+ expect(rendered).to have_css("a[href='#{project_issues_path(project, milestone_title: milestone.title, assignee_id: user.id, state: 'all')}']")
+ end
+ end
+
+ context 'merge request' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project, assignees: [user]) }
+
+ it 'links to merge requests page for user' do
+ expect(rendered).to have_css("a[href='#{project_merge_requests_path(project, milestone_title: milestone.title, assignee_id: user.id, state: 'all')}']")
+ end
+
+ it 'links to the page for the merge request' do
+ expect(rendered).to have_css("a[href='#{project_merge_request_path(project, issuable)}']", class: 'issue-link')
+ end
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 34aaa9bb1e9..72e423db611 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -34,6 +34,31 @@ describe PostReceive do
expect(Gitlab::GitLogger).to receive(:error).with("POST-RECEIVE: #{error_message}")
expect(perform).to be(false)
end
+
+ context 'with PersonalSnippet' do
+ let(:gl_repository) { "snippet-#{snippet.id}" }
+ let(:snippet) { create(:personal_snippet, author: project.owner) }
+
+ it 'does not log an error' do
+ expect(Gitlab::GitLogger).not_to receive(:error)
+ expect(Gitlab::GitPostReceive).to receive(:new).and_call_original
+ expect_any_instance_of(described_class) do |instance|
+ expect(instance).to receive(:process_snippet_changes)
+ end
+
+ perform
+ end
+ end
+
+ context 'with ProjectSnippet' do
+ let(:gl_repository) { "snippet-#{snippet.id}" }
+ let(:snippet) { create(:snippet, type: 'ProjectSnippet', project: nil, author: project.owner) }
+
+ it 'returns false and logs an error' do
+ expect(Gitlab::GitLogger).to receive(:error).with("POST-RECEIVE: #{error_message}")
+ expect(perform).to be(false)
+ end
+ end
end
describe "#process_project_changes" do
@@ -44,7 +69,7 @@ describe PostReceive do
before do
allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(empty_project.owner)
# Need to mock here so we can expect calls on project
- allow(Gitlab::GlRepository).to receive(:parse).and_return([empty_project, Gitlab::GlRepository::PROJECT])
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([empty_project, empty_project, Gitlab::GlRepository::PROJECT])
end
it 'expire the status cache' do
@@ -97,7 +122,7 @@ describe PostReceive do
before do
allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
- allow(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::PROJECT])
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([project, project, Gitlab::GlRepository::PROJECT])
end
shared_examples 'updating remote mirrors' do
@@ -176,7 +201,7 @@ describe PostReceive do
end
before do
- expect(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::PROJECT])
+ expect(Gitlab::GlRepository).to receive(:parse).and_return([project, project, Gitlab::GlRepository::PROJECT])
end
it 'does not expire branches cache' do
@@ -256,7 +281,7 @@ describe PostReceive do
before do
# Need to mock here so we can expect calls on project
- allow(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::WIKI])
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([project, project, Gitlab::GlRepository::WIKI])
end
it 'updates project activity' do
@@ -333,4 +358,82 @@ describe PostReceive do
perform
end
end
+
+ describe '#process_snippet_changes' do
+ let(:gl_repository) { "snippet-#{snippet.id}" }
+
+ before do
+ # Need to mock here so we can expect calls on project
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([snippet, snippet.project, Gitlab::GlRepository::SNIPPET])
+ end
+
+ shared_examples 'snippet changes actions' do
+ context 'unidentified user' do
+ let!(:key_id) { '' }
+
+ it 'returns false' do
+ expect(perform).to be false
+ end
+ end
+
+ context 'with changes' do
+ context 'branches' do
+ let(:changes) do
+ <<~EOF
+ 123456 789012 refs/heads/tést1
+ 123456 789012 refs/heads/tést2
+ EOF
+ end
+
+ it 'expires the branches cache' do
+ expect(snippet.repository).to receive(:expire_branches_cache).once
+
+ perform
+ end
+
+ it 'expires the status cache' do
+ expect(snippet.repository).to receive(:empty?).and_return(true)
+ expect(snippet.repository).to receive(:expire_status_cache)
+
+ perform
+ end
+ end
+
+ context 'tags' do
+ let(:changes) do
+ <<~EOF
+ 654321 210987 refs/tags/tag1
+ 654322 210986 refs/tags/tag2
+ 654323 210985 refs/tags/tag3
+ EOF
+ end
+
+ it 'does not expire branches cache' do
+ expect(snippet.repository).not_to receive(:expire_branches_cache)
+
+ perform
+ end
+
+ it 'only invalidates tags once' do
+ expect(snippet.repository).to receive(:expire_caches_for_tags).once.and_call_original
+ expect(snippet.repository).to receive(:expire_tags_cache).once.and_call_original
+
+ perform
+ end
+ end
+ end
+ end
+
+ context 'with PersonalSnippet' do
+ let!(:snippet) { create(:personal_snippet, author: project.owner) }
+
+ it_behaves_like 'snippet changes actions'
+ end
+
+ context 'with ProjectSnippet' do
+ let!(:snippet) { create(:project_snippet, project: project, author: project.owner) }
+
+ it_behaves_like 'snippet changes actions'
+ end
+ end
end