...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
# Normal values are "production", "test" and "development". # Normal values are "production", "test" and "development".
RAILS_ENV="production" RAILS_ENV="production"
# Uncomment the line below to enable Puma web server instead of Unicorn. # Uncomment the line below to enable the Unicorn web server instead of Puma.
# EXPERIMENTAL_PUMA=1 # USE_UNICORN=1
# app_user defines the user that GitLab is run as. # app_user defines the user that GitLab is run as.
# The default is "git". # The default is "git".
... ...
......
...@@ -16775,6 +16775,9 @@ msgstr "" ...@@ -16775,6 +16775,9 @@ msgstr ""
msgid "Require users to prove ownership of custom domains" msgid "Require users to prove ownership of custom domains"
msgstr "" msgstr ""
   
msgid "Requirements"
msgstr ""
msgid "Requires approval from %{names}." msgid "Requires approval from %{names}."
msgid_plural "Requires %{count} more approvals from %{names}." msgid_plural "Requires %{count} more approvals from %{names}."
msgstr[0] "" msgstr[0] ""
... ...
......
...@@ -18,6 +18,13 @@ describe 'Project navbar' do ...@@ -18,6 +18,13 @@ describe 'Project navbar' do
} }
end end
let(:requirements_nav_item) do
{
nav_item: _('Requirements'),
nav_sub_items: [_('List')]
}
end
let(:structure) do let(:structure) do
[ [
{ {
...@@ -54,6 +61,7 @@ describe 'Project navbar' do ...@@ -54,6 +61,7 @@ describe 'Project navbar' do
nav_item: _('Merge Requests'), nav_item: _('Merge Requests'),
nav_sub_items: [] nav_sub_items: []
}, },
(requirements_nav_item if Gitlab.ee?),
{ {
nav_item: _('CI / CD'), nav_item: _('CI / CD'),
nav_sub_items: [ nav_sub_items: [
...@@ -100,6 +108,7 @@ describe 'Project navbar' do ...@@ -100,6 +108,7 @@ describe 'Project navbar' do
end end
before do before do
stub_licensed_features(requirements: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
end end
...@@ -123,5 +132,15 @@ describe 'Project navbar' do ...@@ -123,5 +132,15 @@ describe 'Project navbar' do
it_behaves_like 'verified navigation bar' it_behaves_like 'verified navigation bar'
end end
context 'when requirements is available' do
before do
stub_licensed_features(requirements: true)
visit project_path(project)
end
it_behaves_like 'verified navigation bar'
end
end end
end end
...@@ -3,24 +3,29 @@ ...@@ -3,24 +3,29 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Checks::ProjectCreated, :clean_gitlab_redis_shared_state do describe Gitlab::Checks::ProjectCreated, :clean_gitlab_redis_shared_state do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project) } let_it_be(:project) { create(:project, :repository, namespace: user.namespace) }
let(:protocol) { 'http' }
let(:git_user) { user }
let(:repository) { project.repository }
subject { described_class.new(repository, git_user, 'http') }
describe '.fetch_message' do describe '.fetch_message' do
context 'with a project created message queue' do context 'with a project created message queue' do
let(:project_created) { described_class.new(project, user, 'http') }
before do before do
project_created.add_message subject.add_message
end end
it 'returns project created message' do it 'returns project created message' do
expect(described_class.fetch_message(user.id, project.id)).to eq(project_created.message) expect(described_class.fetch_message(user.id, project.id)).to eq(subject.message)
end end
it 'deletes the project created message from redis' do it 'deletes the project created message from redis' do
expect(Gitlab::Redis::SharedState.with { |redis| redis.get("project_created:#{user.id}:#{project.id}") }).not_to be_nil expect(Gitlab::Redis::SharedState.with { |redis| redis.get("project_created:#{user.id}:#{project.id}") }).not_to be_nil
described_class.fetch_message(user.id, project.id) described_class.fetch_message(user.id, project.id)
expect(Gitlab::Redis::SharedState.with { |redis| redis.get("project_created:#{user.id}:#{project.id}") }).to be_nil expect(Gitlab::Redis::SharedState.with { |redis| redis.get("project_created:#{user.id}:#{project.id}") }).to be_nil
end end
end end
...@@ -34,15 +39,15 @@ describe Gitlab::Checks::ProjectCreated, :clean_gitlab_redis_shared_state do ...@@ -34,15 +39,15 @@ describe Gitlab::Checks::ProjectCreated, :clean_gitlab_redis_shared_state do
describe '#add_message' do describe '#add_message' do
it 'queues a project created message' do it 'queues a project created message' do
project_created = described_class.new(project, user, 'http') expect(subject.add_message).to eq('OK')
expect(project_created.add_message).to eq('OK')
end end
it 'handles anonymous push' do context 'when user is nil' do
project_created = described_class.new(nil, user, 'http') let(:git_user) { nil }
expect(project_created.add_message).to be_nil it 'handles anonymous push' do
expect(subject.add_message).to be_nil
end
end end
end end
end end
...@@ -3,24 +3,30 @@ ...@@ -3,24 +3,30 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project) } let_it_be(:project) { create(:project, :repository, :wiki_repo, namespace: user.namespace) }
let(:repository) { project.repository }
let(:protocol) { 'http' }
let(:git_user) { user }
let(:redirect_path) { 'foo/bar' }
subject { described_class.new(repository, git_user, protocol, redirect_path) }
describe '.fetch_message' do describe '.fetch_message' do
context 'with a redirect message queue' do context 'with a redirect message queue' do
it 'returns the redirect message' do before do
project_moved = described_class.new(project, user, 'http', 'foo/bar') subject.add_message
project_moved.add_message end
expect(described_class.fetch_message(user.id, project.id)).to eq(project_moved.message) it 'returns the redirect message' do
expect(described_class.fetch_message(user.id, project.id)).to eq(subject.message)
end end
it 'deletes the redirect message from redis' do it 'deletes the redirect message from redis' do
project_moved = described_class.new(project, user, 'http', 'foo/bar')
project_moved.add_message
expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).not_to be_nil expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).not_to be_nil
described_class.fetch_message(user.id, project.id) described_class.fetch_message(user.id, project.id)
expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).to be_nil expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).to be_nil
end end
end end
...@@ -34,29 +40,82 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do ...@@ -34,29 +40,82 @@ describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
describe '#add_message' do describe '#add_message' do
it 'queues a redirect message' do it 'queues a redirect message' do
project_moved = described_class.new(project, user, 'http', 'foo/bar') expect(subject.add_message).to eq("OK")
expect(project_moved.add_message).to eq("OK")
end end
it 'handles anonymous clones' do context 'when user is nil' do
project_moved = described_class.new(project, nil, 'http', 'foo/bar') let(:git_user) { nil }
expect(project_moved.add_message).to eq(nil) it 'handles anonymous clones' do
expect(subject.add_message).to be_nil
end
end end
end end
describe '#message' do describe '#message' do
it 'returns a redirect message' do shared_examples 'errors per protocol' do
project_moved = described_class.new(project, user, 'http', 'foo/bar') shared_examples 'returns redirect message' do
it do
message = <<~MSG message = <<~MSG
Project 'foo/bar' was moved to '#{project.full_path}'. Project '#{redirect_path}' was moved to '#{project.full_path}'.
Please update your Git remote: Please update your Git remote:
git remote set-url origin #{project.http_url_to_repo} git remote set-url origin #{url_to_repo}
MSG MSG
expect(project_moved.message).to eq(message) expect(subject.message).to eq(message)
end
end
context 'when protocol is http' do
it_behaves_like 'returns redirect message' do
let(:url_to_repo) { http_url_to_repo }
end
end
context 'when protocol is ssh' do
let(:protocol) { 'ssh' }
it_behaves_like 'returns redirect message' do
let(:url_to_repo) { ssh_url_to_repo }
end
end
end
context 'with project' do
it_behaves_like 'errors per protocol' do
let(:http_url_to_repo) { project.http_url_to_repo }
let(:ssh_url_to_repo) { project.ssh_url_to_repo }
end
end
context 'with wiki' do
let(:repository) { project.wiki.repository }
it_behaves_like 'errors per protocol' do
let(:http_url_to_repo) { project.wiki.http_url_to_repo }
let(:ssh_url_to_repo) { project.wiki.ssh_url_to_repo }
end
end
context 'with project snippet' do
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
let(:repository) { snippet.repository }
it_behaves_like 'errors per protocol' do
let(:http_url_to_repo) { snippet.http_url_to_repo }
let(:ssh_url_to_repo) { snippet.ssh_url_to_repo }
end
end
context 'with personal snippet' do
let_it_be(:snippet) { create(:personal_snippet, :repository, author: user) }
let(:repository) { snippet.repository }
it 'returns nil' do
expect(subject.add_message).to be_nil
end
end end
end end
end end
...@@ -8,7 +8,8 @@ describe ::Gitlab::RepoPath do ...@@ -8,7 +8,8 @@ describe ::Gitlab::RepoPath do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet) } let_it_be(:personal_snippet) { create(:personal_snippet) }
let_it_be(:project_snippet) { create(:project_snippet, project: project) } let_it_be(:project_snippet) { create(:project_snippet, project: project) }
let_it_be(:redirect) { project.route.create_redirect('foo/bar/baz') } let_it_be(:redirect_route) { 'foo/bar/baz' }
let_it_be(:redirect) { project.route.create_redirect(redirect_route) }
describe '.parse' do describe '.parse' do
context 'a repository storage path' do context 'a repository storage path' do
...@@ -43,22 +44,20 @@ describe ::Gitlab::RepoPath do ...@@ -43,22 +44,20 @@ describe ::Gitlab::RepoPath do
end end
context 'of a redirected project' do context 'of a redirected project' do
let(:redirect) { project.route.create_redirect('foo/bar') }
it 'parses a relative repository path' do it 'parses a relative repository path' do
expect(described_class.parse(redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, 'foo/bar']) expect(described_class.parse(redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, redirect_route])
end end
it 'parses a relative wiki path' do it 'parses a relative wiki path' do
expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, project, Gitlab::GlRepository::WIKI, 'foo/bar.wiki']) expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, project, Gitlab::GlRepository::WIKI, redirect_route])
end end
it 'parses a relative path starting with /' do it 'parses a relative path starting with /' do
expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, 'foo/bar']) expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, project, Gitlab::GlRepository::PROJECT, redirect_route])
end end
it 'parses a redirected project snippet repository path' do 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}"]) expect(described_class.parse(redirect.path + "/snippets/#{project_snippet.id}.git")).to eq([project_snippet, project, Gitlab::GlRepository::SNIPPET, redirect_route])
end end
end end
end end
...@@ -71,8 +70,8 @@ describe ::Gitlab::RepoPath do ...@@ -71,8 +70,8 @@ describe ::Gitlab::RepoPath do
describe '.find_project' do describe '.find_project' do
context 'when finding a project by its canonical path' do context 'when finding a project by its canonical path' do
context 'when the cases match' do context 'when the cases match' do
it 'returns the project and false' do it 'returns the project and nil' do
expect(described_class.find_project(project.full_path)).to eq([project, false]) expect(described_class.find_project(project.full_path)).to eq([project, nil])
end end
end end
...@@ -81,45 +80,45 @@ describe ::Gitlab::RepoPath do ...@@ -81,45 +80,45 @@ describe ::Gitlab::RepoPath do
# easy and safe to redirect someone to the correctly-cased URL. For git # easy and safe to redirect someone to the correctly-cased URL. For git
# requests, we should accept wrongly-cased URLs because it is a pain to # requests, we should accept wrongly-cased URLs because it is a pain to
# block people's git operations and force them to update remote URLs. # block people's git operations and force them to update remote URLs.
it 'returns the project and false' do it 'returns the project and nil' do
expect(described_class.find_project(project.full_path.upcase)).to eq([project, false]) expect(described_class.find_project(project.full_path.upcase)).to eq([project, nil])
end end
end end
end end
context 'when finding a project via a redirect' do context 'when finding a project via a redirect' do
it 'returns the project and true' do it 'returns the project and nil' do
expect(described_class.find_project(redirect.path)).to eq([project, true]) expect(described_class.find_project(redirect.path)).to eq([project, redirect.path])
end end
end end
end end
describe '.find_snippet' do describe '.find_snippet' do
it 'extracts path and id from personal snippet route' 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]) expect(described_class.find_snippet("snippets/#{personal_snippet.id}")).to eq([personal_snippet, nil])
end end
it 'extracts path and id from project snippet route' do 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]) expect(described_class.find_snippet("#{project.full_path}/snippets/#{project_snippet.id}")).to eq([project_snippet, nil])
end end
it 'returns nil for invalid snippet paths' do it 'returns nil for invalid snippet paths' do
aggregate_failures do aggregate_failures do
expect(described_class.find_snippet("snippets/#{project_snippet.id}")).to eq([nil, false]) expect(described_class.find_snippet("snippets/#{project_snippet.id}")).to eq([nil, nil])
expect(described_class.find_snippet("#{project.full_path}/snippets/#{personal_snippet.id}")).to eq([nil, false]) expect(described_class.find_snippet("#{project.full_path}/snippets/#{personal_snippet.id}")).to eq([nil, nil])
expect(described_class.find_snippet('')).to eq([nil, false]) expect(described_class.find_snippet('')).to eq([nil, nil])
end end
end end
it 'returns nil for snippets not associated with the project' do it 'returns nil for snippets not associated with the project' do
snippet = create(:project_snippet) snippet = create(:project_snippet)
expect(described_class.find_snippet("#{project.full_path}/snippets/#{snippet.id}")).to eq([nil, false]) expect(described_class.find_snippet("#{project.full_path}/snippets/#{snippet.id}")).to eq([nil, nil])
end end
context 'when finding a project snippet via a redirect' do context 'when finding a project snippet via a redirect' do
it 'returns the project and true' do it 'returns the project and true' do
expect(described_class.find_snippet("#{redirect.path}/snippets/#{project_snippet.id}")).to eq([project_snippet, true]) expect(described_class.find_snippet("#{redirect.path}/snippets/#{project_snippet.id}")).to eq([project_snippet, redirect.path])
end end
end end
end end
... ...
......
...@@ -697,4 +697,20 @@ describe Snippet do ...@@ -697,4 +697,20 @@ describe Snippet do
it { is_expected.to eq result } it { is_expected.to eq result }
end end
end end
describe '#url_to_repo' do
subject { snippet.url_to_repo }
context 'with personal snippet' do
let(:snippet) { create(:personal_snippet) }
it { is_expected.to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "snippets/#{snippet.id}.git") }
end
context 'with project snippet' do
let(:snippet) { create(:project_snippet) }
it { is_expected.to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "#{snippet.project.full_path}/snippets/#{snippet.id}.git") }
end
end
end end
...@@ -234,7 +234,7 @@ describe PostReceiveService do ...@@ -234,7 +234,7 @@ describe PostReceiveService do
context 'with a redirected data' do context 'with a redirected data' do
it 'returns redirected message on the response' do it 'returns redirected message on the response' do
project_moved = Gitlab::Checks::ProjectMoved.new(project, user, 'http', 'foo/baz') project_moved = Gitlab::Checks::ProjectMoved.new(project.repository, user, 'http', 'foo/baz')
project_moved.add_message project_moved.add_message
expect(subject).to include(build_basic_message(project_moved.message)) expect(subject).to include(build_basic_message(project_moved.message))
...@@ -243,7 +243,7 @@ describe PostReceiveService do ...@@ -243,7 +243,7 @@ describe PostReceiveService do
context 'with new project data' do context 'with new project data' do
it 'returns new project message on the response' do it 'returns new project message on the response' do
project_created = Gitlab::Checks::ProjectCreated.new(project, user, 'http') project_created = Gitlab::Checks::ProjectCreated.new(project.repository, user, 'http')
project_created.add_message project_created.add_message
expect(subject).to include(build_basic_message(project_created.message)) expect(subject).to include(build_basic_message(project_created.message))
... ...
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
# end # end
# #
RSpec.shared_examples 'an idempotent worker' do RSpec.shared_examples 'an idempotent worker' do
let(:worker_exec_times) { IdempotentWorkerHelper::WORKER_EXEC_TIMES }
# Avoid stubbing calls for a more accurate run. # Avoid stubbing calls for a more accurate run.
subject do subject do
defined?(job_args) ? perform_multiple(job_args) : perform_multiple defined?(job_args) ? perform_multiple(job_args) : perform_multiple
... ...
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe ApplicationWorker do describe ApplicationWorker do
let(:worker) do let_it_be(:worker) do
Class.new do Class.new do
def self.name def self.name
'Gitlab::Foo::Bar::DummyWorker' 'Gitlab::Foo::Bar::DummyWorker'
...@@ -13,12 +13,51 @@ describe ApplicationWorker do ...@@ -13,12 +13,51 @@ describe ApplicationWorker do
end end
end end
let(:instance) { worker.new }
describe 'Sidekiq options' do describe 'Sidekiq options' do
it 'sets the queue name based on the class name' do it 'sets the queue name based on the class name' do
expect(worker.sidekiq_options['queue']).to eq('foo_bar_dummy') expect(worker.sidekiq_options['queue']).to eq('foo_bar_dummy')
end end
end end
describe '#structured_payload' do
let(:payload) { {} }
subject(:result) { instance.structured_payload(payload) }
it 'adds worker related payload' do
instance.jid = 'a jid'
expect(result).to include(
'class' => worker.class,
'job_status' => 'running',
'queue' => worker.queue,
'jid' => instance.jid
)
end
it 'adds labkit context' do
user = build_stubbed(:user, username: 'jane-doe')
instance.with_context(user: user) do
expect(result).to include('meta.user' => user.username)
end
end
it 'adds custom payload converting stringified keys' do
payload[:message] = 'some message'
expect(result).to include('message' => payload[:message])
end
it 'does not override predefined context keys with custom payload' do
payload['class'] = 'custom value'
expect(result).to include('class' => worker.class)
end
end
describe '.queue_namespace' do describe '.queue_namespace' do
it 'sets the queue name based on the class name' do it 'sets the queue name based on the class name' do
worker.queue_namespace :some_namespace worker.queue_namespace :some_namespace
... ...
......
...@@ -21,12 +21,12 @@ describe ExpireJobCacheWorker do ...@@ -21,12 +21,12 @@ describe ExpireJobCacheWorker do
allow(Gitlab::EtagCaching::Store).to receive(:new) { spy_store } allow(Gitlab::EtagCaching::Store).to receive(:new) { spy_store }
expect(spy_store).to receive(:touch) expect(spy_store).to receive(:touch)
.exactly(IdempotentWorkerHelper::WORKER_EXEC_TIMES).times .exactly(worker_exec_times).times
.with(pipeline_path) .with(pipeline_path)
.and_call_original .and_call_original
expect(spy_store).to receive(:touch) expect(spy_store).to receive(:touch)
.exactly(IdempotentWorkerHelper::WORKER_EXEC_TIMES).times .exactly(worker_exec_times).times
.with(job_path) .with(job_path)
.and_call_original .and_call_original
... ...
......