summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb399
-rw-r--r--spec/lib/api/helpers_spec.rb14
-rw-r--r--spec/lib/backup/repository_spec.rb2
-rw-r--r--spec/lib/banzai/filter/asset_proxy_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb71
-rw-r--r--spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb48
-rw-r--r--spec/lib/banzai/filter/video_link_filter_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/pull_request_spec.rb1
-rw-r--r--spec/lib/container_registry/client_spec.rb12
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb92
-rw-r--r--spec/lib/gitlab/auth/ldap/auth_hash_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/ldap/config_spec.rb19
-rw-r--r--spec/lib/gitlab/auth/ldap/person_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb15
-rw-r--r--spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb12
-rw-r--r--spec/lib/gitlab/background_migration/schedule_calculate_wiki_sizes_spec.rb2
-rw-r--r--spec/lib/gitlab/badge/pipeline/status_spec.rb2
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb1
-rw-r--r--spec/lib/gitlab/checks/lfs_integrity_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/style_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/ansi2json_spec.rb71
-rw-r--r--spec/lib/gitlab/ci/build/context/build_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/build/context/global_spec.rb25
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/rules_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb77
-rw-r--r--spec/lib/gitlab/ci/config/entry/commands_spec.rb67
-rw-r--r--spec/lib/gitlab/ci/config/entry/default_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config/entry/files_spec.rb54
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb31
-rw-r--r--spec/lib/gitlab/ci/config/entry/key_spec.rb94
-rw-r--r--spec/lib/gitlab/ci/config/entry/need_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/entry/needs_spec.rb84
-rw-r--r--spec/lib/gitlab/ci/config/entry/prefix_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb120
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/config/entry/script_spec.rb67
-rw-r--r--spec/lib/gitlab/ci/config/entry/workflow_spec.rb76
-rw-r--r--spec/lib/gitlab/ci/config/normalizer_spec.rb104
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb60
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb161
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb148
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb261
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb104
-rw-r--r--spec/lib/gitlab/ci/status/composite_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb338
-rw-r--r--spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb9
-rw-r--r--spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb29
-rw-r--r--spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb37
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb6
-rw-r--r--spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb16
-rw-r--r--spec/lib/gitlab/cycle_analytics/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb13
-rw-r--r--spec/lib/gitlab/danger/teammate_spec.rb18
-rw-r--r--spec/lib/gitlab/data_builder/deployment_spec.rb7
-rw-r--r--spec/lib/gitlab/data_builder/push_spec.rb26
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb1
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb9
-rw-r--r--spec/lib/gitlab/devise_failure_spec.rb35
-rw-r--r--spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb2
-rw-r--r--spec/lib/gitlab/exclusive_lease_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/exclusive_lease_spec.rb2
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb261
-rw-r--r--spec/lib/gitlab/external_authorization/access_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization/cache_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization/client_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization/logger_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization/response_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization_spec.rb2
-rw-r--r--spec/lib/gitlab/fake_application_settings_spec.rb2
-rw-r--r--spec/lib/gitlab/favicon_spec.rb2
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb2
-rw-r--r--spec/lib/gitlab/file_finder_spec.rb6
-rw-r--r--spec/lib/gitlab/fogbugz_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb18
-rw-r--r--spec/lib/gitlab/git_access_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/git_ref_validator_spec.rb2
-rw-r--r--spec/lib/gitlab/git_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/blob_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/health_check_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb8
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/storage_settings_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/util_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/wiki_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/bulk_importing_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/caching_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/issues_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/labels_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/note_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/notes_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/issuable_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/label_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/markdown_text_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/milestone_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/page_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/parallel_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_note_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/issue_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/note_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/to_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/user_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/sequential_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/user_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import_spec.rb2
-rw-r--r--spec/lib/gitlab/gl_repository_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb10
-rw-r--r--spec/lib/gitlab/gpg_spec.rb98
-rw-r--r--spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb51
-rw-r--r--spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb26
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/conditions/not_null_condition_spec.rb56
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/conditions/null_condition_spec.rb42
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb281
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb127
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/order_info_spec.rb81
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/query_builder_spec.rb108
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb117
-rw-r--r--spec/lib/gitlab/graphql/loaders/pipeline_for_sha_loader_spec.rb20
-rw-r--r--spec/lib/gitlab/group_search_results_spec.rb2
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/master_check_spec.rb49
-rw-r--r--spec/lib/gitlab/highlight_spec.rb2
-rw-r--r--spec/lib/gitlab/http_io_spec.rb2
-rw-r--r--spec/lib/gitlab/http_spec.rb2
-rw-r--r--spec/lib/gitlab/i18n_spec.rb2
-rw-r--r--spec/lib/gitlab/identifier_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml13
-rw-r--r--spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/fork_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group_project_object_builder_spec.rb36
-rw-r--r--spec/lib/gitlab/import_export/group_tree_saver_spec.rb180
-rw-r--r--spec/lib/gitlab/import_export/import_export_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb114
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/relation_rename_service_spec.rb17
-rw-r--r--spec/lib/gitlab/import_export/relation_tree_saver_spec.rb42
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml13
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/shared_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb2
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb2
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb2
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb37
-rw-r--r--spec/lib/gitlab/issuable_metadata_spec.rb2
-rw-r--r--spec/lib/gitlab/issuable_sorter_spec.rb2
-rw-r--r--spec/lib/gitlab/issuables_count_for_state_spec.rb2
-rw-r--r--spec/lib/gitlab/job_waiter_spec.rb2
-rw-r--r--spec/lib/gitlab/json_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/config_maps/aws_node_auth_spec.rb33
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb27
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb2
-rw-r--r--spec/lib/gitlab/language_detection_spec.rb2
-rw-r--r--spec/lib/gitlab/lazy_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/finder_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/dashboard/processor_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb11
-rw-r--r--spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb106
-rw-r--r--spec/lib/gitlab/metrics/dashboard/url_spec.rb85
-rw-r--r--spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb76
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/pagination/offset_pagination_spec.rb215
-rw-r--r--spec/lib/gitlab/phabricator_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb167
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb14
-rw-r--r--spec/lib/gitlab/project_template_spec.rb3
-rw-r--r--spec/lib/gitlab/prometheus/internal_spec.rb108
-rw-r--r--spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb13
-rw-r--r--spec/lib/gitlab/regex_spec.rb19
-rw-r--r--spec/lib/gitlab/search/found_blob_spec.rb24
-rw-r--r--spec/lib/gitlab/shell_spec.rb76
-rw-r--r--spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb113
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb5
-rw-r--r--spec/lib/gitlab/slash_commands/issue_comment_spec.rb117
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/access_spec.rb10
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb37
-rw-r--r--spec/lib/gitlab/sourcegraph_spec.rb66
-rw-r--r--spec/lib/gitlab/sql/recursive_cte_spec.rb2
-rw-r--r--spec/lib/gitlab/sql/union_spec.rb4
-rw-r--r--spec/lib/gitlab/tracking_spec.rb19
-rw-r--r--spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb34
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb54
-rw-r--r--spec/lib/gitlab/user_access_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/deep_size_spec.rb6
-rw-r--r--spec/lib/gitlab/visibility_level_checker_spec.rb2
-rw-r--r--spec/lib/gitlab/wiki_file_finder_spec.rb2
-rw-r--r--spec/lib/gitlab_spec.rb42
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb3
-rw-r--r--spec/lib/grafana/client_spec.rb26
-rw-r--r--spec/lib/omni_auth/strategies/saml_spec.rb2
-rw-r--r--spec/lib/prometheus/pid_provider_spec.rb12
-rw-r--r--spec/lib/quality/helm_client_spec.rb20
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb52
-rw-r--r--spec/lib/sentry/client_spec.rb9
234 files changed, 5387 insertions, 1523 deletions
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index b57adb46385..040ff1a8ebe 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -3,399 +3,20 @@
require 'spec_helper'
describe API::Helpers::Pagination do
- let(:resource) { Project.all }
- let(:custom_port) { 8080 }
- let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:#{custom_port}/api/v4/projects" }
+ subject { Class.new.include(described_class).new }
- before do
- stub_config_setting(port: custom_port)
- end
-
- subject do
- Class.new.include(described_class).new
- end
-
- describe '#paginate (keyset pagination)' do
- let(:value) { spy('return value') }
- let(:base_query) do
- {
- pagination: 'keyset',
- foo: 'bar',
- bar: 'baz'
- }
- end
- let(:query) { base_query }
-
- before do
- allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(query)
- allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
- end
-
- context 'when resource can be paginated' do
- let!(:projects) do
- [
- create(:project, name: 'One'),
- create(:project, name: 'Two'),
- create(:project, name: 'Three')
- ].sort_by { |e| -e.id } # sort by id desc (this is the default sort order for the API)
- end
-
- describe 'first page' do
- let(:query) { base_query.merge(per_page: 2) }
-
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 2
- end
-
- it 'returns the first two records (by id desc)' do
- expect(subject.paginate(resource)).to eq(projects[0..1])
- end
-
- it 'adds appropriate headers' do
- expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
-
- expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="next"')
- end
-
- subject.paginate(resource)
- end
- end
-
- describe 'second page' do
- let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[1].id) }
-
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 1
- end
-
- it 'returns the third record' do
- expect(subject.paginate(resource)).to eq(projects[2..2])
- end
-
- it 'adds appropriate headers' do
- expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
-
- expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="next"')
- end
-
- subject.paginate(resource)
- end
- end
-
- describe 'third page' do
- let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[2].id) }
-
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 0
- end
-
- it 'adds appropriate headers' do
- expect_header('X-Per-Page', '2')
- expect_no_header('X-Next-Page')
- expect(subject).not_to receive(:header).with('Link')
-
- subject.paginate(resource)
- end
- end
-
- context 'if order' do
- context 'is not present' do
- let(:query) { base_query.merge(per_page: 2) }
-
- it 'is not present it adds default order(:id) desc' do
- resource.order_values = []
-
- paginated_relation = subject.paginate(resource)
-
- expect(resource.order_values).to be_empty
- expect(paginated_relation.order_values).to be_present
- expect(paginated_relation.order_values.size).to eq(1)
- expect(paginated_relation.order_values.first).to be_descending
- expect(paginated_relation.order_values.first.expr.name).to eq 'id'
- end
- end
-
- context 'is present' do
- let(:resource) { Project.all.order(name: :desc) }
- let!(:projects) do
- [
- create(:project, name: 'One'),
- create(:project, name: 'Two'),
- create(:project, name: 'Three'),
- create(:project, name: 'Three'), # Note the duplicate name
- create(:project, name: 'Four'),
- create(:project, name: 'Five'),
- create(:project, name: 'Six')
- ]
-
- # if we sort this by name descending, id descending, this yields:
- # {
- # 2 => "Two",
- # 4 => "Three",
- # 3 => "Three",
- # 7 => "Six",
- # 1 => "One",
- # 5 => "Four",
- # 6 => "Five"
- # }
- #
- # (key is the id)
- end
-
- it 'also orders by primary key' do
- paginated_relation = subject.paginate(resource)
-
- expect(paginated_relation.order_values).to be_present
- expect(paginated_relation.order_values.size).to eq(2)
- expect(paginated_relation.order_values.first).to be_descending
- expect(paginated_relation.order_values.first.expr.name).to eq 'name'
- expect(paginated_relation.order_values.second).to be_descending
- expect(paginated_relation.order_values.second.expr.name).to eq 'id'
- end
-
- it 'returns the right records (first page)' do
- result = subject.paginate(resource)
-
- expect(result.first).to eq(projects[1])
- expect(result.second).to eq(projects[3])
- end
-
- describe 'second page' do
- let(:query) { base_query.merge(ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2) }
-
- it 'returns the right records (second page)' do
- result = subject.paginate(resource)
-
- expect(result.first).to eq(projects[2])
- expect(result.second).to eq(projects[6])
- end
-
- it 'returns the right link to the next page' do
- expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{incoming_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
- expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="next"')
- end
-
- subject.paginate(resource)
- end
- end
-
- describe 'third page' do
- let(:query) { base_query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name, per_page: 5) }
-
- it 'returns the right records (third page), note increased per_page' do
- result = subject.paginate(resource)
-
- expect(result.size).to eq(3)
- expect(result.first).to eq(projects[0])
- expect(result.second).to eq(projects[4])
- expect(result.last).to eq(projects[5])
- end
- end
- end
- end
- end
- end
-
- describe '#paginate (default offset-based pagination)' do
- let(:value) { spy('return value') }
- let(:base_query) { { foo: 'bar', bar: 'baz' } }
- let(:query) { base_query }
-
- before do
- allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(query)
- allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
- end
-
- context 'when resource can be paginated' do
- before do
- create_list(:project, 3)
- end
-
- describe 'first page' do
- shared_examples 'response with pagination headers' do
- it 'adds appropriate headers' do
- expect_header('X-Total', '3')
- expect_header('X-Total-Pages', '2')
- expect_header('X-Per-Page', '2')
- expect_header('X-Page', '1')
- expect_header('X-Next-Page', '2')
- expect_header('X-Prev-Page', '')
-
- expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
- expect(val).not_to include('rel="prev"')
- end
-
- subject.paginate(resource)
- end
- end
-
- shared_examples 'paginated response' do
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 2
- end
-
- it 'executes only one SELECT COUNT query' do
- expect { subject.paginate(resource) }.to make_queries_matching(/SELECT COUNT/, 1)
- end
- end
-
- let(:query) { base_query.merge(page: 1, per_page: 2) }
-
- context 'when the api_kaminari_count_with_limit feature flag is unset' do
- it_behaves_like 'paginated response'
- it_behaves_like 'response with pagination headers'
- end
-
- context 'when the api_kaminari_count_with_limit feature flag is disabled' do
- before do
- stub_feature_flags(api_kaminari_count_with_limit: false)
- end
-
- it_behaves_like 'paginated response'
- it_behaves_like 'response with pagination headers'
- end
-
- context 'when the api_kaminari_count_with_limit feature flag is enabled' do
- before do
- stub_feature_flags(api_kaminari_count_with_limit: true)
- end
-
- context 'when resources count is less than MAX_COUNT_LIMIT' do
- before do
- stub_const("::Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT", 4)
- end
-
- it_behaves_like 'paginated response'
- it_behaves_like 'response with pagination headers'
- end
-
- context 'when resources count is more than MAX_COUNT_LIMIT' do
- before do
- stub_const("::Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT", 2)
- end
-
- it_behaves_like 'paginated response'
-
- it 'does not return the X-Total and X-Total-Pages headers' do
- expect_no_header('X-Total')
- expect_no_header('X-Total-Pages')
- expect_header('X-Per-Page', '2')
- expect_header('X-Page', '1')
- expect_header('X-Next-Page', '2')
- expect_header('X-Prev-Page', '')
+ describe '#paginate' do
+ let(:relation) { double("relation") }
+ let(:offset_pagination) { double("offset pagination") }
+ let(:expected_result) { double("result") }
- expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
- expect(val).not_to include('rel="last"')
- expect(val).not_to include('rel="prev"')
- end
+ it 'delegates to OffsetPagination' do
+ expect(::Gitlab::Pagination::OffsetPagination).to receive(:new).with(subject).and_return(offset_pagination)
+ expect(offset_pagination).to receive(:paginate).with(relation).and_return(expected_result)
- subject.paginate(resource)
- end
- end
- end
- end
+ result = subject.paginate(relation)
- describe 'second page' do
- let(:query) { base_query.merge(page: 2, per_page: 2) }
-
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 1
- end
-
- it 'adds appropriate headers' do
- expect_header('X-Total', '3')
- expect_header('X-Total-Pages', '2')
- expect_header('X-Per-Page', '2')
- expect_header('X-Page', '2')
- expect_header('X-Next-Page', '')
- expect_header('X-Prev-Page', '1')
-
- expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
- expect(val).not_to include('rel="next"')
- end
-
- subject.paginate(resource)
- end
- end
-
- context 'if order' do
- it 'is not present it adds default order(:id) if no order is present' do
- resource.order_values = []
-
- paginated_relation = subject.paginate(resource)
-
- expect(resource.order_values).to be_empty
- expect(paginated_relation.order_values).to be_present
- expect(paginated_relation.order_values.first).to be_ascending
- expect(paginated_relation.order_values.first.expr.name).to eq 'id'
- end
-
- it 'is present it does not add anything' do
- paginated_relation = subject.paginate(resource.order(created_at: :desc))
-
- expect(paginated_relation.order_values).to be_present
- expect(paginated_relation.order_values.first).to be_descending
- expect(paginated_relation.order_values.first.expr.name).to eq 'created_at'
- end
- end
+ expect(result).to eq(expected_result)
end
-
- context 'when resource empty' do
- describe 'first page' do
- let(:query) { base_query.merge(page: 1, per_page: 2) }
-
- it 'returns appropriate amount of resources' do
- expect(subject.paginate(resource).count).to eq 0
- end
-
- it 'adds appropriate headers' do
- expect_header('X-Total', '0')
- expect_header('X-Total-Pages', '1')
- expect_header('X-Per-Page', '2')
- expect_header('X-Page', '1')
- expect_header('X-Next-Page', '')
- expect_header('X-Prev-Page', '')
-
- expect_header('Link', anything) do |_key, val|
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
- expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
- expect(val).not_to include('rel="prev"')
- expect(val).not_to include('rel="next"')
- expect(val).not_to include('page=0')
- end
-
- subject.paginate(resource)
- end
- end
- end
- end
-
- def expect_header(*args, &block)
- expect(subject).to receive(:header).with(*args, &block)
- end
-
- def expect_no_header(*args, &block)
- expect(subject).not_to receive(:header).with(*args)
- end
-
- def expect_message(method)
- expect(subject).to receive(method)
- .at_least(:once).and_return(value)
end
end
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 0624c25e734..81c4563feb6 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -174,4 +174,18 @@ describe API::Helpers do
end
end
end
+
+ describe '#track_event' do
+ it "creates a gitlab tracking event" do
+ expect(Gitlab::Tracking).to receive(:event).with('foo', 'my_event', {})
+
+ subject.track_event('my_event', category: 'foo')
+ end
+
+ it "logs an exception" do
+ expect(Rails.logger).to receive(:warn).with(/Tracking event failed/)
+
+ subject.track_event('my_event', category: nil)
+ end
+ end
end
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index bf827fb3914..5f120f258cd 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -70,7 +70,7 @@ describe Backup::Repository do
end
context 'restoring object pools' do
- it 'schedules restoring of the pool' do
+ it 'schedules restoring of the pool', :sidekiq_might_not_need_inline do
pool_repository = create(:pool_repository, :failed)
pool_repository.delete_object_pool
diff --git a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
index 0c4ccbf28f4..ff2346fe1ba 100644
--- a/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
+++ b/spec/lib/banzai/filter/asset_proxy_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Banzai::Filter::AssetProxyFilter do
diff --git a/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
new file mode 100644
index 00000000000..fd6f8816b63
--- /dev/null
+++ b/spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::InlineGrafanaMetricsFilter do
+ include FilterSpecHelper
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:grafana_integration) { create(:grafana_integration, project: project) }
+
+ let(:input) { %(<a href="#{url}">example</a>) }
+ let(:doc) { filter(input) }
+
+ let(:url) { grafana_integration.grafana_url + dashboard_path }
+ let(:dashboard_path) do
+ '/d/XDaNK6amz/gitlab-omnibus-redis' \
+ '?from=1570397739557&to=1570484139557' \
+ '&var-instance=All&panelId=14'
+ end
+
+ it 'appends a metrics charts placeholder with dashboard url after metrics links' do
+ node = doc.at_css('.js-render-metrics')
+ expect(node).to be_present
+
+ dashboard_url = urls.project_grafana_api_metrics_dashboard_url(
+ project,
+ embedded: true,
+ grafana_url: url,
+ start: "2019-10-06T21:35:39Z",
+ end: "2019-10-07T21:35:39Z"
+ )
+
+ expect(node.attribute('data-dashboard-url').to_s).to eq(dashboard_url)
+ end
+
+ context 'when the dashboard link is part of a paragraph' do
+ let(:paragraph) { %(This is an <a href="#{url}">example</a> of metrics.) }
+ let(:input) { %(<p>#{paragraph}</p>) }
+
+ it 'appends the charts placeholder after the enclosing paragraph' do
+ expect(unescape(doc.at_css('p').to_s)).to include(paragraph)
+ expect(doc.at_css('.js-render-metrics')).to be_present
+ end
+ end
+
+ context 'when grafana is not configured' do
+ before do
+ allow(project).to receive(:grafana_integration).and_return(nil)
+ end
+
+ it 'leaves the markdown unchanged' do
+ expect(unescape(doc.to_s)).to eq(input)
+ end
+ end
+
+ context 'when parameters are missing' do
+ let(:dashboard_path) { '/d/XDaNK6amz/gitlab-omnibus-redis' }
+
+ it 'leaves the markdown unchanged' do
+ expect(unescape(doc.to_s)).to eq(input)
+ end
+ end
+
+ private
+
+ # Nokogiri escapes the URLs, but we don't care about that
+ # distinction for the purposes of this filter
+ def unescape(html)
+ CGI.unescapeHTML(html)
+ end
+end
diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
index a99cd7d6076..745b9133529 100644
--- a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
@@ -18,30 +18,48 @@ describe Banzai::Filter::InlineMetricsRedactorFilter do
end
context 'with a metrics charts placeholder' do
- let(:input) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) }
+ shared_examples_for 'a supported metrics dashboard url' do
+ context 'no user is logged in' do
+ it 'redacts the placeholder' do
+ expect(doc.to_s).to be_empty
+ end
+ end
- context 'no user is logged in' do
- it 'redacts the placeholder' do
- expect(doc.to_s).to be_empty
+ context 'the user does not have permission do see charts' do
+ let(:doc) { filter(input, current_user: build(:user)) }
+
+ it 'redacts the placeholder' do
+ expect(doc.to_s).to be_empty
+ end
end
- end
- context 'the user does not have permission do see charts' do
- let(:doc) { filter(input, current_user: build(:user)) }
+ context 'the user has requisite permissions' do
+ let(:user) { create(:user) }
+ let(:doc) { filter(input, current_user: user) }
- it 'redacts the placeholder' do
- expect(doc.to_s).to be_empty
+ it 'leaves the placeholder' do
+ project.add_maintainer(user)
+
+ expect(doc.to_s).to eq input
+ end
end
end
- context 'the user has requisite permissions' do
- let(:user) { create(:user) }
- let(:doc) { filter(input, current_user: user) }
+ let(:input) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) }
- it 'leaves the placeholder' do
- project.add_maintainer(user)
+ it_behaves_like 'a supported metrics dashboard url'
+
+ context 'for a grafana dashboard' do
+ let(:url) { urls.project_grafana_api_metrics_dashboard_url(project, embedded: true) }
+
+ it_behaves_like 'a supported metrics dashboard url'
+ end
- expect(doc.to_s).to eq input
+ context 'for an internal non-dashboard url' do
+ let(:url) { urls.project_url(project) }
+
+ it 'leaves the placeholder' do
+ expect(doc.to_s).to be_empty
end
end
end
diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb
index a395b021f32..c324c36fe4d 100644
--- a/spec/lib/banzai/filter/video_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/video_link_filter_spec.rb
@@ -32,7 +32,7 @@ describe Banzai::Filter::VideoLinkFilter do
expect(video.name).to eq 'video'
expect(video['src']).to eq src
- expect(video['width']).to eq "100%"
+ expect(video['width']).to eq "400"
expect(paragraph.name).to eq 'p'
diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb
index 70b51b8efec..6a9df0e5099 100644
--- a/spec/lib/bitbucket/representation/pull_request_spec.rb
+++ b/spec/lib/bitbucket/representation/pull_request_spec.rb
@@ -20,6 +20,7 @@ describe Bitbucket::Representation::PullRequest do
describe '#state' do
it { expect(described_class.new({ 'state' => 'MERGED' }).state).to eq('merged') }
it { expect(described_class.new({ 'state' => 'DECLINED' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'SUPERSEDED' }).state).to eq('closed') }
it { expect(described_class.new({}).state).to eq('opened') }
end
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index 3782c30e88a..a493b96b1e4 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -99,8 +99,8 @@ describe ContainerRegistry::Client do
stub_upload('path', 'content', 'sha256:123', 400)
end
- it 'returns nil' do
- expect(subject).to be nil
+ it 'returns a failure' do
+ expect(subject).not_to be_success
end
end
end
@@ -125,6 +125,14 @@ describe ContainerRegistry::Client do
expect(subject).to eq(result_manifest)
end
+
+ context 'when upload fails' do
+ before do
+ stub_upload('path', "{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3', 500)
+ end
+
+ it { is_expected.to be nil }
+ end
end
describe '#put_tag' do
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 7c65525b8dc..415a6e62374 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -58,7 +58,7 @@ module Gitlab
},
'image with onerror' => {
input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]',
- output: "<div>\n<p><span><img src=\"https://localhost.com/image.png\" alt='Alt text\" onerror=\"alert(7)'></span></p>\n</div>"
+ output: "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt='Alt text\" onerror=\"alert(7)' class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
},
'fenced code with inline script' => {
input: '```mypre"><script>alert(3)</script>',
@@ -73,6 +73,20 @@ module Gitlab
end
end
+ context "images" do
+ it "does lazy load and link image" do
+ input = 'image:https://localhost.com/image.png[]'
+ output = "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
+ expect(render(input, context)).to include(output)
+ end
+
+ it "does not automatically link image if link is explicitly defined" do
+ input = 'image:https://localhost.com/image.png[link=https://gitlab.com]'
+ output = "<div>\n<p><span><a href=\"https://gitlab.com\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
+ expect(render(input, context)).to include(output)
+ end
+ end
+
context 'with admonition' do
it 'preserves classes' do
input = <<~ADOC
@@ -107,7 +121,7 @@ module Gitlab
ADOC
output = <<~HTML
- <h2>Title</h2>
+ <h2>Title</h2>
HTML
expect(render(input, context)).to include(output.strip)
@@ -149,15 +163,15 @@ module Gitlab
ADOC
output = <<~HTML
- <div>
- <p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
- </div>
- <div>
- <hr>
- <div id="_footnotedef_1">
- <a href="#_footnoteref_1">1</a>. This is the text of the footnote.
- </div>
- </div>
+ <div>
+ <p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
+ </div>
+ <div>
+ <hr>
+ <div id="_footnotedef_1">
+ <a href="#_footnoteref_1">1</a>. This is the text of the footnote.
+ </div>
+ </div>
HTML
expect(render(input, context)).to include(output.strip)
@@ -183,34 +197,34 @@ module Gitlab
ADOC
output = <<~HTML
- <h1>Title</h1>
- <div>
- <h2 id="user-content-first-section">
- <a class="anchor" href="#user-content-first-section"></a>First section</h2>
- <div>
- <div>
- <p>This is the first section.</p>
- </div>
- </div>
- </div>
- <div>
- <h2 id="user-content-second-section">
- <a class="anchor" href="#user-content-second-section"></a>Second section</h2>
- <div>
- <div>
- <p>This is the second section.</p>
- </div>
- </div>
- </div>
- <div>
- <h2 id="user-content-thunder">
- <a class="anchor" href="#user-content-thunder"></a>Thunder โšก !</h2>
- <div>
- <div>
- <p>This is the third section.</p>
- </div>
- </div>
- </div>
+ <h1>Title</h1>
+ <div>
+ <h2 id="user-content-first-section">
+ <a class="anchor" href="#user-content-first-section"></a>First section</h2>
+ <div>
+ <div>
+ <p>This is the first section.</p>
+ </div>
+ </div>
+ </div>
+ <div>
+ <h2 id="user-content-second-section">
+ <a class="anchor" href="#user-content-second-section"></a>Second section</h2>
+ <div>
+ <div>
+ <p>This is the second section.</p>
+ </div>
+ </div>
+ </div>
+ <div>
+ <h2 id="user-content-thunder">
+ <a class="anchor" href="#user-content-thunder"></a>Thunder โšก !</h2>
+ <div>
+ <div>
+ <p>This is the third section.</p>
+ </div>
+ </div>
+ </div>
HTML
expect(render(input, context)).to include(output.strip)
diff --git a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
index 05541972f87..adb8e138ca7 100644
--- a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Auth::LDAP::AuthHash do
@@ -91,7 +93,7 @@ describe Gitlab::Auth::LDAP::AuthHash do
let(:given_uid) { 'uid=John Smith,ou=People,dc=example,dc=com' }
before do
- raw_info[:uid] = ['JOHN']
+ raw_info[:uid] = [+'JOHN']
end
it 'enabled the username attribute is lower cased' do
diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb
index 577dfe51949..e4a90d4018d 100644
--- a/spec/lib/gitlab/auth/ldap/config_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/config_spec.rb
@@ -535,4 +535,23 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
end
end
end
+
+ describe 'sign_in_enabled?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:enabled, :prevent_ldap_sign_in, :result) do
+ true | false | true
+ 'true' | false | true
+ true | true | false
+ false | nil | false
+ end
+
+ with_them do
+ it do
+ stub_ldap_setting(enabled: enabled, prevent_ldap_sign_in: prevent_ldap_sign_in)
+
+ expect(described_class.sign_in_enabled?).to eq(result)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/auth/ldap/person_spec.rb b/spec/lib/gitlab/auth/ldap/person_spec.rb
index 1527fe60fb9..985732e69f9 100644
--- a/spec/lib/gitlab/auth/ldap/person_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/person_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Auth::LDAP::Person do
@@ -135,7 +137,7 @@ describe Gitlab::Auth::LDAP::Person do
let(:username_attribute) { 'uid' }
before do
- entry[username_attribute] = 'JOHN'
+ entry[username_attribute] = +'JOHN'
@person = described_class.new(entry, 'ldapmain')
end
diff --git a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
index c1eaf1d3433..f2de73d5aea 100644
--- a/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
+++ b/spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb
@@ -91,15 +91,26 @@ describe Gitlab::BackgroundMigration::LegacyUploadMover do
end
end
- context 'when no model found for the upload' do
+ context 'when no note found for the upload' do
before do
- legacy_upload.model = nil
+ legacy_upload.model_id = nil
+ legacy_upload.model_type = 'Note'
expect_error_log
end
it_behaves_like 'legacy upload deletion'
end
+ context 'when upload does not belong to a note' do
+ before do
+ legacy_upload.model = create(:appearance)
+ end
+
+ it 'does not remove the upload' do
+ expect { described_class.new(legacy_upload).execute }.not_to change { Upload.count }
+ end
+ end
+
context 'when the upload move fails' do
before do
expect(FileUploader).to receive(:copy_to).and_raise('failed')
diff --git a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
index cabca3dbef9..85187d039c1 100644
--- a/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
+++ b/spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb
@@ -35,6 +35,8 @@ describe Gitlab::BackgroundMigration::LegacyUploadsMigrator do
let!(:legacy_upload_no_file) { create_upload(note2, false) }
let!(:legacy_upload_legacy_project) { create_upload(note_legacy) }
+ let!(:appearance) { create(:appearance, :with_logo) }
+
let(:start_id) { 1 }
let(:end_id) { 10000 }
@@ -52,12 +54,18 @@ describe Gitlab::BackgroundMigration::LegacyUploadsMigrator do
expect(File.exist?(legacy_upload_legacy_project.absolute_path)).to be_falsey
end
- it 'removes all AttachmentUploader records' do
- expect { subject }.to change { Upload.where(uploader: 'AttachmentUploader').count }.from(3).to(0)
+ it 'removes all Note AttachmentUploader records' do
+ expect { subject }.to change { Upload.where(uploader: 'AttachmentUploader').count }.from(4).to(1)
end
it 'creates new uploads for successfully migrated records' do
expect { subject }.to change { Upload.where(uploader: 'FileUploader').count }.from(0).to(2)
end
+
+ it 'does not remove appearance uploads' do
+ subject
+
+ expect(appearance.logo.file).to exist
+ end
end
# rubocop: enable RSpec/FactoriesInMigrationSpecs
diff --git a/spec/lib/gitlab/background_migration/schedule_calculate_wiki_sizes_spec.rb b/spec/lib/gitlab/background_migration/schedule_calculate_wiki_sizes_spec.rb
index f877e8cc1b8..399db4ac259 100644
--- a/spec/lib/gitlab/background_migration/schedule_calculate_wiki_sizes_spec.rb
+++ b/spec/lib/gitlab/background_migration/schedule_calculate_wiki_sizes_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleCalculateWikiSizes, :migration, :sidekiq do
end
end
- it 'calculates missing wiki sizes' do
+ it 'calculates missing wiki sizes', :sidekiq_might_not_need_inline do
expect(project_statistics.find_by(id: 2).wiki_size).to be_nil
expect(project_statistics.find_by(id: 3).wiki_size).to be_nil
diff --git a/spec/lib/gitlab/badge/pipeline/status_spec.rb b/spec/lib/gitlab/badge/pipeline/status_spec.rb
index 684c6829879..ab8d1f0ec5b 100644
--- a/spec/lib/gitlab/badge/pipeline/status_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/status_spec.rb
@@ -26,7 +26,7 @@ describe Gitlab::Badge::Pipeline::Status do
end
end
- context 'pipeline exists' do
+ context 'pipeline exists', :sidekiq_might_not_need_inline do
let!(:pipeline) { create_pipeline(project, sha, branch) }
context 'pipeline success' do
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 2fb9f1a0a08..ddb1d3cea21 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -90,7 +90,7 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
hook_path = File.join(repo_path, 'hooks')
expect(gitlab_shell.repository_exists?(project.repository_storage, repo_path)).to be(true)
- expect(gitlab_shell.exists?(project.repository_storage, hook_path)).to be(true)
+ expect(TestEnv.storage_dir_exists?(project.repository_storage, hook_path)).to be(true)
end
context 'hashed storage enabled' do
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 7f7a285c453..b0d07c6e0b0 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -158,6 +158,7 @@ describe Gitlab::BitbucketImport::Importer do
expect { subject.execute }.to change { MergeRequest.count }.by(1)
merge_request = MergeRequest.first
+ expect(merge_request.state).to eq('merged')
expect(merge_request.notes.count).to eq(2)
expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
diff --git a/spec/lib/gitlab/checks/lfs_integrity_spec.rb b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
index 88e8f5d74d1..505f117034e 100644
--- a/spec/lib/gitlab/checks/lfs_integrity_spec.rb
+++ b/spec/lib/gitlab/checks/lfs_integrity_spec.rb
@@ -58,7 +58,7 @@ describe Gitlab::Checks::LfsIntegrity do
end
end
- context 'for forked project' do
+ context 'for forked project', :sidekiq_might_not_need_inline do
let(:parent_project) { create(:project, :repository) }
let(:project) { fork_project(parent_project, nil, repository: true) }
diff --git a/spec/lib/gitlab/ci/ansi2json/style_spec.rb b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
index 88a0ca35859..5110c215415 100644
--- a/spec/lib/gitlab/ci/ansi2json/style_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
@@ -143,6 +143,7 @@ describe Gitlab::Ci::Ansi2json::Style do
[[], %w[106], 'term-bg-l-cyan', 'sets bg color light cyan'],
[[], %w[107], 'term-bg-l-white', 'sets bg color light white'],
# reset
+ [%w[1], %w[], '', 'resets style from format bold'],
[%w[1], %w[0], '', 'resets style from format bold'],
[%w[1 3], %w[0], '', 'resets style from format bold and italic'],
[%w[1 3 term-fg-l-red term-bg-yellow], %w[0], '', 'resets all formats and colors'],
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb
index 3c6bc46436b..124379fa321 100644
--- a/spec/lib/gitlab/ci/ansi2json_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json_spec.rb
@@ -12,11 +12,26 @@ describe Gitlab::Ci::Ansi2json do
])
end
- it 'adds new line in a separate element' do
- expect(convert_json("Hello\nworld")).to eq([
- { offset: 0, content: [{ text: 'Hello' }] },
- { offset: 6, content: [{ text: 'world' }] }
- ])
+ context 'new lines' do
+ it 'adds new line when encountering \n' do
+ expect(convert_json("Hello\nworld")).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 6, content: [{ text: 'world' }] }
+ ])
+ end
+
+ it 'adds new line when encountering \r\n' do
+ expect(convert_json("Hello\r\nworld")).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 7, content: [{ text: 'world' }] }
+ ])
+ end
+
+ it 'replace the current line when encountering \r' do
+ expect(convert_json("Hello\rworld")).to eq([
+ { offset: 0, content: [{ text: 'world' }] }
+ ])
+ end
end
it 'recognizes color changing ANSI sequences' do
@@ -113,10 +128,6 @@ describe Gitlab::Ci::Ansi2json do
content: [],
section_duration: '01:03',
section: 'prepare-script'
- },
- {
- offset: 63,
- content: []
}
])
end
@@ -134,10 +145,6 @@ describe Gitlab::Ci::Ansi2json do
content: [],
section: 'prepare-script',
section_duration: '01:03'
- },
- {
- offset: 56,
- content: []
}
])
end
@@ -157,7 +164,7 @@ describe Gitlab::Ci::Ansi2json do
section_duration: '01:03'
},
{
- offset: 49,
+ offset: 91,
content: [{ text: 'world' }]
}
])
@@ -198,7 +205,7 @@ describe Gitlab::Ci::Ansi2json do
expect(convert_json("#{section_start}hello")).to eq([
{
offset: 0,
- content: [{ text: "#{section_start.gsub("\033[0K", '')}hello" }]
+ content: [{ text: 'hello' }]
}
])
end
@@ -211,30 +218,26 @@ describe Gitlab::Ci::Ansi2json do
expect(convert_json("#{section_start}hello")).to eq([
{
offset: 0,
- content: [{ text: "#{section_start.gsub("\033[0K", '').gsub('<', '&lt;')}hello" }]
+ content: [{ text: 'hello' }]
}
])
end
end
- it 'prevents XSS injection' do
- trace = "#{section_start}section_end:1:2<script>alert('XSS Hack!');</script>#{section_end}"
+ it 'prints HTML tags as is' do
+ trace = "#{section_start}section_end:1:2<div>hello</div>#{section_end}"
expect(convert_json(trace)).to eq([
{
offset: 0,
- content: [{ text: "section_end:1:2&lt;script>alert('XSS Hack!');&lt;/script>" }],
+ content: [{ text: "section_end:1:2<div>hello</div>" }],
section: 'prepare-script',
section_header: true
},
{
- offset: 95,
+ offset: 75,
content: [],
section: 'prepare-script',
section_duration: '01:03'
- },
- {
- offset: 95,
- content: []
}
])
end
@@ -274,7 +277,7 @@ describe Gitlab::Ci::Ansi2json do
section_duration: '00:02'
},
{
- offset: 106,
+ offset: 155,
content: [{ text: 'baz' }],
section: 'prepare-script'
},
@@ -285,7 +288,7 @@ describe Gitlab::Ci::Ansi2json do
section_duration: '01:03'
},
{
- offset: 158,
+ offset: 200,
content: [{ text: 'world' }]
}
])
@@ -318,14 +321,10 @@ describe Gitlab::Ci::Ansi2json do
section_duration: '00:02'
},
{
- offset: 115,
+ offset: 164,
content: [],
section: 'prepare-script',
section_duration: '01:03'
- },
- {
- offset: 164,
- content: []
}
])
end
@@ -380,7 +379,7 @@ describe Gitlab::Ci::Ansi2json do
]
end
- it 'returns the full line' do
+ it 'returns the line since last partially processed line' do
expect(pass2.lines).to eq(lines)
expect(pass2.append).to be_truthy
end
@@ -399,7 +398,7 @@ describe Gitlab::Ci::Ansi2json do
]
end
- it 'returns the full line' do
+ it 'returns the line since last partially processed line' do
expect(pass2.lines).to eq(lines)
expect(pass2.append).to be_falsey
end
@@ -416,7 +415,7 @@ describe Gitlab::Ci::Ansi2json do
]
end
- it 'returns the full line' do
+ it 'returns a blank line and the next line' do
expect(pass2.lines).to eq(lines)
expect(pass2.append).to be_falsey
end
@@ -502,10 +501,6 @@ describe Gitlab::Ci::Ansi2json do
content: [],
section: 'prepare-script',
section_duration: '01:03'
- },
- {
- offset: 77,
- content: []
}
]
end
diff --git a/spec/lib/gitlab/ci/build/context/build_spec.rb b/spec/lib/gitlab/ci/build/context/build_spec.rb
new file mode 100644
index 00000000000..3adde213f59
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/context/build_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Context::Build do
+ let(:pipeline) { create(:ci_pipeline) }
+ let(:seed_attributes) { { 'name' => 'some-job' } }
+
+ let(:context) { described_class.new(pipeline, seed_attributes) }
+
+ describe '#variables' do
+ subject { context.variables }
+
+ it { is_expected.to include('CI_COMMIT_REF_NAME' => 'master') }
+ it { is_expected.to include('CI_PIPELINE_IID' => pipeline.iid.to_s) }
+ it { is_expected.to include('CI_PROJECT_PATH' => pipeline.project.full_path) }
+ it { is_expected.to include('CI_JOB_NAME' => 'some-job') }
+ it { is_expected.to include('CI_BUILD_REF_NAME' => 'master') }
+
+ context 'without passed build-specific attributes' do
+ let(:context) { described_class.new(pipeline) }
+
+ it { is_expected.to include('CI_JOB_NAME' => nil) }
+ it { is_expected.to include('CI_BUILD_REF_NAME' => 'master') }
+ it { is_expected.to include('CI_PROJECT_PATH' => pipeline.project.full_path) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/context/global_spec.rb b/spec/lib/gitlab/ci/build/context/global_spec.rb
new file mode 100644
index 00000000000..6bc8f862779
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/context/global_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Context::Global do
+ let(:pipeline) { create(:ci_pipeline) }
+ let(:yaml_variables) { {} }
+
+ let(:context) { described_class.new(pipeline, yaml_variables: yaml_variables) }
+
+ describe '#variables' do
+ subject { context.variables }
+
+ it { is_expected.to include('CI_COMMIT_REF_NAME' => 'master') }
+ it { is_expected.to include('CI_PIPELINE_IID' => pipeline.iid.to_s) }
+ it { is_expected.to include('CI_PROJECT_PATH' => pipeline.project.full_path) }
+
+ it { is_expected.not_to have_key('CI_JOB_NAME') }
+ it { is_expected.not_to have_key('CI_BUILD_REF_NAME') }
+
+ context 'with passed yaml variables' do
+ let(:yaml_variables) { [{ key: 'SUPPORTED', value: 'parsed', public: true }] }
+
+ it { is_expected.to include('SUPPORTED' => 'parsed') }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 7140c14facb..66f2cb640b9 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
let(:seed) do
double('build seed',
to_resource: ci_build,
- scoped_variables_hash: ci_build.scoped_variables_hash
+ variables: ci_build.scoped_variables_hash
)
end
@@ -91,7 +91,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
let(:seed) do
double('bridge seed',
to_resource: bridge,
- scoped_variables_hash: ci_build.scoped_variables_hash
+ variables: ci_build.scoped_variables_hash
)
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule_spec.rb b/spec/lib/gitlab/ci/build/rules/rule_spec.rb
index 99852bd4228..04cdaa9d0ae 100644
--- a/spec/lib/gitlab/ci/build/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Build::Rules::Rule do
let(:seed) do
double('build seed',
to_resource: ci_build,
- scoped_variables_hash: ci_build.scoped_variables_hash
+ variables: ci_build.scoped_variables_hash
)
end
diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb
index d7793ebc806..1ebcc4f9414 100644
--- a/spec/lib/gitlab/ci/build/rules_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Build::Rules do
@@ -7,11 +9,11 @@ describe Gitlab::Ci::Build::Rules do
let(:seed) do
double('build seed',
to_resource: ci_build,
- scoped_variables_hash: ci_build.scoped_variables_hash
+ variables: ci_build.scoped_variables_hash
)
end
- let(:rules) { described_class.new(rule_list) }
+ let(:rules) { described_class.new(rule_list, default_when: 'on_success') }
describe '.new' do
let(:rules_ivar) { rules.instance_variable_get :@rule_list }
@@ -60,7 +62,7 @@ describe Gitlab::Ci::Build::Rules do
context 'with a specified default when:' do
let(:rule_list) { [{ if: '$VAR == null', when: 'always' }] }
- let(:rules) { described_class.new(rule_list, 'manual') }
+ let(:rules) { described_class.new(rule_list, default_when: 'manual') }
it 'sets @rule_list to an array of a single rule' do
expect(rules_ivar).to be_an(Array)
@@ -81,7 +83,7 @@ describe Gitlab::Ci::Build::Rules do
it { is_expected.to eq(described_class::Result.new('on_success')) }
context 'and when:manual set as the default' do
- let(:rules) { described_class.new(rule_list, 'manual') }
+ let(:rules) { described_class.new(rule_list, default_when: 'manual') }
it { is_expected.to eq(described_class::Result.new('manual')) }
end
@@ -93,7 +95,7 @@ describe Gitlab::Ci::Build::Rules do
it { is_expected.to eq(described_class::Result.new('never')) }
context 'and when:manual set as the default' do
- let(:rules) { described_class.new(rule_list, 'manual') }
+ let(:rules) { described_class.new(rule_list, default_when: 'manual') }
it { is_expected.to eq(described_class::Result.new('never')) }
end
@@ -157,7 +159,7 @@ describe Gitlab::Ci::Build::Rules do
it { is_expected.to eq(described_class::Result.new('never')) }
context 'and when:manual set as the default' do
- let(:rules) { described_class.new(rule_list, 'manual') }
+ let(:rules) { described_class.new(rule_list, default_when: 'manual') }
it 'does not return the default when:' do
expect(subject).to eq(described_class::Result.new('never'))
diff --git a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index a7f457e0f5e..513a9b8f2b4 100644
--- a/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -28,6 +28,14 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
expect(entry.value).to eq config
end
end
+
+ context "when value includes 'expose_as' keyword" do
+ let(:config) { { paths: %w[results.txt], expose_as: "Test results" } }
+
+ 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
@@ -58,6 +66,84 @@ describe Gitlab::Ci::Config::Entry::Artifacts do
.to include 'artifacts reports should be a hash'
end
end
+
+ context "when 'expose_as' is not a string" do
+ let(:config) { { paths: %w[results.txt], expose_as: 1 } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts expose as should be a string'
+ end
+ end
+
+ context "when 'expose_as' is too long" do
+ let(:config) { { paths: %w[results.txt], expose_as: 'A' * 101 } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts expose as is too long (maximum is 100 characters)'
+ end
+ end
+
+ context "when 'expose_as' is an empty string" do
+ let(:config) { { paths: %w[results.txt], expose_as: '' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts expose as ' + Gitlab::Ci::Config::Entry::Artifacts::EXPOSE_AS_ERROR_MESSAGE
+ end
+ end
+
+ context "when 'expose_as' contains invalid characters" do
+ let(:config) do
+ { paths: %w[results.txt], expose_as: '<script>alert("xss");</script>' }
+ end
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include 'artifacts expose as ' + Gitlab::Ci::Config::Entry::Artifacts::EXPOSE_AS_ERROR_MESSAGE
+ end
+ end
+
+ context "when 'expose_as' is used without 'paths'" do
+ let(:config) { { expose_as: 'Test results' } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include "artifacts paths can't be blank"
+ end
+ end
+
+ context "when 'paths' includes '*' and 'expose_as' is defined" do
+ let(:config) { { expose_as: 'Test results', paths: ['test.txt', 'test*.txt'] } }
+
+ it 'reports error' do
+ expect(entry.errors)
+ .to include "artifacts paths can't contain '*' when used with 'expose_as'"
+ end
+ end
+ end
+
+ context 'when feature flag :ci_expose_arbitrary_artifacts_in_mr is disabled' do
+ before do
+ stub_feature_flags(ci_expose_arbitrary_artifacts_in_mr: false)
+ end
+
+ context 'when syntax is correct' do
+ let(:config) { { expose_as: 'Test results', paths: ['test.txt'] } }
+
+ it 'is valid' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ context 'when syntax for :expose_as is incorrect' do
+ let(:config) { { paths: %w[results.txt], expose_as: '' } }
+
+ it 'is valid' do
+ expect(entry.errors).to be_empty
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 9aab3664e1c..4fa0a57dc82 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -12,22 +12,53 @@ describe Gitlab::Ci::Config::Entry::Cache do
context 'when entry config value is correct' do
let(:policy) { nil }
+ let(:key) { 'some key' }
let(:config) do
- { key: 'some key',
+ { key: key,
untracked: true,
paths: ['some/path/'],
policy: policy }
end
describe '#value' do
- it 'returns hash value' do
- expect(entry.value).to eq(key: 'some key', untracked: true, paths: ['some/path/'], policy: 'pull-push')
+ shared_examples 'hash key value' do
+ it 'returns hash value' do
+ expect(entry.value).to eq(key: key, untracked: true, paths: ['some/path/'], policy: 'pull-push')
+ end
+ end
+
+ it_behaves_like 'hash key value'
+
+ context 'with files' do
+ let(:key) { { files: ['a-file', 'other-file'] } }
+
+ it_behaves_like 'hash key value'
+ end
+
+ context 'with files and prefix' do
+ let(:key) { { files: ['a-file', 'other-file'], prefix: 'prefix-value' } }
+
+ it_behaves_like 'hash key value'
+ end
+
+ context 'with prefix' do
+ let(:key) { { prefix: 'prefix-value' } }
+
+ it 'key is nil' do
+ expect(entry.value).to match(a_hash_including(key: nil))
+ end
end
end
describe '#valid?' do
it { is_expected.to be_valid }
+
+ context 'with files' do
+ let(:key) { { files: ['a-file', 'other-file'] } }
+
+ it { is_expected.to be_valid }
+ end
end
context 'policy is pull-push' do
@@ -87,10 +118,44 @@ describe Gitlab::Ci::Config::Entry::Cache do
end
context 'when descendants are invalid' do
- let(:config) { { key: 1 } }
+ context 'with invalid keys' do
+ let(:config) { { key: 1 } }
- it 'reports error with descendants' do
- is_expected.to include 'key config should be a string or symbol'
+ it 'reports error with descendants' do
+ is_expected.to include 'key should be a hash, a string or a symbol'
+ end
+ end
+
+ context 'with empty key' do
+ let(:config) { { key: {} } }
+
+ it 'reports error with descendants' do
+ is_expected.to include 'key config missing required keys: files'
+ end
+ end
+
+ context 'with invalid files' do
+ let(:config) { { key: { files: 'a-file' } } }
+
+ it 'reports error with descendants' do
+ is_expected.to include 'key:files config should be an array of strings'
+ end
+ end
+
+ context 'with prefix without files' do
+ let(:config) { { key: { prefix: 'a-prefix' } } }
+
+ it 'reports error with descendants' do
+ is_expected.to include 'key config missing required keys: files'
+ end
+ end
+
+ context 'when there is an unknown key present' do
+ let(:config) { { key: { unknown: 'a-file' } } }
+
+ it 'reports error with descendants' do
+ is_expected.to include 'key config contains unknown keys: unknown'
+ 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 269a3406913..8e7f9ab9706 100644
--- a/spec/lib/gitlab/ci/config/entry/commands_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Commands do
let(:entry) { described_class.new(config) }
- context 'when entry config value is an array' do
+ context 'when entry config value is an array of strings' do
let(:config) { %w(ls pwd) }
describe '#value' do
@@ -37,13 +37,74 @@ describe Gitlab::Ci::Config::Entry::Commands do
end
end
- context 'when entry value is not valid' do
+ context 'when entry config value is array of arrays of strings' do
+ let(:config) { [['ls'], ['pwd', 'echo 1']] }
+
+ describe '#value' do
+ it 'returns array of strings' do
+ expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
+ end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when entry config value is array of strings and arrays of strings' do
+ let(:config) { ['ls', ['pwd', 'echo 1']] }
+
+ describe '#value' do
+ it 'returns array of strings' do
+ expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
+ end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when entry value is integer' do
let(:config) { 1 }
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include 'commands config should be an array of strings or a string'
+ .to include 'commands config should be a string or an array containing strings and arrays of strings'
+ end
+ end
+ end
+
+ context 'when entry value is multi-level nested array' do
+ let(:config) { [['ls', ['echo 1']], 'pwd'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'commands config should be a string or an array containing strings and arrays of strings'
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb
index 27d63dbd407..dad4f408e50 100644
--- a/spec/lib/gitlab/ci/config/entry/default_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb
@@ -5,6 +5,18 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Default do
let(:entry) { described_class.new(config) }
+ it_behaves_like 'with inheritable CI config' do
+ let(:inheritable_key) { nil }
+ let(:inheritable_class) { Gitlab::Ci::Config::Entry::Root }
+
+ # These are entries defined in Root
+ # that we know that we don't want to inherit
+ # as they do not have sense in context of Default
+ let(:ignored_inheritable_columns) do
+ %i[default include variables stages types workflow]
+ end
+ end
+
describe '.nodes' do
it 'returns a hash' do
expect(described_class.nodes).to be_a(Hash)
@@ -14,7 +26,7 @@ describe Gitlab::Ci::Config::Entry::Default do
it 'contains the expected node names' do
expect(described_class.nodes.keys)
.to match_array(%i[before_script image services
- after_script cache])
+ after_script cache interruptible])
end
end
end
@@ -87,7 +99,7 @@ describe Gitlab::Ci::Config::Entry::Default do
it 'raises error' do
expect { entry.compose!(deps) }.to raise_error(
- Gitlab::Ci::Config::Entry::Default::DuplicateError)
+ Gitlab::Ci::Config::Entry::Default::InheritError)
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/files_spec.rb b/spec/lib/gitlab/ci/config/entry/files_spec.rb
new file mode 100644
index 00000000000..2bebbd7b198
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/files_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Files do
+ let(:entry) { described_class.new(config) }
+
+ describe 'validations' do
+ context 'when entry config value is valid' do
+ let(:config) { ['some/file', 'some/path/'] }
+
+ describe '#value' do
+ it 'returns key value' do
+ expect(entry.value).to eq config
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ describe '#errors' do
+ context 'when entry value is not an array' do
+ let(:config) { 'string' }
+
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'files config should be an array of strings'
+ end
+ end
+
+ context 'when entry value is not an array of strings' do
+ let(:config) { [1] }
+
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'files config should be an array of strings'
+ end
+ end
+
+ context 'when entry value contains more than two values' do
+ let(:config) { %w[file1 file2 file3] }
+
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'files config has too many items (maximum is 2)'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 1c4887e87c4..fe83171c57a 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -5,14 +5,26 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Entry::Job do
let(:entry) { described_class.new(config, name: :rspec) }
+ it_behaves_like 'with inheritable CI config' do
+ let(:inheritable_key) { 'default' }
+ let(:inheritable_class) { Gitlab::Ci::Config::Entry::Default }
+
+ # These are entries defined in Default
+ # that we know that we don't want to inherit
+ # as they do not have sense in context of Job
+ let(:ignored_inheritable_columns) do
+ %i[]
+ end
+ end
+
describe '.nodes' do
context 'when filtering all the entry/node names' do
subject { described_class.nodes.keys }
let(:result) do
%i[before_script script stage type after_script cache
- image services only except rules variables artifacts
- environment coverage retry]
+ image services only except rules needs variables artifacts
+ environment coverage retry interruptible]
end
it { is_expected.to match_array result }
@@ -372,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do
end
context 'when has needs' do
- context 'that are not a array of strings' do
- let(:config) do
- {
- stage: 'test',
- script: 'echo',
- needs: 'build-job'
- }
- end
-
- it 'returns error about invalid type' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include 'job needs should be an array of strings'
- end
- end
-
context 'when have dependencies that are not subset of needs' do
let(:config) do
{
diff --git a/spec/lib/gitlab/ci/config/entry/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb
index a7874447725..327607e2266 100644
--- a/spec/lib/gitlab/ci/config/entry/key_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb
@@ -6,38 +6,38 @@ describe Gitlab::Ci::Config::Entry::Key do
let(:entry) { described_class.new(config) }
describe 'validations' do
- shared_examples 'key with slash' do
- it 'is invalid' do
- expect(entry).not_to be_valid
- end
+ it_behaves_like 'key entry validations', 'simple key'
- it 'reports errors with config value' do
- expect(entry.errors).to include 'key config cannot contain the "/" character'
- end
- end
+ context 'when entry config value is correct' do
+ context 'when key is a hash' do
+ let(:config) { { files: ['test'], prefix: 'something' } }
- shared_examples 'key with only dots' do
- it 'is invalid' do
- expect(entry).not_to be_valid
- end
+ describe '#value' do
+ it 'returns key value' do
+ expect(entry.value).to match(config)
+ end
+ end
- it 'reports errors with config value' do
- expect(entry.errors).to include 'key config cannot be "." or ".."'
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
end
- end
- context 'when entry config value is correct' do
- let(:config) { 'test' }
+ context 'when key is a symbol' do
+ let(:config) { :key }
- describe '#value' do
- it 'returns key value' do
- expect(entry.value).to eq 'test'
+ describe '#value' do
+ it 'returns key value' do
+ expect(entry.value).to eq(config.to_s)
+ end
end
- end
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
end
end
end
@@ -47,53 +47,11 @@ describe Gitlab::Ci::Config::Entry::Key do
describe '#errors' do
it 'saves errors' do
- expect(entry.errors)
- .to include 'key config should be a string or symbol'
+ expect(entry.errors.first)
+ .to match /should be a hash, a string or a symbol/
end
end
end
-
- context 'when entry value contains slash' do
- let(:config) { 'key/with/some/slashes' }
-
- it_behaves_like 'key with slash'
- end
-
- context 'when entry value contains URI encoded slash (%2F)' do
- let(:config) { 'key%2Fwith%2Fsome%2Fslashes' }
-
- it_behaves_like 'key with slash'
- end
-
- context 'when entry value is a dot' do
- let(:config) { '.' }
-
- it_behaves_like 'key with only dots'
- end
-
- context 'when entry value is two dots' do
- let(:config) { '..' }
-
- it_behaves_like 'key with only dots'
- end
-
- context 'when entry value is a URI encoded dot (%2E)' do
- let(:config) { '%2e' }
-
- it_behaves_like 'key with only dots'
- end
-
- context 'when entry value is two URI encoded dots (%2E)' do
- let(:config) { '%2E%2e' }
-
- it_behaves_like 'key with only dots'
- end
-
- context 'when entry value is one dot and one URI encoded dot' do
- let(:config) { '.%2e' }
-
- it_behaves_like 'key with only dots'
- end
end
describe '.default' do
diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb
new file mode 100644
index 00000000000..d119e604900
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/need_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Gitlab::Ci::Config::Entry::Need do
+ subject(:need) { described_class.new(config) }
+
+ context 'when job is specified' do
+ let(:config) { 'job_name' }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name')
+ end
+ end
+ end
+
+ context 'when need is empty' do
+ let(:config) { '' }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about an empty config' do
+ expect(need.errors)
+ .to contain_exactly("job config can't be blank")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
new file mode 100644
index 00000000000..f4a76b52d30
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Gitlab::Ci::Config::Entry::Needs do
+ subject(:needs) { described_class.new(config) }
+
+ before do
+ needs.metadata[:allowed_needs] = %i[job]
+ end
+
+ describe 'validations' do
+ before do
+ needs.compose!
+ end
+
+ context 'when entry config value is correct' do
+ let(:config) { ['job_name'] }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context 'when config value has wrong type' do
+ let(:config) { 123 }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors)
+ .to include('needs config can only be a hash or an array')
+ end
+ end
+ end
+
+ context 'when wrong needs type is used' do
+ let(:config) { [123] }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors).to contain_exactly(
+ 'need has an unsupported type')
+ end
+ end
+ end
+ end
+
+ describe '.compose!' do
+ context 'when valid job entries composed' do
+ let(:config) { %w[first_job_name second_job_name] }
+
+ before do
+ needs.compose!
+ end
+
+ describe '#value' do
+ it 'returns key value' do
+ expect(needs.value).to eq(
+ job: [
+ { name: 'first_job_name' },
+ { name: 'second_job_name' }
+ ]
+ )
+ end
+ end
+
+ describe '#descendants' do
+ it 'creates valid descendant nodes' do
+ expect(needs.descendants.count).to eq 2
+ expect(needs.descendants)
+ .to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/prefix_spec.rb b/spec/lib/gitlab/ci/config/entry/prefix_spec.rb
new file mode 100644
index 00000000000..8132a674488
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/prefix_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Prefix do
+ let(:entry) { described_class.new(config) }
+
+ describe 'validations' do
+ it_behaves_like 'key entry validations', :prefix
+
+ context 'when entry value is not correct' do
+ let(:config) { ['incorrect'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'prefix config should be a string or symbol'
+ end
+ end
+ end
+ end
+
+ describe '.default' do
+ it 'returns default key' do
+ expect(described_class.default).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 7e1a80414d4..43bd53b780f 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -12,10 +12,14 @@ describe Gitlab::Ci::Config::Entry::Root do
context 'when filtering all the entry/node names' do
it 'contains the expected node names' do
+ # No inheritable fields should be added to the `Root`
+ #
+ # Inheritable configuration can only be added to `default:`
+ #
+ # The purpose of `Root` is have only globally defined configuration.
expect(described_class.nodes.keys)
- .to match_array(%i[before_script image services
- after_script variables cache
- stages types include default])
+ .to match_array(%i[before_script image services after_script
+ variables cache stages types include default workflow])
end
end
end
@@ -45,7 +49,7 @@ describe Gitlab::Ci::Config::Entry::Root do
end
it 'creates node object for each entry' do
- expect(root.descendants.count).to eq 10
+ expect(root.descendants.count).to eq 11
end
it 'creates node object using valid class' do
@@ -198,7 +202,7 @@ describe Gitlab::Ci::Config::Entry::Root do
describe '#nodes' do
it 'instantizes all nodes' do
- expect(root.descendants.count).to eq 10
+ expect(root.descendants.count).to eq 11
end
it 'contains unspecified nodes' do
@@ -293,7 +297,7 @@ describe Gitlab::Ci::Config::Entry::Root do
describe '#errors' do
it 'reports errors from child nodes' do
expect(root.errors)
- .to include 'before_script config should be an array of strings'
+ .to include 'before_script config should be an array containing strings and arrays of strings'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index 9d4f7153cd0..216f5d0c77d 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -1,10 +1,22 @@
+# frozen_string_literal: true
+
require 'fast_spec_helper'
require 'gitlab_chronic_duration'
require 'support/helpers/stub_feature_flags'
require_dependency 'active_model'
describe Gitlab::Ci::Config::Entry::Rules::Rule do
- let(:entry) { described_class.new(config) }
+ let(:factory) do
+ Gitlab::Config::Entry::Factory.new(described_class)
+ .metadata(metadata)
+ .value(config)
+ end
+
+ let(:metadata) do
+ { allowed_when: %w[on_success on_failure always never manual delayed] }
+ end
+
+ let(:entry) { factory.create! }
describe '.new' do
subject { entry }
@@ -210,6 +222,112 @@ describe Gitlab::Ci::Config::Entry::Rules::Rule do
.to include(/should be a hash/)
end
end
+
+ context 'when: validation' do
+ context 'with an invalid boolean when:' do
+ let(:config) do
+ { if: '$THIS == "that"', when: false }
+ end
+
+ it { is_expected.to be_a(described_class) }
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: false/)
+ end
+
+ context 'when composed' do
+ before do
+ subject.compose!
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: false/)
+ end
+ end
+ end
+
+ context 'with an invalid string when:' do
+ let(:config) do
+ { if: '$THIS == "that"', when: 'explode' }
+ end
+
+ it { is_expected.to be_a(described_class) }
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: explode/)
+ end
+
+ context 'when composed' do
+ before do
+ subject.compose!
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: explode/)
+ end
+ end
+ end
+
+ context 'with a string passed in metadata but not allowed in the class' do
+ let(:metadata) { { allowed_when: %w[explode] } }
+
+ let(:config) do
+ { if: '$THIS == "that"', when: 'explode' }
+ end
+
+ it { is_expected.to be_a(described_class) }
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: explode/)
+ end
+
+ context 'when composed' do
+ before do
+ subject.compose!
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: explode/)
+ end
+ end
+ end
+
+ context 'with a string allowed in the class but not passed in metadata' do
+ let(:metadata) { { allowed_when: %w[always never] } }
+
+ let(:config) do
+ { if: '$THIS == "that"', when: 'on_success' }
+ end
+
+ it { is_expected.to be_a(described_class) }
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: on_success/)
+ end
+
+ context 'when composed' do
+ before do
+ subject.compose!
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns an error about invalid when:' do
+ expect(subject.errors).to include(/when unknown value: on_success/)
+ end
+ end
+ end
+ end
end
describe '#value' do
diff --git a/spec/lib/gitlab/ci/config/entry/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/rules_spec.rb
index 291e7373daf..3c050801023 100644
--- a/spec/lib/gitlab/ci/config/entry/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules_spec.rb
@@ -1,9 +1,18 @@
+# frozen_string_literal: true
+
require 'fast_spec_helper'
require 'support/helpers/stub_feature_flags'
require_dependency 'active_model'
describe Gitlab::Ci::Config::Entry::Rules do
- let(:entry) { described_class.new(config) }
+ let(:factory) do
+ Gitlab::Config::Entry::Factory.new(described_class)
+ .metadata(metadata)
+ .value(config)
+ end
+
+ let(:metadata) { { allowed_when: %w[always never] } }
+ let(:entry) { factory.create! }
describe '.new' do
subject { entry }
@@ -16,7 +25,7 @@ describe Gitlab::Ci::Config::Entry::Rules do
it { is_expected.to be_a(described_class) }
it { is_expected.to be_valid }
- context 'after #compose!' do
+ context 'when composed' do
before do
subject.compose!
end
@@ -36,7 +45,7 @@ describe Gitlab::Ci::Config::Entry::Rules do
it { is_expected.to be_a(described_class) }
it { is_expected.to be_valid }
- context 'after #compose!' do
+ context 'when composed' do
before do
subject.compose!
end
@@ -52,48 +61,6 @@ describe Gitlab::Ci::Config::Entry::Rules do
it { is_expected.not_to be_valid }
end
-
- context 'with an invalid boolean when:' do
- let(:config) do
- [{ if: '$THIS == "that"', when: false }]
- end
-
- it { is_expected.to be_a(described_class) }
- it { is_expected.to be_valid }
-
- context 'after #compose!' do
- before do
- subject.compose!
- end
-
- it { is_expected.not_to be_valid }
-
- it 'returns an error about invalid when:' do
- expect(subject.errors).to include(/when unknown value: false/)
- end
- end
- end
-
- context 'with an invalid string when:' do
- let(:config) do
- [{ if: '$THIS == "that"', when: 'explode' }]
- end
-
- it { is_expected.to be_a(described_class) }
- it { is_expected.to be_valid }
-
- context 'after #compose!' do
- before do
- subject.compose!
- end
-
- it { is_expected.not_to be_valid }
-
- it 'returns an error about invalid when:' do
- expect(subject.errors).to include(/when unknown value: explode/)
- end
- end
- end
end
describe '#value' do
diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb
index d523243d3b6..57dc20ea628 100644
--- a/spec/lib/gitlab/ci/config/entry/script_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/script_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Ci::Config::Entry::Script do
let(:entry) { described_class.new(config) }
describe 'validations' do
- context 'when entry config value is correct' do
+ context 'when entry config value is array of strings' do
let(:config) { %w(ls pwd) }
describe '#value' do
@@ -28,13 +28,74 @@ describe Gitlab::Ci::Config::Entry::Script do
end
end
- context 'when entry value is not correct' do
+ context 'when entry config value is array of arrays of strings' do
+ let(:config) { [['ls'], ['pwd', 'echo 1']] }
+
+ describe '#value' do
+ it 'returns array of strings' do
+ expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
+ end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when entry config value is array containing strings and arrays of strings' do
+ let(:config) { ['ls', ['pwd', 'echo 1']] }
+
+ describe '#value' do
+ it 'returns array of strings' do
+ expect(entry.value).to eq ['ls', 'pwd', 'echo 1']
+ end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when entry value is string' do
let(:config) { 'ls' }
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include 'script config should be an array of strings'
+ .to include 'script config should be an array containing strings and arrays of strings'
+ end
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+ end
+
+ context 'when entry value is multi-level nested array' do
+ let(:config) { [['ls', ['echo 1']], 'pwd'] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include 'script config should be an array containing strings and arrays of strings'
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/workflow_spec.rb b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
new file mode 100644
index 00000000000..f2832b94bf0
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/workflow_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Workflow do
+ let(:factory) { Gitlab::Config::Entry::Factory.new(described_class).value(rules_hash) }
+ let(:config) { factory.create! }
+
+ describe 'validations' do
+ context 'when work config value is a string' do
+ let(:rules_hash) { 'build' }
+
+ describe '#valid?' do
+ it 'is invalid' do
+ expect(config).not_to be_valid
+ end
+
+ it 'attaches an error specifying that workflow should point to a hash' do
+ expect(config.errors).to include('workflow config should be a hash')
+ end
+ end
+
+ describe '#value' do
+ it 'returns the invalid configuration' do
+ expect(config.value).to eq(rules_hash)
+ end
+ end
+ end
+
+ context 'when work config value is a hash' do
+ let(:rules_hash) { { rules: [{ if: '$VAR' }] } }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(config).to be_valid
+ end
+
+ it 'attaches no errors' do
+ expect(config.errors).to be_empty
+ end
+ end
+
+ describe '#value' do
+ it 'returns the config' do
+ expect(config.value).to eq(rules_hash)
+ end
+ end
+
+ context 'with an invalid key' do
+ let(:rules_hash) { { trash: [{ if: '$VAR' }] } }
+
+ describe '#valid?' do
+ it 'is invalid' do
+ expect(config).not_to be_valid
+ end
+
+ it 'attaches an error specifying the unknown key' do
+ expect(config.errors).to include('workflow config contains unknown keys: trash')
+ end
+ end
+
+ describe '#value' do
+ it 'returns the invalid configuration' do
+ expect(config.value).to eq(rules_hash)
+ end
+ end
+ end
+ end
+ end
+
+ describe '.default' do
+ it 'is nil' do
+ expect(described_class.default).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb
index 6b766cc37bf..bf880478387 100644
--- a/spec/lib/gitlab/ci/config/normalizer_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb
@@ -7,6 +7,16 @@ describe Gitlab::Ci::Config::Normalizer do
let(:job_config) { { script: 'rspec', parallel: 5, name: 'rspec' } }
let(:config) { { job_name => job_config } }
+ let(:expanded_job_names) do
+ [
+ "rspec 1/5",
+ "rspec 2/5",
+ "rspec 3/5",
+ "rspec 4/5",
+ "rspec 5/5"
+ ]
+ end
+
describe '.normalize_jobs' do
subject { described_class.new(config).normalize_jobs }
@@ -15,9 +25,7 @@ describe Gitlab::Ci::Config::Normalizer do
end
it 'has parallelized jobs' do
- job_names = [:"rspec 1/5", :"rspec 2/5", :"rspec 3/5", :"rspec 4/5", :"rspec 5/5"]
-
- is_expected.to include(*job_names)
+ is_expected.to include(*expanded_job_names.map(&:to_sym))
end
it 'sets job instance in options' do
@@ -43,49 +51,109 @@ describe Gitlab::Ci::Config::Normalizer do
let(:job_name) { :"rspec 35/2" }
it 'properly parallelizes job names' do
- job_names = [:"rspec 35/2 1/5", :"rspec 35/2 2/5", :"rspec 35/2 3/5", :"rspec 35/2 4/5", :"rspec 35/2 5/5"]
+ job_names = [
+ :"rspec 35/2 1/5",
+ :"rspec 35/2 2/5",
+ :"rspec 35/2 3/5",
+ :"rspec 35/2 4/5",
+ :"rspec 35/2 5/5"
+ ]
is_expected.to include(*job_names)
end
end
- %i[dependencies needs].each do |context|
- context "when job has #{context} on parallelized jobs" do
+ context 'for dependencies' do
+ context "when job has dependencies on parallelized jobs" do
let(:config) do
{
job_name => job_config,
- other_job: { script: 'echo 1', context => [job_name.to_s] }
+ other_job: { script: 'echo 1', dependencies: [job_name.to_s] }
}
end
- it "parallelizes #{context}" do
- job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"]
-
- expect(subject[:other_job][context]).to include(*job_names)
+ it "parallelizes dependencies" do
+ expect(subject[:other_job][:dependencies]).to eq(expanded_job_names)
end
it "does not include original job name in #{context}" do
- expect(subject[:other_job][context]).not_to include(job_name)
+ expect(subject[:other_job][:dependencies]).not_to include(job_name)
end
end
- context "when there are #{context} which are both parallelized and not" do
+ context "when there are dependencies which are both parallelized and not" do
let(:config) do
{
job_name => job_config,
other_job: { script: 'echo 1' },
- final_job: { script: 'echo 1', context => [job_name.to_s, "other_job"] }
+ final_job: { script: 'echo 1', dependencies: [job_name.to_s, "other_job"] }
}
end
- it "parallelizes #{context}" do
+ it "parallelizes dependencies" do
job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"]
- expect(subject[:final_job][context]).to include(*job_names)
+ expect(subject[:final_job][:dependencies]).to include(*job_names)
+ end
+
+ it "includes the regular job in dependencies" do
+ expect(subject[:final_job][:dependencies]).to include('other_job')
+ end
+ end
+ end
+
+ context 'for needs' do
+ let(:expanded_job_attributes) do
+ expanded_job_names.map do |job_name|
+ { name: job_name }
+ end
+ end
+
+ context "when job has needs on parallelized jobs" do
+ let(:config) do
+ {
+ job_name => job_config,
+ other_job: {
+ script: 'echo 1',
+ needs: {
+ job: [
+ { name: job_name.to_s }
+ ]
+ }
+ }
+ }
+ end
+
+ it "parallelizes needs" do
+ expect(subject.dig(:other_job, :needs, :job)).to eq(expanded_job_attributes)
+ end
+ end
+
+ context "when there are dependencies which are both parallelized and not" do
+ let(:config) do
+ {
+ job_name => job_config,
+ other_job: {
+ script: 'echo 1'
+ },
+ final_job: {
+ script: 'echo 1',
+ needs: {
+ job: [
+ { name: job_name.to_s },
+ { name: "other_job" }
+ ]
+ }
+ }
+ }
+ end
+
+ it "parallelizes dependencies" do
+ expect(subject.dig(:final_job, :needs, :job)).to include(*expanded_job_attributes)
end
- it "includes the regular job in #{context}" do
- expect(subject[:final_job][context]).to include('other_job')
+ it "includes the regular job in dependencies" do
+ expect(subject.dig(:final_job, :needs, :job)).to include(name: 'other_job')
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index ba4f841cf43..a631cd2777b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
[{ key: 'first', secret_value: 'world' },
{ key: 'second', secret_value: 'second_world' }]
end
+
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
source: :push,
@@ -51,12 +52,6 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
.to eq variables_attributes.map(&:with_indifferent_access)
end
- it 'sets a valid config source' do
- step.perform!
-
- expect(pipeline.repository_source?).to be true
- end
-
it 'returns a valid pipeline' do
step.perform!
diff --git a/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
new file mode 100644
index 00000000000..7b76adaf683
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
+ end
+
+ let(:step) { described_class.new(pipeline, command) }
+
+ describe '#perform!' do
+ context 'when pipeline has been skipped by workflow configuration' do
+ before do
+ allow(step).to receive(:workflow_passed?)
+ .and_return(false)
+
+ step.perform!
+ end
+
+ it 'does not save the pipeline' do
+ expect(pipeline).not_to be_persisted
+ end
+
+ it 'breaks the chain' do
+ expect(step.break?).to be true
+ end
+
+ it 'attaches an error to the pipeline' do
+ expect(pipeline.errors[:base]).to include('Pipeline filtered out by workflow rules.')
+ end
+ end
+
+ context 'when pipeline has not been skipped by workflow configuration' do
+ before do
+ allow(step).to receive(:workflow_passed?)
+ .and_return(true)
+
+ step.perform!
+ end
+
+ it 'continues the pipeline processing chain' do
+ expect(step.break?).to be false
+ end
+
+ it 'does not skip the pipeline' do
+ expect(pipeline).not_to be_persisted
+ expect(pipeline).not_to be_skipped
+ end
+
+ it 'attaches no errors' do
+ expect(pipeline.errors).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
index 9bccd5be4fe..52e9432dc92 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb
@@ -7,9 +7,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
set(:user) { create(:user) }
let(:pipeline) do
- build(:ci_pipeline_with_one_job, project: project,
- ref: 'master',
- user: user)
+ build(:ci_pipeline, project: project, ref: 'master', user: user)
end
let(:command) do
@@ -20,11 +18,32 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
seeds_block: nil)
end
+ let(:dependencies) do
+ [
+ Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
+ Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command),
+ Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command)
+ ]
+ end
+
let(:step) { described_class.new(pipeline, command) }
+ let(:config) do
+ { rspec: { script: 'rspec' } }
+ end
+
+ def run_chain
+ dependencies.map(&:perform!)
+ step.perform!
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(config))
+ end
+
context 'when pipeline doesn not have seeds block' do
before do
- step.perform!
+ run_chain
end
it 'does not persist the pipeline' do
@@ -59,12 +78,8 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
} }
end
- let(:pipeline) do
- build(:ci_pipeline, project: project, config: config)
- end
-
before do
- step.perform!
+ run_chain
end
it 'breaks the chain' do
@@ -82,16 +97,16 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
describe 'pipeline protect' do
- subject { step.perform! }
-
context 'when ref is protected' do
before do
allow(project).to receive(:protected_for?).with('master').and_return(true)
allow(project).to receive(:protected_for?).with('refs/heads/master').and_return(true)
+
+ dependencies.map(&:perform!)
end
it 'does not protect the pipeline' do
- subject
+ run_chain
expect(pipeline.protected).to eq(true)
end
@@ -99,7 +114,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
context 'when ref is not protected' do
it 'does not protect the pipeline' do
- subject
+ run_chain
expect(pipeline.protected).to eq(false)
end
@@ -112,7 +127,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
before do
- step.perform!
+ run_chain
end
it 'breaks the chain' do
@@ -144,7 +159,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
it 'populates pipeline with resources described in the seeds block' do
- step.perform!
+ run_chain
expect(pipeline).not_to be_persisted
expect(pipeline.variables).not_to be_empty
@@ -154,7 +169,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
it 'has pipeline iid' do
- step.perform!
+ run_chain
expect(pipeline.iid).to be > 0
end
@@ -166,7 +181,7 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end
it 'wastes pipeline iid' do
- expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved)
+ expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved)
last_iid = InternalId.ci_pipelines
.where(project_id: project.id)
@@ -181,14 +196,14 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
let(:pipeline) { create(:ci_pipeline, project: project) }
it 'raises error' do
- expect { step.perform! }.to raise_error(described_class::PopulateError)
+ expect { run_chain }.to raise_error(described_class::PopulateError)
end
end
context 'when variables policy is specified' do
shared_examples_for 'a correct pipeline' do
it 'populates pipeline according to used policies' do
- step.perform!
+ run_chain
expect(pipeline.stages.size).to eq 1
expect(pipeline.stages.first.statuses.size).to eq 1
@@ -202,10 +217,6 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
prod: { script: 'cap prod', stage: 'deploy', only: ['tags'] } }
end
- let(:pipeline) do
- build(:ci_pipeline, ref: 'master', project: project, config: config)
- end
-
it_behaves_like 'a correct pipeline'
context 'when variables expression is specified' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
index 7c1c016b4bb..92eadf5548c 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
@@ -2,32 +2,38 @@
require 'spec_helper'
-describe Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs do
- let(:project) { create(:project, :repository) }
+describe ::Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs do
+ let(:project) { create(:project) }
let(:pipeline) do
- build(:ci_pipeline_with_one_job, project: project, ref: 'master')
+ build(:ci_pipeline, project: project)
end
let(:command) do
- double(:command, project: project, chat_data: { command: 'echo' })
+ double(:command,
+ config_processor: double(:processor,
+ jobs: { echo: double(:job_echo), rspec: double(:job_rspec) }),
+ project: project,
+ chat_data: { command: 'echo' })
end
describe '#perform!' do
- it 'removes unwanted jobs for chat pipelines' do
- allow(pipeline).to receive(:chat?).and_return(true)
+ subject { described_class.new(pipeline, command).perform! }
- pipeline.config_processor.jobs[:echo] = double(:job)
+ it 'removes unwanted jobs for chat pipelines' do
+ expect(pipeline).to receive(:chat?).and_return(true)
- described_class.new(pipeline, command).perform!
+ subject
- expect(pipeline.config_processor.jobs.keys).to eq([:echo])
+ expect(command.config_processor.jobs.keys).to eq([:echo])
end
- end
- it 'does not remove any jobs for non-chat pipelines' do
- described_class.new(pipeline, command).perform!
+ it 'does not remove any jobs for non chat-pipelines' do
+ expect(pipeline).to receive(:chat?).and_return(false)
- expect(pipeline.config_processor.jobs.keys).to eq([:rspec])
+ subject
+
+ expect(command.config_processor.jobs.keys).to eq([:echo, :rspec])
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
new file mode 100644
index 00000000000..aa54f19b26c
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Seed do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user, developer_projects: [project]) }
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ origin_ref: 'master',
+ seeds_block: nil)
+ end
+
+ def run_chain(pipeline, command)
+ [
+ Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
+ Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command)
+ ].map(&:perform!)
+
+ described_class.new(pipeline, command).perform!
+ end
+
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+
+ describe '#perform!' do
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(config))
+ run_chain(pipeline, command)
+ end
+
+ let(:config) do
+ { rspec: { script: 'rake' } }
+ end
+
+ it 'allocates next IID' do
+ expect(pipeline.iid).to be_present
+ end
+
+ it 'sets the seeds in the command object' do
+ expect(command.stage_seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Base)
+ expect(command.stage_seeds.count).to eq 1
+ end
+
+ context 'when no ref policy is specified' do
+ let(:config) do
+ {
+ production: { stage: 'deploy', script: 'cap prod' },
+ rspec: { stage: 'test', script: 'rspec' },
+ spinach: { stage: 'test', script: 'spinach' }
+ }
+ end
+
+ it 'correctly fabricates a stage seeds object' do
+ seeds = command.stage_seeds
+ expect(seeds.size).to eq 2
+ expect(seeds.first.attributes[:name]).to eq 'test'
+ expect(seeds.second.attributes[:name]).to eq 'deploy'
+ expect(seeds.dig(0, 0, :name)).to eq 'rspec'
+ expect(seeds.dig(0, 1, :name)).to eq 'spinach'
+ expect(seeds.dig(1, 0, :name)).to eq 'production'
+ end
+ end
+
+ context 'when refs policy is specified' do
+ let(:pipeline) do
+ build(:ci_pipeline, project: project, ref: 'feature', tag: true)
+ end
+
+ let(:config) do
+ {
+ production: { stage: 'deploy', script: 'cap prod', only: ['master'] },
+ spinach: { stage: 'test', script: 'spinach', only: ['tags'] }
+ }
+ end
+
+ it 'returns stage seeds only assigned to master' do
+ seeds = command.stage_seeds
+
+ expect(seeds.size).to eq 1
+ expect(seeds.first.attributes[:name]).to eq 'test'
+ expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ end
+ end
+
+ context 'when source policy is specified' do
+ let(:pipeline) { create(:ci_pipeline, source: :schedule) }
+
+ let(:config) do
+ {
+ production: { stage: 'deploy', script: 'cap prod', only: ['triggers'] },
+ spinach: { stage: 'test', script: 'spinach', only: ['schedules'] }
+ }
+ end
+
+ it 'returns stage seeds only assigned to schedules' do
+ seeds = command.stage_seeds
+
+ expect(seeds.size).to eq 1
+ expect(seeds.first.attributes[:name]).to eq 'test'
+ expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ end
+ end
+
+ context 'when kubernetes policy is specified' do
+ let(:config) do
+ {
+ spinach: { stage: 'test', script: 'spinach' },
+ production: {
+ stage: 'deploy',
+ script: 'cap',
+ only: { kubernetes: 'active' }
+ }
+ }
+ end
+
+ context 'when kubernetes is active' do
+ context 'when user configured kubernetes from CI/CD > Clusters' do
+ let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:project) { cluster.project }
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+
+ it 'returns seeds for kubernetes dependent job' do
+ seeds = command.stage_seeds
+
+ expect(seeds.size).to eq 2
+ expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ expect(seeds.dig(1, 0, :name)).to eq 'production'
+ end
+ end
+ end
+
+ context 'when kubernetes is not active' do
+ it 'does not return seeds for kubernetes dependent job' do
+ seeds = command.stage_seeds
+
+ expect(seeds.size).to eq 1
+ expect(seeds.dig(0, 0, :name)).to eq 'spinach'
+ end
+ end
+ end
+
+ context 'when variables policy is specified' do
+ let(:config) do
+ {
+ unit: { script: 'minitest', only: { variables: ['$CI_PIPELINE_SOURCE'] } },
+ feature: { script: 'spinach', only: { variables: ['$UNDEFINED'] } }
+ }
+ end
+
+ it 'returns stage seeds only when variables expression is truthy' do
+ seeds = command.stage_seeds
+
+ expect(seeds.size).to eq 1
+ expect(seeds.dig(0, 0, :name)).to eq 'unit'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
deleted file mode 100644
index 79acd3e4f54..00000000000
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
+++ /dev/null
@@ -1,148 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
- set(:project) { create(:project, :repository) }
- set(:user) { create(:user) }
-
- let(:command) do
- Gitlab::Ci::Pipeline::Chain::Command.new(
- project: project,
- current_user: user,
- save_incompleted: true)
- end
-
- let!(:step) { described_class.new(pipeline, command) }
-
- before do
- step.perform!
- end
-
- context 'when pipeline has no YAML configuration' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, project: project)
- end
-
- it 'appends errors about missing configuration' do
- expect(pipeline.errors.to_a)
- .to include 'Missing .gitlab-ci.yml file'
- end
-
- it 'breaks the chain' do
- expect(step.break?).to be true
- end
- end
-
- context 'when YAML configuration contains errors' do
- let(:pipeline) do
- build(:ci_pipeline, project: project, config: 'invalid YAML')
- end
-
- it 'appends errors about YAML errors' do
- expect(pipeline.errors.to_a)
- .to include 'Invalid configuration format'
- end
-
- it 'breaks the chain' do
- expect(step.break?).to be true
- end
-
- context 'when saving incomplete pipeline is allowed' do
- let(:command) do
- double('command', project: project,
- current_user: user,
- save_incompleted: true)
- end
-
- it 'fails the pipeline' do
- expect(pipeline.reload).to be_failed
- end
-
- it 'sets a config error failure reason' do
- expect(pipeline.reload.config_error?).to eq true
- end
- end
-
- context 'when saving incomplete pipeline is not allowed' do
- let(:command) do
- double('command', project: project,
- current_user: user,
- save_incompleted: false)
- end
-
- it 'does not drop pipeline' do
- expect(pipeline).not_to be_failed
- expect(pipeline).not_to be_persisted
- end
- end
- end
-
- context 'when pipeline contains configuration validation errors' do
- let(:config) do
- {
- rspec: {
- before_script: 10,
- script: 'ls -al'
- }
- }
- end
-
- let(:pipeline) do
- build(:ci_pipeline, project: project, config: config)
- end
-
- it 'appends configuration validation errors to pipeline errors' do
- expect(pipeline.errors.to_a)
- .to include "jobs:rspec:before_script config should be an array of strings"
- end
-
- it 'breaks the chain' do
- expect(step.break?).to be true
- end
- end
-
- context 'when pipeline is correct and complete' do
- let(:pipeline) do
- build(:ci_pipeline_with_one_job, project: project)
- end
-
- it 'does not invalidate the pipeline' do
- expect(pipeline).to be_valid
- end
-
- it 'does not break the chain' do
- expect(step.break?).to be false
- end
- end
-
- context 'when pipeline source is merge request' do
- before do
- stub_ci_pipeline_yaml_file(YAML.dump(config))
- end
-
- let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
-
- let(:merge_request_pipeline) do
- build(:ci_pipeline, source: :merge_request_event, project: project)
- end
-
- let(:chain) { described_class.new(merge_request_pipeline, command).tap(&:perform!) }
-
- context "when config contains 'merge_requests' keyword" do
- let(:config) { { rspec: { script: 'echo', only: ['merge_requests'] } } }
-
- it 'does not break the chain' do
- expect(chain).not_to be_break
- end
- end
-
- context "when config contains 'merge_request' keyword" do
- let(:config) { { rspec: { script: 'echo', only: ['merge_request'] } } }
-
- it 'does not break the chain' do
- expect(chain).not_to be_break
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
new file mode 100644
index 00000000000..6a8b804597c
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
@@ -0,0 +1,261 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:head_sha) { project.repository.head_commit.id }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: head_sha) }
+
+ let(:processor) { described_class.new(pipeline, config) }
+
+ describe '#build_attributes' do
+ subject { processor.build_attributes }
+
+ context 'with cache:key' do
+ let(:config) do
+ {
+ key: 'a-key',
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it { is_expected.to include(options: { cache: config }) }
+ end
+
+ context 'with cache:key as a symbol' do
+ let(:config) do
+ {
+ key: :a_key,
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it { is_expected.to include(options: { cache: config.merge(key: "a_key") }) }
+ end
+
+ context 'with cache:key:files' do
+ shared_examples 'default key' do
+ let(:config) do
+ { key: { files: files } }
+ end
+
+ it 'uses default key' do
+ expected = { options: { cache: { key: 'default' } } }
+
+ is_expected.to include(expected)
+ end
+ end
+
+ shared_examples 'version and gemfile files' do
+ let(:config) do
+ {
+ key: {
+ files: files
+ },
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it 'builds a string key' do
+ expected = {
+ options: {
+ cache: {
+ key: '703ecc8fef1635427a1f86a8a1a308831c122392',
+ paths: ['vendor/ruby']
+ }
+ }
+ }
+
+ is_expected.to include(expected)
+ end
+ end
+
+ context 'with existing files' do
+ let(:files) { ['VERSION', 'Gemfile.zip'] }
+
+ it_behaves_like 'version and gemfile files'
+ end
+
+ context 'with files starting with ./' do
+ let(:files) { ['Gemfile.zip', './VERSION'] }
+
+ it_behaves_like 'version and gemfile files'
+ end
+
+ context 'with feature flag disabled' do
+ let(:files) { ['VERSION', 'Gemfile.zip'] }
+
+ before do
+ stub_feature_flags(ci_file_based_cache: false)
+ end
+
+ it_behaves_like 'default key'
+ end
+
+ context 'with files ending with /' do
+ let(:files) { ['Gemfile.zip/'] }
+
+ it_behaves_like 'default key'
+ end
+
+ context 'with new line in filenames' do
+ let(:files) { ["Gemfile.zip\nVERSION"] }
+
+ it_behaves_like 'default key'
+ end
+
+ context 'with missing files' do
+ let(:files) { ['project-gemfile.lock', ''] }
+
+ it_behaves_like 'default key'
+ end
+
+ context 'with directories' do
+ shared_examples 'foo/bar directory key' do
+ let(:config) do
+ {
+ key: {
+ files: files
+ }
+ }
+ end
+
+ it 'builds a string key' do
+ expected = {
+ options: {
+ cache: { key: '74bf43fb1090f161bdd4e265802775dbda2f03d1' }
+ }
+ }
+
+ is_expected.to include(expected)
+ end
+ end
+
+ context 'with directory' do
+ let(:files) { ['foo/bar'] }
+
+ it_behaves_like 'foo/bar directory key'
+ end
+
+ context 'with directory ending in slash' do
+ let(:files) { ['foo/bar/'] }
+
+ it_behaves_like 'foo/bar directory key'
+ end
+
+ context 'with directories ending in slash star' do
+ let(:files) { ['foo/bar/*'] }
+
+ it_behaves_like 'foo/bar directory key'
+ end
+ end
+ end
+
+ context 'with cache:key:prefix' do
+ context 'without files' do
+ let(:config) do
+ {
+ key: {
+ prefix: 'a-prefix'
+ },
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it 'adds prefix to default key' do
+ expected = {
+ options: {
+ cache: {
+ key: 'a-prefix-default',
+ paths: ['vendor/ruby']
+ }
+ }
+ }
+
+ is_expected.to include(expected)
+ end
+ end
+
+ context 'with existing files' do
+ let(:config) do
+ {
+ key: {
+ files: ['VERSION', 'Gemfile.zip'],
+ prefix: 'a-prefix'
+ },
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it 'adds prefix key' do
+ expected = {
+ options: {
+ cache: {
+ key: 'a-prefix-703ecc8fef1635427a1f86a8a1a308831c122392',
+ paths: ['vendor/ruby']
+ }
+ }
+ }
+
+ is_expected.to include(expected)
+ end
+ end
+
+ context 'with missing files' do
+ let(:config) do
+ {
+ key: {
+ files: ['project-gemfile.lock', ''],
+ prefix: 'a-prefix'
+ },
+ paths: ['vendor/ruby']
+ }
+ end
+
+ it 'adds prefix to default key' do
+ expected = {
+ options: {
+ cache: {
+ key: 'a-prefix-default',
+ paths: ['vendor/ruby']
+ }
+ }
+ }
+
+ is_expected.to include(expected)
+ end
+ end
+ end
+
+ context 'with all cache option keys' do
+ let(:config) do
+ {
+ key: 'a-key',
+ paths: ['vendor/ruby'],
+ untracked: true,
+ policy: 'push'
+ }
+ end
+
+ it { is_expected.to include(options: { cache: config }) }
+ end
+
+ context 'with unknown cache option keys' do
+ let(:config) do
+ {
+ key: 'a-key',
+ unknown_key: true
+ }
+ end
+
+ it { expect { subject }.to raise_error(ArgumentError, /unknown_key/) }
+ end
+
+ context 'with empty config' do
+ let(:config) { {} }
+
+ it { is_expected.to include(options: {}) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 945baf47b7b..53dcb6359fe 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
describe Gitlab::Ci::Pipeline::Seed::Build do
let(:project) { create(:project, :repository) }
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let(:head_sha) { project.repository.head_commit.id }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: head_sha) }
let(:attributes) { { name: 'rspec', ref: 'master' } }
let(:previous_stages) { [] }
@@ -69,6 +70,101 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
it { is_expected.to include(when: 'never') }
end
end
+
+ context 'with cache:key' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {
+ key: 'a-value'
+ }
+ }
+ end
+
+ it { is_expected.to include(options: { cache: { key: 'a-value' } }) }
+ end
+
+ context 'with cache:key:files' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {
+ key: {
+ files: ['VERSION']
+ }
+ }
+ }
+ end
+
+ it 'includes cache options' do
+ cache_options = {
+ options: {
+ cache: {
+ key: 'f155568ad0933d8358f66b846133614f76dd0ca4'
+ }
+ }
+ }
+
+ is_expected.to include(cache_options)
+ end
+ end
+
+ context 'with cache:key:prefix' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {
+ key: {
+ prefix: 'something'
+ }
+ }
+ }
+ end
+
+ it { is_expected.to include(options: { cache: { key: 'something-default' } }) }
+ end
+
+ context 'with cache:key:files and prefix' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {
+ key: {
+ files: ['VERSION'],
+ prefix: 'something'
+ }
+ }
+ }
+ end
+
+ it 'includes cache options' do
+ cache_options = {
+ options: {
+ cache: {
+ key: 'something-f155568ad0933d8358f66b846133614f76dd0ca4'
+ }
+ }
+ }
+
+ is_expected.to include(cache_options)
+ end
+ end
+
+ context 'with empty cache' do
+ let(:attributes) do
+ {
+ name: 'rspec',
+ ref: 'master',
+ cache: {}
+ }
+ end
+
+ it { is_expected.to include(options: {}) }
+ end
end
describe '#bridge?' do
@@ -773,10 +869,4 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
end
end
end
-
- describe '#scoped_variables_hash' do
- subject { seed_build.scoped_variables_hash }
-
- it { is_expected.to eq(seed_build.to_resource.scoped_variables_hash) }
- end
end
diff --git a/spec/lib/gitlab/ci/status/composite_spec.rb b/spec/lib/gitlab/ci/status/composite_spec.rb
index 1725d954b92..857483a9e0a 100644
--- a/spec/lib/gitlab/ci/status/composite_spec.rb
+++ b/spec/lib/gitlab/ci/status/composite_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Status::Composite do
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
index 1baea13299b..45b59541ce6 100644
--- a/spec/lib/gitlab/ci/trace/stream_spec.rb
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
@@ -100,7 +102,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
describe '#append' do
shared_examples_for 'appends' do
it "truncates and append content" do
- stream.append("89", 4)
+ stream.append(+"89", 4)
stream.seek(0)
expect(stream.size).to eq(6)
@@ -108,7 +110,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
end
it 'appends in binary mode' do
- '๐Ÿ˜บ'.force_encoding('ASCII-8BIT').each_char.with_index do |byte, offset|
+ (+'๐Ÿ˜บ').force_encoding('ASCII-8BIT').each_char.with_index do |byte, offset|
stream.append(byte, offset)
end
@@ -154,7 +156,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
describe '#set' do
shared_examples_for 'sets' do
before do
- stream.set("8901")
+ stream.set(+"8901")
end
it "overwrite content" do
@@ -168,7 +170,7 @@ describe Gitlab::Ci::Trace::Stream, :clean_gitlab_redis_cache do
context 'when stream is StringIO' do
let(:stream) do
described_class.new do
- StringIO.new("12345678")
+ StringIO.new(+"12345678")
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index cb5ebde16d7..4b1c7483b11 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -108,6 +108,25 @@ module Gitlab
it { expect(subject[:interruptible]).to be_falsy }
end
+
+ it "returns interruptible when overridden for job" do
+ config = YAML.dump({ default: { interruptible: true },
+ rspec: { script: "rspec" } })
+
+ config_processor = Gitlab::Ci::YamlProcessor.new(config)
+
+ expect(config_processor.stage_builds_attributes("test").size).to eq(1)
+ expect(config_processor.stage_builds_attributes("test").first).to eq({
+ stage: "test",
+ stage_idx: 2,
+ name: "rspec",
+ options: { script: ["rspec"] },
+ interruptible: true,
+ allow_failure: false,
+ when: "on_success",
+ yaml_variables: []
+ })
+ end
end
describe 'retry entry' do
@@ -249,6 +268,108 @@ module Gitlab
end
end
+ describe '#workflow_attributes' do
+ context 'with disallowed workflow:variables' do
+ let(:config) do
+ <<-EOYML
+ workflow:
+ rules:
+ - if: $VAR == "value"
+ variables:
+ UNSUPPORTED: "unparsed"
+ EOYML
+ end
+
+ it 'parses the workflow:rules configuration' do
+ expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'workflow config contains unknown keys: variables')
+ end
+ end
+
+ context 'with rules and variables' do
+ let(:config) do
+ <<-EOYML
+ variables:
+ SUPPORTED: "parsed"
+
+ workflow:
+ rules:
+ - if: $VAR == "value"
+
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:rules configuration' do
+ expect(subject.workflow_attributes[:rules]).to contain_exactly({ if: '$VAR == "value"' })
+ end
+
+ it 'parses the root:variables as yaml_variables:' do
+ expect(subject.workflow_attributes[:yaml_variables])
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ end
+ end
+
+ context 'with rules and no variables' do
+ let(:config) do
+ <<-EOYML
+ workflow:
+ rules:
+ - if: $VAR == "value"
+
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:rules configuration' do
+ expect(subject.workflow_attributes[:rules]).to contain_exactly({ if: '$VAR == "value"' })
+ end
+
+ it 'parses the root:variables as yaml_variables:' do
+ expect(subject.workflow_attributes[:yaml_variables]).to eq([])
+ end
+ end
+
+ context 'with variables and no rules' do
+ let(:config) do
+ <<-EOYML
+ variables:
+ SUPPORTED: "parsed"
+
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:rules configuration' do
+ expect(subject.workflow_attributes[:rules]).to be_nil
+ end
+
+ it 'parses the root:variables as yaml_variables:' do
+ expect(subject.workflow_attributes[:yaml_variables])
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ end
+ end
+
+ context 'with no rules and no variables' do
+ let(:config) do
+ <<-EOYML
+ hello:
+ script: echo world
+ EOYML
+ end
+
+ it 'parses the workflow:rules configuration' do
+ expect(subject.workflow_attributes[:rules]).to be_nil
+ end
+
+ it 'parses the root:variables as yaml_variables:' do
+ expect(subject.workflow_attributes[:yaml_variables]).to eq([])
+ end
+ end
+ end
+
describe 'only / except policies validations' do
context 'when `only` has an invalid value' do
let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
@@ -330,7 +451,7 @@ module Gitlab
}
end
- it "return commands with scripts concencaced" do
+ it "return commands with scripts concatenated" do
expect(subject[:options][:before_script]).to eq(["global script"])
end
end
@@ -343,7 +464,7 @@ module Gitlab
}
end
- it "return commands with scripts concencaced" do
+ it "return commands with scripts concatenated" do
expect(subject[:options][:before_script]).to eq(["global script"])
end
end
@@ -356,21 +477,48 @@ module Gitlab
}
end
- it "return commands with scripts concencaced" do
+ it "return commands with scripts concatenated" do
expect(subject[:options][:before_script]).to eq(["local script"])
end
end
+
+ context 'when script is array of arrays of strings' do
+ let(:config) do
+ {
+ before_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return commands with scripts concatenated" do
+ expect(subject[:options][:before_script]).to eq(["global script", "echo 1", "ls", "pwd"])
+ end
+ end
end
describe "script" do
- let(:config) do
- {
- test: { script: ["script"] }
- }
+ context 'when script is array of strings' do
+ let(:config) do
+ {
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return commands with scripts concatenated" do
+ expect(subject[:options][:script]).to eq(["script"])
+ end
end
- it "return commands with scripts concencaced" do
- expect(subject[:options][:script]).to eq(["script"])
+ context 'when script is array of arrays of strings' do
+ let(:config) do
+ {
+ test: { script: [["script"], ["echo 1"], "ls"] }
+ }
+ end
+
+ it "return commands with scripts concatenated" do
+ expect(subject[:options][:script]).to eq(["script", "echo 1", "ls"])
+ end
end
end
@@ -413,6 +561,19 @@ module Gitlab
expect(subject[:options][:after_script]).to eq(["local after_script"])
end
end
+
+ context 'when script is array of arrays of strings' do
+ let(:config) do
+ {
+ after_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ test: { script: ["script"] }
+ }
+ end
+
+ it "return after_script in options" do
+ expect(subject[:options][:after_script]).to eq(["global script", "echo 1", "ls", "pwd"])
+ end
+ end
end
end
@@ -891,7 +1052,7 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
+ expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq(
paths: ["logs/", "binaries/"],
untracked: true,
key: 'key',
@@ -903,7 +1064,7 @@ module Gitlab
config = YAML.dump(
{
default: {
- cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' }
+ cache: { paths: ["logs/", "binaries/"], untracked: true, key: { files: ['file'] } }
},
rspec: {
script: "rspec"
@@ -913,33 +1074,79 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
+ expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq(
paths: ["logs/", "binaries/"],
untracked: true,
- key: 'key',
+ key: { files: ['file'] },
policy: 'pull-push'
)
end
- it "returns cache when defined in a job" do
+ it 'returns cache key when defined in a job' do
config = YAML.dump({
rspec: {
- cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
- script: "rspec"
+ cache: { paths: ['logs/', 'binaries/'], untracked: true, key: 'key' },
+ script: 'rspec'
}
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
- expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
- paths: ["logs/", "binaries/"],
+ expect(config_processor.stage_builds_attributes('test').size).to eq(1)
+ expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq(
+ paths: ['logs/', 'binaries/'],
untracked: true,
key: 'key',
policy: 'pull-push'
)
end
+ it 'returns cache files' do
+ config = YAML.dump(
+ rspec: {
+ cache: {
+ paths: ['logs/', 'binaries/'],
+ untracked: true,
+ key: { files: ['file'] }
+ },
+ script: 'rspec'
+ }
+ )
+
+ config_processor = Gitlab::Ci::YamlProcessor.new(config)
+
+ expect(config_processor.stage_builds_attributes('test').size).to eq(1)
+ expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq(
+ paths: ['logs/', 'binaries/'],
+ untracked: true,
+ key: { files: ['file'] },
+ policy: 'pull-push'
+ )
+ end
+
+ it 'returns cache files with prefix' do
+ config = YAML.dump(
+ rspec: {
+ cache: {
+ paths: ['logs/', 'binaries/'],
+ untracked: true,
+ key: { files: ['file'], prefix: 'prefix' }
+ },
+ script: 'rspec'
+ }
+ )
+
+ config_processor = Gitlab::Ci::YamlProcessor.new(config)
+
+ expect(config_processor.stage_builds_attributes('test').size).to eq(1)
+ expect(config_processor.stage_builds_attributes('test').first[:cache]).to eq(
+ paths: ['logs/', 'binaries/'],
+ untracked: true,
+ key: { files: ['file'], prefix: 'prefix' },
+ policy: 'pull-push'
+ )
+ end
+
it "overwrite cache when defined for a job and globally" do
config = YAML.dump({
cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
@@ -952,7 +1159,7 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
- expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
+ expect(config_processor.stage_builds_attributes("test").first[:cache]).to eq(
paths: ["test/"],
untracked: false,
key: 'local',
@@ -970,6 +1177,7 @@ module Gitlab
rspec: {
artifacts: {
paths: ["logs/", "binaries/"],
+ expose_as: "Exposed artifacts",
untracked: true,
name: "custom_name",
expire_in: "7d"
@@ -993,6 +1201,7 @@ module Gitlab
artifacts: {
name: "custom_name",
paths: ["logs/", "binaries/"],
+ expose_as: "Exposed artifacts",
untracked: true,
expire_in: "7d"
}
@@ -1251,7 +1460,7 @@ module Gitlab
end
end
- describe "Needs" do
+ describe "Job Needs" do
let(:needs) { }
let(:dependencies) { }
@@ -1259,6 +1468,7 @@ module Gitlab
{
build1: { stage: 'build', script: 'test' },
build2: { stage: 'build', script: 'test' },
+ parallel: { stage: 'build', script: 'test', parallel: 2 },
test1: { stage: 'test', script: 'test', needs: needs, dependencies: dependencies },
test2: { stage: 'test', script: 'test' },
deploy: { stage: 'test', script: 'test' }
@@ -1275,7 +1485,7 @@ module Gitlab
let(:needs) { %w(build1 build2) }
it "does create jobs with valid specification" do
- expect(subject.builds.size).to eq(5)
+ expect(subject.builds.size).to eq(7)
expect(subject.builds[0]).to eq(
stage: "build",
stage_idx: 1,
@@ -1287,16 +1497,11 @@ module Gitlab
allow_failure: false,
yaml_variables: []
)
- expect(subject.builds[2]).to eq(
+ expect(subject.builds[4]).to eq(
stage: "test",
stage_idx: 2,
name: "test1",
- options: {
- script: ["test"],
- # This does not make sense, there is a follow-up:
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/65569
- bridge_needs: %w[build1 build2]
- },
+ options: { script: ["test"] },
needs_attributes: [
{ name: "build1" },
{ name: "build2" }
@@ -1308,10 +1513,25 @@ module Gitlab
end
end
- context 'needs two builds defined as symbols' do
- let(:needs) { [:build1, :build2] }
+ context 'needs parallel job' do
+ let(:needs) { %w(parallel) }
- it { expect { subject }.not_to raise_error }
+ it "does create jobs with valid specification" do
+ expect(subject.builds.size).to eq(7)
+ expect(subject.builds[4]).to eq(
+ stage: "test",
+ stage_idx: 2,
+ name: "test1",
+ options: { script: ["test"] },
+ needs_attributes: [
+ { name: "parallel 1/2" },
+ { name: "parallel 2/2" }
+ ],
+ when: "on_success",
+ allow_failure: false,
+ yaml_variables: []
+ )
+ end
end
context 'undefined need' do
@@ -1545,28 +1765,42 @@ module Gitlab
config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "before_script config should be an array of strings")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "before_script config should be an array containing strings and arrays of strings")
end
it "returns errors if job before_script parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array of strings")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array containing strings and arrays of strings")
+ end
+
+ it "returns errors if job before_script parameter is multi-level nested array of strings" do
+ config = YAML.dump({ rspec: { script: "test", before_script: [["ls", ["pwd"]], "test"] } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:before_script config should be an array containing strings and arrays of strings")
end
it "returns errors if after_script parameter is invalid" do
config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "after_script config should be an array of strings")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "after_script config should be an array containing strings and arrays of strings")
end
it "returns errors if job after_script parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array of strings")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array containing strings and arrays of strings")
+ end
+
+ it "returns errors if job after_script parameter is multi-level nested array of strings" do
+ config = YAML.dump({ rspec: { script: "test", after_script: [["ls", ["pwd"]], "test"] } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:after_script config should be an array containing strings and arrays of strings")
end
it "returns errors if image parameter is invalid" do
@@ -1776,14 +2010,42 @@ module Gitlab
config = YAML.dump({ cache: { key: 1 }, rspec: { script: "test" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "cache:key config should be a string or symbol")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "cache:key should be a hash, a string or a symbol")
end
it "returns errors if job cache:key is not an a string" do
config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: 1 } } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:cache:key config should be a string or symbol")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:cache:key should be a hash, a string or a symbol")
+ end
+
+ it 'returns errors if job cache:key:files is not an array of strings' do
+ config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { files: [1] } } } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:files config should be an array of strings')
+ end
+
+ it 'returns errors if job cache:key:files is an empty array' do
+ config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { files: [] } } } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:files config requires at least 1 item')
+ end
+
+ it 'returns errors if job defines only cache:key:prefix' do
+ config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { prefix: 'prefix-key' } } } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key config missing required keys: files')
+ end
+
+ it 'returns errors if job cache:key:prefix is not an a string' do
+ config = YAML.dump({ types: %w(build test), rspec: { script: "test", cache: { key: { prefix: 1, files: ['file'] } } } })
+ expect do
+ Gitlab::Ci::YamlProcessor.new(config)
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:rspec:cache:key:prefix config should be a string or symbol')
end
it "returns errors if job cache:untracked is not an array of strings" do
diff --git a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
index 974cc2c4660..fc9792e16d7 100644
--- a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
+++ b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
@@ -21,11 +21,10 @@ describe Gitlab::Cleanup::OrphanJobArtifactFiles do
end
it 'errors when invalid niceness is given' do
+ allow(Gitlab::Utils).to receive(:which).with('ionice').and_return('/fake/ionice')
cleanup = described_class.new(logger: null_logger, niceness: 'FooBar')
- expect(null_logger).to receive(:error).with(/FooBar/)
-
- cleanup.run!
+ expect { cleanup.run! }.to raise_error('Invalid niceness')
end
it 'finds artifacts on disk' do
@@ -63,6 +62,8 @@ describe Gitlab::Cleanup::OrphanJobArtifactFiles do
def mock_artifacts_found(cleanup, *files)
mock = allow(cleanup).to receive(:find_artifacts)
- files.each { |file| mock.and_yield(file) }
+ # Because we shell out to run `find -L ...`, each file actually
+ # contains a trailing newline
+ files.each { |file| mock.and_yield("#{file}\n") }
end
end
diff --git a/spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb b/spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb
index 1eddf488c5d..b8ac8c5b95c 100644
--- a/spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb
+++ b/spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb
@@ -8,15 +8,28 @@ describe Gitlab::Cluster::Mixins::PumaCluster do
PUMA_STARTUP_TIMEOUT = 30
context 'when running Puma in Cluster-mode' do
- %i[USR1 USR2 INT HUP].each do |signal|
- it "for #{signal} does execute phased restart block" do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:signal, :exitstatus, :termsig) do
+ # executes phased restart block
+ :USR1 | 140 | nil
+ :USR2 | 140 | nil
+ :INT | 140 | nil
+ :HUP | 140 | nil
+
+ # does not execute phased restart block
+ :TERM | nil | 15
+ end
+
+ with_them do
+ it 'properly handles process lifecycle' do
with_puma(workers: 1) do |pid|
Process.kill(signal, pid)
child_pid, child_status = Process.wait2(pid)
expect(child_pid).to eq(pid)
- expect(child_status).to be_exited
- expect(child_status.exitstatus).to eq(140)
+ expect(child_status.exitstatus).to eq(exitstatus)
+ expect(child_status.termsig).to eq(termsig)
end
end
end
@@ -62,8 +75,12 @@ describe Gitlab::Cluster::Mixins::PumaCluster do
Puma::Cluster.prepend(#{described_class})
- Gitlab::Cluster::LifecycleEvents.on_before_phased_restart do
- exit(140)
+ mutex = Mutex.new
+
+ Gitlab::Cluster::LifecycleEvents.on_before_blackout_period do
+ mutex.synchronize do
+ exit(140)
+ end
end
# redirect stderr to stdout
diff --git a/spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb b/spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb
index 2b3a267991c..ebe019924d5 100644
--- a/spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb
+++ b/spec/lib/gitlab/cluster/mixins/unicorn_http_server_spec.rb
@@ -5,31 +5,30 @@ require 'spec_helper'
# For easier debugging set `UNICORN_DEBUG=1`
describe Gitlab::Cluster::Mixins::UnicornHttpServer do
- UNICORN_STARTUP_TIMEOUT = 10
+ UNICORN_STARTUP_TIMEOUT = 30
context 'when running Unicorn' do
- %i[USR2].each do |signal|
- it "for #{signal} does execute phased restart block" do
- with_unicorn(workers: 1) do |pid|
- Process.kill(signal, pid)
+ using RSpec::Parameterized::TableSyntax
- child_pid, child_status = Process.wait2(pid)
- expect(child_pid).to eq(pid)
- expect(child_status).to be_exited
- expect(child_status.exitstatus).to eq(140)
- end
- end
+ where(:signal, :exitstatus, :termsig) do
+ # executes phased restart block
+ :USR2 | 140 | nil
+ :QUIT | 140 | nil
+
+ # does not execute phased restart block
+ :INT | 0 | nil
+ :TERM | 0 | nil
end
- %i[QUIT TERM INT].each do |signal|
- it "for #{signal} does not execute phased restart block" do
+ with_them do
+ it 'properly handles process lifecycle' do
with_unicorn(workers: 1) do |pid|
Process.kill(signal, pid)
child_pid, child_status = Process.wait2(pid)
expect(child_pid).to eq(pid)
- expect(child_status).to be_exited
- expect(child_status.exitstatus).to eq(0)
+ expect(child_status.exitstatus).to eq(exitstatus)
+ expect(child_status.termsig).to eq(termsig)
end
end
end
@@ -74,8 +73,12 @@ describe Gitlab::Cluster::Mixins::UnicornHttpServer do
Unicorn::HttpServer.prepend(#{described_class})
- Gitlab::Cluster::LifecycleEvents.on_before_phased_restart do
- exit(140)
+ mutex = Mutex.new
+
+ Gitlab::Cluster::LifecycleEvents.on_before_blackout_period do
+ mutex.synchronize do
+ exit(140)
+ end
end
# redirect stderr to stdout
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
index a163de07967..9eee7e89062 100644
--- a/spec/lib/gitlab/cycle_analytics/events_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -129,7 +129,7 @@ describe 'cycle analytics events' do
end
end
- describe '#test_events' do
+ describe '#test_events', :sidekiq_might_not_need_inline do
let(:stage) { :test }
let(:merge_request) { MergeRequest.first }
@@ -234,7 +234,7 @@ describe 'cycle analytics events' do
end
end
- describe '#staging_events' do
+ describe '#staging_events', :sidekiq_might_not_need_inline do
let(:stage) { :staging }
let(:merge_request) { MergeRequest.first }
@@ -306,7 +306,7 @@ describe 'cycle analytics events' do
end
end
- describe '#production_events' do
+ describe '#production_events', :sidekiq_might_not_need_inline do
let(:stage) { :production }
let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb
index d5c2f7cc579..664009f140f 100644
--- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb
@@ -44,6 +44,14 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do
expect(subject.first[:value]).to eq(2)
end
end
+
+ context 'when `from` and `to` parameters are provided' do
+ subject { described_class.new(group, options: { from: 10.days.ago, to: Time.now, current_user: user }).data }
+
+ it 'finds issues from 5 days ago' do
+ expect(subject.first[:value]).to eq(2)
+ end
+ end
end
context 'with other projects' do
@@ -97,6 +105,14 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do
expect(subject.second[:value]).to eq(2)
end
end
+
+ context 'when `from` and `to` parameters are provided' do
+ subject { described_class.new(group, options: { from: 10.days.ago, to: Time.now, current_user: user }).data }
+
+ it 'finds deployments from 5 days ago' do
+ expect(subject.second[:value]).to eq(2)
+ end
+ end
end
context 'with other projects' do
diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
index e568ea633db..d4ab9bc225b 100644
--- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
@@ -71,7 +71,7 @@ describe Gitlab::CycleAnalytics::UsageData do
}
end
- it 'returns the aggregated usage data of every selected project' do
+ it 'returns the aggregated usage data of every selected project', :sidekiq_might_not_need_inline do
result = subject.to_json
expect(result).to have_key(:avg_cycle_analytics)
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 1696d3566ad..8056418e697 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -178,6 +178,7 @@ describe Gitlab::Danger::Helper do
'app/assets/foo' | :frontend
'app/views/foo' | :frontend
'public/foo' | :frontend
+ 'scripts/frontend/foo' | :frontend
'spec/javascripts/foo' | :frontend
'spec/frontend/bar' | :frontend
'vendor/assets/foo' | :frontend
@@ -193,10 +194,8 @@ describe Gitlab::Danger::Helper do
'app/models/foo' | :backend
'bin/foo' | :backend
'config/foo' | :backend
- 'danger/foo' | :backend
'lib/foo' | :backend
'rubocop/foo' | :backend
- 'scripts/foo' | :backend
'spec/foo' | :backend
'spec/foo/bar' | :backend
@@ -209,16 +208,24 @@ describe Gitlab::Danger::Helper do
'vendor/languages.yml' | :backend
'vendor/licenses.csv' | :backend
- 'Dangerfile' | :backend
'Gemfile' | :backend
'Gemfile.lock' | :backend
'Procfile' | :backend
'Rakefile' | :backend
'FOO_VERSION' | :backend
+ 'Dangerfile' | :engineering_productivity
+ 'danger/commit_messages/Dangerfile' | :engineering_productivity
+ 'ee/danger/commit_messages/Dangerfile' | :engineering_productivity
+ 'danger/commit_messages/' | :engineering_productivity
+ 'ee/danger/commit_messages/' | :engineering_productivity
'.gitlab-ci.yml' | :engineering_productivity
'.gitlab/ci/cng.gitlab-ci.yml' | :engineering_productivity
'.gitlab/ci/ee-specific-checks.gitlab-ci.yml' | :engineering_productivity
+ 'scripts/foo' | :engineering_productivity
+ 'lib/gitlab/danger/foo' | :engineering_productivity
+ 'ee/lib/gitlab/danger/foo' | :engineering_productivity
+
'lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml' | :backend
'ee/FOO_VERSION' | :unknown
diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb
index bd1c2b10dc8..35edfa08a63 100644
--- a/spec/lib/gitlab/danger/teammate_spec.rb
+++ b/spec/lib/gitlab/danger/teammate_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Danger::Teammate do
expect(subject.maintainer?(project, :frontend, labels)).to be_truthy
end
- context 'when labels contain Create and the category is test' do
+ context 'when labels contain devops::create and the category is test' do
let(:labels) { ['devops::create'] }
context 'when role is Test Automation Engineer, Create' do
@@ -79,6 +79,22 @@ describe Gitlab::Danger::Teammate do
it '#maintainer? returns false' do
expect(subject.maintainer?(project, :engineering_productivity, labels)).to be_falsey
end
+
+ context 'when capabilities include maintainer backend' do
+ let(:capabilities) { ['maintainer backend'] }
+
+ it '#maintainer? returns true' do
+ expect(subject.maintainer?(project, :engineering_productivity, labels)).to be_truthy
+ end
+ end
+
+ context 'when capabilities include trainee_maintainer backend' do
+ let(:capabilities) { ['trainee_maintainer backend'] }
+
+ it '#traintainer? returns true' do
+ expect(subject.traintainer?(project, :engineering_productivity, labels)).to be_truthy
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/data_builder/deployment_spec.rb b/spec/lib/gitlab/data_builder/deployment_spec.rb
index 0a6e2302b09..42d7329494d 100644
--- a/spec/lib/gitlab/data_builder/deployment_spec.rb
+++ b/spec/lib/gitlab/data_builder/deployment_spec.rb
@@ -35,5 +35,12 @@ describe Gitlab::DataBuilder::Deployment do
expect(data[:commit_url]).to eq(expected_commit_url)
expect(data[:commit_title]).to eq(commit.title)
end
+
+ it 'does not include the deployable URL when there is no deployable' do
+ deployment = create(:deployment, status: :failed, deployable: nil)
+ data = described_class.build(deployment)
+
+ expect(data[:deployable_url]).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb
index 58509b69463..cbc03fc38eb 100644
--- a/spec/lib/gitlab/data_builder/push_spec.rb
+++ b/spec/lib/gitlab/data_builder/push_spec.rb
@@ -57,6 +57,32 @@ describe Gitlab::DataBuilder::Push do
include_examples 'deprecated repository hook data'
end
+ describe '.sample_data' do
+ let(:data) { described_class.sample_data }
+
+ it { expect(data).to be_a(Hash) }
+ it { expect(data[:before]).to eq('95790bf891e76fee5e1747ab589903a6a1f80f22') }
+ it { expect(data[:after]).to eq('da1560886d4f094c3e6c9ef40349f7d38b5d27d7') }
+ it { expect(data[:ref]).to eq('refs/heads/master') }
+ it { expect(data[:project_id]).to eq(15) }
+ it { expect(data[:commits].size).to eq(1) }
+ it { expect(data[:total_commits_count]).to eq(1) }
+ it 'contains project data' do
+ expect(data[:project]).to be_a(Hash)
+ expect(data[:project][:id]).to eq(15)
+ expect(data[:project][:name]).to eq('gitlab')
+ expect(data[:project][:description]).to eq('')
+ expect(data[:project][:web_url]).to eq('http://test.example.com/gitlab/gitlab')
+ expect(data[:project][:avatar_url]).to eq('https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80')
+ expect(data[:project][:git_http_url]).to eq('http://test.example.com/gitlab/gitlab.git')
+ expect(data[:project][:git_ssh_url]).to eq('git@test.example.com:gitlab/gitlab.git')
+ expect(data[:project][:namespace]).to eq('gitlab')
+ expect(data[:project][:visibility_level]).to eq(0)
+ expect(data[:project][:path_with_namespace]).to eq('gitlab/gitlab')
+ expect(data[:project][:default_branch]).to eq('master')
+ end
+ end
+
describe '.build' do
let(:data) do
described_class.build(
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 49f92f14559..449eee7a371 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -142,7 +142,6 @@ describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:transaction_open?).and_return(false)
allow(model).to receive(:index_exists?).and_return(true)
allow(model).to receive(:disable_statement_timeout).and_call_original
- allow(model).to receive(:supports_drop_index_concurrently?).and_return(true)
end
describe 'by column name' do
diff --git a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
index aab6fbcbbd1..5b1a17e734d 100644
--- a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
+++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
@@ -164,15 +164,6 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do
end
it_behaves_like 'has prometheus service', 'http://localhost:9090'
-
- it 'does not overwrite the existing whitelist' do
- application_setting.outbound_local_requests_whitelist = ['example.com']
-
- expect(result[:status]).to eq(:success)
- expect(application_setting.outbound_local_requests_whitelist).to contain_exactly(
- 'example.com', 'localhost'
- )
- end
end
context 'with non default prometheus address' do
diff --git a/spec/lib/gitlab/devise_failure_spec.rb b/spec/lib/gitlab/devise_failure_spec.rb
new file mode 100644
index 00000000000..eee05c7befd
--- /dev/null
+++ b/spec/lib/gitlab/devise_failure_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::DeviseFailure do
+ let(:env) do
+ {
+ 'REQUEST_URI' => 'http://test.host/',
+ 'HTTP_HOST' => 'test.host',
+ 'REQUEST_METHOD' => 'GET',
+ 'warden.options' => { scope: :user },
+ 'rack.session' => {},
+ 'rack.session.options' => {},
+ 'rack.input' => "",
+ 'warden' => OpenStruct.new(message: nil)
+ }
+ end
+
+ let(:response) { described_class.call(env).to_a }
+ let(:request) { ActionDispatch::Request.new(env) }
+
+ context 'When redirecting' do
+ it 'sets the expire_after key' do
+ response
+
+ expect(env['rack.session.options']).to have_key(:expire_after)
+ end
+
+ it 'returns to the default redirect location' do
+ expect(response.first).to eq(302)
+ expect(request.flash[:alert]).to eq('You need to sign in or sign up before continuing.')
+ expect(response.second['Location']).to eq('http://test.host/users/sign_in')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb b/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb
index 35aa663b0a5..a65214fab61 100644
--- a/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb
+++ b/spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Email::Hook::SmimeSignatureInterceptor do
diff --git a/spec/lib/gitlab/exclusive_lease_helpers_spec.rb b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
index c3b706fc538..747fe369c78 100644
--- a/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
+++ b/spec/lib/gitlab/exclusive_lease_helpers_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state do
diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb
index aed7d8d81ce..0739f622af5 100644
--- a/spec/lib/gitlab/exclusive_lease_spec.rb
+++ b/spec/lib/gitlab/exclusive_lease_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index 2e5fd16d370..9be6ace3be5 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -2,81 +2,194 @@
require 'spec_helper'
-describe Gitlab::Experimentation::ControllerConcern, type: :controller do
- controller(ApplicationController) do
- include Gitlab::Experimentation::ControllerConcern
+describe Gitlab::Experimentation do
+ before do
+ stub_const('Gitlab::Experimentation::EXPERIMENTS', {
+ test_experiment: {
+ feature_toggle: feature_toggle,
+ environment: environment,
+ enabled_ratio: enabled_ratio,
+ tracking_category: 'Team'
+ }
+ })
- def index
- head :ok
- end
+ stub_feature_flags(feature_toggle => true)
end
- describe '#set_experimentation_subject_id_cookie' do
- before do
- get :index
+ let(:feature_toggle) { :test_experiment_toggle }
+ let(:environment) { Rails.env.test? }
+ let(:enabled_ratio) { 0.1 }
+
+ describe Gitlab::Experimentation::ControllerConcern, type: :controller do
+ controller(ApplicationController) do
+ include Gitlab::Experimentation::ControllerConcern
+
+ def index
+ head :ok
+ end
end
- context 'cookie is present' do
+ describe '#set_experimentation_subject_id_cookie' do
before do
- cookies[:experimentation_subject_id] = 'test'
+ get :index
end
- it 'does not change the cookie' do
- expect(cookies[:experimentation_subject_id]).to eq 'test'
+ context 'cookie is present' do
+ before do
+ cookies[:experimentation_subject_id] = 'test'
+ end
+
+ it 'does not change the cookie' do
+ expect(cookies[:experimentation_subject_id]).to eq 'test'
+ end
end
- end
- context 'cookie is not present' do
- it 'sets a permanent signed cookie' do
- expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
+ context 'cookie is not present' do
+ it 'sets a permanent signed cookie' do
+ expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
+ end
end
end
- end
- describe '#experiment_enabled?' do
- context 'cookie is not present' do
- it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of nil' do
- expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil)
- controller.experiment_enabled?(:test_experiment)
+ describe '#experiment_enabled?' do
+ context 'cookie is not present' do
+ it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of nil' do
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, nil) # rubocop:disable RSpec/DescribedClass
+ controller.experiment_enabled?(:test_experiment)
+ end
+ end
+
+ context 'cookie is present' do
+ before do
+ cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
+ get :index
+ end
+
+ it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
+ # 'abcd1234'.hex % 100 = 76
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, 76) # rubocop:disable RSpec/DescribedClass
+ controller.experiment_enabled?(:test_experiment)
+ end
+ end
+
+ describe 'URL parameter to force enable experiment' do
+ it 'returns true' do
+ get :index, params: { force_experiment: :test_experiment }
+
+ expect(controller.experiment_enabled?(:test_experiment)).to be_truthy
+ end
end
end
- context 'cookie is present' do
- before do
- cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
- get :index
+ describe '#track_experiment_event' do
+ context 'when the experiment is enabled' do
+ before do
+ stub_experiment(test_experiment: true)
+ end
+
+ context 'the user is part of the experimental group' do
+ before do
+ stub_experiment_for_user(test_experiment: true)
+ end
+
+ it 'tracks the event with the right parameters' do
+ expect(Gitlab::Tracking).to receive(:event).with(
+ 'Team',
+ 'start',
+ label: nil,
+ property: 'experimental_group'
+ )
+ controller.track_experiment_event(:test_experiment, 'start')
+ end
+ end
+
+ context 'the user is part of the control group' do
+ before do
+ stub_experiment_for_user(test_experiment: false)
+ end
+
+ it 'tracks the event with the right parameters' do
+ expect(Gitlab::Tracking).to receive(:event).with(
+ 'Team',
+ 'start',
+ label: nil,
+ property: 'control_group'
+ )
+ controller.track_experiment_event(:test_experiment, 'start')
+ end
+ end
end
- it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
- # 'abcd1234'.hex % 100 = 76
- expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, 76)
- controller.experiment_enabled?(:test_experiment)
+ context 'when the experiment is disabled' do
+ before do
+ stub_experiment(test_experiment: false)
+ end
+
+ it 'does not track the event' do
+ expect(Gitlab::Tracking).not_to receive(:event)
+ controller.track_experiment_event(:test_experiment, 'start')
+ end
end
end
- end
-end
-describe Gitlab::Experimentation do
- before do
- stub_const('Gitlab::Experimentation::EXPERIMENTS', {
- test_experiment: {
- feature_toggle: feature_toggle,
- environment: environment,
- enabled_ratio: enabled_ratio
- }
- })
+ describe '#frontend_experimentation_tracking_data' do
+ context 'when the experiment is enabled' do
+ before do
+ stub_experiment(test_experiment: true)
+ end
- stub_feature_flags(feature_toggle => true)
- end
+ context 'the user is part of the experimental group' do
+ before do
+ stub_experiment_for_user(test_experiment: true)
+ end
+
+ it 'pushes the right parameters to gon' do
+ controller.frontend_experimentation_tracking_data(:test_experiment, 'start')
+ expect(Gon.tracking_data).to eq(
+ {
+ category: 'Team',
+ action: 'start',
+ label: nil,
+ property: 'experimental_group'
+ }
+ )
+ end
+ end
- let(:feature_toggle) { :test_experiment_toggle }
- let(:environment) { Rails.env.test? }
- let(:enabled_ratio) { 0.1 }
+ context 'the user is part of the control group' do
+ before do
+ allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
+ end
+
+ it 'pushes the right parameters to gon' do
+ controller.frontend_experimentation_tracking_data(:test_experiment, 'start')
+ expect(Gon.tracking_data).to eq(
+ {
+ category: 'Team',
+ action: 'start',
+ label: nil,
+ property: 'control_group'
+ }
+ )
+ end
+ end
+ end
- describe '.enabled?' do
- subject { described_class.enabled?(:test_experiment, experimentation_subject_index) }
+ context 'when the experiment is disabled' do
+ before do
+ stub_experiment(test_experiment: false)
+ end
- let(:experimentation_subject_index) { 9 }
+ it 'does not push data to gon' do
+ expect(Gon.method_defined?(:tracking_data)).to be_falsey
+ controller.track_experiment_event(:test_experiment, 'start')
+ end
+ end
+ end
+ end
+
+ describe '.enabled?' do
+ subject { described_class.enabled?(:test_experiment) }
context 'feature toggle is enabled, we are on the right environment and we are selected' do
it { is_expected.to be_truthy }
@@ -84,7 +197,7 @@ describe Gitlab::Experimentation do
describe 'experiment is not defined' do
it 'returns false' do
- expect(described_class.enabled?(:missing_experiment, experimentation_subject_index)).to be_falsey
+ expect(described_class.enabled?(:missing_experiment)).to be_falsey
end
end
@@ -127,30 +240,52 @@ describe Gitlab::Experimentation do
it { is_expected.to be_falsey }
end
end
+ end
- describe 'enabled ratio' do
- context 'enabled ratio is not set' do
- let(:enabled_ratio) { nil }
+ describe '.enabled_for_user?' do
+ subject { described_class.enabled_for_user?(:test_experiment, experimentation_subject_index) }
- it { is_expected.to be_falsey }
+ let(:experimentation_subject_index) { 9 }
+
+ context 'experiment is disabled' do
+ before do
+ allow(described_class).to receive(:enabled?).and_return(false)
end
- context 'experimentation_subject_index is not set' do
- let(:experimentation_subject_index) { nil }
+ it { is_expected.to be_falsey }
+ end
- it { is_expected.to be_falsey }
+ context 'experiment is enabled' do
+ before do
+ allow(described_class).to receive(:enabled?).and_return(true)
end
- context 'experimentation_subject_index is an empty string' do
- let(:experimentation_subject_index) { '' }
+ it { is_expected.to be_truthy }
+
+ context 'enabled ratio is not set' do
+ let(:enabled_ratio) { nil }
it { is_expected.to be_falsey }
end
- context 'experimentation_subject_index outside enabled ratio' do
- let(:experimentation_subject_index) { 11 }
+ describe 'experimentation_subject_index' do
+ context 'experimentation_subject_index is not set' do
+ let(:experimentation_subject_index) { nil }
- it { is_expected.to be_falsey }
+ it { is_expected.to be_falsey }
+ end
+
+ context 'experimentation_subject_index is an empty string' do
+ let(:experimentation_subject_index) { '' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'experimentation_subject_index outside enabled ratio' do
+ let(:experimentation_subject_index) { 11 }
+
+ it { is_expected.to be_falsey }
+ end
end
end
end
diff --git a/spec/lib/gitlab/external_authorization/access_spec.rb b/spec/lib/gitlab/external_authorization/access_spec.rb
index 5dc2521b310..8a08b2a6275 100644
--- a/spec/lib/gitlab/external_authorization/access_spec.rb
+++ b/spec/lib/gitlab/external_authorization/access_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/external_authorization/cache_spec.rb b/spec/lib/gitlab/external_authorization/cache_spec.rb
index 58e7d626707..1f217249f97 100644
--- a/spec/lib/gitlab/external_authorization/cache_spec.rb
+++ b/spec/lib/gitlab/external_authorization/cache_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization::Cache, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/external_authorization/client_spec.rb b/spec/lib/gitlab/external_authorization/client_spec.rb
index a87f50b4586..a17d933e3bb 100644
--- a/spec/lib/gitlab/external_authorization/client_spec.rb
+++ b/spec/lib/gitlab/external_authorization/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization::Client do
diff --git a/spec/lib/gitlab/external_authorization/logger_spec.rb b/spec/lib/gitlab/external_authorization/logger_spec.rb
index 81f1b2390e6..380e765309c 100644
--- a/spec/lib/gitlab/external_authorization/logger_spec.rb
+++ b/spec/lib/gitlab/external_authorization/logger_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization::Logger do
diff --git a/spec/lib/gitlab/external_authorization/response_spec.rb b/spec/lib/gitlab/external_authorization/response_spec.rb
index 43211043eca..e1f6e9ac1fa 100644
--- a/spec/lib/gitlab/external_authorization/response_spec.rb
+++ b/spec/lib/gitlab/external_authorization/response_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization::Response do
diff --git a/spec/lib/gitlab/external_authorization_spec.rb b/spec/lib/gitlab/external_authorization_spec.rb
index c45fcca3f06..97055e7b3f9 100644
--- a/spec/lib/gitlab/external_authorization_spec.rb
+++ b/spec/lib/gitlab/external_authorization_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ExternalAuthorization, :request_store do
diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb
index c81cb83d9f4..6a872185713 100644
--- a/spec/lib/gitlab/fake_application_settings_spec.rb
+++ b/spec/lib/gitlab/fake_application_settings_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::FakeApplicationSettings do
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 617c0f88a89..884425dab3b 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Gitlab::Favicon, :request_store do
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index 4ba9094b24e..f3a9f706e86 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::FileDetector do
diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb
index b49c5817131..7ea9d43c9f7 100644
--- a/spec/lib/gitlab/file_finder_spec.rb
+++ b/spec/lib/gitlab/file_finder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::FileFinder do
@@ -6,11 +8,11 @@ describe Gitlab::FileFinder do
subject { described_class.new(project, project.default_branch) }
it_behaves_like 'file finder' do
- let(:expected_file_by_name) { 'files/images/wm.svg' }
+ let(:expected_file_by_path) { 'files/images/wm.svg' }
let(:expected_file_by_content) { 'CHANGELOG' }
end
- it 'filters by name' do
+ it 'filters by filename' do
results = subject.find('files filename:wm.svg')
expect(results.count).to eq(1)
diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb
index dcd1a2d9813..676511211c8 100644
--- a/spec/lib/gitlab/fogbugz_import/client_spec.rb
+++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::FogbugzImport::Client do
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 790b0428d19..026fd1fedde 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Gfm::ReferenceRewriter do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index eef3b9de476..5a930d44dcb 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Gfm::UploadsRewriter do
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 23651e3d7f2..cdab7127748 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -428,7 +428,9 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
- shared_examples 'extracting commit signature' do
+ describe '.extract_signature_lazily' do
+ subject { described_class.extract_signature_lazily(repository, commit_id).itself }
+
context 'when the commit is signed' do
let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
@@ -492,10 +494,8 @@ describe Gitlab::Git::Commit, :seed_helper do
expect { subject }.to raise_error(ArgumentError)
end
end
- end
- describe '.extract_signature_lazily' do
- describe 'loading signatures in batch once' do
+ context 'when loading signatures in batch once' do
it 'fetches signatures in batch once' do
commit_ids = %w[0b4bc9a49b562e85de7cc9e834518ea6828729b9 4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6]
signatures = commit_ids.map do |commit_id|
@@ -516,16 +516,6 @@ describe Gitlab::Git::Commit, :seed_helper do
2.times { signatures.each(&:itself) }
end
end
-
- subject { described_class.extract_signature_lazily(repository, commit_id).itself }
-
- it_behaves_like 'extracting commit signature'
- end
-
- describe '.extract_signature' do
- subject { described_class.extract_signature(repository, commit_id) }
-
- it_behaves_like 'extracting commit signature'
end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 81dc96b538a..f74cc5623c9 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitAccess do
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 6ba65b56618..99c9369a2b9 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitAccessWiki do
diff --git a/spec/lib/gitlab/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb
index b63389af29f..1531317c514 100644
--- a/spec/lib/gitlab/git_ref_validator_spec.rb
+++ b/spec/lib/gitlab/git_ref_validator_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitRefValidator do
diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb
index 505bc470644..fbc49e05c37 100644
--- a/spec/lib/gitlab/git_spec.rb
+++ b/spec/lib/gitlab/git_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git do
diff --git a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
index a2770ef2fe4..887a6baf659 100644
--- a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::BlobService do
diff --git a/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
index 742b2872c40..e88b86c71f2 100644
--- a/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::BlobsStitcher do
diff --git a/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb b/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
index c42332dc27b..c6c7fa1c38a 100644
--- a/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::CleanupService do
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 71489adb373..1abdabe17bb 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::CommitService do
diff --git a/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
index a3602463756..db734b1c129 100644
--- a/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/conflict_files_stitcher_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::ConflictFilesStitcher do
diff --git a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
index 52630ba0223..f19bcae2470 100644
--- a/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/conflicts_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::ConflictsService do
diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb
index ec7ab2fdedb..d86497da7f5 100644
--- a/spec/lib/gitlab/gitaly_client/diff_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::Diff do
diff --git a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
index cd3242b9326..c9d42ad32cf 100644
--- a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::DiffStitcher do
diff --git a/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb b/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
index 2c7e5eb5787..615bc80fff2 100644
--- a/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/health_check_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::HealthCheckService do
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index f38b8d31237..d4337c51279 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::OperationService do
@@ -209,10 +211,12 @@ describe Gitlab::GitalyClient::OperationService do
end
context 'when a create_tree_error is present' do
- let(:response) { response_class.new(create_tree_error: "something failed") }
+ let(:response) { response_class.new(create_tree_error: "something failed", create_tree_error_code: 'EMPTY') }
it 'raises a CreateTreeError' do
- expect { subject }.to raise_error(Gitlab::Git::Repository::CreateTreeError, "something failed")
+ expect { subject }.to raise_error(Gitlab::Git::Repository::CreateTreeError) do |error|
+ expect(error.error_code).to eq(:empty)
+ 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 0bb6e582159..2b4fe2ea5c0 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::RefService do
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
index d5508dbff5d..929ff5dee5d 100644
--- a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::RemoteService do
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index f4b73931f21..503ac57ade6 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::RepositoryService do
diff --git a/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
index 2f83e5a5221..a6b29489df3 100644
--- a/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::StorageSettings do
diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb
index 78a5e195ad1..f31b7c349ff 100644
--- a/spec/lib/gitlab/gitaly_client/util_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/util_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::Util do
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index 4fa8e97aca0..cb04f9a1637 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitalyClient::WikiService do
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index b8df9ad642a..b6c0c0ad523 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
# We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want
@@ -399,6 +401,8 @@ describe Gitlab::GitalyClient do
context 'when the request store is active', :request_store do
it 'records call details if a RPC is called' do
+ expect(described_class).to receive(:measure_timings).and_call_original
+
gitaly_server.server_version
expect(described_class.list_call_details).not_to be_empty
diff --git a/spec/lib/gitlab/github_import/bulk_importing_spec.rb b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
index 91229d9c7d4..3266ec4ab50 100644
--- a/spec/lib/gitlab/github_import/bulk_importing_spec.rb
+++ b/spec/lib/gitlab/github_import/bulk_importing_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::BulkImporting do
diff --git a/spec/lib/gitlab/github_import/caching_spec.rb b/spec/lib/gitlab/github_import/caching_spec.rb
index 70ecdc16da1..18c3e382532 100644
--- a/spec/lib/gitlab/github_import/caching_spec.rb
+++ b/spec/lib/gitlab/github_import/caching_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Caching, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 5b2642d9473..3b269d64b07 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Client do
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 1568c657a1e..484458289af 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::DiffNoteImporter do
diff --git a/spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb
index 4713c6795bb..23ed21294e3 100644
--- a/spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::DiffNotesImporter do
diff --git a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
index 665b31ef244..399e2d9a563 100644
--- a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssueAndLabelLinksImporter do
diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
index dab5767ece1..a003ad7e091 100644
--- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
index e237e79e94b..8920ef9fedb 100644
--- a/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issues_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::IssuesImporter do
diff --git a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
index e2a71e78574..19d40b2f380 100644
--- a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LabelLinksImporter do
diff --git a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
index 156ef96a0fa..2dcf1433154 100644
--- a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb
index 8fd328d9c1e..a02b620f131 100644
--- a/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LfsObjectImporter do
diff --git a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
index 50442552eee..bec039a48eb 100644
--- a/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
diff --git a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
index 120a07ff2b3..eaf63e0e11b 100644
--- a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
index 9bdcc42be19..d2b8ba186c8 100644
--- a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::NoteImporter do
diff --git a/spec/lib/gitlab/github_import/importer/notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/notes_importer_spec.rb
index f046d13f879..128f8f95fa0 100644
--- a/spec/lib/gitlab/github_import/importer/notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/notes_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::NotesImporter do
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 8331f0b6bc7..50c27e7f4b7 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redis_cache do
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 c51985f00a2..e2d810d5ddc 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
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::PullRequestsImporter do
diff --git a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
index 6a31c57a73d..f8d53208619 100644
--- a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::ReleasesImporter do
diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
index 705df1f4fe7..c65b28fafbf 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Importer::RepositoryImporter do
diff --git a/spec/lib/gitlab/github_import/issuable_finder_spec.rb b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
index da69911812a..b8a6feb6c73 100644
--- a/spec/lib/gitlab/github_import/issuable_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/label_finder_spec.rb b/spec/lib/gitlab/github_import/label_finder_spec.rb
index 8ba766944d6..039ae27ad57 100644
--- a/spec/lib/gitlab/github_import/label_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/label_finder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::LabelFinder, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/markdown_text_spec.rb b/spec/lib/gitlab/github_import/markdown_text_spec.rb
index 1ff5b9d66b3..a1216db7aac 100644
--- a/spec/lib/gitlab/github_import/markdown_text_spec.rb
+++ b/spec/lib/gitlab/github_import/markdown_text_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::MarkdownText do
diff --git a/spec/lib/gitlab/github_import/milestone_finder_spec.rb b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
index dff931a2fe8..407e2e67ec9 100644
--- a/spec/lib/gitlab/github_import/milestone_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/page_counter_spec.rb b/spec/lib/gitlab/github_import/page_counter_spec.rb
index c2613a9a415..87f3ce45fd3 100644
--- a/spec/lib/gitlab/github_import/page_counter_spec.rb
+++ b/spec/lib/gitlab/github_import/page_counter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::PageCounter, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import/parallel_importer_spec.rb b/spec/lib/gitlab/github_import/parallel_importer_spec.rb
index ecab64a372a..a9b7d3d388c 100644
--- a/spec/lib/gitlab/github_import/parallel_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::ParallelImporter do
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index 98205d3ee25..f4d107e3dce 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::ParallelScheduling do
diff --git a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
index 7b0a1ea4948..e743a87cdd1 100644
--- a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::DiffNote do
diff --git a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
index 15de0fe49ff..e3b48df4ae9 100644
--- a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::ExposeAttribute do
diff --git a/spec/lib/gitlab/github_import/representation/issue_spec.rb b/spec/lib/gitlab/github_import/representation/issue_spec.rb
index 99330ce42cb..741a912e53b 100644
--- a/spec/lib/gitlab/github_import/representation/issue_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/issue_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::Issue do
diff --git a/spec/lib/gitlab/github_import/representation/note_spec.rb b/spec/lib/gitlab/github_import/representation/note_spec.rb
index f2c1c66b357..a171a38bc9e 100644
--- a/spec/lib/gitlab/github_import/representation/note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/note_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::Note do
diff --git a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
index d478e5ae899..b6dcd098c9c 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::PullRequest do
diff --git a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
index c296aa0a45b..9c47349b376 100644
--- a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::ToHash do
diff --git a/spec/lib/gitlab/github_import/representation/user_spec.rb b/spec/lib/gitlab/github_import/representation/user_spec.rb
index 4e63e8ea568..a7ad6bda3ad 100644
--- a/spec/lib/gitlab/github_import/representation/user_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/user_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation::User do
diff --git a/spec/lib/gitlab/github_import/representation_spec.rb b/spec/lib/gitlab/github_import/representation_spec.rb
index 0b0610817b0..76753a0ff21 100644
--- a/spec/lib/gitlab/github_import/representation_spec.rb
+++ b/spec/lib/gitlab/github_import/representation_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::Representation do
diff --git a/spec/lib/gitlab/github_import/sequential_importer_spec.rb b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
index 05d3243f806..8b1e8fbf3b7 100644
--- a/spec/lib/gitlab/github_import/sequential_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::SequentialImporter do
diff --git a/spec/lib/gitlab/github_import/user_finder_spec.rb b/spec/lib/gitlab/github_import/user_finder_spec.rb
index 29f4c00d9c7..74b5c1c52cd 100644
--- a/spec/lib/gitlab/github_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/user_finder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
diff --git a/spec/lib/gitlab/github_import_spec.rb b/spec/lib/gitlab/github_import_spec.rb
index 496244c91bf..c3ddac01c87 100644
--- a/spec/lib/gitlab/github_import_spec.rb
+++ b/spec/lib/gitlab/github_import_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GithubImport do
diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb
index d4b6c629659..3290bef8aa5 100644
--- a/spec/lib/gitlab/gl_repository_spec.rb
+++ b/spec/lib/gitlab/gl_repository_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe ::Gitlab::GlRepository do
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 1dfca0b056c..da307754243 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
verification_status: 'verified'
end
- it 'assigns the gpg key to the signature when the missing gpg key is added' do
+ it 'assigns the gpg key to the signature when the missing gpg key is added', :sidekiq_might_not_need_inline do
# InvalidGpgSignatureUpdater is called by the after_create hook
gpg_key = create :gpg_key,
key: GpgHelpers::User1.public_key,
@@ -86,7 +86,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
verification_status: 'unknown_key'
end
- it 'updates the signature to being valid when the missing gpg key is added' do
+ it 'updates the signature to being valid when the missing gpg key is added', :sidekiq_might_not_need_inline do
# InvalidGpgSignatureUpdater is called by the after_create hook
gpg_key = create :gpg_key,
key: GpgHelpers::User1.public_key,
@@ -133,7 +133,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
verification_status: 'unknown_key'
end
- it 'updates the signature to being valid when the user updates the email address' do
+ it 'updates the signature to being valid when the user updates the email address', :sidekiq_might_not_need_inline do
gpg_key = create :gpg_key,
key: GpgHelpers::User1.public_key,
user: user
@@ -152,7 +152,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
)
end
- it 'keeps the signature at being invalid when the changed email address is still unrelated' do
+ it 'keeps the signature at being invalid when the changed email address is still unrelated', :sidekiq_might_not_need_inline do
gpg_key = create :gpg_key,
key: GpgHelpers::User1.public_key,
user: user
@@ -192,7 +192,7 @@ RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
verification_status: 'unknown_key'
end
- it 'updates the signature to being valid when the missing gpg key is added' do
+ it 'updates the signature to being valid when the missing gpg key is added', :sidekiq_might_not_need_inline do
# InvalidGpgSignatureUpdater is called by the after_create hook
gpg_key = create(:gpg_key, key: GpgHelpers::User3.public_key, user: user)
subkey = gpg_key.subkeys.last
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index 77d318c9b23..52d6a86f7d0 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Gpg do
@@ -63,7 +65,7 @@ describe Gitlab::Gpg do
it 'downcases the email' do
public_key = double(:key)
fingerprints = double(:fingerprints)
- uid = double(:uid, name: 'Nannie Bernhard', email: 'NANNIE.BERNHARD@EXAMPLE.COM')
+ uid = double(:uid, name: +'Nannie Bernhard', email: +'NANNIE.BERNHARD@EXAMPLE.COM')
raw_key = double(:raw_key, uids: [uid])
allow(Gitlab::Gpg::CurrentKeyChain).to receive(:fingerprints_from_key).with(public_key).and_return(fingerprints)
allow(GPGME::Key).to receive(:find).with(:public, anything).and_return([raw_key])
@@ -78,8 +80,8 @@ describe Gitlab::Gpg do
it 'rejects non UTF-8 names and addresses' do
public_key = double(:key)
fingerprints = double(:fingerprints)
- email = "\xEEch@test.com".force_encoding('ASCII-8BIT')
- uid = double(:uid, name: 'Test User', email: email)
+ email = (+"\xEEch@test.com").force_encoding('ASCII-8BIT')
+ uid = double(:uid, name: +'Test User', email: email)
raw_key = double(:raw_key, uids: [uid])
allow(Gitlab::Gpg::CurrentKeyChain).to receive(:fingerprints_from_key).with(public_key).and_return(fingerprints)
allow(GPGME::Key).to receive(:find).with(:public, anything).and_return([raw_key])
@@ -139,6 +141,96 @@ describe Gitlab::Gpg do
end
end.not_to raise_error
end
+
+ it 'keeps track of created and removed keychains in counters' do
+ created = Gitlab::Metrics.counter(:gpg_tmp_keychains_created_total, 'The number of temporary GPG keychains')
+ removed = Gitlab::Metrics.counter(:gpg_tmp_keychains_removed_total, 'The number of temporary GPG keychains')
+
+ initial_created = created.get
+ initial_removed = removed.get
+
+ described_class.using_tmp_keychain do
+ expect(created.get).to eq(initial_created + 1)
+ expect(removed.get).to eq(initial_removed)
+ end
+
+ expect(removed.get).to eq(initial_removed + 1)
+ end
+
+ it 'cleans up the tmp directory after finishing' do
+ tmp_directory = nil
+
+ described_class.using_tmp_keychain do
+ tmp_directory = described_class.current_home_dir
+ expect(File.exist?(tmp_directory)).to be true
+ end
+
+ expect(tmp_directory).not_to be_nil
+ expect(File.exist?(tmp_directory)).to be false
+ end
+
+ it 'does not fail if the homedir was deleted while running' do
+ expect do
+ described_class.using_tmp_keychain do
+ FileUtils.remove_entry(described_class.current_home_dir)
+ end
+ end.not_to raise_error
+ end
+
+ shared_examples 'multiple deletion attempts of the tmp-dir' do |seconds|
+ let(:tmp_dir) do
+ tmp_dir = Dir.mktmpdir
+ allow(Dir).to receive(:mktmpdir).and_return(tmp_dir)
+ tmp_dir
+ end
+
+ before do
+ # Stub all the other calls for `remove_entry`
+ allow(FileUtils).to receive(:remove_entry).with(any_args).and_call_original
+ end
+
+ it "tries for #{seconds}" do
+ expect(Retriable).to receive(:retriable).with(a_hash_including(max_elapsed_time: seconds))
+
+ described_class.using_tmp_keychain {}
+ end
+
+ it 'tries at least 2 times to remove the tmp dir before raising', :aggregate_failures do
+ expect(Retriable).to receive(:sleep).at_least(2).times
+ expect(FileUtils).to receive(:remove_entry).with(tmp_dir).at_least(2).times.and_raise('Deletion failed')
+
+ expect { described_class.using_tmp_keychain { } }.to raise_error(described_class::CleanupError)
+ end
+
+ it 'does not attempt multiple times when the deletion succeeds' do
+ expect(Retriable).to receive(:sleep).once
+ expect(FileUtils).to receive(:remove_entry).with(tmp_dir).once.and_raise('Deletion failed')
+ expect(FileUtils).to receive(:remove_entry).with(tmp_dir).and_call_original
+
+ expect { described_class.using_tmp_keychain { } }.not_to raise_error
+
+ expect(File.exist?(tmp_dir)).to be false
+ end
+
+ it 'does not retry when the feature flag is disabled' do
+ stub_feature_flags(gpg_cleanup_retries: false)
+
+ expect(FileUtils).to receive(:remove_entry).with(tmp_dir, true).and_call_original
+ expect(Retriable).not_to receive(:retriable)
+
+ described_class.using_tmp_keychain {}
+ end
+ end
+
+ it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::FG_CLEANUP_RUNTIME_S
+
+ context 'when running in Sidekiq' do
+ before do
+ allow(Sidekiq).to receive(:server?).and_return(true)
+ end
+
+ it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::BG_CLEANUP_RUNTIME_S
+ end
end
end
diff --git a/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
new file mode 100644
index 00000000000..8d7826c0a56
--- /dev/null
+++ b/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Gitlab::GrapeLogging::Loggers::ExceptionLogger do
+ subject { described_class.new }
+
+ let(:mock_request) { OpenStruct.new(env: {}) }
+
+ describe ".parameters" do
+ describe 'when no exception is available' do
+ it 'returns an empty hash' do
+ expect(subject.parameters(mock_request, nil)).to eq({})
+ end
+ end
+
+ describe 'when an exception is available' do
+ let(:exception) { RuntimeError.new('This is a test') }
+ let(:mock_request) do
+ OpenStruct.new(
+ env: {
+ ::API::Helpers::API_EXCEPTION_ENV => exception
+ }
+ )
+ end
+
+ let(:expected) do
+ {
+ exception: {
+ class: 'RuntimeError',
+ message: 'This is a test'
+ }
+ }
+ end
+
+ it 'returns the correct fields' do
+ expect(subject.parameters(mock_request, nil)).to eq(expected)
+ end
+
+ context 'with backtrace' do
+ before do
+ current_backtrace = caller
+ allow(exception).to receive(:backtrace).and_return(current_backtrace)
+ expected[:exception][:backtrace] = Gitlab::Profiler.clean_backtrace(current_backtrace)
+ end
+
+ it 'includes the backtrace' do
+ expect(subject.parameters(mock_request, nil)).to eq(expected)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb b/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb
new file mode 100644
index 00000000000..1fda84f777e
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::FilterableArrayConnection do
+ let(:callback) { proc { |nodes| nodes } }
+ let(:all_nodes) { Gitlab::Graphql::FilterableArray.new(callback, 1, 2, 3, 4, 5) }
+ let(:arguments) { {} }
+ subject(:connection) do
+ described_class.new(all_nodes, arguments, max_page_size: 3)
+ end
+
+ describe '#paged_nodes' do
+ let(:paged_nodes) { subject.paged_nodes }
+
+ it_behaves_like "connection with paged nodes"
+
+ context 'when callback filters some nodes' do
+ let(:callback) { proc { |nodes| nodes[1..-1] } }
+
+ it 'does not return filtered elements' do
+ expect(subject.paged_nodes).to contain_exactly(all_nodes[1], all_nodes[2])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/conditions/not_null_condition_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/conditions/not_null_condition_spec.rb
new file mode 100644
index 00000000000..d943540fe1f
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/conditions/not_null_condition_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::Conditions::NotNullCondition do
+ describe '#build' do
+ let(:condition) { described_class.new(Issue.arel_table, %w(relative_position id), [1500, 500], ['>', '>'], before_or_after) }
+
+ context 'when there is only one ordering field' do
+ let(:condition) { described_class.new(Issue.arel_table, ['id'], [500], ['>'], :after) }
+
+ it 'generates a single condition sql' do
+ expected_sql = <<~SQL
+ ("issues"."id" > 500)
+ SQL
+
+ expect(condition.build.squish).to eq expected_sql.squish
+ end
+ end
+
+ context 'when :after' do
+ let(:before_or_after) { :after }
+
+ it 'generates :after sql' do
+ expected_sql = <<~SQL
+ ("issues"."relative_position" > 1500)
+ OR (
+ "issues"."relative_position" = 1500
+ AND
+ "issues"."id" > 500
+ )
+ OR ("issues"."relative_position" IS NULL)
+ SQL
+
+ expect(condition.build.squish).to eq expected_sql.squish
+ end
+ end
+
+ context 'when :before' do
+ let(:before_or_after) { :before }
+
+ it 'generates :before sql' do
+ expected_sql = <<~SQL
+ ("issues"."relative_position" > 1500)
+ OR (
+ "issues"."relative_position" = 1500
+ AND
+ "issues"."id" > 500
+ )
+ SQL
+
+ expect(condition.build.squish).to eq expected_sql.squish
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/conditions/null_condition_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/conditions/null_condition_spec.rb
new file mode 100644
index 00000000000..7fce94adb81
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/conditions/null_condition_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::Conditions::NullCondition do
+ describe '#build' do
+ let(:condition) { described_class.new(Issue.arel_table, %w(relative_position id), [nil, 500], [nil, '>'], before_or_after) }
+
+ context 'when :after' do
+ let(:before_or_after) { :after }
+
+ it 'generates sql' do
+ expected_sql = <<~SQL
+ (
+ "issues"."relative_position" IS NULL
+ AND
+ "issues"."id" > 500
+ )
+ SQL
+
+ expect(condition.build.squish).to eq expected_sql.squish
+ end
+ end
+
+ context 'when :before' do
+ let(:before_or_after) { :before }
+
+ it 'generates :before sql' do
+ expected_sql = <<~SQL
+ (
+ "issues"."relative_position" IS NULL
+ AND
+ "issues"."id" > 500
+ )
+ OR ("issues"."relative_position" IS NOT NULL)
+ SQL
+
+ expect(condition.build.squish).to eq expected_sql.squish
+ end
+ 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..9dda2a41ec6
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
@@ -0,0 +1,281 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::Connection 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_cursor(node)
+ described_class.new(nodes, {}).cursor_from_node(node)
+ end
+
+ def decoded_cursor(cursor)
+ JSON.parse(Base64Bp.urlsafe_decode64(cursor))
+ end
+
+ describe '#cursor_from_nodes' do
+ let(:project) { create(:project) }
+ let(:cursor) { connection.cursor_from_node(project) }
+
+ it 'returns an encoded ID' do
+ expect(decoded_cursor(cursor)).to eq('id' => project.id.to_s)
+ end
+
+ context 'when an order is specified' do
+ let(:nodes) { Project.order(:updated_at) }
+
+ it 'returns the encoded value of the order' do
+ expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s)
+ end
+
+ it 'includes the :id even when not specified in the order' do
+ expect(decoded_cursor(cursor)).to include('id' => project.id.to_s)
+ end
+ end
+
+ context 'when multiple orders are specified' do
+ let(:nodes) { Project.order(:updated_at).order(:created_at) }
+
+ it 'returns the encoded value of the order' do
+ expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s)
+ end
+ end
+
+ context 'when multiple orders with SQL are specified' do
+ let(:nodes) { Project.order(Arel.sql('projects.updated_at IS NULL')).order(:updated_at).order(:id) }
+
+ it 'returns the encoded value of the order' do
+ expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s)
+ end
+ end
+ end
+
+ describe '#sliced_nodes' do
+ let(:projects) { create_list(:project, 4) }
+
+ context 'when before is passed' do
+ let(:arguments) { { before: encoded_cursor(projects[1]) } }
+
+ 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_cursor(projects[1]) } }
+
+ 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_cursor(projects[1]),
+ before: encoded_cursor(projects[3])
+ }
+ end
+
+ it 'returns the expected set' do
+ expect(subject.sliced_nodes).to contain_exactly(projects[2])
+ end
+ end
+
+ context 'when multiple orders are defined' do
+ let!(:project1) { create(:project, last_repository_check_at: 10.days.ago) } # Asc: project5 Desc: project3
+ let!(:project2) { create(:project, last_repository_check_at: nil) } # Asc: project1 Desc: project1
+ let!(:project3) { create(:project, last_repository_check_at: 5.days.ago) } # Asc: project3 Desc: project5
+ let!(:project4) { create(:project, last_repository_check_at: nil) } # Asc: project2 Desc: project2
+ let!(:project5) { create(:project, last_repository_check_at: 20.days.ago) } # Asc: project4 Desc: project4
+
+ context 'when ascending' do
+ let(:nodes) do
+ Project.order(Arel.sql('projects.last_repository_check_at IS NULL')).order(last_repository_check_at: :asc).order(id: :asc)
+ end
+
+ context 'when no cursor is passed' do
+ let(:arguments) { {} }
+
+ it 'returns projects in ascending order' do
+ expect(subject.sliced_nodes).to eq([project5, project1, project3, project2, project4])
+ end
+ end
+
+ context 'when before cursor value is NULL' do
+ let(:arguments) { { before: encoded_cursor(project4) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project5, project1, project3, project2])
+ end
+ end
+
+ context 'when before cursor value is not NULL' do
+ let(:arguments) { { before: encoded_cursor(project3) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project5, project1])
+ end
+ end
+
+ context 'when after cursor value is NULL' do
+ let(:arguments) { { after: encoded_cursor(project2) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project4])
+ end
+ end
+
+ context 'when after cursor value is not NULL' do
+ let(:arguments) { { after: encoded_cursor(project1) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project3, project2, project4])
+ end
+ end
+
+ context 'when before and after cursor' do
+ let(:arguments) { { before: encoded_cursor(project4), after: encoded_cursor(project5) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project1, project3, project2])
+ end
+ end
+ end
+
+ context 'when descending' do
+ let(:nodes) do
+ Project.order(Arel.sql('projects.last_repository_check_at IS NULL')).order(last_repository_check_at: :desc).order(id: :asc)
+ end
+
+ context 'when no cursor is passed' do
+ let(:arguments) { {} }
+
+ it 'only returns projects in descending order' do
+ expect(subject.sliced_nodes).to eq([project3, project1, project5, project2, project4])
+ end
+ end
+
+ context 'when before cursor value is NULL' do
+ let(:arguments) { { before: encoded_cursor(project4) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project3, project1, project5, project2])
+ end
+ end
+
+ context 'when before cursor value is not NULL' do
+ let(:arguments) { { before: encoded_cursor(project5) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project3, project1])
+ end
+ end
+
+ context 'when after cursor value is NULL' do
+ let(:arguments) { { after: encoded_cursor(project2) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project4])
+ end
+ end
+
+ context 'when after cursor value is not NULL' do
+ let(:arguments) { { after: encoded_cursor(project1) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project5, project2, project4])
+ end
+ end
+
+ context 'when before and after cursor' do
+ let(:arguments) { { before: encoded_cursor(project4), after: encoded_cursor(project3) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project1, project5, project2])
+ end
+ end
+ end
+ end
+
+ # TODO Enable this as part of below issue
+ # https://gitlab.com/gitlab-org/gitlab/issues/32933
+ # context 'when an invalid cursor is provided' do
+ # let(:arguments) { { before: 'invalidcursor' } }
+ #
+ # it 'raises an error' do
+ # expect { expect(subject.sliced_nodes) }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ # end
+ # end
+
+ # TODO Remove this as part of below issue
+ # https://gitlab.com/gitlab-org/gitlab/issues/32933
+ context 'when an old style cursor is provided' do
+ let(:arguments) { { before: Base64Bp.urlsafe_encode64(projects[1].id.to_s, padding: false) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+ end
+ end
+
+ describe '#paged_nodes' do
+ let_it_be(:all_nodes) { create_list(:project, 5) }
+ let(:paged_nodes) { subject.paged_nodes }
+
+ it_behaves_like "connection with paged nodes"
+
+ context 'when both are passed' do
+ let(:arguments) { { first: 2, last: 2 } }
+
+ it 'raises an error' do
+ expect { paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'when primary key is not in original order' do
+ let(:nodes) { Project.order(last_repository_check_at: :desc) }
+
+ it 'is added to end' do
+ sliced = subject.sliced_nodes
+ last_order_name = sliced.order_values.last.expr.name
+
+ expect(last_order_name).to eq sliced.primary_key
+ end
+ end
+
+ context 'when there is no primary key' do
+ let(:nodes) { NoPrimaryKey.all }
+
+ it 'raises an error' do
+ expect(NoPrimaryKey.primary_key).to be_nil
+ expect { subject.sliced_nodes }.to raise_error(ArgumentError, 'Relation must have a primary key')
+ end
+ end
+ end
+
+ class NoPrimaryKey < ActiveRecord::Base
+ self.table_name = 'no_primary_key'
+ self.primary_key = nil
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
new file mode 100644
index 00000000000..aaf28fed684
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+# TODO https://gitlab.com/gitlab-org/gitlab/issues/35104
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::LegacyKeysetConnection do
+ describe 'old keyset_connection' do
+ let(:described_class) { Gitlab::Graphql::Connections::Keyset::Connection }
+ let(:nodes) { Project.all.order(id: :asc) }
+ let(:arguments) { {} }
+ subject(:connection) do
+ described_class.new(nodes, arguments, max_page_size: 3)
+ end
+
+ before do
+ stub_feature_flags(graphql_keyset_pagination: false)
+ end
+
+ def encoded_property(value)
+ Base64Bp.urlsafe_encode64(value.to_s, padding: false)
+ 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
+
+ it 'is a loaded memoized array' do
+ expect(subject.paged_nodes).to be_an(Array)
+ expect(subject.paged_nodes.object_id).to eq(subject.paged_nodes.object_id)
+ 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
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/order_info_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/order_info_spec.rb
new file mode 100644
index 00000000000..17ddcaefeeb
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/order_info_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::OrderInfo do
+ describe '#build_order_list' do
+ let(:order_list) { described_class.build_order_list(relation) }
+
+ context 'when multiple orders with SQL is specified' do
+ let(:relation) { Project.order(Arel.sql('projects.updated_at IS NULL')).order(:updated_at).order(:id) }
+
+ it 'ignores the SQL order' do
+ expect(order_list.count).to eq 2
+ expect(order_list.first.attribute_name).to eq 'updated_at'
+ expect(order_list.first.operator_for(:after)).to eq '>'
+ expect(order_list.last.attribute_name).to eq 'id'
+ expect(order_list.last.operator_for(:after)).to eq '>'
+ end
+ end
+
+ context 'when order contains NULLS LAST' do
+ let(:relation) { Project.order(Arel.sql('projects.updated_at Asc Nulls Last')).order(:id) }
+
+ it 'does not ignore the SQL order' do
+ expect(order_list.count).to eq 2
+ expect(order_list.first.attribute_name).to eq 'projects.updated_at'
+ expect(order_list.first.operator_for(:after)).to eq '>'
+ expect(order_list.last.attribute_name).to eq 'id'
+ expect(order_list.last.operator_for(:after)).to eq '>'
+ end
+ end
+
+ context 'when order contains invalid formatted NULLS LAST ' do
+ let(:relation) { Project.order(Arel.sql('projects.updated_at created_at Asc Nulls Last')).order(:id) }
+
+ it 'ignores the SQL order' do
+ expect(order_list.count).to eq 1
+ end
+ end
+ end
+
+ describe '#validate_ordering' do
+ let(:order_list) { described_class.build_order_list(relation) }
+
+ context 'when number of ordering fields is 0' do
+ let(:relation) { Project.all }
+
+ it 'raises an error' do
+ expect { described_class.validate_ordering(relation, order_list) }
+ .to raise_error(ArgumentError, 'A minimum of 1 ordering field is required')
+ end
+ end
+
+ context 'when number of ordering fields is over 2' do
+ let(:relation) { Project.order(last_repository_check_at: :desc).order(updated_at: :desc).order(:id) }
+
+ it 'raises an error' do
+ expect { described_class.validate_ordering(relation, order_list) }
+ .to raise_error(ArgumentError, 'A maximum of 2 ordering fields are allowed')
+ end
+ end
+
+ context 'when the second (or first) column is nullable' do
+ let(:relation) { Project.order(last_repository_check_at: :desc).order(updated_at: :desc) }
+
+ it 'raises an error' do
+ expect { described_class.validate_ordering(relation, order_list) }
+ .to raise_error(ArgumentError, "Column `updated_at` must not allow NULL")
+ end
+ end
+
+ context 'for last ordering field' do
+ let(:relation) { Project.order(namespace_id: :desc) }
+
+ it 'raises error if primary key is not last field' do
+ expect { described_class.validate_ordering(relation, order_list) }
+ .to raise_error(ArgumentError, "Last ordering field must be the primary key, `#{relation.primary_key}`")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/query_builder_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/query_builder_spec.rb
new file mode 100644
index 00000000000..59e153d9e07
--- /dev/null
+++ b/spec/lib/gitlab/graphql/connections/keyset/query_builder_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Graphql::Connections::Keyset::QueryBuilder do
+ context 'when number of ordering fields is 0' do
+ it 'raises an error' do
+ expect { described_class.new(Issue.arel_table, [], {}, :after) }
+ .to raise_error(ArgumentError, 'No ordering scopes have been supplied')
+ end
+ end
+
+ describe '#conditions' do
+ let(:relation) { Issue.order(relative_position: :desc).order(:id) }
+ let(:order_list) { Gitlab::Graphql::Connections::Keyset::OrderInfo.build_order_list(relation) }
+ let(:builder) { described_class.new(arel_table, order_list, decoded_cursor, before_or_after) }
+ let(:before_or_after) { :after }
+
+ context 'when only a single ordering' do
+ let(:relation) { Issue.order(id: :desc) }
+
+ context 'when the value is nil' do
+ let(:decoded_cursor) { { 'id' => nil } }
+
+ it 'raises an error' do
+ expect { builder.conditions }
+ .to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'Before/after cursor invalid: `nil` was provided as only sortable value')
+ end
+ end
+
+ context 'when value is not nil' do
+ let(:decoded_cursor) { { 'id' => 100 } }
+ let(:conditions) { builder.conditions }
+
+ context 'when :after' do
+ it 'generates the correct condition' do
+ expect(conditions.strip).to eq '("issues"."id" < 100)'
+ end
+ end
+
+ context 'when :before' do
+ let(:before_or_after) { :before }
+
+ it 'generates the correct condition' do
+ expect(conditions.strip).to eq '("issues"."id" > 100)'
+ end
+ end
+ end
+ end
+
+ context 'when two orderings' do
+ let(:decoded_cursor) { { 'relative_position' => 1500, 'id' => 100 } }
+
+ context 'when no values are nil' do
+ context 'when :after' do
+ it 'generates the correct condition' do
+ conditions = builder.conditions
+
+ expect(conditions).to include '"issues"."relative_position" < 1500'
+ expect(conditions).to include '"issues"."id" > 100'
+ expect(conditions).to include 'OR ("issues"."relative_position" IS NULL)'
+ end
+ end
+
+ context 'when :before' do
+ let(:before_or_after) { :before }
+
+ it 'generates the correct condition' do
+ conditions = builder.conditions
+
+ expect(conditions).to include '("issues"."relative_position" > 1500)'
+ expect(conditions).to include '"issues"."id" < 100'
+ expect(conditions).to include '"issues"."relative_position" = 1500'
+ end
+ end
+ end
+
+ context 'when first value is nil' do
+ let(:decoded_cursor) { { 'relative_position' => nil, 'id' => 100 } }
+
+ context 'when :after' do
+ it 'generates the correct condition' do
+ conditions = builder.conditions
+
+ expect(conditions).to include '"issues"."relative_position" IS NULL'
+ expect(conditions).to include '"issues"."id" > 100'
+ end
+ end
+
+ context 'when :before' do
+ let(:before_or_after) { :before }
+
+ it 'generates the correct condition' do
+ conditions = builder.conditions
+
+ expect(conditions).to include '"issues"."relative_position" IS NULL'
+ expect(conditions).to include '"issues"."id" < 100'
+ expect(conditions).to include 'OR ("issues"."relative_position" IS NOT NULL)'
+ end
+ end
+ end
+ end
+ end
+
+ def arel_table
+ Issue.arel_table
+ end
+end
diff --git a/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
deleted file mode 100644
index 4eb121794e1..00000000000
--- a/spec/lib/gitlab/graphql/connections/keyset_connection_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-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)
- Base64Bp.urlsafe_encode64(value.to_s, padding: false)
- 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
-
- it 'is a loaded memoized array' do
- expect(subject.paged_nodes).to be_an(Array)
- expect(subject.paged_nodes.object_id).to eq(subject.paged_nodes.object_id)
- 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/graphql/loaders/pipeline_for_sha_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/pipeline_for_sha_loader_spec.rb
deleted file mode 100644
index 136027736c3..00000000000
--- a/spec/lib/gitlab/graphql/loaders/pipeline_for_sha_loader_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Graphql::Loaders::PipelineForShaLoader do
- include GraphqlHelpers
-
- describe '#find_last' do
- it 'batch-resolves latest pipeline' do
- project = create(:project, :repository)
- pipeline1 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha)
- pipeline2 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha)
- pipeline3 = create(:ci_pipeline, project: project, ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
-
- result = batch_sync(max_queries: 1) do
- [pipeline1.sha, pipeline3.sha].map { |sha| described_class.new(project, sha).find_last }
- end
-
- expect(result).to contain_exactly(pipeline2, pipeline3)
- end
- end
-end
diff --git a/spec/lib/gitlab/group_search_results_spec.rb b/spec/lib/gitlab/group_search_results_spec.rb
index 53a91a35ec9..570b0cb7401 100644
--- a/spec/lib/gitlab/group_search_results_spec.rb
+++ b/spec/lib/gitlab/group_search_results_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GroupSearchResults do
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 8e253b51597..ce7f2c4530d 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -42,7 +42,7 @@ describe Gitlab::HashedStorage::Migrator, :sidekiq, :redis do
subject.bulk_migrate(start: ids.min, finish: ids.max)
end
- it 'has all projects migrated and set as writable' do
+ it 'has all projects migrated and set as writable', :sidekiq_might_not_need_inline do
perform_enqueued_jobs do
subject.bulk_migrate(start: ids.min, finish: ids.max)
end
@@ -79,7 +79,7 @@ describe Gitlab::HashedStorage::Migrator, :sidekiq, :redis do
subject.bulk_rollback(start: ids.min, finish: ids.max)
end
- it 'has all projects rolledback and set as writable' do
+ it 'has all projects rolledback and set as writable', :sidekiq_might_not_need_inline do
perform_enqueued_jobs do
subject.bulk_rollback(start: ids.min, finish: ids.max)
end
@@ -108,7 +108,7 @@ describe Gitlab::HashedStorage::Migrator, :sidekiq, :redis do
expect { subject.migrate(project) }.not_to raise_error
end
- it 'migrates project storage' do
+ it 'migrates project storage', :sidekiq_might_not_need_inline do
perform_enqueued_jobs do
subject.migrate(project)
end
@@ -154,7 +154,7 @@ describe Gitlab::HashedStorage::Migrator, :sidekiq, :redis do
expect { subject.rollback(project) }.not_to raise_error
end
- it 'rolls-back project storage' do
+ it 'rolls-back project storage', :sidekiq_might_not_need_inline do
perform_enqueued_jobs do
subject.rollback(project)
end
diff --git a/spec/lib/gitlab/health_checks/master_check_spec.rb b/spec/lib/gitlab/health_checks/master_check_spec.rb
new file mode 100644
index 00000000000..91441a7ddc3
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/master_check_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+require_relative './simple_check_shared'
+
+describe Gitlab::HealthChecks::MasterCheck do
+ let(:result_class) { Gitlab::HealthChecks::Result }
+
+ SUCCESS_CODE = 100
+ FAILURE_CODE = 101
+
+ before do
+ described_class.register_master
+ end
+
+ after do
+ described_class.finish_master
+ end
+
+ describe '#readiness' do
+ context 'when master is running' do
+ it 'worker does return success' do
+ _, child_status = run_worker
+
+ expect(child_status.exitstatus).to eq(SUCCESS_CODE)
+ end
+ end
+
+ context 'when master finishes early' do
+ before do
+ described_class.send(:close_write)
+ end
+
+ it 'worker does return failure' do
+ _, child_status = run_worker
+
+ expect(child_status.exitstatus).to eq(FAILURE_CODE)
+ end
+ end
+
+ def run_worker
+ pid = fork do
+ described_class.register_worker
+
+ exit(described_class.readiness.success ? SUCCESS_CODE : FAILURE_CODE)
+ end
+
+ Process.wait2(pid)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index 4676db6b8d8..5a45d724b83 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Highlight do
diff --git a/spec/lib/gitlab/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
index 788bddb8f59..f30528916dc 100644
--- a/spec/lib/gitlab/http_io_spec.rb
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HttpIO do
diff --git a/spec/lib/gitlab/http_spec.rb b/spec/lib/gitlab/http_spec.rb
index d3f9be845dd..192816ad057 100644
--- a/spec/lib/gitlab/http_spec.rb
+++ b/spec/lib/gitlab/http_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HTTP do
diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb
index 785035d993f..2664423af88 100644
--- a/spec/lib/gitlab/i18n_spec.rb
+++ b/spec/lib/gitlab/i18n_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::I18n do
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index 1e583f4cee2..9c7972d4bde 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Identifier do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 4fd61383c6b..8f627fcc24d 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -29,6 +29,9 @@ issues:
- prometheus_alerts
- prometheus_alert_events
- self_managed_prometheus_alert_events
+- zoom_meetings
+- vulnerability_links
+- related_vulnerabilities
events:
- author
- project
@@ -119,6 +122,7 @@ merge_requests:
- pipelines_for_merge_request
- merge_request_assignees
- suggestions
+- unresolved_notes
- assignees
- reviews
- approval_rules
@@ -338,6 +342,7 @@ project:
- triggers
- pipeline_schedules
- environments
+- environments_for_dashboard
- deployments
- project_feature
- auto_devops
@@ -421,6 +426,12 @@ project:
- pages_metadatum
- alerts_service
- grafana_integration
+- remove_source_branch_after_merge
+- deleting_user
+- upstream_projects
+- downstream_projects
+- upstream_project_subscriptions
+- downstream_project_subscriptions
award_emoji:
- awardable
- user
@@ -528,4 +539,6 @@ versions: &version
- issue
- designs
- actions
+zoom_meetings:
+- issue
design_versions: *version
diff --git a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
index 934e676d020..b190a1007a0 100644
--- a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
@@ -132,10 +132,6 @@ describe Gitlab::ImportExport::FastHashSerializer do
end
it 'has no when YML attributes but only the DB column' do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:ci_yaml_file)
- .and_return(File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')))
-
expect_any_instance_of(Gitlab::Ci::YamlProcessor).not_to receive(:build_attributes)
subject
diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb
index 71fd5a51c3b..5752fd8fa0d 100644
--- a/spec/lib/gitlab/import_export/fork_spec.rb
+++ b/spec/lib/gitlab/import_export/fork_spec.rb
@@ -47,7 +47,7 @@ describe 'forked project import' do
end
end
- it 'can access the MR' do
+ it 'can access the MR', :sidekiq_might_not_need_inline do
project.merge_requests.first.fetch_ref!
expect(project.repository.ref_exists?('refs/merge-requests/1/head')).to be_truthy
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
index 6a803c48b34..1a5cb7806a3 100644
--- a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::GroupProjectObjectBuilder do
let(:project) do
- create(:project,
+ create(:project, :repository,
:builds_disabled,
:issues_disabled,
name: 'project',
@@ -11,8 +11,8 @@ describe Gitlab::ImportExport::GroupProjectObjectBuilder do
end
context 'labels' do
- it 'finds the right group label' do
- group_label = create(:group_label, 'name': 'group label', 'group': project.group)
+ it 'finds the existing group label' do
+ group_label = create(:group_label, name: 'group label', group: project.group)
expect(described_class.build(Label,
'title' => 'group label',
@@ -31,8 +31,8 @@ describe Gitlab::ImportExport::GroupProjectObjectBuilder do
end
context 'milestones' do
- it 'finds the right group milestone' do
- milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group)
+ it 'finds the existing group milestone' do
+ milestone = create(:milestone, name: 'group milestone', group: project.group)
expect(described_class.build(Milestone,
'title' => 'group milestone',
@@ -49,4 +49,30 @@ describe Gitlab::ImportExport::GroupProjectObjectBuilder do
expect(milestone.persisted?).to be true
end
end
+
+ context 'merge_request' do
+ it 'finds the existing merge_request' do
+ merge_request = create(:merge_request, title: 'MergeRequest', iid: 7, target_project: project, source_project: project)
+ expect(described_class.build(MergeRequest,
+ 'title' => 'MergeRequest',
+ 'source_project_id' => project.id,
+ 'target_project_id' => project.id,
+ 'source_branch' => 'SourceBranch',
+ 'iid' => 7,
+ 'target_branch' => 'TargetBranch',
+ 'author_id' => project.creator.id)).to eq(merge_request)
+ end
+
+ it 'creates a new merge_request' do
+ merge_request = described_class.build(MergeRequest,
+ 'title' => 'MergeRequest',
+ 'iid' => 8,
+ 'source_project_id' => project.id,
+ 'target_project_id' => project.id,
+ 'source_branch' => 'SourceBranch',
+ 'target_branch' => 'TargetBranch',
+ 'author_id' => project.creator.id)
+ expect(merge_request.persisted?).to be true
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/group_tree_saver_spec.rb b/spec/lib/gitlab/import_export/group_tree_saver_spec.rb
new file mode 100644
index 00000000000..b856441981a
--- /dev/null
+++ b/spec/lib/gitlab/import_export/group_tree_saver_spec.rb
@@ -0,0 +1,180 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ImportExport::GroupTreeSaver do
+ describe 'saves the group tree into a json object' do
+ let(:shared) { Gitlab::ImportExport::Shared.new(group) }
+ let(:group_tree_saver) { described_class.new(group: group, current_user: user, shared: shared) }
+ let(:export_path) { "#{Dir.tmpdir}/group_tree_saver_spec" }
+ let(:user) { create(:user) }
+ let!(:group) { setup_group }
+
+ before do
+ group.add_maintainer(user)
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ end
+
+ it 'saves group successfully' do
+ expect(group_tree_saver.save).to be true
+ end
+
+ context ':export_fast_serialize feature flag checks' do
+ before do
+ expect(Gitlab::ImportExport::Reader).to receive(:new).with(shared: shared, config: group_config).and_return(reader)
+ expect(reader).to receive(:group_tree).and_return(group_tree)
+ end
+
+ let(:reader) { instance_double('Gitlab::ImportExport::Reader') }
+ let(:group_config) { Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.group_config_file).to_h }
+ let(:group_tree) do
+ {
+ include: [{ milestones: { include: [] } }],
+ preload: { milestones: nil }
+ }
+ end
+
+ context 'when :export_fast_serialize feature is enabled' do
+ let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
+
+ before do
+ stub_feature_flags(export_fast_serialize: true)
+
+ expect(Gitlab::ImportExport::FastHashSerializer).to receive(:new).with(group, group_tree).and_return(serializer)
+ end
+
+ it 'uses FastHashSerializer' do
+ expect(serializer).to receive(:execute)
+
+ group_tree_saver.save
+ end
+ end
+
+ context 'when :export_fast_serialize feature is disabled' do
+ before do
+ stub_feature_flags(export_fast_serialize: false)
+ end
+
+ it 'is serialized via built-in `as_json`' do
+ expect(group).to receive(:as_json).with(group_tree).and_call_original
+
+ group_tree_saver.save
+ end
+ end
+ end
+
+ # It is mostly duplicated in
+ # `spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb`
+ # except:
+ # context 'with description override' do
+ # context 'group members' do
+ # ^ These are specific for the groupTreeSaver
+ context 'JSON' do
+ let(:saved_group_json) do
+ group_tree_saver.save
+ group_json(group_tree_saver.full_path)
+ end
+
+ it 'saves the correct json' do
+ expect(saved_group_json).to include({ 'description' => 'description', 'visibility_level' => 20 })
+ end
+
+ it 'has milestones' do
+ expect(saved_group_json['milestones']).not_to be_empty
+ end
+
+ it 'has labels' do
+ expect(saved_group_json['labels']).not_to be_empty
+ end
+
+ it 'has boards' do
+ expect(saved_group_json['boards']).not_to be_empty
+ end
+
+ it 'has group members' do
+ expect(saved_group_json['members']).not_to be_empty
+ end
+
+ it 'has priorities associated to labels' do
+ expect(saved_group_json['labels'].first['priorities']).not_to be_empty
+ end
+
+ it 'has badges' do
+ expect(saved_group_json['badges']).not_to be_empty
+ end
+
+ context 'group children' do
+ let(:children) { group.children }
+
+ it 'exports group children' do
+ expect(saved_group_json['children'].length).to eq(children.count)
+ end
+
+ it 'exports group children of children' do
+ expect(saved_group_json['children'].first['children'].length).to eq(children.first.children.count)
+ end
+ end
+
+ context 'group members' do
+ let(:user2) { create(:user, email: 'group@member.com') }
+ let(:member_emails) do
+ saved_group_json['members'].map do |pm|
+ pm['user']['email']
+ end
+ end
+
+ before do
+ group.add_developer(user2)
+ end
+
+ it 'exports group members as group owner' do
+ group.add_owner(user)
+
+ expect(member_emails).to include('group@member.com')
+ end
+
+ context 'as admin' do
+ let(:user) { create(:admin) }
+
+ it 'exports group members as admin' do
+ expect(member_emails).to include('group@member.com')
+ end
+
+ it 'exports group members' do
+ member_types = saved_group_json['members'].map { |pm| pm['source_type'] }
+
+ expect(member_types).to all(eq('Namespace'))
+ end
+ end
+ end
+
+ context 'group attributes' do
+ it 'does not contain the runners token' do
+ expect(saved_group_json).not_to include("runners_token" => 'token')
+ end
+ end
+ end
+ end
+
+ def setup_group
+ group = create(:group, description: 'description')
+ sub_group = create(:group, description: 'description', parent: group)
+ create(:group, description: 'description', parent: sub_group)
+ create(:milestone, group: group)
+ create(:group_badge, group: group)
+ group_label = create(:group_label, group: group)
+ create(:label_priority, label: group_label, priority: 1)
+ create(:board, group: group)
+ create(:group_badge, group: group)
+
+ group
+ end
+
+ def group_json(filename)
+ JSON.parse(IO.read(filename))
+ end
+end
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
index 40a5f2294a2..a6b0dc758cd 100644
--- a/spec/lib/gitlab/import_export/import_export_spec.rb
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -6,17 +6,17 @@ describe Gitlab::ImportExport do
let(:project) { create(:project, :public, path: 'project-path', namespace: group) }
it 'contains the project path' do
- expect(described_class.export_filename(project: project)).to include(project.path)
+ expect(described_class.export_filename(exportable: project)).to include(project.path)
end
it 'contains the namespace path' do
- expect(described_class.export_filename(project: project)).to include(project.namespace.full_path.tr('/', '_'))
+ expect(described_class.export_filename(exportable: project)).to include(project.namespace.full_path.tr('/', '_'))
end
it 'does not go over a certain length' do
project.path = 'a' * 100
- expect(described_class.export_filename(project: project).length).to be < 70
+ expect(described_class.export_filename(exportable: project).length).to be < 70
end
end
end
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 ebd2c6089ce..459b1eed1a7 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
include ImportExport::CommonUtil
describe Gitlab::ImportExport::ProjectTreeRestorer do
+ include ImportExport::CommonUtil
+
let(:shared) { project.import_export_shared }
describe 'restore project tree' do
@@ -16,7 +18,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
RSpec::Mocks.with_temporary_scope do
@project = create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project')
@shared = @project.import_export_shared
- allow(@shared).to receive(:export_path).and_return('spec/fixtures/lib/gitlab/import_export/')
+
+ setup_import_export_config('complex')
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)
@@ -207,10 +210,27 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(@project.project_badges.count).to eq(2)
end
+ it 'has snippets' do
+ expect(@project.snippets.count).to eq(1)
+ end
+
+ it 'has award emoji for a snippet' do
+ award_emoji = @project.snippets.first.award_emoji
+
+ expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee')
+ end
+
it 'restores the correct service' do
expect(CustomIssueTrackerService.first).not_to be_nil
end
+ it 'restores zoom meetings' do
+ meetings = @project.issues.first.zoom_meetings
+
+ expect(meetings.count).to eq(1)
+ expect(meetings.first.url).to eq('https://zoom.us/j/123456789')
+ end
+
context 'Merge requests' do
it 'always has the new project as a target' do
expect(MergeRequest.find_by_title('MR1').target_project).to eq(@project)
@@ -250,9 +270,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
it 'has the correct number of pipelines and statuses' do
- expect(@project.ci_pipelines.size).to eq(5)
+ expect(@project.ci_pipelines.size).to eq(6)
- @project.ci_pipelines.zip([2, 2, 2, 2, 2])
+ @project.ci_pipelines.order(:id).zip([2, 2, 2, 2, 2, 0])
.each do |(pipeline, expected_status_size)|
expect(pipeline.statuses.size).to eq(expected_status_size)
end
@@ -261,7 +281,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
context 'when restoring hierarchy of pipeline, stages and jobs' do
it 'restores pipelines' do
- expect(Ci::Pipeline.all.count).to be 5
+ expect(Ci::Pipeline.all.count).to be 6
end
it 'restores pipeline stages' do
@@ -307,21 +327,33 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
- context 'Light JSON' do
+ context 'project.json file access check' do
let(:user) { create(:user) }
let!(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
let(:restored_project_json) { project_tree_restorer.restore }
- before do
- allow(shared).to receive(:export_path).and_return('spec/fixtures/lib/gitlab/import_export/')
+ it 'does not read a symlink' do
+ Dir.mktmpdir do |tmpdir|
+ setup_symlink(tmpdir, 'project.json')
+ allow(shared).to receive(:export_path).and_call_original
+
+ expect(project_tree_restorer.restore).to eq(false)
+ expect(shared.errors).to include('Incorrect JSON format')
+ end
end
+ end
+
+ context 'Light JSON' do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
+ let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
+ let(:restored_project_json) { project_tree_restorer.restore }
context 'with a simple project' do
before do
- project_tree_restorer.instance_variable_set(:@path, "spec/fixtures/lib/gitlab/import_export/project.light.json")
-
- restored_project_json
+ setup_import_export_config('light')
+ expect(restored_project_json).to eq(true)
end
it_behaves_like 'restores project correctly',
@@ -332,19 +364,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
first_issue_labels: 1,
services: 1
- context 'project.json file access check' do
- it 'does not read a symlink' do
- Dir.mktmpdir do |tmpdir|
- setup_symlink(tmpdir, 'project.json')
- allow(shared).to receive(:export_path).and_call_original
-
- restored_project_json
-
- expect(shared.errors).to be_empty
- end
- end
- end
-
context 'when there is an existing build with build token' do
before do
create(:ci_build, token: 'abcd')
@@ -360,6 +379,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
context 'when the project has overridden params in import data' do
+ before do
+ setup_import_export_config('light')
+ end
+
it 'handles string versions of visibility_level' do
# Project needs to be in a group for visibility level comparison
# to happen
@@ -368,24 +391,21 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
project.create_import_data(data: { override_params: { visibility_level: Gitlab::VisibilityLevel::INTERNAL.to_s } })
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
it 'overwrites the params stored in the JSON' do
project.create_import_data(data: { override_params: { description: "Overridden" } })
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.description).to eq("Overridden")
end
it 'does not allow setting params that are excluded from import_export settings' do
project.create_import_data(data: { override_params: { lfs_enabled: true } })
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.lfs_enabled).to be_falsey
end
@@ -401,7 +421,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
project.create_import_data(data: { override_params: disabled_access_levels })
- restored_project_json
+ expect(restored_project_json).to eq(true)
aggregate_failures do
access_level_keys.each do |key|
@@ -422,9 +442,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
before do
- project_tree_restorer.instance_variable_set(:@path, "spec/fixtures/lib/gitlab/import_export/project.group.json")
-
- restored_project_json
+ setup_import_export_config('group')
+ expect(restored_project_json).to eq(true)
end
it_behaves_like 'restores project correctly',
@@ -456,11 +475,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
before do
- project_tree_restorer.instance_variable_set(:@path, "spec/fixtures/lib/gitlab/import_export/project.light.json")
+ setup_import_export_config('light')
end
it 'does not import any templated services' do
- restored_project_json
+ expect(restored_project_json).to eq(true)
expect(project.services.where(template: true).count).to eq(0)
end
@@ -470,8 +489,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.labels.count).to eq(1)
end
@@ -480,8 +498,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.group.milestones.count).to eq(1)
expect(project.milestones.count).to eq(0)
end
@@ -497,13 +514,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
group: create(:group))
end
- it 'preserves the project milestone IID' do
- project_tree_restorer.instance_variable_set(:@path, "spec/fixtures/lib/gitlab/import_export/project.milestone-iid.json")
+ before do
+ setup_import_export_config('milestone-iid')
+ end
+ it 'preserves the project milestone IID' do
expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error)
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
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)
@@ -511,19 +529,21 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
context 'with external authorization classification labels' do
+ before do
+ setup_import_export_config('light')
+ end
+
it 'converts empty external classification authorization labels to nil' do
project.create_import_data(data: { override_params: { external_authorization_classification_label: "" } })
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.external_authorization_classification_label).to be_nil
end
it 'preserves valid external classification authorization labels' do
project.create_import_data(data: { override_params: { external_authorization_classification_label: "foobar" } })
- restored_project_json
-
+ expect(restored_project_json).to eq(true)
expect(project.external_authorization_classification_label).to eq("foobar")
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 ff46e062a5d..97d8b155826 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -203,7 +203,6 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
end
it 'has no when YML attributes but only the DB column' do
- allow_any_instance_of(Ci::Pipeline).to receive(:ci_yaml_file).and_return(File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')))
expect_any_instance_of(Gitlab::Ci::YamlProcessor).not_to receive(:build_attributes)
saved_project_json
diff --git a/spec/lib/gitlab/import_export/relation_rename_service_spec.rb b/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
index 472bf55d37e..d62f5725f9e 100644
--- a/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_rename_service_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
describe Gitlab::ImportExport::RelationRenameService do
+ include ImportExport::CommonUtil
+
let(:renames) do
{
'example_relation1' => 'new_example_relation1',
@@ -21,12 +23,12 @@ describe Gitlab::ImportExport::RelationRenameService do
context 'when importing' do
let(:project_tree_restorer) { Gitlab::ImportExport::ProjectTreeRestorer.new(user: user, shared: shared, project: project) }
- let(:import_path) { 'spec/fixtures/lib/gitlab/import_export' }
- let(:file_content) { IO.read("#{import_path}/project.json") }
- let!(:json_file) { ActiveSupport::JSON.decode(file_content) }
+ let(:file_content) { IO.read(File.join(shared.export_path, 'project.json')) }
+ let(:json_file) { ActiveSupport::JSON.decode(file_content) }
before do
- allow(shared).to receive(:export_path).and_return(import_path)
+ setup_import_export_config('complex')
+
allow(ActiveSupport::JSON).to receive(:decode).and_call_original
allow(ActiveSupport::JSON).to receive(:decode).with(file_content).and_return(json_file)
end
@@ -94,15 +96,20 @@ describe Gitlab::ImportExport::RelationRenameService do
let(:export_content_path) { project_tree_saver.full_path }
let(:export_content_hash) { ActiveSupport::JSON.decode(File.read(export_content_path)) }
let(:injected_hash) { renames.values.product([{}]).to_h }
+ let(:relation_tree_saver) { Gitlab::ImportExport::RelationTreeSaver.new }
let(:project_tree_saver) do
Gitlab::ImportExport::ProjectTreeSaver.new(
project: project, current_user: user, shared: shared)
end
+ before do
+ allow(project_tree_saver).to receive(:tree_saver).and_return(relation_tree_saver)
+ end
+
it 'adds old relationships to the exported file' do
# we inject relations with new names that should be rewritten
- expect(project_tree_saver).to receive(:serialize_project_tree).and_wrap_original do |method, *args|
+ expect(relation_tree_saver).to receive(:serialize).and_wrap_original do |method, *args|
method.call(*args).merge(injected_hash)
end
diff --git a/spec/lib/gitlab/import_export/relation_tree_saver_spec.rb b/spec/lib/gitlab/import_export/relation_tree_saver_spec.rb
new file mode 100644
index 00000000000..2fc26c0e3d4
--- /dev/null
+++ b/spec/lib/gitlab/import_export/relation_tree_saver_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ImportExport::RelationTreeSaver do
+ let(:exportable) { create(:group) }
+ let(:relation_tree_saver) { described_class.new }
+ let(:tree) { {} }
+
+ describe '#serialize' do
+ context 'when :export_fast_serialize feature is enabled' do
+ let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
+
+ before do
+ stub_feature_flags(export_fast_serialize: true)
+ end
+
+ it 'uses FastHashSerializer' do
+ expect(Gitlab::ImportExport::FastHashSerializer)
+ .to receive(:new)
+ .with(exportable, tree)
+ .and_return(serializer)
+
+ expect(serializer).to receive(:execute)
+
+ relation_tree_saver.serialize(exportable, tree)
+ end
+ end
+
+ context 'when :export_fast_serialize feature is disabled' do
+ before do
+ stub_feature_flags(export_fast_serialize: false)
+ end
+
+ it 'is serialized via built-in `as_json`' do
+ expect(exportable).to receive(:as_json).with(tree)
+
+ relation_tree_saver.serialize(exportable, tree)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 8ae571a69ef..04fe985cdb5 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -185,6 +185,7 @@ MergeRequest:
- merge_when_pipeline_succeeds
- merge_user_id
- merge_commit_sha
+- squash_commit_sha
- in_progress_merge_commit_sha
- lock_version
- milestone_id
@@ -512,6 +513,7 @@ Project:
- request_access_enabled
- has_external_wiki
- only_allow_merge_if_all_discussions_are_resolved
+- remove_source_branch_after_merge
- auto_cancel_pending_pipelines
- printing_merge_request_link_enabled
- resolve_outdated_diff_discussions
@@ -537,7 +539,6 @@ Project:
- external_webhook_token
- pages_https_only
- merge_requests_disable_committers_approval
-- merge_requests_require_code_owner_approval
- require_password_to_approve
ProjectTracingSetting:
- external_url
@@ -752,4 +753,12 @@ DesignManagement::Version:
- created_at
- sha
- issue_id
-- user_id
+- author_id
+ZoomMeeting:
+- id
+- issue_id
+- project_id
+- issue_status
+- url
+- created_at
+- updated_at
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
index d185ff2dfcc..aca63953677 100644
--- a/spec/lib/gitlab/import_export/saver_spec.rb
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -5,7 +5,7 @@ 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) }
+ subject { described_class.new(exportable: project, shared: shared) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
diff --git a/spec/lib/gitlab/import_export/shared_spec.rb b/spec/lib/gitlab/import_export/shared_spec.rb
index 62669836973..fc011f7e1be 100644
--- a/spec/lib/gitlab/import_export/shared_spec.rb
+++ b/spec/lib/gitlab/import_export/shared_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::ImportExport::Shared do
context 'with a repository on disk' do
let(:project) { create(:project, :repository) }
- let(:base_path) { %(/tmp/project_exports/#{project.disk_path}/) }
+ let(:base_path) { %(/tmp/gitlab_exports/#{project.disk_path}/) }
describe '#archive_path' do
it 'uses a random hash to avoid conflicts' do
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 8060b5d4448..265241dc2af 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportSources do
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index 2db62ab983a..598336d0b31 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::IncomingEmail do
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
index 6532579b1c9..7f20ae98b06 100644
--- a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::InsecureKeyFingerprint do
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
new file mode 100644
index 00000000000..c2674638743
--- /dev/null
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+describe Gitlab::InstrumentationHelper do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '.queue_duration_for_job' do
+ where(:enqueued_at, :created_at, :time_now, :expected_duration) do
+ "2019-06-01T00:00:00.000+0000" | nil | "2019-06-01T02:00:00.000+0000" | 2.hours.to_f
+ "2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T02:00:00.001+0000" | 0.001
+ "2019-06-01T02:00:00.000+0000" | "2019-05-01T02:00:00.000+0000" | "2019-06-01T02:00:01.000+0000" | 1
+ nil | "2019-06-01T02:00:00.000+0000" | "2019-06-01T02:00:00.001+0000" | 0.001
+ nil | nil | "2019-06-01T02:00:00.001+0000" | nil
+ "2019-06-01T02:00:00.000+0200" | nil | "2019-06-01T02:00:00.000-0200" | 4.hours.to_f
+ 1571825569.998168 | nil | "2019-10-23T12:13:16.000+0200" | 26.001832
+ 1571825569 | nil | "2019-10-23T12:13:16.000+0200" | 27
+ "invalid_date" | nil | "2019-10-23T12:13:16.000+0200" | nil
+ "" | nil | "2019-10-23T12:13:16.000+0200" | nil
+ 0 | nil | "2019-10-23T12:13:16.000+0200" | nil
+ -1 | nil | "2019-10-23T12:13:16.000+0200" | nil
+ "2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T00:00:00.000+0000" | 0
+ Time.at(1571999233) | nil | "2019-10-25T12:29:16.000+0200" | 123
+ end
+
+ with_them do
+ let(:job) { { 'enqueued_at' => enqueued_at, 'created_at' => created_at } }
+
+ it "returns the correct duration" do
+ Timecop.freeze(Time.iso8601(time_now)) do
+ expect(described_class.queue_duration_for_job(job)).to eq(expected_duration)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/issuable_metadata_spec.rb b/spec/lib/gitlab/issuable_metadata_spec.rb
index 032467b8b4e..7632bc3060a 100644
--- a/spec/lib/gitlab/issuable_metadata_spec.rb
+++ b/spec/lib/gitlab/issuable_metadata_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::IssuableMetadata do
diff --git a/spec/lib/gitlab/issuable_sorter_spec.rb b/spec/lib/gitlab/issuable_sorter_spec.rb
index 5bd76bc6081..486e9539b92 100644
--- a/spec/lib/gitlab/issuable_sorter_spec.rb
+++ b/spec/lib/gitlab/issuable_sorter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::IssuableSorter do
diff --git a/spec/lib/gitlab/issuables_count_for_state_spec.rb b/spec/lib/gitlab/issuables_count_for_state_spec.rb
index c262fdfcb61..9380aa53470 100644
--- a/spec/lib/gitlab/issuables_count_for_state_spec.rb
+++ b/spec/lib/gitlab/issuables_count_for_state_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::IssuablesCountForState do
diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb
index b0b4fdc09bc..efa7fd4b975 100644
--- a/spec/lib/gitlab/job_waiter_spec.rb
+++ b/spec/lib/gitlab/job_waiter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::JobWaiter do
diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb
index 3d4f9b5db86..5d544198c40 100644
--- a/spec/lib/gitlab/json_logger_spec.rb
+++ b/spec/lib/gitlab/json_logger_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::JsonLogger do
diff --git a/spec/lib/gitlab/kubernetes/config_maps/aws_node_auth_spec.rb b/spec/lib/gitlab/kubernetes/config_maps/aws_node_auth_spec.rb
new file mode 100644
index 00000000000..f701643860a
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/config_maps/aws_node_auth_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::ConfigMaps::AwsNodeAuth do
+ describe '#generate' do
+ let(:role) { 'arn:aws:iam::123456789012:role/node-instance-role' }
+
+ let(:name) { 'aws-auth' }
+ let(:namespace) { 'kube-system' }
+ let(:role_config) do
+ [{
+ 'rolearn' => role,
+ 'username' => 'system:node:{{EC2PrivateDNSName}}',
+ 'groups' => [
+ 'system:bootstrappers',
+ 'system:nodes'
+ ]
+ }]
+ end
+
+ subject { described_class.new(role).generate }
+
+ it 'builds a Kubeclient Resource' do
+ expect(subject).to be_a(Kubeclient::Resource)
+
+ expect(subject.metadata.name).to eq(name)
+ expect(subject.metadata.namespace).to eq(namespace)
+
+ expect(YAML.safe_load(subject.data.mapRoles)).to eq(role_config)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 9eb3322f1a6..e5a361bdab3 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -86,33 +86,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
end
end
- context 'when there is no repository' do
- let(:repository) { nil }
-
- it_behaves_like 'helm commands' do
- let(:commands) do
- <<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
- #{helm_install_command}
- EOS
- end
-
- let(:helm_install_command) do
- <<~EOS.squish
- helm upgrade app-name chart-name
- --install
- --reset-values
- #{tls_flags}
- --version 1.2.3
- --set rbac.create\\=false,rbac.enabled\\=false
- --namespace gitlab-managed-apps
- -f /data/helm/app-name/config/values.yaml
- EOS
- end
- end
- end
-
context 'when there is a pre-install script' do
let(:preinstall) { ['/bin/date', '/bin/true'] }
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 64cadcc011c..e1b4bd0b664 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'generates the appropriate specifications for the container' do
container = subject.generate.spec.containers.first
expect(container.name).to eq('helm')
- expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.14.3-kube-1.11.10')
+ expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.16.1-kube-1.13.12')
expect(container.env.count).to eq(3)
expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
expect(container.command).to match_array(["/bin/sh"])
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index a7ea942960b..31bfd20449d 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes do
diff --git a/spec/lib/gitlab/language_detection_spec.rb b/spec/lib/gitlab/language_detection_spec.rb
index 9636fbd401b..f558ce0d527 100644
--- a/spec/lib/gitlab/language_detection_spec.rb
+++ b/spec/lib/gitlab/language_detection_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LanguageDetection do
diff --git a/spec/lib/gitlab/lazy_spec.rb b/spec/lib/gitlab/lazy_spec.rb
index 37a3ac74316..19758a18589 100644
--- a/spec/lib/gitlab/lazy_spec.rb
+++ b/spec/lib/gitlab/lazy_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Lazy do
diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
index af5df1fab43..697bedf7362 100644
--- a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb
@@ -136,7 +136,7 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
describe '.find_all_paths' do
let(:all_dashboard_paths) { described_class.find_all_paths(project) }
- let(:system_dashboard) { { path: system_dashboard_path, display_name: 'Default', default: true } }
+ let(:system_dashboard) { { path: system_dashboard_path, display_name: 'Default', default: true, system_dashboard: true } }
it 'includes only the system dashboard by default' do
expect(all_dashboard_paths).to eq([system_dashboard])
@@ -147,7 +147,7 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
let(:project) { project_with_dashboard(dashboard_path) }
it 'includes system and project dashboards' do
- project_dashboard = { path: dashboard_path, display_name: 'test.yml', default: false }
+ project_dashboard = { path: dashboard_path, display_name: 'test.yml', default: false, system_dashboard: false }
expect(all_dashboard_paths).to contain_exactly(system_dashboard, project_dashboard)
end
diff --git a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
index e2ce1869810..4fa136bc405 100644
--- a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb
@@ -25,6 +25,14 @@ describe Gitlab::Metrics::Dashboard::Processor do
end
end
+ context 'when the dashboard is not present' do
+ let(:dashboard_yml) { nil }
+
+ it 'returns nil' do
+ expect(dashboard).to be_nil
+ end
+ end
+
context 'when dashboard config corresponds to common metrics' do
let!(:common_metric) { create(:prometheus_metric, :common, identifier: 'metric_a1') }
diff --git a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
index 095d0a2df78..0d4562f78f1 100644
--- a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
@@ -75,6 +75,17 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
it { is_expected.to be Metrics::Dashboard::CustomMetricEmbedService }
end
+
+ context 'with a grafana link' do
+ let(:arguments) do
+ {
+ embedded: true,
+ grafana_url: 'https://grafana.example.com'
+ }
+ end
+
+ it { is_expected.to be Metrics::Dashboard::GrafanaMetricEmbedService }
+ end
end
end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb b/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb
new file mode 100644
index 00000000000..5c2ec6dae6b
--- /dev/null
+++ b/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter do
+ include GrafanaApiHelpers
+
+ let_it_be(:namespace) { create(:namespace, name: 'foo') }
+ let_it_be(:project) { create(:project, namespace: namespace, name: 'bar') }
+
+ describe '#transform!' do
+ let(:grafana_dashboard) { JSON.parse(fixture_file('grafana/simplified_dashboard_response.json'), symbolize_names: true) }
+ let(:datasource) { JSON.parse(fixture_file('grafana/datasource_response.json'), symbolize_names: true) }
+
+ let(:dashboard) { described_class.new(project, {}, params).transform! }
+
+ let(:params) do
+ {
+ grafana_dashboard: grafana_dashboard,
+ datasource: datasource,
+ grafana_url: valid_grafana_dashboard_link('https://grafana.example.com')
+ }
+ end
+
+ context 'when the query and resources are configured correctly' do
+ let(:expected_dashboard) { JSON.parse(fixture_file('grafana/expected_grafana_embed.json'), symbolize_names: true) }
+
+ it 'generates a gitlab-yml formatted dashboard' do
+ expect(dashboard).to eq(expected_dashboard)
+ end
+ end
+
+ context 'when the inputs are invalid' do
+ shared_examples_for 'processing error' do
+ it 'raises a processing error' do
+ expect { dashboard }
+ .to raise_error(Gitlab::Metrics::Dashboard::Stages::InputFormatValidator::DashboardProcessingError)
+ end
+ end
+
+ context 'when the datasource is not proxyable' do
+ before do
+ params[:datasource][:access] = 'not-proxy'
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when query param "panelId" is not specified' do
+ before do
+ params[:grafana_url].gsub!('panelId=8', '')
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when query param "from" is not specified' do
+ before do
+ params[:grafana_url].gsub!('from=1570397739557', '')
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when query param "to" is not specified' do
+ before do
+ params[:grafana_url].gsub!('to=1570484139557', '')
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when the panel is not a graph' do
+ before do
+ params[:grafana_dashboard][:dashboard][:panels][0][:type] = 'singlestat'
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when the panel is not a line graph' do
+ before do
+ params[:grafana_dashboard][:dashboard][:panels][0][:lines] = false
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when the query dashboard includes undefined variables' do
+ before do
+ params[:grafana_url].gsub!('&var-instance=localhost:9121', '')
+ end
+
+ it_behaves_like 'processing error'
+ end
+
+ context 'when the expression contains unsupported global variables' do
+ before do
+ params[:grafana_dashboard][:dashboard][:panels][0][:targets][0][:expr] = 'sum(important_metric[$__interval_ms])'
+ end
+
+ it_behaves_like 'processing error'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
index e0dc6d98efc..daaf66cba46 100644
--- a/spec/lib/gitlab/metrics/dashboard/url_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
@@ -3,13 +3,41 @@
require 'spec_helper'
describe Gitlab::Metrics::Dashboard::Url do
- describe '#regex' do
- it 'returns a regular expression' do
- expect(described_class.regex).to be_a Regexp
- end
+ shared_examples_for 'a regex which matches the expected url' do
+ it { is_expected.to be_a Regexp }
it 'matches a metrics dashboard link with named params' do
- url = Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(
+ expect(subject).to match url
+
+ subject.match(url) do |m|
+ expect(m.named_captures).to eq expected_params
+ end
+ end
+ end
+
+ shared_examples_for 'does not match non-matching urls' do
+ it 'does not match other gitlab urls that contain the term metrics' do
+ url = Gitlab::Routing.url_helpers.active_common_namespace_project_prometheus_metrics_url('foo', 'bar', :json)
+
+ expect(subject).not_to match url
+ end
+
+ it 'does not match other gitlab urls' do
+ url = Gitlab.config.gitlab.url
+
+ expect(subject).not_to match url
+ end
+
+ it 'does not match non-gitlab urls' do
+ url = 'https://www.super_awesome_site.com/'
+
+ expect(subject).not_to match url
+ end
+ end
+
+ describe '#regex' do
+ let(:url) do
+ Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(
'foo',
'bar',
1,
@@ -18,8 +46,10 @@ describe Gitlab::Metrics::Dashboard::Url do
group: 'awesome group',
anchor: 'title'
)
+ end
- expected_params = {
+ let(:expected_params) do
+ {
'url' => url,
'namespace' => 'foo',
'project' => 'bar',
@@ -27,31 +57,40 @@ describe Gitlab::Metrics::Dashboard::Url do
'query' => '?dashboard=config%2Fprometheus%2Fcommon_metrics.yml&group=awesome+group&start=2019-08-02T05%3A43%3A09.000Z',
'anchor' => '#title'
}
-
- expect(described_class.regex).to match url
-
- described_class.regex.match(url) do |m|
- expect(m.named_captures).to eq expected_params
- end
end
- it 'does not match other gitlab urls that contain the term metrics' do
- url = Gitlab::Routing.url_helpers.active_common_namespace_project_prometheus_metrics_url('foo', 'bar', :json)
+ subject { described_class.regex }
- expect(described_class.regex).not_to match url
- end
+ it_behaves_like 'a regex which matches the expected url'
+ it_behaves_like 'does not match non-matching urls'
+ end
- it 'does not match other gitlab urls' do
- url = Gitlab.config.gitlab.url
+ describe '#grafana_regex' do
+ let(:url) do
+ Gitlab::Routing.url_helpers.namespace_project_grafana_api_metrics_dashboard_url(
+ 'foo',
+ 'bar',
+ start: '2019-08-02T05:43:09.000Z',
+ dashboard: 'config/prometheus/common_metrics.yml',
+ group: 'awesome group',
+ anchor: 'title'
+ )
+ end
- expect(described_class.regex).not_to match url
+ let(:expected_params) do
+ {
+ 'url' => url,
+ 'namespace' => 'foo',
+ 'project' => 'bar',
+ 'query' => '?dashboard=config%2Fprometheus%2Fcommon_metrics.yml&group=awesome+group&start=2019-08-02T05%3A43%3A09.000Z',
+ 'anchor' => '#title'
+ }
end
- it 'does not match non-gitlab urls' do
- url = 'https://www.super_awesome_site.com/'
+ subject { described_class.grafana_regex }
- expect(described_class.regex).not_to match url
- end
+ it_behaves_like 'a regex which matches the expected url'
+ it_behaves_like 'does not match non-matching urls'
end
describe '#build_dashboard_url' do
diff --git a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb
index 99349934e63..f22993cf057 100644
--- a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb
+++ b/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb
@@ -4,61 +4,41 @@ require 'spec_helper'
describe Gitlab::Metrics::Exporter::WebExporter do
let(:exporter) { described_class.new }
-
- context 'when blackout seconds is used' do
- let(:blackout_seconds) { 0 }
- let(:readiness_probe) { exporter.send(:readiness_probe).execute }
-
- before do
- stub_config(
- monitoring: {
- web_exporter: {
- enabled: true,
- port: 0,
- address: '127.0.0.1',
- blackout_seconds: blackout_seconds
- }
+ let(:readiness_probe) { exporter.send(:readiness_probe).execute }
+
+ before do
+ stub_config(
+ monitoring: {
+ web_exporter: {
+ enabled: true,
+ port: 0,
+ address: '127.0.0.1'
}
- )
-
- exporter.start
- end
-
- after do
- exporter.stop
- end
+ }
+ )
- context 'when running server' do
- it 'readiness probe returns succesful status' do
- expect(readiness_probe.http_status).to eq(200)
- expect(readiness_probe.json).to include(status: 'ok')
- expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'ok' }])
- end
- end
-
- context 'when blackout seconds is 10s' do
- let(:blackout_seconds) { 10 }
+ exporter.start
+ end
- it 'readiness probe returns a failure status' do
- # during sleep we check the status of readiness probe
- expect(exporter).to receive(:sleep).with(10) do
- expect(readiness_probe.http_status).to eq(503)
- expect(readiness_probe.json).to include(status: 'failed')
- expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'failed' }])
- end
+ after do
+ exporter.stop
+ end
- exporter.stop
- end
+ context 'when running server' do
+ it 'readiness probe returns succesful status' do
+ expect(readiness_probe.http_status).to eq(200)
+ expect(readiness_probe.json).to include(status: 'ok')
+ expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'ok' }])
end
+ end
- context 'when blackout is disabled' do
- let(:blackout_seconds) { 0 }
-
- it 'readiness probe returns a failure status' do
- expect(exporter).not_to receive(:sleep)
+ describe '#mark_as_not_running!' do
+ it 'readiness probe returns a failure status' do
+ exporter.mark_as_not_running!
- exporter.stop
- end
+ expect(readiness_probe.http_status).to eq(503)
+ expect(readiness_probe.json).to include(status: 'failed')
+ expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'failed' }])
end
end
end
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index f48cd096a98..335670278c4 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -31,7 +31,7 @@ describe Gitlab::Metrics::RequestsRackMiddleware do
end
it 'measures execution time' do
- expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ status: 200, method: 'get' }, a_positive_execution_time)
+ expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ status: '200', method: 'get' }, a_positive_execution_time)
Timecop.scale(3600) { subject.call(env) }
end
@@ -69,7 +69,7 @@ describe Gitlab::Metrics::RequestsRackMiddleware do
expected_labels = []
described_class::HTTP_METHODS.each do |method, statuses|
statuses.each do |status|
- expected_labels << { method: method, status: status.to_i }
+ expected_labels << { method: method, status: status.to_s }
end
end
diff --git a/spec/lib/gitlab/pagination/offset_pagination_spec.rb b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
new file mode 100644
index 00000000000..9c7dd385726
--- /dev/null
+++ b/spec/lib/gitlab/pagination/offset_pagination_spec.rb
@@ -0,0 +1,215 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Pagination::OffsetPagination do
+ let(:resource) { Project.all }
+ let(:custom_port) { 8080 }
+ let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:#{custom_port}/api/v4/projects" }
+
+ before do
+ stub_config_setting(port: custom_port)
+ end
+
+ let(:request_context) { double("request_context") }
+
+ subject do
+ described_class.new(request_context)
+ end
+
+ describe '#paginate' do
+ let(:value) { spy('return value') }
+ let(:base_query) { { foo: 'bar', bar: 'baz' } }
+ let(:query) { base_query }
+
+ before do
+ allow(request_context).to receive(:header).and_return(value)
+ allow(request_context).to receive(:params).and_return(query)
+ allow(request_context).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
+ end
+
+ context 'when resource can be paginated' do
+ before do
+ create_list(:project, 3)
+ end
+
+ describe 'first page' do
+ shared_examples 'response with pagination headers' do
+ it 'adds appropriate headers' do
+ expect_header('X-Total', '3')
+ expect_header('X-Total-Pages', '2')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '1')
+ expect_header('X-Next-Page', '2')
+ expect_header('X-Prev-Page', '')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).not_to include('rel="prev"')
+ end
+
+ subject.paginate(resource)
+ end
+ end
+
+ shared_examples 'paginated response' do
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 2
+ end
+
+ it 'executes only one SELECT COUNT query' do
+ expect { subject.paginate(resource) }.to make_queries_matching(/SELECT COUNT/, 1)
+ end
+ end
+
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
+
+ context 'when the api_kaminari_count_with_limit feature flag is unset' do
+ it_behaves_like 'paginated response'
+ it_behaves_like 'response with pagination headers'
+ end
+
+ context 'when the api_kaminari_count_with_limit feature flag is disabled' do
+ before do
+ stub_feature_flags(api_kaminari_count_with_limit: false)
+ end
+
+ it_behaves_like 'paginated response'
+ it_behaves_like 'response with pagination headers'
+ end
+
+ context 'when the api_kaminari_count_with_limit feature flag is enabled' do
+ before do
+ stub_feature_flags(api_kaminari_count_with_limit: true)
+ end
+
+ context 'when resources count is less than MAX_COUNT_LIMIT' do
+ before do
+ stub_const("::Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT", 4)
+ end
+
+ it_behaves_like 'paginated response'
+ it_behaves_like 'response with pagination headers'
+ end
+
+ context 'when resources count is more than MAX_COUNT_LIMIT' do
+ before do
+ stub_const("::Kaminari::ActiveRecordRelationMethods::MAX_COUNT_LIMIT", 2)
+ end
+
+ it_behaves_like 'paginated response'
+
+ it 'does not return the X-Total and X-Total-Pages headers' do
+ expect_no_header('X-Total')
+ expect_no_header('X-Total-Pages')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '1')
+ expect_header('X-Next-Page', '2')
+ expect_header('X-Prev-Page', '')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
+ expect(val).not_to include('rel="last"')
+ expect(val).not_to include('rel="prev"')
+ end
+
+ subject.paginate(resource)
+ end
+ end
+ end
+ end
+
+ describe 'second page' do
+ let(:query) { base_query.merge(page: 2, per_page: 2) }
+
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 1
+ end
+
+ it 'adds appropriate headers' do
+ expect_header('X-Total', '3')
+ expect_header('X-Total-Pages', '2')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '2')
+ expect_header('X-Next-Page', '')
+ expect_header('X-Prev-Page', '1')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
+ expect(val).not_to include('rel="next"')
+ end
+
+ subject.paginate(resource)
+ end
+ end
+
+ context 'if order' do
+ it 'is not present it adds default order(:id) if no order is present' do
+ resource.order_values = []
+
+ paginated_relation = subject.paginate(resource)
+
+ expect(resource.order_values).to be_empty
+ expect(paginated_relation.order_values).to be_present
+ expect(paginated_relation.order_values.first).to be_ascending
+ expect(paginated_relation.order_values.first.expr.name).to eq 'id'
+ end
+
+ it 'is present it does not add anything' do
+ paginated_relation = subject.paginate(resource.order(created_at: :desc))
+
+ expect(paginated_relation.order_values).to be_present
+ expect(paginated_relation.order_values.first).to be_descending
+ expect(paginated_relation.order_values.first.expr.name).to eq 'created_at'
+ end
+ end
+ end
+
+ context 'when resource empty' do
+ describe 'first page' do
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
+
+ it 'returns appropriate amount of resources' do
+ expect(subject.paginate(resource).count).to eq 0
+ end
+
+ it 'adds appropriate headers' do
+ expect_header('X-Total', '0')
+ expect_header('X-Total-Pages', '1')
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Page', '1')
+ expect_header('X-Next-Page', '')
+ expect_header('X-Prev-Page', '')
+
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{incoming_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
+ expect(val).not_to include('rel="prev"')
+ expect(val).not_to include('rel="next"')
+ expect(val).not_to include('page=0')
+ end
+
+ subject.paginate(resource)
+ end
+ end
+ end
+ end
+
+ def expect_header(*args, &block)
+ expect(subject).to receive(:header).with(*args, &block)
+ end
+
+ def expect_no_header(*args, &block)
+ expect(subject).not_to receive(:header).with(*args)
+ end
+
+ def expect_message(method)
+ expect(subject).to receive(method)
+ .at_least(:once).and_return(value)
+ end
+end
diff --git a/spec/lib/gitlab/phabricator_import/project_creator_spec.rb b/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
index e9455b866ac..fd17284eea2 100644
--- a/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::PhabricatorImport::ProjectCreator do
subject(:creator) { described_class.new(user, params) }
describe '#execute' do
- it 'creates a project correctly and schedule an import' do
+ it 'creates a project correctly and schedule an import', :sidekiq_might_not_need_inline do
expect_next_instance_of(Gitlab::PhabricatorImport::Importer) do |importer|
expect(importer).to receive(:execute)
end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 82ccb42f8a6..6e5c36172e2 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -3,48 +3,55 @@
require 'spec_helper'
describe Gitlab::ProjectAuthorizations do
- let(:group) { create(:group) }
- let!(:owned_project) { create(:project) }
- let!(:other_project) { create(:project) }
- let!(:group_project) { create(:project, namespace: group) }
-
- let(:user) { owned_project.namespace.owner }
-
def map_access_levels(rows)
rows.each_with_object({}) do |row, hash|
hash[row.project_id] = row.access_level
end
end
- before do
- other_project.add_reporter(user)
- group.add_developer(user)
- end
-
- let(:authorizations) do
+ subject(:authorizations) do
described_class.new(user).calculate
end
- it 'returns the correct number of authorizations' do
- expect(authorizations.length).to eq(3)
- end
+ context 'user added to group and project' do
+ let(:group) { create(:group) }
+ let!(:other_project) { create(:project) }
+ let!(:group_project) { create(:project, namespace: group) }
+ let!(:owned_project) { create(:project) }
+ let(:user) { owned_project.namespace.owner }
- it 'includes the correct projects' do
- expect(authorizations.pluck(:project_id))
- .to include(owned_project.id, other_project.id, group_project.id)
- end
+ before do
+ other_project.add_reporter(user)
+ group.add_developer(user)
+ end
+
+ it 'returns the correct number of authorizations' do
+ expect(authorizations.length).to eq(3)
+ end
- it 'includes the correct access levels' do
- mapping = map_access_levels(authorizations)
+ it 'includes the correct projects' do
+ expect(authorizations.pluck(:project_id))
+ .to include(owned_project.id, other_project.id, group_project.id)
+ end
+
+ it 'includes the correct access levels' do
+ mapping = map_access_levels(authorizations)
- 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)
+ 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
end
context 'with nested groups' do
+ let(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
let!(:nested_project) { create(:project, namespace: nested_group) }
+ let(:user) { create(:user) }
+
+ before do
+ group.add_developer(user)
+ end
it 'includes nested groups' do
expect(authorizations.pluck(:project_id)).to include(nested_project.id)
@@ -64,4 +71,114 @@ describe Gitlab::ProjectAuthorizations do
expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
+
+ context 'with shared groups' do
+ let(:parent_group_user) { create(:user) }
+ let(:group_user) { create(:user) }
+ let(:child_group_user) { create(:user) }
+
+ let_it_be(:group_parent) { create(:group, :private) }
+ let_it_be(:group) { create(:group, :private, parent: group_parent) }
+ let_it_be(:group_child) { create(:group, :private, parent: group) }
+
+ let_it_be(:shared_group_parent) { create(:group, :private) }
+ let_it_be(:shared_group) { create(:group, :private, parent: shared_group_parent) }
+ let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) }
+
+ let_it_be(:project_parent) { create(:project, group: shared_group_parent) }
+ let_it_be(:project) { create(:project, group: shared_group) }
+ let_it_be(:project_child) { create(:project, group: shared_group_child) }
+
+ before do
+ group_parent.add_owner(parent_group_user)
+ group.add_owner(group_user)
+ group_child.add_owner(child_group_user)
+
+ create(:group_group_link, shared_group: shared_group, shared_with_group: group)
+ end
+
+ context 'when feature flag share_group_with_group is enabled' do
+ before do
+ stub_feature_flags(share_group_with_group: true)
+ end
+
+ context 'group user' do
+ let(:user) { group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER)
+ expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER)
+ end
+ end
+
+ context 'parent group user' do
+ let(:user) { parent_group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to be_nil
+ expect(mapping[project_child.id]).to be_nil
+ end
+ end
+
+ context 'child group user' do
+ let(:user) { child_group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to be_nil
+ expect(mapping[project_child.id]).to be_nil
+ end
+ end
+ end
+
+ context 'when feature flag share_group_with_group is disabled' do
+ before do
+ stub_feature_flags(share_group_with_group: false)
+ end
+
+ context 'group user' do
+ let(:user) { group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to be_nil
+ expect(mapping[project_child.id]).to be_nil
+ end
+ end
+
+ context 'parent group user' do
+ let(:user) { parent_group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to be_nil
+ expect(mapping[project_child.id]).to be_nil
+ end
+ end
+
+ context 'child group user' do
+ let(:user) { child_group_user }
+
+ it 'creates proper authorizations' do
+ mapping = map_access_levels(authorizations)
+
+ expect(mapping[project_parent.id]).to be_nil
+ expect(mapping[project.id]).to be_nil
+ expect(mapping[project_child.id]).to be_nil
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index d6e50c672e6..99078f19361 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -79,20 +79,20 @@ describe Gitlab::ProjectSearchResults do
end
it 'finds by name' do
- expect(results.map(&:filename)).to include(expected_file_by_name)
+ expect(results.map(&:path)).to include(expected_file_by_path)
end
- it "loads all blobs for filename matches in single batch" do
+ it "loads all blobs for path matches in single batch" do
expect(Gitlab::Git::Blob).to receive(:batch).once.and_call_original
expected = project.repository.search_files_by_name(query, 'master')
- expect(results.map(&:filename)).to include(*expected)
+ expect(results.map(&:path)).to include(*expected)
end
it 'finds by content' do
- blob = results.select { |result| result.filename == expected_file_by_content }.flatten.last
+ blob = results.select { |result| result.path == expected_file_by_content }.flatten.last
- expect(blob.filename).to eq(expected_file_by_content)
+ expect(blob.path).to eq(expected_file_by_content)
end
end
@@ -146,7 +146,7 @@ describe Gitlab::ProjectSearchResults do
let(:blob_type) { 'blobs' }
let(:disabled_project) { create(:project, :public, :repository, :repository_disabled) }
let(:private_project) { create(:project, :public, :repository, :repository_private) }
- let(:expected_file_by_name) { 'files/images/wm.svg' }
+ let(:expected_file_by_path) { 'files/images/wm.svg' }
let(:expected_file_by_content) { 'CHANGELOG' }
end
@@ -169,7 +169,7 @@ describe Gitlab::ProjectSearchResults do
let(:blob_type) { 'wiki_blobs' }
let(:disabled_project) { create(:project, :public, :wiki_repo, :wiki_disabled) }
let(:private_project) { create(:project, :public, :wiki_repo, :wiki_private) }
- let(:expected_file_by_name) { 'Files/Title.md' }
+ let(:expected_file_by_path) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 83acd979a80..5559b1e4291 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -22,7 +22,8 @@ describe Gitlab::ProjectTemplate do
described_class.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll'),
described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
- described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo')
+ described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo'),
+ described_class.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
]
expect(described_class.all).to be_an(Array)
diff --git a/spec/lib/gitlab/prometheus/internal_spec.rb b/spec/lib/gitlab/prometheus/internal_spec.rb
new file mode 100644
index 00000000000..884bdcb4e9b
--- /dev/null
+++ b/spec/lib/gitlab/prometheus/internal_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Prometheus::Internal do
+ let(:listen_address) { 'localhost:9090' }
+
+ let(:prometheus_settings) do
+ {
+ enable: true,
+ listen_address: listen_address
+ }
+ end
+
+ before do
+ stub_config(prometheus: prometheus_settings)
+ end
+
+ describe '.uri' do
+ shared_examples 'returns valid uri' do |uri_string|
+ it do
+ expect(described_class.uri).to eq(uri_string)
+ expect { Addressable::URI.parse(described_class.uri) }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'returns valid uri', 'http://localhost:9090'
+
+ context 'with non default prometheus address' do
+ let(:listen_address) { 'https://localhost:9090' }
+
+ it_behaves_like 'returns valid uri', 'https://localhost:9090'
+
+ context 'with :9090 symbol' do
+ let(:listen_address) { :':9090' }
+
+ it_behaves_like 'returns valid uri', 'http://localhost:9090'
+ end
+
+ context 'with 0.0.0.0:9090' do
+ let(:listen_address) { '0.0.0.0:9090' }
+
+ it_behaves_like 'returns valid uri', 'http://localhost:9090'
+ end
+ end
+
+ context 'when listen_address is nil' do
+ let(:listen_address) { nil }
+
+ it 'does not fail' do
+ expect(described_class.uri).to eq(nil)
+ end
+ end
+
+ context 'when prometheus listen address is blank in gitlab.yml' do
+ let(:listen_address) { '' }
+
+ it 'does not configure prometheus' do
+ expect(described_class.uri).to eq(nil)
+ end
+ end
+ end
+
+ describe 'prometheus_enabled?' do
+ it 'returns correct value' do
+ expect(described_class.prometheus_enabled?).to eq(true)
+ end
+
+ context 'when prometheus setting is disabled in gitlab.yml' do
+ let(:prometheus_settings) do
+ {
+ enable: false,
+ listen_address: listen_address
+ }
+ end
+
+ it 'returns correct value' do
+ expect(described_class.prometheus_enabled?).to eq(false)
+ end
+ end
+
+ context 'when prometheus setting is not present in gitlab.yml' do
+ before do
+ allow(Gitlab.config).to receive(:prometheus).and_raise(Settingslogic::MissingSetting)
+ end
+
+ it 'does not fail' do
+ expect(described_class.prometheus_enabled?).to eq(false)
+ end
+ end
+ end
+
+ describe '.listen_address' do
+ it 'returns correct value' do
+ expect(described_class.listen_address).to eq(listen_address)
+ end
+
+ context 'when prometheus setting is not present in gitlab.yml' do
+ before do
+ allow(Gitlab.config).to receive(:prometheus).and_raise(Settingslogic::MissingSetting)
+ end
+
+ it 'does not fail' do
+ expect(described_class.listen_address).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
index 7f6283715f2..6361893c53c 100644
--- a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
@@ -13,14 +13,19 @@ describe Gitlab::Prometheus::Queries::KnativeInvocationQuery do
context 'verify queries' do
before do
- allow(PrometheusMetric).to receive(:find_by_identifier).and_return(create(:prometheus_metric, query: prometheus_istio_query('test-name', 'test-ns')))
- allow(client).to receive(:query_range)
+ create(:prometheus_metric,
+ :common,
+ identifier: :system_metrics_knative_function_invocation_count,
+ query: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_app=~"%{function_name}.*"}[1m])*60))')
end
it 'has the query, but no data' do
- results = subject.query(serverless_func.id)
+ expect(client).to receive(:query_range).with(
+ 'sum(ceil(rate(istio_requests_total{destination_service_namespace="test-ns", destination_app=~"test-name.*"}[1m])*60))',
+ hash_including(:start, :stop)
+ )
- expect(results.queries[0][:query_range]).to eql('floor(sum(rate(istio_revision_request_count{destination_configuration="test-name", destination_namespace="test-ns"}[1m])*30))')
+ subject.query(serverless_func.id)
end
end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index b557baed258..1397add9f5a 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -66,6 +66,15 @@ describe Gitlab::Regex do
end
describe '.aws_account_id_regex' do
+ subject { described_class.aws_account_id_regex }
+
+ it { is_expected.to match('123456789012') }
+ it { is_expected.not_to match('12345678901') }
+ it { is_expected.not_to match('1234567890123') }
+ it { is_expected.not_to match('12345678901a') }
+ end
+
+ describe '.aws_arn_regex' do
subject { described_class.aws_arn_regex }
it { is_expected.to match('arn:aws:iam::123456789012:role/role-name') }
@@ -75,4 +84,14 @@ describe Gitlab::Regex do
it { is_expected.not_to match('123456789012') }
it { is_expected.not_to match('role/role-name') }
end
+
+ describe '.utc_date_regex' do
+ subject { described_class.utc_date_regex }
+
+ it { is_expected.to match('2019-10-20') }
+ it { is_expected.to match('1990-01-01') }
+ it { is_expected.not_to match('11-1234-90') }
+ it { is_expected.not_to match('aa-1234-cc') }
+ it { is_expected.not_to match('9/9/2018') }
+ end
end
diff --git a/spec/lib/gitlab/search/found_blob_spec.rb b/spec/lib/gitlab/search/found_blob_spec.rb
index a575f6e2f11..07842faa638 100644
--- a/spec/lib/gitlab/search/found_blob_spec.rb
+++ b/spec/lib/gitlab/search/found_blob_spec.rb
@@ -15,7 +15,6 @@ describe Gitlab::Search::FoundBlob do
is_expected.to be_an described_class
expect(subject.id).to be_nil
expect(subject.path).to eq('CHANGELOG')
- expect(subject.filename).to eq('CHANGELOG')
expect(subject.basename).to eq('CHANGELOG')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(188)
@@ -25,12 +24,12 @@ describe Gitlab::Search::FoundBlob do
it 'does not parse content if not needed' do
expect(subject).not_to receive(:parse_search_result)
expect(subject.project_id).to eq(project.id)
- expect(subject.binary_filename).to eq('CHANGELOG')
+ expect(subject.binary_path).to eq('CHANGELOG')
end
it 'parses content only once when needed' do
expect(subject).to receive(:parse_search_result).once.and_call_original
- expect(subject.filename).to eq('CHANGELOG')
+ expect(subject.path).to eq('CHANGELOG')
expect(subject.startline).to eq(188)
end
@@ -38,7 +37,7 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { "master:testdata/project::function1.yaml\x001\x00---\n" }
it 'returns a valid FoundBlob' do
- expect(subject.filename).to eq('testdata/project::function1.yaml')
+ expect(subject.path).to eq('testdata/project::function1.yaml')
expect(subject.basename).to eq('testdata/project::function1')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -50,7 +49,7 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { "master:testdata/foo.txt\x001\x00blah:9:blah" }
it 'returns a valid FoundBlob' do
- expect(subject.filename).to eq('testdata/foo.txt')
+ expect(subject.path).to eq('testdata/foo.txt')
expect(subject.basename).to eq('testdata/foo')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -62,7 +61,7 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { "master:testdata/foo.txt\x001\x00blah\x001\x00foo" }
it 'returns a valid FoundBlob' do
- expect(subject.filename).to eq('testdata/foo.txt')
+ expect(subject.path).to eq('testdata/foo.txt')
expect(subject.basename).to eq('testdata/foo')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -74,7 +73,7 @@ describe Gitlab::Search::FoundBlob do
let(:results) { project.repository.search_files_by_content('Role models', 'master') }
it 'returns a valid FoundBlob that ends with an empty line' do
- expect(subject.filename).to eq('files/markdown/ruby-style-guide.md')
+ expect(subject.path).to eq('files/markdown/ruby-style-guide.md')
expect(subject.basename).to eq('files/markdown/ruby-style-guide')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -87,7 +86,7 @@ describe Gitlab::Search::FoundBlob do
let(:results) { project.repository.search_files_by_content('ั„ะฐะนะป', 'master') }
it 'returns results as UTF-8' do
- expect(subject.filename).to eq('encoding/russian.rb')
+ expect(subject.path).to eq('encoding/russian.rb')
expect(subject.basename).to eq('encoding/russian')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -99,7 +98,7 @@ describe Gitlab::Search::FoundBlob do
let(:results) { project.repository.search_files_by_content('webhook', 'master') }
it 'returns results as UTF-8' do
- expect(subject.filename).to eq('encoding/ใƒ†ใ‚นใƒˆ.txt')
+ expect(subject.path).to eq('encoding/ใƒ†ใ‚นใƒˆ.txt')
expect(subject.basename).to eq('encoding/ใƒ†ใ‚นใƒˆ')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(3)
@@ -111,7 +110,7 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { (+"master:encoding/iso8859.txt\x001\x00\xC4\xFC\nmaster:encoding/iso8859.txt\x002\x00\nmaster:encoding/iso8859.txt\x003\x00foo\n").force_encoding(Encoding::ASCII_8BIT) }
it 'returns results as UTF-8' do
- expect(subject.filename).to eq('encoding/iso8859.txt')
+ expect(subject.path).to eq('encoding/iso8859.txt')
expect(subject.basename).to eq('encoding/iso8859')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(1)
@@ -124,7 +123,6 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { "master:CONTRIBUTE.md\x005\x00- [Contribute to GitLab](#contribute-to-gitlab)\n" }
it { expect(subject.path).to eq('CONTRIBUTE.md') }
- it { expect(subject.filename).to eq('CONTRIBUTE.md') }
it { expect(subject.basename).to eq('CONTRIBUTE') }
end
@@ -132,7 +130,6 @@ describe Gitlab::Search::FoundBlob do
let(:search_result) { "master:a/b/c.md\x005\x00a b c\n" }
it { expect(subject.path).to eq('a/b/c.md') }
- it { expect(subject.filename).to eq('a/b/c.md') }
it { expect(subject.basename).to eq('a/b/c') }
end
end
@@ -141,7 +138,7 @@ describe Gitlab::Search::FoundBlob do
context 'when file is under directory' do
let(:path) { 'a/b/c.md' }
- subject { described_class.new(blob_filename: path, project: project, ref: 'master') }
+ subject { described_class.new(blob_path: path, project: project, ref: 'master') }
before do
allow(Gitlab::Git::Blob).to receive(:batch).and_return([
@@ -150,7 +147,6 @@ describe Gitlab::Search::FoundBlob do
end
it { expect(subject.path).to eq('a/b/c.md') }
- it { expect(subject.filename).to eq('a/b/c.md') }
it { expect(subject.basename).to eq('a/b/c') }
context 'when filename has multiple extensions' do
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index a17e9a31212..eefc548a4d9 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -310,18 +310,18 @@ describe Gitlab::Shell do
let(:disk_path) { "#{project.disk_path}.git" }
it 'returns true when the command succeeds' do
- expect(gitlab_shell.exists?(project.repository_storage, disk_path)).to be(true)
+ expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(true)
expect(gitlab_shell.remove_repository(project.repository_storage, project.disk_path)).to be(true)
- expect(gitlab_shell.exists?(project.repository_storage, disk_path)).to be(false)
+ expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(false)
end
it 'keeps the namespace directory' do
gitlab_shell.remove_repository(project.repository_storage, project.disk_path)
- expect(gitlab_shell.exists?(project.repository_storage, disk_path)).to be(false)
- expect(gitlab_shell.exists?(project.repository_storage, project.disk_path.gsub(project.name, ''))).to be(true)
+ expect(TestEnv.storage_dir_exists?(project.repository_storage, disk_path)).to be(false)
+ expect(TestEnv.storage_dir_exists?(project.repository_storage, project.disk_path.gsub(project.name, ''))).to be(true)
end
end
@@ -332,18 +332,18 @@ describe Gitlab::Shell do
old_path = project2.disk_path
new_path = "project/new_path"
- expect(gitlab_shell.exists?(project2.repository_storage, "#{old_path}.git")).to be(true)
- expect(gitlab_shell.exists?(project2.repository_storage, "#{new_path}.git")).to be(false)
+ expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{old_path}.git")).to be(true)
+ expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{new_path}.git")).to be(false)
expect(gitlab_shell.mv_repository(project2.repository_storage, old_path, new_path)).to be_truthy
- expect(gitlab_shell.exists?(project2.repository_storage, "#{old_path}.git")).to be(false)
- expect(gitlab_shell.exists?(project2.repository_storage, "#{new_path}.git")).to be(true)
+ expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{old_path}.git")).to be(false)
+ expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{new_path}.git")).to be(true)
end
it 'returns false when the command fails' do
expect(gitlab_shell.mv_repository(project2.repository_storage, project2.disk_path, '')).to be_falsy
- expect(gitlab_shell.exists?(project2.repository_storage, "#{project2.disk_path}.git")).to be(true)
+ expect(TestEnv.storage_dir_exists?(project2.repository_storage, "#{project2.disk_path}.git")).to be(true)
end
end
@@ -401,68 +401,48 @@ describe Gitlab::Shell do
describe '#add_namespace' do
it 'creates a namespace' do
- subject.add_namespace(storage, "mepmep")
+ Gitlab::GitalyClient::NamespaceService.allow { subject.add_namespace(storage, "mepmep") }
- expect(subject.exists?(storage, "mepmep")).to be(true)
+ expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(true)
end
end
- describe '#exists?' do
- context 'when the namespace does not exist' do
+ describe '#repository_exists?' do
+ context 'when the repository does not exist' do
it 'returns false' do
- expect(subject.exists?(storage, "non-existing")).to be(false)
+ expect(subject.repository_exists?(storage, "non-existing.git")).to be(false)
end
end
- context 'when the namespace exists' do
+ context 'when the repository exists' do
it 'returns true' do
- subject.add_namespace(storage, "mepmep")
+ project = create(:project, :repository, :legacy_storage)
- expect(subject.exists?(storage, "mepmep")).to be(true)
+ expect(subject.repository_exists?(storage, project.repository.disk_path + ".git")).to be(true)
end
end
end
- describe '#repository_exists?' do
- context 'when the storage path does not exist' do
- subject { described_class.new.repository_exists?(storage, "non-existing.git") }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when the repository does not exist' do
- let(:project) { create(:project, :repository, :legacy_storage) }
-
- subject { described_class.new.repository_exists?(storage, "#{project.repository.disk_path}-some-other-repo.git") }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when the repository exists' do
- let(:project) { create(:project, :repository, :legacy_storage) }
-
- subject { described_class.new.repository_exists?(storage, "#{project.repository.disk_path}.git") }
-
- it { is_expected.to be_truthy }
- end
- end
-
describe '#remove' do
it 'removes the namespace' do
- subject.add_namespace(storage, "mepmep")
- subject.rm_namespace(storage, "mepmep")
+ Gitlab::GitalyClient::NamespaceService.allow do
+ subject.add_namespace(storage, "mepmep")
+ subject.rm_namespace(storage, "mepmep")
+ end
- expect(subject.exists?(storage, "mepmep")).to be(false)
+ expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(false)
end
end
describe '#mv_namespace' do
it 'renames the namespace' do
- subject.add_namespace(storage, "mepmep")
- subject.mv_namespace(storage, "mepmep", "2mep")
+ Gitlab::GitalyClient::NamespaceService.allow do
+ subject.add_namespace(storage, "mepmep")
+ subject.mv_namespace(storage, "mepmep", "2mep")
+ end
- expect(subject.exists?(storage, "mepmep")).to be(false)
- expect(subject.exists?(storage, "2mep")).to be(true)
+ expect(TestEnv.storage_dir_exists?(storage, "mepmep")).to be(false)
+ expect(TestEnv.storage_dir_exists?(storage, "2mep")).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index 46fbc069efb..cb870cc996b 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
describe Gitlab::SidekiqLogging::StructuredLogger do
describe '#call' do
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
index 8410467ef1f..27eea963402 100644
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
@@ -19,7 +19,7 @@ describe Gitlab::SidekiqMiddleware::CorrelationLogger do
end
end
- it 'injects into payload the correlation id' do
+ it 'injects into payload the correlation id', :sidekiq_might_not_need_inline do
expect_any_instance_of(described_class).to receive(:call).and_call_original
expect_any_instance_of(TestWorker).to receive(:perform).with(1234) do
diff --git a/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
index 806112fcb16..0d8cff3a295 100644
--- a/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
@@ -1,69 +1,108 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
describe Gitlab::SidekiqMiddleware::Metrics do
+ let(:middleware) { described_class.new }
+ let(:concurrency_metric) { double('concurrency metric') }
+
+ let(:queue_duration_seconds) { double('queue duration seconds metric') }
+ let(:completion_seconds_metric) { double('completion seconds metric') }
+ let(:user_execution_seconds_metric) { double('user execution seconds metric') }
+ let(:failed_total_metric) { double('failed total metric') }
+ let(:retried_total_metric) { double('retried total metric') }
+ let(:running_jobs_metric) { double('running jobs metric') }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_queue_duration_seconds, anything, anything, anything).and_return(queue_duration_seconds)
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric)
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric)
+ allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
+ allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_retried_total, anything).and_return(retried_total_metric)
+ allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
+ allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
+
+ allow(concurrency_metric).to receive(:set)
+ end
+
+ describe '#initialize' do
+ it 'sets general metrics' do
+ expect(concurrency_metric).to receive(:set).with({}, Sidekiq.options[:concurrency].to_i)
+
+ middleware
+ end
+ end
+
+ it 'ignore user execution when measured 0' do
+ allow(completion_seconds_metric).to receive(:observe)
+
+ expect(user_execution_seconds_metric).not_to receive(:observe)
+ end
+
describe '#call' do
- let(:middleware) { described_class.new }
let(:worker) { double(:worker) }
- let(:completion_seconds_metric) { double('completion seconds metric') }
- let(:user_execution_seconds_metric) { double('user execution seconds metric') }
- let(:failed_total_metric) { double('failed total metric') }
- let(:retried_total_metric) { double('retried total metric') }
- let(:running_jobs_metric) { double('running jobs metric') }
+ let(:job) { {} }
+ let(:job_status) { :done }
+ let(:labels) { { queue: :test } }
+ let(:labels_with_job_status) { { queue: :test, job_status: job_status } }
- before do
- allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric)
- allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric)
- allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
- allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_retried_total, anything).and_return(retried_total_metric)
- allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :livesum).and_return(running_jobs_metric)
+ let(:thread_cputime_before) { 1 }
+ let(:thread_cputime_after) { 2 }
+ let(:thread_cputime_duration) { thread_cputime_after - thread_cputime_before }
- allow(running_jobs_metric).to receive(:increment)
- end
+ let(:monotonic_time_before) { 11 }
+ let(:monotonic_time_after) { 20 }
+ let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
- it 'yields block' do
- allow(completion_seconds_metric).to receive(:observe)
- allow(user_execution_seconds_metric).to receive(:observe)
+ let(:queue_duration_for_job) { 0.01 }
- expect { |b| middleware.call(worker, {}, :test, &b) }.to yield_control.once
- end
-
- it 'sets metrics' do
- labels = { queue: :test }
- allow(middleware).to receive(:get_thread_cputime).and_return(1, 3)
+ before do
+ allow(middleware).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+ allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
- expect(user_execution_seconds_metric).to receive(:observe).with(labels, 2)
expect(running_jobs_metric).to receive(:increment).with(labels, 1)
expect(running_jobs_metric).to receive(:increment).with(labels, -1)
- expect(completion_seconds_metric).to receive(:observe).with(labels, kind_of(Numeric))
- middleware.call(worker, {}, :test) { nil }
+ expect(queue_duration_seconds).to receive(:observe).with(labels, queue_duration_for_job) if queue_duration_for_job
+ expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, thread_cputime_duration)
+ expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, monotonic_time_duration)
+ end
+
+ it 'yields block' do
+ expect { |b| middleware.call(worker, job, :test, &b) }.to yield_control.once
+ end
+
+ it 'sets queue specific metrics' do
+ middleware.call(worker, job, :test) { nil }
end
- it 'ignore user execution when measured 0' do
- allow(completion_seconds_metric).to receive(:observe)
- allow(middleware).to receive(:get_thread_cputime).and_return(0, 0)
+ context 'when job_duration is not available' do
+ let(:queue_duration_for_job) { nil }
- expect(user_execution_seconds_metric).not_to receive(:observe)
+ it 'does not set the queue_duration_seconds histogram' do
+ middleware.call(worker, job, :test) { nil }
+ end
end
context 'when job is retried' do
- it 'sets sidekiq_jobs_retried_total metric' do
- allow(completion_seconds_metric).to receive(:observe)
- expect(user_execution_seconds_metric).to receive(:observe)
+ let(:job) { { 'retry_count' => 1 } }
+ it 'sets sidekiq_jobs_retried_total metric' do
expect(retried_total_metric).to receive(:increment)
- middleware.call(worker, { 'retry_count' => 1 }, :test) { nil }
+ middleware.call(worker, job, :test) { nil }
end
end
context 'when error is raised' do
+ let(:job_status) { :fail }
+
it 'sets sidekiq_jobs_failed_total and reraises' do
- expect(failed_total_metric).to receive(:increment)
- expect { middleware.call(worker, {}, :test) { raise } }.to raise_error
+ expect(failed_total_metric).to receive(:increment).with(labels, 1)
+
+ expect { middleware.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed")
end
end
end
diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index dc412c80e68..5a8c721a634 100644
--- a/spec/lib/gitlab/slash_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -115,5 +115,10 @@ describe Gitlab::SlashCommands::Command do
let(:params) { { text: 'issue move #78291 to gitlab/gitlab-ci' } }
it { is_expected.to eq(Gitlab::SlashCommands::IssueMove) }
end
+
+ context 'IssueComment is triggered' do
+ let(:params) { { text: "issue comment #503\ncomment body" } }
+ it { is_expected.to eq(Gitlab::SlashCommands::IssueComment) }
+ end
end
end
diff --git a/spec/lib/gitlab/slash_commands/issue_comment_spec.rb b/spec/lib/gitlab/slash_commands/issue_comment_spec.rb
new file mode 100644
index 00000000000..c6f56d10d1f
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/issue_comment_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::IssueComment do
+ describe '#execute' do
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:user) { issue.author }
+ let(:chat_name) { double(:chat_name, user: user) }
+ let(:regex_match) { described_class.match("issue comment #{issue.iid}\nComment body") }
+
+ subject { described_class.new(project, chat_name).execute(regex_match) }
+
+ context 'when the issue exists' do
+ context 'when project is private' do
+ let(:project) { create(:project) }
+
+ context 'when the user is not a member of the project' do
+ let(:chat_name) { double(:chat_name, user: create(:user)) }
+
+ it 'does not allow the user to comment' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to match('not found')
+ expect(issue.reload.notes.count).to be_zero
+ end
+ end
+ end
+
+ context 'when the user is not a member of the project' do
+ let(:chat_name) { double(:chat_name, user: create(:user)) }
+
+ context 'when the discussion is locked in the issue' do
+ before do
+ issue.update!(discussion_locked: true)
+ end
+
+ it 'does not allow the user to comment' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to match('You are not allowed')
+ expect(issue.reload.notes.count).to be_zero
+ end
+ end
+ end
+
+ context 'when the user can comment on the issue' do
+ context 'when comment body exists' do
+ it 'creates a new comment' do
+ expect { subject }.to change { issue.notes.count }.by(1)
+ end
+
+ it 'a new comment has a correct body' do
+ subject
+
+ expect(issue.notes.last.note).to eq('Comment body')
+ end
+ end
+
+ context 'when comment body does not exist' do
+ let(:regex_match) { described_class.match("issue comment #{issue.iid}") }
+
+ it 'does not create a new comment' do
+ expect { subject }.not_to change { issue.notes.count }
+ end
+
+ it 'displays the errors' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to match("- Note can't be blank")
+ end
+ end
+ end
+ end
+
+ context 'when the issue does not exist' do
+ let(:regex_match) { described_class.match("issue comment 2343242\nComment body") }
+
+ it 'returns not found' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to match('not found')
+ end
+ end
+ end
+
+ describe '.match' do
+ subject(:match) { described_class.match(command) }
+
+ context 'when a command has an issue ID' do
+ context 'when command has a comment body' do
+ let(:command) { "issue comment 503\nComment body" }
+
+ it 'matches an issue ID' do
+ expect(match[:iid]).to eq('503')
+ end
+
+ it 'matches an note body' do
+ expect(match[:note_body]).to eq('Comment body')
+ end
+ end
+ end
+
+ context 'when a command has a reference prefix for issue ID' do
+ let(:command) { "issue comment #503\nComment body" }
+
+ it 'matches an issue ID' do
+ expect(match[:iid]).to eq('503')
+ end
+ end
+
+ context 'when a command does not have an issue ID' do
+ let(:command) { 'issue comment' }
+
+ it 'does not match' do
+ is_expected.to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/presenters/access_spec.rb b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
index c7b83467660..804184a7173 100644
--- a/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
@@ -22,6 +22,16 @@ describe Gitlab::SlashCommands::Presenters::Access do
end
end
+ describe '#generic_access_denied' do
+ subject { described_class.new.generic_access_denied }
+
+ it { is_expected.to be_a(Hash) }
+
+ it_behaves_like 'displays an error message' do
+ let(:error_message) { 'You are not allowed to perform the given chatops command.' }
+ end
+ end
+
describe '#deactivated' do
subject { described_class.new.deactivated }
diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb
new file mode 100644
index 00000000000..b5ef417cb93
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Presenters::IssueComment do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:note) { create(:note, project: project, noteable: issue) }
+ let(:author) { note.author }
+
+ describe '#present' do
+ let(:attachment) { subject[:attachments].first }
+ subject { described_class.new(note).present }
+
+ it { is_expected.to be_a(Hash) }
+
+ it 'sets ephemeral response type' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ end
+
+ it 'sets the title' do
+ expect(attachment[:title]).to eq("#{issue.title} ยท #{issue.to_reference}")
+ end
+
+ it 'sets the fallback text' do
+ expect(attachment[:fallback]).to eq("New comment on #{issue.to_reference}: #{issue.title}")
+ end
+
+ it 'sets the fields' do
+ expect(attachment[:fields]).to eq([{ title: 'Comment', value: note.note }])
+ end
+
+ it 'sets the color' do
+ expect(attachment[:color]).to eq('#38ae67')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sourcegraph_spec.rb b/spec/lib/gitlab/sourcegraph_spec.rb
new file mode 100644
index 00000000000..e081ae32175
--- /dev/null
+++ b/spec/lib/gitlab/sourcegraph_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Sourcegraph do
+ let_it_be(:user) { create(:user) }
+ let(:feature_scope) { true }
+
+ before do
+ Feature.enable(:sourcegraph, feature_scope)
+ end
+
+ describe '.feature_conditional?' do
+ subject { described_class.feature_conditional? }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '.feature_available?' do
+ subject { described_class.feature_available? }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '.feature_enabled?' do
+ let(:current_user) { nil }
+
+ subject { described_class.feature_enabled?(current_user) }
+
+ context 'when feature is enabled globally' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when feature is enabled only to a resource' do
+ let(:feature_scope) { user }
+
+ context 'for the same resource' do
+ let(:current_user) { user }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'for a different resource' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sql/recursive_cte_spec.rb b/spec/lib/gitlab/sql/recursive_cte_spec.rb
index 20e36c224b0..b15be56dd6d 100644
--- a/spec/lib/gitlab/sql/recursive_cte_spec.rb
+++ b/spec/lib/gitlab/sql/recursive_cte_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::SQL::RecursiveCTE do
[rel1.except(:order).to_sql, rel2.except(:order).to_sql]
end
- expect(sql).to eq("#{name} AS (#{sql1}\nUNION\n#{sql2})")
+ expect(sql).to eq("#{name} AS ((#{sql1})\nUNION\n(#{sql2}))")
end
end
diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb
index f8f6da19fa5..f736614ae53 100644
--- a/spec/lib/gitlab/sql/union_spec.rb
+++ b/spec/lib/gitlab/sql/union_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::SQL::Union do
it 'returns a String joining relations together using a UNION' do
union = described_class.new([relation_1, relation_2])
- expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
+ expect(union.to_sql).to eq("(#{to_sql(relation_1)})\nUNION\n(#{to_sql(relation_2)})")
end
it 'skips Model.none segements' do
@@ -22,7 +22,7 @@ describe Gitlab::SQL::Union do
union = described_class.new([empty_relation, relation_1, relation_2])
expect {User.where("users.id IN (#{union.to_sql})").to_a}.not_to raise_error
- expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
+ expect(union.to_sql).to eq("(#{to_sql(relation_1)})\nUNION\n(#{to_sql(relation_2)})")
end
it 'uses UNION ALL when removing duplicates is disabled' do
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index 50488dba48c..dc877f20cae 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -8,19 +8,23 @@ describe Gitlab::Tracking do
stub_application_setting(snowplow_enabled: true)
stub_application_setting(snowplow_collector_hostname: 'gitfoo.com')
stub_application_setting(snowplow_cookie_domain: '.gitfoo.com')
- stub_application_setting(snowplow_site_id: '_abc123_')
+ stub_application_setting(snowplow_app_id: '_abc123_')
+ stub_application_setting(snowplow_iglu_registry_url: 'https://example.org')
end
describe '.snowplow_options' do
it 'returns useful client options' do
- expect(described_class.snowplow_options(nil)).to eq(
+ expected_fields = {
namespace: 'gl',
hostname: 'gitfoo.com',
cookieDomain: '.gitfoo.com',
appId: '_abc123_',
formTracking: true,
- linkClickTracking: true
- )
+ linkClickTracking: true,
+ igluRegistryUrl: 'https://example.org'
+ }
+
+ expect(subject.snowplow_options(nil)).to match(expected_fields)
end
it 'enables features using feature flags' do
@@ -29,11 +33,12 @@ describe Gitlab::Tracking do
:additional_snowplow_tracking,
'_group_'
).and_return(false)
-
- expect(described_class.snowplow_options('_group_')).to include(
+ addition_feature_fields = {
formTracking: false,
linkClickTracking: false
- )
+ }
+
+ expect(subject.snowplow_options('_group_')).to include(addition_feature_fields)
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
index 7a01f7d1de8..96ebeb8ff76 100644
--- a/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb
@@ -34,22 +34,54 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
it_behaves_like 'counter examples'
end
+ describe 'previews counter' do
+ let(:setting_enabled) { true }
+
+ before do
+ stub_application_setting(web_ide_clientside_preview_enabled: setting_enabled)
+ end
+
+ context 'when web ide clientside preview is enabled' do
+ let(:increment_counter_method) { :increment_previews_count }
+ let(:total_counter_method) { :total_previews_count }
+
+ it_behaves_like 'counter examples'
+ end
+
+ context 'when web ide clientside preview is not enabled' do
+ let(:setting_enabled) { false }
+
+ it 'does not increment the counter' do
+ expect(described_class.total_previews_count).to eq(0)
+
+ 2.times { described_class.increment_previews_count }
+
+ expect(described_class.total_previews_count).to eq(0)
+ end
+ end
+ end
+
describe '.totals' do
commits = 5
merge_requests = 3
views = 2
+ previews = 4
before do
+ stub_application_setting(web_ide_clientside_preview_enabled: true)
+
commits.times { described_class.increment_commits_count }
merge_requests.times { described_class.increment_merge_requests_count }
views.times { described_class.increment_views_count }
+ previews.times { described_class.increment_previews_count }
end
it 'can report all totals' do
expect(described_class.totals).to include(
web_ide_commits: commits,
web_ide_views: views,
- web_ide_merge_requests: merge_requests
+ web_ide_merge_requests: merge_requests,
+ web_ide_previews: previews
)
end
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index f2e864472c5..484684eeb65 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -17,21 +17,41 @@ describe Gitlab::UsageData do
create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:service, project: projects[1], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'SlackService', active: true)
+ create(:service, project: projects[2], type: 'MattermostService', active: true)
+ create(:service, project: projects[2], type: 'JenkinsService', active: true)
+ create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
-
- gcp_cluster = create(:cluster, :provided_by_gcp)
- create(:cluster, :provided_by_user)
- create(:cluster, :provided_by_user, :disabled)
+ create_list(:issue, 4, project: projects[0])
+ create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added)
+ create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed)
+ create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added)
+ create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed)
+
+ # Enabled clusters
+ gcp_cluster = create(:cluster_provider_gcp, :created).cluster
+ create(:cluster_provider_aws, :created)
+ create(:cluster_platform_kubernetes)
create(:cluster, :group)
+
+ # Disabled clusters
+ create(:cluster, :disabled)
create(:cluster, :group, :disabled)
create(:cluster, :group, :disabled)
+
+ # Applications
create(:clusters_applications_helm, :installed, cluster: gcp_cluster)
create(:clusters_applications_ingress, :installed, cluster: gcp_cluster)
create(:clusters_applications_cert_manager, :installed, cluster: gcp_cluster)
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
+ create(:clusters_applications_crossplane, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
+ create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster)
+
+ create(:grafana_integration, project: projects[0], enabled: true)
+ create(:grafana_integration, project: projects[1], enabled: true)
+ create(:grafana_integration, project: projects[2], enabled: false)
ProjectFeature.first.update_attribute('repository_access_level', 0)
end
@@ -64,6 +84,8 @@ describe Gitlab::UsageData do
avg_cycle_analytics
influxdb_metrics_enabled
prometheus_metrics_enabled
+ web_ide_clientside_preview_enabled
+ ingress_modsecurity_enabled
))
end
@@ -81,6 +103,7 @@ describe Gitlab::UsageData do
web_ide_views
web_ide_commits
web_ide_merge_requests
+ web_ide_previews
navbar_searches
cycle_analytics_views
productivity_analytics_views
@@ -112,17 +135,23 @@ describe Gitlab::UsageData do
clusters_disabled
project_clusters_disabled
group_clusters_disabled
+ clusters_platforms_eks
clusters_platforms_gke
clusters_platforms_user
clusters_applications_helm
clusters_applications_ingress
clusters_applications_cert_managers
clusters_applications_prometheus
+ clusters_applications_crossplane
clusters_applications_runner
clusters_applications_knative
+ clusters_applications_elastic_stack
in_review_folder
+ grafana_integrated_projects
groups
issues
+ issues_with_associated_zoom_link
+ issues_using_zoom_quick_actions
keys
label_lists
labels
@@ -139,6 +168,9 @@ describe Gitlab::UsageData do
projects_jira_cloud_active
projects_slack_notifications_active
projects_slack_slash_active
+ projects_custom_issue_tracker_active
+ projects_jenkins_active
+ projects_mattermost_active
projects_prometheus_active
projects_with_repositories_enabled
projects_with_error_tracking_enabled
@@ -172,24 +204,33 @@ describe Gitlab::UsageData do
expect(count_data[:projects_jira_cloud_active]).to eq(2)
expect(count_data[:projects_slack_notifications_active]).to eq(2)
expect(count_data[:projects_slack_slash_active]).to eq(1)
+ expect(count_data[:projects_custom_issue_tracker_active]).to eq(1)
+ expect(count_data[:projects_jenkins_active]).to eq(1)
+ expect(count_data[:projects_mattermost_active]).to eq(1)
expect(count_data[:projects_with_repositories_enabled]).to eq(3)
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
+ expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
+ expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
- expect(count_data[:clusters_enabled]).to eq(7)
- expect(count_data[:project_clusters_enabled]).to eq(6)
+ expect(count_data[:clusters_enabled]).to eq(4)
+ expect(count_data[:project_clusters_enabled]).to eq(3)
expect(count_data[:group_clusters_enabled]).to eq(1)
expect(count_data[:clusters_disabled]).to eq(3)
expect(count_data[:project_clusters_disabled]).to eq(1)
expect(count_data[:group_clusters_disabled]).to eq(2)
expect(count_data[:group_clusters_enabled]).to eq(1)
+ expect(count_data[:clusters_platforms_eks]).to eq(1)
expect(count_data[:clusters_platforms_gke]).to eq(1)
expect(count_data[:clusters_platforms_user]).to eq(1)
expect(count_data[:clusters_applications_helm]).to eq(1)
expect(count_data[:clusters_applications_ingress]).to eq(1)
expect(count_data[:clusters_applications_cert_managers]).to eq(1)
+ expect(count_data[:clusters_applications_crossplane]).to eq(1)
expect(count_data[:clusters_applications_prometheus]).to eq(1)
expect(count_data[:clusters_applications_runner]).to eq(1)
expect(count_data[:clusters_applications_knative]).to eq(1)
+ expect(count_data[:clusters_applications_elastic_stack]).to eq(1)
+ expect(count_data[:grafana_integrated_projects]).to eq(2)
end
it 'works when queries time out' do
@@ -232,6 +273,7 @@ describe Gitlab::UsageData do
expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled)
expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
+ expect(subject[:web_ide_clientside_preview_enabled]).to eq(Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?)
end
end
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index c25bd14fcba..4e7c43a6856 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -148,7 +148,7 @@ describe Gitlab::UserAccess do
)
end
- it 'allows users that have push access to the canonical project to push to the MR branch' do
+ it 'allows users that have push access to the canonical project to push to the MR branch', :sidekiq_might_not_need_inline do
canonical_project.add_developer(user)
expect(access.can_push_to_branch?('awesome-feature')).to be_truthy
diff --git a/spec/lib/gitlab/utils/deep_size_spec.rb b/spec/lib/gitlab/utils/deep_size_spec.rb
index 47dfc04f46f..ccd202b33f7 100644
--- a/spec/lib/gitlab/utils/deep_size_spec.rb
+++ b/spec/lib/gitlab/utils/deep_size_spec.rb
@@ -42,4 +42,10 @@ describe Gitlab::Utils::DeepSize do
end
end
end
+
+ describe '.human_default_max_size' do
+ it 'returns 1 MB' do
+ expect(described_class.human_default_max_size).to eq('1 MB')
+ end
+ end
end
diff --git a/spec/lib/gitlab/visibility_level_checker_spec.rb b/spec/lib/gitlab/visibility_level_checker_spec.rb
index 325ac3c6f31..fc929d5cbbf 100644
--- a/spec/lib/gitlab/visibility_level_checker_spec.rb
+++ b/spec/lib/gitlab/visibility_level_checker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::VisibilityLevelChecker do
diff --git a/spec/lib/gitlab/wiki_file_finder_spec.rb b/spec/lib/gitlab/wiki_file_finder_spec.rb
index fdd95d5e6e6..aeba081f3d3 100644
--- a/spec/lib/gitlab/wiki_file_finder_spec.rb
+++ b/spec/lib/gitlab/wiki_file_finder_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::WikiFileFinder do
it_behaves_like 'file finder' do
subject { described_class.new(project, project.wiki.default_branch) }
- let(:expected_file_by_name) { 'Files/Title.md' }
+ let(:expected_file_by_path) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 6bf837f1d3f..9362ff72fbc 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -96,6 +96,48 @@ describe Gitlab do
end
end
+ describe '.canary?' do
+ it 'is true when CANARY env var is set to true' do
+ stub_env('CANARY', '1')
+
+ expect(described_class.canary?).to eq true
+ end
+
+ it 'is false when CANARY env var is set to false' do
+ stub_env('CANARY', '0')
+
+ expect(described_class.canary?).to eq false
+ end
+ end
+
+ describe '.com_and_canary?' do
+ it 'is true when on .com and canary' do
+ allow(described_class).to receive_messages(com?: true, canary?: true)
+
+ expect(described_class.com_and_canary?).to eq true
+ end
+
+ it 'is false when on .com but not on canary' do
+ allow(described_class).to receive_messages(com?: true, canary?: false)
+
+ expect(described_class.com_and_canary?).to eq false
+ end
+ end
+
+ describe '.com_but_not_canary?' do
+ it 'is false when on .com and canary' do
+ allow(described_class).to receive_messages(com?: true, canary?: true)
+
+ expect(described_class.com_but_not_canary?).to eq false
+ end
+
+ it 'is true when on .com but not on canary' do
+ allow(described_class).to receive_messages(com?: true, canary?: false)
+
+ expect(described_class.com_but_not_canary?).to eq true
+ end
+ end
+
describe '.dev_env_org_or_com?' do
it 'is true when on .com' do
allow(described_class).to receive_messages(com?: true, org?: false)
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 0f7f57095df..473ad639ead 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -104,7 +104,8 @@ describe GoogleApi::CloudPlatform::Client do
enabled: legacy_abac
},
ip_allocation_policy: {
- use_ip_aliases: true
+ use_ip_aliases: true,
+ cluster_ipv4_cidr_block: '/16'
},
addons_config: addons_config
}
diff --git a/spec/lib/grafana/client_spec.rb b/spec/lib/grafana/client_spec.rb
index bd93a3c59a2..699344e940e 100644
--- a/spec/lib/grafana/client_spec.rb
+++ b/spec/lib/grafana/client_spec.rb
@@ -35,7 +35,7 @@ describe Grafana::Client do
it 'does not follow redirects' do
expect { subject }.to raise_exception(
Grafana::Client::Error,
- 'Grafana response status code: 302'
+ 'Grafana response status code: 302, Message: {}'
)
expect(redirect_req_stub).to have_been_requested
@@ -67,6 +67,30 @@ describe Grafana::Client do
end
end
+ describe '#get_dashboard' do
+ let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/dashboards/uid/FndfgnX' }
+
+ subject do
+ client.get_dashboard(uid: 'FndfgnX')
+ end
+
+ it_behaves_like 'calls grafana api'
+ it_behaves_like 'no redirects'
+ it_behaves_like 'handles exceptions'
+ end
+
+ describe '#get_datasource' do
+ let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/datasources/name/Test%20Name' }
+
+ subject do
+ client.get_datasource(name: 'Test Name')
+ end
+
+ it_behaves_like 'calls grafana api'
+ it_behaves_like 'no redirects'
+ it_behaves_like 'handles exceptions'
+ end
+
describe '#proxy_datasource' do
let(:grafana_api_url) do
'https://grafanatest.com/-/grafana-project/' \
diff --git a/spec/lib/omni_auth/strategies/saml_spec.rb b/spec/lib/omni_auth/strategies/saml_spec.rb
index 3c59de86d98..73e86872308 100644
--- a/spec/lib/omni_auth/strategies/saml_spec.rb
+++ b/spec/lib/omni_auth/strategies/saml_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe OmniAuth::Strategies::SAML, type: :strategy do
diff --git a/spec/lib/prometheus/pid_provider_spec.rb b/spec/lib/prometheus/pid_provider_spec.rb
index ba843b27254..6fdc11b14c4 100644
--- a/spec/lib/prometheus/pid_provider_spec.rb
+++ b/spec/lib/prometheus/pid_provider_spec.rb
@@ -18,7 +18,17 @@ describe Prometheus::PidProvider do
expect(Sidekiq).to receive(:server?).and_return(true)
end
- it { is_expected.to eq 'sidekiq' }
+ context 'in a clustered setup' do
+ before do
+ stub_env('SIDEKIQ_WORKER_ID', '123')
+ end
+
+ it { is_expected.to eq 'sidekiq_123' }
+ end
+
+ context 'in a single process setup' do
+ it { is_expected.to eq 'sidekiq' }
+ end
end
context 'when running in Unicorn mode' do
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
index 7abb9688d5a..da5ba4c4d99 100644
--- a/spec/lib/quality/helm_client_spec.rb
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -107,5 +107,25 @@ RSpec.describe Quality::HelmClient do
expect(subject.delete(release_name: release_name)).to eq('')
end
+
+ context 'with multiple release names' do
+ let(:release_name) { ['my-release', 'my-release-2'] }
+
+ it 'raises an error if the Helm command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls helm delete with multiple release names' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(subject.delete(release_name: release_name)).to eq('')
+ end
+ end
end
end
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 4e77dcc97e6..5bac102ac41 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Quality::KubernetesClient do
expect(Gitlab::Popen).to receive(:popen_with_detail)
.with([%(kubectl --namespace "#{namespace}" delete ) \
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized -l release=\"#{release_name}\""])
+ "--now --ignore-not-found --include-uninitialized --wait=true -l release=\"#{release_name}\""])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
@@ -23,11 +23,59 @@ RSpec.describe Quality::KubernetesClient do
expect(Gitlab::Popen).to receive(:popen_with_detail)
.with([%(kubectl --namespace "#{namespace}" delete ) \
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized -l release=\"#{release_name}\""])
+ "--now --ignore-not-found --include-uninitialized --wait=true -l release=\"#{release_name}\""])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
end
+
+ context 'with multiple releases' do
+ let(:release_name) { ['my-release', 'my-release-2'] }
+
+ it 'raises an error if the Kubernetes command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})'"])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls kubectl with the correct arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})'"])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ # We're not verifying the output here, just silencing it
+ expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
+ end
+ end
+
+ context 'with `wait: false`' do
+ it 'raises an error if the Kubernetes command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized --wait=false -l release=\"#{release_name}\""])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.cleanup(release_name: release_name, wait: false) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls kubectl with the correct arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized --wait=false -l release=\"#{release_name}\""])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ # We're not verifying the output here, just silencing it
+ expect { subject.cleanup(release_name: release_name, wait: false) }.to output.to_stdout
+ end
+ end
end
end
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index ca2b17b44e0..8101664d34f 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -192,6 +192,15 @@ describe Sentry::Client do
end
end
+ context 'sentry api response too large' do
+ it 'raises exception' do
+ deep_size = double('Gitlab::Utils::DeepSize', valid?: false)
+ allow(Gitlab::Utils::DeepSize).to receive(:new).with(sentry_api_response).and_return(deep_size)
+
+ expect { subject }.to raise_error(Sentry::Client::ResponseInvalidSizeError, 'Sentry API response is too big. Limit is 1 MB.')
+ end
+ end
+
it_behaves_like 'maps exceptions'
end