...@@ -3,62 +3,101 @@ ...@@ -3,62 +3,101 @@
require 'spec_helper' require 'spec_helper'
describe Import::GitlabProjectsController do describe Import::GitlabProjectsController do
include WorkhorseHelpers
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let_it_be(:namespace) { create(:namespace) } let_it_be(:namespace) { create(:namespace) }
let_it_be(:user) { namespace.owner } let_it_be(:user) { namespace.owner }
let(:file) { fixture_file_upload('spec/fixtures/project_export.tar.gz', 'text/plain') }
before do before do
sign_in(user) login_as(user)
end end
describe 'POST create' do describe 'POST create' do
context 'with an invalid path' do subject { upload_archive(file_upload, workhorse_headers, params) }
it 'redirects with an error' do
post :create, params: { namespace_id: namespace.id, path: '/test', file: file }
expect(flash[:alert]).to start_with('Project could not be imported') let(:file) { File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:file_upload) { fixture_file_upload(file) }
let(:params) { { namespace_id: namespace.id, path: 'test' } }
before do
allow(ImportExportUploader).to receive(:workhorse_upload_path).and_return('/')
end
context 'with a valid path' do
it 'schedules an import and redirects to the new project path' do
stub_import(namespace)
subject
expect(flash[:notice]).to include('is being imported')
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:found)
end end
end
context 'with an invalid path' do
['/test', '../test'].each do |invalid_path|
it "redirects with an error when path is `#{invalid_path}`" do
params[:path] = invalid_path
it 'redirects with an error when a relative path is used' do subject
post :create, params: { namespace_id: namespace.id, path: '../test', file: file }
expect(flash[:alert]).to start_with('Project could not be imported') expect(flash[:alert]).to start_with('Project could not be imported')
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:found)
end end
end end
end
context 'with a valid path' do context 'when request exceeds the rate limit' do
it 'redirects to the new project path' do before do
post :create, params: { namespace_id: namespace.id, path: 'test', file: file } allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
end
expect(flash[:notice]).to include('is being imported') it 'prevents users from importing projects' do
subject
expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.')
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:found)
end end
end end
it_behaves_like 'project import rate limiter' def upload_archive(file, headers = {}, params = {})
workhorse_finalize(
import_gitlab_project_path,
method: :post,
file_key: :file,
params: params.merge(file: file),
headers: headers,
send_rewritten_field: true
)
end end
describe 'POST authorize' do def stub_import(namespace)
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') } expect_any_instance_of(ProjectImportState).to receive(:schedule)
expect(::Projects::CreateService)
before do .to receive(:new)
request.headers['GitLab-Workhorse'] = '1.0' .with(user, instance_of(ActionController::Parameters))
request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = workhorse_token .and_call_original
end
end end
describe 'POST authorize' do
subject { post authorize_import_gitlab_project_path, headers: workhorse_headers }
it 'authorizes importing project with workhorse header' do it 'authorizes importing project with workhorse header' do
post :authorize, format: :json subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
end end
it 'rejects requests that bypassed gitlab-workhorse or have invalid header' do it 'rejects requests that bypassed gitlab-workhorse' do
request.headers[Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER] = 'INVALID_HEADER' workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
expect { post :authorize, format: :json }.to raise_error(JWT::DecodeError) expect { subject }.to raise_error(JWT::DecodeError)
end end
context 'when using remote storage' do context 'when using remote storage' do
...@@ -68,7 +107,7 @@ describe Import::GitlabProjectsController do ...@@ -68,7 +107,7 @@ describe Import::GitlabProjectsController do
end end
it 'responds with status 200, location of file remote store and object details' do it 'responds with status 200, location of file remote store and object details' do
post :authorize, format: :json subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
...@@ -87,7 +126,7 @@ describe Import::GitlabProjectsController do ...@@ -87,7 +126,7 @@ describe Import::GitlabProjectsController do
end end
it 'handles as a local file' do it 'handles as a local file' do
post :authorize, format: :json subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
... ...
......
# frozen_string_literal: true
module NavbarStructureHelper
def insert_after_nav_item(before_nav_item_name, new_nav_item:)
expect(structure).to include(a_hash_including(nav_item: before_nav_item_name))
index = structure.find_index { |h| h[:nav_item] == before_nav_item_name }
structure.insert(index + 1, new_nav_item)
end
def insert_after_sub_nav_item(before_sub_nav_item_name, within:, new_sub_nav_item_name:)
expect(structure).to include(a_hash_including(nav_item: within))
hash = structure.find { |h| h[:nav_item] == within }
expect(hash).to have_key(:nav_sub_items)
expect(hash[:nav_sub_items]).to include(before_sub_nav_item_name)
index = hash[:nav_sub_items].find_index(before_sub_nav_item_name)
hash[:nav_sub_items].insert(index + 1, new_sub_nav_item_name)
end
end
# frozen_string_literal: true
RSpec.shared_context 'project navbar structure' do
let(:requirements_nav_item) do
{
nav_item: _('Requirements'),
nav_sub_items: [_('List')]
}
end
let(:analytics_nav_item) do
{
nav_item: _('Analytics'),
nav_sub_items: [
_('CI / CD'),
(_('Code Review') if Gitlab.ee?),
_('Repository'),
_('Value Stream')
]
}
end
let(:structure) do
[
{
nav_item: _('Project overview'),
nav_sub_items: [
_('Details'),
_('Activity'),
_('Releases')
]
},
{
nav_item: _('Repository'),
nav_sub_items: [
_('Files'),
_('Commits'),
_('Branches'),
_('Tags'),
_('Contributors'),
_('Graph'),
_('Compare'),
(_('Locked Files') if Gitlab.ee?)
]
},
{
nav_item: _('Issues'),
nav_sub_items: [
_('List'),
_('Boards'),
_('Labels'),
_('Milestones')
]
},
{
nav_item: _('Merge Requests'),
nav_sub_items: []
},
(requirements_nav_item if Gitlab.ee?),
{
nav_item: _('CI / CD'),
nav_sub_items: [
_('Pipelines'),
_('Jobs'),
_('Artifacts'),
_('Schedules')
]
},
{
nav_item: _('Operations'),
nav_sub_items: [
_('Metrics'),
_('Environments'),
_('Error Tracking'),
_('Serverless'),
_('Logs'),
_('Kubernetes')
]
},
analytics_nav_item,
{
nav_item: _('Wiki'),
nav_sub_items: []
},
{
nav_item: _('Snippets'),
nav_sub_items: []
},
{
nav_item: _('Settings'),
nav_sub_items: [
_('General'),
_('Members'),
_('Integrations'),
_('Webhooks'),
_('Repository'),
_('CI / CD'),
_('Operations'),
(_('Audit Events') if Gitlab.ee?)
].compact
}
].compact
end
end
RSpec.shared_context 'group navbar structure' do
let(:analytics_nav_item) do
{
nav_item: _('Analytics'),
nav_sub_items: [
_('Contribution')
]
}
end
let(:settings_nav_item) do
{
nav_item: _('Settings'),
nav_sub_items: [
_('General'),
_('Projects'),
_('CI / CD'),
_('Webhooks'),
_('Audit Events'),
_('Usage Quotas')
]
}
end
let(:structure) do
[
{
nav_item: _('Group overview'),
nav_sub_items: [
_('Details'),
_('Activity')
]
},
{
nav_item: _('Issues'),
nav_sub_items: [
_('List'),
_('Board'),
_('Labels'),
_('Milestones')
]
},
{
nav_item: _('Merge Requests'),
nav_sub_items: []
},
{
nav_item: _('Kubernetes'),
nav_sub_items: []
},
(analytics_nav_item if Gitlab.ee?),
{
nav_item: _('Members'),
nav_sub_items: []
}
]
end
end
...@@ -34,36 +34,23 @@ describe 'layouts/_head' do ...@@ -34,36 +34,23 @@ describe 'layouts/_head' do
expect(rendered).to match(%{content="foo" http-equiv="refresh"}) expect(rendered).to match(%{content="foo" http-equiv="refresh"})
end end
context 'when an asset_host is set and feature is activated in the config it will' do context 'when an asset_host is set' do
let(:asset_host) { 'http://assets' } let(:asset_host) { 'http://assets' }
before do before do
stub_feature_flags(asset_host_prefetch: true)
allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
end end
it 'add a link dns-prefetch tag' do it 'adds a link dns-prefetch tag' do
render render
expect(rendered).to match('<link href="http://assets" rel="dns-prefetch">')
end
it 'add a link preconnect tag' do
render
expect(rendered).to match('<link crossorigin="" href="http://assets" rel="preconnnect">')
end
end
context 'when an asset_host is set and feature is not activated in the config it will' do expect(rendered).to match(%Q(<link href="#{asset_host}" rel="dns-prefetch">))
let(:asset_host) { 'http://assets' }
before do
stub_feature_flags(asset_host_prefetch: false)
allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
end end
it 'not add a link dns-prefetch tag' do it 'adds a link preconnect tag' do
render render
expect(rendered).not_to match('<link href="http://assets" rel="dns-prefetch">')
expect(rendered).to match(%Q(<link crossorigin="" href="#{asset_host}" rel="preconnnect">))
end end
end end
... ...
......
...@@ -781,15 +781,15 @@ ...@@ -781,15 +781,15 @@
eslint-plugin-vue "^6.2.1" eslint-plugin-vue "^6.2.1"
vue-eslint-parser "^7.0.0" vue-eslint-parser "^7.0.0"
"@gitlab/svgs@^1.113.0": "@gitlab/svgs@^1.114.0":
version "1.113.0" version "1.114.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.113.0.tgz#0ea9cb3122a479f3ed4bb22943d68a9a38f69948" resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.114.0.tgz#ffee243fa540016c8198596686a46e3c459adb32"
integrity sha512-upT+sKEnnwZDU7vzI5VSyg2l6IOd/0Icm/MLae6po/nOGbf2vftkUVfbalzISAmk9eYTuJQ1sGmRWdKXPGy1cw== integrity sha512-r2tizWDx1fX2QbeXzU0Z5Fi9YS7TLIxt7K+vAnZxOeddgd5KdipCvhT/H7n9Oa9jLU4bMwGSrHZ1usCmCoWOnw==
"@gitlab/ui@^9.29.0": "@gitlab/ui@^9.31.1":
version "9.29.0" version "9.31.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.29.0.tgz#2308aec1d3d837392eaaf9d441aecec4b695ed73" resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.31.1.tgz#53206aac643d79f8eddbc5715131bad5ffc1e412"
integrity sha512-zIY/aChWaU4UBlAjZ95PpBxgUd9VrGiXs9ujVU6Gbi+ZsHbpDvPlBHsHEG9isnUVBE2AD4GPqMLO8K9i+B9O4A== integrity sha512-MHBVIpVzUYPkr70ti21dTZbzzZttOidWZdN3TeZOdqYbjYg2ONujFBxOGSCNYcaMYr+1/wFtuVRL/JmMVGjJrg==
dependencies: dependencies:
"@babel/standalone" "^7.0.0" "@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0" "@gitlab/vue-toasted" "^1.3.0"
... ...
......