summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/backup/repository_spec.rb40
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb14
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb58
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb12
-rw-r--r--spec/lib/banzai/pipeline/emoji_pipeline_spec.rb21
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb88
-rw-r--r--spec/lib/bitbucket_server/connection_spec.rb68
-rw-r--r--spec/lib/bitbucket_server/page_spec.rb51
-rw-r--r--spec/lib/bitbucket_server/paginator_spec.rb35
-rw-r--r--spec/lib/bitbucket_server/representation/activity_spec.rb38
-rw-r--r--spec/lib/bitbucket_server/representation/comment_spec.rb55
-rw-r--r--spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb48
-rw-r--r--spec/lib/bitbucket_server/representation/pull_request_spec.rb79
-rw-r--r--spec/lib/bitbucket_server/representation/repo_spec.rb80
-rw-r--r--spec/lib/extracts_path_spec.rb26
-rw-r--r--spec/lib/feature_spec.rb32
-rw-r--r--spec/lib/gitlab/auth/activity_spec.rb30
-rw-r--r--spec/lib/gitlab/auth/blocked_user_tracker_spec.rb68
-rw-r--r--spec/lib/gitlab/background_migration/delete_diff_files_spec.rb62
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb22
-rw-r--r--spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb109
-rw-r--r--spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb124
-rw-r--r--spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb43
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb54
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb75
-rw-r--r--spec/lib/gitlab/bitbucket_server_import/importer_spec.rb291
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/config/entry/commands_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb53
-rw-r--r--spec/lib/gitlab/ci/parsers/junit_spec.rb118
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/reports/test_case_spec.rb90
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb134
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_spec.rb132
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb225
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_spec.rb120
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb8
-rw-r--r--spec/lib/gitlab/cleanup/project_uploads_spec.rb278
-rw-r--r--spec/lib/gitlab/cleanup/remote_uploads_spec.rb74
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb18
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb4
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb115
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb9
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb15
-rw-r--r--spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb (renamed from spec/lib/additional_email_headers_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb35
-rw-r--r--spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb (renamed from spec/lib/disable_email_interceptor_spec.rb)2
-rw-r--r--spec/lib/gitlab/exclusive_lease_helpers_spec.rb76
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb64
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blame_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_snippet_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb173
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb10
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb20
-rw-r--r--spec/lib/gitlab/git/committer_with_hooks_spec.rb2
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb11
-rw-r--r--spec/lib/gitlab/git/hooks_service_spec.rb2
-rw-r--r--spec/lib/gitlab/git/index_spec.rb18
-rw-r--r--spec/lib/gitlab/git/popen_spec.rb30
-rw-r--r--spec/lib/gitlab/git/remote_repository_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb729
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb96
-rw-r--r--spec/lib/gitlab/git/tag_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb25
-rw-r--r--spec/lib/gitlab/git_access_spec.rb64
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb175
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb38
-rw-r--r--spec/lib/gitlab/gitaly_client/storage_service_spec.rb13
-rw-r--r--spec/lib/gitlab/gitaly_client/wiki_service_spec.rb22
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb7
-rw-r--r--spec/lib/gitlab/gitlab_import/client_spec.rb84
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb103
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb112
-rw-r--r--spec/lib/gitlab/graphs/commits_spec.rb2
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb64
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb9
-rw-r--r--spec/lib/gitlab/http_io_spec.rb (renamed from spec/lib/gitlab/ci/trace/http_io_spec.rb)43
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb105
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb31
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml6
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb89
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb11
-rw-r--r--spec/lib/gitlab/import_export/group_project_object_builder_spec.rb52
-rw-r--r--spec/lib/gitlab/import_export/importer_object_storage_spec.rb115
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project.light.json8
-rw-r--r--spec/lib/gitlab/import_export/project.milestone-iid.json80
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb84
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb43
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb80
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb33
-rw-r--r--spec/lib/gitlab/json_logger_spec.rb29
-rw-r--r--spec/lib/gitlab/kubernetes/config_map_spec.rb10
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb14
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb28
-rw-r--r--spec/lib/gitlab/kubernetes/helm/certificate_spec.rb28
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb4
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb69
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb29
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb15
-rw-r--r--spec/lib/gitlab/language_detection_spec.rb85
-rw-r--r--spec/lib/gitlab/manifest_import/manifest_spec.rb46
-rw-r--r--spec/lib/gitlab/manifest_import/project_creator_spec.rb33
-rw-r--r--spec/lib/gitlab/middleware/basic_health_check_spec.rb57
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb86
-rw-r--r--spec/lib/gitlab/middleware/read_only_spec.rb77
-rw-r--r--spec/lib/gitlab/popen_spec.rb13
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb6
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb55
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb12
-rw-r--r--spec/lib/gitlab/sanitizers/svg_spec.rb4
-rw-r--r--spec/lib/gitlab/serializer/ci/variables_spec.rb6
-rw-r--r--spec/lib/gitlab/shell_spec.rb105
-rw-r--r--spec/lib/gitlab/slash_commands/issue_move_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb2
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb7
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/user_access_spec.rb40
-rw-r--r--spec/lib/gitlab/user_activities_spec.rb127
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb152
-rw-r--r--spec/lib/uploaded_file_spec.rb37
154 files changed, 5742 insertions, 1498 deletions
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index 92a27e308d2..c5a854b5660 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -73,37 +73,27 @@ describe Backup::Repository do
end
end
- describe '#delete_all_repositories', :seed_helper do
- shared_examples('delete_all_repositories') do
- before do
- allow(FileUtils).to receive(:mkdir_p).and_call_original
- allow(FileUtils).to receive(:mv).and_call_original
- end
-
- after(:all) do
- ensure_seeds
- end
-
- it 'removes all repositories' do
- # Sanity check: there should be something for us to delete
- expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
+ describe '#prepare_directories', :seed_helper do
+ before do
+ allow(FileUtils).to receive(:mkdir_p).and_call_original
+ allow(FileUtils).to receive(:mv).and_call_original
+ end
- subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default'])
+ after(:all) do
+ ensure_seeds
+ end
- expect(list_repositories).to be_empty
- end
+ it' removes all repositories' do
+ # Sanity check: there should be something for us to delete
+ expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
- def list_repositories
- Dir[File.join(SEED_STORAGE_PATH, '*.git')]
- end
- end
+ subject.prepare_directories
- context 'with gitaly' do
- it_behaves_like 'delete_all_repositories'
+ expect(list_repositories).to be_empty
end
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like 'delete_all_repositories'
+ def list_repositories
+ Dir[File.join(SEED_STORAGE_PATH, '*.git')]
end
end
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index 41f957c4e00..d06c5535309 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do
%(<img src="#{path}" />)
end
+ def image_with_class(path, class_attr = nil)
+ %(<img src="#{path}" class="#{class_attr}"/>)
+ end
+
+ it 'adds a class attribute' do
+ doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+ expect(doc.at_css('img')['class']).to eq 'lazy'
+ end
+
+ it 'appends to the current class attribute' do
+ doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test'))
+ expect(doc.at_css('img')['class']).to eq 'test lazy'
+ end
+
it 'transforms the image src to a data-src' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index ab14d77d552..a515d07b072 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -3,17 +3,61 @@ require 'spec_helper'
describe Banzai::Filter::MarkdownFilter do
include FilterSpecHelper
- context 'code block' do
- it 'adds language to lang attribute when specified' do
- result = filter("```html\nsome code\n```")
+ describe 'markdown engine from context' do
+ it 'defaults to CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code lang=\"html\">")
+ filter('test')
end
- it 'does not add language to lang attribute when not specified' do
- result = filter("```\nsome code\n```")
+ it 'uses Redcarpet' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::Redcarpet).to receive(:render).and_return('test')
- expect(result).to start_with("<pre><code>")
+ filter('test', { markdown_engine: :redcarpet })
+ end
+
+ it 'uses CommonMark' do
+ expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
+
+ filter('test', { markdown_engine: :common_mark })
+ end
+ end
+
+ describe 'code block' do
+ context 'using CommonMark' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("<pre><code>")
+ end
+ end
+
+ context 'using Redcarpet' do
+ before do
+ stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :redcarpet)
+ end
+
+ it 'adds language to lang attribute when specified' do
+ result = filter("```html\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code lang=\"html\">")
+ end
+
+ it 'does not add language to lang attribute when not specified' do
+ result = filter("```\nsome code\n```")
+
+ expect(result).to start_with("\n<pre><code>")
+ end
end
end
end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index a1dd72c498f..55c41e55437 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -210,6 +210,13 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
.to eq reference
end
+ it 'commit ref tag is valid' do
+ doc = reference_filter("See #{reference}")
+ commit_ref_tag = doc.css('a').first.css('span.gfm.gfm-commit')
+
+ expect(commit_ref_tag.text).to eq(commit.short_id)
+ end
+
it 'has valid text' do
doc = reference_filter("See #{reference}")
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 9a2e521fdcf..919825a6102 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do
it 'allows permitted Project references' do
user = create(:user)
project = create(:project)
- project.add_master(user)
+ project.add_maintainer(user)
link = reference_link(project: project.id, reference_type: 'test')
doc = filter(link, current_user: user)
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index d930c608b18..0b3c2390304 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -54,6 +54,18 @@ describe Banzai::Filter::SanitizationFilter do
expect(instance.whitelist[:transformers].size).to eq control_count
end
+ it 'customizes the whitelist only once for different instances' do
+ instance1 = described_class.new('Foo1')
+ instance2 = described_class.new('Foo2')
+ control_count = instance1.whitelist[:transformers].size
+
+ instance1.whitelist
+ instance2.whitelist
+
+ expect(instance1.whitelist[:transformers].size).to eq control_count
+ expect(instance2.whitelist[:transformers].size).to eq control_count
+ end
+
it 'sanitizes `class` attribute from all elements' do
act = %q{<pre class="code highlight white c"><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
exp = %q{<pre><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
diff --git a/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
new file mode 100644
index 00000000000..744df3e0b96
--- /dev/null
+++ b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Pipeline::EmojiPipeline do
+ def parse(text)
+ described_class.to_html(text, {})
+ end
+
+ it 'replaces emoji' do
+ expected_result = "Hello world #{Gitlab::Emoji.gl_emoji_tag('100')}"
+
+ expect(parse('Hello world :100:')).to eq(expected_result)
+ end
+
+ it 'filters out HTML tags' do
+ expected_result = "Hello &lt;b&gt;world&lt;/b&gt; #{Gitlab::Emoji.gl_emoji_tag('100')}"
+
+ expect(parse('Hello <b>world</b> :100:')).to eq(expected_result)
+ end
+end
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
new file mode 100644
index 00000000000..f926ae963a4
--- /dev/null
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe BitbucketServer::Client do
+ let(:base_uri) { 'https://test:7990/stash/' }
+ let(:options) { { base_uri: base_uri, user: 'bitbucket', password: 'mypassword' } }
+ let(:project) { 'SOME-PROJECT' }
+ let(:repo_slug) { 'my-repo' }
+ let(:headers) { { "Content-Type" => "application/json" } }
+
+ subject { described_class.new(options) }
+
+ describe '#pull_requests' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests?state=ALL" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :pull_request)
+
+ subject.pull_requests(project, repo_slug)
+ end
+
+ it 'throws an exception when connection fails' do
+ allow(BitbucketServer::Collection).to receive(:new).and_raise(OpenSSL::SSL::SSLError)
+
+ expect { subject.pull_requests(project, repo_slug) }.to raise_error(described_class::ServerError)
+ end
+ end
+
+ describe '#activities' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}/pull-requests/1/activities" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :activity)
+
+ subject.activities(project, repo_slug, 1)
+ end
+ end
+
+ describe '#repo' do
+ let(:path) { "/projects/#{project}/repos/#{repo_slug}" }
+ let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo" }
+
+ it 'requests a specific repository' do
+ stub_request(:get, url).to_return(status: 200, headers: headers, body: '{}')
+
+ subject.repo(project, repo_slug)
+
+ expect(WebMock).to have_requested(:get, url)
+ end
+ end
+
+ describe '#repos' do
+ let(:path) { "/repos" }
+
+ it 'requests a collection' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, path, :repo)
+
+ subject.repos
+ end
+ end
+
+ describe '#create_branch' do
+ let(:branch) { 'test-branch' }
+ let(:sha) { '12345678' }
+ let(:url) { "#{base_uri}rest/api/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
+
+ it 'requests Bitbucket to create a branch' do
+ stub_request(:post, url).to_return(status: 204, headers: headers, body: '{}')
+
+ subject.create_branch(project, repo_slug, branch, sha)
+
+ expect(WebMock).to have_requested(:post, url)
+ end
+ end
+
+ describe '#delete_branch' do
+ let(:branch) { 'test-branch' }
+ let(:sha) { '12345678' }
+ let(:url) { "#{base_uri}rest/branch-utils/1.0/projects/SOME-PROJECT/repos/my-repo/branches" }
+
+ it 'requests Bitbucket to create a branch' do
+ stub_request(:delete, url).to_return(status: 204, headers: headers, body: '{}')
+
+ subject.delete_branch(project, repo_slug, branch, sha)
+
+ expect(WebMock).to have_requested(:delete, url)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/connection_spec.rb b/spec/lib/bitbucket_server/connection_spec.rb
new file mode 100644
index 00000000000..b5da4cb1a49
--- /dev/null
+++ b/spec/lib/bitbucket_server/connection_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe BitbucketServer::Connection do
+ let(:options) { { base_uri: 'https://test:7990', user: 'bitbucket', password: 'mypassword' } }
+ let(:payload) { { 'test' => 1 } }
+ let(:headers) { { "Content-Type" => "application/json" } }
+ let(:url) { 'https://test:7990/rest/api/1.0/test?something=1' }
+
+ subject { described_class.new(options) }
+
+ describe '#get' do
+ it 'returns JSON body' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.get(url, { something: 1 })).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
+ end
+
+ it 'throws an exception if the response is not JSON' do
+ WebMock.stub_request(:get, url).with(headers: { 'Accept' => 'application/json' }).to_return(body: 'bad data', status: 200, headers: headers)
+
+ expect { subject.get(url) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+
+ describe '#post' do
+ let(:headers) { { 'Accept' => 'application/json', 'Content-Type' => 'application/json' } }
+
+ it 'returns JSON body' do
+ WebMock.stub_request(:post, url).with(headers: headers).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.post(url, payload)).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:post, url).with(headers: headers).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.post(url, payload) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+
+ describe '#delete' do
+ let(:headers) { { 'Accept' => 'application/json', 'Content-Type' => 'application/json' } }
+
+ context 'branch API' do
+ let(:branch_path) { '/projects/foo/repos/bar/branches' }
+ let(:branch_url) { 'https://test:7990/rest/branch-utils/1.0/projects/foo/repos/bar/branches' }
+ let(:path) { }
+
+ it 'returns JSON body' do
+ WebMock.stub_request(:delete, branch_url).with(headers: headers).to_return(body: payload.to_json, status: 200, headers: headers)
+
+ expect(subject.delete(:branches, branch_path, payload)).to eq(payload)
+ end
+
+ it 'throws an exception if the response is not 200' do
+ WebMock.stub_request(:delete, branch_url).with(headers: headers).to_return(body: payload.to_json, status: 500, headers: headers)
+
+ expect { subject.delete(:branches, branch_path, payload) }.to raise_error(described_class::ConnectionError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/page_spec.rb b/spec/lib/bitbucket_server/page_spec.rb
new file mode 100644
index 00000000000..cf419a9045b
--- /dev/null
+++ b/spec/lib/bitbucket_server/page_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe BitbucketServer::Page do
+ let(:response) { { 'values' => [{ 'description' => 'Test' }], 'isLastPage' => false, 'nextPageStart' => 2 } }
+
+ before do
+ # Autoloading hack
+ BitbucketServer::Representation::PullRequest.new({})
+ end
+
+ describe '#items' do
+ it 'returns collection of needed objects' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.items.first).to be_a(BitbucketServer::Representation::PullRequest)
+ expect(page.items.count).to eq(1)
+ end
+ end
+
+ describe '#attrs' do
+ it 'returns attributes' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.attrs.keys).to include(:isLastPage, :nextPageStart)
+ end
+ end
+
+ describe '#next?' do
+ it 'returns true' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next?).to be_truthy
+ end
+
+ it 'returns false' do
+ response['isLastPage'] = true
+ response.delete('nextPageStart')
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next?).to be_falsey
+ end
+ end
+
+ describe '#next' do
+ it 'returns next attribute' do
+ page = described_class.new(response, :pull_request)
+
+ expect(page.next).to eq(2)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/paginator_spec.rb b/spec/lib/bitbucket_server/paginator_spec.rb
new file mode 100644
index 00000000000..2de50eba3c4
--- /dev/null
+++ b/spec/lib/bitbucket_server/paginator_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe BitbucketServer::Paginator do
+ let(:last_page) { double(:page, next?: false, items: ['item_2']) }
+ let(:first_page) { double(:page, next?: true, next: last_page, items: ['item_1']) }
+ let(:connection) { instance_double(BitbucketServer::Connection) }
+
+ describe '#items' do
+ let(:paginator) { described_class.new(connection, 'http://more-data', :pull_request) }
+ let(:page_attrs) { { 'isLastPage' => false, 'nextPageStart' => 1 } }
+
+ it 'returns items and raises StopIteration in the end' do
+ allow(paginator).to receive(:fetch_next_page).and_return(first_page)
+ expect(paginator.items).to match(['item_1'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(last_page)
+ expect(paginator.items).to match(['item_2'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(nil)
+ expect { paginator.items }.to raise_error(StopIteration)
+ end
+
+ it 'calls the connection with different offsets' do
+ expect(connection).to receive(:get).with('http://more-data', start: 0, limit: BitbucketServer::Paginator::PAGE_LENGTH).and_return(page_attrs)
+
+ expect(paginator.items).to eq([])
+
+ expect(connection).to receive(:get).with('http://more-data', start: 1, limit: BitbucketServer::Paginator::PAGE_LENGTH).and_return({})
+
+ expect(paginator.items).to eq([])
+
+ expect { paginator.items }.to raise_error(StopIteration)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/activity_spec.rb b/spec/lib/bitbucket_server/representation/activity_spec.rb
new file mode 100644
index 00000000000..15c50e40472
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/activity_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Activity do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:inline_comment) { activities.first }
+ let(:comment) { activities[3] }
+ let(:merge_event) { activities[4] }
+
+ describe 'regular comment' do
+ subject { described_class.new(comment) }
+
+ it { expect(subject.comment?).to be_truthy }
+ it { expect(subject.inline_comment?).to be_falsey }
+ it { expect(subject.comment).to be_a(BitbucketServer::Representation::Comment) }
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe 'inline comment' do
+ subject { described_class.new(inline_comment) }
+
+ it { expect(subject.comment?).to be_truthy }
+ it { expect(subject.inline_comment?).to be_truthy }
+ it { expect(subject.comment).to be_a(BitbucketServer::Representation::PullRequestComment) }
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe 'merge event' do
+ subject { described_class.new(merge_event) }
+
+ it { expect(subject.comment?).to be_falsey }
+ it { expect(subject.inline_comment?).to be_falsey }
+ it { expect(subject.committer_user).to eq('root') }
+ it { expect(subject.committer_email).to eq('test.user@example.com') }
+ it { expect(subject.merge_timestamp).to be_a(Time) }
+ it { expect(subject.created_at).to be_a(Time) }
+ it { expect(subject.merge_commit).to eq('839fa9a2d434eb697815b8fcafaecc51accfdbbc') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/comment_spec.rb b/spec/lib/bitbucket_server/representation/comment_spec.rb
new file mode 100644
index 00000000000..53a20a1d80a
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/comment_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Comment do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:comment) { activities.first }
+
+ subject { described_class.new(comment) }
+
+ describe '#id' do
+ it { expect(subject.id).to eq(9) }
+ end
+
+ describe '#author_username' do
+ it { expect(subject.author_username).to eq('root' ) }
+ end
+
+ describe '#author_email' do
+ it { expect(subject.author_email).to eq('test.user@example.com' ) }
+ end
+
+ describe '#note' do
+ it { expect(subject.note).to eq('is this a new line?') }
+ end
+
+ describe '#created_at' do
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe '#updated_at' do
+ it { expect(subject.created_at).to be_a(Time) }
+ end
+
+ describe '#comments' do
+ it { expect(subject.comments.count).to eq(4) }
+ it { expect(subject.comments).to all( be_a(described_class) ) }
+ it { expect(subject.comments.map(&:note)).to match_array(["Hello world", "Ok", "hello", "hi"]) }
+
+ # The thread should look like:
+ #
+ # is this a new line? (subject)
+ # -> Hello world (first)
+ # -> Ok (third)
+ # -> Hi (fourth)
+ # -> hello (second)
+ it 'comments have the right parent' do
+ first, second, third, fourth = subject.comments[0..4]
+
+ expect(subject.parent_comment).to be_nil
+ expect(first.parent_comment).to eq(subject)
+ expect(second.parent_comment).to eq(subject)
+ expect(third.parent_comment).to eq(first)
+ expect(fourth.parent_comment).to eq(first)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb
new file mode 100644
index 00000000000..bd7e3597486
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::PullRequestComment do
+ let(:activities) { JSON.parse(fixture_file('importers/bitbucket_server/activities.json'))['values'] }
+ let(:comment) { activities.second }
+
+ subject { described_class.new(comment) }
+
+ describe '#id' do
+ it { expect(subject.id).to eq(7) }
+ end
+
+ describe '#from_sha' do
+ it { expect(subject.from_sha).to eq('c5f4288162e2e6218180779c7f6ac1735bb56eab') }
+ end
+
+ describe '#to_sha' do
+ it { expect(subject.to_sha).to eq('a4c2164330f2549f67c13f36a93884cf66e976be') }
+ end
+
+ describe '#to?' do
+ it { expect(subject.to?).to be_falsey }
+ end
+
+ describe '#from?' do
+ it { expect(subject.from?).to be_truthy }
+ end
+
+ describe '#added?' do
+ it { expect(subject.added?).to be_falsey }
+ end
+
+ describe '#removed?' do
+ it { expect(subject.removed?).to be_falsey }
+ end
+
+ describe '#new_pos' do
+ it { expect(subject.new_pos).to eq(11) }
+ end
+
+ describe '#old_pos' do
+ it { expect(subject.old_pos).to eq(9) }
+ end
+
+ describe '#file_path' do
+ it { expect(subject.file_path).to eq('CHANGELOG.md') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/pull_request_spec.rb b/spec/lib/bitbucket_server/representation/pull_request_spec.rb
new file mode 100644
index 00000000000..4b8afdb006b
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/pull_request_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::PullRequest do
+ let(:sample_data) { JSON.parse(fixture_file('importers/bitbucket_server/pull_request.json')) }
+
+ subject { described_class.new(sample_data) }
+
+ describe '#author' do
+ it { expect(subject.author).to eq('root') }
+ end
+
+ describe '#author_email' do
+ it { expect(subject.author_email).to eq('joe.montana@49ers.com') }
+ end
+
+ describe '#description' do
+ it { expect(subject.description).to eq('Test') }
+ end
+
+ describe '#iid' do
+ it { expect(subject.iid).to eq(7) }
+ end
+
+ describe '#state' do
+ it { expect(subject.state).to eq('merged') }
+
+ context 'declined pull requests' do
+ before do
+ sample_data['state'] = 'DECLINED'
+ end
+
+ it 'returns closed' do
+ expect(subject.state).to eq('closed')
+ end
+ end
+
+ context 'open pull requests' do
+ before do
+ sample_data['state'] = 'OPEN'
+ end
+
+ it 'returns open' do
+ expect(subject.state).to eq('opened')
+ end
+ end
+ end
+
+ describe '#merged?' do
+ it { expect(subject.merged?).to be_truthy }
+ end
+
+ describe '#created_at' do
+ it { expect(subject.created_at.to_i).to eq(sample_data['createdDate'] / 1000) }
+ end
+
+ describe '#updated_at' do
+ it { expect(subject.updated_at.to_i).to eq(sample_data['updatedDate'] / 1000) }
+ end
+
+ describe '#title' do
+ it { expect(subject.title).to eq('Added a new line') }
+ end
+
+ describe '#source_branch_name' do
+ it { expect(subject.source_branch_name).to eq('refs/heads/root/CODE_OF_CONDUCTmd-1530600625006') }
+ end
+
+ describe '#source_branch_sha' do
+ it { expect(subject.source_branch_sha).to eq('074e2b4dddc5b99df1bf9d4a3f66cfc15481fdc8') }
+ end
+
+ describe '#target_branch_name' do
+ it { expect(subject.target_branch_name).to eq('refs/heads/master') }
+ end
+
+ describe '#target_branch_sha' do
+ it { expect(subject.target_branch_sha).to eq('839fa9a2d434eb697815b8fcafaecc51accfdbbc') }
+ end
+end
diff --git a/spec/lib/bitbucket_server/representation/repo_spec.rb b/spec/lib/bitbucket_server/representation/repo_spec.rb
new file mode 100644
index 00000000000..3ac1030fbb0
--- /dev/null
+++ b/spec/lib/bitbucket_server/representation/repo_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe BitbucketServer::Representation::Repo do
+ let(:sample_data) do
+ <<~DATA
+ {
+ "slug": "rouge",
+ "id": 1,
+ "name": "rouge",
+ "scmId": "git",
+ "state": "AVAILABLE",
+ "statusMessage": "Available",
+ "forkable": true,
+ "project": {
+ "key": "TEST",
+ "id": 1,
+ "name": "test",
+ "description": "Test",
+ "public": false,
+ "type": "NORMAL",
+ "links": {
+ "self": [
+ {
+ "href": "http://localhost:7990/projects/TEST"
+ }
+ ]
+ }
+ },
+ "public": false,
+ "links": {
+ "clone": [
+ {
+ "href": "http://root@localhost:7990/scm/test/rouge.git",
+ "name": "http"
+ },
+ {
+ "href": "ssh://git@localhost:7999/test/rouge.git",
+ "name": "ssh"
+ }
+ ],
+ "self": [
+ {
+ "href": "http://localhost:7990/projects/TEST/repos/rouge/browse"
+ }
+ ]
+ }
+ }
+ DATA
+ end
+
+ subject { described_class.new(JSON.parse(sample_data)) }
+
+ describe '#project_key' do
+ it { expect(subject.project_key).to eq('TEST') }
+ end
+
+ describe '#project_name' do
+ it { expect(subject.project_name).to eq('test') }
+ end
+
+ describe '#slug' do
+ it { expect(subject.slug).to eq('rouge') }
+ end
+
+ describe '#browse_url' do
+ it { expect(subject.browse_url).to eq('http://localhost:7990/projects/TEST/repos/rouge/browse') }
+ end
+
+ describe '#clone_url' do
+ it { expect(subject.clone_url).to eq('http://root@localhost:7990/scm/test/rouge.git') }
+ end
+
+ describe '#description' do
+ it { expect(subject.description).to eq('Test') }
+ end
+
+ describe '#full_name' do
+ it { expect(subject.full_name).to eq('test/rouge') }
+ end
+end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index e13406d1972..8947e2ac4fb 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -203,4 +203,30 @@ describe ExtractsPath do
expect(extract_ref_without_atom('foo.atom')).to eq(nil)
end
end
+
+ describe '#lfs_blob_ids' do
+ shared_examples '#lfs_blob_ids' do
+ let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') }
+ let(:ref) { tag.target }
+ let(:params) { { ref: ref, path: 'README.md' } }
+
+ before do
+ @project = create(:project, :repository)
+ end
+
+ it 'handles annotated tags' do
+ assign_ref_vars
+
+ expect(lfs_blob_ids).to eq([])
+ end
+ end
+
+ context 'when gitaly is enabled' do
+ it_behaves_like '#lfs_blob_ids'
+ end
+
+ context 'when gitaly is disabled', :skip_gitaly_mock do
+ it_behaves_like '#lfs_blob_ids'
+ end
+ end
end
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 6eb10497428..f313e675654 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -39,18 +39,36 @@ describe Feature do
end
describe '.persisted?' do
- it 'returns true for a persisted feature' do
- Feature::FlipperFeature.create!(key: 'foo')
+ context 'when the feature is persisted' do
+ it 'returns true when feature name is a string' do
+ Feature::FlipperFeature.create!(key: 'foo')
+
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
+
+ it 'returns true when feature name is a symbol' do
+ Feature::FlipperFeature.create!(key: 'foo')
- feature = double(:feature, name: 'foo')
+ feature = double(:feature, name: :foo)
- expect(described_class.persisted?(feature)).to eq(true)
+ expect(described_class.persisted?(feature)).to eq(true)
+ end
end
- it 'returns false for a feature that is not persisted' do
- feature = double(:feature, name: 'foo')
+ context 'when the feature is not persisted' do
+ it 'returns false when feature name is a string' do
+ feature = double(:feature, name: 'foo')
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
- expect(described_class.persisted?(feature)).to eq(false)
+ it 'returns false when feature name is a symbol' do
+ feature = double(:feature, name: :bar)
+
+ expect(described_class.persisted?(feature)).to eq(false)
+ end
end
end
diff --git a/spec/lib/gitlab/auth/activity_spec.rb b/spec/lib/gitlab/auth/activity_spec.rb
new file mode 100644
index 00000000000..07854cb1eba
--- /dev/null
+++ b/spec/lib/gitlab/auth/activity_spec.rb
@@ -0,0 +1,30 @@
+require 'fast_spec_helper'
+
+describe Gitlab::Auth::Activity do
+ describe '.each_counter' do
+ it 'has all static counters defined' do
+ described_class.each_counter do |counter|
+ expect(described_class).to respond_to(counter)
+ end
+ end
+
+ it 'has all static incrementers defined' do
+ described_class.each_counter do |counter|
+ expect(described_class).to respond_to("#{counter}_increment!")
+ end
+ end
+
+ it 'has all counters starting with `user_`' do
+ described_class.each_counter do |counter|
+ expect(counter).to start_with('user_')
+ end
+ end
+
+ it 'yields counter method, name and description' do
+ described_class.each_counter do |method, name, description|
+ expect(method).to eq "#{name}_counter"
+ expect(description).to start_with('Counter of')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
index 43b68e69131..f39863fdda1 100644
--- a/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
+++ b/spec/lib/gitlab/auth/blocked_user_tracker_spec.rb
@@ -1,62 +1,30 @@
require 'spec_helper'
describe Gitlab::Auth::BlockedUserTracker do
- set(:user) { create(:user) }
+ describe '#log_blocked_user_activity!' do
+ context 'when user is not blocked' do
+ it 'does not log blocked user activity' do
+ expect_any_instance_of(SystemHooksService)
+ .not_to receive(:execute_hooks_for)
+ expect(Gitlab::AppLogger).not_to receive(:info)
- describe '.log_if_user_blocked' do
- it 'does not log if user failed to login due to undefined reason' do
- expect_any_instance_of(SystemHooksService).not_to receive(:execute_hooks_for)
+ user = create(:user)
- expect(described_class.log_if_user_blocked({})).to be_nil
- end
-
- it 'gracefully handles malformed environment variables' do
- env = { 'warden.options' => 'test' }
-
- expect(described_class.log_if_user_blocked(env)).to be_nil
- end
-
- context 'failed login due to blocked user' do
- let(:base_env) { { 'warden.options' => { message: User::BLOCKED_MESSAGE } } }
- let(:env) { base_env.merge(request_env) }
-
- subject { described_class.log_if_user_blocked(env) }
-
- before do
- expect_any_instance_of(SystemHooksService).to receive(:execute_hooks_for).with(user, :failed_login)
- end
-
- context 'via GitLab login' do
- let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'user' => { 'login' => user.username } } } }
-
- it 'logs a blocked user' do
- user.block!
-
- expect(subject).to be_truthy
- end
-
- it 'logs a blocked user by e-mail' do
- user.block!
- env[described_class::ACTIVE_RECORD_REQUEST_PARAMS]['user']['login'] = user.email
-
- expect(subject).to be_truthy
- end
+ described_class.new(user, spy('auth')).log_activity!
end
+ end
- context 'via LDAP login' do
- let(:request_env) { { described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'username' => user.username } } }
-
- it 'logs a blocked user' do
- user.block!
-
- expect(subject).to be_truthy
- end
+ context 'when user is not blocked' do
+ it 'logs blocked user activity' do
+ user = create(:user, :blocked)
- it 'logs a LDAP blocked user' do
- user.ldap_block!
+ expect_any_instance_of(SystemHooksService)
+ .to receive(:execute_hooks_for)
+ .with(user, :failed_login)
+ expect(Gitlab::AppLogger).to receive(:info)
+ .with(/Failed login for blocked user/)
- expect(subject).to be_truthy
- end
+ described_class.new(user, spy('auth')).log_activity!
end
end
end
diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
index a251ab323d8..1969aed51da 100644
--- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
@@ -1,31 +1,35 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do
+describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, :sidekiq, schema: 20180619121030 do
describe '#perform' do
context 'when diff files can be deleted' do
let(:merge_request) { create(:merge_request, :merged) }
- let(:merge_request_diff) do
+ let!(:merge_request_diff) do
merge_request.create_merge_request_diff
merge_request.merge_request_diffs.first
end
+ let(:perform) do
+ described_class.new.perform(MergeRequestDiff.pluck(:id))
+ end
+
it 'deletes all merge request diff files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.merge_request_diff_files.count }
.from(20).to(0)
end
it 'updates state to without_files' do
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to change { merge_request_diff.reload.state }
.from('collected').to('without_files')
end
it 'rollsback if something goes wrong' do
- expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
+ expect(described_class::MergeRequestDiffFile).to receive_message_chain(:where, :delete_all)
.and_raise
- expect { described_class.new.perform(merge_request_diff.id) }
+ expect { perform }
.to raise_error
merge_request_diff.reload
@@ -35,35 +39,35 @@ describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180
end
end
- it 'deletes no merge request diff files when MR is not merged' do
- merge_request = create(:merge_request, :opened)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
-
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
- end
-
- it 'deletes no merge request diff files when diff is marked as "without_files"' do
+ it 'reschedules itself when should_wait_deadtuple_vacuum' do
merge_request = create(:merge_request, :merged)
- merge_request.create_merge_request_diff
- merge_request_diff = merge_request.merge_request_diffs.first
+ first_diff = merge_request.merge_request_diff
+ second_diff = merge_request.create_merge_request_diff
- merge_request_diff.clean!
+ Sidekiq::Testing.fake! do
+ worker = described_class.new
+ allow(worker).to receive(:should_wait_deadtuple_vacuum?) { true }
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ worker.perform([first_diff.id, second_diff.id])
+
+ expect(described_class.name.demodulize).to be_scheduled_delayed_migration(5.minutes, [first_diff.id, second_diff.id])
+ expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ end
end
+ end
- it 'deletes no merge request diff files when diff is the latest' do
- merge_request = create(:merge_request, :merged)
- merge_request_diff = merge_request.merge_request_diff
+ describe '#should_wait_deadtuple_vacuum?' do
+ it 'returns true when hitting merge_request_diff_files hits DEAD_TUPLES_THRESHOLD', :postgresql do
+ worker = described_class.new
+ threshold_query_result = [{ "n_dead_tup" => described_class::DEAD_TUPLES_THRESHOLD.to_s }]
+ normal_query_result = [{ "n_dead_tup" => '3' }]
+
+ allow(worker)
+ .to receive(:execute_statement)
+ .with(/SELECT n_dead_tup */)
+ .and_return(threshold_query_result, normal_query_result)
- expect { described_class.new.perform(merge_request_diff.id) }
- .not_to change { merge_request_diff.merge_request_diff_files.count }
- .from(20)
+ expect(worker.should_wait_deadtuple_vacuum?).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index 211e3aaa94b..0735ebd6dcb 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -9,6 +9,11 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) }
let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff }
let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
+ end
before do
allow_any_instance_of(MergeRequestDiff)
@@ -299,11 +304,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.patches
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).patches }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
@@ -313,14 +314,15 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:commits) { merge_request_diff.commits.map(&:to_hash) }
let(:first_commit) { project.repository.commit(merge_request_diff.head_commit_sha) }
let(:expected_commits) { commits }
- let(:diffs) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- first_commit.rugged_diff_from_parent.deltas
- end
- end
+ let(:diffs) { rugged_diff(first_commit.sha).deltas }
let(:expected_diffs) { [] }
include_examples 'updated MR diff'
end
+
+ def rugged_diff(commit_sha)
+ rugged_commit = rugged.lookup(commit_sha)
+ rugged_commit.parents[0].diff(rugged_commit)
+ end
end
end
diff --git a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
new file mode 100644
index 00000000000..20af63bc6c8
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
@@ -0,0 +1,109 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, schema: 20180702120647 do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:issues_table) { table(:issues) }
+ let(:merge_requests_table) { table(:merge_requests) }
+ let(:labels_table) { table(:labels) }
+ let(:label_links_table) { table(:label_links) }
+
+ let!(:group1) { namespaces_table.create(id: 10, type: 'Group', name: 'group1', path: 'group1') }
+ let!(:group2) { namespaces_table.create(id: 20, type: 'Group', name: 'group2', path: 'group2') }
+
+ let!(:project1) { projects_table.create(id: 1, name: 'project1', path: 'group1/project1', namespace_id: 10) }
+ let!(:project2) { projects_table.create(id: 3, name: 'project2', path: 'group1/project2', namespace_id: 20) }
+
+ let!(:label1) { labels_table.create(id: 1, title: 'bug', color: 'red', group_id: 10, type: 'GroupLabel') }
+ let!(:label2) { labels_table.create(id: 2, title: 'bug', color: 'red', group_id: 20, type: 'GroupLabel') }
+
+ def create_merge_request(id, project_id)
+ merge_requests_table.create(id: id,
+ target_project_id: project_id,
+ target_branch: 'master',
+ source_project_id: project_id,
+ source_branch: 'mr name',
+ title: "mr name#{id}")
+ end
+
+ def create_issue(id, project_id)
+ issues_table.create(id: id, title: "issue#{id}", project_id: project_id)
+ end
+
+ def create_resource(target_type, id, project_id)
+ target_type == 'Issue' ? create_issue(id, project_id) : create_merge_request(id, project_id)
+ end
+
+ shared_examples_for 'resource with cross-project labels' do
+ it 'updates only cross-project label links which exist in the local project or group' do
+ create_resource(target_type, 1, 1)
+ create_resource(target_type, 2, 3)
+ labels_table.create(id: 3, title: 'bug', color: 'red', project_id: 3, type: 'ProjectLabel')
+ link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
+ link2 = label_links_table.create(label_id: 3, target_type: target_type, target_id: 2)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ expect(link2.reload.label_id).to eq(3)
+ end
+
+ it 'ignores cross-project label links if label color is different' do
+ labels_table.create(id: 3, title: 'bug', color: 'green', group_id: 20, type: 'GroupLabel')
+ create_resource(target_type, 1, 1)
+ link = label_links_table.create(label_id: 3, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(3)
+ end
+
+ it 'ignores cross-project label links if label name is different' do
+ labels_table.create(id: 3, title: 'bug1', color: 'red', group_id: 20, type: 'GroupLabel')
+ create_resource(target_type, 1, 1)
+ link = label_links_table.create(label_id: 3, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(3)
+ end
+
+ context 'with nested group' do
+ before do
+ namespaces_table.create(id: 11, type: 'Group', name: 'subgroup1', path: 'group1/subgroup1', parent_id: 10)
+ projects_table.create(id: 2, name: 'subproject1', path: 'group1/subgroup1/subproject1', namespace_id: 11)
+ create_resource(target_type, 1, 2)
+ end
+
+ it 'ignores label links referencing ancestor group labels', :nested_groups do
+ labels_table.create(id: 4, title: 'bug', color: 'red', project_id: 2, type: 'ProjectLabel')
+ label_links_table.create(label_id: 4, target_type: target_type, target_id: 1)
+ link = label_links_table.create(label_id: 1, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ end
+
+ it 'checks also issues and MRs in subgroups', :nested_groups do
+ link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
+
+ subject.perform(1, 100)
+
+ expect(link.reload.label_id).to eq(1)
+ end
+ end
+ end
+
+ context 'resource is Issue' do
+ it_behaves_like 'resource with cross-project labels' do
+ let(:target_type) { 'Issue' }
+ end
+ end
+
+ context 'resource is Merge Request' do
+ it_behaves_like 'resource with cross-project labels' do
+ let(:target_type) { 'MergeRequest' }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
index ee60e498b59..2e77e80ee46 100644
--- a/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/move_personal_snippet_files_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::BackgroundMigration::MovePersonalSnippetFiles do
let(:snippet) do
snippet = create(:personal_snippet)
create_upload_for_snippet(snippet)
- snippet.update_attributes!(description: markdown_linking_file(snippet))
+ snippet.update!(description: markdown_linking_file(snippet))
snippet
end
diff --git a/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb b/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb
new file mode 100644
index 00000000000..dae754112dc
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_restricted_todos_spec.rb
@@ -0,0 +1,124 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::RemoveRestrictedTodos, :migration, schema: 20180704204006 do
+ let(:projects) { table(:projects) }
+ let(:users) { table(:users) }
+ let(:todos) { table(:todos) }
+ let(:issues) { table(:issues) }
+ let(:assignees) { table(:issue_assignees) }
+ let(:project_authorizations) { table(:project_authorizations) }
+ let(:project_features) { table(:project_features) }
+
+ let(:todo_params) { { author_id: 1, target_type: 'Issue', action: 1, state: :pending } }
+
+ before do
+ users.create(id: 1, email: 'user@example.com', projects_limit: 10)
+ users.create(id: 2, email: 'reporter@example.com', projects_limit: 10)
+ users.create(id: 3, email: 'guest@example.com', projects_limit: 10)
+
+ projects.create!(id: 1, name: 'project-1', path: 'project-1', visibility_level: 0, namespace_id: 1)
+ projects.create!(id: 2, name: 'project-2', path: 'project-2', visibility_level: 0, namespace_id: 1)
+
+ issues.create(id: 1, project_id: 1)
+ issues.create(id: 2, project_id: 2)
+
+ project_authorizations.create(user_id: 2, project_id: 2, access_level: 20) # reporter
+ project_authorizations.create(user_id: 3, project_id: 2, access_level: 10) # guest
+
+ todos.create(todo_params.merge(user_id: 1, project_id: 1, target_id: 1)) # out of project ids range
+ todos.create(todo_params.merge(user_id: 1, project_id: 2, target_id: 2)) # non member
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 2)) # reporter
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 2)) # guest
+ end
+
+ subject { described_class.new.perform(2, 5) }
+
+ context 'when a project is private' do
+ it 'removes todos of users without project access' do
+ expect { subject }.to change { Todo.count }.from(4).to(3)
+ end
+
+ context 'with a confidential issue' do
+ it 'removes todos of users without project access and guests for confidential issues' do
+ issues.create(id: 3, project_id: 2, confidential: true)
+ issues.create(id: 4, project_id: 1, confidential: true) # not in the batch
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 3))
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 3))
+ todos.create(todo_params.merge(user_id: 1, project_id: 1, target_id: 4))
+
+ expect { subject }.to change { Todo.count }.from(7).to(5)
+ end
+ end
+ end
+
+ context 'when a project is public' do
+ before do
+ projects.find(2).update_attribute(:visibility_level, 20)
+ end
+
+ context 'when all features have the same visibility as the project, no confidential issues' do
+ it 'does not remove any todos' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'with confidential issues' do
+ before do
+ users.create(id: 4, email: 'author@example.com', projects_limit: 10)
+ users.create(id: 5, email: 'assignee@example.com', projects_limit: 10)
+ issues.create(id: 3, project_id: 2, confidential: true, author_id: 4)
+ assignees.create(user_id: 5, issue_id: 3)
+
+ todos.create(todo_params.merge(user_id: 1, project_id: 2, target_id: 3)) # to be deleted
+ todos.create(todo_params.merge(user_id: 2, project_id: 2, target_id: 3)) # authorized user
+ todos.create(todo_params.merge(user_id: 3, project_id: 2, target_id: 3)) # to be deleted guest
+ todos.create(todo_params.merge(user_id: 4, project_id: 2, target_id: 3)) # conf issue author
+ todos.create(todo_params.merge(user_id: 5, project_id: 2, target_id: 3)) # conf issue assignee
+ end
+
+ it 'removes confidential issue todos for non authorized users' do
+ expect { subject }.to change { Todo.count }.from(9).to(7)
+ end
+ end
+
+ context 'features visibility restrictions' do
+ before do
+ todo_params.merge!(project_id: 2, user_id: 1, target_id: 3)
+ todos.create(todo_params.merge(user_id: 1, target_id: 3, target_type: 'MergeRequest'))
+ todos.create(todo_params.merge(user_id: 1, target_id: 3, target_type: 'Commit'))
+ end
+
+ context 'when issues are restricted to project members' do
+ before do
+ project_features.create(issues_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members issue todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(5)
+ end
+ end
+
+ context 'when merge requests are restricted to project members' do
+ before do
+ project_features.create(merge_requests_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members issue todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(5)
+ end
+ end
+
+ context 'when repository and merge requests are restricted to project members' do
+ before do
+ project_features.create(repository_access_level: 10, merge_requests_access_level: 10, project_id: 2)
+ end
+
+ it 'removes non members commit and merge requests todos' do
+ expect { subject }.to change { Todo.count }.from(6).to(4)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
new file mode 100644
index 00000000000..ec8ba0ce127
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, :sidekiq, schema: 20180619121030 do
+ describe '#perform' do
+ let(:merge_request_diffs) { table(:merge_request_diffs) }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+
+ before do
+ stub_const("#{described_class.name}::DIFF_BATCH_SIZE", 3)
+
+ namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab')
+ projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab')
+
+ merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged')
+
+ merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'empty')
+ merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'without_files')
+ merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected')
+ merge_request_diffs.create!(id: 7, merge_request_id: 1, state: 'collected')
+
+ merge_requests.update(1, latest_merge_request_diff_id: 7)
+ end
+
+ it 'correctly schedules diff file deletion workers' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ described_class.new.perform
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, [1, 4, 5])
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, [6])
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 468f6ff6d24..6e21c846c0a 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -1,10 +1,11 @@
require 'spec_helper'
-describe Gitlab::BareRepositoryImport::Importer, repository: true do
+describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
let!(:admin) { create(:admin) }
let!(:base_dir) { Dir.mktmpdir + '/' }
let(:bare_repository) { Gitlab::BareRepositoryImport::Repository.new(base_dir, File.join(base_dir, "#{project_path}.git")) }
let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:source_project) { TEST_REPO_PATH }
subject(:importer) { described_class.new(admin, bare_repository) }
@@ -17,16 +18,11 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
after do
FileUtils.rm_rf(base_dir)
+ TestEnv.clean_test_path
+ ensure_seeds
Rainbow.enabled = @rainbow
end
- around do |example|
- # TODO migrate BareRepositoryImport https://gitlab.com/gitlab-org/gitaly/issues/953
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
shared_examples 'importing a repository' do
describe '.execute' do
it 'creates a project for a repository in storage' do
@@ -86,8 +82,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
importer.create_project_if_needed
end
- it 'creates the Git repo on disk with the proper symlink for hooks' do
- create_bare_repository("#{project_path}.git")
+ it 'creates the Git repo on disk' do
+ prepare_repository("#{project_path}.git", source_project)
importer.create_project_if_needed
@@ -97,9 +93,6 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(gitlab_shell.exists?(project.repository_storage, repo_path)).to be(true)
expect(gitlab_shell.exists?(project.repository_storage, hook_path)).to be(true)
-
- full_hook_path = File.join(project.repository.path_to_repo, 'hooks')
- expect(File.readlink(full_hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
context 'hashed storage enabled' do
@@ -148,7 +141,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
it 'creates the Git repo in disk' do
- create_bare_repository("#{project_path}.git")
+ prepare_repository("#{project_path}.git", source_project)
importer.create_project_if_needed
@@ -158,23 +151,23 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(gitlab_shell.exists?(project.repository_storage, project.disk_path + '.wiki.git')).to be(true)
end
- it 'moves an existing project to the correct path' do
+ context 'with a repository already on disk' do
+ let!(:base_dir) { TestEnv.repos_path }
# This is a quick way to get a valid repository instead of copying an
# existing one. Since it's not persisted, the importer will try to
# create the project.
- project = build(:project, :legacy_storage, :repository)
- original_commit_count = project.repository.commit_count
-
- legacy_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path
+ let(:project) { build(:project, :legacy_storage, :repository) }
+ let(:project_path) { project.full_path }
- bare_repo = Gitlab::BareRepositoryImport::Repository.new(legacy_path, project.repository.path)
- gitlab_importer = described_class.new(admin, bare_repo)
+ it 'moves an existing project to the correct path' do
+ original_commit_count = project.repository.commit_count
- expect(gitlab_importer).to receive(:create_project).and_call_original
+ expect(importer).to receive(:create_project).and_call_original
- new_project = gitlab_importer.create_project_if_needed
+ new_project = importer.create_project_if_needed
- expect(new_project.repository.commit_count).to eq(original_commit_count)
+ expect(new_project.repository.commit_count).to eq(original_commit_count)
+ end
end
end
@@ -185,8 +178,8 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
it_behaves_like 'importing a repository'
it 'creates the Wiki git repo in disk' do
- create_bare_repository("#{project_path}.git")
- create_bare_repository("#{project_path}.wiki.git")
+ prepare_repository("#{project_path}.git", source_project)
+ prepare_repository("#{project_path}.wiki.git", source_project)
expect(Projects::CreateService).to receive(:new).with(admin, hash_including(skip_wiki: true,
import_type: 'bare_repository')).and_call_original
@@ -213,8 +206,13 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
end
end
- def create_bare_repository(project_path)
+ def prepare_repository(project_path, source_project)
repo_path = File.join(base_dir, project_path)
- Gitlab::Git::Repository.create(repo_path, bare: true)
+
+ return create_bare_repository(repo_path) unless source_project
+
+ cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{source_project} #{repo_path})
+
+ system(git_env, *cmd, chdir: SEED_STORAGE_PATH, out: '/dev/null', err: '/dev/null')
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 05c232d22cf..7a681bc6610 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -69,6 +69,7 @@ describe Gitlab::BitbucketImport::Importer do
let(:project) do
create(
:project,
+ :repository,
import_source: project_identifier,
import_url: "https://bitbucket.org/#{project_identifier}.git",
import_data_attributes: { credentials: data }
@@ -85,10 +86,84 @@ describe Gitlab::BitbucketImport::Importer do
}
end
+ let(:sample) { RepoHelpers.sample_compare }
+
before do
allow(importer).to receive(:gitlab_shell) { gitlab_shell }
end
+ subject { described_class.new(project) }
+
+ describe '#import_pull_requests' do
+ before do
+ allow(subject).to receive(:import_wiki)
+ allow(subject).to receive(:import_issues)
+
+ pull_request = instance_double(
+ Bitbucket::Representation::PullRequest,
+ iid: 10,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'other',
+ created_at: Time.now,
+ updated_at: Time.now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ @inline_note = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 2,
+ file_path: '.gitmodules',
+ old_pos: nil,
+ new_pos: 4,
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: false)
+
+ @reply = instance_double(
+ Bitbucket::Representation::PullRequestComment,
+ iid: 3,
+ file_path: '.gitmodules',
+ note: 'Hello world',
+ author: 'root',
+ created_at: Time.now,
+ updated_at: Time.now,
+ inline?: true,
+ has_parent?: true,
+ parent_id: 2)
+
+ comments = [@inline_note, @reply]
+
+ allow(subject.client).to receive(:repo)
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request])
+ allow(subject.client).to receive(:pull_request_comments).with(anything, pull_request.iid).and_return(comments)
+ end
+
+ it 'imports threaded discussions' do
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note).to be_a(DiffNote)
+ expect(start_note.note).to eq(@inline_note.note)
+
+ reply_note = notes.last
+ expect(reply_note).to be_a(DiffNote)
+ expect(reply_note.note).to eq(@reply.note)
+ end
+ end
+
context 'issues statuses' do
before do
# HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this
diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
new file mode 100644
index 00000000000..70423823b89
--- /dev/null
+++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
@@ -0,0 +1,291 @@
+require 'spec_helper'
+
+describe Gitlab::BitbucketServerImport::Importer do
+ include ImportSpecHelper
+
+ let(:project) { create(:project, :repository, import_url: 'http://my-bitbucket') }
+ let(:now) { Time.now.utc.change(usec: 0) }
+ let(:project_key) { 'TEST' }
+ let(:repo_slug) { 'rouge' }
+ let(:sample) { RepoHelpers.sample_compare }
+
+ subject { described_class.new(project, recover_missing_commits: true) }
+
+ before do
+ data = project.create_or_update_import_data(
+ data: { project_key: project_key, repo_slug: repo_slug },
+ credentials: { base_uri: 'http://my-bitbucket', user: 'bitbucket', password: 'test' }
+ )
+ data.save
+ project.save
+ end
+
+ describe '#import_repository' do
+ before do
+ expect(subject).to receive(:import_pull_requests)
+ expect(subject).to receive(:delete_temp_branches)
+ end
+
+ it 'adds a remote' do
+ expect(project.repository).to receive(:fetch_as_mirror)
+ .with('http://bitbucket:test@my-bitbucket',
+ refmap: [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'],
+ remote_name: 'bitbucket_server')
+
+ subject.execute
+ end
+ end
+
+ describe '#import_pull_requests' do
+ before do
+ allow(subject).to receive(:import_repository)
+ allow(subject).to receive(:delete_temp_branches)
+ allow(subject).to receive(:restore_branches)
+
+ pull_request = instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: 10,
+ source_branch_sha: sample.commits.last,
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: sample.commits.first,
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'Test Author',
+ author_email: project.owner.email,
+ created_at: Time.now,
+ updated_at: Time.now,
+ merged?: true)
+
+ allow(subject.client).to receive(:pull_requests).and_return([pull_request])
+
+ @merge_event = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: false,
+ merge_event?: true,
+ committer_email: project.owner.email,
+ merge_timestamp: now,
+ merge_commit: '12345678'
+ )
+
+ @pr_note = instance_double(
+ BitbucketServer::Representation::Comment,
+ note: 'Hello world',
+ author_email: 'unknown@gmail.com',
+ author_username: 'The Flash',
+ comments: [],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ @pr_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: false,
+ merge_event?: false,
+ comment: @pr_note)
+ end
+
+ it 'imports merge event' do
+ expect(subject.client).to receive(:activities).and_return([@merge_event])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.metrics.merged_by).to eq(project.owner)
+ expect(merge_request.metrics.merged_at).to eq(@merge_event.merge_timestamp)
+ expect(merge_request.merge_commit_sha).to eq('12345678')
+ end
+
+ it 'imports comments' do
+ expect(subject.client).to receive(:activities).and_return([@pr_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(1)
+ note = merge_request.notes.first
+ expect(note.note).to end_with(@pr_note.note)
+ expect(note.author).to eq(project.owner)
+ expect(note.created_at).to eq(@pr_note.created_at)
+ expect(note.updated_at).to eq(@pr_note.created_at)
+ end
+
+ it 'imports threaded discussions' do
+ reply = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ author_email: 'someuser@gitlab.com',
+ author_username: 'Batman',
+ note: 'I agree',
+ created_at: now,
+ updated_at: now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ inline_note = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ file_type: 'ADDED',
+ from_sha: sample.commits.first,
+ to_sha: sample.commits.last,
+ file_path: '.gitmodules',
+ old_pos: nil,
+ new_pos: 4,
+ note: 'Hello world',
+ author_email: 'unknown@gmail.com',
+ author_username: 'Superman',
+ comments: [reply],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ allow(reply).to receive(:parent_comment).and_return(inline_note)
+
+ inline_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: true,
+ merge_event?: false,
+ comment: inline_note)
+
+ expect(subject.client).to receive(:activities).and_return([inline_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note.type).to eq('DiffNote')
+ expect(start_note.note).to end_with(inline_note.note)
+ expect(start_note.created_at).to eq(inline_note.created_at)
+ expect(start_note.updated_at).to eq(inline_note.updated_at)
+ expect(start_note.position.base_sha).to eq(inline_note.from_sha)
+ expect(start_note.position.start_sha).to eq(inline_note.from_sha)
+ expect(start_note.position.head_sha).to eq(inline_note.to_sha)
+ expect(start_note.position.old_line).to be_nil
+ expect(start_note.position.new_line).to eq(inline_note.new_pos)
+
+ reply_note = notes.last
+ # Make sure author and reply context is included
+ expect(reply_note.note).to start_with("*By #{reply.author_username} (#{reply.author_email})*\n\n")
+ expect(reply_note.note).to end_with("> #{inline_note.note}\n\n#{reply.note}")
+ expect(reply_note.author).to eq(project.owner)
+ expect(reply_note.created_at).to eq(reply.created_at)
+ expect(reply_note.updated_at).to eq(reply.created_at)
+ expect(reply_note.position.base_sha).to eq(inline_note.from_sha)
+ expect(reply_note.position.start_sha).to eq(inline_note.from_sha)
+ expect(reply_note.position.head_sha).to eq(inline_note.to_sha)
+ expect(reply_note.position.old_line).to be_nil
+ expect(reply_note.position.new_line).to eq(inline_note.new_pos)
+ end
+
+ it 'falls back to comments if diff comments fail to validate' do
+ reply = instance_double(
+ BitbucketServer::Representation::Comment,
+ author_email: 'someuser@gitlab.com',
+ author_username: 'Aquaman',
+ note: 'I agree',
+ created_at: now,
+ updated_at: now)
+
+ # https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
+ inline_note = instance_double(
+ BitbucketServer::Representation::PullRequestComment,
+ file_type: 'REMOVED',
+ from_sha: sample.commits.first,
+ to_sha: sample.commits.last,
+ file_path: '.gitmodules',
+ old_pos: 8,
+ new_pos: 9,
+ note: 'This is a note with an invalid line position.',
+ author_email: project.owner.email,
+ author_username: 'Owner',
+ comments: [reply],
+ created_at: now,
+ updated_at: now,
+ parent_comment: nil)
+
+ inline_comment = instance_double(
+ BitbucketServer::Representation::Activity,
+ comment?: true,
+ inline_comment?: true,
+ merge_event?: false,
+ comment: inline_note)
+
+ allow(reply).to receive(:parent_comment).and_return(inline_note)
+
+ expect(subject.client).to receive(:activities).and_return([inline_comment])
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+ expect(merge_request.notes.count).to eq(2)
+ notes = merge_request.notes
+
+ expect(notes.first.note).to start_with('*Comment on .gitmodules')
+ expect(notes.second.note).to start_with('*Comment on .gitmodules')
+ end
+ end
+
+ describe 'inaccessible branches' do
+ let(:id) { 10 }
+ let(:temp_branch_from) { "gitlab/import/pull-request/#{id}/from" }
+ let(:temp_branch_to) { "gitlab/import/pull-request/#{id}/to" }
+
+ before do
+ pull_request = instance_double(
+ BitbucketServer::Representation::PullRequest,
+ iid: id,
+ source_branch_sha: '12345678',
+ source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
+ target_branch_sha: '98765432',
+ target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
+ title: 'This is a title',
+ description: 'This is a test pull request',
+ state: 'merged',
+ author: 'Test Author',
+ author_email: project.owner.email,
+ created_at: Time.now,
+ updated_at: Time.now,
+ merged?: true)
+
+ expect(subject.client).to receive(:pull_requests).and_return([pull_request])
+ expect(subject.client).to receive(:activities).and_return([])
+ expect(subject).to receive(:import_repository).twice
+ end
+
+ it '#restore_branches' do
+ expect(subject).to receive(:restore_branches).and_call_original
+ expect(subject).to receive(:delete_temp_branches)
+ expect(subject.client).to receive(:create_branch)
+ .with(project_key, repo_slug,
+ temp_branch_from,
+ '12345678')
+ expect(subject.client).to receive(:create_branch)
+ .with(project_key, repo_slug,
+ temp_branch_to,
+ '98765432')
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+ end
+
+ it '#delete_temp_branches' do
+ expect(subject.client).to receive(:create_branch).twice
+ expect(subject).to receive(:delete_temp_branches).and_call_original
+ expect(subject.client).to receive(:delete_branch)
+ .with(project_key, repo_slug,
+ temp_branch_from,
+ '12345678')
+ expect(subject.client).to receive(:delete_branch)
+ .with(project_key, repo_slug,
+ temp_branch_to,
+ '98765432')
+ expect(project.repository).to receive(:delete_branch).with(temp_branch_from)
+ expect(project.repository).to receive(:delete_branch).with(temp_branch_to)
+
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 1cb8143a9e9..4df426c54ae 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'as maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'deletion' do
@@ -132,6 +132,16 @@ describe Gitlab::Checks::ChangeAccess do
expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
end
+ context 'when project repository is empty' do
+ let(:project) { create(:project) }
+
+ it 'raises an error if the user is not allowed to push to protected branches' do
+ expect(user_access).to receive(:can_push_to_branch?).and_return(false)
+
+ expect { subject.exec }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
+ end
+ end
+
context 'branch deletion' do
let(:newrev) { '0000000000000000000000000000000000000000' }
let(:ref) { 'refs/heads/feature' }
@@ -144,7 +154,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'if the user is allowed to delete protected branches' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'through the web interface' do
diff --git a/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb b/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
new file mode 100644
index 00000000000..384329dda18
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/gzip_file_adapter_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::GzipFileAdapter do
+ describe '#initialize' do
+ context 'when stream is passed' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'initialized' do
+ expect { described_class.new(stream) }.not_to raise_error
+ end
+ end
+
+ context 'when stream is not passed' do
+ let(:stream) { nil }
+
+ it 'raises an error' do
+ expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+
+ describe '#each_blob' do
+ let(:adapter) { described_class.new(stream) }
+
+ context 'when stream is gzip file' do
+ context 'when gzip file contains one file' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_with_args(fixture_file('junit/junit.xml'), 'rspec.xml')
+ end
+ end
+
+ context 'when gzip file contains three files' do
+ let(:stream) { File.open(expand_fixture_path('junit/junit_with_three_testsuites.xml.gz'), 'rb') }
+
+ it 'iterates content and file_name' do
+ expect { |b| adapter.each_blob(&b) }
+ .to yield_successive_args(
+ [fixture_file('junit/junit_with_three_testsuites_1.xml'), 'rspec-3.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_2.xml'), 'rspec-1.xml'],
+ [fixture_file('junit/junit_with_three_testsuites_3.xml'), 'rspec-2.xml'])
+ end
+ end
+ end
+
+ context 'when stream is zip file' do
+ let(:stream) { File.open(expand_fixture_path('ci_build_artifacts.zip'), 'rb') }
+
+ it 'raises an error' do
+ expect { |b| adapter.each_blob(&b) }.to raise_error(described_class::InvalidStreamError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index 6a52ae01b2f..e327399d82d 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -2,13 +2,21 @@ require 'spec_helper'
describe Gitlab::Ci::Build::Artifacts::Metadata do
def metadata(path = '', **opts)
- described_class.new(metadata_file_path, path, **opts)
+ described_class.new(metadata_file_stream, path, **opts)
end
let(:metadata_file_path) do
Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
end
+ let(:metadata_file_stream) do
+ File.open(metadata_file_path) if metadata_file_path
+ end
+
+ after do
+ metadata_file_stream&.close
+ end
+
context 'metadata file exists' do
describe '#find_entries! empty string' do
subject { metadata('').find_entries! }
@@ -86,11 +94,21 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
end
context 'metadata file does not exist' do
- let(:metadata_file_path) { '' }
+ let(:metadata_file_path) { nil }
+
+ describe '#find_entries!' do
+ it 'raises error' do
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /Invalid stream/)
+ end
+ end
+ end
+
+ context 'metadata file is invalid' do
+ let(:metadata_file_path) { Rails.root + 'spec/fixtures/ci_build_artifacts.zip' }
describe '#find_entries!' do
it 'raises error' do
- expect { metadata.find_entries! }.to raise_error(Errno::ENOENT)
+ expect { metadata.find_entries! }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index 5c31423fdee..d48aac15f28 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -18,6 +18,14 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
expect(entry).to be_valid
end
end
+
+ context "when value includes 'reports' keyword" do
+ let(:config) { { paths: %w[public/], reports: { junit: 'junit.xml' } } }
+
+ it 'returns general artifact and report-type artifacts configuration' do
+ expect(entry.value).to eq config
+ end
+ end
end
context 'when entry value is not correct' do
@@ -39,6 +47,15 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
.to include 'artifacts config contains unknown keys: test'
end
end
+
+ context "when 'reports' keyword is not hash" do
+ let(:config) { { paths: %w[public/], reports: 'junit.xml' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts reports should be a hash'
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
index afa4a089418..8934aeb83db 100644
--- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
@@ -41,8 +41,7 @@ describe Gitlab::Ci::Config::Entry::Commands do
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include 'commands config should be a ' \
- 'string or an array of strings'
+ .to include 'commands config should be an array of strings or a string'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
new file mode 100644
index 00000000000..b3a3a6bee1d
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Reports do
+ let(:entry) { described_class.new(config) }
+
+ describe 'validation' do
+ context 'when entry config value is correct' do
+ let(:config) { { junit: %w[junit.xml] } }
+
+ describe '#value' do
+ it 'returns artifacs configuration' do
+ expect(entry.value).to eq config
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ context 'when value is not array' do
+ let(:config) { { junit: 'junit.xml' } }
+
+ it 'converts to array' do
+ expect(entry.value).to eq({ junit: ['junit.xml'] } )
+ end
+ end
+ end
+
+ context 'when entry value is not correct' do
+ describe '#errors' do
+ context 'when value of attribute is invalid' do
+ let(:config) { { junit: 10 } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'reports junit should be an array of strings or a string'
+ end
+ end
+
+ context 'when there is an unknown key present' do
+ let(:config) { { codeclimate: 'codeclimate.json' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'reports config contains unknown keys: codeclimate'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/junit_spec.rb b/spec/lib/gitlab/ci/parsers/junit_spec.rb
new file mode 100644
index 00000000000..f7ec86f5385
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/junit_spec.rb
@@ -0,0 +1,118 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers::Junit do
+ describe '#parse!' do
+ subject { described_class.new.parse!(junit, test_suite) }
+
+ let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:test_cases) { flattened_test_cases(test_suite) }
+
+ context 'when data is JUnit style XML' do
+ context 'when there are no test cases' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite></testsuite>
+ EOF
+ end
+
+ it 'raises an error and does not add any test cases' do
+ expect { subject }.to raise_error(described_class::JunitParserError)
+
+ expect(test_cases.count).to eq(0)
+ end
+ end
+
+ context 'when there is a test case' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds a test case to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ end
+ end
+
+ context 'when there are two test cases' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ end
+ end
+
+ context 'when there are two test suites' do
+ let(:junit) do
+ <<-EOF.strip_heredoc
+ <testsuites>
+ <testsuite>
+ <testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
+ <testcase classname='Calculator' name='sumTest2' time='0.02'></testcase>
+ </testsuite>
+ <testsuite>
+ <testcase classname='Statemachine' name='happy path' time='100'></testcase>
+ <testcase classname='Statemachine' name='unhappy path' time='200'></testcase>
+ </testsuite>
+ </testsuites>
+ EOF
+ end
+
+ it 'parses XML and adds test cases to a suite' do
+ expect { subject }.not_to raise_error
+
+ expect(test_cases[0].classname).to eq('Calculator')
+ expect(test_cases[0].name).to eq('sumTest1')
+ expect(test_cases[0].execution_time).to eq(0.01)
+ expect(test_cases[1].classname).to eq('Calculator')
+ expect(test_cases[1].name).to eq('sumTest2')
+ expect(test_cases[1].execution_time).to eq(0.02)
+ expect(test_cases[2].classname).to eq('Statemachine')
+ expect(test_cases[2].name).to eq('happy path')
+ expect(test_cases[2].execution_time).to eq(100)
+ expect(test_cases[3].classname).to eq('Statemachine')
+ expect(test_cases[3].name).to eq('unhappy path')
+ expect(test_cases[3].execution_time).to eq(200)
+ end
+ end
+ end
+
+ context 'when data is not JUnit style XML' do
+ let(:junit) { { testsuite: 'abc' }.to_json }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(described_class::JunitParserError)
+ end
+ end
+
+ private
+
+ def flattened_test_cases(test_suite)
+ test_suite.test_cases.map do |status, value|
+ value.map do |key, test_case|
+ test_case
+ end
+ end.flatten
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
new file mode 100644
index 00000000000..2fa83c4abae
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Parsers do
+ describe '.fabricate!' do
+ subject { described_class.fabricate!(file_type) }
+
+ context 'when file_type exists' do
+ let(:file_type) { 'junit' }
+
+ it 'fabricates the class' do
+ is_expected.to be_a(described_class::Junit)
+ end
+ end
+
+ context 'when file_type does not exist' do
+ let(:file_type) { 'undefined' }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(NameError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index a973ccda8de..8ba56d73838 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -99,9 +99,9 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
end
- context 'when user is a master' do
+ context 'when user is a maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it { is_expected.to be_truthy }
diff --git a/spec/lib/gitlab/ci/reports/test_case_spec.rb b/spec/lib/gitlab/ci/reports/test_case_spec.rb
new file mode 100644
index 00000000000..6932f79f0ce
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_case_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestCase do
+ describe '#initialize' do
+ let(:test_case) { described_class.new(**params)}
+
+ context 'when both classname and name are given' do
+ context 'when test case is passed' do
+ let(:params) do
+ {
+ name: 'test-1',
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'initializes an instance' do
+ expect { test_case }.not_to raise_error
+
+ expect(test_case.name).to eq('test-1')
+ expect(test_case.classname).to eq('trace')
+ expect(test_case.file).to eq('spec/trace_spec.rb')
+ expect(test_case.execution_time).to eq(1.23)
+ expect(test_case.status).to eq(described_class::STATUS_SUCCESS)
+ expect(test_case.system_output).to be_nil
+ end
+ end
+
+ context 'when test case is failed' do
+ let(:params) do
+ {
+ name: 'test-1',
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_FAILED,
+ system_output: "Failure/Error: is_expected.to eq(300) expected: 300 got: -100"
+ }
+ end
+
+ it 'initializes an instance' do
+ expect { test_case }.not_to raise_error
+
+ expect(test_case.name).to eq('test-1')
+ expect(test_case.classname).to eq('trace')
+ expect(test_case.file).to eq('spec/trace_spec.rb')
+ expect(test_case.execution_time).to eq(1.23)
+ expect(test_case.status).to eq(described_class::STATUS_FAILED)
+ expect(test_case.system_output)
+ .to eq('Failure/Error: is_expected.to eq(300) expected: 300 got: -100')
+ end
+ end
+ end
+
+ context 'when classname is missing' do
+ let(:params) do
+ {
+ name: 'test-1',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'raises an error' do
+ expect { test_case }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when name is missing' do
+ let(:params) do
+ {
+ classname: 'trace',
+ file: 'spec/trace_spec.rb',
+ execution_time: 1.23,
+ status: described_class::STATUS_SUCCESS,
+ system_output: nil
+ }
+ end
+
+ it 'raises an error' do
+ expect { test_case }.to raise_error(ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
new file mode 100644
index 00000000000..71c61e0345f
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
@@ -0,0 +1,134 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestReportsComparer do
+ include TestReportsHelper
+
+ let(:comparer) { described_class.new(base_reports, head_reports) }
+ let(:base_reports) { Gitlab::Ci::Reports::TestReports.new }
+ let(:head_reports) { Gitlab::Ci::Reports::TestReports.new }
+
+ describe '#suite_comparers' do
+ subject { comparer.suite_comparers }
+
+ context 'when head and base reports include two test suites' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns test suite comparers with specified values' do
+ expect(subject[0]).to be_a(Gitlab::Ci::Reports::TestSuiteComparer)
+ expect(subject[0].name).to eq('rspec')
+ expect(subject[0].head_suite).to eq(head_reports.get_suite('rspec'))
+ expect(subject[0].base_suite).to eq(base_reports.get_suite('rspec'))
+ expect(subject[1]).to be_a(Gitlab::Ci::Reports::TestSuiteComparer)
+ expect(subject[1].name).to eq('junit')
+ expect(subject[1].head_suite).to eq(head_reports.get_suite('junit'))
+ expect(subject[1].base_suite).to eq(base_reports.get_suite('junit'))
+ end
+ end
+ end
+
+ describe '#total_status' do
+ subject { comparer.total_status }
+
+ context 'when all tests cases are success in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { comparer.total_count }
+
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the total test counts in head suites' do
+ is_expected.to eq(2)
+ end
+ end
+
+ describe '#resolved_count' do
+ subject { comparer.resolved_count }
+
+ context 'when there is a resolved test case in head suites' do
+ let(:create_test_case_java_resolved) do
+ create_test_case_java_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there are no resolved test cases in head suites' do
+ before do
+ base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(0)
+ end
+ end
+ end
+
+ describe '#failed_count' do
+ subject { comparer.failed_count }
+
+ context 'when there is a failed test case in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there are no failed test cases in head suites' do
+ before do
+ head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ head_reports.get_suite('junit').add_test_case(create_test_case_rspec_success)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(0)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_reports_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
new file mode 100644
index 00000000000..74ff134b239
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_reports_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestReports do
+ include TestReportsHelper
+
+ let(:test_reports) { described_class.new }
+
+ describe '#get_suite' do
+ subject { test_reports.get_suite(suite_name) }
+
+ context 'when suite name is rspec' do
+ let(:suite_name) { 'rspec' }
+
+ it { expect(subject.name).to eq('rspec') }
+
+ it 'initializes a new test suite and returns it' do
+ expect(Gitlab::Ci::Reports::TestSuite).to receive(:new).and_call_original
+
+ is_expected.to be_a(Gitlab::Ci::Reports::TestSuite)
+ end
+
+ context 'when suite name is already allocated' do
+ before do
+ subject
+ end
+
+ it 'does not initialize a new test suite' do
+ expect(Gitlab::Ci::Reports::TestSuite).not_to receive(:new)
+
+ is_expected.to be_a(Gitlab::Ci::Reports::TestSuite)
+ end
+ end
+ end
+ end
+
+ describe '#total_time' do
+ subject { test_reports.total_time }
+
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total time' do
+ is_expected.to eq(6.66)
+ end
+ end
+
+ describe '#total_count' do
+ subject { test_reports.total_count }
+
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns the total count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ describe '#total_status' do
+ subject { test_reports.total_status }
+
+ context 'when all test cases succeeded' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_success)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+
+ context 'when there is a skipped test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_skipped)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is an error test case' do
+ before do
+ test_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
+ test_reports.get_suite('junit').add_test_case(create_test_case_java_error)
+ end
+
+ it 'returns correct total status' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}_count" do
+ subject { test_reports.public_send("#{status_type}_count") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_reports.get_suite('rspec').add_test_case(public_send("create_test_case_rspec_#{status_type}"))
+ test_reports.get_suite('junit').add_test_case(public_send("create_test_case_java_#{status_type}"))
+ end
+
+ it 'returns the count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be(0)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
new file mode 100644
index 00000000000..6ab16e5518d
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
@@ -0,0 +1,225 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestSuiteComparer do
+ include TestReportsHelper
+
+ let(:comparer) { described_class.new(name, base_suite, head_suite) }
+ let(:name) { 'rpsec' }
+ let(:base_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:head_suite) { Gitlab::Ci::Reports::TestSuite.new(name) }
+ let(:test_case_success) { create_test_case_rspec_success }
+ let(:test_case_failed) { create_test_case_rspec_failed }
+
+ let(:test_case_resolved) do
+ create_test_case_rspec_failed.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ describe '#new_failures' do
+ subject { comparer.new_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to eq([test_case_failed])
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#existing_failures' do
+ subject { comparer.existing_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to eq([test_case_failed])
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#resolved_failures' do
+ subject { comparer.resolved_failures }
+
+ context 'when head sutie has a newly failed test case which does not exist in base' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the failed test case' do
+ is_expected.to be_empty
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(0)
+ end
+ end
+
+ context 'when head sutie still has a failed test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'does not return the failed test case' do
+ is_expected.to be_empty
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(0)
+ end
+ end
+
+ context 'when head sutie has a success test case which failed in base' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_resolved)
+ end
+
+ it 'does not return the resolved test case' do
+ is_expected.to eq([test_case_resolved])
+ end
+
+ it 'returns the correct resolved count' do
+ expect(comparer.resolved_count).to eq(1)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { comparer.total_count }
+
+ before do
+ head_suite.add_test_case(test_case_success)
+ end
+
+ it 'returns the total test counts in head suite' do
+ is_expected.to eq(1)
+ end
+ end
+
+ describe '#failed_count' do
+ subject { comparer.failed_count }
+
+ context 'when there are a new failure and an existing failure' do
+ let(:test_case_1_success) { create_test_case_rspec_success }
+ let(:test_case_2_failed) { create_test_case_rspec_failed }
+
+ let(:test_case_1_failed) do
+ create_test_case_rspec_success.tap do |test_case|
+ test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+
+ before do
+ base_suite.add_test_case(test_case_1_success)
+ base_suite.add_test_case(test_case_2_failed)
+ head_suite.add_test_case(test_case_1_failed)
+ head_suite.add_test_case(test_case_2_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(2)
+ end
+ end
+
+ context 'when there is a new failure' do
+ before do
+ base_suite.add_test_case(test_case_success)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context 'when there is an existing failure' do
+ before do
+ base_suite.add_test_case(test_case_failed)
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the correct count' do
+ is_expected.to eq(1)
+ end
+ end
+ end
+
+ describe '#total_status' do
+ subject { comparer.total_status }
+
+ context 'when all test cases in head suite are success' do
+ before do
+ head_suite.add_test_case(test_case_success)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ end
+ end
+
+ context 'when there is a failed test case in head suite' do
+ before do
+ head_suite.add_test_case(test_case_failed)
+ end
+
+ it 'returns the total status in head suite' do
+ is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/test_suite_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
new file mode 100644
index 00000000000..cd34dbaf62f
--- /dev/null
+++ b/spec/lib/gitlab/ci/reports/test_suite_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Reports::TestSuite do
+ include TestReportsHelper
+
+ let(:test_suite) { described_class.new('Rspec') }
+ let(:test_case_success) { create_test_case_rspec_success }
+ let(:test_case_failed) { create_test_case_rspec_failed }
+ let(:test_case_skipped) { create_test_case_rspec_skipped }
+ let(:test_case_error) { create_test_case_rspec_error }
+
+ it { expect(test_suite.name).to eq('Rspec') }
+
+ describe '#add_test_case' do
+ context 'when status of the test case is success' do
+ it 'stores data correctly' do
+ test_suite.add_test_case(test_case_success)
+
+ expect(test_suite.test_cases[test_case_success.status][test_case_success.key])
+ .to eq(test_case_success)
+ expect(test_suite.total_time).to eq(1.11)
+ end
+ end
+
+ context 'when status of the test case is failed' do
+ it 'stores data correctly' do
+ test_suite.add_test_case(test_case_failed)
+
+ expect(test_suite.test_cases[test_case_failed.status][test_case_failed.key])
+ .to eq(test_case_failed)
+ expect(test_suite.total_time).to eq(2.22)
+ end
+ end
+
+ context 'when two test cases are added' do
+ it 'sums up total time' do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+
+ expect(test_suite.total_time).to eq(3.33)
+ end
+ end
+ end
+
+ describe '#total_count' do
+ subject { test_suite.total_count }
+
+ before do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+ end
+
+ it { is_expected.to eq(2) }
+ end
+
+ describe '#total_status' do
+ subject { test_suite.total_status }
+
+ context 'when all test cases succeeded' do
+ before do
+ test_suite.add_test_case(test_case_success)
+ end
+
+ it { is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS) }
+ end
+
+ context 'when a test case failed' do
+ before do
+ test_suite.add_test_case(test_case_success)
+ test_suite.add_test_case(test_case_failed)
+ end
+
+ it { is_expected.to eq(Gitlab::Ci::Reports::TestCase::STATUS_FAILED) }
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}" do
+ subject { test_suite.public_send("#{status_type}") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_suite.add_test_case(public_send("test_case_#{status_type}"))
+ end
+
+ it 'returns all success test cases' do
+ is_expected.to eq( { public_send("test_case_#{status_type}").key => public_send("test_case_#{status_type}") })
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+ end
+ end
+
+ Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status_type|
+ describe "##{status_type}_count" do
+ subject { test_suite.public_send("#{status_type}_count") }
+
+ context "when #{status_type} test case exists" do
+ before do
+ test_suite.add_test_case(public_send("test_case_#{status_type}"))
+ end
+
+ it 'returns the count' do
+ is_expected.to eq(1)
+ end
+ end
+
+ context "when #{status_type} test case do not exist" do
+ it 'returns nothing' do
+ is_expected.to be(0)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb
index cadb424ea2c..b6676b40fd3 100644
--- a/spec/lib/gitlab/ci/status/build/failed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb
@@ -80,4 +80,31 @@ describe Gitlab::Ci::Status::Build::Failed do
end
end
end
+
+ describe 'covers all failure reasons' do
+ let(:status) { Gitlab::Ci::Status::Failed.new(build, user) }
+ let(:tooltip) { subject.status_tooltip }
+
+ CommitStatus.failure_reasons.keys.each do |failure_reason|
+ context failure_reason do
+ before do
+ build.failure_reason = failure_reason
+ end
+
+ it "is a valid status" do
+ expect { tooltip }.not_to raise_error
+ end
+ end
+ end
+
+ context 'invalid failure message' do
+ before do
+ expect(build).to receive(:failure_reason) { 'invalid failure message' }
+ end
+
+ it "is an invalid status" do
+ expect { tooltip }.to raise_error(/key not found:/)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index e2bb378f663..02f8c4c114b 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Play do
context 'when user can not push to the branch' do
before do
build.project.add_developer(user)
- create(:protected_branch, :masters_can_push,
+ create(:protected_branch, :maintainers_can_push,
name: build.ref, project: project)
end
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
index 6ec35f8da7e..bb2d0a2c75c 100644
--- a/spec/lib/gitlab/ci/status/stage/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do
context 'when user has permission to read pipeline' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has details' do
diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
index adb3ff4321f..46874662edd 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -75,6 +75,14 @@ describe Gitlab::Ci::Variables::Collection::Item do
expect(resource).to eq variable
end
+ it 'supports using a hash with stringified values' do
+ variable = { 'key' => 'VARIABLE', 'value' => 'my value' }
+
+ resource = described_class.fabricate(variable)
+
+ expect(resource).to eq(key: 'VARIABLE', value: 'my value')
+ end
+
it 'supports using an active record resource' do
variable = create(:ci_variable, key: 'CI_VAR', value: '123')
resource = described_class.fabricate(variable)
diff --git a/spec/lib/gitlab/cleanup/project_uploads_spec.rb b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
new file mode 100644
index 00000000000..37b38776775
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/project_uploads_spec.rb
@@ -0,0 +1,278 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Cleanup::ProjectUploads do
+ subject { described_class.new(logger: logger) }
+ let(:logger) { double(:logger) }
+
+ before do
+ allow(logger).to receive(:info).at_least(1).times
+ allow(logger).to receive(:debug).at_least(1).times
+ end
+
+ describe '#run!' do
+ shared_examples_for 'moves the file' do
+ shared_examples_for 'a real run' do
+ let(:args) { [dry_run: false] }
+
+ it 'moves the file to its proper location' do
+ subject.run!(*args)
+
+ expect(File.exist?(path)).to be_falsey
+ expect(File.exist?(new_path)).to be_truthy
+ end
+
+ it 'logs action as done' do
+ expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up...")
+ expect(logger).to receive(:info).with("Did #{action}")
+
+ subject.run!(*args)
+ end
+ end
+
+ shared_examples_for 'a dry run' do
+ it 'does not move the file' do
+ subject.run!(*args)
+
+ expect(File.exist?(path)).to be_truthy
+ expect(File.exist?(new_path)).to be_falsey
+ end
+
+ it 'logs action as able to be done' do
+ expect(logger).to receive(:info).with("Looking for orphaned project uploads to clean up. Dry run...")
+ expect(logger).to receive(:info).with("Can #{action}")
+
+ subject.run!(*args)
+ end
+ end
+
+ context 'when dry_run is false' do
+ let(:args) { [dry_run: false] }
+
+ it_behaves_like 'a real run'
+ end
+
+ context 'when dry_run is nil' do
+ let(:args) { [dry_run: nil] }
+
+ it_behaves_like 'a real run'
+ end
+
+ context 'when dry_run is true' do
+ let(:args) { [dry_run: true] }
+
+ it_behaves_like 'a dry run'
+ end
+
+ context 'with dry_run not specified' do
+ let(:args) { [] }
+
+ it_behaves_like 'a dry run'
+ end
+ end
+
+ shared_examples_for 'moves the file to lost and found' do
+ let(:action) { "move to lost and found #{path} -> #{new_path}" }
+
+ it_behaves_like 'moves the file'
+ end
+
+ shared_examples_for 'fixes the file' do
+ let(:action) { "fix #{path} -> #{new_path}" }
+
+ it_behaves_like 'moves the file'
+ end
+
+ context 'orphaned project upload file' do
+ context 'when an upload record matching the secret and filename is found' do
+ context 'when the project is still in legacy storage' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: create(:project, :legacy_storage)) }
+ let(:new_path) { orphaned.absolute_path }
+ let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mv(new_path, path)
+ end
+
+ it_behaves_like 'fixes the file'
+ end
+
+ context 'when the project was moved to hashed storage' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file) }
+ let(:new_path) { orphaned.absolute_path }
+ let(:path) { File.join(FileUploader.root, 'some', 'wrong', 'location', orphaned.path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.mv(new_path, path)
+ end
+
+ it_behaves_like 'fixes the file'
+ end
+
+ context 'when the project is missing (the upload *record* is an orphan)' do
+ let(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) }
+ let!(:path) { orphaned.absolute_path }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) }
+
+ before do
+ orphaned.model.delete
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+
+ # We will probably want to add logic (Reschedule background upload) to
+ # cover Case 2 in https://gitlab.com/gitlab-org/gitlab-ce/issues/46535#note_75355104
+ context 'when the file should be in object storage' do
+ context 'when the file otherwise has the correct local path' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) }
+ let!(:path) { File.join(FileUploader.root, orphaned.model.full_path, orphaned.path) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ it 'does not move the file' do
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ # E.g. the upload file was orphaned, and then uploads were migrated to
+ # object storage
+ context 'when the file has the wrong local path' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :object_storage, model: build(:project, :legacy_storage)) }
+ let!(:path) { File.join(FileUploader.root, 'wrong', orphaned.path) }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', 'wrong', orphaned.path) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+ end
+ end
+
+ context 'when a matching upload record can not be found' do
+ context 'when the file path fits the known pattern' do
+ let!(:orphaned) { create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage)) }
+ let!(:path) { orphaned.absolute_path }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', orphaned.model.full_path, orphaned.path) }
+
+ before do
+ orphaned.delete
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+
+ context 'when the file path does not fit the known pattern' do
+ let!(:invalid_path) { File.join('group', 'file.jpg') }
+ let!(:path) { File.join(FileUploader.root, invalid_path) }
+ let!(:new_path) { File.join(FileUploader.root, '-', 'project-lost-found', invalid_path) }
+
+ before do
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+ end
+
+ after do
+ File.delete(path) if File.exist?(path)
+ end
+
+ it_behaves_like 'moves the file to lost and found'
+ end
+ end
+ end
+
+ context 'non-orphaned project upload file' do
+ it 'does not move the file' do
+ tracked = create(:upload, :issuable_upload, :with_file, model: build(:project, :legacy_storage))
+ tracked_path = tracked.absolute_path
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ expect(File.exist?(tracked_path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(tracked_path)).to be_truthy
+ end
+ end
+
+ context 'ignorable cases' do
+ # Because we aren't concerned about these, and can save a lot of
+ # processing time by ignoring them. If we wish to cleanup hashed storage
+ # directories, it should simply require removing this test and modifying
+ # the find command.
+ context 'when the file is already in hashed storage' do
+ let(:project) { create(:project) }
+
+ before do
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ end
+
+ it 'does not move even an orphan file' do
+ orphaned = create(:upload, :issuable_upload, :with_file, model: project)
+ path = orphaned.absolute_path
+ orphaned.delete
+
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ it 'does not move any non-project (FileUploader) uploads' do
+ paths = []
+ orphaned1 = create(:upload, :personal_snippet_upload, :with_file)
+ orphaned2 = create(:upload, :namespace_upload, :with_file)
+ orphaned3 = create(:upload, :attachment_upload, :with_file)
+ paths << orphaned1.absolute_path
+ paths << orphaned2.absolute_path
+ paths << orphaned3.absolute_path
+ Upload.delete_all
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ paths.each do |path|
+ expect(File.exist?(path)).to be_truthy
+ end
+
+ subject.run!(dry_run: false)
+
+ paths.each do |path|
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+
+ it 'does not move any uploads in tmp (which would interfere with ongoing upload activity)' do
+ path = File.join(FileUploader.root, 'tmp', 'foo.jpg')
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.touch(path)
+
+ expect(logger).not_to receive(:info).with(/move|fix/i)
+ expect(File.exist?(path)).to be_truthy
+
+ subject.run!(dry_run: false)
+
+ expect(File.exist?(path)).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cleanup/remote_uploads_spec.rb b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
new file mode 100644
index 00000000000..8d03baeb07b
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/remote_uploads_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Cleanup::RemoteUploads do
+ context 'when object_storage is enabled' do
+ let(:connection) { double }
+ let(:directory) { double }
+ let!(:uploads) do
+ [
+ create(:upload, path: 'dir/file1', store: ObjectStorage::Store::REMOTE),
+ create(:upload, path: 'dir/file2', store: ObjectStorage::Store::LOCAL)
+ ]
+ end
+ let(:remote_files) do
+ [
+ double(key: 'dir/file1'),
+ double(key: 'dir/file2'),
+ double(key: 'dir/file3'),
+ double(key: 'lost_and_found/dir/file3')
+ ]
+ end
+
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ expect(::Fog::Storage).to receive(:new).and_return(connection)
+
+ expect(connection).to receive(:directories).and_return(double(get: directory))
+ expect(directory).to receive(:files).and_return(remote_files)
+ end
+
+ context 'when dry_run is set to false' do
+ subject { described_class.new.run!(dry_run: false) }
+
+ it 'moves files that are not in uploads table' do
+ expect(remote_files[0]).not_to receive(:copy)
+ expect(remote_files[0]).not_to receive(:destroy)
+ expect(remote_files[1]).to receive(:copy)
+ expect(remote_files[1]).to receive(:destroy)
+ expect(remote_files[2]).to receive(:copy)
+ expect(remote_files[2]).to receive(:destroy)
+ expect(remote_files[3]).not_to receive(:copy)
+ expect(remote_files[3]).not_to receive(:destroy)
+
+ subject
+ end
+ end
+
+ context 'when dry_run is set to true' do
+ subject { described_class.new.run!(dry_run: true) }
+
+ it 'does not move filese' do
+ expect(remote_files[0]).not_to receive(:copy)
+ expect(remote_files[0]).not_to receive(:destroy)
+ expect(remote_files[1]).not_to receive(:copy)
+ expect(remote_files[1]).not_to receive(:destroy)
+ expect(remote_files[2]).not_to receive(:copy)
+ expect(remote_files[2]).not_to receive(:destroy)
+ expect(remote_files[3]).not_to receive(:copy)
+ expect(remote_files[3]).not_to receive(:destroy)
+
+ subject
+ end
+ end
+ end
+
+ context 'when object_storage is not enabled' do
+ it 'does not connect to any storage' do
+ expect(::Fog::Storage).not_to receive(:new)
+
+ subject
+ end
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 8d4862932b2..1f35d1e4880 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::ClosingIssueExtractor do
before do
project.add_developer(project.creator)
project.add_developer(project2.creator)
- project2.add_master(project.creator)
+ project2.add_maintainer(project.creator)
end
describe "#closed_by_message" do
@@ -298,7 +298,7 @@ describe Gitlab::ClosingIssueExtractor do
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
- jira_project.add_master(jira_project.creator)
+ jira_project.add_maintainer(jira_project.creator)
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
closing_issue_extractor = described_class.new(jira_project, jira_project.creator)
message = "Resolve #{jira_issue.to_reference}"
@@ -379,6 +379,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
+ it 'allows non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference} #{reference2} #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
+ it 'allows mixed comma-separated and non-comma-separated issue numbers in single line message' do
+ message = "Closes #{reference}, #{reference2} and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
index 6de4bd3dc7c..f670c7f6c75 100644
--- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -36,9 +36,9 @@ describe Gitlab::CycleAnalytics::Permissions do
end
end
- context 'user is master' do
+ context 'user is maintainer' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'has permissions to issue stage' do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index b411aaa19da..0a8c77b0ad9 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -281,7 +281,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
it "doesn't break when the namespace was renamed" do
subject.rename_namespace(namespace)
- namespace.update_attributes!(path: 'renamed-afterwards')
+ namespace.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index b4896d69077..d4d7a83921c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -169,7 +169,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
it "doesn't break when the project was renamed" do
subject.rename_project(project)
- project.update_attributes!(path: 'renamed-afterwards')
+ project.update!(path: 'renamed-afterwards')
expect { subject.revert_renames }.not_to raise_error
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8bb246aa4bd..7d76519dddd 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -65,6 +65,28 @@ describe Gitlab::Database do
end
end
+ describe '.postgresql_9_or_less?' do
+ it 'returns false when using MySQL' do
+ allow(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(described_class.postgresql_9_or_less?).to eq(false)
+ end
+
+ it 'returns true when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.postgresql_9_or_less?).to eq(true)
+ end
+
+ it 'returns false when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.postgresql_9_or_less?).to eq(false)
+ end
+ end
+
describe '.join_lateral_supported?' do
it 'returns false when using MySQL' do
allow(described_class).to receive(:postgresql?).and_return(false)
@@ -109,6 +131,70 @@ describe Gitlab::Database do
end
end
+ describe '.pg_wal_lsn_diff' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff')
+ end
+ end
+
+ describe '.pg_current_wal_insert_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn')
+ end
+ end
+
+ describe '.pg_last_wal_receive_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn')
+ end
+ end
+
+ describe '.pg_last_wal_replay_lsn' do
+ it 'returns old name when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location')
+ end
+
+ it 'returns new name when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn')
+ end
+ end
+
describe '.nulls_last_order' do
context 'when using PostgreSQL' do
before do
@@ -357,6 +443,35 @@ describe Gitlab::Database do
end
end
+ describe '.db_read_only?' do
+ context 'when using PostgreSQL' do
+ before do
+ allow(ActiveRecord::Base.connection).to receive(:execute).and_call_original
+ expect(described_class).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'detects a read only database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "t" }])
+
+ expect(described_class.db_read_only?).to be_truthy
+ end
+
+ it 'detects a read write database' do
+ allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => "f" }])
+
+ expect(described_class.db_read_only?).to be_falsey
+ end
+ end
+
+ context 'when using MySQL' do
+ before do
+ expect(described_class).to receive(:postgresql?).and_return(false)
+ end
+
+ it { expect(described_class.db_read_only?).to be_falsey }
+ end
+ end
+
describe '#sanitize_timestamp' do
let(:max_timestamp) { Time.at((1 << 31) - 1) }
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index f48ee8924e8..79287021981 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -20,6 +20,15 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
diff_files
end
+ it 'it uses a different cache key if diff line keys change' do
+ mr_diff = described_class.new(merge_request.merge_request_diff, diff_options: nil)
+ key = mr_diff.cache_key
+
+ stub_const('Gitlab::Diff::Line::SERIALIZE_KEYS', [:foo])
+
+ expect(mr_diff.cache_key).not_to eq(key)
+ end
+
shared_examples 'initializes a DiffCollection' do
it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 5dfbb8e71f8..ebeb05d6e02 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -26,6 +26,21 @@ describe Gitlab::Diff::File do
end
end
+ describe '#diff_lines_for_serializer' do
+ it 'includes bottom match line if not in the end' do
+ expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
+ end
+
+ context 'when deleted' do
+ let(:commit) { project.commit('d59c60028b053793cecfb4022de34602e1a9218e') }
+ let(:diff_file) { commit.diffs.diff_file_with_old_path('files/js/commit.js.coffee') }
+
+ it 'does not include bottom match line' do
+ expect(diff_file.diff_lines_for_serializer.last.type).not_to eq('match')
+ end
+ end
+ end
+
describe '#mode_changed?' do
it { expect(diff_file.mode_changed?).to be_falsey }
end
diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
index b5c1a360ba9..ae61ece8029 100644
--- a/spec/lib/additional_email_headers_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe AdditionalEmailHeadersInterceptor do
+describe Gitlab::Email::Hook::AdditionalHeadersInterceptor do
let(:mail) do
ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
end
diff --git a/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
new file mode 100644
index 00000000000..4497d4002da
--- /dev/null
+++ b/spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::Email::Hook::DeliveryMetricsObserver do
+ let(:email) do
+ ActionMailer::Base.mail(to: 'test@example.com',
+ from: 'info@example.com',
+ body: 'hello')
+ end
+
+ context 'when email has been delivered' do
+ it 'increments both email delivery metrics' do
+ expect(described_class.delivery_attempts_counter).to receive(:increment)
+ expect(described_class.delivered_emails_counter).to receive(:increment)
+
+ email.deliver_now
+ end
+ end
+
+ context 'when email has not been delivered due to an error' do
+ before do
+ allow(email.delivery_method).to receive(:deliver!)
+ .and_raise(StandardError, 'Some SMTP error')
+ end
+
+ it 'increments only delivery attempt metric' do
+ expect(described_class.delivery_attempts_counter)
+ .to receive(:increment)
+ expect(described_class.delivered_emails_counter)
+ .not_to receive(:increment)
+
+ expect { email.deliver_now }
+ .to raise_error(StandardError, 'Some SMTP error')
+ end
+ end
+end
diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
index 3652d928c43..91aa3bc7c2e 100644
--- a/spec/lib/disable_email_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe DisableEmailInterceptor do
+describe Gitlab::Email::Hook::DisableEmailInterceptor do
before do
Mail.register_interceptor(described_class)
end
diff --git a/spec/lib/gitlab/exclusive_lease_helpers_spec.rb b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
new file mode 100644
index 00000000000..2e3656b52fb
--- /dev/null
+++ b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state do
+ include ::ExclusiveLeaseHelpers
+
+ let(:class_instance) { (Class.new { include ::Gitlab::ExclusiveLeaseHelpers }).new }
+ let(:unique_key) { SecureRandom.hex(10) }
+
+ describe '#in_lock' do
+ subject { class_instance.in_lock(unique_key, **options) { } }
+
+ let(:options) { {} }
+
+ context 'when the lease is not obtained yet' do
+ before do
+ stub_exclusive_lease(unique_key, 'uuid')
+ end
+
+ it 'calls the given block' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'calls the given block continuously' do
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ expect { |b| class_instance.in_lock(unique_key, &b) }.to yield_control.once
+ end
+
+ it 'cancels the exclusive lease after the block' do
+ expect_to_cancel_exclusive_lease(unique_key, 'uuid')
+
+ subject
+ end
+ end
+
+ context 'when the lease is obtained already' do
+ let!(:lease) { stub_exclusive_lease_taken(unique_key) }
+
+ it 'retries to obtain a lease and raises an error' do
+ expect(lease).to receive(:try_obtain).exactly(11).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+
+ context 'when ttl is specified' do
+ let(:options) { { ttl: 10.minutes } }
+
+ it 'receives the specified argument' do
+ expect(Gitlab::ExclusiveLease).to receive(:new).with(unique_key, { timeout: 10.minutes } )
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when retry count is specified' do
+ let(:options) { { retries: 3 } }
+
+ it 'retries for the specified times' do
+ expect(lease).to receive(:try_obtain).exactly(4).times
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+
+ context 'when sleep second is specified' do
+ let(:options) { { retries: 0, sleep_sec: 0.05.seconds } }
+
+ it 'receives the specified argument' do
+ expect(class_instance).to receive(:sleep).with(0.05.seconds).once
+
+ expect { subject }.to raise_error('Failed to obtain a lock')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 13df8531b63..ef52a25f47e 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -20,37 +20,55 @@ describe Gitlab::Gfm::UploadsRewriter do
"Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
end
- describe '#rewrite' do
- let!(:new_text) { rewriter.rewrite(new_project) }
+ shared_examples "files are accessible" do
+ describe '#rewrite' do
+ let!(:new_text) { rewriter.rewrite(new_project) }
- let(:old_files) { [image_uploader, zip_uploader].map(&:file) }
- let(:new_files) do
- described_class.new(new_text, new_project, user).files
- end
+ let(:old_files) { [image_uploader, zip_uploader] }
+ let(:new_files) do
+ described_class.new(new_text, new_project, user).files
+ end
- let(:old_paths) { old_files.map(&:path) }
- let(:new_paths) { new_files.map(&:path) }
+ let(:old_paths) { old_files.map(&:path) }
+ let(:new_paths) { new_files.map(&:path) }
- it 'rewrites content' do
- expect(new_text).not_to eq text
- expect(new_text.length).to eq text.length
- end
+ it 'rewrites content' do
+ expect(new_text).not_to eq text
+ expect(new_text.length).to eq text.length
+ end
- it 'copies files' do
- expect(new_files).to all(exist)
- expect(old_paths).not_to match_array new_paths
- expect(old_paths).to all(include(old_project.disk_path))
- expect(new_paths).to all(include(new_project.disk_path))
- end
+ it 'copies files' do
+ expect(new_files).to all(exist)
+ expect(old_paths).not_to match_array new_paths
+ expect(old_paths).to all(include(old_project.disk_path))
+ expect(new_paths).to all(include(new_project.disk_path))
+ end
- it 'does not remove old files' do
- expect(old_files).to all(exist)
+ it 'does not remove old files' do
+ expect(old_files).to all(exist)
+ end
+
+ it 'generates a new secret for each file' do
+ expect(new_paths).not_to include image_uploader.secret
+ expect(new_paths).not_to include zip_uploader.secret
+ end
end
+ end
- it 'generates a new secret for each file' do
- expect(new_paths).not_to include image_uploader.secret
- expect(new_paths).not_to include zip_uploader.secret
+ context "file are stored locally" do
+ include_examples "files are accessible"
+ end
+
+ context "files are stored remotely" do
+ before do
+ stub_uploads_object_storage(FileUploader)
+
+ old_files.each do |file|
+ file.migrate!(ObjectStorage::Store::REMOTE)
+ end
end
+
+ include_examples "files are accessible"
end
describe '#needs_rewrite?' do
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
index 5d22dcfb508..ca067a29174 100644
--- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::AttributesAtRefParser, seed_helper: true do
+describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb
index 2d103123998..18ebfef38f0 100644
--- a/spec/lib/gitlab/git/attributes_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_parser_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::AttributesParser, seed_helper: true do
+describe Gitlab::Git::AttributesParser, :seed_helper do
let(:attributes_path) { File.join(SEED_STORAGE_PATH, 'with-git-attributes.git', 'info', 'attributes') }
let(:data) { File.read(attributes_path) }
diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb
index ba790b717ae..e704d1c673c 100644
--- a/spec/lib/gitlab/git/blame_spec.rb
+++ b/spec/lib/gitlab/git/blame_spec.rb
@@ -1,7 +1,7 @@
# coding: utf-8
require "spec_helper"
-describe Gitlab::Git::Blame, seed_helper: true do
+describe Gitlab::Git::Blame, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:blame) do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb
index d6d365f6492..6effec8295c 100644
--- a/spec/lib/gitlab/git/blob_snippet_spec.rb
+++ b/spec/lib/gitlab/git/blob_snippet_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe Gitlab::Git::BlobSnippet, seed_helper: true do
+describe Gitlab::Git::BlobSnippet, :seed_helper do
describe '#data' do
context 'empty lines' do
let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) }
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index b6061df349d..ea49502ae2e 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe Gitlab::Git::Blob, seed_helper: true do
+describe Gitlab::Git::Blob, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
describe 'initialize' do
@@ -178,77 +178,67 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
describe '.batch' do
- shared_examples 'loading blobs in batch' do
- let(:blob_references) do
- [
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
- ]
- end
+ let(:blob_references) do
+ [
+ [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
+ [SeedRepo::Commit::ID, 'six']
+ ]
+ end
- subject { described_class.batch(repository, blob_references) }
+ subject { described_class.batch(repository, blob_references) }
- it { expect(subject.size).to eq(blob_references.size) }
+ it { expect(subject.size).to eq(blob_references.size) }
- context 'first blob' do
- let(:blob) { subject[0] }
+ context 'first blob' do
+ let(:blob) { subject[0] }
- it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
- it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
- it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
- it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
- it { expect(blob.size).to eq(669) }
- it { expect(blob.mode).to eq("100644") }
- end
+ it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
+ it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
+ it { expect(blob.path).to eq("files/ruby/popen.rb") }
+ it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
+ it { expect(blob.size).to eq(669) }
+ it { expect(blob.mode).to eq("100644") }
+ end
- context 'second blob' do
- let(:blob) { subject[1] }
+ context 'second blob' do
+ let(:blob) { subject[1] }
- it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
- it { expect(blob.data).to eq('') }
- it 'does not mark the blob as binary' do
- expect(blob).not_to be_binary
- end
+ it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
+ it { expect(blob.data).to eq('') }
+ it 'does not mark the blob as binary' do
+ expect(blob).not_to be_binary
end
+ end
- context 'limiting' do
- subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
+ context 'limiting' do
+ subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'positive' do
- let(:blob_size_limit) { 10 }
+ context 'positive' do
+ let(:blob_size_limit) { 10 }
- it { expect(subject.first.data.size).to eq(10) }
- end
+ it { expect(subject.first.data.size).to eq(10) }
+ end
- context 'zero' do
- let(:blob_size_limit) { 0 }
+ context 'zero' do
+ let(:blob_size_limit) { 0 }
- it 'only loads the metadata' do
- expect(subject.first.size).not_to be(0)
- expect(subject.first.data).to eq('')
- end
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
end
+ end
- context 'negative' do
- let(:blob_size_limit) { -1 }
+ context 'negative' do
+ let(:blob_size_limit) { -1 }
- it 'ignores MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
+ it 'ignores MAX_DATA_DISPLAY_SIZE' do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
- expect(subject.first.data.size).to eq(669)
- end
+ expect(subject.first.data.size).to eq(669)
end
end
end
-
- context 'when Gitaly list_blobs_by_sha_path feature is enabled' do
- it_behaves_like 'loading blobs in batch'
- end
-
- context 'when Gitaly list_blobs_by_sha_path feature is disabled', :disable_gitaly do
- it_behaves_like 'loading blobs in batch'
- end
end
describe '.batch_metadata' do
@@ -294,58 +284,48 @@ describe Gitlab::Git::Blob, seed_helper: true do
)
end
- shared_examples 'fetching batch of LFS pointers' do
- it 'returns a list of Gitlab::Git::Blob' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- expect(blobs).to be_an(Array)
- end
-
- it 'accepts blob IDs as a lazy enumerator' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
-
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- end
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
- it 'handles empty list of IDs gracefully' do
- blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
- blobs_2 = described_class.batch_lfs_pointers(repository, [])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ expect(blobs).to be_an(Array)
+ end
- expect(blobs_1).to eq([])
- expect(blobs_2).to eq([])
- end
+ it 'accepts blob IDs as a lazy enumerator' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
- expect(blobs).to eq([])
- end
+ it 'handles empty list of IDs gracefully' do
+ blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
+ blobs_2 = described_class.batch_lfs_pointers(repository, [])
- it 'silently ignores non lfs objects' do
- blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ expect(blobs_1).to eq([])
+ expect(blobs_2).to eq([])
+ end
- expect(blobs).to eq([])
- end
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
- it 'avoids loading large blobs into memory' do
- # This line could call `lookup` on `repository`, so do here before mocking.
- non_lfs_blob_id = non_lfs_blob.id
+ expect(blobs).to eq([])
+ end
- expect(repository).not_to receive(:lookup)
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
- described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
- end
+ expect(blobs).to eq([])
end
- context 'when Gitaly batch_lfs_pointers is enabled' do
- it_behaves_like 'fetching batch of LFS pointers'
- end
+ it 'avoids loading large blobs into memory' do
+ # This line could call `lookup` on `repository`, so do here before mocking.
+ non_lfs_blob_id = non_lfs_blob.id
+
+ expect(repository).not_to receive(:lookup)
- context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do
- it_behaves_like 'fetching batch of LFS pointers'
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
end
end
@@ -532,8 +512,8 @@ describe Gitlab::Git::Blob, seed_helper: true do
subject { blob.load_all_data!(repository) }
it 'loads missing data' do
- expect(Gitlab::GitalyClient).to receive(:migrate)
- .with(:git_blob_load_all_data).and_return(full_data)
+ expect(repository.gitaly_blob_client).to receive(:get_blob)
+ .and_return(double(:response, data: full_data))
subject
@@ -544,8 +524,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
let(:blob) { Gitlab::Git::Blob.new(name: 'test', size: 4, data: full_data) }
it "doesn't perform any loading" do
- expect(Gitlab::GitalyClient).not_to receive(:migrate)
- .with(:git_blob_load_all_data)
+ expect(repository.gitaly_blob_client).not_to receive(:get_blob)
subject
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index ec1a684cfbc..79ccbb79966 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -1,7 +1,12 @@
require "spec_helper"
-describe Gitlab::Git::Branch, seed_helper: true do
+describe Gitlab::Git::Branch, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged
+ end
+ end
subject { repository.branches }
@@ -124,6 +129,7 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
- repository.create_commit(params.merge(committer: committer.merge(time: Time.now)))
+ params[:message].delete!("\r")
+ Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
end
end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index ee74c2769eb..2718a3c5e49 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Commit, seed_helper: true do
+describe Gitlab::Git::Commit, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
let(:rugged_commit) do
@@ -27,7 +27,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
}
@parents = [repo.head.target]
- @gitlab_parents = @parents.map { |c| described_class.decorate(repository, c) }
+ @gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) }
@tree = @parents.first.tree
sha = Rugged::Commit.create(
@@ -41,7 +41,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
)
@raw_commit = repo.lookup(sha)
- @commit = described_class.new(repository, @raw_commit)
+ @commit = described_class.find(repository, sha)
end
it { expect(@commit.short_id).to eq(@raw_commit.oid[0..10]) }
@@ -488,13 +488,15 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe '#init_from_rugged' do
- let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
- subject { gitlab_commit }
+ skip 'move this test to gitaly-ruby' do
+ describe '#init_from_rugged' do
+ let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
+ subject { gitlab_commit }
- describe '#id' do
- subject { super().id }
- it { is_expected.to eq(SeedRepo::Commit::ID) }
+ describe '#id' do
+ subject { super().id }
+ it { is_expected.to eq(SeedRepo::Commit::ID) }
+ end
end
end
diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
index 2100690f873..c7626058acd 100644
--- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb
+++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::CommitterWithHooks, seed_helper: true do
+describe Gitlab::Git::CommitterWithHooks, :seed_helper do
# TODO https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'needs to be moved to gitaly-ruby test suite' do
shared_examples 'calling wiki hooks' do
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index b6a42e422b5..7cc6f52f8ee 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Compare, seed_helper: true do
+describe Gitlab::Git::Compare, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) }
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 65edc750f39..81658874be7 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::DiffCollection, seed_helper: true do
+describe Gitlab::Git::DiffCollection, :seed_helper do
subject do
Gitlab::Git::DiffCollection.new(
iterator,
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 3bb0b5be15b..87d9fcee39e 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Diff, seed_helper: true do
+describe Gitlab::Git::Diff, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
before do
@@ -27,6 +27,7 @@ EOT
too_large: false
}
+ # TODO use a Gitaly diff object instead
@rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
[".gitmodules"]).patches.first
@@ -266,8 +267,12 @@ EOT
describe '#submodule?' do
before do
- commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- @diffs = commit.parents[0].diff(commit).patches
+ # TODO use a Gitaly diff object instead
+ rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ end
+
+ @diffs = rugged_commit.parents[0].diff(rugged_commit).patches
end
it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb
index 9337aa39e13..55ffced36ac 100644
--- a/spec/lib/gitlab/git/hooks_service_spec.rb
+++ b/spec/lib/gitlab/git/hooks_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::HooksService, seed_helper: true do
+describe Gitlab::Git::HooksService, :seed_helper do
let(:gl_id) { 'user-456' }
let(:gl_username) { 'janedoe' }
let(:user) { Gitlab::Git::User.new(gl_username, 'Jane Doe', 'janedoe@example.com', gl_id) }
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
index 16e6bd35449..c4edd6961e1 100644
--- a/spec/lib/gitlab/git/index_spec.rb
+++ b/spec/lib/gitlab/git/index_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-describe Gitlab::Git::Index, seed_helper: true do
+describe Gitlab::Git::Index, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:index) { described_class.new(repository) }
before do
- index.read_tree(repository.lookup('master').tree)
+ index.read_tree(lookup('master').tree)
end
around do |example|
@@ -30,7 +30,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
end
@@ -54,7 +54,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
+ expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content]))
end
end
@@ -68,7 +68,7 @@ describe Gitlab::Git::Index, seed_helper: true do
index.create(options)
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld")
+ expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld")
end
end
end
@@ -135,7 +135,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -190,7 +190,7 @@ describe Gitlab::Git::Index, seed_helper: true do
entry = index.get(options[:file_path])
expect(entry).not_to be_nil
- expect(repository.lookup(entry[:oid]).content).to eq(options[:content])
+ expect(lookup(entry[:oid]).content).to eq(options[:content])
end
it 'preserves file mode' do
@@ -232,4 +232,8 @@ describe Gitlab::Git::Index, seed_helper: true do
end
end
end
+
+ def lookup(revision)
+ repository.rugged.rev_parse(revision)
+ end
end
diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb
index b033ede9062..074e66d2a5d 100644
--- a/spec/lib/gitlab/git/popen_spec.rb
+++ b/spec/lib/gitlab/git/popen_spec.rb
@@ -2,6 +2,9 @@ require 'spec_helper'
describe 'Gitlab::Git::Popen' do
let(:path) { Rails.root.join('tmp').to_s }
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
let(:klass) do
Class.new(Object) do
@@ -70,6 +73,15 @@ describe 'Gitlab::Git::Popen' do
end
end
end
+
+ context 'with a process that writes a lot of data to stderr' do
+ it 'returns zero' do
+ output, status = klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
end
context 'popen_with_timeout' do
@@ -85,6 +97,17 @@ describe 'Gitlab::Git::Popen' do
it { expect(output).to include('tests') }
end
+ context 'multi-line string' do
+ let(:test_string) { "this is 1 line\n2nd line\n3rd line\n" }
+ let(:result) { klass.new.popen_with_timeout(['echo', test_string], timeout, path) }
+ let(:output) { result.first }
+ let(:status) { result.last }
+
+ it { expect(status).to be_zero }
+ # echo adds its own line
+ it { expect(output).to eq(test_string + "\n") }
+ end
+
context 'non-zero status' do
let(:result) { klass.new.popen_with_timeout(%w(cat NOTHING), timeout, path) }
let(:output) { result.first }
@@ -110,6 +133,13 @@ describe 'Gitlab::Git::Popen' do
it "handles processes that do not shutdown correctly" do
expect { klass.new.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error)
end
+
+ it 'handles process that writes a lot of data to stderr' do
+ output, status = klass.new.popen_with_timeout(spew_command, timeout, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
end
context 'timeout period' do
diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb
index eb148cc3804..53ed7c5a13a 100644
--- a/spec/lib/gitlab/git/remote_repository_spec.rb
+++ b/spec/lib/gitlab/git/remote_repository_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Git::RemoteRepository, seed_helper: true do
+describe Gitlab::Git::RemoteRepository, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
subject { described_class.new(repository) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 6ec4b90d70c..35a6fc94753 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1,7 +1,7 @@
# coding: utf-8
require "spec_helper"
-describe Gitlab::Git::Repository, seed_helper: true do
+describe Gitlab::Git::Repository, :seed_helper do
include Gitlab::EncodingHelper
using RSpec::Parameterized::TableSyntax
@@ -321,90 +321,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context '#submodules' do
- around do |example|
- # TODO #submodules will be removed, has been migrated to gitaly
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
-
- context 'where repo has submodules' do
- let(:submodules) { repository.send(:submodules, 'master') }
- let(:submodule) { submodules.first }
-
- it { expect(submodules).to be_kind_of Hash }
- it { expect(submodules.empty?).to be_falsey }
-
- it 'should have valid data' do
- expect(submodule).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should handle nested submodules correctly' do
- nested = submodules['nested/six']
- expect(nested['name']).to eq('nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should handle deeply nested submodules correctly' do
- nested = submodules['deeper/nested/six']
- expect(nested['name']).to eq('deeper/nested/six')
- expect(nested['url']).to eq('git://github.com/randx/six.git')
- expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
- end
-
- it 'should not have an entry for an invalid submodule' do
- expect(submodules).not_to have_key('invalid/path')
- end
-
- it 'should not have an entry for an uncommited submodule dir' do
- submodules = repository.send(:submodules, 'fix-existing-submodule-dir')
- expect(submodules).not_to have_key('submodule-existing-dir')
- end
-
- it 'should handle tags correctly' do
- submodules = repository.send(:submodules, 'v1.2.1')
-
- expect(submodules.first).to eq([
- "six", {
- "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
- "name" => "six",
- "url" => "git://github.com/randx/six.git"
- }
- ])
- end
-
- it 'should not break on invalid syntax' do
- allow(repository).to receive(:blob_content).and_return(<<-GITMODULES.strip_heredoc)
- [submodule "six"]
- path = six
- url = git://github.com/randx/six.git
-
- [submodule]
- foo = bar
- GITMODULES
-
- expect(submodules).to have_key('six')
- end
- end
-
- context 'where repo doesn\'t have submodules' do
- let(:submodules) { repository.send(:submodules, '6d39438') }
- it 'should return an empty hash' do
- expect(submodules).to be_empty
- end
- end
- end
-
describe '#commit_count' do
shared_examples 'simple commit counting' do
it { expect(repository.commit_count("master")).to eq(25) }
@@ -611,38 +527,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe "#remove_remote" do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.remove_remote("expendable")
- end
-
- it "should remove the remote" do
- expect(@repo.rugged.remotes).not_to include("expendable")
- end
-
- after(:all) do
- ensure_seeds
- end
- end
-
- describe "#remote_update" do
- before(:all) do
- @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- @repo.remote_update("expendable", url: TEST_NORMAL_REPO_PATH)
- end
-
- it "should add the remote" do
- expect(@repo.rugged.remotes["expendable"].url).to(
- eq(TEST_NORMAL_REPO_PATH)
- )
- end
-
- after(:all) do
- ensure_seeds
- end
- end
-
describe '#fetch_repository_as_mirror' do
let(:new_repository) do
Gitlab::Git::Repository.new('default', 'my_project.git', '')
@@ -699,58 +583,24 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#remote_tags' do
- let(:remote_name) { 'upstream' }
- let(:target_commit_id) { SeedRepo::Commit::ID }
- let(:tag_name) { 'v0.0.1' }
- let(:tag_message) { 'My tag' }
- let(:remote_repository) do
- Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
- end
-
- around do |example|
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- subject { repository.remote_tags(remote_name) }
-
- before do
- remote_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { remote_repository.path }
- repository.add_remote(remote_name, remote_repository_path)
- remote_repository.add_tag(tag_name, user: user, target: target_commit_id)
- end
-
- after do
- ensure_seeds
- end
-
- it 'gets the remote tags' do
- expect(subject.first).to be_an_instance_of(Gitlab::Git::Tag)
- expect(subject.first.name).to eq(tag_name)
- expect(subject.first.dereferenced_target.id).to eq(target_commit_id)
- end
- end
-
describe "#log" do
shared_examples 'repository log' do
let(:commit_with_old_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_old_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_old_name_id)
end
let(:commit_with_new_name) do
- Gitlab::Git::Commit.decorate(repository, @commit_with_new_name_id)
+ Gitlab::Git::Commit.find(repository, @commit_with_new_name_id)
end
let(:rename_commit) do
- Gitlab::Git::Commit.decorate(repository, @rename_commit_id)
+ Gitlab::Git::Commit.find(repository, @rename_commit_id)
end
before(:context) do
# Add new commits so that there's a renamed file in the commit history
repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- @commit_with_old_name_id = new_commit_edit_old_file(repo)
- @rename_commit_id = new_commit_move_file(repo)
- @commit_with_new_name_id = new_commit_edit_new_file(repo)
+ @commit_with_old_name_id = new_commit_edit_old_file(repo).oid
+ @rename_commit_id = new_commit_move_file(repo).oid
+ @commit_with_new_name_id = new_commit_edit_new_file(repo).oid
end
after(:context) do
@@ -952,8 +802,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
def commit_files(commit)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- commit.rugged_diff_from_parent.deltas.flat_map do |delta|
- [delta.old_file[:path], delta.new_file[:path]].uniq.compact
+ commit.deltas.flat_map do |delta|
+ [delta.old_path, delta.new_path].uniq.compact
end
end
end
@@ -990,10 +840,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'when Gitaly find_commits feature is enabled' do
it_behaves_like 'repository log'
end
-
- context 'when Gitaly find_commits feature is disabled', :disable_gitaly do
- it_behaves_like 'repository log'
- end
end
describe '#count_commits_between' do
@@ -1157,6 +1003,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = true
end
+ around do |example|
+ # OK because autocrlf is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'return the value of the autocrlf option' do
expect(@repo.autocrlf).to be(true)
end
@@ -1172,6 +1025,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = false
end
+ around do |example|
+ # OK because autocrlf= is only used in gitaly-ruby
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
it 'should set the autocrlf option to the provided option' do
@repo.autocrlf = :input
@@ -1186,50 +1046,17 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#find_branch' do
- shared_examples 'finding a branch' do
- it 'should return a Branch for master' do
- branch = repository.find_branch('master')
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
-
- it 'should handle non-existent branch' do
- branch = repository.find_branch('this-is-garbage')
+ it 'should return a Branch for master' do
+ branch = repository.find_branch('master')
- expect(branch).to eq(nil)
- end
- end
-
- context 'when Gitaly find_branch feature is enabled' do
- it_behaves_like 'finding a branch'
+ expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
+ expect(branch.name).to eq('master')
end
- context 'when Gitaly find_branch feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'finding a branch'
-
- context 'force_reload is true' do
- it 'should reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).twice.and_call_original
+ it 'should handle non-existent branch' do
+ branch = repository.find_branch('this-is-garbage')
- repository.find_branch('master')
- branch = repository.find_branch('master', force_reload: true)
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
-
- context 'force_reload is false' do
- it 'should not reload Rugged::Repository' do
- expect(Rugged::Repository).to receive(:new).once.and_call_original
-
- branch = repository.find_branch('master', force_reload: false)
-
- expect(branch).to be_a_kind_of(Gitlab::Git::Branch)
- expect(branch.name).to eq('master')
- end
- end
+ expect(branch).to eq(nil)
end
end
@@ -1402,94 +1229,84 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe "#copy_gitattributes" do
- shared_examples 'applying git attributes' do
- let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
+ let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
- after do
- FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
- end
-
- it "raises an error with invalid ref" do
- expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
- end
-
- context 'when forcing encoding issues' do
- let(:branch_name) { "ʕ•ᴥ•ʔ" }
+ after do
+ FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
+ end
- before do
- repository.create_branch(branch_name, "master")
- end
+ it "raises an error with invalid ref" do
+ expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
+ end
- after do
- repository.rm_branch(branch_name, user: build(:admin))
- end
+ context 'when forcing encoding issues' do
+ let(:branch_name) { "ʕ•ᴥ•ʔ" }
- it "doesn't raise with a valid unicode ref" do
- expect { repository.copy_gitattributes(branch_name) }.not_to raise_error
+ before do
+ repository.create_branch(branch_name, "master")
+ end
- repository
- end
+ after do
+ repository.rm_branch(branch_name, user: build(:admin))
end
- context "with no .gitattrbutes" do
- before do
- repository.copy_gitattributes("master")
- end
+ it "doesn't raise with a valid unicode ref" do
+ expect { repository.copy_gitattributes(branch_name) }.not_to raise_error
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
+ repository
end
+ end
- context "with .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- end
+ context "with no .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("master")
+ end
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
+ it "does not have an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_falsey
+ end
+ end
- it "has the same content in info/attributes as .gitattributes" do
- contents = File.open(attributes_path, "rb") { |f| f.read }
- expect(contents).to eq("*.md binary\n")
- end
+ context "with .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
end
- context "with updated .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("gitattributes-updated")
- end
+ it "has an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_truthy
+ end
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
+ it "has the same content in info/attributes as .gitattributes" do
+ contents = File.open(attributes_path, "rb") { |f| f.read }
+ expect(contents).to eq("*.md binary\n")
+ end
+ end
- it "has the updated content in info/attributes" do
- contents = File.read(attributes_path)
- expect(contents).to eq("*.txt binary\n")
- end
+ context "with updated .gitattrbutes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
+ repository.copy_gitattributes("gitattributes-updated")
end
- context "with no .gitattrbutes in HEAD but with previous info/attributes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("master")
- end
+ it "has an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_truthy
+ end
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
+ it "has the updated content in info/attributes" do
+ contents = File.read(attributes_path)
+ expect(contents).to eq("*.txt binary\n")
end
end
- context 'when gitaly is enabled' do
- it_behaves_like 'applying git attributes'
- end
+ context "with no .gitattrbutes in HEAD but with previous info/attributes" do
+ before do
+ repository.copy_gitattributes("gitattributes")
+ repository.copy_gitattributes("master")
+ end
- context 'when gitaly is disabled', :disable_gitaly do
- it_behaves_like 'applying git attributes'
+ it "does not have an info/attributes" do
+ expect(File.exist?(attributes_path)).to be_falsey
+ end
end
end
@@ -1567,31 +1384,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#batch_existence' do
- let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] }
-
- around do |example|
- # TODO #batch_existence isn't used anywhere, can we remove it?
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it 'returns existing refs back' do
- result = repository.batch_existence(refs)
-
- expect(result).to eq([SeedRepo::RubyBlob::ID])
- end
-
- context 'existing: true' do
- it 'inverts meaning and returns non-existing refs' do
- result = repository.batch_existence(refs, existing: false)
-
- expect(result).to eq(%w(deadbeef 909e6157199))
- end
- end
- end
-
describe '#local_branches' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1726,59 +1518,51 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#fetch_source_branch!' do
- shared_examples '#fetch_source_branch!' do
- let(:local_ref) { 'refs/merge-requests/1/head' }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
- let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
-
- after do
- ensure_seeds
- end
+ let(:local_ref) { 'refs/merge-requests/1/head' }
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
- context 'when the branch exists' do
- context 'when the commit does not exist locally' do
- let(:source_branch) { 'new-branch-for-fetch-source-branch' }
- let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
- let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
+ after do
+ ensure_seeds
+ end
- before do
- source_rugged.branches.create(source_branch, new_oid)
- end
+ context 'when the branch exists' do
+ context 'when the commit does not exist locally' do
+ let(:source_branch) { 'new-branch-for-fetch-source-branch' }
+ let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } }
+ let(:new_oid) { new_commit_edit_old_file(source_rugged).oid }
- it 'writes the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(new_oid)
- end
+ before do
+ source_rugged.branches.create(source_branch, new_oid)
end
- context 'when the commit exists locally' do
- let(:source_branch) { 'master' }
- let(:expected_oid) { SeedRepo::LastCommit::ID }
-
- it 'writes the ref' do
- # Sanity check: the commit should already exist
- expect(repository.commit(expected_oid)).not_to be_nil
-
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(expected_oid)
- end
+ it 'writes the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(new_oid)
end
end
- context 'when the branch does not exist' do
- let(:source_branch) { 'definitely-not-master' }
+ context 'when the commit exists locally' do
+ let(:source_branch) { 'master' }
+ let(:expected_oid) { SeedRepo::LastCommit::ID }
- it 'does not write the ref' do
- expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
- expect(repository.commit(local_ref)).to be_nil
+ it 'writes the ref' do
+ # Sanity check: the commit should already exist
+ expect(repository.commit(expected_oid)).not_to be_nil
+
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
+ expect(repository.commit(local_ref).sha).to eq(expected_oid)
end
end
end
- it_behaves_like '#fetch_source_branch!'
+ context 'when the branch does not exist' do
+ let(:source_branch) { 'definitely-not-master' }
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#fetch_source_branch!'
+ it 'does not write the ref' do
+ expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(false)
+ expect(repository.commit(local_ref)).to be_nil
+ end
end
end
@@ -1866,6 +1650,54 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#set_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can set config settings' do
+ expect(repository.set_config(entries)).to be_nil
+
+ expect(rugged.config['test.foo1']).to eq('bla bla')
+ expect(rugged.config['test.foo2']).to eq('1234')
+ expect(rugged.config['test.foo3']).to eq('true')
+ end
+
+ after do
+ entries.keys.each { |k| rugged.config.delete(k) }
+ end
+ end
+
+ describe '#delete_config' do
+ let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') }
+ let(:rugged) { repository_rugged }
+ let(:entries) do
+ {
+ 'test.foo1' => 'bla bla',
+ 'test.foo2' => 1234,
+ 'test.foo3' => true
+ }
+ end
+
+ it 'can delete config settings' do
+ entries.each do |key, value|
+ rugged.config[key] = value
+ end
+
+ expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
+
+ config_keys = rugged.config.each_key.to_a
+ expect(config_keys).not_to include('test.foo1')
+ expect(config_keys).not_to include('test.foo2')
+ end
+ end
+
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1971,21 +1803,15 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context 'with gitaly' do
- it "calls Gitaly's OperationService" do
- expect_any_instance_of(Gitlab::GitalyClient::OperationService)
- .to receive(:user_ff_branch).with(user, source_sha, target_branch)
- .and_return(nil)
-
- subject
- end
+ it "calls Gitaly's OperationService" do
+ expect_any_instance_of(Gitlab::GitalyClient::OperationService)
+ .to receive(:user_ff_branch).with(user, source_sha, target_branch)
+ .and_return(nil)
- it_behaves_like '#ff_merge'
+ subject
end
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#ff_merge'
- end
+ it_behaves_like '#ff_merge'
end
describe '#delete_all_refs_except' do
@@ -2018,54 +1844,61 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
+ let(:rugged) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
+ end
let(:remote_name) { 'my-remote' }
+ let(:url) { 'http://my-repo.git' }
after do
ensure_seeds
end
describe '#add_remote' do
- let(:url) { 'http://my-repo.git' }
let(:mirror_refmap) { '+refs/*:refs/*' }
- it 'creates a new remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:add_remote).with(remote_name, url, mirror_refmap)
+ shared_examples 'add_remote' do
+ it 'added the remote' do
+ begin
+ rugged.remotes.delete(remote_name)
+ rescue Rugged::ConfigError
+ end
+
+ repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
+ expect(rugged.remotes[remote_name]).not_to be_nil
+ expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true')
+ expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'creates a new remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:create)
- .with(remote_name, url)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.mirror", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.prune", true)
- expect_any_instance_of(Rugged::Config).to receive(:[]=)
- .with("remote.#{remote_name}.fetch", mirror_refmap)
+ context 'using Gitaly' do
+ it_behaves_like 'add_remote'
+ end
- repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'add_remote'
end
end
describe '#remove_remote' do
- it 'removes the remote via Gitaly' do
- expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
- .to receive(:remove_remote).with(remote_name)
+ shared_examples 'remove_remote' do
+ it 'removes the remote' do
+ rugged.remotes.create(remote_name, url)
+
+ repository.remove_remote(remote_name)
- repository.remove_remote(remote_name)
+ expect(rugged.remotes[remote_name]).to be_nil
+ end
end
- context 'with Gitaly disabled', :skip_gitaly_mock do
- it 'removes the remote via Rugged' do
- expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete)
- .with(remote_name)
+ context 'using Gitaly' do
+ it_behaves_like 'remove_remote'
+ end
- repository.remove_remote(remote_name)
- end
+ context 'with Gitaly disabled', :disable_gitaly do
+ it_behaves_like 'remove_remote'
end
end
end
@@ -2257,20 +2090,25 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
it 'cleans up the files' do
- repository.with_worktree(worktree_path, 'master', env: ENV) do
- FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
- # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
- # but the HEAD must be 40 characters long or git will ignore it.
- File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
+ create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
+ raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
- # git 2.16 fails with "fatal: bad object HEAD"
- expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError)
+ FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
+ # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
+ # but the HEAD must be 40 characters long or git will ignore it.
+ File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
- repository.clean_stale_repository_files
+ # git 2.16 fails with "fatal: bad object HEAD"
+ expect(rev_list_all).to be false
- expect { repository.rev_list(including: :all) }.not_to raise_error
- expect(File.exist?(worktree_path)).to be_falsey
- end
+ repository.clean_stale_repository_files
+
+ expect(rev_list_all).to be true
+ expect(File.exist?(worktree_path)).to be_falsey
+ end
+
+ def rev_list_all
+ system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
end
it 'increments a counter upon an error' do
@@ -2308,92 +2146,95 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect { subject }.to raise_error(Gitlab::Git::CommandError, 'error')
end
end
+ end
- describe '#squash' do
- let(:squash_id) { '1' }
- let(:branch_name) { 'fix' }
- let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
- let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
+ describe '#squash' do
+ let(:squash_id) { '1' }
+ let(:branch_name) { 'fix' }
+ let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
- subject do
- opts = {
- branch: branch_name,
- start_sha: start_sha,
- end_sha: end_sha,
- author: user,
- message: 'Squash commit message'
- }
+ subject do
+ opts = {
+ branch: branch_name,
+ start_sha: start_sha,
+ end_sha: end_sha,
+ author: user,
+ message: 'Squash commit message'
+ }
+
+ repository.squash(user, squash_id, opts)
+ end
+
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'sparse checkout' do
+ let(:expected_files) { %w(files files/js files/js/application.js) }
+
+ it 'checks out only the files in the diff' do
+ allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
+ m.call(*args) do
+ worktree_path = args[0]
+ files_pattern = File.join(worktree_path, '**', '*')
+ expected = expected_files.map do |path|
+ File.expand_path(path, worktree_path)
+ end
+
+ expect(Dir[files_pattern]).to eq(expected)
+ end
+ end
- repository.squash(user, squash_id, opts)
+ subject
end
- context 'sparse checkout', :skip_gitaly_mock do
- let(:expected_files) { %w(files files/js files/js/application.js) }
+ context 'when the diff contains a rename' do
+ let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
+ let(:end_sha) { new_commit_move_file(repo).oid }
+
+ after do
+ # Erase our commits so other tests get the original repo
+ repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
+ end
- it 'checks out only the files in the diff' do
+ it 'does not include the renamed file in the sparse checkout' do
allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
m.call(*args) do
worktree_path = args[0]
files_pattern = File.join(worktree_path, '**', '*')
- expected = expected_files.map do |path|
- File.expand_path(path, worktree_path)
- end
- expect(Dir[files_pattern]).to eq(expected)
+ expect(Dir[files_pattern]).not_to include('CHANGELOG')
+ expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
end
end
subject
end
-
- context 'when the diff contains a rename' do
- let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
- let(:end_sha) { new_commit_move_file(repo).oid }
-
- after do
- # Erase our commits so other tests get the original repo
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
- repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
- end
-
- it 'does not include the renamed file in the sparse checkout' do
- allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
- m.call(*args) do
- worktree_path = args[0]
- files_pattern = File.join(worktree_path, '**', '*')
-
- expect(Dir[files_pattern]).not_to include('CHANGELOG')
- expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
- end
- end
-
- subject
- end
- end
end
+ end
- context 'with an ASCII-8BIT diff', :skip_gitaly_mock do
- let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'with an ASCII-8BIT diff' do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
- it 'applies a ASCII-8BIT diff' do
- allow(repository).to receive(:run_git!).and_call_original
- allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+ it 'applies a ASCII-8BIT diff' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
- expect(subject).to match(/\h{40}/)
- end
+ expect(subject).to match(/\h{40}/)
end
+ end
- context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do
- let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
+ # Should be ported to gitaly-ruby rspec suite https://gitlab.com/gitlab-org/gitaly/issues/1234
+ skip 'with trailing whitespace in an invalid patch' do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
- it 'does not include whitespace warnings in the error' do
- allow(repository).to receive(:run_git!).and_call_original
- allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+ it 'does not include whitespace warnings in the error' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
- expect { subject }.to raise_error do |error|
- expect(error).to be_a(described_class::GitError)
- expect(error.message).not_to include('trailing whitespace')
- end
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(described_class::GitError)
+ expect(error.message).not_to include('trailing whitespace')
end
end
end
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
deleted file mode 100644
index b752c3e8341..00000000000
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Git::RevList do
- let(:repository) { create(:project, :repository).repository.raw }
- let(:rev_list) { described_class.new(repository, newrev: 'newrev') }
-
- def args_for_popen(args_list)
- [Gitlab.config.git.bin_path, 'rev-list', *args_list]
- end
-
- def stub_popen_rev_list(*additional_args, with_lazy_block: true, output:)
- repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.path }
-
- params = [
- args_for_popen(additional_args),
- repo_path,
- {},
- hash_including(lazy_block: with_lazy_block ? anything : nil)
- ]
-
- expect(repository).to receive(:popen).with(*params) do |*_, lazy_block:|
- output = lazy_block.call(output.lines.lazy.map(&:chomp)) if with_lazy_block
-
- [output, 0]
- end
- end
-
- context "#new_refs" do
- it 'calls out to `popen`' do
- stub_popen_rev_list('newrev', '--not', '--all', with_lazy_block: false, output: "sha1\nsha2")
-
- expect(rev_list.new_refs).to eq(%w[sha1 sha2])
- end
- end
-
- context '#new_objects' do
- it 'fetches list of newly pushed objects using rev-list' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'can skip pathless objects' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
-
- expect { |b| rev_list.new_objects(require_path: true, &b) }.to yield_with_args(%w[sha2])
- end
-
- it 'can handle non utf-8 paths' do
- non_utf_char = [0x89].pack("c*").force_encoding("UTF-8")
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1")
-
- rev_list.new_objects(require_path: true) do |object_ids|
- expect(object_ids.force).to eq(%w[sha2])
- end
- end
-
- it 'can yield a lazy enumerator' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- rev_list.new_objects do |object_ids|
- expect(object_ids).to be_a Enumerator::Lazy
- end
- end
-
- it 'returns the result of the block when given' do
- stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
-
- objects = rev_list.new_objects do |object_ids|
- object_ids.first
- end
-
- expect(objects).to eq 'sha1'
- end
-
- it 'can accept list of references to exclude' do
- stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: ['master'], &b) }.to yield_with_args(%w[sha1 sha2])
- end
-
- it 'handles empty list of references to exclude as listing all known objects' do
- stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.new_objects(not_in: [], &b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
-
- context '#all_objects' do
- it 'fetches list of all pushed objects using rev-list' do
- stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
-
- expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
- end
- end
-end
diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb
index be2f5bfb819..2d9db576a6c 100644
--- a/spec/lib/gitlab/git/tag_spec.rb
+++ b/spec/lib/gitlab/git/tag_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Tag, seed_helper: true do
+describe Gitlab::Git::Tag, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
shared_examples 'Gitlab::Git::Repository#tags' do
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 001e406a930..3792d6bf67b 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -1,6 +1,6 @@
require "spec_helper"
-describe Gitlab::Git::Tree, seed_helper: true do
+describe Gitlab::Git::Tree, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
context :repo do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index b63658e1b3b..c5666e4ec61 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -6,6 +6,31 @@ describe Gitlab::Git::Wiki do
let(:project_wiki) { ProjectWiki.new(project, user) }
subject { project_wiki.wiki }
+ describe '#pages' do
+ before do
+ create_page('page1', 'content')
+ create_page('page2', 'content2')
+ end
+
+ after do
+ destroy_page('page1')
+ destroy_page('page2')
+ end
+
+ it 'returns all the pages' do
+ expect(subject.pages.count).to eq(2)
+ expect(subject.pages.first.title).to eq 'page1'
+ expect(subject.pages.last.title).to eq 'page2'
+ end
+
+ it 'returns only one page' do
+ pages = subject.pages(limit: 1)
+
+ expect(pages.count).to eq(1)
+ expect(pages.first.title).to eq 'page1'
+ end
+ end
+
describe '#page' do
before do
create_page('page1', 'content')
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index ff32025253a..dbd64c4bec0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -13,14 +13,6 @@ describe Gitlab::GitAccess do
let(:authentication_abilities) { %i[read_project download_code push_code] }
let(:redirected_path) { nil }
let(:auth_result_type) { nil }
-
- let(:access) do
- described_class.new(actor, project,
- protocol, authentication_abilities: authentication_abilities,
- namespace_path: namespace_path, project_path: project_path,
- redirected_path: redirected_path, auth_result_type: auth_result_type)
- end
-
let(:changes) { '_any' }
let(:push_access_check) { access.check('git-receive-pack', changes) }
let(:pull_access_check) { access.check('git-upload-pack', changes) }
@@ -48,7 +40,7 @@ describe Gitlab::GitAccess do
before do
disable_protocol('http')
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'blocks http push and pull' do
@@ -113,7 +105,7 @@ describe Gitlab::GitAccess do
context 'when actor is a User' do
context 'when the User can read the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'allows push and pull access' do
@@ -254,7 +246,7 @@ describe Gitlab::GitAccess do
shared_examples '#check with a key that is not valid' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'key is too small' do
@@ -307,7 +299,7 @@ describe Gitlab::GitAccess do
describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when a redirect was not followed to find the project' do
@@ -335,7 +327,7 @@ describe Gitlab::GitAccess do
describe '#check_authentication_abilities!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'when download' do
@@ -381,7 +373,7 @@ describe Gitlab::GitAccess do
describe '#check_command_disabled!' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
context 'over http' do
@@ -529,8 +521,8 @@ describe Gitlab::GitAccess do
end
describe '#check_download_access!' do
- it 'allows masters to pull' do
- project.add_master(user)
+ it 'allows maintainers to pull' do
+ project.add_maintainer(user)
expect { pull_access_check }.not_to raise_error
end
@@ -542,7 +534,7 @@ describe Gitlab::GitAccess do
end
it 'disallows blocked users to pull' do
- project.add_master(user)
+ project.add_maintainer(user)
user.block
expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.')
@@ -724,10 +716,11 @@ describe Gitlab::GitAccess do
end
describe '#check_push_access!' do
+ let(:unprotected_branch) { 'unprotected_branch' }
+
before do
merge_into_protected_branch
end
- let(:unprotected_branch) { 'unprotected_branch' }
let(:changes) do
{ push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow",
@@ -741,26 +734,18 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
end
- def stub_git_hooks
- # Running the `pre-receive` hook is expensive, and not necessary for this test.
- allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block|
- block.call(service)
- end
- end
-
def merge_into_protected_branch
@protected_branch_merge_commit ||= begin
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- stub_git_hooks
project.repository.add_branch(user, unprotected_branch, 'feature')
- target_branch = project.repository.lookup('feature')
+ rugged = project.repository.rugged
+ target_branch = rugged.rev_parse('feature')
source_branch = project.repository.create_file(
user,
'filename',
'This is the file content',
message: 'This is a good commit message',
branch_name: unprotected_branch)
- rugged = project.repository.rugged
author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
merge_index = rugged.merge_commits(target_branch, source_branch)
@@ -785,7 +770,7 @@ describe Gitlab::GitAccess do
aggregate_failures do
matrix.each do |action, allowed|
- check = -> { access.send(:check_push_access!, changes[action]) }
+ check = -> { push_changes(changes[action]) }
if allowed
expect(&check).not_to raise_error,
@@ -812,7 +797,7 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: true
},
- master: {
+ maintainer: {
push_new_branch: true,
push_master: true,
push_protected_branch: true,
@@ -917,7 +902,7 @@ describe Gitlab::GitAccess do
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
- master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
+ maintainer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }))
end
end
@@ -989,7 +974,7 @@ describe Gitlab::GitAccess do
let(:project) { create(:project, :repository, :read_only) }
it 'denies push access' do
- project.add_master(user)
+ project.add_maintainer(user)
expect { push_access_check }.to raise_unauthorized('The repository is temporarily read-only. Please try again later.')
end
@@ -1119,9 +1104,9 @@ describe Gitlab::GitAccess do
it_behaves_like 'access after accepting terms'
end
- describe 'as a master of the project' do
+ describe 'as a maintainer of the project' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it_behaves_like 'access after accepting terms'
@@ -1152,6 +1137,17 @@ describe Gitlab::GitAccess do
private
+ def access
+ described_class.new(actor, project, protocol,
+ authentication_abilities: authentication_abilities,
+ namespace_path: namespace_path, project_path: project_path,
+ redirected_path: redirected_path, auth_result_type: auth_result_type)
+ end
+
+ def push_changes(changes)
+ access.check('git-receive-pack', changes)
+ end
+
def raise_unauthorized(message)
raise_error(Gitlab::GitAccess::UnauthorizedError, message)
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 7951cbe7b1d..54f2ea33f90 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message,
left_commit_id: 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660',
right_commit_id: commit.id,
- collapse_diffs: true,
+ collapse_diffs: false,
enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h
)
@@ -35,7 +35,7 @@ describe Gitlab::GitalyClient::CommitService do
repository: repository_message,
left_commit_id: Gitlab::Git::EMPTY_TREE_ID,
right_commit_id: initial_commit.id,
- collapse_diffs: true,
+ collapse_diffs: false,
enforce_limits: true,
**Gitlab::Git::DiffCollection.collection_limits.to_h
)
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 9709f1f5646..eaf64e3c9b4 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
describe Gitlab::GitalyClient::OperationService do
- let(:project) { create(:project) }
+ set(:project) { create(:project, :repository) }
let(:repository) { project.repository.raw }
let(:client) { described_class.new(repository) }
- let(:user) { create(:user) }
+ set(:user) { create(:user) }
let(:gitaly_user) { Gitlab::Git::User.from_gitlab(user).to_gitaly }
describe '#user_create_branch' do
@@ -53,6 +53,47 @@ describe Gitlab::GitalyClient::OperationService do
end
end
+ describe '#user_update_branch' do
+ let(:branch_name) { 'my-branch' }
+ let(:newrev) { '01e' }
+ let(:oldrev) { '01d' }
+ let(:request) do
+ Gitaly::UserUpdateBranchRequest.new(
+ repository: repository.gitaly_repository,
+ branch_name: branch_name,
+ newrev: newrev,
+ oldrev: oldrev,
+ user: gitaly_user
+ )
+ end
+ let(:response) { Gitaly::UserUpdateBranchResponse.new }
+
+ subject { client.user_update_branch(branch_name, user, newrev, oldrev) }
+
+ it 'sends a user_update_branch message' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ subject
+ end
+
+ context "when pre_receive_error is present" do
+ let(:response) do
+ Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed")
+ end
+
+ it "throws a PreReceive exception" do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_update_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ expect { subject }.to raise_error(
+ Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+ end
+
describe '#user_delete_branch' do
let(:branch_name) { 'my-branch' }
let(:request) do
@@ -110,18 +151,104 @@ describe Gitlab::GitalyClient::OperationService do
end
let(:response) { Gitaly::UserFFBranchResponse.new(branch_update: branch_update) }
- subject { client.user_ff_branch(user, source_sha, target_branch) }
-
- it 'sends a user_ff_branch message and returns a BranchUpdate object' do
+ before do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_ff_branch).with(request, kind_of(Hash))
.and_return(response)
+ end
+ subject { client.user_ff_branch(user, source_sha, target_branch) }
+
+ it 'sends a user_ff_branch message and returns a BranchUpdate object' do
expect(subject).to be_a(Gitlab::Git::OperationService::BranchUpdate)
expect(subject.newrev).to eq(source_sha)
expect(subject.repo_created).to be(false)
expect(subject.branch_created).to be(false)
end
+
+ context 'when the response has no branch_update' do
+ let(:response) { Gitaly::UserFFBranchResponse.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
+
+ shared_examples 'cherry pick and revert errors' do
+ context 'when a pre_receive_error is present' do
+ let(:response) { response_class.new(pre_receive_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+
+ context 'when a commit_error is present' do
+ let(:response) { response_class.new(commit_error: "something failed") }
+
+ it 'raises a CommitError' do
+ expect { subject }.to raise_error(Gitlab::Git::CommitError, "something failed")
+ end
+ end
+
+ context 'when a create_tree_error is present' do
+ let(:response) { response_class.new(create_tree_error: "something failed") }
+
+ it 'raises a CreateTreeError' do
+ expect { subject }.to raise_error(Gitlab::Git::Repository::CreateTreeError, "something failed")
+ end
+ end
+
+ context 'when branch_update is nil' do
+ let(:response) { response_class.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
+
+ describe '#user_cherry_pick' do
+ let(:response_class) { Gitaly::UserCherryPickResponse }
+
+ subject do
+ client.user_cherry_pick(
+ user: user,
+ commit: repository.commit,
+ branch_name: 'master',
+ message: 'Cherry-pick message',
+ start_branch_name: 'master',
+ start_repository: repository
+ )
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_cherry_pick).with(kind_of(Gitaly::UserCherryPickRequest), kind_of(Hash))
+ .and_return(response)
+ end
+
+ it_behaves_like 'cherry pick and revert errors'
+ end
+
+ describe '#user_revert' do
+ let(:response_class) { Gitaly::UserRevertResponse }
+
+ subject do
+ client.user_revert(
+ user: user,
+ commit: repository.commit,
+ branch_name: 'master',
+ message: 'Revert message',
+ start_branch_name: 'master',
+ start_repository: repository
+ )
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_revert).with(kind_of(Gitaly::UserRevertRequest), kind_of(Hash))
+ .and_return(response)
+ end
+
+ it_behaves_like 'cherry pick and revert errors'
end
describe '#user_squash' do
@@ -162,7 +289,7 @@ describe Gitlab::GitalyClient::OperationService do
Gitaly::UserSquashResponse.new(git_error: "something failed")
end
- it "throws a PreReceive exception" do
+ it "raises a GitError exception" do
expect_any_instance_of(Gitaly::OperationService::Stub)
.to receive(:user_squash).with(request, kind_of(Hash))
.and_return(response)
@@ -171,5 +298,41 @@ describe Gitlab::GitalyClient::OperationService do
Gitlab::Git::Repository::GitError, "something failed")
end
end
+
+ describe '#user_commit_files' do
+ subject do
+ client.user_commit_files(
+ gitaly_user, 'my-branch', 'Commit files message', [], 'janedoe@example.com', 'Jane Doe',
+ 'master', repository)
+ end
+
+ before do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_commit_files).with(kind_of(Enumerator), kind_of(Hash))
+ .and_return(response)
+ end
+
+ context 'when a pre_receive_error is present' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
+ end
+ end
+
+ context 'when an index_error is present' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new(index_error: "something failed") }
+
+ it 'raises a PreReceiveError' do
+ expect { subject }.to raise_error(Gitlab::Git::Index::IndexError, "something failed")
+ end
+ end
+
+ context 'when branch_update is nil' do
+ let(:response) { Gitaly::UserCommitFilesResponse.new }
+
+ it { expect(subject).to be_nil }
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 257e4c50f2d..400d426c949 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -18,6 +18,44 @@ describe Gitlab::GitalyClient::RefService do
end
end
+ describe '#remote_branches' do
+ let(:remote_name) { 'my_remote' }
+ subject { client.remote_branches(remote_name) }
+
+ it 'sends a find_all_remote_branches message' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_remote_branches)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return([])
+
+ subject
+ end
+
+ it 'concantes and returns the response branches as Gitlab::Git::Branch objects' do
+ target_commits = create_list(:gitaly_commit, 4)
+ response_branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit)
+ end
+ response = [
+ Gitaly::FindAllRemoteBranchesResponse.new(branches: response_branches[0, 2]),
+ Gitaly::FindAllRemoteBranchesResponse.new(branches: response_branches[2, 2])
+ ]
+
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_remote_branches).and_return(response)
+
+ expect(subject.length).to be(response_branches.length)
+
+ response_branches.each_with_index do |gitaly_branch, i|
+ branch = subject[i]
+ commit = Gitlab::Git::Commit.new(repository, gitaly_branch.target_commit)
+
+ expect(branch.name).to eq(i.to_s) # It removes the `remote/` prefix
+ expect(branch.dereferenced_target).to eq(commit)
+ end
+ end
+ end
+
describe '#branch_names' do
it 'sends a find_all_branch_names message' do
expect_any_instance_of(Gitaly::RefService::Stub)
diff --git a/spec/lib/gitlab/gitaly_client/storage_service_spec.rb b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb
new file mode 100644
index 00000000000..6c25e2d6ebd
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/storage_service_spec.rb
@@ -0,0 +1,13 @@
+require 'spec_helper'
+
+describe Gitlab::GitalyClient::StorageService do
+ describe '#delete_all_repositories' do
+ let!(:project) { create(:project, :repository) }
+
+ it 'removes all repositories' do
+ described_class.new(project.repository_storage).delete_all_repositories
+
+ expect(project.repository.exists?).to be(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index 6ad9f5ef766..5f67fe6b952 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -70,6 +70,15 @@ describe Gitlab::GitalyClient::WikiService do
subject
end
+ it 'sends a limit of 0 to wiki_get_all_pages' do
+ expect_any_instance_of(Gitaly::WikiService::Stub)
+ .to receive(:wiki_get_all_pages)
+ .with(gitaly_request_with_params(limit: 0), kind_of(Hash))
+ .and_return([].each)
+
+ subject
+ end
+
it 'concatenates the raw data and returns a pair of WikiPage and WikiPageVersion for each page' do
expect_any_instance_of(Gitaly::WikiService::Stub)
.to receive(:wiki_get_all_pages)
@@ -84,5 +93,18 @@ describe Gitlab::GitalyClient::WikiService do
expect(wiki_page_2.raw_data).to eq('cd')
expect(wiki_page_2_version.format).to eq('markdown')
end
+
+ context 'with limits' do
+ subject { client.get_all_pages(limit: 1) }
+
+ it 'sends a request with the limit' do
+ expect_any_instance_of(Gitaly::WikiService::Stub)
+ .to receive(:wiki_get_all_pages)
+ .with(gitaly_request_with_params(limit: 1), kind_of(Hash))
+ .and_return([].each)
+
+ subject
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
index 51fad6c6838..c51985f00a2 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb
@@ -27,9 +27,9 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
milestone: double(:milestone, number: 4),
user: double(:user, id: 4, login: 'alice'),
assignee: double(:user, id: 4, login: 'alice'),
- created_at: Time.zone.now,
- updated_at: Time.zone.now,
- merged_at: Time.zone.now
+ created_at: 1.second.ago,
+ updated_at: 1.second.ago,
+ merged_at: 1.second.ago
)
end
@@ -158,7 +158,6 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
expect(importer.repository_updates_counter)
.to receive(:increment)
- .with(project: project.path_with_namespace)
.and_call_original
Timecop.freeze do
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index 50e8d7183ce..22ad88e28cb 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -15,4 +15,88 @@ describe Gitlab::GitlabImport::Client do
expect(key).to be_kind_of(Symbol)
end
end
+
+ it 'uses membership and simple flags' do
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true')
+
+ expect_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+
+ expect(client.projects.to_a).to eq []
+ end
+
+ shared_examples 'pagination params' do
+ before do
+ allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+ end
+
+ it 'allows page_limit param' do
+ allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return(element_list)
+
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original
+
+ client.send(method, *args, page_limit: 2, per_page: 1).to_a
+ end
+
+ it 'allows per_page param' do
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(per_page: 2)).and_call_original
+
+ client.send(method, *args, per_page: 2).to_a
+ end
+
+ it 'allows starting_page param' do
+ expect(client).to receive(:lazy_page_iterator).with(hash_including(starting_page: 3)).and_call_original
+
+ client.send(method, *args, starting_page: 3).to_a
+ end
+ end
+
+ describe '#projects' do
+ subject(:method) { :projects }
+ let(:args) { [] }
+ let(:element_list) { build_list(:project, 2) }
+
+ before do
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=1&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=2&per_page=1&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=1&per_page=2&simple=true')
+ stub_request('/api/v4/projects?membership=true&page=3&per_page=100&simple=true')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ describe '#issues' do
+ subject(:method) { :issues }
+ let(:args) { [1] }
+ let(:element_list) { build_list(:issue, 2) }
+
+ before do
+ stub_request('/api/v4/projects/1/issues?page=1&per_page=1')
+ stub_request('/api/v4/projects/1/issues?page=2&per_page=1')
+ stub_request('/api/v4/projects/1/issues?page=1&per_page=2')
+ stub_request('/api/v4/projects/1/issues?page=3&per_page=100')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ describe '#issue_comments' do
+ subject(:method) { :issue_comments }
+ let(:args) { [1, 1] }
+ let(:element_list) { build_list(:note_on_issue, 2) }
+
+ before do
+ stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=1')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=2&per_page=1')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=2')
+ stub_request('/api/v4/projects/1/issues/1/notes?page=3&per_page=100')
+ end
+
+ it_behaves_like 'pagination params'
+ end
+
+ def stub_request(path)
+ WebMock.stub_request(:get, "https://gitlab.com#{path}")
+ .to_return(status: 200)
+ end
end
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 017facd0f5e..031f57dbc65 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do
subject { described_class.new(project) }
before do
- project.add_master(project.creator)
+ project.add_maintainer(project.creator)
project.create_import_data(data: import_data)
end
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index 6fbffc38444..1a2c6ef25c4 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -141,7 +141,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
expect(invalid_gpg_signature.reload.verification_status).to eq 'unverified_key'
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: GpgHelpers::User1.emails.first)
+ user.update!(email: GpgHelpers::User1.emails.first)
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
)
# InvalidGpgSignatureUpdater is called by the after_update hook
- user.update_attributes!(email: 'still.unrelated@example.com')
+ user.update!(email: 'still.unrelated@example.com')
expect(invalid_gpg_signature.reload).to have_attributes(
project: project,
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
new file mode 100644
index 00000000000..95bf7685ade
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize::AuthorizeResource do
+ let(:fake_class) do
+ Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ attr_reader :user, :found_object
+
+ authorize :read_the_thing
+
+ def initialize(user, found_object)
+ @user, @found_object = user, found_object
+ end
+
+ def find_object
+ found_object
+ end
+
+ def current_user
+ user
+ end
+ end
+ end
+
+ let(:user) { build(:user) }
+ let(:project) { build(:project) }
+ subject(:loading_resource) { fake_class.new(user, project) }
+
+ context 'when the user is allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ true
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find).to eq(project)
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find!).to eq(project)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.not_to raise_error
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is true' do
+ expect(loading_resource.authorized?(project)).to be(true)
+ end
+ end
+ end
+
+ context 'when the user is not allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ false
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns `nil`' do
+ expect(loading_resource.authorized_find).to be_nil
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'raises an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is false' do
+ expect(loading_resource.authorized?(project)).to be(false)
+ end
+ end
+ end
+
+ context 'when the class does not define #find_object' do
+ let(:fake_class) do
+ Class.new { include Gitlab::Graphql::Authorize::AuthorizeResource }
+ end
+
+ it 'raises a comprehensive error message' do
+ expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
new file mode 100644
index 00000000000..9c17a3b0e4b
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize do
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ extend Gitlab::Graphql::Authorize
+
+ authorize :base_authorization
+ end
+ sub_class = Class.new(base_class) do
+ authorize :sub_authorization
+ end
+
+ expect(base_class.required_permissions).to contain_exactly(:base_authorization)
+ expect(sub_class.required_permissions)
+ .to contain_exactly(:base_authorization, :sub_authorization)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
new file mode 100644
index 00000000000..96615ae80de
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
@@ -0,0 +1,112 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::KeysetConnection do
+ let(:nodes) { Project.all.order(id: :asc) }
+ let(:arguments) { {} }
+ subject(:connection) do
+ described_class.new(nodes, arguments, max_page_size: 3)
+ end
+
+ def encoded_property(value)
+ Base64.strict_encode64(value.to_s)
+ end
+
+ describe '#cursor_from_nodes' do
+ let(:project) { create(:project) }
+
+ it 'returns an encoded ID' do
+ expect(connection.cursor_from_node(project))
+ .to eq(encoded_property(project.id))
+ end
+
+ context 'when an order was specified' do
+ let(:nodes) { Project.order(:updated_at) }
+
+ it 'returns the encoded value of the order' do
+ expect(connection.cursor_from_node(project))
+ .to eq(encoded_property(project.updated_at))
+ end
+ end
+ end
+
+ describe '#sliced_nodes' do
+ let(:projects) { create_list(:project, 4) }
+
+ context 'when before is passed' do
+ let(:arguments) { { before: encoded_property(projects[1].id) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(id: :desc) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
+ end
+ end
+ end
+
+ context 'when after is passed' do
+ let(:arguments) { { after: encoded_property(projects[1].id) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(id: :desc) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+ end
+ end
+
+ context 'when both before and after are passed' do
+ let(:arguments) do
+ {
+ after: encoded_property(projects[1].id),
+ before: encoded_property(projects[3].id)
+ }
+ end
+
+ it 'returns the expected set' do
+ expect(subject.sliced_nodes).to contain_exactly(projects[2])
+ end
+ end
+ end
+
+ describe '#paged_nodes' do
+ let!(:projects) { create_list(:project, 5) }
+
+ it 'returns the collection limited to max page size' do
+ expect(subject.paged_nodes.size).to eq(3)
+ end
+
+ context 'when `first` is passed' do
+ let(:arguments) { { first: 2 } }
+
+ it 'returns only the first elements' do
+ expect(subject.paged_nodes).to contain_exactly(projects.first, projects.second)
+ end
+ end
+
+ context 'when `last` is passed' do
+ let(:arguments) { { last: 2 } }
+
+ it 'returns only the last elements' do
+ expect(subject.paged_nodes).to contain_exactly(projects[3], projects[4])
+ end
+ end
+
+ context 'when both are passed' do
+ let(:arguments) { { first: 2, last: 2 } }
+
+ it 'raises an error' do
+ expect { subject.paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb
index b2084f56640..530d4a981bf 100644
--- a/spec/lib/gitlab/graphs/commits_spec.rb
+++ b/spec/lib/gitlab/graphs/commits_spec.rb
@@ -29,7 +29,7 @@ describe Gitlab::Graphs::Commits do
context 'with commits from yesterday and today' do
subject { described_class.new([commit2, commit1_yesterday]) }
describe '#commit_per_day' do
- it { expect(subject.commit_per_day).to eq 1 }
+ it { expect(subject.commit_per_day).to eq 1.0 }
end
describe '#duration' do
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 813ae43b4d3..7eac2cacb90 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do
end
it 'migrate project' do
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
subject.migrate(project)
end
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
new file mode 100644
index 00000000000..a921dd766c3
--- /dev/null
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe Gitlab::HookData::BaseBuilder do
+ describe '#absolute_image_urls' do
+ let(:subclass) do
+ Class.new(described_class) do
+ public :absolute_image_urls
+ end
+ end
+
+ subject { subclass.new(nil) }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ 'relative image URL' => {
+ input: '![an image](foo.png)',
+ output: "![an image](#{Gitlab.config.gitlab.url}/foo.png)"
+ },
+ 'HTTP URL' => {
+ input: '![an image](http://example.com/foo.png)',
+ output: '![an image](http://example.com/foo.png)'
+ },
+ 'HTTPS URL' => {
+ input: '![an image](https://example.com/foo.png)',
+ output: '![an image](https://example.com/foo.png)'
+ },
+ 'protocol-relative URL' => {
+ input: '![an image](//example.com/foo.png)',
+ output: '![an image](//example.com/foo.png)'
+ },
+ 'URL reference by title' => {
+ input: "![foo]\n\n[foo]: foo.png",
+ output: "![foo]\n\n[foo]: foo.png"
+ },
+ 'URL reference by label' => {
+ input: "![][foo]\n\n[foo]: foo.png",
+ output: "![][foo]\n\n[foo]: foo.png"
+ },
+ 'in Markdown inline code block' => {
+ input: '`![an image](foo.png)`',
+ output: "`![an image](#{Gitlab.config.gitlab.url}/foo.png)`"
+ },
+ 'in HTML tag on the same line' => {
+ input: '<p>![an image](foo.png)</p>',
+ output: "<p>![an image](#{Gitlab.config.gitlab.url}/foo.png)</p>"
+ },
+ 'in Markdown multi-line code block' => {
+ input: "```\n![an image](foo.png)\n```",
+ output: "```\n![an image](foo.png)\n```"
+ },
+ 'in HTML tag on different lines' => {
+ input: "<p>\n![an image](foo.png)\n</p>",
+ output: "<p>\n![an image](foo.png)\n</p>"
+ }
+ }
+ end
+
+ with_them do
+ it { expect(subject.absolute_image_urls(input)).to eq(output) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index 506b2c0be20..60093474f8a 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -40,5 +40,14 @@ describe Gitlab::HookData::IssueBuilder do
expect(data).to include(:human_total_time_spent)
expect(data).to include(:assignee_ids)
end
+
+ context 'when the issue has an image in the description' do
+ let(:issue_with_description) { create(:issue, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(issue_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index b61614e4790..dd586af6118 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -56,5 +56,14 @@ describe Gitlab::HookData::MergeRequestBuilder do
expect(data).to include(:human_time_estimate)
expect(data).to include(:human_total_time_spent)
end
+
+ context 'when the MR has an image in the description' do
+ let(:mr_with_description) { create(:merge_request, description: 'test![Issue_Image](/uploads/abc/Issue_Image.png)') }
+ let(:builder) { described_class.new(mr_with_description) }
+
+ it 'sets the image to use an absolute URL' do
+ expect(data[:description]).to eq("test![Issue_Image](#{Settings.gitlab.url}/uploads/abc/Issue_Image.png)")
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/trace/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
index 5474e2f518c..788bddb8f59 100644
--- a/spec/lib/gitlab/ci/trace/http_io_spec.rb
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -1,11 +1,14 @@
require 'spec_helper'
-describe Gitlab::Ci::Trace::HttpIO do
+describe Gitlab::HttpIO do
include HttpIOHelpers
let(:http_io) { described_class.new(url, size) }
- let(:url) { remote_trace_url }
- let(:size) { remote_trace_size }
+
+ let(:url) { 'http://object-storage/trace' }
+ let(:file_path) { expand_fixture_path('trace/sample_trace') }
+ let(:file_body) { File.read(file_path).force_encoding(Encoding::BINARY) }
+ let(:size) { File.size(file_path) }
describe '#close' do
subject { http_io.close }
@@ -86,10 +89,10 @@ describe Gitlab::Ci::Trace::HttpIO do
describe '#each_line' do
subject { http_io.each_line }
- let(:string_io) { StringIO.new(remote_trace_body) }
+ let(:string_io) { StringIO.new(file_body) }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
it 'yields lines' do
@@ -99,7 +102,7 @@ describe Gitlab::Ci::Trace::HttpIO do
context 'when buckets on GCS' do
context 'when BUFFER_SIZE is larger than file size' do
before do
- stub_remote_trace_200
+ stub_remote_url_200(url, file_path)
set_larger_buffer_size_than(size)
end
@@ -117,7 +120,7 @@ describe Gitlab::Ci::Trace::HttpIO do
context 'when there are no network issue' do
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
context 'when read whole size' do
@@ -129,7 +132,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
@@ -139,7 +142,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
end
@@ -153,7 +156,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
+ is_expected.to eq(file_body[0, length])
end
end
@@ -163,7 +166,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body[0, length])
+ is_expected.to eq(file_body[0, length])
end
end
end
@@ -177,7 +180,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
@@ -187,7 +190,7 @@ describe Gitlab::Ci::Trace::HttpIO do
end
it 'reads a trace' do
- is_expected.to eq(remote_trace_body)
+ is_expected.to eq(file_body)
end
end
end
@@ -221,11 +224,11 @@ describe Gitlab::Ci::Trace::HttpIO do
let(:length) { nil }
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
end
@@ -233,15 +236,15 @@ describe Gitlab::Ci::Trace::HttpIO do
describe '#readline' do
subject { http_io.readline }
- let(:string_io) { StringIO.new(remote_trace_body) }
+ let(:string_io) { StringIO.new(file_body) }
before do
- stub_remote_trace_206
+ stub_remote_url_206(url, file_path)
end
shared_examples 'all line matching' do
it 'reads a line' do
- (0...remote_trace_body.lines.count).each do
+ (0...file_body.lines.count).each do
expect(http_io.readline).to eq(string_io.readline)
end
end
@@ -251,11 +254,11 @@ describe Gitlab::Ci::Trace::HttpIO do
let(:length) { nil }
before do
- stub_remote_trace_500
+ stub_remote_url_500(url)
end
it 'reads a trace' do
- expect { subject }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
+ expect { subject }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
end
end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
new file mode 100644
index 00000000000..5059d68e54b
--- /dev/null
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_object_storage_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
+ let!(:service) { described_class.new }
+ let!(:project) { create(:project, :with_object_export) }
+ let(:shared) { project.import_export_shared }
+ let!(:user) { create(:user) }
+
+ describe '#execute' do
+ before do
+ allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: true)
+ end
+
+ it 'returns if project exported file is not found' do
+ allow(project).to receive(:export_project_object_exists?).and_return(false)
+
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'creates a lock file in the export dir' do
+ allow(service).to receive(:delete_after_export_lock)
+
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_truthy
+ end
+
+ context 'when the method succeeds' do
+ it 'removes the lock file' do
+ service.execute(user, project)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+
+ context 'when the method fails' do
+ before do
+ allow(service).to receive(:strategy_execute).and_call_original
+ end
+
+ context 'when validation fails' do
+ before do
+ allow(service).to receive(:invalid?).and_return(true)
+ end
+
+ it 'does not create the lock file' do
+ expect(service).not_to receive(:create_or_update_after_export_lock)
+
+ service.execute(user, project)
+ end
+
+ it 'does not execute main logic' do
+ expect(service).not_to receive(:strategy_execute)
+
+ service.execute(user, project)
+ end
+
+ it 'logs validation errors in shared context' do
+ expect(service).to receive(:log_validation_errors)
+
+ service.execute(user, project)
+ end
+ end
+
+ context 'when an exception is raised' do
+ it 'removes the lock' do
+ expect { service.execute(user, project) }.to raise_error(NotImplementedError)
+
+ expect(lock_path_exist?).to be_falsey
+ end
+ end
+ end
+ end
+
+ describe '#log_validation_errors' do
+ it 'add the message to the shared context' do
+ errors = %w(test_message test_message2)
+
+ allow(service).to receive(:invalid?).and_return(true)
+ allow(service.errors).to receive(:full_messages).and_return(errors)
+
+ expect(shared).to receive(:add_error_message).twice.and_call_original
+
+ service.execute(user, project)
+
+ expect(shared.errors).to eq errors
+ end
+ end
+
+ describe '#to_json' do
+ it 'adds the current strategy class to the serialized attributes' do
+ params = { param1: 1 }
+ result = params.merge(klass: described_class.to_s).to_json
+
+ expect(described_class.new(params).to_json).to eq result
+ end
+ end
+
+ def lock_path_exist?
+ File.exist?(described_class.lock_file_path(project))
+ end
+end
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
index ed54d87de4a..566b7f46c87 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
describe '#execute' do
before do
allow(service).to receive(:strategy_execute)
+ stub_feature_flags(import_export_object_storage: false)
end
it 'returns if project exported file is not found' do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index 5fe57d9987b..7f2e0a4ee2c 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -24,13 +24,34 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
end
describe '#execute' do
- it 'removes the exported project file after the upload' do
- allow(strategy).to receive(:send_file)
- allow(strategy).to receive(:handle_response_error)
+ context 'without object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: false)
+ end
+
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
+
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ end
+ end
+
+ context 'with object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ end
- expect(project).to receive(:remove_exported_project_file)
+ it 'removes the exported project file after the upload' do
+ allow(strategy).to receive(:send_file)
+ allow(strategy).to receive(:handle_response_error)
- strategy.execute(user, project)
+ expect(project).to receive(:remove_exported_project_file)
+
+ strategy.execute(user, project)
+ end
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 2ea66479c1b..e9a1932407d 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -7,6 +7,7 @@ issues:
- updated_by
- milestone
- notes
+- resource_label_events
- label_links
- labels
- last_edited_by
@@ -76,6 +77,7 @@ merge_requests:
- updated_by
- milestone
- notes
+- resource_label_events
- label_links
- labels
- last_edited_by
@@ -87,6 +89,7 @@ merge_requests:
- merge_request_diff
- events
- merge_requests_closing_issues
+- cached_closes_issues
- metrics
- timelogs
- head_pipeline
@@ -211,6 +214,7 @@ project:
- slack_service
- microsoft_teams_service
- mattermost_service
+- hangouts_chat_service
- buildkite_service
- bamboo_service
- teamcity_service
@@ -293,6 +297,8 @@ project:
- deploy_tokens
- settings
- ci_cd_settings
+- import_export_upload
+- repository_languages
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 2223f163177..90e6d653d34 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ stub_feature_flags(import_export_object_storage: false)
end
after do
diff --git a/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
new file mode 100644
index 00000000000..287745eb40e
--- /dev/null
+++ b/spec/lib/gitlab/import_export/file_importer_object_storage_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::FileImporter do
+ let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
+ let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" }
+ let(:valid_file) { "#{shared.export_path}/valid.json" }
+ let(:symlink_file) { "#{shared.export_path}/invalid.json" }
+ let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
+ let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+ let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
+
+ before do
+ stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path)
+ allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
+ allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test')
+ allow(SecureRandom).to receive(:hex).and_return('abcd')
+ setup_files
+ end
+
+ after do
+ FileUtils.rm_rf(storage_path)
+ end
+
+ context 'normal run' do
+ before do
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
+ end
+
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes evil symlinks in root folder' do
+ expect(File.exist?(evil_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
+
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
+
+ it 'creates the file in the right subfolder' do
+ expect(shared.export_path).to include('test/abcd')
+ end
+ end
+
+ context 'error' do
+ before do
+ allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
+ described_class.import(project: build(:project), archive_file: '', shared: shared)
+ end
+
+ it 'removes symlinks in root folder' do
+ expect(File.exist?(symlink_file)).to be false
+ end
+
+ it 'removes hidden symlinks in root folder' do
+ expect(File.exist?(hidden_symlink_file)).to be false
+ end
+
+ it 'removes symlinks in subfolders' do
+ expect(File.exist?(subfolder_symlink_file)).to be false
+ end
+
+ it 'does not remove a valid file' do
+ expect(File.exist?(valid_file)).to be true
+ end
+ end
+
+ def setup_files
+ FileUtils.mkdir_p("#{shared.export_path}/subfolder/")
+ FileUtils.touch(valid_file)
+ FileUtils.ln_s(valid_file, symlink_file)
+ FileUtils.ln_s(valid_file, subfolder_symlink_file)
+ FileUtils.ln_s(valid_file, hidden_symlink_file)
+ FileUtils.ln_s(valid_file, evil_symlink_file)
+ end
+end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 58b9fb06cc5..78fccdf1dfc 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::FileImporter do
let(:symlink_file) { "#{shared.export_path}/invalid.json" }
let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+ let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
before do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
@@ -23,7 +24,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'normal run' do
before do
- described_class.import(archive_file: '', shared: shared)
+ described_class.import(project: nil, archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -34,6 +35,10 @@ describe Gitlab::ImportExport::FileImporter do
expect(File.exist?(hidden_symlink_file)).to be false
end
+ it 'removes evil symlinks in root folder' do
+ expect(File.exist?(evil_symlink_file)).to be false
+ end
+
it 'removes symlinks in subfolders' do
expect(File.exist?(subfolder_symlink_file)).to be false
end
@@ -50,7 +55,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'error' do
before do
allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
- described_class.import(archive_file: '', shared: shared)
+ described_class.import(project: nil, archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -75,5 +80,7 @@ describe Gitlab::ImportExport::FileImporter do
FileUtils.touch(valid_file)
FileUtils.ln_s(valid_file, symlink_file)
FileUtils.ln_s(valid_file, subfolder_symlink_file)
+ FileUtils.ln_s(valid_file, hidden_symlink_file)
+ FileUtils.ln_s(valid_file, evil_symlink_file)
end
end
diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
new file mode 100644
index 00000000000..6a803c48b34
--- /dev/null
+++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::GroupProjectObjectBuilder do
+ let(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ context 'labels' do
+ it 'finds the right group label' do
+ group_label = create(:group_label, 'name': 'group label', 'group': project.group)
+
+ expect(described_class.build(Label,
+ 'title' => 'group label',
+ 'project' => project,
+ 'group' => project.group)).to eq(group_label)
+ end
+
+ it 'creates a new label' do
+ label = described_class.build(Label,
+ 'title' => 'group label',
+ 'project' => project,
+ 'group' => project.group)
+
+ expect(label.persisted?).to be true
+ end
+ end
+
+ context 'milestones' do
+ it 'finds the right group milestone' do
+ milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group)
+
+ expect(described_class.build(Milestone,
+ 'title' => 'group milestone',
+ 'project' => project,
+ 'group' => project.group)).to eq(milestone)
+ end
+
+ it 'creates a new milestone' do
+ milestone = described_class.build(Milestone,
+ 'title' => 'group milestone',
+ 'project' => project,
+ 'group' => project.group)
+
+ expect(milestone.persisted?).to be true
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/importer_object_storage_spec.rb b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
new file mode 100644
index 00000000000..24a994b3611
--- /dev/null
+++ b/spec/lib/gitlab/import_export/importer_object_storage_spec.rb
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::Importer do
+ let(:user) { create(:user) }
+ let(:test_path) { "#{Dir.tmpdir}/importer_spec" }
+ let(:shared) { project.import_export_shared }
+ let(:project) { create(:project) }
+ let(:import_file) { fixture_file_upload('spec/features/projects/import_export/test_project_export.tar.gz') }
+
+ subject(:importer) { described_class.new(project) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
+ allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(shared.export_path)
+ ImportExportUpload.create(project: project, import_file: import_file)
+ end
+
+ after do
+ FileUtils.rm_rf(test_path)
+ end
+
+ describe '#execute' do
+ it 'succeeds' do
+ importer.execute
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'extracts the archive' do
+ expect(Gitlab::ImportExport::FileImporter).to receive(:import).and_call_original
+
+ importer.execute
+ end
+
+ it 'checks the version' do
+ expect(Gitlab::ImportExport::VersionChecker).to receive(:check!).and_call_original
+
+ importer.execute
+ end
+
+ context 'all restores are executed' do
+ [
+ Gitlab::ImportExport::AvatarRestorer,
+ Gitlab::ImportExport::RepoRestorer,
+ Gitlab::ImportExport::WikiRestorer,
+ Gitlab::ImportExport::UploadsRestorer,
+ Gitlab::ImportExport::LfsRestorer,
+ Gitlab::ImportExport::StatisticsRestorer
+ ].each do |restorer|
+ it "calls the #{restorer}" do
+ fake_restorer = double(restorer.to_s)
+
+ expect(fake_restorer).to receive(:restore).and_return(true).at_least(1)
+ expect(restorer).to receive(:new).and_return(fake_restorer).at_least(1)
+
+ importer.execute
+ end
+ end
+
+ it 'restores the ProjectTree' do
+ expect(Gitlab::ImportExport::ProjectTreeRestorer).to receive(:new).and_call_original
+
+ importer.execute
+ end
+
+ it 'removes the import file' do
+ expect(importer).to receive(:remove_import_file).and_call_original
+
+ importer.execute
+
+ expect(project.import_export_upload.import_file&.file).to be_nil
+ end
+ end
+
+ context 'when project successfully restored' do
+ let!(:existing_project) { create(:project, namespace: user.namespace) }
+ let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') }
+
+ before do
+ restorers = double(:restorers, all?: true)
+
+ allow(subject).to receive(:import_file).and_return(true)
+ allow(subject).to receive(:check_version!).and_return(true)
+ allow(subject).to receive(:restorers).and_return(restorers)
+ allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path }))
+ end
+
+ context 'when import_data' do
+ context 'has original_path' do
+ it 'overwrites existing project' do
+ expect_any_instance_of(::Projects::OverwriteProjectService).to receive(:execute).with(existing_project)
+
+ subject.execute
+ end
+ end
+
+ context 'has not original_path' do
+ before do
+ allow(project).to receive(:import_data).and_return(double(data: {}))
+ end
+
+ it 'does not call the overwrite service' do
+ expect_any_instance_of(::Projects::OverwriteProjectService).not_to receive(:execute).with(existing_project)
+
+ subject.execute
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index c074e61da26..f07946824c4 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -10,9 +10,10 @@ describe Gitlab::ImportExport::Importer do
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
+ allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
+
FileUtils.mkdir_p(shared.export_path)
FileUtils.cp(Rails.root.join('spec/features/projects/import_export/test_project_export.tar.gz'), test_path)
- allow(subject).to receive(:remove_import_file)
end
after do
@@ -69,7 +70,7 @@ describe Gitlab::ImportExport::Importer do
let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') }
before do
- restorers = double
+ restorers = double(:restorers, all?: true)
allow(subject).to receive(:import_file).and_return(true)
allow(subject).to receive(:check_version!).and_return(true)
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 246f009ad27..67e4c289906 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -111,7 +111,7 @@ describe Gitlab::ImportExport::MembersMapper do
end
it 'maps the project member if it already exists' do
- project.add_master(user2)
+ project.add_maintainer(user2)
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json
index c13cf4a0507..ba2248073f5 100644
--- a/spec/lib/gitlab/import_export/project.light.json
+++ b/spec/lib/gitlab/import_export/project.light.json
@@ -7,7 +7,7 @@
"milestones": [
{
"id": 1,
- "title": "Project milestone",
+ "title": "A milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
@@ -66,8 +66,8 @@
"group_milestone_id": null,
"milestone": {
"id": 1,
- "title": "Project milestone",
- "project_id": 8,
+ "title": "A milestone",
+ "group_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
@@ -86,7 +86,7 @@
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 6,
- "title": "Another project label",
+ "title": "Another label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
diff --git a/spec/lib/gitlab/import_export/project.milestone-iid.json b/spec/lib/gitlab/import_export/project.milestone-iid.json
new file mode 100644
index 00000000000..b028147b5eb
--- /dev/null
+++ b/spec/lib/gitlab/import_export/project.milestone-iid.json
@@ -0,0 +1,80 @@
+{
+ "description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
+ "import_type": "gitlab_project",
+ "creator_id": 123,
+ "visibility_level": 10,
+ "archived": false,
+ "issues": [
+ {
+ "id": 1,
+ "title": "Fugiat est minima quae maxime non similique.",
+ "assignee_id": null,
+ "project_id": 8,
+ "author_id": 1,
+ "created_at": "2017-07-07T18:13:01.138Z",
+ "updated_at": "2017-08-15T18:37:40.807Z",
+ "branch_name": null,
+ "description": "Quam totam fuga numquam in eveniet.",
+ "state": "opened",
+ "iid": 20,
+ "updated_by_id": 1,
+ "confidential": false,
+ "due_date": null,
+ "moved_to_id": null,
+ "lock_version": null,
+ "time_estimate": 0,
+ "closed_at": null,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "group_milestone_id": null,
+ "milestone": {
+ "id": 1,
+ "title": "Group-level milestone",
+ "description": "Group-level milestone",
+ "due_date": null,
+ "created_at": "2016-06-14T15:02:04.415Z",
+ "updated_at": "2016-06-14T15:02:04.415Z",
+ "state": "active",
+ "iid": 1,
+ "group_id": 8
+ }
+ },
+ {
+ "id": 2,
+ "title": "est minima quae maxime non similique.",
+ "assignee_id": null,
+ "project_id": 8,
+ "author_id": 1,
+ "created_at": "2017-07-07T18:13:01.138Z",
+ "updated_at": "2017-08-15T18:37:40.807Z",
+ "branch_name": null,
+ "description": "Quam totam fuga numquam in eveniet.",
+ "state": "opened",
+ "iid": 21,
+ "updated_by_id": 1,
+ "confidential": false,
+ "due_date": null,
+ "moved_to_id": null,
+ "lock_version": null,
+ "time_estimate": 0,
+ "closed_at": null,
+ "last_edited_at": null,
+ "last_edited_by_id": null,
+ "group_milestone_id": null,
+ "milestone": {
+ "id": 2,
+ "title": "Another milestone",
+ "project_id": 8,
+ "description": "milestone",
+ "due_date": null,
+ "created_at": "2016-06-14T15:02:04.415Z",
+ "updated_at": "2016-06-14T15:02:04.415Z",
+ "state": "active",
+ "iid": 1,
+ "group_id": null
+ }
+ }
+ ],
+ "snippets": [],
+ "hooks": []
+}
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 68ddc947e02..a88ac0a091e 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@shared = @project.import_export_shared
allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/')
- allow_any_instance_of(Repository).to receive(:fetch_ref).and_return(true)
+ allow_any_instance_of(Repository).to receive(:fetch_source_branch!).and_return(true)
allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false)
expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA')
@@ -189,8 +189,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
@project.pipelines.zip([2, 2, 2, 2, 2])
.each do |(pipeline, expected_status_size)|
- expect(pipeline.statuses.size).to eq(expected_status_size)
- end
+ expect(pipeline.statuses.size).to eq(expected_status_size)
+ end
end
end
@@ -246,13 +246,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(project.issues.size).to eq(results.fetch(:issues, 0))
end
- it 'has issue with group label and project label' do
- labels = project.issues.first.labels
-
- expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0))
- expect(labels.where(type: "ProjectLabel").where.not(group_id: nil).count).to eq(0)
- end
-
it 'does not set params that are excluded from import_export settings' do
expect(project.import_type).to be_nil
expect(project.creator_id).not_to eq 123
@@ -268,12 +261,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'has group milestone' do
expect(project.group.milestones.size).to eq(results.fetch(:milestones, 0))
end
-
- it 'has issue with group label' do
- labels = project.issues.first.labels
-
- expect(labels.where(type: "GroupLabel").count).to eq(results.fetch(:first_issue_labels, 0))
- end
end
context 'Light JSON' do
@@ -360,13 +347,72 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it_behaves_like 'restores project correctly',
issues: 2,
labels: 1,
- milestones: 1,
+ milestones: 2,
first_issue_labels: 1
it_behaves_like 'restores group correctly',
- labels: 1,
- milestones: 1,
+ labels: 0,
+ milestones: 0,
first_issue_labels: 1
end
+
+ context 'with existing group models' do
+ let!(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ before do
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json")
+ end
+
+ it 'imports labels' do
+ create(:group_label, name: 'Another label', group: project.group)
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.labels.count).to eq(1)
+ end
+
+ it 'imports milestones' do
+ create(:milestone, name: 'A milestone', group: project.group)
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.group.milestones.count).to eq(1)
+ expect(project.milestones.count).to eq(0)
+ end
+ end
+
+ context 'with clashing milestones on IID' do
+ let!(:project) do
+ create(:project,
+ :builds_disabled,
+ :issues_disabled,
+ name: 'project',
+ path: 'project',
+ group: create(:group))
+ end
+
+ it 'preserves the project milestone IID' do
+ project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json")
+
+ expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
+
+ restored_project_json
+
+ expect(project.milestones.count).to eq(2)
+ expect(Milestone.find_by_title('Another milestone').iid).to eq(1)
+ expect(Milestone.find_by_title('Group-level milestone').iid).to eq(2)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 2b8a11ce8f9..fec8a2af9ab 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
let!(:project) { setup_project }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD')
allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA')
@@ -217,8 +217,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(member_emails).not_to include('group@member.com')
end
- it 'does not export group members as master' do
- Group.first.add_master(user)
+ it 'does not export group members as maintainer' do
+ Group.first.add_maintainer(user)
expect(member_emails).not_to include('group@member.com')
end
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index 187ec8fcfa2..5a646b4aac8 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do
let(:bundler) { described_class.new(project: project, shared: shared) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
new file mode 100644
index 00000000000..02f1a4b81aa
--- /dev/null
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require 'fileutils'
+
+describe Gitlab::ImportExport::Saver do
+ let!(:project) { create(:project, :public, name: 'project') }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { project.import_export_shared }
+ subject { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ FileUtils.mkdir_p(shared.export_path)
+ FileUtils.touch("#{shared.export_path}/tmp.bundle")
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ end
+
+ context 'local archive' do
+ it 'saves the repo to disk' do
+ stub_feature_flags(import_export_object_storage: false)
+
+ subject.save
+
+ expect(shared.errors).to be_empty
+ expect(Dir.empty?(shared.archive_path)).to be false
+ end
+ end
+
+ context 'object storage' do
+ it 'saves the repo using object storage' do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(ImportExportUploader)
+
+ subject.save
+
+ expect(ImportExportUpload.find_by(project: project).export_file.url)
+ .to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
new file mode 100644
index 00000000000..9c3870a0af8
--- /dev/null
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -0,0 +1,80 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::UploadsManager do
+ let(:shared) { project.import_export_shared }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:project) { create(:project) }
+ let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" }
+
+ subject(:manager) { described_class.new(project: project, shared: shared) }
+
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ FileUtils.mkdir_p(shared.export_path)
+ end
+
+ after do
+ FileUtils.rm_rf(shared.export_path)
+ end
+
+ describe '#save' do
+ context 'when the project has uploads locally stored' do
+ let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) }
+
+ before do
+ project.uploads << upload
+ end
+
+ it 'does not cause errors' do
+ manager.save
+
+ expect(shared.errors).to be_empty
+ end
+
+ it 'copies the file in the correct location when there is an upload' do
+ manager.save
+
+ expect(File).to exist(exported_file_path)
+ end
+ end
+
+ context 'using object storage' do
+ let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) }
+
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+ end
+
+ it 'saves the file' do
+ fake_uri = double
+
+ expect(fake_uri).to receive(:open).and_return(StringIO.new('File content'))
+ expect(URI).to receive(:parse).and_return(fake_uri)
+
+ manager.save
+
+ expect(File.read(exported_file_path)).to eq('File content')
+ end
+ end
+
+ describe '#restore' do
+ context 'using object storage' do
+ before do
+ stub_feature_flags(import_export_object_storage: true)
+ stub_uploads_object_storage(FileUploader)
+
+ FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038'))
+ FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt"))
+ end
+
+ it 'restores the file' do
+ manager.restore
+
+ expect(project.uploads.size).to eq(1)
+ expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 095687fa89d..c716edd9397 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
+ stub_feature_flags(import_export_object_storage: false)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
@@ -30,7 +31,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
@@ -52,7 +53,7 @@ describe Gitlab::ImportExport::UploadsSaver do
it 'copies the uploads to the export path' do
saver.save
- uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) }
+ uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) }
expect(uploads).to include('banana_sample.gif')
end
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 24bc231d5a0..441aa1defe6 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do
let!(:project_wiki) { ProjectWiki.new(project, user) }
before do
- project.add_master(user)
+ project.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
project_wiki.wiki
project_wiki.create_page("index", "test content")
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 10341486512..94abf9679c4 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -5,14 +5,16 @@ describe Gitlab::ImportSources do
it 'returns a hash' do
expected =
{
- 'GitHub' => 'github',
- 'Bitbucket' => 'bitbucket',
- 'GitLab.com' => 'gitlab',
- 'Google Code' => 'google_code',
- 'FogBugz' => 'fogbugz',
- 'Repo by URL' => 'git',
- 'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea'
+ 'GitHub' => 'github',
+ 'Bitbucket Cloud' => 'bitbucket',
+ 'Bitbucket Server' => 'bitbucket_server',
+ 'GitLab.com' => 'gitlab',
+ 'Google Code' => 'google_code',
+ 'FogBugz' => 'fogbugz',
+ 'Repo by URL' => 'git',
+ 'GitLab export' => 'gitlab_project',
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest'
}
expect(described_class.options).to eq(expected)
@@ -25,12 +27,14 @@ describe Gitlab::ImportSources do
%w(
github
bitbucket
+ bitbucket_server
gitlab
google_code
fogbugz
git
gitlab_project
gitea
+ manifest
)
expect(described_class.values).to eq(expected)
@@ -43,6 +47,7 @@ describe Gitlab::ImportSources do
%w(
github
bitbucket
+ bitbucket_server
gitlab
google_code
fogbugz
@@ -58,12 +63,14 @@ describe Gitlab::ImportSources do
import_sources = {
'github' => Gitlab::GithubImport::ParallelImporter,
'bitbucket' => Gitlab::BitbucketImport::Importer,
+ 'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
'google_code' => Gitlab::GoogleCodeImport::Importer,
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
- 'gitea' => Gitlab::LegacyGithubImport::Importer
+ 'gitea' => Gitlab::LegacyGithubImport::Importer,
+ 'manifest' => nil
}
import_sources.each do |name, klass|
@@ -76,13 +83,15 @@ describe Gitlab::ImportSources do
describe '.title' do
import_sources = {
'github' => 'GitHub',
- 'bitbucket' => 'Bitbucket',
+ 'bitbucket' => 'Bitbucket Cloud',
+ 'bitbucket_server' => 'Bitbucket Server',
'gitlab' => 'GitLab.com',
'google_code' => 'Google Code',
'fogbugz' => 'FogBugz',
'git' => 'Repo by URL',
'gitlab_project' => 'GitLab export',
- 'gitea' => 'Gitea'
+ 'gitea' => 'Gitea',
+ 'manifest' => 'Manifest file'
}
import_sources.each do |name, title|
@@ -93,7 +102,7 @@ describe Gitlab::ImportSources do
end
describe 'imports_repository? checker' do
- let(:allowed_importers) { %w[github gitlab_project] }
+ let(:allowed_importers) { %w[github gitlab_project bitbucket_server] }
it 'fails if any importer other than the allowed ones implements this method' do
current_importers = described_class.values.select { |kind| described_class.importer(kind).try(:imports_repository?) }
diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb
new file mode 100644
index 00000000000..0a62785f880
--- /dev/null
+++ b/spec/lib/gitlab/json_logger_spec.rb
@@ -0,0 +1,29 @@
+# coding: utf-8
+require 'spec_helper'
+
+describe Gitlab::JsonLogger do
+ subject { described_class.new('/dev/null') }
+
+ let(:now) { Time.now }
+
+ describe '#format_message' do
+ it 'formats strings' do
+ output = subject.format_message('INFO', now, 'test', 'Hello world')
+ data = JSON.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['message']).to eq('Hello world')
+ end
+
+ it 'formats hashes' do
+ output = subject.format_message('INFO', now, 'test', { hello: 1 })
+ data = JSON.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['hello']).to eq(1)
+ expect(data['message']).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/config_map_spec.rb b/spec/lib/gitlab/kubernetes/config_map_spec.rb
index 33dfa461202..fe65d03875f 100644
--- a/spec/lib/gitlab/kubernetes/config_map_spec.rb
+++ b/spec/lib/gitlab/kubernetes/config_map_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Kubernetes::ConfigMap do
let(:kubeclient) { double('kubernetes client') }
let(:application) { create(:clusters_applications_prometheus) }
- let(:config_map) { described_class.new(application.name, application.values) }
+ let(:config_map) { described_class.new(application.name, application.files) }
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
let(:metadata) do
@@ -15,11 +15,17 @@ describe Gitlab::Kubernetes::ConfigMap do
end
describe '#generate' do
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: { values: application.values }) }
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: application.files) }
subject { config_map.generate }
it 'should build a Kubeclient Resource' do
is_expected.to eq(resource)
end
end
+
+ describe '#config_map_name' do
+ it 'returns the config_map name' do
+ expect(config_map.config_map_name).to eq("values-content-configuration-#{application.name}")
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index aa7e43dfb16..341f71a3e49 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -39,7 +39,7 @@ describe Gitlab::Kubernetes::Helm::Api do
end
context 'with a ConfigMap' do
- let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.values).generate }
+ let(:resource) { Gitlab::Kubernetes::ConfigMap.new(application.name, application.files).generate }
it 'creates a ConfigMap on kubeclient' do
expect(client).to receive(:create_config_map).with(resource).once
@@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do
end
end
- describe '#installation_status' do
+ describe '#status' do
let(:phase) { Gitlab::Kubernetes::Pod::RUNNING }
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod)
- expect(subject.installation_status(command.pod_name)).to eq(phase)
+ expect(subject.status(command.pod_name)).to eq(phase)
end
end
- describe '#installation_log' do
+ describe '#log' do
let(:log) { 'some output' }
let(:response) { RestClient::Response.new(log) }
it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response)
- expect(subject.installation_log(command.pod_name)).to eq(log)
+ expect(subject.log(command.pod_name)).to eq(log)
end
end
- describe '#delete_installation_pod!' do
+ describe '#delete_pod!' do
it 'deletes the POD from kubernetes cluster' do
expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once
- subject.delete_installation_pod!(command.pod_name)
+ subject.delete_pod!(command.pod_name)
end
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 7be8be54d5e..d50616e95e8 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -2,7 +2,25 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:base_command) { described_class.new(application.name) }
+ let(:test_class) do
+ Class.new do
+ include Gitlab::Kubernetes::Helm::BaseCommand
+
+ def name
+ "test-class-name"
+ end
+
+ def files
+ {
+ some: 'value'
+ }
+ end
+ end
+ end
+
+ let(:base_command) do
+ test_class.new
+ end
subject { base_command }
@@ -18,15 +36,9 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
end
end
- describe '#config_map?' do
- subject { base_command.config_map? }
-
- it { is_expected.to be_falsy }
- end
-
describe '#pod_name' do
subject { base_command.pod_name }
- it { is_expected.to eq('install-helm') }
+ it { is_expected.to eq('install-test-class-name') }
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb b/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb
new file mode 100644
index 00000000000..167bee22fc3
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/certificate_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::Helm::Certificate do
+ describe '.generate_root' do
+ subject { described_class.generate_root }
+
+ it 'should generate a root CA that expires a long way in the future' do
+ expect(subject.cert.not_after).to be > 999.years.from_now
+ end
+ end
+
+ describe '#issue' do
+ subject { described_class.generate_root.issue }
+
+ it 'should generate a cert that expires soon' do
+ expect(subject.cert.not_after).to be < 60.minutes.from_now
+ end
+
+ context 'passing in INFINITE_EXPIRY' do
+ subject { described_class.generate_root.issue(expires_in: described_class::INFINITE_EXPIRY) }
+
+ it 'should generate a cert that expires a long way in the future' do
+ expect(subject.cert.not_after).to be > 999.years.from_now
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index 89e36a298f8..dcbc046cf00 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:commands) { 'helm init >/dev/null' }
+ let(:commands) { 'helm init --tiller-tls --tiller-tls-verify --tls-ca-cert /data/helm/helm/config/ca.pem --tiller-tls-cert /data/helm/helm/config/cert.pem --tiller-tls-key /data/helm/helm/config/key.pem >/dev/null' }
- subject { described_class.new(application.name) }
+ subject { described_class.new(name: application.name, files: {}) }
it_behaves_like 'helm commands'
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 25c6fa3b9a3..982e2f41043 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -1,83 +1,82 @@
require 'rails_helper'
describe Gitlab::Kubernetes::Helm::InstallCommand do
- let(:application) { create(:clusters_applications_prometheus) }
- let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
- let(:install_command) { application.install_command }
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:repository) { 'https://repository.example.com' }
+ let(:version) { '1.2.3' }
+
+ let(:install_command) do
+ described_class.new(
+ name: 'app-name',
+ chart: 'chart-name',
+ files: files,
+ version: version, repository: repository
+ )
+ end
subject { install_command }
- context 'for ingress' do
- let(:application) { create(:clusters_applications_ingress) }
-
- it_behaves_like 'helm commands' do
- let(:commands) do
- <<~EOS
- helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- EOS
- end
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
+ EOS
end
end
- context 'for prometheus' do
- let(:application) { create(:clusters_applications_prometheus) }
+ context 'when there is no repository' do
+ let(:repository) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --version #{application.version} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- context 'for runner' do
- let(:ci_runner) { create(:ci_runner) }
- let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ context 'when there is no ca.pem file' do
+ let(:files) { { 'file.txt': 'some content' } }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --version 1.2.3 --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- context 'for jupyter' do
- let(:application) { create(:clusters_applications_jupyter) }
+ context 'when there is no version' do
+ let(:version) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ helm repo add app-name https://repository.example.com
+ helm install chart-name --name app-name --tls --tls-ca-cert /data/helm/app-name/config/ca.pem --tls-cert /data/helm/app-name/config/cert.pem --tls-key /data/helm/app-name/config/key.pem --namespace gitlab-managed-apps -f /data/helm/app-name/config/values.yaml >/dev/null
EOS
end
end
end
- describe '#config_map?' do
- subject { install_command.config_map? }
-
- it { is_expected.to be_truthy }
- end
-
describe '#config_map_resource' do
let(:metadata) do
{
- name: "values-content-configuration-#{application.name}",
- namespace: namespace,
- labels: { name: "values-content-configuration-#{application.name}" }
+ name: "values-content-configuration-app-name",
+ namespace: 'gitlab-managed-apps',
+ labels: { name: "values-content-configuration-app-name" }
}
end
- let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: { values: application.values }) }
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
subject { install_command.config_map_resource }
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 43adc80d576..ec64193c0b2 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -2,14 +2,13 @@ require 'rails_helper'
describe Gitlab::Kubernetes::Helm::Pod do
describe '#generate' do
- let(:cluster) { create(:cluster) }
- let(:app) { create(:clusters_applications_prometheus, cluster: cluster) }
+ let(:app) { create(:clusters_applications_prometheus) }
let(:command) { app.install_command }
let(:namespace) { Gitlab::Kubernetes::Helm::NAMESPACE }
subject { described_class.new(command, namespace) }
- shared_examples 'helm pod' do
+ context 'with a command' do
it 'should generate a Kubeclient::Resource' do
expect(subject.generate).to be_a_kind_of(Kubeclient::Resource)
end
@@ -41,10 +40,6 @@ describe Gitlab::Kubernetes::Helm::Pod do
spec = subject.generate.spec
expect(spec.restartPolicy).to eq('Never')
end
- end
-
- context 'with a install command' do
- it_behaves_like 'helm pod'
it 'should include volumes for the container' do
container = subject.generate.spec.containers.first
@@ -60,24 +55,8 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should mount configMap specification in the volume' do
volume = subject.generate.spec.volumes.first
expect(volume.configMap['name']).to eq("values-content-configuration-#{app.name}")
- expect(volume.configMap['items'].first['key']).to eq('values')
- expect(volume.configMap['items'].first['path']).to eq('values.yaml')
- end
- end
-
- context 'with a init command' do
- let(:app) { create(:clusters_applications_helm, cluster: cluster) }
-
- it_behaves_like 'helm pod'
-
- it 'should not include volumeMounts inside the container' do
- container = subject.generate.spec.containers.first
- expect(container.volumeMounts).to be_nil
- end
-
- it 'should not a volume inside the specification' do
- spec = subject.generate.spec
- expect(spec.volumes).to be_nil
+ expect(volume.configMap['items'].first['key']).to eq(:'values.yaml')
+ expect(volume.configMap['items'].first['path']).to eq(:'values.yaml')
end
end
end
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 34b33772578..5c03a2ce7d3 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -70,4 +70,19 @@ describe Gitlab::Kubernetes do
it { is_expected.to eq(YAML.load_file(path)) }
end
end
+
+ describe '#add_terminal_auth' do
+ it 'adds authentication parameters to a hash' do
+ terminal = { original: 'value' }
+
+ add_terminal_auth(terminal, token: 'foo', max_session_time: 0, ca_pem: 'bar')
+
+ expect(terminal).to eq(
+ original: 'value',
+ headers: { 'Authorization' => ['Bearer foo'] },
+ max_session_time: 0,
+ ca_pem: 'bar'
+ )
+ end
+ end
end
diff --git a/spec/lib/gitlab/language_detection_spec.rb b/spec/lib/gitlab/language_detection_spec.rb
new file mode 100644
index 00000000000..9636fbd401b
--- /dev/null
+++ b/spec/lib/gitlab/language_detection_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+describe Gitlab::LanguageDetection do
+ set(:project) { create(:project, :repository) }
+ set(:ruby) { create(:programming_language, name: 'Ruby') }
+ set(:haskell) { create(:programming_language, name: 'Haskell') }
+ let(:repository) { project.repository }
+ let(:detection) do
+ [{ value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 12.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.9, label: "Elixir", color: "#e34c26", highlight: "#e34c26" },
+ { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" },
+ { value: 1.51, label: "Go", color: "#2a4776", highlight: "#244776" },
+ { value: 1.1, label: "MepmepLang", color: "#2a4776", highlight: "#244776" }]
+ end
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 10, programming_language: ruby)]
+ end
+
+ subject { described_class.new(repository, repository_languages) }
+
+ before do
+ allow(repository).to receive(:languages).and_return(detection)
+ end
+
+ describe '#languages' do
+ it 'returns the language names' do
+ expect(subject.languages).to eq(%w[Ruby JavaScript Elixir CoffeeScript Go])
+ end
+ end
+
+ describe '#insertions' do
+ let(:programming_languages) { [ruby, haskell] }
+ let(:detection) do
+ [{ value: 10, label: haskell.name, color: haskell.color }]
+ end
+
+ it 'only includes new languages' do
+ insertions = subject.insertions(programming_languages)
+
+ expect(insertions).not_to be_empty
+ expect(insertions.first[:project_id]).to be(project.id)
+ expect(insertions.first[:programming_language_id]).to be(haskell.id)
+ expect(insertions.first[:share]).to be(10)
+ end
+ end
+
+ describe '#updates' do
+ it 'updates the share of languages' do
+ first_update = subject.updates.first
+
+ expect(first_update).not_to be_nil
+ expect(first_update[:programming_language_id]).to eq(ruby.id)
+ expect(first_update[:share]).to eq(66.63)
+ end
+
+ it 'does not include languages to be removed' do
+ ids = subject.updates.map { |h| h[:programming_language_id] }
+
+ expect(ids).not_to include(haskell.id)
+ end
+
+ context 'when silent writes occur' do
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 66.63, programming_language: ruby)]
+ end
+
+ it "doesn't include them in the result" do
+ expect(subject.updates).to be_empty
+ end
+ end
+ end
+
+ describe '#deletions' do
+ let(:repository_languages) do
+ [RepositoryLanguage.new(share: 10, programming_language: ruby),
+ RepositoryLanguage.new(share: 5, programming_language: haskell)]
+ end
+
+ it 'lists undetected languages' do
+ expect(subject.deletions).not_to be_empty
+ expect(subject.deletions).to include(haskell.id)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb
new file mode 100644
index 00000000000..ab305fb2316
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::Manifest, :postgresql do
+ let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
+ let(:manifest) { described_class.new(file) }
+
+ describe '#valid?' do
+ context 'valid file' do
+ it { expect(manifest.valid?).to be true }
+ end
+
+ context 'missing or invalid attributes' do
+ let(:file) { Tempfile.new('foo') }
+
+ before do
+ content = <<~EOS
+ <manifest>
+ <remote review="invalid-url" />
+ <project name="platform/build"/>
+ </manifest>
+ EOS
+
+ file.write(content)
+ file.rewind
+ end
+
+ it { expect(manifest.valid?).to be false }
+
+ describe 'errors' do
+ before do
+ manifest.valid?
+ end
+
+ it { expect(manifest.errors).to include('Make sure a <remote> tag is present and is valid.') }
+ it { expect(manifest.errors).to include('Make sure every <project> tag has name and path attributes.') }
+ end
+ end
+ end
+
+ describe '#projects' do
+ it { expect(manifest.projects.size).to eq(660) }
+ it { expect(manifest.projects[0][:name]).to eq('platform/build') }
+ it { expect(manifest.projects[0][:path]).to eq('build/make') }
+ it { expect(manifest.projects[0][:url]).to eq('https://android-review.googlesource.com/platform/build') }
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
new file mode 100644
index 00000000000..1d01d437535
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+ let(:repository) do
+ {
+ path: 'device/common',
+ url: 'https://android-review.googlesource.com/device/common'
+ }
+ end
+
+ before do
+ group.add_owner(user)
+ end
+
+ subject { described_class.new(repository, group, user) }
+
+ describe '#execute' do
+ it { expect(subject.execute).to be_a(Project) }
+ it { expect { subject.execute }.to change { Project.count }.by(1) }
+ it { expect { subject.execute }.to change { Group.count }.by(1) }
+
+ it 'creates project with valid full path and import url' do
+ subject.execute
+
+ project = Project.last
+
+ expect(project.full_path).to eq(File.join(group.path, 'device/common'))
+ expect(project.import_url).to eq('https://android-review.googlesource.com/device/common')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/basic_health_check_spec.rb b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
new file mode 100644
index 00000000000..187d903a5e1
--- /dev/null
+++ b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+
+describe Gitlab::Middleware::BasicHealthCheck do
+ let(:app) { double(:app) }
+ let(:middleware) { described_class.new(app) }
+ let(:env) { {} }
+
+ describe '#call' do
+ context 'outside IP' do
+ before do
+ env['REMOTE_ADDR'] = '8.8.8.8'
+ end
+
+ it 'returns a 404' do
+ env['PATH_INFO'] = described_class::HEALTH_PATH
+
+ response = middleware.call(env)
+
+ expect(response[0]).to eq(404)
+ end
+
+ it 'forwards the call for other paths' do
+ env['PATH_INFO'] = '/'
+
+ expect(app).to receive(:call)
+
+ middleware.call(env)
+ end
+ end
+
+ context 'whitelisted IP' do
+ before do
+ env['REMOTE_ADDR'] = '127.0.0.1'
+ end
+
+ it 'returns 200 response when endpoint is hit' do
+ env['PATH_INFO'] = described_class::HEALTH_PATH
+
+ expect(app).not_to receive(:call)
+
+ response = middleware.call(env)
+
+ expect(response[0]).to eq(200)
+ expect(response[1]).to eq({ 'Content-Type' => 'text/plain' })
+ expect(response[2]).to eq(['GitLab OK'])
+ end
+
+ it 'forwards the call for other paths' do
+ env['PATH_INFO'] = '/-/readiness'
+
+ expect(app).to receive(:call)
+
+ middleware.call(env)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index b24c9882c0c..7a3a9ab875b 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -79,7 +79,7 @@ describe Gitlab::Middleware::Go do
let(:current_user) { project.creator }
before do
- project.team.add_master(current_user)
+ project.team.add_maintainer(current_user)
end
shared_examples 'authenticated' do
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
index a2ba91dae80..f788f8ee276 100644
--- a/spec/lib/gitlab/middleware/multipart_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -7,18 +7,47 @@ describe Gitlab::Middleware::Multipart do
let(:middleware) { described_class.new(app) }
let(:original_filename) { 'filename' }
- it 'opens top-level files' do
- Tempfile.open('top-level') do |tempfile|
- env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ shared_examples_for 'multipart upload files' do
+ it 'opens top-level files' do
+ Tempfile.open('top-level') do |tempfile|
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename, 'file.path' => tempfile.path, 'file.remote_id' => remote_id }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ expect_uploaded_file(tempfile, %w(file))
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files one level deep' do
+ Tempfile.open('one-level') do |tempfile|
+ in_params = { 'user' => { 'avatar' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } }
+ env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect_uploaded_file(tempfile, %w(user avatar))
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files two levels deep' do
+ Tempfile.open('two-levels') do |tempfile|
+ in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename, '.path' => tempfile.path, '.remote_id' => remote_id } } } }
+ env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect_uploaded_file(tempfile, %w(project milestone themesong))
+
+ middleware.call(env)
+ end
+ end
+
+ def expect_uploaded_file(tempfile, path, remote: false)
expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['file']
+ file = Rack::Request.new(env).params.dig(*path)
expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path)
expect(file.original_filename).to eq(original_filename)
+ expect(file.remote_id).to eq(remote_id)
end
-
- middleware.call(env)
end
end
@@ -34,32 +63,39 @@ describe Gitlab::Middleware::Multipart do
expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError)
end
- it 'opens files one level deep' do
- Tempfile.open('one-level') do |tempfile|
- in_params = { 'user' => { 'avatar' => { '.name' => original_filename } } }
- env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ context 'with remote file' do
+ let(:remote_id) { 'someid' }
- expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['user']['avatar']
- expect(file).to be_a(::UploadedFile)
- expect(file.path).to eq(tempfile.path)
- expect(file.original_filename).to eq(original_filename)
- end
+ it_behaves_like 'multipart upload files'
+ end
- middleware.call(env)
- end
+ context 'with local file' do
+ let(:remote_id) { nil }
+
+ it_behaves_like 'multipart upload files'
end
- it 'opens files two levels deep' do
+ it 'allows symlinks for uploads dir' do
Tempfile.open('two-levels') do |tempfile|
- in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename } } } }
- env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ symlinked_dir = '/some/dir/uploads'
+ symlinked_path = File.join(symlinked_dir, File.basename(tempfile.path))
+ env = post_env({ 'file' => symlinked_path }, { 'file.name' => original_filename, 'file.path' => symlinked_path }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ allow(FileUploader).to receive(:root).and_return(symlinked_dir)
+ allow(UploadedFile).to receive(:allowed_paths).and_return([symlinked_dir, Gitlab.config.uploads.storage_path])
+ allow(File).to receive(:realpath).and_call_original
+ allow(File).to receive(:realpath).with(symlinked_dir).and_return(Dir.tmpdir)
+ allow(File).to receive(:realpath).with(symlinked_path).and_return(tempfile.path)
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with(symlinked_dir).and_return(true)
+
+ # override Dir.tmpdir because this dir is in the list of allowed paths
+ # and it would match FileUploader.root path (which in this test is linked
+ # to /tmp too)
+ allow(Dir).to receive(:tmpdir).and_return(File.join(Dir.tmpdir, 'tmpsubdir'))
expect(app).to receive(:call) do |env|
- file = Rack::Request.new(env).params['project']['milestone']['themesong']
- expect(file).to be_a(::UploadedFile)
- expect(file.path).to eq(tempfile.path)
- expect(file.original_filename).to eq(original_filename)
+ expect(Rack::Request.new(env).params['file']).to be_a(::UploadedFile)
end
middleware.call(env)
diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb
index 39ec2f37a83..8fbeaa065fa 100644
--- a/spec/lib/gitlab/middleware/read_only_spec.rb
+++ b/spec/lib/gitlab/middleware/read_only_spec.rb
@@ -2,28 +2,7 @@ require 'spec_helper'
describe Gitlab::Middleware::ReadOnly do
include Rack::Test::Methods
-
- RSpec::Matchers.define :be_a_redirect do
- match do |response|
- response.status == 301
- end
- end
-
- RSpec::Matchers.define :disallow_request do
- match do |middleware|
- alert = middleware.env['rack.session'].to_hash
- .dig('flash', 'flashes', 'alert')
-
- alert&.include?('You cannot perform write operations')
- end
- end
-
- RSpec::Matchers.define :disallow_request_in_json do
- match do |response|
- json_response = JSON.parse(response.body)
- response.body.include?('You cannot perform write operations') && json_response.key?('message')
- end
- end
+ using RSpec::Parameterized::TableSyntax
let(:rack_stack) do
rack = Rack::Builder.new do
@@ -65,38 +44,38 @@ describe Gitlab::Middleware::ReadOnly do
it 'expects PATCH requests to be disallowed' do
response = request.patch('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects PUT requests to be disallowed' do
response = request.put('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects POST requests to be disallowed' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
it 'expects a internal POST request to be allowed after a disallowed request' do
response = request.post('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
end
it 'expects DELETE requests to be disallowed' do
response = request.delete('/test_request')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -104,7 +83,7 @@ describe Gitlab::Middleware::ReadOnly do
expect(Rails.application.routes).to receive(:recognize_path).and_call_original
response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch')
- expect(response).to be_a_redirect
+ expect(response).to be_redirect
expect(subject).to disallow_request
end
@@ -117,39 +96,41 @@ describe Gitlab::Middleware::ReadOnly do
context 'whitelisted requests' do
it 'expects a POST internal request to be allowed' do
expect(Rails.application.routes).not_to receive(:recognize_path)
-
response = request.post("/api/#{API::API.version}/internal")
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
- it 'expects a POST LFS request to batch URL to be allowed' do
- expect(Rails.application.routes).to receive(:recognize_path).and_call_original
- response = request.post('/root/rouge.git/info/lfs/objects/batch')
+ it 'expects requests to sidekiq admin to be allowed' do
+ response = request.post('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
- end
- it 'expects a POST request to git-upload-pack URL to be allowed' do
- expect(Rails.application.routes).to receive(:recognize_path).and_call_original
- response = request.post('/root/rouge.git/git-upload-pack')
+ response = request.get('/admin/sidekiq')
- expect(response).not_to be_a_redirect
+ expect(response).not_to be_redirect
expect(subject).not_to disallow_request
end
- it 'expects requests to sidekiq admin to be allowed' do
- response = request.post('/admin/sidekiq')
+ where(:description, :path) do
+ 'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch'
+ 'LFS request to locks verify' | '/root/rouge.git/info/lfs/locks/verify'
+ 'LFS request to locks create' | '/root/rouge.git/info/lfs/locks'
+ 'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock'
+ 'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
+ 'request to git-receive-pack' | '/root/rouge.git/git-receive-pack'
+ end
- expect(response).not_to be_a_redirect
- expect(subject).not_to disallow_request
+ with_them do
+ it "expects a POST #{description} URL to be allowed" do
+ expect(Rails.application.routes).to receive(:recognize_path).and_call_original
+ response = request.post(path)
- response = request.get('/admin/sidekiq')
-
- expect(response).not_to be_a_redirect
- expect(subject).not_to disallow_request
+ expect(response).not_to be_redirect
+ expect(subject).not_to disallow_request
+ end
end
end
end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 1dbead16d5b..c1b84e9f077 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -55,6 +55,19 @@ describe Gitlab::Popen do
end
end
+ context 'with a process that writes a lot of data to stderr' do
+ let(:test_string) { 'The quick brown fox jumped over the lazy dog' }
+ # The pipe buffer is typically 64K. This string is about 440K.
+ let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] }
+
+ it 'returns zero' do
+ output, status = @klass.new.popen(spew_command, path)
+
+ expect(output).to include(test_string)
+ expect(status).to eq(0)
+ end
+ end
+
context 'without a directory argument' do
before do
@output, @status = @klass.new.popen(%w(ls))
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index f3cd6961e94..00c62c7bf96 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -41,7 +41,7 @@ describe Gitlab::ProjectAuthorizations do
it 'includes the correct access levels' do
mapping = map_access_levels(authorizations)
- expect(mapping[owned_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER)
expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER)
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
@@ -62,11 +62,11 @@ describe Gitlab::ProjectAuthorizations do
end
it 'uses the greatest access level when a user is a member of a nested group' do
- nested_group.add_master(user)
+ nested_group.add_maintainer(user)
mapping = map_access_levels(authorizations)
- expect(mapping[nested_project.id]).to eq(Gitlab::Access::MASTER)
+ expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 50224bde722..4a0dc3686ec 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -64,6 +64,49 @@ describe Gitlab::ProjectSearchResults do
end
end
+ shared_examples 'blob search repository ref' do |entity_type|
+ let(:query) { 'files' }
+ let(:file_finder) { double }
+ let(:project_branch) { 'project_branch' }
+
+ subject(:results) { described_class.new(user, project, query, repository_ref).objects(blob_type) }
+
+ before do
+ allow(entity).to receive(:default_branch).and_return(project_branch)
+ allow(file_finder).to receive(:find).and_return([])
+ end
+
+ context 'when repository_ref exists' do
+ let(:repository_ref) { 'ref_branch' }
+
+ it 'uses it' do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder)
+
+ results
+ end
+ end
+
+ context 'when repository_ref is not present' do
+ let(:repository_ref) { nil }
+
+ it "uses #{entity_type} repository default reference" do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
+
+ results
+ end
+ end
+
+ context 'when repository_ref is blank' do
+ let(:repository_ref) { '' }
+
+ it "uses #{entity_type} repository default reference" do
+ expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder)
+
+ results
+ end
+ end
+ end
+
describe 'blob search' do
let(:project) { create(:project, :public, :repository) }
@@ -75,6 +118,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_content) { 'CHANGELOG' }
end
+ it_behaves_like 'blob search repository ref', 'project' do
+ let(:blob_type) { 'blobs' }
+ let(:entity) { project }
+ end
+
describe 'parsing results' do
let(:results) { project.repository.search_files_by_content('feature', 'master') }
let(:search_result) { results.first }
@@ -212,6 +260,11 @@ describe Gitlab::ProjectSearchResults do
let(:expected_file_by_name) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
+
+ it_behaves_like 'blob search repository ref', 'wiki' do
+ let(:blob_type) { 'wiki_blobs' }
+ let(:entity) { project.wiki }
+ end
end
it 'does not list issues on private projects' do
@@ -385,7 +438,7 @@ describe Gitlab::ProjectSearchResults do
let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) }
let(:team_master) do
user = create(:user, username: 'private-project-master')
- private_project.add_master(user)
+ private_project.add_maintainer(user)
user
end
let(:team_reporter) do
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
index 85971f2a7ef..5bd4d6c6a48 100644
--- a/spec/lib/gitlab/repository_cache_adapter_spec.rb
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -67,10 +67,18 @@ describe Gitlab::RepositoryCacheAdapter do
describe '#expire_method_caches' do
it 'expires the caches of the given methods' do
- expect(cache).to receive(:expire).with(:readme)
+ expect(cache).to receive(:expire).with(:rendered_readme)
expect(cache).to receive(:expire).with(:gitignore)
- repository.expire_method_caches(%i(readme gitignore))
+ repository.expire_method_caches(%i(rendered_readme gitignore))
+ end
+
+ it 'does not expire caches for non-existent methods' do
+ expect(cache).not_to receive(:expire).with(:nonexistent)
+ expect(Rails.logger).to(
+ receive(:error).with("Requested to expire non-existent method 'nonexistent' for Repository"))
+
+ repository.expire_method_caches(%i(nonexistent))
end
end
end
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
index 030c2063ab2..df46a874528 100644
--- a/spec/lib/gitlab/sanitizers/svg_spec.rb
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -7,9 +7,9 @@ describe Gitlab::Sanitizers::SVG do
describe '.clean' do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
- let(:data) { open(input_svg_path).read }
+ let(:data) { File.read(input_svg_path) }
let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
- let(:sanitized) { open(sanitized_svg_path).read }
+ let(:sanitized) { File.read(sanitized_svg_path) }
it 'delegates sanitization to scrubber' do
expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
diff --git a/spec/lib/gitlab/serializer/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb
index c4b7fda5dbb..1d1fd5b0763 100644
--- a/spec/lib/gitlab/serializer/ci/variables_spec.rb
+++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb
@@ -1,4 +1,4 @@
-require 'spec_helper'
+require 'fast_spec_helper'
describe Gitlab::Serializer::Ci::Variables do
subject do
@@ -6,11 +6,11 @@ describe Gitlab::Serializer::Ci::Variables do
end
let(:object) do
- [{ key: :key, value: 'value', public: true },
+ [{ 'key' => :key, 'value' => 'value', 'public' => true },
{ key: 'wee', value: 1, public: false }]
end
- it 'converts keys into strings' do
+ it 'converts keys into strings and symbolizes hash' do
is_expected.to eq([
{ key: 'key', value: 'value', public: true },
{ key: 'wee', value: 1, public: false }
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index c435f988cdd..f8bf896950e 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -403,46 +403,36 @@ describe Gitlab::Shell do
end
describe '#create_repository' do
- shared_examples '#create_repository' do
- let(:repository_storage) { 'default' }
- let(:repository_storage_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
- end
- end
- let(:repo_name) { 'project/path' }
- let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
-
- after do
- FileUtils.rm_rf(created_path)
+ let(:repository_storage) { 'default' }
+ let(:repository_storage_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
end
+ end
+ let(:repo_name) { 'project/path' }
+ let(:created_path) { File.join(repository_storage_path, repo_name + '.git') }
- it 'creates a repository' do
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
-
- expect(File.stat(created_path).mode & 0o777).to eq(0o770)
+ after do
+ FileUtils.rm_rf(created_path)
+ end
- hooks_path = File.join(created_path, 'hooks')
- expect(File.lstat(hooks_path)).to be_symlink
- expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
- end
+ it 'creates a repository' do
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
- it 'returns false when the command fails' do
- FileUtils.mkdir_p(File.dirname(created_path))
- # This file will block the creation of the repo's .git directory. That
- # should cause #create_repository to fail.
- FileUtils.touch(created_path)
+ expect(File.stat(created_path).mode & 0o777).to eq(0o770)
- expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
- end
+ hooks_path = File.join(created_path, 'hooks')
+ expect(File.lstat(hooks_path)).to be_symlink
+ expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path)
end
- context 'with gitaly' do
- it_behaves_like '#create_repository'
- end
+ it 'returns false when the command fails' do
+ FileUtils.mkdir_p(File.dirname(created_path))
+ # This file will block the creation of the repo's .git directory. That
+ # should cause #create_repository to fail.
+ FileUtils.touch(created_path)
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#create_repository'
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
end
end
@@ -513,22 +503,12 @@ describe Gitlab::Shell do
end
end
- shared_examples 'fetch_remote' do |gitaly_on|
+ describe '#fetch_remote' do
def fetch_remote(ssh_auth = nil, prune = true)
gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth, prune: prune)
end
- def expect_gitlab_projects(fail = false, options = {})
- expect(gitlab_projects).to receive(:fetch_remote).with(
- 'remote-name',
- timeout,
- options
- ).and_return(!fail)
-
- allow(gitlab_projects).to receive(:output).and_return('error') if fail
- end
-
- def expect_gitaly_call(fail, options = {})
+ def expect_call(fail, options = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
@@ -539,16 +519,6 @@ describe Gitlab::Shell do
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive_fetch_remote
end
- if gitaly_on
- def expect_call(fail, options = {})
- expect_gitaly_call(fail, options)
- end
- else
- def expect_call(fail, options = {})
- expect_gitlab_projects(fail, options)
- end
- end
-
def build_ssh_auth(opts = {})
defaults = {
ssh_import?: true,
@@ -634,14 +604,6 @@ describe Gitlab::Shell do
expect(fetch_remote(ssh_auth)).to be_truthy
end
end
- end
-
- describe '#fetch_remote local', :skip_gitaly_mock do
- it_should_behave_like 'fetch_remote', false
- end
-
- describe '#fetch_remote gitaly' do
- it_should_behave_like 'fetch_remote', true
context 'gitaly call' do
let(:remote_name) { 'remote-name' }
@@ -683,25 +645,6 @@ describe Gitlab::Shell do
end.to raise_error(Gitlab::Shell::Error, "error")
end
end
-
- context 'without gitaly', :disable_gitaly do
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
-
- result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
-
- expect(result).to be_truthy
- end
-
- it 'raises an exception when the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:import_project) { false }
-
- expect do
- gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
- end.to raise_error(Gitlab::Shell::Error, "error")
- end
- end
end
end
diff --git a/spec/lib/gitlab/slash_commands/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
index d41441c9472..9a990e1fad7 100644
--- a/spec/lib/gitlab/slash_commands/issue_move_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_move_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::SlashCommands::IssueMove, service: true do
set(:other_project) { create(:project, namespace: project.namespace) }
before do
- [project, other_project].each { |prj| prj.add_master(user) }
+ [project, other_project].each { |prj| prj.add_maintainer(user) }
end
subject { described_class.new(project, chat_name) }
diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 8e7df946529..724c76ade6e 100644
--- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueNew do
let(:regex_match) { described_class.match("issue create bird is the word") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 189e9592f1b..47787307990 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::IssueSearch do
context 'the user has access' do
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
it 'returns all results' do
diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index b1db1638237..5c4ba2736ba 100644
--- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
before do
- project.add_master(user)
+ project.add_maintainer(user)
end
subject do
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index fc8991fd31f..b41a81a8167 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -92,6 +92,7 @@ describe Gitlab::UrlSanitizer do
context 'credentials in URL' do
where(:url, :credentials) do
'http://foo:bar@example.com' | { user: 'foo', password: 'bar' }
+ 'http://foo:bar:baz@example.com' | { user: 'foo', password: 'bar:baz' }
'http://:bar@example.com' | { user: nil, password: 'bar' }
'http://foo:@example.com' | { user: 'foo', password: nil }
'http://foo@example.com' | { user: 'foo', password: nil }
@@ -144,6 +145,10 @@ describe Gitlab::UrlSanitizer do
'http://foo:@example.com' | 'http://foo@example.com'
'http://:bar@example.com' | :same
'http://foo:bar@example.com' | :same
+ 'http://foo:g p@example.com' | 'http://foo:g%20p@example.com'
+ 'http://foo:s/h@example.com' | 'http://foo:s%2Fh@example.com'
+ 'http://t u:a#b@example.com' | 'http://t%20u:a%23b@example.com'
+ 'http://t+u:a#b@example.com' | 'http://t%2Bu:a%23b@example.com'
end
with_them do
@@ -159,7 +164,7 @@ describe Gitlab::UrlSanitizer do
url_sanitizer = described_class.new("https://foo:b?r@github.com/me/project.git")
expect(url_sanitizer.sanitized_url).to eq("https://github.com/me/project.git")
- expect(url_sanitizer.full_url).to eq("https://foo:b?r@github.com/me/project.git")
+ expect(url_sanitizer.full_url).to eq("https://foo:b%3Fr@github.com/me/project.git")
end
end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 20def4fefe2..a19b3c0ba66 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -133,7 +133,7 @@ describe Gitlab::UsageData do
expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?)
expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?)
- expect(subject[:omniauth_enabled]).to eq(Gitlab.config.omniauth.enabled)
+ expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?)
expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 0469d984a40..9da06bb40f4 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -9,8 +9,8 @@ describe Gitlab::UserAccess do
describe '#can_push_to_branch?' do
describe 'push to none protected branch' do
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?('random_branch')).to be_truthy
end
@@ -38,8 +38,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?('master')).to be_truthy
end
- it 'returns true if user is master' do
- empty_project.add_master(user)
+ it 'returns true if user is maintainer' do
+ empty_project.add_maintainer(user)
expect(project_access.can_push_to_branch?('master')).to be_truthy
end
@@ -83,8 +83,8 @@ describe Gitlab::UserAccess do
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(branch.name)).to be_truthy
end
@@ -113,8 +113,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_push, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_push_to_branch?(@branch.name)).to be_truthy
end
@@ -170,8 +170,8 @@ describe Gitlab::UserAccess do
@branch = create :protected_branch, :developers_can_merge, project: project
end
- it 'returns true if user is a master' do
- project.add_master(user)
+ it 'returns true if user is a maintainer' do
+ project.add_maintainer(user)
expect(access.can_merge_to_branch?(@branch.name)).to be_truthy
end
@@ -192,8 +192,8 @@ describe Gitlab::UserAccess do
describe '#can_create_tag?' do
describe 'push to none protected tag' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?('random_tag')).to be_truthy
end
@@ -215,8 +215,8 @@ describe Gitlab::UserAccess do
let(:tag) { create(:protected_tag, project: project, name: "test") }
let(:not_existing_tag) { create :protected_tag, project: project }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(tag.name)).to be_truthy
end
@@ -239,8 +239,8 @@ describe Gitlab::UserAccess do
@tag = create(:protected_tag, :developers_can_create, project: project)
end
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_create_tag?(@tag.name)).to be_truthy
end
@@ -261,8 +261,8 @@ describe Gitlab::UserAccess do
describe '#can_delete_branch?' do
describe 'delete unprotected branch' do
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?('random_branch')).to be_truthy
end
@@ -283,8 +283,8 @@ describe Gitlab::UserAccess do
describe 'delete protected branch' do
let(:branch) { create(:protected_branch, project: project, name: "test") }
- it 'returns true if user is a master' do
- project.add_user(user, :master)
+ it 'returns true if user is a maintainer' do
+ project.add_user(user, :maintainer)
expect(access.can_delete_branch?(branch.name)).to be_truthy
end
diff --git a/spec/lib/gitlab/user_activities_spec.rb b/spec/lib/gitlab/user_activities_spec.rb
deleted file mode 100644
index 6bce2ee13cf..00000000000
--- a/spec/lib/gitlab/user_activities_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::UserActivities, :clean_gitlab_redis_shared_state do
- let(:now) { Time.now }
-
- describe '.record' do
- context 'with no time given' do
- it 'uses Time.now and records an activity in SharedState' do
- Timecop.freeze do
- now # eager-load now
- described_class.record(42)
- end
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
- end
- end
-
- context 'with a time given' do
- it 'uses the given time and records an activity in SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
- end
- end
- end
-
- describe '.delete' do
- context 'with a single key' do
- context 'and key exists' do
- it 'removes the pair from SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
-
- subject.delete(42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
-
- context 'and key does not exist' do
- it 'removes the pair from SharedState' do
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
-
- subject.delete(42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
- end
-
- context 'with multiple keys' do
- context 'and all keys exist' do
- it 'removes the pair from SharedState' do
- described_class.record(41, now)
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['41', now.to_i.to_s], ['42', now.to_i.to_s]]])
- end
-
- subject.delete(41, 42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
-
- context 'and some keys does not exist' do
- it 'removes the existing pair from SharedState' do
- described_class.record(42, now)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]])
- end
-
- subject.delete(41, 42)
-
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []])
- end
- end
- end
- end
- end
-
- describe 'Enumerable' do
- before do
- described_class.record(40, now)
- described_class.record(41, now)
- described_class.record(42, now)
- end
-
- it 'allows to read the activities sequentially' do
- expected = { '40' => now.to_i.to_s, '41' => now.to_i.to_s, '42' => now.to_i.to_s }
-
- actual = described_class.new.each_with_object({}) do |(key, time), actual|
- actual[key] = time
- end
-
- expect(actual).to eq(expected)
- end
-
- context 'with many records' do
- before do
- 1_000.times { |i| described_class.record(i, now) }
- end
-
- it 'is possible to loop through all the records' do
- expect(described_class.new.count).to eq(1_000)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 660671cefaf..23869f3d2da 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -36,22 +36,20 @@ describe Gitlab::Workhorse do
allow(described_class).to receive(:git_archive_cache_disabled?).and_return(cache_disabled)
end
- context 'when Gitaly workhorse_archive feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-archive')
- expect(params).to include(gitaly_params)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-archive')
+ expect(params).to include(gitaly_params)
+ end
- context 'when archive caching is disabled' do
- let(:cache_disabled) { true }
+ context 'when archive caching is disabled' do
+ let(:cache_disabled) { true }
- it 'tells workhorse not to use the cache' do
- _, _, params = decode_workhorse_header(subject)
- expect(params).to include({ 'DisableCache' => true })
- end
+ it 'tells workhorse not to use the cache' do
+ _, _, params = decode_workhorse_header(subject)
+ expect(params).to include({ 'DisableCache' => true })
end
end
@@ -70,34 +68,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_patch(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_patch feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_patch feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-format-patch")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-format-patch")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawPatchRequest' => Gitaly::RawPatchRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -143,34 +129,22 @@ describe Gitlab::Workhorse do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
subject { described_class.send_git_diff(repository, diff_refs) }
- context 'when Gitaly workhorse_send_git_diff feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
- repository: repository.gitaly_repository,
- left_commit_id: 'base',
- right_commit_id: 'head'
- ).to_json
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_send_git_diff feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq("Gitlab-Workhorse-Send-Data")
- expect(command).to eq("git-diff")
- expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head")
- end
+ expect(key).to eq("Gitlab-Workhorse-Send-Data")
+ expect(command).to eq("git-diff")
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'RawDiffRequest' => Gitaly::RawDiffRequest.new(
+ repository: repository.gitaly_repository,
+ left_commit_id: 'base',
+ right_commit_id: 'head'
+ ).to_json
+ }.deep_stringify_keys)
end
end
@@ -189,7 +163,7 @@ describe Gitlab::Workhorse do
end
it 'accepts a trailing newline' do
- open(described_class.secret_path, 'a') { |f| f.write "\n" }
+ File.open(described_class.secret_path, 'a') { |f| f.write "\n" }
expect(subject.length).to eq(32)
end
@@ -425,34 +399,22 @@ describe Gitlab::Workhorse do
subject { described_class.send_git_blob(repository, blob) }
- context 'when Gitaly workhorse_raw_show feature is enabled' do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
-
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq({
- 'GitalyServer' => {
- address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
- },
- 'GetBlobRequest' => {
- repository: repository.gitaly_repository.to_h,
- oid: blob.id,
- limit: -1
- }
- }.deep_stringify_keys)
- end
- end
-
- context 'when Gitaly workhorse_raw_show feature is disabled', :disable_gitaly do
- it 'sets the header correctly' do
- key, command, params = decode_workhorse_header(subject)
+ it 'sets the header correctly' do
+ key, command, params = decode_workhorse_header(subject)
- expect(key).to eq('Gitlab-Workhorse-Send-Data')
- expect(command).to eq('git-blob')
- expect(params).to eq('RepoPath' => repository.path_to_repo, 'BlobId' => blob.id)
- end
+ expect(key).to eq('Gitlab-Workhorse-Send-Data')
+ expect(command).to eq('git-blob')
+ expect(params).to eq({
+ 'GitalyServer' => {
+ address: Gitlab::GitalyClient.address(project.repository_storage),
+ token: Gitlab::GitalyClient.token(project.repository_storage)
+ },
+ 'GetBlobRequest' => {
+ repository: repository.gitaly_repository.to_h,
+ oid: blob.id,
+ limit: -1
+ }
+ }.deep_stringify_keys)
end
end
diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb
index cc99e7e8911..a2f5c2e7121 100644
--- a/spec/lib/uploaded_file_spec.rb
+++ b/spec/lib/uploaded_file_spec.rb
@@ -1,24 +1,28 @@
require 'spec_helper'
describe UploadedFile do
- describe ".from_params" do
- let(:temp_dir) { Dir.tmpdir }
- let(:temp_file) { Tempfile.new("test", temp_dir) }
- let(:upload_path) { nil }
+ let(:temp_dir) { Dir.tmpdir }
+ let(:temp_file) { Tempfile.new("test", temp_dir) }
- subject do
- described_class.from_params(params, :file, upload_path)
- end
+ before do
+ FileUtils.touch(temp_file)
+ end
- before do
- FileUtils.touch(temp_file)
- end
+ after do
+ FileUtils.rm_f(temp_file)
+ end
+
+ describe ".from_params" do
+ let(:upload_path) { nil }
after do
- FileUtils.rm_f(temp_file)
FileUtils.rm_r(upload_path) if upload_path
end
+ subject do
+ described_class.from_params(params, :file, upload_path)
+ end
+
context 'when valid file is specified' do
context 'only local path is specified' do
let(:params) do
@@ -37,7 +41,7 @@ describe UploadedFile do
context 'all parameters are specified' do
let(:params) do
{ 'file.path' => temp_file.path,
- 'file.name' => 'my_file.txt',
+ 'file.name' => 'dir/my file&.txt',
'file.type' => 'my/type',
'file.sha256' => 'sha256',
'file.remote_id' => 'remote_id' }
@@ -48,7 +52,7 @@ describe UploadedFile do
end
it "generates filename from path" do
- expect(subject.original_filename).to eq('my_file.txt')
+ expect(subject.original_filename).to eq('my_file_.txt')
expect(subject.content_type).to eq('my/type')
expect(subject.sha256).to eq('sha256')
expect(subject.remote_id).to eq('remote_id')
@@ -113,4 +117,11 @@ describe UploadedFile do
end
end
end
+
+ describe '#sanitize_filename' do
+ it { expect(described_class.new(temp_file.path).sanitize_filename('spaced name')).to eq('spaced_name') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('#$%^&')).to eq('_____') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('..')).to eq('_..') }
+ it { expect(described_class.new(temp_file.path).sanitize_filename('')).to eq('unnamed') }
+ end
end