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-03 00:08:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-03 00:08:01 +0300
commit561e1b470f0a99fe6304c8f197348c47a637d594 (patch)
tree6b361b6b0b412b70450aca167079c50a13bd88d8 /spec
parent7b52c7cb634ef7047d30b0337fe477bcdcedf41d (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/code_navigation/store/actions_spec.js26
-rw-r--r--spec/graphql/resolvers/projects/snippets_resolver_spec.rb10
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb3
-rw-r--r--spec/models/project_spec.rb38
-rw-r--r--spec/presenters/snippet_blob_presenter_spec.rb83
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb4
-rw-r--r--spec/requests/api/lsif_data_spec.rb75
-rw-r--r--spec/requests/api/project_container_repositories_spec.rb55
-rw-r--r--spec/requests/api/projects_spec.rb65
-rw-r--r--spec/services/projects/container_repository/cleanup_tags_service_spec.rb84
-rw-r--r--spec/services/projects/fork_service_spec.rb21
-rw-r--r--spec/services/projects/lsif_data_service_spec.rb15
-rw-r--r--spec/services/projects/update_repository_storage_service_spec.rb83
-rw-r--r--spec/services/projects/update_service_spec.rb19
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb101
-rw-r--r--spec/workers/project_update_repository_storage_worker_spec.rb24
17 files changed, 606 insertions, 104 deletions
diff --git a/spec/frontend/code_navigation/store/actions_spec.js b/spec/frontend/code_navigation/store/actions_spec.js
index 2230e0880bb..9c44480ca67 100644
--- a/spec/frontend/code_navigation/store/actions_spec.js
+++ b/spec/frontend/code_navigation/store/actions_spec.js
@@ -45,18 +45,20 @@ describe('Code navigation actions', () => {
describe('success', () => {
beforeEach(() => {
- mock.onGet(apiUrl).replyOnce(200, [
- {
- start_line: 0,
- start_char: 0,
- hover: { value: '123' },
- },
- {
- start_line: 1,
- start_char: 0,
- hover: null,
- },
- ]);
+ mock.onGet(apiUrl).replyOnce(200, {
+ index: [
+ {
+ start_line: 0,
+ start_char: 0,
+ hover: { value: '123' },
+ },
+ {
+ start_line: 1,
+ start_char: 0,
+ hover: null,
+ },
+ ],
+ });
});
it('commits REQUEST_DATA_SUCCESS with normalized data', done => {
diff --git a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
index eef891bf984..6d301b1c742 100644
--- a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
+++ b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb
@@ -75,6 +75,16 @@ describe Resolvers::Projects::SnippetsResolver do
expect(resolve_snippets(context: { current_user: other_user }, args: { ids: project_snippet.to_global_id })).to be_empty
end
end
+
+ context 'when project snippets are disabled' do
+ it 'raises an error' do
+ disabled_snippet_project = create(:project, :snippets_disabled)
+ disabled_snippet_project.add_developer(current_user)
+
+ expect(SnippetsFinder).not_to receive(:new)
+ expect { resolve_snippets(obj: disabled_snippet_project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
end
def resolve_snippets(args: {}, context: { current_user: current_user }, obj: project)
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 4b378936965..c76a1ffaa14 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -175,9 +175,12 @@ describe Gitlab::Danger::Helper do
'spec/javascripts/foo' | :frontend
'spec/frontend/bar' | :frontend
'vendor/assets/foo' | :frontend
+ 'babel.config.js' | :frontend
'jest.config.js' | :frontend
'package.json' | :frontend
'yarn.lock' | :frontend
+ 'config/foo.js' | :frontend
+ 'config/deep/foo.js' | :frontend
'ee/app/assets/foo' | :frontend
'ee/app/views/foo' | :frontend
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 4885d4b63ff..cbd55077316 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2822,6 +2822,44 @@ describe Project do
end
end
+ describe '#change_repository_storage' do
+ let(:project) { create(:project, :repository) }
+ let(:read_only_project) { create(:project, :repository, repository_read_only: true) }
+
+ before do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
+ end
+
+ it 'schedules the transfer of the repository to the new storage and locks the project' do
+ expect(ProjectUpdateRepositoryStorageWorker).to receive(:perform_async).with(project.id, 'test_second_storage')
+
+ project.change_repository_storage('test_second_storage')
+ project.save!
+
+ expect(project).to be_repository_read_only
+ end
+
+ it "doesn't schedule the transfer if the repository is already read-only" do
+ expect(ProjectUpdateRepositoryStorageWorker).not_to receive(:perform_async)
+
+ read_only_project.change_repository_storage('test_second_storage')
+ read_only_project.save!
+ end
+
+ it "doesn't lock or schedule the transfer if the storage hasn't changed" do
+ expect(ProjectUpdateRepositoryStorageWorker).not_to receive(:perform_async)
+
+ project.change_repository_storage(project.repository_storage)
+ project.save!
+
+ expect(project).not_to be_repository_read_only
+ end
+
+ it 'throws an error if an invalid repository storage is provided' do
+ expect { project.change_repository_storage('unknown') }.to raise_error(ArgumentError)
+ end
+ end
+
describe '#pushes_since_gc' do
let(:project) { create(:project) }
diff --git a/spec/presenters/snippet_blob_presenter_spec.rb b/spec/presenters/snippet_blob_presenter_spec.rb
index 89f4d65b47f..eb7621cc591 100644
--- a/spec/presenters/snippet_blob_presenter_spec.rb
+++ b/spec/presenters/snippet_blob_presenter_spec.rb
@@ -4,36 +4,73 @@ require 'spec_helper'
describe SnippetBlobPresenter do
describe '#rich_data' do
- let(:snippet) { build(:personal_snippet) }
+ before do
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:current_user).and_return(nil)
+ end
+ end
subject { described_class.new(snippet.blob).rich_data }
- it 'returns nil when the snippet blob is binary' do
- allow(snippet.blob).to receive(:binary?).and_return(true)
+ context 'with PersonalSnippet' do
+ let(:raw_url) { "http://127.0.0.1:3000/snippets/#{snippet.id}/raw" }
+ let(:snippet) { build(:personal_snippet) }
- expect(subject).to be_nil
- end
+ it 'returns nil when the snippet blob is binary' do
+ allow(snippet.blob).to receive(:binary?).and_return(true)
- it 'returns markdown content when snippet file is markup' do
- snippet.file_name = 'test.md'
- snippet.content = '*foo*'
+ expect(subject).to be_nil
+ end
- expect(subject).to eq '<p data-sourcepos="1:1-1:5" dir="auto"><em>foo</em></p>'
- end
+ context 'with markdown format' do
+ let(:snippet) { create(:personal_snippet, file_name: 'test.md', content: '*foo*') }
- it 'returns syntax highlighted content' do
- snippet.file_name = 'test.rb'
- snippet.content = 'class Foo;end'
+ it 'returns rich markdown content' do
+ expected = <<~HTML
+ <div class="file-content md">
+ <p data-sourcepos="1:1-1:5" dir="auto"><em>foo</em></p>
+ </div>
+ HTML
- expect(subject)
- .to eq '<span id="LC1" class="line" lang="ruby"><span class="k">class</span> <span class="nc">Foo</span><span class="p">;</span><span class="k">end</span></span>'
- end
+ expect(subject).to eq(expected)
+ end
+ end
- it 'returns plain text highlighted content' do
- snippet.file_name = 'test'
- snippet.content = 'foo'
+ context 'with notebook format' do
+ let(:snippet) { create(:personal_snippet, file_name: 'test.ipynb') }
- expect(subject).to eq '<span id="LC1" class="line" lang="plaintext">foo</span>'
+ it 'returns rich notebook content' do
+ expect(subject.strip).to eq %Q(<div class="file-content" data-endpoint="/snippets/#{snippet.id}/raw" id="js-notebook-viewer"></div>)
+ end
+ end
+
+ context 'with openapi format' do
+ let(:snippet) { create(:personal_snippet, file_name: 'openapi.yml') }
+
+ it 'returns rich openapi content' do
+ expect(subject).to eq %Q(<div class="file-content" data-endpoint="/snippets/#{snippet.id}/raw" id="js-openapi-viewer"></div>\n)
+ end
+ end
+
+ context 'with svg format' do
+ let(:snippet) { create(:personal_snippet, file_name: 'test.svg') }
+
+ it 'returns rich svg content' do
+ result = Nokogiri::HTML::DocumentFragment.parse(subject)
+ image_tag = result.search('img').first
+
+ expect(image_tag.attr('src')).to include("data:#{snippet.blob.mime_type};base64")
+ expect(image_tag.attr('alt')).to eq('test.svg')
+ end
+ end
+
+ context 'with other format' do
+ let(:snippet) { create(:personal_snippet, file_name: 'test') }
+
+ it 'does not return no rich content' do
+ expect(subject).to be_nil
+ end
+ end
end
end
@@ -55,19 +92,19 @@ describe SnippetBlobPresenter do
expect(subject).to eq '<span id="LC1" class="line" lang="markdown"><span class="ge">*foo*</span></span>'
end
- it 'returns plain syntax content' do
+ it 'returns highlighted syntax content' do
snippet.file_name = 'test.rb'
snippet.content = 'class Foo;end'
expect(subject)
- .to eq '<span id="LC1" class="line" lang="">class Foo;end</span>'
+ .to eq '<span id="LC1" class="line" lang="ruby"><span class="k">class</span> <span class="nc">Foo</span><span class="p">;</span><span class="k">end</span></span>'
end
it 'returns plain text highlighted content' do
snippet.file_name = 'test'
snippet.content = 'foo'
- expect(subject).to eq '<span id="LC1" class="line" lang="">foo</span>'
+ expect(subject).to eq '<span id="LC1" class="line" lang="plaintext">foo</span>'
end
end
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index cb19f50b5b5..cef7fc5cbe3 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -67,7 +67,7 @@ describe 'Creating a Snippet' do
it 'returns the created Snippet' do
post_graphql_mutation(mutation, current_user: current_user)
- expect(mutation_response['snippet']['blob']['richData']).to match(content)
+ expect(mutation_response['snippet']['blob']['richData']).to be_nil
expect(mutation_response['snippet']['blob']['plainData']).to match(content)
expect(mutation_response['snippet']['title']).to eq(title)
expect(mutation_response['snippet']['description']).to eq(description)
@@ -93,7 +93,7 @@ describe 'Creating a Snippet' do
it 'returns the created Snippet' do
post_graphql_mutation(mutation, current_user: current_user)
- expect(mutation_response['snippet']['blob']['richData']).to match(content)
+ expect(mutation_response['snippet']['blob']['richData']).to be_nil
expect(mutation_response['snippet']['blob']['plainData']).to match(content)
expect(mutation_response['snippet']['title']).to eq(title)
expect(mutation_response['snippet']['description']).to eq(description)
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index e9481a36287..820c97e8341 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -56,7 +56,7 @@ describe 'Updating a Snippet' do
it 'returns the updated Snippet' do
post_graphql_mutation(mutation, current_user: current_user)
- expect(mutation_response['snippet']['blob']['richData']).to match(updated_content)
+ expect(mutation_response['snippet']['blob']['richData']).to be_nil
expect(mutation_response['snippet']['blob']['plainData']).to match(updated_content)
expect(mutation_response['snippet']['title']).to eq(updated_title)
expect(mutation_response['snippet']['description']).to eq(updated_description)
@@ -78,7 +78,7 @@ describe 'Updating a Snippet' do
it 'returns the Snippet with its original values' do
post_graphql_mutation(mutation, current_user: current_user)
- expect(mutation_response['snippet']['blob']['richData']).to match(original_content)
+ expect(mutation_response['snippet']['blob']['richData']).to be_nil
expect(mutation_response['snippet']['blob']['plainData']).to match(original_content)
expect(mutation_response['snippet']['title']).to eq(original_title)
expect(mutation_response['snippet']['description']).to eq(original_description)
diff --git a/spec/requests/api/lsif_data_spec.rb b/spec/requests/api/lsif_data_spec.rb
index 214bc832cda..a1516046e3e 100644
--- a/spec/requests/api/lsif_data_spec.rb
+++ b/spec/requests/api/lsif_data_spec.rb
@@ -9,18 +9,20 @@ describe API::LsifData do
let(:commit) { project.commit }
describe 'GET lsif/info' do
- let(:endpoint_path) { "/projects/#{project.id}/commits/#{commit.id}/lsif/info" }
+ subject do
+ endpoint_path = "/projects/#{project.id}/commits/#{commit.id}/lsif/info"
+
+ get api(endpoint_path, user), params: { paths: ['main.go', 'morestrings/reverse.go'] }
+
+ response
+ end
context 'user does not have access to the project' do
before do
project.add_guest(user)
end
- it 'returns 403' do
- get api(endpoint_path, user), params: { path: 'main.go' }
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
end
context 'user has access to the project' do
@@ -28,35 +30,27 @@ describe API::LsifData do
project.add_reporter(user)
end
- context 'code_navigation feature is disabled' do
- before do
- stub_feature_flags(code_navigation: false)
- end
-
- it 'returns 404' do
- get api(endpoint_path, user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
context 'there is no job artifact for the passed commit' do
- it 'returns 404' do
- get api(endpoint_path, user), params: { path: 'main.go' }
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
+ it { is_expected.to have_gitlab_http_status(:not_found) }
end
context 'lsif data is stored as a job artifact' do
let!(:pipeline) { create(:ci_pipeline, project: project, sha: commit.id) }
let!(:artifact) { create(:ci_job_artifact, :lsif, job: create(:ci_build, pipeline: pipeline)) }
- it 'returns code navigation info for a given path' do
- get api(endpoint_path, user), params: { path: 'main.go' }
+ context 'code_navigation feature is disabled' do
+ before do
+ stub_feature_flags(code_navigation: false)
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.parsed_body.last).to eq({
+ it { is_expected.to have_gitlab_http_status(:not_found) }
+ end
+
+ it 'returns code navigation info for a given path', :aggregate_failures do
+ expect(subject).to have_gitlab_http_status(:ok)
+
+ data_for_main = response.parsed_body['main.go']
+ expect(data_for_main.last).to eq({
'end_char' => 18,
'end_line' => 8,
'start_char' => 13,
@@ -67,26 +61,33 @@ describe API::LsifData do
'value' => Gitlab::Highlight.highlight(nil, 'func Func2(i int) string', language: 'go')
}]
})
+
+ data_for_reverse = response.parsed_body['morestrings/reverse.go']
+ expect(data_for_reverse.last).to eq({
+ 'end_char' => 9,
+ 'end_line' => 7,
+ 'start_char' => 8,
+ 'start_line' => 7,
+ 'definition_url' => project_blob_path(project, "#{commit.id}/morestrings/reverse.go", anchor: 'L6'),
+ 'hover' => [{
+ 'language' => 'go',
+ 'value' => Gitlab::Highlight.highlight(nil, 'var b string', language: 'go')
+ }]
+ })
end
context 'the stored file is too large' do
- it 'returns 413' do
+ before do
allow_any_instance_of(JobArtifactUploader).to receive(:cached_size).and_return(20.megabytes)
-
- get api(endpoint_path, user), params: { path: 'main.go' }
-
- expect(response).to have_gitlab_http_status(:payload_too_large)
end
+
+ it { is_expected.to have_gitlab_http_status(:payload_too_large) }
end
context 'the user does not have access to the pipeline' do
let(:project) { create(:project, :repository, builds_access_level: ProjectFeature::DISABLED) }
- it 'returns 403' do
- get api(endpoint_path, user), params: { path: 'main.go' }
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
end
end
end
diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb
index 1854d4db920..91905635c3f 100644
--- a/spec/requests/api/project_container_repositories_spec.rb
+++ b/spec/requests/api/project_container_repositories_spec.rb
@@ -109,7 +109,7 @@ describe API::ProjectContainerRepositories do
context 'disallowed' do
let(:params) do
- { name_regex: 'v10.*' }
+ { name_regex_delete: 'v10.*' }
end
it_behaves_like 'rejected container repository access', :developer, :forbidden
@@ -130,16 +130,33 @@ describe API::ProjectContainerRepositories do
end
end
+ context 'without name_regex' do
+ let(:params) do
+ { keep_n: 100,
+ older_than: '1 day',
+ other: 'some value' }
+ end
+
+ it 'returns bad request' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
context 'passes all declared parameters' do
let(:params) do
- { name_regex: 'v10.*',
+ { name_regex_delete: 'v10.*',
+ name_regex_keep: 'v10.1.*',
keep_n: 100,
older_than: '1 day',
other: 'some value' }
end
let(:worker_params) do
- { name_regex: 'v10.*',
+ { name_regex: nil,
+ name_regex_delete: 'v10.*',
+ name_regex_keep: 'v10.1.*',
keep_n: 100,
older_than: '1 day',
container_expiration_policy: false }
@@ -174,6 +191,38 @@ describe API::ProjectContainerRepositories do
end
end
end
+
+ context 'with deprecated name_regex param' do
+ let(:params) do
+ { name_regex: 'v10.*',
+ name_regex_keep: 'v10.1.*',
+ keep_n: 100,
+ older_than: '1 day',
+ other: 'some value' }
+ end
+
+ let(:worker_params) do
+ { name_regex: 'v10.*',
+ name_regex_delete: nil,
+ name_regex_keep: 'v10.1.*',
+ keep_n: 100,
+ older_than: '1 day',
+ container_expiration_policy: false }
+ end
+
+ let(:lease_key) { "container_repository:cleanup_tags:#{root_repository.id}" }
+
+ it 'schedules cleanup of tags repository' do
+ stub_last_activity_update
+ stub_exclusive_lease(lease_key, timeout: 1.hour)
+ expect(CleanupContainerRepositoryWorker).to receive(:perform_async)
+ .with(maintainer.id, root_repository.id, worker_params)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+ end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 2f1760a570e..59c394d8d8d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1751,6 +1751,27 @@ describe API::Projects do
subject { get api("/projects/#{project.id}", user) }
end
+
+ describe 'repository_storage attribute' do
+ before do
+ get api("/projects/#{project.id}", user)
+ end
+
+ context 'when authenticated as an admin' do
+ let(:user) { create(:admin) }
+
+ it 'returns repository_storage attribute' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['repository_storage']).to eq(project.repository_storage)
+ end
+ end
+
+ context 'when authenticated as a regular user' do
+ it 'does not return repository_storage attribute' do
+ expect(json_response).not_to have_key('repository_storage')
+ end
+ end
+ end
end
describe 'GET /projects/:id/users' do
@@ -2402,6 +2423,50 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
+
+ context 'when updating repository storage' do
+ let(:unknown_storage) { 'new-storage' }
+ let(:new_project) { create(:project, :repository, namespace: user.namespace) }
+
+ context 'as a user' do
+ it 'returns 200 but does not change repository_storage' do
+ expect do
+ Sidekiq::Testing.fake! do
+ put(api("/projects/#{new_project.id}", user), params: { repository_storage: unknown_storage, issues_enabled: false })
+ end
+ end.not_to change(ProjectUpdateRepositoryStorageWorker.jobs, :size)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['issues_enabled']).to eq(false)
+ expect(new_project.reload.repository.storage).to eq('default')
+ end
+ end
+
+ context 'as an admin' do
+ include_context 'custom session'
+
+ let(:admin) { create(:admin) }
+
+ it 'returns 500 when repository storage is unknown' do
+ put(api("/projects/#{new_project.id}", admin), params: { repository_storage: unknown_storage })
+
+ expect(response).to have_gitlab_http_status(:internal_server_error)
+ expect(json_response['message']).to match('ArgumentError')
+ end
+
+ it 'returns 200 when repository storage has changed' do
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/second_storage' })
+
+ expect do
+ Sidekiq::Testing.fake! do
+ put(api("/projects/#{new_project.id}", admin), params: { repository_storage: 'test_second_storage' })
+ end
+ end.to change(ProjectUpdateRepositoryStorageWorker.jobs, :size).by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
end
describe 'POST /projects/:id/archive' do
diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
index ef7e9cda9e0..96cddef4628 100644
--- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
@@ -48,25 +48,37 @@ describe Projects::ContainerRepository::CleanupTagsService do
end
context 'when regex matching everything is specified' do
+ shared_examples 'removes all matches' do
+ it 'does remove B* and C' do
+ # The :A cannot be removed as config is shared with :latest
+ # The :E cannot be removed as it does not have valid manifest
+
+ expect_delete('sha256:configB').twice
+ expect_delete('sha256:configC')
+ expect_delete('sha256:configD')
+
+ is_expected.to include(status: :success, deleted: %w(D Bb Ba C))
+ end
+ end
+
let(:params) do
- { 'name_regex' => '.*' }
+ { 'name_regex_delete' => '.*' }
end
- it 'does remove B* and C' do
- # The :A cannot be removed as config is shared with :latest
- # The :E cannot be removed as it does not have valid manifest
+ it_behaves_like 'removes all matches'
- expect_delete('sha256:configB').twice
- expect_delete('sha256:configC')
- expect_delete('sha256:configD')
+ context 'with deprecated name_regex param' do
+ let(:params) do
+ { 'name_regex' => '.*' }
+ end
- is_expected.to include(status: :success, deleted: %w(D Bb Ba C))
+ it_behaves_like 'removes all matches'
end
end
- context 'when regex matching specific tags is used' do
+ context 'when delete regex matching specific tags is used' do
let(:params) do
- { 'name_regex' => 'C|D' }
+ { 'name_regex_delete' => 'C|D' }
end
it 'does remove C and D' do
@@ -75,11 +87,37 @@ describe Projects::ContainerRepository::CleanupTagsService do
is_expected.to include(status: :success, deleted: %w(D C))
end
+
+ context 'with overriding allow regex' do
+ let(:params) do
+ { 'name_regex_delete' => 'C|D',
+ 'name_regex_keep' => 'C' }
+ end
+
+ it 'does not remove C' do
+ expect_delete('sha256:configD')
+
+ is_expected.to include(status: :success, deleted: %w(D))
+ end
+ end
+
+ context 'with name_regex_delete overriding deprecated name_regex' do
+ let(:params) do
+ { 'name_regex' => 'C|D',
+ 'name_regex_delete' => 'D' }
+ end
+
+ it 'does not remove C' do
+ expect_delete('sha256:configD')
+
+ is_expected.to include(status: :success, deleted: %w(D))
+ end
+ end
end
context 'when removing a tagged image that is used by another tag' do
let(:params) do
- { 'name_regex' => 'Ba' }
+ { 'name_regex_delete' => 'Ba' }
end
it 'does not remove the tag' do
@@ -89,9 +127,23 @@ describe Projects::ContainerRepository::CleanupTagsService do
end
end
+ context 'with allow regex value' do
+ let(:params) do
+ { 'name_regex_delete' => '.*',
+ 'name_regex_keep' => 'B.*' }
+ end
+
+ it 'does not remove B*' do
+ expect_delete('sha256:configC')
+ expect_delete('sha256:configD')
+
+ is_expected.to include(status: :success, deleted: %w(D C))
+ end
+ end
+
context 'when removing keeping only 3' do
let(:params) do
- { 'name_regex' => '.*',
+ { 'name_regex_delete' => '.*',
'keep_n' => 3 }
end
@@ -104,7 +156,7 @@ describe Projects::ContainerRepository::CleanupTagsService do
context 'when removing older than 1 day' do
let(:params) do
- { 'name_regex' => '.*',
+ { 'name_regex_delete' => '.*',
'older_than' => '1 day' }
end
@@ -118,7 +170,7 @@ describe Projects::ContainerRepository::CleanupTagsService do
context 'when combining all parameters' do
let(:params) do
- { 'name_regex' => '.*',
+ { 'name_regex_delete' => '.*',
'keep_n' => 1,
'older_than' => '1 day' }
end
@@ -136,7 +188,7 @@ describe Projects::ContainerRepository::CleanupTagsService do
context 'with valid container_expiration_policy param' do
let(:params) do
- { 'name_regex' => '.*',
+ { 'name_regex_delete' => '.*',
'keep_n' => 1,
'older_than' => '1 day',
'container_expiration_policy' => true }
@@ -152,7 +204,7 @@ describe Projects::ContainerRepository::CleanupTagsService do
context 'without container_expiration_policy param' do
let(:params) do
- { 'name_regex' => '.*',
+ { 'name_regex_delete' => '.*',
'keep_n' => 1,
'older_than' => '1 day' }
end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index fa6d42369c8..12c51d01d63 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -307,6 +307,27 @@ describe Projects::ForkService do
end
end
+ context 'when a project is already forked' do
+ it 'creates a new poolresository after the project is moved to a new shard' do
+ project = create(:project, :public, :repository)
+ fork_before_move = fork_project(project)
+
+ # Stub everything required to move a project to a Gitaly shard that does not exist
+ stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/second_storage' })
+ allow_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror).and_return(true)
+
+ Projects::UpdateRepositoryStorageService.new(project).execute('test_second_storage')
+ fork_after_move = fork_project(project)
+ pool_repository_before_move = PoolRepository.joins(:shard)
+ .where(source_project: project, shards: { name: 'default' }).first
+ pool_repository_after_move = PoolRepository.joins(:shard)
+ .where(source_project: project, shards: { name: 'test_second_storage' }).first
+
+ expect(fork_before_move.pool_repository).to eq(pool_repository_before_move)
+ expect(fork_after_move.pool_repository).to eq(pool_repository_after_move)
+ end
+ end
+
context 'when forking with object pools' do
let(:fork_from_project) { create(:project, :public) }
let(:forker) { create(:user) }
diff --git a/spec/services/projects/lsif_data_service_spec.rb b/spec/services/projects/lsif_data_service_spec.rb
index 93579869d1d..4866f848121 100644
--- a/spec/services/projects/lsif_data_service_spec.rb
+++ b/spec/services/projects/lsif_data_service_spec.rb
@@ -7,9 +7,8 @@ describe Projects::LsifDataService do
let(:project) { build_stubbed(:project) }
let(:path) { 'main.go' }
let(:commit_id) { Digest::SHA1.hexdigest(SecureRandom.hex) }
- let(:params) { { path: path, commit_id: commit_id } }
- let(:service) { described_class.new(artifact.file, project, params) }
+ let(:service) { described_class.new(artifact.file, project, commit_id) }
describe '#execute' do
def highlighted_value(value)
@@ -18,7 +17,7 @@ describe Projects::LsifDataService do
context 'fetched lsif file', :use_clean_rails_memory_store_caching do
it 'is cached' do
- service.execute
+ service.execute(path)
cached_data = Rails.cache.fetch("project:#{project.id}:lsif:#{commit_id}")
@@ -30,7 +29,7 @@ describe Projects::LsifDataService do
let(:path_prefix) { "/#{project.full_path}/-/blob/#{commit_id}" }
it 'returns lsif ranges for the file' do
- expect(service.execute).to eq([
+ expect(service.execute(path)).to eq([
{
end_char: 9,
end_line: 6,
@@ -87,7 +86,7 @@ describe Projects::LsifDataService do
let(:path) { 'morestrings/reverse.go' }
it 'returns lsif ranges for the file' do
- expect(service.execute.first).to eq({
+ expect(service.execute(path).first).to eq({
end_char: 2,
end_line: 11,
start_char: 1,
@@ -102,7 +101,7 @@ describe Projects::LsifDataService do
let(:path) { 'unknown.go' }
it 'returns nil' do
- expect(service.execute).to eq(nil)
+ expect(service.execute(path)).to eq(nil)
end
end
end
@@ -120,9 +119,7 @@ describe Projects::LsifDataService do
end
it 'fetches the document with the shortest absolute path' do
- service.instance_variable_set(:@docs, docs)
-
- expect(service.__send__(:doc_id)).to eq(3)
+ expect(service.__send__(:find_doc_id, docs, path)).to eq(3)
end
end
end
diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb
new file mode 100644
index 00000000000..a0917f718e6
--- /dev/null
+++ b/spec/services/projects/update_repository_storage_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::UpdateRepositoryStorageService do
+ include Gitlab::ShellAdapter
+
+ subject { described_class.new(project) }
+
+ describe "#execute" do
+ let(:time) { Time.now }
+
+ before do
+ allow(Time).to receive(:now).and_return(time)
+ end
+
+ context 'without wiki and design repository' do
+ let(:project) { create(:project, :repository, repository_read_only: true, wiki_enabled: false) }
+
+ context 'when the move succeeds' do
+ it 'moves the repository to the new storage and unmarks the repository as read only' do
+ old_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+
+ expect_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror)
+ .with(project.repository.raw).and_return(true)
+
+ subject.execute('test_second_storage')
+ expect(project).not_to be_repository_read_only
+ expect(project.repository_storage).to eq('test_second_storage')
+ expect(gitlab_shell.repository_exists?('default', old_path)).to be(false)
+ expect(project.project_repository.shard_name).to eq('test_second_storage')
+ end
+ end
+
+ context 'when the project is already on the target storage' do
+ it 'bails out and does nothing' do
+ expect do
+ subject.execute(project.repository_storage)
+ end.to raise_error(described_class::RepositoryAlreadyMoved)
+ end
+ end
+
+ context 'when the move fails' do
+ it 'unmarks the repository as read-only without updating the repository storage' do
+ expect_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror)
+ .with(project.repository.raw).and_return(false)
+ expect(GitlabShellWorker).not_to receive(:perform_async)
+
+ subject.execute('test_second_storage')
+
+ expect(project).not_to be_repository_read_only
+ expect(project.repository_storage).to eq('default')
+ end
+ end
+ end
+
+ context 'with wiki repository' do
+ include_examples 'moves repository to another storage', 'wiki' do
+ let(:project) { create(:project, :repository, repository_read_only: true, wiki_enabled: true) }
+ let(:repository) { project.wiki.repository }
+
+ before do
+ project.create_wiki
+ end
+ end
+ end
+
+ context 'when a object pool was joined' do
+ let(:project) { create(:project, :repository, wiki_enabled: false, repository_read_only: true) }
+ let(:pool) { create(:pool_repository, :ready, source_project: project) }
+
+ it 'leaves the pool' do
+ allow_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror).and_return(true)
+
+ subject.execute('test_second_storage')
+
+ expect(project.reload_pool_repository).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 90fb6b932ee..8b5b6e5ac4e 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -613,6 +613,25 @@ describe Projects::UpdateService do
end
end
+ describe 'repository_storage' do
+ let(:admin) { create(:admin) }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:opts) { { repository_storage: 'test_second_storage' } }
+
+ it 'calls the change repository storage method if the storage changed' do
+ expect(project).to receive(:change_repository_storage).with('test_second_storage')
+
+ update_project(project, admin, opts).inspect
+ end
+
+ it "doesn't call the change repository storage for non-admin users" do
+ expect(project).not_to receive(:change_repository_storage)
+
+ update_project(project, user, opts).inspect
+ end
+ end
+
def update_project(project, user, opts)
described_class.new(project, user, opts).execute
end
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
new file mode 100644
index 00000000000..f222dff60ab
--- /dev/null
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'moves repository to another storage' do |repository_type|
+ let(:project_repository_double) { double(:repository) }
+ let(:repository_double) { double(:repository) }
+
+ before do
+ # Default stub for non-specified params
+ allow(Gitlab::Git::Repository).to receive(:new).and_call_original
+
+ allow(Gitlab::Git::Repository).to receive(:new)
+ .with('test_second_storage', project.repository.raw.relative_path, project.repository.gl_repository, project.repository.full_path)
+ .and_return(project_repository_double)
+
+ allow(Gitlab::Git::Repository).to receive(:new)
+ .with('test_second_storage', repository.raw.relative_path, repository.gl_repository, repository.full_path)
+ .and_return(repository_double)
+ end
+
+ context 'when the move succeeds', :clean_gitlab_redis_shared_state do
+ before do
+ allow(project_repository_double)
+ .to receive(:fetch_repository_as_mirror)
+ .with(project.repository.raw)
+ .and_return(true)
+
+ allow(repository_double)
+ .to receive(:fetch_repository_as_mirror)
+ .with(repository.raw)
+ .and_return(true)
+ end
+
+ it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read only" do
+ old_project_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+
+ old_repository_path = repository.full_path
+
+ subject.execute('test_second_storage')
+
+ expect(project).not_to be_repository_read_only
+ expect(project.repository_storage).to eq('test_second_storage')
+ expect(gitlab_shell.repository_exists?('default', old_project_repository_path)).to be(false)
+ expect(gitlab_shell.repository_exists?('default', old_repository_path)).to be(false)
+ end
+
+ context ':repack_after_shard_migration feature flag disabled' do
+ before do
+ stub_feature_flags(repack_after_shard_migration: false)
+ end
+
+ it 'does not enqueue a GC run' do
+ expect { subject.execute('test_second_storage') }
+ .not_to change(GitGarbageCollectWorker.jobs, :count)
+ end
+ end
+
+ context ':repack_after_shard_migration feature flag enabled' do
+ before do
+ stub_feature_flags(repack_after_shard_migration: true)
+ end
+
+ it 'does not enqueue a GC run if housekeeping is disabled' do
+ stub_application_setting(housekeeping_enabled: false)
+
+ expect { subject.execute('test_second_storage') }
+ .not_to change(GitGarbageCollectWorker.jobs, :count)
+ end
+
+ it 'enqueues a GC run' do
+ expect { subject.execute('test_second_storage') }
+ .to change(GitGarbageCollectWorker.jobs, :count).by(1)
+ end
+ end
+ end
+
+ context 'when the project is already on the target storage' do
+ it 'bails out and does nothing' do
+ expect do
+ subject.execute(project.repository_storage)
+ end.to raise_error(described_class::RepositoryAlreadyMoved)
+ end
+ end
+
+ context "when the move of the #{repository_type} repository fails" do
+ it 'unmarks the repository as read-only without updating the repository storage' do
+ allow(project_repository_double).to receive(:fetch_repository_as_mirror)
+ .with(project.repository.raw).and_return(true)
+ allow(repository_double).to receive(:fetch_repository_as_mirror)
+ .with(repository.raw).and_return(false)
+
+ expect(GitlabShellWorker).not_to receive(:perform_async)
+
+ subject.execute('test_second_storage')
+
+ expect(project).not_to be_repository_read_only
+ expect(project.repository_storage).to eq('default')
+ end
+ end
+end
diff --git a/spec/workers/project_update_repository_storage_worker_spec.rb b/spec/workers/project_update_repository_storage_worker_spec.rb
new file mode 100644
index 00000000000..aa6545f7f89
--- /dev/null
+++ b/spec/workers/project_update_repository_storage_worker_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectUpdateRepositoryStorageWorker do
+ let(:project) { create(:project, :repository) }
+
+ subject { described_class.new }
+
+ describe "#perform" do
+ it "calls the update repository storage service" do
+ expect_any_instance_of(Projects::UpdateRepositoryStorageService)
+ .to receive(:execute).with('new_storage')
+
+ subject.perform(project.id, 'new_storage')
+ end
+
+ it 'catches and logs RepositoryAlreadyMoved' do
+ expect(Rails.logger).to receive(:info).with(/repository already moved/)
+
+ expect { subject.perform(project.id, project.repository_storage) }.not_to raise_error
+ end
+ end
+end