summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/api/entities/basic_project_details_spec.rb2
-rw-r--r--spec/lib/api/entities/deployment_extended_spec.rb15
-rw-r--r--spec/lib/api/helpers_spec.rb8
-rw-r--r--spec/lib/backup/database_spec.rb9
-rw-r--r--spec/lib/backup/gitaly_backup_spec.rb6
-rw-r--r--spec/lib/backup/gitaly_rpc_backup_spec.rb6
-rw-r--r--spec/lib/backup/manager_spec.rb11
-rw-r--r--spec/lib/backup/repositories_spec.rb72
-rw-r--r--spec/lib/banzai/filter/external_link_filter_spec.rb7
-rw-r--r--spec/lib/banzai/filter/references/issue_reference_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/table_of_contents_filter_spec.rb6
-rw-r--r--spec/lib/banzai/object_renderer_spec.rb2
-rw-r--r--spec/lib/bitbucket_server/representation/repo_spec.rb5
-rw-r--r--spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb15
-rw-r--r--spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb56
-rw-r--r--spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb210
-rw-r--r--spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb161
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb27
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb35
-rw-r--r--spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb40
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb119
-rw-r--r--spec/lib/bulk_imports/groups/stage_spec.rb2
-rw-r--r--spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb24
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb27
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb32
-rw-r--r--spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb78
-rw-r--r--spec/lib/bulk_imports/projects/stage_spec.rb1
-rw-r--r--spec/lib/container_registry/client_spec.rb110
-rw-r--r--spec/lib/container_registry/gitlab_api_client_spec.rb204
-rw-r--r--spec/lib/container_registry/migration_spec.rb168
-rw-r--r--spec/lib/container_registry/registry_spec.rb10
-rw-r--r--spec/lib/extracts_path_spec.rb21
-rw-r--r--spec/lib/extracts_ref_spec.rb15
-rw-r--r--spec/lib/feature_spec.rb4
-rw-r--r--spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb48
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb15
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb14
-rw-r--r--spec/lib/gitlab/application_context_spec.rb11
-rw-r--r--spec/lib/gitlab/audit/ci_runner_token_author_spec.rb83
-rw-r--r--spec/lib/gitlab/audit/null_author_spec.rb40
-rw-r--r--spec/lib/gitlab/auth/ldap/user_spec.rb28
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb74
-rw-r--r--spec/lib/gitlab/auth/request_authenticator_spec.rb6
-rw-r--r--spec/lib/gitlab/auth/saml/user_spec.rb46
-rw-r--r--spec/lib/gitlab/auth_spec.rb49
-rw-r--r--spec/lib/gitlab/authorized_keys_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb244
-rw-r--r--spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb7
-rw-r--r--spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb50
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_updated_at_after_repository_storage_move_spec.rb35
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/backfill_project_namespace_per_group_batching_strategy_spec.rb53
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb9
-rw-r--r--spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb134
-rw-r--r--spec/lib/gitlab/background_migration/populate_issue_email_participants_spec.rb20
-rw-r--r--spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb50
-rw-r--r--spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb93
-rw-r--r--spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb28
-rw-r--r--spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer_spec.rb130
-rw-r--r--spec/lib/gitlab/bitbucket_server_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/buffered_io_spec.rb54
-rw-r--r--spec/lib/gitlab/changelog/config_spec.rb14
-rw-r--r--spec/lib/gitlab/changelog/release_spec.rb10
-rw-r--r--spec/lib/gitlab/checks/branch_check_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/badge/release/latest_release_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/badge/release/metadata_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/badge/release/template_spec.rb90
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/include/rules_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/include_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/policy_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/script_spec.rb109
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/external/rules_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb124
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb148
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/logger_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb165
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/reports/security/finding_key_spec.rb61
-rw-r--r--spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/npm_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/variables/builder/instance_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/variables/builder/project_spec.rb149
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb247
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb58
-rw-r--r--spec/lib/gitlab/cluster/lifecycle_events_spec.rb3
-rw-r--r--spec/lib/gitlab/config/entry/factory_spec.rb6
-rw-r--r--spec/lib/gitlab/console_spec.rb51
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb12
-rw-r--r--spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb7
-rw-r--r--spec/lib/gitlab/database/background_migration/batch_metrics_spec.rb2
-rw-r--r--spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb17
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_spec.rb95
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_transition_log_spec.rb19
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb49
-rw-r--r--spec/lib/gitlab/database/dynamic_model_helpers_spec.rb17
-rw-r--r--spec/lib/gitlab/database/each_database_spec.rb100
-rw-r--r--spec/lib/gitlab/database/gitlab_schema_spec.rb2
-rw-r--r--spec/lib/gitlab/database/load_balancing/configuration_spec.rb23
-rw-r--r--spec/lib/gitlab/database/load_balancing/setup_spec.rb31
-rw-r--r--spec/lib/gitlab/database/loose_foreign_keys_spec.rb39
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb102
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb14
-rw-r--r--spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migrations/instrumentation_spec.rb58
-rw-r--r--spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb18
-rw-r--r--spec/lib/gitlab/database/migrations/observers/query_details_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migrations/observers/query_log_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migrations/observers/transaction_duration_spec.rb2
-rw-r--r--spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb57
-rw-r--r--spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb57
-rw-r--r--spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb29
-rw-r--r--spec/lib/gitlab/database/with_lock_retries_spec.rb2
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb6
-rw-r--r--spec/lib/gitlab/database_spec.rb28
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb6
-rw-r--r--spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb2
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb39
-rw-r--r--spec/lib/gitlab/endpoint_attributes_spec.rb5
-rw-r--r--spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb3
-rw-r--r--spec/lib/gitlab/error_tracking/log_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/event_store/store_spec.rb27
-rw-r--r--spec/lib/gitlab/experiment/rollout/feature_spec.rb75
-rw-r--r--spec/lib/gitlab/feature_categories_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb38
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_design_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb5
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb47
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb10
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb167
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_importer_spec.rb24
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_note_spec.rb19
-rw-r--r--spec/lib/gitlab/gl_repository/identifier_spec.rb4
-rw-r--r--spec/lib/gitlab/gl_repository/repo_type_spec.rb4
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb32
-rw-r--r--spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/batch_key_spec.rb6
-rw-r--r--spec/lib/gitlab/graphql/markdown_field_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/queries_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb5
-rw-r--r--spec/lib/gitlab/hook_data/project_builder_spec.rb3
-rw-r--r--spec/lib/gitlab/http_connection_adapter_spec.rb36
-rw-r--r--spec/lib/gitlab/http_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/import_export/command_line_util_spec.rb23
-rw-r--r--spec/lib/gitlab/import_export/config_spec.rb3
-rw-r--r--spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/fork_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/group/relation_factory_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group/tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb34
-rw-r--r--spec/lib/gitlab/import_export/lfs_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/lfs_saver_spec.rb8
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb1
-rw-r--r--spec/lib/gitlab/import_export/project/relation_factory_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/tree_saver_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb48
-rw-r--r--spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb10
-rw-r--r--spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb2
-rw-r--r--spec/lib/gitlab/json_spec.rb42
-rw-r--r--spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/boot_time_tracker_spec.rb84
-rw-r--r--spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb53
-rw-r--r--spec/lib/gitlab/metrics/rails_slis_spec.rb10
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb14
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb34
-rw-r--r--spec/lib/gitlab/middleware/memory_report_spec.rb91
-rw-r--r--spec/lib/gitlab/net_http_adapter_spec.rb22
-rw-r--r--spec/lib/gitlab/omniauth_initializer_spec.rb14
-rw-r--r--spec/lib/gitlab/pagination/keyset/in_operator_optimization/array_scope_columns_spec.rb9
-rw-r--r--spec/lib/gitlab/pipeline_scope_counts_spec.rb48
-rw-r--r--spec/lib/gitlab/popen_spec.rb11
-rw-r--r--spec/lib/gitlab/process_memory_cache/helper_spec.rb9
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb74
-rw-r--r--spec/lib/gitlab/rack_attack/request_spec.rb266
-rw-r--r--spec/lib/gitlab/regex_spec.rb2
-rw-r--r--spec/lib/gitlab/request_profiler/profile_spec.rb2
-rw-r--r--spec/lib/gitlab/runtime_spec.rb24
-rw-r--r--spec/lib/gitlab/security/scan_configuration_spec.rb33
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb123
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb1
-rw-r--r--spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb5
-rw-r--r--spec/lib/gitlab/usage/service_ping_report_spec.rb69
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb3
-rw-r--r--spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb15
-rw-r--r--spec/lib/gitlab/usage_data_counters/vscode_extenion_activity_unique_counter_spec.rb63
-rw-r--r--spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb15
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb6
-rw-r--r--spec/lib/gitlab/utils_spec.rb17
-rw-r--r--spec/lib/gitlab/web_ide/config/entry/global_spec.rb2
-rw-r--r--spec/lib/gitlab/web_ide/config_spec.rb2
-rw-r--r--spec/lib/gitlab/webpack/file_loader_spec.rb4
-rw-r--r--spec/lib/gitlab_edition_spec.rb25
-rw-r--r--spec/lib/gitlab_spec.rb7
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb90
-rw-r--r--spec/lib/learn_gitlab/project_spec.rb7
-rw-r--r--spec/lib/peek/views/detailed_view_spec.rb2
-rw-r--r--spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb191
-rw-r--r--spec/lib/security/ci_configuration/sast_build_action_spec.rb9
-rw-r--r--spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb4
-rw-r--r--spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb4
-rw-r--r--spec/lib/serializers/json_spec.rb1
-rw-r--r--spec/lib/serializers/symbolized_json_spec.rb1
-rw-r--r--spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb21
-rw-r--r--spec/lib/sidebars/projects/menus/analytics_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/confluence_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/deployments_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/hidden_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/issues_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/monitor_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/project_information_menu_spec.rb8
-rw-r--r--spec/lib/sidebars/projects/menus/repository_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/scope_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/settings_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/shimo_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/snippets_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/wiki_menu_spec.rb2
-rw-r--r--spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb2
268 files changed, 5641 insertions, 2097 deletions
diff --git a/spec/lib/api/entities/basic_project_details_spec.rb b/spec/lib/api/entities/basic_project_details_spec.rb
index dc7c4fdce4e..8419eb0a932 100644
--- a/spec/lib/api/entities/basic_project_details_spec.rb
+++ b/spec/lib/api/entities/basic_project_details_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe API::Entities::BasicProjectDetails do
let_it_be(:project) { create(:project) }
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
subject(:output) { described_class.new(project, current_user: current_user).as_json }
diff --git a/spec/lib/api/entities/deployment_extended_spec.rb b/spec/lib/api/entities/deployment_extended_spec.rb
new file mode 100644
index 00000000000..733c47362be
--- /dev/null
+++ b/spec/lib/api/entities/deployment_extended_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Entities::DeploymentExtended do
+ describe '#as_json' do
+ subject { described_class.new(deployment).as_json }
+
+ let(:deployment) { create(:deployment) }
+
+ it 'includes fields from deployment entity' do
+ is_expected.to include(:id, :iid, :ref, :sha, :created_at, :updated_at, :user, :environment, :deployable, :status)
+ end
+ end
+end
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 2277bd78e86..b2d4a3094af 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -76,6 +76,12 @@ RSpec.describe API::Helpers do
expect(subject.find_project(non_existing_id)).to be_nil
end
end
+
+ context 'when project id is not provided' do
+ it 'returns nil' do
+ expect(subject.find_project(nil)).to be_nil
+ end
+ end
end
context 'when ID is used as an argument' do
@@ -160,7 +166,7 @@ RSpec.describe API::Helpers do
describe '#find_project!' do
let_it_be(:project) { create(:project) }
- let(:user) { project.owner}
+ let(:user) { project.first_owner}
before do
allow(subject).to receive(:current_user).and_return(user)
diff --git a/spec/lib/backup/database_spec.rb b/spec/lib/backup/database_spec.rb
index f57037d5652..4345778ba92 100644
--- a/spec/lib/backup/database_spec.rb
+++ b/spec/lib/backup/database_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe Backup::Database do
let(:progress) { StringIO.new }
let(:output) { progress.string }
+ before do
+ allow(Gitlab::TaskHelpers).to receive(:ask_to_continue)
+ end
+
describe '#restore' do
let(:cmd) { %W[#{Gem.ruby} -e $stdout.puts(1)] }
let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
@@ -20,7 +24,7 @@ RSpec.describe Backup::Database do
let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
it 'returns successfully' do
- expect(subject.restore).to eq([])
+ subject.restore
expect(output).to include("Restoring PostgreSQL database")
expect(output).to include("[DONE]")
@@ -42,7 +46,8 @@ RSpec.describe Backup::Database do
let(:cmd) { %W[#{Gem.ruby} -e $stderr.write("#{noise}#{visible_error}")] }
it 'filters out noise from errors' do
- expect(subject.restore).to eq([visible_error])
+ subject.restore
+
expect(output).to include("ERRORS")
expect(output).not_to include(noise)
expect(output).to include(visible_error)
diff --git a/spec/lib/backup/gitaly_backup_spec.rb b/spec/lib/backup/gitaly_backup_spec.rb
index cd0d984fbdb..6bf4f833c1f 100644
--- a/spec/lib/backup/gitaly_backup_spec.rb
+++ b/spec/lib/backup/gitaly_backup_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Backup::GitalyBackup do
create(:wiki_page, container: project)
create(:design, :with_file, issue: create(:issue, project: project))
project_snippet = create(:project_snippet, :repository, project: project)
- personal_snippet = create(:personal_snippet, :repository, author: project.owner)
+ personal_snippet = create(:personal_snippet, :repository, author: project.first_owner)
expect(Open3).to receive(:popen2).with(expected_env, anything, 'create', '-path', anything).and_call_original
@@ -122,8 +122,8 @@ RSpec.describe Backup::GitalyBackup do
context 'restore' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
- let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) }
def copy_bundle_to_backup_path(bundle_name, destination)
FileUtils.mkdir_p(File.join(Gitlab.config.backup.path, 'repositories', File.dirname(destination)))
diff --git a/spec/lib/backup/gitaly_rpc_backup_spec.rb b/spec/lib/backup/gitaly_rpc_backup_spec.rb
index 14f9d27ca6e..4829d51ac9d 100644
--- a/spec/lib/backup/gitaly_rpc_backup_spec.rb
+++ b/spec/lib/backup/gitaly_rpc_backup_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe Backup::GitalyRpcBackup do
create(:wiki_page, container: project)
create(:design, :with_file, issue: create(:issue, project: project))
project_snippet = create(:project_snippet, :repository, project: project)
- personal_snippet = create(:personal_snippet, :repository, author: project.owner)
+ personal_snippet = create(:personal_snippet, :repository, author: project.first_owner)
subject.start(:create)
subject.enqueue(project, Gitlab::GlRepository::PROJECT)
@@ -75,8 +75,8 @@ RSpec.describe Backup::GitalyRpcBackup do
context 'restore' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
- let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) }
def copy_bundle_to_backup_path(bundle_name, destination)
FileUtils.mkdir_p(File.join(Gitlab.config.backup.path, 'repositories', File.dirname(destination)))
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 31cc3012eb1..ac693ad8b98 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -12,6 +12,11 @@ RSpec.describe Backup::Manager do
before do
allow(progress).to receive(:puts)
allow(progress).to receive(:print)
+ FileUtils.mkdir_p('tmp/tests/public/uploads')
+ end
+
+ after do
+ FileUtils.rm_rf('tmp/tests/public/uploads', secure: true)
end
describe '#pack' do
@@ -409,7 +414,7 @@ RSpec.describe Backup::Manager do
# the Fog mock only knows about directories we create explicitly
connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys)
- connection.directories.create(key: Gitlab.config.backup.upload.remote_directory)
+ connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang
end
context 'target path' do
@@ -455,7 +460,7 @@ RSpec.describe Backup::Manager do
}
)
- connection.directories.create(key: Gitlab.config.backup.upload.remote_directory)
+ connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang
end
context 'with SSE-S3 without using storage_options' do
@@ -521,7 +526,7 @@ RSpec.describe Backup::Manager do
)
connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys)
- connection.directories.create(key: Gitlab.config.backup.upload.remote_directory)
+ connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang
end
it 'does not attempt to set ACL' do
diff --git a/spec/lib/backup/repositories_spec.rb b/spec/lib/backup/repositories_spec.rb
index f3830da344b..0b29a25360d 100644
--- a/spec/lib/backup/repositories_spec.rb
+++ b/spec/lib/backup/repositories_spec.rb
@@ -6,8 +6,17 @@ RSpec.describe Backup::Repositories do
let(:progress) { spy(:stdout) }
let(:parallel_enqueue) { true }
let(:strategy) { spy(:strategy, parallel_enqueue?: parallel_enqueue) }
-
- subject { described_class.new(progress, strategy: strategy) }
+ let(:max_concurrency) { 1 }
+ let(:max_storage_concurrency) { 1 }
+
+ subject do
+ described_class.new(
+ progress,
+ strategy: strategy,
+ max_concurrency: max_concurrency,
+ max_storage_concurrency: max_storage_concurrency
+ )
+ end
describe '#dump' do
let_it_be(:projects) { create_list(:project, 5, :repository) }
@@ -15,9 +24,9 @@ RSpec.describe Backup::Repositories do
RSpec.shared_examples 'creates repository bundles' do
it 'calls enqueue for each repository type', :aggregate_failures do
project_snippet = create(:project_snippet, :repository, project: project)
- personal_snippet = create(:personal_snippet, :repository, author: project.owner)
+ personal_snippet = create(:personal_snippet, :repository, author: project.first_owner)
- subject.dump(max_concurrency: 1, max_storage_concurrency: 1)
+ subject.dump
expect(strategy).to have_received(:start).with(:create)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
@@ -51,38 +60,40 @@ RSpec.describe Backup::Repositories do
end
expect(strategy).to receive(:finish!)
- subject.dump(max_concurrency: 1, max_storage_concurrency: 1)
+ subject.dump
end
describe 'command failure' do
it 'enqueue_project raises an error' do
allow(strategy).to receive(:enqueue).with(anything, Gitlab::GlRepository::PROJECT).and_raise(IOError)
- expect { subject.dump(max_concurrency: 1, max_storage_concurrency: 1) }.to raise_error(IOError)
+ expect { subject.dump }.to raise_error(IOError)
end
it 'project query raises an error' do
allow(Project).to receive_message_chain(:includes, :find_each).and_raise(ActiveRecord::StatementTimeout)
- expect { subject.dump(max_concurrency: 1, max_storage_concurrency: 1) }.to raise_error(ActiveRecord::StatementTimeout)
+ expect { subject.dump }.to raise_error(ActiveRecord::StatementTimeout)
end
end
it 'avoids N+1 database queries' do
control_count = ActiveRecord::QueryRecorder.new do
- subject.dump(max_concurrency: 1, max_storage_concurrency: 1)
+ subject.dump
end.count
create_list(:project, 2, :repository)
expect do
- subject.dump(max_concurrency: 1, max_storage_concurrency: 1)
+ subject.dump
end.not_to exceed_query_limit(control_count)
end
end
context 'concurrency with a strategy without parallel enqueueing support' do
let(:parallel_enqueue) { false }
+ let(:max_concurrency) { 2 }
+ let(:max_storage_concurrency) { 2 }
it 'enqueues all projects sequentially' do
expect(Thread).not_to receive(:new)
@@ -93,13 +104,14 @@ RSpec.describe Backup::Repositories do
end
expect(strategy).to receive(:finish!)
- subject.dump(max_concurrency: 2, max_storage_concurrency: 2)
+ subject.dump
end
end
[4, 10].each do |max_storage_concurrency|
context "max_storage_concurrency #{max_storage_concurrency}", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/241701' do
let(:storage_keys) { %w[default test_second_storage] }
+ let(:max_storage_concurrency) { max_storage_concurrency }
before do
allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(storage_keys)
@@ -116,54 +128,58 @@ RSpec.describe Backup::Repositories do
end
expect(strategy).to receive(:finish!)
- subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency)
+ subject.dump
end
- it 'creates the expected number of threads with extra max concurrency' do
- expect(Thread).to receive(:new)
- .exactly(storage_keys.length * (max_storage_concurrency + 1)).times
- .and_call_original
+ context 'with extra max concurrency' do
+ let(:max_concurrency) { 3 }
- expect(strategy).to receive(:start).with(:create)
- projects.each do |project|
- expect(strategy).to receive(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
- end
- expect(strategy).to receive(:finish!)
+ it 'creates the expected number of threads' do
+ expect(Thread).to receive(:new)
+ .exactly(storage_keys.length * (max_storage_concurrency + 1)).times
+ .and_call_original
- subject.dump(max_concurrency: 3, max_storage_concurrency: max_storage_concurrency)
+ expect(strategy).to receive(:start).with(:create)
+ projects.each do |project|
+ expect(strategy).to receive(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
+ end
+ expect(strategy).to receive(:finish!)
+
+ subject.dump
+ end
end
describe 'command failure' do
it 'enqueue_project raises an error' do
allow(strategy).to receive(:enqueue).and_raise(IOError)
- expect { subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency) }.to raise_error(IOError)
+ expect { subject.dump }.to raise_error(IOError)
end
it 'project query raises an error' do
allow(Project).to receive_message_chain(:for_repository_storage, :includes, :find_each).and_raise(ActiveRecord::StatementTimeout)
- expect { subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency) }.to raise_error(ActiveRecord::StatementTimeout)
+ expect { subject.dump }.to raise_error(ActiveRecord::StatementTimeout)
end
context 'misconfigured storages' do
let(:storage_keys) { %w[test_second_storage] }
it 'raises an error' do
- expect { subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency) }.to raise_error(Backup::Error, 'repositories.storages in gitlab.yml is misconfigured')
+ expect { subject.dump }.to raise_error(Backup::Error, 'repositories.storages in gitlab.yml is misconfigured')
end
end
end
it 'avoids N+1 database queries' do
control_count = ActiveRecord::QueryRecorder.new do
- subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency)
+ subject.dump
end.count
create_list(:project, 2, :repository)
expect do
- subject.dump(max_concurrency: 1, max_storage_concurrency: max_storage_concurrency)
+ subject.dump
end.not_to exceed_query_limit(control_count)
end
end
@@ -172,8 +188,8 @@ RSpec.describe Backup::Repositories do
describe '#restore' do
let_it_be(:project) { create(:project) }
- let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
- let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) }
it 'calls enqueue for each repository type', :aggregate_failures do
subject.restore
diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb
index 24d13bdb42c..036817834d5 100644
--- a/spec/lib/banzai/filter/external_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_link_filter_spec.rb
@@ -71,6 +71,13 @@ RSpec.describe Banzai::Filter::ExternalLinkFilter do
expect(doc.to_html).to eq(expected)
end
+
+ it 'adds rel and target attributes to improperly formatted protocols' do
+ doc = filter %q(<p><a target="_blank" href="http:evil.com">Reverse Tabnabbing</a></p>)
+ expected = %q(<p><a target="_blank" href="http:evil.com" rel="nofollow noreferrer noopener">Reverse Tabnabbing</a></p>)
+
+ expect(doc.to_html).to eq(expected)
+ end
end
context 'for links with a username' do
diff --git a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
index b3523a25116..c493cb77c98 100644
--- a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
@@ -515,7 +515,7 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter do
enable_design_management(enabled)
end
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
let(:enabled) { true }
let(:matches) { Issue.link_reference_pattern.match(input_text) }
let(:extras) { subject.object_link_text_extras(issue, matches) }
diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
index 6e90f4457fa..91c644cb16a 100644
--- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
+++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
@@ -91,6 +91,12 @@ RSpec.describe Banzai::Filter::TableOfContentsFilter do
# ExternalLinkFilter (see https://gitlab.com/gitlab-org/gitlab/issues/26210)
expect(doc.css('h1 a').first.attr('href')).to eq "##{CGI.escape('한글')}"
end
+
+ it 'limits header href length with 255 characters' do
+ doc = filter(header(1, 'a' * 500))
+
+ expect(doc.css('h1 a').first.attr('href')).to eq "##{'a' * 255}"
+ end
end
end
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index e64ab5dfce3..8f69480c65f 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Banzai::ObjectRenderer do
let(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:renderer) do
described_class.new(
default_project: project,
diff --git a/spec/lib/bitbucket_server/representation/repo_spec.rb b/spec/lib/bitbucket_server/representation/repo_spec.rb
index 7a773f47ca5..5de4360bbd0 100644
--- a/spec/lib/bitbucket_server/representation/repo_spec.rb
+++ b/spec/lib/bitbucket_server/representation/repo_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe BitbucketServer::Representation::Repo do
"slug": "rouge",
"id": 1,
"name": "rouge",
+ "description": "Rogue Repo",
"scmId": "git",
"state": "AVAILABLE",
"statusMessage": "Available",
@@ -17,7 +18,7 @@ RSpec.describe BitbucketServer::Representation::Repo do
"key": "TEST",
"id": 1,
"name": "test",
- "description": "Test",
+ "description": "Test Project",
"public": false,
"type": "NORMAL",
"links": {
@@ -73,7 +74,7 @@ RSpec.describe BitbucketServer::Representation::Repo do
end
describe '#description' do
- it { expect(subject.description).to eq('Test') }
+ it { expect(subject.description).to eq('Rogue Repo') }
end
describe '#full_name' do
diff --git a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
index 80607485b6e..50c54a7b47f 100644
--- a/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
+++ b/spec/lib/bulk_imports/common/extractors/graphql_extractor_spec.rb
@@ -8,12 +8,15 @@ RSpec.describe BulkImports::Common::Extractors::GraphqlExtractor do
let(:response) { double(original_hash: { 'data' => { 'foo' => 'bar' }, 'page_info' => {} }) }
let(:options) do
{
- query: double(
- to_s: 'test',
- variables: {},
- data_path: %w[data foo],
- page_info_path: %w[data page_info]
- )
+ query:
+ double(
+ new: double(
+ to_s: 'test',
+ variables: {},
+ data_path: %w[data foo],
+ page_info_path: %w[data page_info]
+ )
+ )
}
end
diff --git a/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb b/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
new file mode 100644
index 00000000000..e3a7335a238
--- /dev/null
+++ b/spec/lib/bulk_imports/common/graphql/get_members_query_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
+ let(:entity) { create(:bulk_import_entity, :group_entity) }
+ let(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
+ describe '#data_path' do
+ it 'returns data path' do
+ expected = %w[data portable members nodes]
+
+ expect(query.data_path).to eq(expected)
+ end
+ end
+
+ describe '#page_info_path' do
+ it 'returns pagination information path' do
+ expected = %w[data portable members page_info]
+
+ expect(query.page_info_path).to eq(expected)
+ end
+ end
+
+ describe '#to_s' do
+ context 'when entity is group' do
+ it 'queries group & group members' do
+ expect(query.to_s).to include('group')
+ expect(query.to_s).to include('groupMembers')
+ end
+ end
+
+ context 'when entity is project' do
+ let(:entity) { create(:bulk_import_entity, :project_entity) }
+
+ it 'queries project & project members' do
+ expect(query.to_s).to include('project')
+ expect(query.to_s).to include('projectMembers')
+ end
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb
new file mode 100644
index 00000000000..b769aa4af5a
--- /dev/null
+++ b/spec/lib/bulk_imports/common/pipelines/lfs_objects_pipeline_spec.rb
@@ -0,0 +1,210 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Pipelines::LfsObjectsPipeline do
+ let_it_be(:portable) { create(:project) }
+ let_it_be(:oid) { 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }
+
+ let(:tmpdir) { Dir.mktmpdir }
+ let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, source_full_path: 'test') }
+ let(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+ let(:lfs_dir_path) { tmpdir }
+ let(:lfs_json_file_path) { File.join(lfs_dir_path, 'lfs_objects.json')}
+ let(:lfs_file_path) { File.join(lfs_dir_path, oid)}
+
+ subject(:pipeline) { described_class.new(context) }
+
+ before do
+ FileUtils.mkdir_p(lfs_dir_path)
+ FileUtils.touch(lfs_json_file_path)
+ FileUtils.touch(lfs_file_path)
+ File.write(lfs_json_file_path, { oid => [0, 1, 2, nil] }.to_json )
+
+ allow(Dir).to receive(:mktmpdir).with('bulk_imports').and_return(tmpdir)
+ end
+
+ after do
+ FileUtils.remove_entry(tmpdir) if Dir.exist?(tmpdir)
+ end
+
+ describe '#run' do
+ it 'imports lfs objects into destination project and removes tmpdir' do
+ allow(pipeline)
+ .to receive(:extract)
+ .and_return(BulkImports::Pipeline::ExtractedData.new(data: [lfs_json_file_path, lfs_file_path]))
+
+ pipeline.run
+
+ expect(portable.lfs_objects.count).to eq(1)
+ expect(portable.lfs_objects_projects.count).to eq(4)
+ expect(Dir.exist?(tmpdir)).to eq(false)
+ end
+ end
+
+ describe '#extract' do
+ it 'downloads & extracts lfs objects filepaths' do
+ download_service = instance_double("BulkImports::FileDownloadService")
+ decompression_service = instance_double("BulkImports::FileDecompressionService")
+ extraction_service = instance_double("BulkImports::ArchiveExtractionService")
+
+ expect(BulkImports::FileDownloadService)
+ .to receive(:new)
+ .with(
+ configuration: context.configuration,
+ relative_url: "/#{entity.pluralized_name}/test/export_relations/download?relation=lfs_objects",
+ tmpdir: tmpdir,
+ filename: 'lfs_objects.tar.gz')
+ .and_return(download_service)
+ expect(BulkImports::FileDecompressionService).to receive(:new).with(tmpdir: tmpdir, filename: 'lfs_objects.tar.gz').and_return(decompression_service)
+ expect(BulkImports::ArchiveExtractionService).to receive(:new).with(tmpdir: tmpdir, filename: 'lfs_objects.tar').and_return(extraction_service)
+
+ expect(download_service).to receive(:execute)
+ expect(decompression_service).to receive(:execute)
+ expect(extraction_service).to receive(:execute)
+
+ extracted_data = pipeline.extract(context)
+
+ expect(extracted_data.data).to contain_exactly(lfs_json_file_path, lfs_file_path)
+ end
+ end
+
+ describe '#load' do
+ before do
+ allow(pipeline)
+ .to receive(:extract)
+ .and_return(BulkImports::Pipeline::ExtractedData.new(data: [lfs_json_file_path, lfs_file_path]))
+ end
+
+ context 'when file path is lfs json' do
+ it 'returns' do
+ filepath = File.join(tmpdir, 'lfs_objects.json')
+
+ allow(Gitlab::Json).to receive(:parse).with(filepath).and_return({})
+
+ expect { pipeline.load(context, filepath) }.not_to change { portable.lfs_objects.count }
+ end
+ end
+
+ context 'when file path is tar file' do
+ it 'returns' do
+ filepath = File.join(tmpdir, 'lfs_objects.tar')
+
+ expect { pipeline.load(context, filepath) }.not_to change { portable.lfs_objects.count }
+ end
+ end
+
+ context 'when lfs json read failed' do
+ it 'raises an error' do
+ File.write(lfs_json_file_path, 'invalid json')
+
+ expect { pipeline.load(context, lfs_file_path) }.to raise_error(BulkImports::Error, 'LFS Objects JSON read failed')
+ end
+ end
+
+ context 'when file path is being traversed' do
+ it 'raises an error' do
+ expect { pipeline.load(context, File.join(tmpdir, '..')) }.to raise_error(Gitlab::Utils::PathTraversalAttackError, 'Invalid path')
+ end
+ end
+
+ context 'when file path is not under tmpdir' do
+ it 'returns' do
+ expect { pipeline.load(context, '/home/test.txt') }.to raise_error(StandardError, 'path /home/test.txt is not allowed')
+ end
+ end
+
+ context 'when file path is symlink' do
+ it 'returns' do
+ symlink = File.join(tmpdir, 'symlink')
+
+ FileUtils.ln_s(File.join(tmpdir, lfs_file_path), symlink)
+
+ expect { pipeline.load(context, symlink) }.not_to change { portable.lfs_objects.count }
+ end
+ end
+
+ context 'when path is a directory' do
+ it 'returns' do
+ expect { pipeline.load(context, Dir.tmpdir) }.not_to change { portable.lfs_objects.count }
+ end
+ end
+
+ context 'lfs objects project' do
+ context 'when lfs objects json is invalid' do
+ context 'when oid value is not Array' do
+ it 'does not create lfs objects project' do
+ File.write(lfs_json_file_path, { oid => 'test' }.to_json )
+
+ expect { pipeline.load(context, lfs_file_path) }.not_to change { portable.lfs_objects_projects.count }
+ end
+ end
+
+ context 'when oid value is nil' do
+ it 'does not create lfs objects project' do
+ File.write(lfs_json_file_path, { oid => nil }.to_json )
+
+ expect { pipeline.load(context, lfs_file_path) }.not_to change { portable.lfs_objects_projects.count }
+ end
+ end
+
+ context 'when oid value is not allowed' do
+ it 'does not create lfs objects project' do
+ File.write(lfs_json_file_path, { oid => ['invalid'] }.to_json )
+
+ expect { pipeline.load(context, lfs_file_path) }.not_to change { portable.lfs_objects_projects.count }
+ end
+ end
+
+ context 'when repository type is duplicated' do
+ it 'creates only one lfs objects project' do
+ File.write(lfs_json_file_path, { oid => [0, 0, 1, 1, 2, 2] }.to_json )
+
+ expect { pipeline.load(context, lfs_file_path) }.to change { portable.lfs_objects_projects.count }.by(3)
+ end
+ end
+ end
+
+ context 'when lfs objects project fails to be created' do
+ it 'logs the failure' do
+ allow_next_instance_of(LfsObjectsProject) do |object|
+ allow(object).to receive(:persisted?).and_return(false)
+ end
+
+ expect_next_instance_of(Gitlab::Import::Logger) do |logger|
+ expect(logger)
+ .to receive(:warn)
+ .with(project_id: portable.id,
+ message: 'Failed to save lfs objects project',
+ errors: '', **Gitlab::ApplicationContext.current)
+ .exactly(4).times
+ end
+
+ pipeline.load(context, lfs_file_path)
+ end
+ end
+ end
+ end
+
+ describe '#after_run' do
+ it 'removes tmpdir' do
+ allow(FileUtils).to receive(:remove_entry).and_call_original
+ expect(FileUtils).to receive(:remove_entry).with(tmpdir).and_call_original
+
+ pipeline.after_run(nil)
+
+ expect(Dir.exist?(tmpdir)).to eq(false)
+ end
+
+ context 'when tmpdir does not exist' do
+ it 'does not attempt to remove tmpdir' do
+ FileUtils.remove_entry(tmpdir)
+
+ expect(FileUtils).not_to receive(:remove_entry).with(tmpdir)
+
+ pipeline.after_run(nil)
+ end
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
new file mode 100644
index 00000000000..f9b95f79104
--- /dev/null
+++ b/spec/lib/bulk_imports/common/pipelines/members_pipeline_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Common::Pipelines::MembersPipeline do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:bulk_import) { create(:bulk_import, user: user) }
+ let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
+ let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
+ let_it_be(:member_data) do
+ {
+ user_id: member_user1.id,
+ created_by_id: member_user2.id,
+ access_level: 30,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
+ }
+ end
+
+ let(:parent) { create(:group) }
+ let(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+ let(:members) { portable.members.map { |m| m.slice(:user_id, :access_level) } }
+
+ subject(:pipeline) { described_class.new(context) }
+
+ def extracted_data(email:, has_next_page: false)
+ data = {
+ 'created_at' => '2020-01-01T00:00:00Z',
+ 'updated_at' => '2020-01-02T00:00:00Z',
+ 'expires_at' => nil,
+ 'access_level' => {
+ 'integer_value' => 30
+ },
+ 'user' => {
+ 'public_email' => email
+ }
+ }
+
+ page_info = {
+ 'has_next_page' => has_next_page,
+ 'next_page' => has_next_page ? 'cursor' : nil
+ }
+
+ BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
+ end
+
+ shared_examples 'members import' do
+ before do
+ portable.members.delete_all
+ end
+
+ describe '#run' do
+ it 'creates memberships for existing users' do
+ first_page = extracted_data(email: member_user1.email, has_next_page: true)
+ last_page = extracted_data(email: member_user2.email)
+
+ allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
+ allow(extractor).to receive(:extract).and_return(first_page, last_page)
+ end
+
+ expect { pipeline.run }.to change(portable.members, :count).by(2)
+
+ expect(members).to contain_exactly(
+ { user_id: member_user1.id, access_level: 30 },
+ { user_id: member_user2.id, access_level: 30 }
+ )
+ end
+ end
+
+ describe '#load' do
+ it 'creates new membership' do
+ expect { subject.load(context, member_data) }.to change(portable.members, :count).by(1)
+
+ member = portable.members.find_by_user_id(member_user1.id)
+
+ expect(member.user).to eq(member_user1)
+ expect(member.created_by).to eq(member_user2)
+ expect(member.access_level).to eq(30)
+ expect(member.created_at).to eq('2020-01-01T00:00:00Z')
+ expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
+ expect(member.expires_at).to eq(nil)
+ end
+
+ context 'when user_id is current user id' do
+ it 'does not create new membership' do
+ data = { user_id: user.id }
+
+ expect { pipeline.load(context, data) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when data is nil' do
+ it 'does not create new membership' do
+ expect { pipeline.load(context, nil) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when user membership already exists with the same access level' do
+ it 'does not create new membership' do
+ portable.members.create!(member_data)
+
+ expect { pipeline.load(context, member_data) }.not_to change(portable.members, :count)
+ end
+ end
+
+ context 'when portable is in a parent group' do
+ let(:tracker) { create(:bulk_import_tracker, entity: entity_with_parent) }
+
+ before do
+ parent.members.create!(member_data)
+ end
+
+ context 'when the same membership exists in parent group' do
+ it 'does not create new membership' do
+ expect { pipeline.load(context, member_data) }.not_to change(portable_with_parent.members, :count)
+ end
+ end
+
+ context 'when membership with higher access level exists in parent group' do
+ it 'creates new direct membership' do
+ data = member_data.merge(access_level: Gitlab::Access::MAINTAINER)
+
+ expect { pipeline.load(context, data) }.to change(portable_with_parent.members, :count)
+
+ member = portable_with_parent.members.find_by_user_id(member_user1.id)
+
+ expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+
+ context 'when membership with lower access level exists in parent group' do
+ it 'does not create new membership' do
+ data = member_data.merge(access_level: Gitlab::Access::GUEST)
+
+ expect { pipeline.load(context, data) }.not_to change(portable_with_parent.members, :count)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when importing to group' do
+ let(:portable) { create(:group) }
+ let(:portable_with_parent) { create(:group, parent: parent) }
+ let(:entity) { create(:bulk_import_entity, :group_entity, group: portable, bulk_import: bulk_import) }
+ let(:entity_with_parent) { create(:bulk_import_entity, :group_entity, group: portable_with_parent, bulk_import: bulk_import) }
+
+ include_examples 'members import'
+ end
+
+ context 'when importing to project' do
+ let(:portable) { create(:project) }
+ let(:portable_with_parent) { create(:project, namespace: parent) }
+ let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, bulk_import: bulk_import) }
+ let(:entity_with_parent) { create(:bulk_import_entity, :project_entity, project: portable_with_parent, bulk_import: bulk_import) }
+
+ include_examples 'members import'
+ end
+end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
index b0f8f74783b..d03b8d8b5b2 100644
--- a/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
+++ b/spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb
@@ -3,14 +3,27 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
describe '#variables' do
it 'returns query variables based on entity information' do
- entity = double(source_full_path: 'test', bulk_import: nil)
- tracker = double(entity: entity)
- context = BulkImports::Pipeline::Context.new(tracker)
- expected = { full_path: entity.source_full_path }
+ expected = { full_path: tracker.entity.source_full_path }
- expect(described_class.variables(context)).to eq(expected)
+ expect(subject.variables).to eq(expected)
end
end
@@ -18,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns data path' do
expected = %w[data group]
- expect(described_class.data_path).to eq(expected)
+ expect(subject.data_path).to eq(expected)
end
end
@@ -26,7 +39,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns pagination information path' do
expected = %w[data group page_info]
- expect(described_class.page_info_path).to eq(expected)
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
deleted file mode 100644
index d0c4bb817b2..00000000000
--- a/spec/lib/bulk_imports/groups/graphql/get_members_query_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Groups::Graphql::GetMembersQuery do
- it 'has a valid query' do
- tracker = create(:bulk_import_tracker)
- context = BulkImports::Pipeline::Context.new(tracker)
-
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
-
- describe '#data_path' do
- it 'returns data path' do
- expected = %w[data group group_members nodes]
-
- expect(described_class.data_path).to eq(expected)
- end
- end
-
- describe '#page_info_path' do
- it 'returns pagination information path' do
- expected = %w[data group group_members page_info]
-
- expect(described_class.page_info_path).to eq(expected)
- end
- end
-end
diff --git a/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb b/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
index 1a7c5a4993c..fe28e3959a0 100644
--- a/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
+++ b/spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb
@@ -3,25 +3,25 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
- describe '#variables' do
- it 'returns valid variables based on entity information' do
- tracker = create(:bulk_import_tracker)
- context = BulkImports::Pipeline::Context.new(tracker)
-
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
- context 'with invalid variables' do
- it 'raises an error' do
- expect { GraphQL::Query.new(GitlabSchema, described_class.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
- end
+ context 'with invalid variables' do
+ it 'raises an error' do
+ expect { GraphQL::Query.new(GitlabSchema, subject.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
end
end
@@ -29,7 +29,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns data path' do
expected = %w[data group projects nodes]
- expect(described_class.data_path).to eq(expected)
+ expect(subject.data_path).to eq(expected)
end
end
@@ -37,7 +37,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns pagination information path' do
expected = %w[data group projects page_info]
- expect(described_class.page_info_path).to eq(expected)
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
deleted file mode 100644
index 0126acb320b..00000000000
--- a/spec/lib/bulk_imports/groups/pipelines/members_pipeline_spec.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Groups::Pipelines::MembersPipeline do
- let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
- let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
-
- let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:bulk_import) { create(:bulk_import, user: user) }
- let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import, group: group) }
- let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
- let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
-
- subject { described_class.new(context) }
-
- describe '#run' do
- it 'maps existing users to the imported group' do
- first_page = extracted_data(email: member_user1.email, has_next_page: true)
- last_page = extracted_data(email: member_user2.email)
-
- allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
- allow(extractor)
- .to receive(:extract)
- .and_return(first_page, last_page)
- end
-
- expect { subject.run }.to change(GroupMember, :count).by(2)
-
- members = group.members.map { |m| m.slice(:user_id, :access_level) }
-
- expect(members).to contain_exactly(
- { user_id: member_user1.id, access_level: 30 },
- { user_id: member_user2.id, access_level: 30 }
- )
- end
- end
-
- describe '#load' do
- it 'does nothing when there is no data' do
- expect { subject.load(context, nil) }.not_to change(GroupMember, :count)
- end
-
- it 'creates the member' do
- data = {
- 'user_id' => member_user1.id,
- 'created_by_id' => member_user2.id,
- 'access_level' => 30,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
- }
-
- expect { subject.load(context, data) }.to change(GroupMember, :count).by(1)
-
- member = group.members.last
-
- expect(member.user).to eq(member_user1)
- expect(member.created_by).to eq(member_user2)
- expect(member.access_level).to eq(30)
- expect(member.created_at).to eq('2020-01-01T00:00:00Z')
- expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
- expect(member.expires_at).to eq(nil)
- end
-
- context 'when user_id is current user id' do
- it 'does not create new member' do
- data = { 'user_id' => user.id }
-
- expect { subject.load(context, data) }.not_to change(GroupMember, :count)
- end
- end
- end
-
- describe 'pipeline parts' do
- it { expect(described_class).to include_module(BulkImports::Pipeline) }
- it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) }
-
- it 'has extractors' do
- expect(described_class.get_extractor)
- .to eq(
- klass: BulkImports::Common::Extractors::GraphqlExtractor,
- options: {
- query: BulkImports::Groups::Graphql::GetMembersQuery
- }
- )
- end
-
- it 'has transformers' do
- expect(described_class.transformers)
- .to contain_exactly(
- { klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil },
- { klass: BulkImports::Groups::Transformers::MemberAttributesTransformer, options: nil }
- )
- end
- end
-
- def extracted_data(email:, has_next_page: false)
- data = {
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil,
- 'access_level' => {
- 'integer_value' => 30
- },
- 'user' => {
- 'public_email' => email
- }
- }
-
- page_info = {
- 'has_next_page' => has_next_page,
- 'next_page' => has_next_page ? 'cursor' : nil
- }
-
- BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
- end
-end
diff --git a/spec/lib/bulk_imports/groups/stage_spec.rb b/spec/lib/bulk_imports/groups/stage_spec.rb
index 55a8e40f480..b6bb8a7d195 100644
--- a/spec/lib/bulk_imports/groups/stage_spec.rb
+++ b/spec/lib/bulk_imports/groups/stage_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[0, BulkImports::Groups::Pipelines::GroupPipeline],
[1, BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline],
- [1, BulkImports::Groups::Pipelines::MembersPipeline],
+ [1, BulkImports::Common::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Common::Pipelines::MilestonesPipeline],
[1, BulkImports::Common::Pipelines::BadgesPipeline],
diff --git a/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
index af99428e0c1..c8935f71f10 100644
--- a/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
+++ b/spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb
@@ -48,12 +48,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: user.email)
expect(subject.transform(context, data)).to eq(
- 'access_level' => 30,
- 'user_id' => user.id,
- 'created_by_id' => user.id,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
+ access_level: 30,
+ user_id: user.id,
+ created_by_id: user.id,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
)
end
@@ -62,12 +62,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: secondary_email)
expect(subject.transform(context, data)).to eq(
- 'access_level' => 30,
- 'user_id' => user.id,
- 'created_by_id' => user.id,
- 'created_at' => '2020-01-01T00:00:00Z',
- 'updated_at' => '2020-01-01T00:00:00Z',
- 'expires_at' => nil
+ access_level: 30,
+ user_id: user.id,
+ created_by_id: user.id,
+ created_at: '2020-01-01T00:00:00Z',
+ updated_at: '2020-01-01T00:00:00Z',
+ expires_at: nil
)
end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
new file mode 100644
index 00000000000..6593aa56506
--- /dev/null
+++ b/spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::Projects::Graphql::GetProjectQuery do
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
+
+ subject(:query) { described_class.new(context: context) }
+
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
+
+ expect(result[:errors]).to be_empty
+ end
+
+ it 'queries project based on source_full_path' do
+ expected = { full_path: tracker.entity.source_full_path }
+
+ expect(subject.variables).to eq(expected)
+ end
+end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
index 4dba81dc0d2..8ed105bc0c9 100644
--- a/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
+++ b/spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb
@@ -3,19 +3,29 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetRepositoryQuery do
- describe 'query repository based on full_path' do
- let(:entity) { double(source_full_path: 'test', bulk_import: nil) }
- let(:tracker) { double(entity: entity) }
- let(:context) { BulkImports::Pipeline::Context.new(tracker) }
+ let_it_be(:tracker) { create(:bulk_import_tracker) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
- it 'returns project repository url' do
- expect(described_class.to_s).to include('httpUrlToRepo')
- end
+ subject(:query) { described_class.new(context: context) }
- it 'queries project based on source_full_path' do
- expected = { full_path: entity.source_full_path }
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
- expect(described_class.variables(context)).to eq(expected)
- end
+ expect(result[:errors]).to be_empty
+ end
+
+ it 'returns project repository url' do
+ expect(subject.to_s).to include('httpUrlToRepo')
+ end
+
+ it 'queries project based on source_full_path' do
+ expected = { full_path: tracker.entity.source_full_path }
+
+ expect(subject.variables).to eq(expected)
end
end
diff --git a/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb b/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
index b680fa5cbfc..1bd4106297d 100644
--- a/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
+++ b/spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb
@@ -3,56 +3,56 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetSnippetRepositoryQuery do
- describe 'query repository based on full_path' do
- let_it_be(:entity) { create(:bulk_import_entity) }
- let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
- let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
-
- it 'has a valid query' do
- query = GraphQL::Query.new(
- GitlabSchema,
- described_class.to_s,
- variables: described_class.variables(context)
- )
- result = GitlabSchema.static_validator.validate(query)
-
- expect(result[:errors]).to be_empty
- end
+ let_it_be(:entity) { create(:bulk_import_entity) }
+ let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
+ let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
- it 'returns snippet httpUrlToRepo' do
- expect(described_class.to_s).to include('httpUrlToRepo')
- end
+ subject(:query) { described_class.new(context: context) }
- it 'returns snippet createdAt' do
- expect(described_class.to_s).to include('createdAt')
- end
+ it 'has a valid query' do
+ parsed_query = GraphQL::Query.new(
+ GitlabSchema,
+ query.to_s,
+ variables: query.variables
+ )
+ result = GitlabSchema.static_validator.validate(parsed_query)
- it 'returns snippet title' do
- expect(described_class.to_s).to include('title')
- end
+ expect(result[:errors]).to be_empty
+ end
- describe '.variables' do
- it 'queries project based on source_full_path and pagination' do
- expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
+ it 'returns snippet httpUrlToRepo' do
+ expect(subject.to_s).to include('httpUrlToRepo')
+ end
- expect(described_class.variables(context)).to eq(expected)
- end
+ it 'returns snippet createdAt' do
+ expect(subject.to_s).to include('createdAt')
+ end
+
+ it 'returns snippet title' do
+ expect(subject.to_s).to include('title')
+ end
+
+ describe '.variables' do
+ it 'queries project based on source_full_path and pagination' do
+ expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
+
+ expect(subject.variables).to eq(expected)
end
+ end
- describe '.data_path' do
- it '.data_path returns data path' do
- expected = %w[data project snippets nodes]
+ describe '.data_path' do
+ it '.data_path returns data path' do
+ expected = %w[data project snippets nodes]
- expect(described_class.data_path).to eq(expected)
- end
+ expect(subject.data_path).to eq(expected)
end
+ end
- describe '.page_info_path' do
- it '.page_info_path returns pagination information path' do
- expected = %w[data project snippets page_info]
+ describe '.page_info_path' do
+ it '.page_info_path returns pagination information path' do
+ expected = %w[data project snippets page_info]
- expect(described_class.page_info_path).to eq(expected)
- end
+ expect(subject.page_info_path).to eq(expected)
end
end
end
diff --git a/spec/lib/bulk_imports/projects/stage_spec.rb b/spec/lib/bulk_imports/projects/stage_spec.rb
index 81cbdcae9d1..ef98613dc25 100644
--- a/spec/lib/bulk_imports/projects/stage_spec.rb
+++ b/spec/lib/bulk_imports/projects/stage_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe BulkImports::Projects::Stage do
[4, BulkImports::Projects::Pipelines::ServiceDeskSettingPipeline],
[5, BulkImports::Common::Pipelines::WikiPipeline],
[5, BulkImports::Common::Pipelines::UploadsPipeline],
+ [5, BulkImports::Common::Pipelines::LfsObjectsPipeline],
[5, BulkImports::Projects::Pipelines::AutoDevopsPipeline],
[5, BulkImports::Projects::Pipelines::PipelineSchedulesPipeline],
[6, BulkImports::Common::Pipelines::EntityFinisher]
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index 259d7d5ad13..974c3478ddc 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -5,29 +5,7 @@ require 'spec_helper'
RSpec.describe ContainerRegistry::Client do
using RSpec::Parameterized::TableSyntax
- let(:token) { '12345' }
- let(:options) { { token: token } }
- let(:registry_api_url) { 'http://container-registry' }
- let(:client) { described_class.new(registry_api_url, options) }
- let(:push_blob_headers) do
- {
- 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
- 'Authorization' => "bearer #{token}",
- 'Content-Type' => 'application/octet-stream',
- 'User-Agent' => "GitLab/#{Gitlab::VERSION}"
- }
- end
-
- let(:headers_with_accept_types) do
- {
- 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
- 'Authorization' => "bearer #{token}",
- 'User-Agent' => "GitLab/#{Gitlab::VERSION}"
- }
- end
-
- let(:expected_faraday_headers) { { user_agent: "GitLab/#{Gitlab::VERSION}" } }
- let(:expected_faraday_request_options) { Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS }
+ include_context 'container registry client'
shared_examples 'handling timeouts' do
let(:retry_options) do
@@ -48,14 +26,14 @@ RSpec.describe ContainerRegistry::Client do
retry_block: -> (_, _, _, _) { actual_retries += 1 }
)
- stub_const('ContainerRegistry::Client::RETRY_OPTIONS', retry_options_with_block)
+ stub_const('ContainerRegistry::BaseClient::RETRY_OPTIONS', retry_options_with_block)
expect { subject }.to raise_error(Faraday::ConnectionFailed)
expect(actual_retries).to eq(retry_options_with_block[:max])
end
it 'logs the error' do
- stub_const('ContainerRegistry::Client::RETRY_OPTIONS', retry_options)
+ stub_const('ContainerRegistry::BaseClient::RETRY_OPTIONS', retry_options)
expect(Gitlab::ErrorTracking)
.to receive(:log_exception)
@@ -63,7 +41,7 @@ RSpec.describe ContainerRegistry::Client do
.times
.with(
an_instance_of(Faraday::ConnectionFailed),
- class: described_class.name,
+ class: ::ContainerRegistry::BaseClient.name,
url: URI(url)
)
@@ -325,14 +303,14 @@ RSpec.describe ContainerRegistry::Client do
subject { client.supports_tag_delete? }
where(:registry_tags_support_enabled, :is_on_dot_com, :container_registry_features, :expect_registry_to_be_pinged, :expected_result) do
- true | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | true
- true | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | true | true
- true | true | [] | true | true
- true | false | [] | true | true
- false | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | true
- false | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | true | false
- false | true | [] | true | false
- false | false | [] | true | false
+ true | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | true
+ true | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | true | true
+ true | true | [] | true | true
+ true | false | [] | true | true
+ false | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | true
+ false | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | true | false
+ false | true | [] | true | false
+ false | false | [] | true | false
end
with_them do
@@ -366,38 +344,38 @@ RSpec.describe ContainerRegistry::Client do
subject { described_class.supports_tag_delete? }
where(:registry_api_url, :registry_enabled, :registry_tags_support_enabled, :is_on_dot_com, :container_registry_features, :expect_registry_to_be_pinged, :expected_result) do
- 'http://sandbox.local' | true | true | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | true
- 'http://sandbox.local' | true | true | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | true | true
- 'http://sandbox.local' | true | false | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | true
- 'http://sandbox.local' | true | false | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | true | false
- 'http://sandbox.local' | false | true | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- 'http://sandbox.local' | false | true | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- 'http://sandbox.local' | false | false | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- 'http://sandbox.local' | false | false | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- 'http://sandbox.local' | true | true | true | [] | true | true
- 'http://sandbox.local' | true | true | false | [] | true | true
- 'http://sandbox.local' | true | false | true | [] | true | false
- 'http://sandbox.local' | true | false | false | [] | true | false
- 'http://sandbox.local' | false | true | true | [] | false | false
- 'http://sandbox.local' | false | true | false | [] | false | false
- 'http://sandbox.local' | false | false | true | [] | false | false
- 'http://sandbox.local' | false | false | false | [] | false | false
- '' | true | true | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | true | true | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | true | false | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | true | false | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | false | true | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | false | true | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | false | false | true | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | false | false | false | [ContainerRegistry::Client::REGISTRY_TAG_DELETE_FEATURE] | false | false
- '' | true | true | true | [] | false | false
- '' | true | true | false | [] | false | false
- '' | true | false | true | [] | false | false
- '' | true | false | false | [] | false | false
- '' | false | true | true | [] | false | false
- '' | false | true | false | [] | false | false
- '' | false | false | true | [] | false | false
- '' | false | false | false | [] | false | false
+ 'http://sandbox.local' | true | true | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | true
+ 'http://sandbox.local' | true | true | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | true | true
+ 'http://sandbox.local' | true | false | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | true
+ 'http://sandbox.local' | true | false | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | true | false
+ 'http://sandbox.local' | false | true | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ 'http://sandbox.local' | false | true | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ 'http://sandbox.local' | false | false | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ 'http://sandbox.local' | false | false | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ 'http://sandbox.local' | true | true | true | [] | true | true
+ 'http://sandbox.local' | true | true | false | [] | true | true
+ 'http://sandbox.local' | true | false | true | [] | true | false
+ 'http://sandbox.local' | true | false | false | [] | true | false
+ 'http://sandbox.local' | false | true | true | [] | false | false
+ 'http://sandbox.local' | false | true | false | [] | false | false
+ 'http://sandbox.local' | false | false | true | [] | false | false
+ 'http://sandbox.local' | false | false | false | [] | false | false
+ '' | true | true | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | true | true | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | true | false | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | true | false | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | false | true | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | false | true | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | false | false | true | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | false | false | false | [described_class::REGISTRY_TAG_DELETE_FEATURE] | false | false
+ '' | true | true | true | [] | false | false
+ '' | true | true | false | [] | false | false
+ '' | true | false | true | [] | false | false
+ '' | true | false | false | [] | false | false
+ '' | false | true | true | [] | false | false
+ '' | false | true | false | [] | false | false
+ '' | false | false | true | [] | false | false
+ '' | false | false | false | [] | false | false
end
with_them do
diff --git a/spec/lib/container_registry/gitlab_api_client_spec.rb b/spec/lib/container_registry/gitlab_api_client_spec.rb
new file mode 100644
index 00000000000..292582a8d83
--- /dev/null
+++ b/spec/lib/container_registry/gitlab_api_client_spec.rb
@@ -0,0 +1,204 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ContainerRegistry::GitlabApiClient do
+ using RSpec::Parameterized::TableSyntax
+
+ include_context 'container registry client'
+
+ let(:path) { 'namespace/path/to/repository' }
+
+ describe '#supports_gitlab_api?' do
+ subject { client.supports_gitlab_api? }
+
+ where(:registry_gitlab_api_enabled, :is_on_dot_com, :container_registry_features, :expect_registry_to_be_pinged, :expected_result) do
+ false | true | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | false | true
+ true | false | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | true | true
+ true | true | [] | true | true
+ true | false | [] | true | true
+ false | true | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | false | true
+ false | false | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | true | false
+ false | true | [] | true | false
+ false | false | [] | true | false
+ end
+
+ with_them do
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(is_on_dot_com)
+ stub_registry_gitlab_api_support(registry_gitlab_api_enabled)
+ stub_application_setting(container_registry_features: container_registry_features)
+ end
+
+ it 'returns the expected result' do
+ if expect_registry_to_be_pinged
+ expect(Faraday::Connection).to receive(:new).and_call_original
+ else
+ expect(Faraday::Connection).not_to receive(:new)
+ end
+
+ expect(subject).to be expected_result
+ end
+ end
+
+ context 'with 401 response' do
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(false)
+ stub_application_setting(container_registry_features: [])
+ stub_request(:get, "#{registry_api_url}/gitlab/v1/")
+ .to_return(status: 401, body: '')
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '#pre_import_repository' do
+ subject { client.pre_import_repository(path) }
+
+ where(:status_code, :expected_result) do
+ 200 | :already_imported
+ 202 | :ok
+ 401 | :unauthorized
+ 404 | :not_found
+ 409 | :already_being_imported
+ 418 | :error
+ 424 | :pre_import_failed
+ 425 | :already_being_imported
+ 429 | :too_many_imports
+ end
+
+ with_them do
+ before do
+ stub_pre_import(path, status_code, pre: true)
+ end
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ describe '#import_repository' do
+ subject { client.import_repository(path) }
+
+ where(:status_code, :expected_result) do
+ 200 | :already_imported
+ 202 | :ok
+ 401 | :unauthorized
+ 404 | :not_found
+ 409 | :already_being_imported
+ 418 | :error
+ 424 | :pre_import_failed
+ 425 | :already_being_imported
+ 429 | :too_many_imports
+ end
+
+ with_them do
+ before do
+ stub_pre_import(path, status_code, pre: false)
+ end
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ describe '#import_status' do
+ subject { client.import_status(path) }
+
+ before do
+ stub_import_status(path, status)
+ end
+
+ context 'with a status' do
+ let(:status) { 'this_is_a_test' }
+
+ it { is_expected.to eq(status) }
+ end
+
+ context 'with no status' do
+ let(:status) { nil }
+
+ it { is_expected.to eq('error') }
+ end
+ end
+
+ describe '.supports_gitlab_api?' do
+ subject { described_class.supports_gitlab_api? }
+
+ where(:registry_gitlab_api_enabled, :is_on_dot_com, :container_registry_features, :expect_registry_to_be_pinged, :expected_result) do
+ true | true | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | false | true
+ true | false | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | true | true
+ false | true | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | false | true
+ false | false | [described_class::REGISTRY_GITLAB_V1_API_FEATURE] | true | false
+ true | true | [] | true | true
+ true | false | [] | true | true
+ false | true | [] | true | false
+ false | false | [] | true | false
+ end
+
+ with_them do
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(is_on_dot_com)
+ stub_container_registry_config(enabled: true, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key')
+ stub_registry_gitlab_api_support(registry_gitlab_api_enabled)
+ stub_application_setting(container_registry_features: container_registry_features)
+ end
+
+ it 'returns the expected result' do
+ if expect_registry_to_be_pinged
+ expect(Faraday::Connection).to receive(:new).and_call_original
+ else
+ expect(Faraday::Connection).not_to receive(:new)
+ end
+
+ expect(subject).to be expected_result
+ end
+ end
+
+ context 'with the registry disabled' do
+ before do
+ stub_container_registry_config(enabled: false, api_url: 'http://sandbox.local', key: 'spec/fixtures/x509_certificate_pk.key')
+ end
+
+ it 'returns false' do
+ expect(Faraday::Connection).not_to receive(:new)
+
+ expect(subject).to be_falsey
+ end
+ end
+
+ context 'with a blank registry url' do
+ before do
+ stub_container_registry_config(enabled: true, api_url: '', key: 'spec/fixtures/x509_certificate_pk.key')
+ end
+
+ it 'returns false' do
+ expect(Faraday::Connection).not_to receive(:new)
+
+ expect(subject).to be_falsey
+ end
+ end
+ end
+
+ def stub_pre_import(path, status_code, pre:)
+ stub_request(:put, "#{registry_api_url}/gitlab/v1/import/#{path}/?pre=#{pre}")
+ .with(headers: { 'Accept' => described_class::JSON_TYPE })
+ .to_return(status: status_code, body: '')
+ end
+
+ def stub_registry_gitlab_api_support(supported = true)
+ status_code = supported ? 200 : 404
+ stub_request(:get, "#{registry_api_url}/gitlab/v1/")
+ .with(headers: { 'Accept' => described_class::JSON_TYPE })
+ .to_return(status: status_code, body: '')
+ end
+
+ def stub_import_status(path, status)
+ stub_request(:get, "#{registry_api_url}/gitlab/v1/import/#{path}/")
+ .with(headers: { 'Accept' => described_class::JSON_TYPE })
+ .to_return(
+ status: 200,
+ body: { status: status }.to_json,
+ headers: { content_type: 'application/json' }
+ )
+ end
+end
diff --git a/spec/lib/container_registry/migration_spec.rb b/spec/lib/container_registry/migration_spec.rb
new file mode 100644
index 00000000000..ffbbfb249e3
--- /dev/null
+++ b/spec/lib/container_registry/migration_spec.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ContainerRegistry::Migration do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '.enabled?' do
+ subject { described_class.enabled? }
+
+ it { is_expected.to eq(true) }
+
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(container_registry_migration_phase2_enabled: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '.limit_gitlab_org?' do
+ subject { described_class.limit_gitlab_org? }
+
+ it { is_expected.to eq(true) }
+
+ context 'feature flag disabled' do
+ before do
+ stub_feature_flags(container_registry_migration_limit_gitlab_org: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '.enqueue_waiting_time' do
+ subject { described_class.enqueue_waiting_time }
+
+ where(:slow_enabled, :fast_enabled, :expected_result) do
+ false | false | 1.hour
+ true | false | 6.hours
+ false | true | 0
+ true | true | 0
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(
+ container_registry_migration_phase2_enqueue_speed_slow: slow_enabled,
+ container_registry_migration_phase2_enqueue_speed_fast: fast_enabled
+ )
+ end
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ describe '.capacity' do
+ subject { described_class.capacity }
+
+ where(:ff_1_enabled, :ff_10_enabled, :ff_25_enabled, :expected_result) do
+ false | false | false | 0
+ true | false | false | 1
+ true | true | false | 10
+ true | true | true | 25
+ false | true | false | 10
+ false | true | true | 25
+ false | false | true | 25
+ true | false | true | 25
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(
+ container_registry_migration_phase2_capacity_1: ff_1_enabled,
+ container_registry_migration_phase2_capacity_10: ff_10_enabled,
+ container_registry_migration_phase2_capacity_25: ff_25_enabled
+ )
+ end
+
+ it { is_expected.to eq(expected_result) }
+ end
+ end
+
+ describe '.max_tags_count' do
+ let(:value) { 1 }
+
+ before do
+ stub_application_setting(container_registry_import_max_tags_count: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.max_tags_count).to eq(value)
+ end
+ end
+
+ describe '.max_retries' do
+ let(:value) { 1 }
+
+ before do
+ stub_application_setting(container_registry_import_max_retries: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.max_retries).to eq(value)
+ end
+ end
+
+ describe '.start_max_retries' do
+ let(:value) { 1 }
+
+ before do
+ stub_application_setting(container_registry_import_start_max_retries: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.start_max_retries).to eq(value)
+ end
+ end
+
+ describe '.max_step_duration' do
+ let(:value) { 5.minutes }
+
+ before do
+ stub_application_setting(container_registry_import_max_step_duration: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.max_step_duration).to eq(value)
+ end
+ end
+
+ describe '.target_plan_name' do
+ let(:value) { 'free' }
+
+ before do
+ stub_application_setting(container_registry_import_target_plan: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.target_plan_name).to eq(value)
+ end
+ end
+
+ describe '.created_before' do
+ let(:value) { 1.day.ago }
+
+ before do
+ stub_application_setting(container_registry_import_created_before: value)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.created_before).to eq(value)
+ end
+ end
+
+ describe '.target_plan' do
+ let_it_be(:plan) { create(:plan) }
+
+ before do
+ stub_application_setting(container_registry_import_target_plan: plan.name)
+ end
+
+ it 'returns the matching application_setting' do
+ expect(described_class.target_plan).to eq(plan)
+ end
+ end
+end
diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb
index d6e2b17f53b..c690d96b4f5 100644
--- a/spec/lib/container_registry/registry_spec.rb
+++ b/spec/lib/container_registry/registry_spec.rb
@@ -27,4 +27,14 @@ RSpec.describe ContainerRegistry::Registry do
it { is_expected.to eq(path) }
end
end
+
+ describe '#gitlab_api_client' do
+ subject { registry.gitlab_api_client }
+
+ it 'returns a GitLabApiClient with an import token' do
+ expect(Auth::ContainerRegistryAuthenticationService).to receive(:import_access_token)
+
+ expect(subject).to be_instance_of(ContainerRegistry::GitlabApiClient)
+ end
+ end
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 9b2bb024fa6..5db2fbd923e 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe ExtractsPath do
describe '#assign_ref_vars' do
let(:ref) { sample_commit[:id] }
let(:path) { sample_commit[:line_code_path] }
- let(:params) { { path: path, ref: ref } }
+ let(:params) { ActionController::Parameters.new(path: path, ref: ref) }
it_behaves_like 'assigns ref vars'
@@ -54,7 +54,8 @@ RSpec.describe ExtractsPath do
context 'ref only exists without .atom suffix' do
context 'with a path' do
- let(:params) { { ref: 'v1.0.0.atom', path: 'README.md' } }
+ let(:ref) { 'v1.0.0.atom' }
+ let(:path) { 'README.md' }
it 'renders a 404' do
expect(self).to receive(:render_404)
@@ -64,7 +65,8 @@ RSpec.describe ExtractsPath do
end
context 'without a path' do
- let(:params) { { ref: 'v1.0.0.atom' } }
+ let(:ref) { 'v1.0.0.atom' }
+ let(:path) { nil }
before do
assign_ref_vars
@@ -82,7 +84,8 @@ RSpec.describe ExtractsPath do
context 'ref exists with .atom suffix' do
context 'with a path' do
- let(:params) { { ref: 'master.atom', path: 'README.md' } }
+ let(:ref) { 'master.atom' }
+ let(:path) { 'README.md' }
before do
repository = @project.repository
@@ -102,7 +105,8 @@ RSpec.describe ExtractsPath do
end
context 'without a path' do
- let(:params) { { ref: 'master.atom' } }
+ let(:ref) { 'master.atom' }
+ let(:path) { nil }
before do
repository = @project.repository
@@ -125,7 +129,8 @@ RSpec.describe ExtractsPath do
end
context 'ref and path are nil' do
- let(:params) { { path: nil, ref: nil } }
+ let(:path) { nil }
+ let(:ref) { nil }
it 'does not set commit' do
expect(container.repository).not_to receive(:commit).with('')
@@ -209,8 +214,8 @@ RSpec.describe ExtractsPath do
expect(extract_ref_without_atom('release/app/v1.0.0.atom')).to eq('release/app/v1.0.0')
end
- it 'returns nil if there are no matching refs' do
- expect(extract_ref_without_atom('foo.atom')).to eq(nil)
+ it 'raises an error if there are no matching refs' do
+ expect { extract_ref_without_atom('foo.atom') }.to raise_error(ExtractsRef::InvalidPathError)
end
end
end
diff --git a/spec/lib/extracts_ref_spec.rb b/spec/lib/extracts_ref_spec.rb
index 3cdce150de9..3e9a7499fdd 100644
--- a/spec/lib/extracts_ref_spec.rb
+++ b/spec/lib/extracts_ref_spec.rb
@@ -10,7 +10,8 @@ RSpec.describe ExtractsRef do
let_it_be(:container) { create(:snippet, :repository, author: owner) }
let(:ref) { sample_commit[:id] }
- let(:params) { { path: sample_commit[:line_code_path], ref: ref } }
+ let(:path) { sample_commit[:line_code_path] }
+ let(:params) { ActionController::Parameters.new(path: path, ref: ref) }
before do
ref_names = ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0', 'release/app', 'release/app/v1.0.0']
@@ -23,7 +24,8 @@ RSpec.describe ExtractsRef do
it_behaves_like 'assigns ref vars'
context 'ref and path are nil' do
- let(:params) { { path: nil, ref: nil } }
+ let(:ref) { nil }
+ let(:path) { nil }
it 'does not set commit' do
expect(container.repository).not_to receive(:commit).with('')
@@ -33,6 +35,15 @@ RSpec.describe ExtractsRef do
expect(@commit).to be_nil
end
end
+
+ context 'when ref and path have incorrect format' do
+ let(:ref) { { wrong: :format } }
+ let(:path) { { also: :wrong } }
+
+ it 'does not raise an exception' do
+ expect { assign_ref_vars }.not_to raise_error
+ end
+ end
end
it_behaves_like 'extracts refs'
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 8c546390201..5080d21d564 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -728,13 +728,13 @@ RSpec.describe Feature, stub_feature_flags: false do
describe '#targets' do
let(:project) { create(:project) }
let(:group) { create(:group) }
- let(:user_name) { project.owner.username }
+ let(:user_name) { project.first_owner.username }
subject { described_class.new(user: user_name, project: project.full_path, group: group.full_path) }
it 'returns all found targets' do
expect(subject.targets).to be_an(Array)
- expect(subject.targets).to eq([project.owner, project, group])
+ expect(subject.targets).to eq([project.first_owner, project, group])
end
end
end
diff --git a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
index 4e172dd32f0..d9fa6b931ad 100644
--- a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
+++ b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do
let(:ce_temp_dir) { Dir.mktmpdir }
let(:ee_temp_dir) { Dir.mktmpdir }
+ let(:timestamp) { Time.current.to_i }
let(:generator_options) { { 'category' => 'Groups::EmailCampaignsController', 'action' => 'click' } }
before do
@@ -12,6 +13,10 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do
stub_const("#{described_class}::EE_DIR", ee_temp_dir)
end
+ around do |example|
+ freeze_time { example.run }
+ end
+
after do
FileUtils.rm_rf([ce_temp_dir, ee_temp_dir])
end
@@ -22,16 +27,41 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do
end
let(:sample_event_dir) { 'lib/generators/gitlab/snowplow_event_definition_generator' }
+ let(:file_name) { Dir.children(ce_temp_dir).first }
it 'creates CE event definition file using the template' do
sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event.yml'))).load_raw!
described_class.new([], generator_options).invoke_all
- event_definition_path = File.join(ce_temp_dir, 'groups__email_campaigns_controller_click.yml')
+ event_definition_path = File.join(ce_temp_dir, file_name)
expect(::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!).to eq(sample_event)
end
+ describe 'generated filename' do
+ it 'includes timestamp' do
+ described_class.new([], generator_options).invoke_all
+
+ expect(file_name).to include(timestamp.to_s)
+ end
+
+ it 'removes special characters' do
+ generator_options = { 'category' => '"`ui:[mavenpackages | t5%348()-=@ ]`"', 'action' => 'click' }
+
+ described_class.new([], generator_options).invoke_all
+
+ expect(file_name).to include('uimavenpackagest')
+ end
+
+ it 'cuts name if longer than 100 characters' do
+ generator_options = { 'category' => 'a' * 100, 'action' => 'click' }
+
+ described_class.new([], generator_options).invoke_all
+
+ expect(file_name.length).to eq(100)
+ end
+ end
+
context 'event definition already exists' do
before do
stub_const('Gitlab::VERSION', '12.11.0-pre')
@@ -44,7 +74,7 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do
stub_const('Gitlab::VERSION', '13.11.0-pre')
described_class.new([], generator_options.merge('force' => true)).invoke_all
- event_definition_path = File.join(ce_temp_dir, 'groups__email_campaigns_controller_click.yml')
+ event_definition_path = File.join(ce_temp_dir, file_name)
event_data = ::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!
expect(event_data).to eq(sample_event)
@@ -56,13 +86,17 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do
end
end
- it 'creates EE event definition file using the template' do
- sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event_ee.yml'))).load_raw!
+ describe 'EE' do
+ let(:file_name) { Dir.children(ee_temp_dir).first }
- described_class.new([], generator_options.merge('ee' => true)).invoke_all
+ it 'creates EE event definition file using the template' do
+ sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event_ee.yml'))).load_raw!
- event_definition_path = File.join(ee_temp_dir, 'groups__email_campaigns_controller_click.yml')
- expect(::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!).to eq(sample_event)
+ described_class.new([], generator_options.merge('ee' => true)).invoke_all
+
+ event_definition_path = File.join(ee_temp_dir, file_name)
+ expect(::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw!).to eq(sample_event)
+ end
end
end
end
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb
index 55ba6e56237..8eb75feaa8d 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb
@@ -8,16 +8,17 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Aggregated::RecordsFetcher do
let_it_be(:issue_2) { create(:issue, project: project) }
let_it_be(:issue_3) { create(:issue, project: project) }
- let_it_be(:stage_event_1) { create(:cycle_analytics_issue_stage_event, issue_id: issue_1.id, start_event_timestamp: 2.years.ago, end_event_timestamp: 1.year.ago) } # duration: 1 year
- let_it_be(:stage_event_2) { create(:cycle_analytics_issue_stage_event, issue_id: issue_2.id, start_event_timestamp: 5.years.ago, end_event_timestamp: 2.years.ago) } # duration: 3 years
- let_it_be(:stage_event_3) { create(:cycle_analytics_issue_stage_event, issue_id: issue_3.id, start_event_timestamp: 6.years.ago, end_event_timestamp: 3.months.ago) } # duration: 5+ years
-
let_it_be(:stage) { create(:cycle_analytics_project_stage, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production, project: project) }
- let(:params) { {} }
+ let_it_be(:stage_event_1) { create(:cycle_analytics_issue_stage_event, stage_event_hash_id: stage.stage_event_hash_id, project_id: project.id, issue_id: issue_1.id, start_event_timestamp: 2.years.ago, end_event_timestamp: 1.year.ago) } # duration: 1 year
+ let_it_be(:stage_event_2) { create(:cycle_analytics_issue_stage_event, stage_event_hash_id: stage.stage_event_hash_id, project_id: project.id, issue_id: issue_2.id, start_event_timestamp: 5.years.ago, end_event_timestamp: 2.years.ago) } # duration: 3 years
+ let_it_be(:stage_event_3) { create(:cycle_analytics_issue_stage_event, stage_event_hash_id: stage.stage_event_hash_id, project_id: project.id, issue_id: issue_3.id, start_event_timestamp: 6.years.ago, end_event_timestamp: 3.months.ago) } # duration: 5+ years
+
+ let(:params) { { from: 10.years.ago, to: Date.today } }
subject(:records_fetcher) do
- described_class.new(stage: stage, query: Analytics::CycleAnalytics::IssueStageEvent.all, params: params)
+ query_builder = Gitlab::Analytics::CycleAnalytics::Aggregated::BaseQueryBuilder.new(stage: stage, params: params)
+ described_class.new(stage: stage, query: query_builder.build_sorted_query, params: params)
end
shared_examples 'match returned records' do
@@ -123,6 +124,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Aggregated::RecordsFetcher do
it 'skips non-existing issue records' do
create(:cycle_analytics_issue_stage_event, {
issue_id: 0, # non-existing id
+ stage_event_hash_id: stage.stage_event_hash_id,
+ project_id: project.id,
start_event_timestamp: 5.months.ago,
end_event_timestamp: 3.months.ago
})
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
index 4fe55ba0c0c..dc46dade87e 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb
@@ -43,8 +43,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
end
before do
- issue1.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
- issue2.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
+ issue1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
+ issue2.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
end
context 'when records are loaded by guest' do
@@ -73,8 +73,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
end
before do
- mr1.metrics.update(merged_at: 3.days.ago)
- mr2.metrics.update(merged_at: 3.days.ago)
+ mr1.metrics.update!(merged_at: 3.days.ago)
+ mr2.metrics.update!(merged_at: 3.days.ago)
end
include_context 'when records are loaded by maintainer'
@@ -95,9 +95,9 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
end
before(:all) do
- issue1.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
- issue2.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
- issue3.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
+ issue1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
+ issue2.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
+ issue3.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
end
before do
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
index 5ecec978017..55f5ae7d7dc 100644
--- a/spec/lib/gitlab/application_context_spec.rb
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -125,6 +125,17 @@ RSpec.describe Gitlab::ApplicationContext do
.to include(project: project.full_path, root_namespace: project.full_path_components.first)
end
+ it 'contains known keys' do
+ context = described_class.new(project: project)
+
+ # Make sure all possible keys would be included
+ allow(context).to receive_message_chain(:set_values, :include?).and_return(true)
+
+ # If a newly added key is added to the context hash, we need to list it in
+ # the known keys constant. This spec ensures that we do.
+ expect(context.to_lazy_hash.keys).to contain_exactly(*described_class.known_keys)
+ end
+
describe 'setting the client' do
let_it_be(:remote_ip) { '127.0.0.1' }
let_it_be(:runner) { create(:ci_runner) }
diff --git a/spec/lib/gitlab/audit/ci_runner_token_author_spec.rb b/spec/lib/gitlab/audit/ci_runner_token_author_spec.rb
new file mode 100644
index 00000000000..f55e1b44936
--- /dev/null
+++ b/spec/lib/gitlab/audit/ci_runner_token_author_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Audit::CiRunnerTokenAuthor do
+ describe '.initialize' do
+ subject { described_class.new(audit_event) }
+
+ let(:details) { }
+ let(:audit_event) { instance_double(AuditEvent, details: details, entity_type: 'Project', entity_path: 'd/e') }
+
+ context 'with runner_authentication_token' do
+ let(:details) do
+ { runner_authentication_token: 'abc1234567' }
+ end
+
+ it 'returns CiRunnerTokenAuthor with expected attributes' do
+ is_expected.to have_attributes(id: -1, name: 'Authentication token: abc1234567')
+ end
+ end
+
+ context 'with runner_registration_token' do
+ let(:details) do
+ { runner_registration_token: 'abc1234567' }
+ end
+
+ it 'returns CiRunnerTokenAuthor with expected attributes' do
+ is_expected.to have_attributes(id: -1, name: 'Registration token: abc1234567')
+ end
+ end
+
+ context 'with runner token missing' do
+ let(:details) do
+ {}
+ end
+
+ it 'raises ArgumentError' do
+ expect { subject }.to raise_error ArgumentError, 'Runner token missing'
+ end
+ end
+ end
+
+ describe '#full_path' do
+ subject { author.full_path }
+
+ let(:author) { described_class.new(audit_event) }
+
+ context 'with instance registration token' do
+ let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'User', entity_path: nil) }
+
+ it 'returns correct url' do
+ is_expected.to eq('/admin/runners')
+ end
+ end
+
+ context 'with group registration token' do
+ let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'Group', entity_path: 'a/b') }
+
+ it 'returns correct url' do
+ expect(::Gitlab::Routing.url_helpers).to receive(:group_settings_ci_cd_path)
+ .once
+ .with('a/b', { anchor: 'js-runners-settings' })
+ .and_return('/path/to/group/runners')
+
+ is_expected.to eq('/path/to/group/runners')
+ end
+ end
+
+ context 'with project registration token' do
+ let(:audit_event) { instance_double(AuditEvent, details: { runner_registration_token: 'abc1234567' }, entity_type: 'Project', entity_path: project.full_path) }
+ let(:project) { create(:project) }
+
+ it 'returns correct url' do
+ expect(::Gitlab::Routing.url_helpers).to receive(:project_settings_ci_cd_path)
+ .once
+ .with(project, { anchor: 'js-runners-settings' })
+ .and_return('/path/to/project/runners')
+
+ is_expected.to eq('/path/to/project/runners')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/audit/null_author_spec.rb b/spec/lib/gitlab/audit/null_author_spec.rb
index eb80e5faa89..7203a0cd816 100644
--- a/spec/lib/gitlab/audit/null_author_spec.rb
+++ b/spec/lib/gitlab/audit/null_author_spec.rb
@@ -6,13 +6,47 @@ RSpec.describe Gitlab::Audit::NullAuthor do
subject { described_class }
describe '.for' do
+ let(:audit_event) { instance_double(AuditEvent) }
+
it 'returns an DeletedAuthor' do
- expect(subject.for(666, 'Old Hat')).to be_a(Gitlab::Audit::DeletedAuthor)
+ allow(audit_event).to receive(:[]).with(:author_name).and_return('Old Hat')
+ allow(audit_event).to receive(:details).and_return({})
+ allow(audit_event).to receive(:target_type)
+
+ expect(subject.for(666, audit_event)).to be_a(Gitlab::Audit::DeletedAuthor)
end
it 'returns an UnauthenticatedAuthor when id equals -1', :aggregate_failures do
- expect(subject.for(-1, 'Frank')).to be_a(Gitlab::Audit::UnauthenticatedAuthor)
- expect(subject.for(-1, 'Frank')).to have_attributes(id: -1, name: 'Frank')
+ allow(audit_event).to receive(:[]).with(:author_name).and_return('Frank')
+ allow(audit_event).to receive(:details).and_return({})
+ allow(audit_event).to receive(:target_type)
+
+ expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::UnauthenticatedAuthor)
+ expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Frank')
+ end
+
+ it 'returns a CiRunnerTokenAuthor when details contain runner registration token', :aggregate_failures do
+ allow(audit_event).to receive(:[]).with(:author_name).and_return('cde456')
+ allow(audit_event).to receive(:entity_type).and_return('User')
+ allow(audit_event).to receive(:entity_path).and_return('/a/b')
+ allow(audit_event).to receive(:target_type).and_return(::Ci::Runner.name)
+ allow(audit_event).to receive(:details)
+ .and_return({ runner_registration_token: 'cde456', author_name: 'cde456', entity_type: 'User', entity_path: '/a/b' })
+
+ expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::CiRunnerTokenAuthor)
+ expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Registration token: cde456')
+ end
+
+ it 'returns a CiRunnerTokenAuthor when details contain runner authentication token', :aggregate_failures do
+ allow(audit_event).to receive(:[]).with(:author_name).and_return('cde456')
+ allow(audit_event).to receive(:entity_type).and_return('User')
+ allow(audit_event).to receive(:entity_path).and_return('/a/b')
+ allow(audit_event).to receive(:target_type).and_return(::Ci::Runner.name)
+ allow(audit_event).to receive(:details)
+ .and_return({ runner_authentication_token: 'cde456', author_name: 'cde456', entity_type: 'User', entity_path: '/a/b' })
+
+ expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::CiRunnerTokenAuthor)
+ expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Authentication token: cde456')
end
end
diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb
index e910ac09448..da0bb5fe675 100644
--- a/spec/lib/gitlab/auth/ldap/user_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/user_spec.rb
@@ -53,12 +53,12 @@ RSpec.describe Gitlab::Auth::Ldap::User do
it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain')
- expect { ldap_user.save }.not_to change { User.count }
+ expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang
end
it "connects to existing non-ldap user if the email matches" do
existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter")
- expect { ldap_user.save }.not_to change { User.count }
+ expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang
existing_user.reload
expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com'
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
it 'connects to existing ldap user if the extern_uid changes' do
existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
- expect { ldap_user.save }.not_to change { User.count }
+ expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang
existing_user.reload
expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com'
@@ -77,7 +77,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do
existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
- expect { ldap_user_upper_case.save }.not_to change { User.count }
+ expect { ldap_user_upper_case.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang
existing_user.reload
expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com'
@@ -89,7 +89,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter')
expect(existing_user.identities.count).to be(1)
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(ldap_user.gl_user.identities.count).to be(2)
# Expect that find_by provider only returns a single instance of an identity and not an Enumerable
@@ -98,7 +98,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it "creates a new user if not found" do
- expect { ldap_user.save }.to change { User.count }.by(1)
+ expect { ldap_user.save }.to change { User.count }.by(1) # rubocop:disable Rails/SaveBang
end
context 'when signup is disabled' do
@@ -107,7 +107,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it 'creates the user' do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -119,7 +119,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it 'creates and confirms the user anyway' do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
expect(gl_user).to be_confirmed
@@ -132,7 +132,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it 'creates the user' do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -189,7 +189,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -201,7 +201,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
@@ -210,7 +210,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
context 'sign-in' do
before do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
ldap_user.gl_user.activate
end
@@ -220,7 +220,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -232,7 +232,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do
end
it do
- ldap_user.save
+ ldap_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index 7a8e6e77d52..8d36507ec7a 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
create(:omniauth_user, extern_uid: 'my-uid', provider: provider)
stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_falsey
@@ -83,7 +83,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -97,7 +97,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'creates and confirms the user anyway' do
stub_omniauth_config(allow_single_sign_on: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
expect(gl_user).to be_confirmed
@@ -112,7 +112,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -121,7 +121,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'marks user as having password_automatically_set' do
stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
expect(gl_user).to be_password_automatically_set
@@ -131,7 +131,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'provider is marked as external' do
it 'marks user as external' do
stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_truthy
end
@@ -141,7 +141,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'does not mark external user as internal' do
create(:omniauth_user, extern_uid: 'my-uid', provider: provider, external: true)
stub_omniauth_config(allow_single_sign_on: [provider], external_providers: ['facebook'])
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_truthy
end
@@ -151,9 +151,9 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'when adding a new OAuth identity' do
it 'does not promote an external user to internal' do
user = create(:user, email: 'john@mail.com', external: true)
- user.identities.create(provider: provider, extern_uid: uid)
+ user.identities.create!(provider: provider, extern_uid: uid)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_truthy
end
@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it "creates a user from Omniauth" do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
identity = gl_user.identities.first
@@ -181,7 +181,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it "creates a user from Omniauth" do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
identity = gl_user.identities.first
@@ -196,7 +196,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it 'throws an error' do
- expect { oauth_user.save }.to raise_error StandardError
+ expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang
end
end
@@ -206,7 +206,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it 'throws an error' do
- expect { oauth_user.save }.to raise_error StandardError
+ expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang
end
end
end
@@ -228,7 +228,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
it "adds the OmniAuth identity to the GitLab user account" do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).not_to be_valid
end
@@ -248,7 +248,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
it "adds the OmniAuth identity to the GitLab user account" do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -277,7 +277,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') }
it "adds the OmniAuth identity to the GitLab user account" do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -337,7 +337,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
before do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
end
it "creates a user with dual LDAP and omniauth identities" do
@@ -376,7 +376,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(uid, any_args).and_return(nil)
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(info_hash[:email], any_args).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
end
it 'creates the LDAP identity' do
@@ -392,7 +392,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it "adds the omniauth identity to the LDAP account" do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -414,7 +414,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil)
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(result_identities(dn, uid))
@@ -426,7 +426,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(nil)
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_dn).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array(result_identities(dn, uid))
@@ -447,7 +447,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it 'does not save the identity' do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash).to match_array([{ provider: 'twitter', extern_uid: uid }])
@@ -467,7 +467,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it 'creates a user favoring the LDAP username and strips email domain' do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'johndoe'
@@ -510,7 +510,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
before do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
end
it "creates a user with dual LDAP and omniauth identities" do
@@ -549,7 +549,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it "adds the omniauth identity to the LDAP account" do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user)
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -584,7 +584,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -596,7 +596,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
@@ -622,7 +622,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -636,7 +636,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
@@ -654,7 +654,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -668,7 +668,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -678,7 +678,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'sign-in' do
before do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
oauth_user.gl_user.activate
end
@@ -688,7 +688,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -700,7 +700,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -714,7 +714,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -728,7 +728,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
it do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -791,7 +791,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'when collision with existing user' do
it 'generates the username with a counter' do
- oauth_user.save
+ oauth_user.save # rubocop:disable Rails/SaveBang
oauth_user2 = described_class.new(OmniAuth::AuthHash.new(uid: 'my-uid2', provider: provider, info: { nickname: 'johngitlab-ETC@othermail.com', email: 'john@othermail.com' }))
expect(oauth_user2.gl_user.username).to eq('johngitlab-ETC1')
diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb
index 6f3d6187076..5e9d07a8bf7 100644
--- a/spec/lib/gitlab/auth/request_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb
@@ -21,8 +21,10 @@ RSpec.describe Gitlab::Auth::RequestAuthenticator do
let_it_be(:session_user) { build(:user) }
it 'returns sessionless user first' do
- allow_any_instance_of(described_class).to receive(:find_sessionless_user).and_return(sessionless_user)
- allow_any_instance_of(described_class).to receive(:find_user_from_warden).and_return(session_user)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:find_sessionless_user).and_return(sessionless_user)
+ allow(instance).to receive(:find_user_from_warden).and_return(session_user)
+ end
expect(subject.user([:api])).to eq sessionless_user
end
diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb
index fd48492f18d..796512bc52b 100644
--- a/spec/lib/gitlab/auth/saml/user_spec.rb
+++ b/spec/lib/gitlab/auth/saml/user_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'and should bind with SAML' do
it 'adds the SAML identity to the existing user' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).to eq existing_user
identity = gl_user.identities.first
@@ -49,7 +49,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'are defined' do
it 'marks the user as external' do
stub_saml_group_config(%w(Freelancers))
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_truthy
end
@@ -61,7 +61,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'are defined but the user does not belong there' do
it 'does not mark the user as external' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_falsey
end
@@ -70,7 +70,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'user was external, now should not be' do
it 'makes user internal' do
existing_user.update_attribute('external', true)
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_falsey
end
@@ -86,7 +86,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'creates a user from SAML' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
identity = gl_user.identities.first
@@ -101,7 +101,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'does not throw an error' do
- expect { saml_user.save }.not_to raise_error
+ expect { saml_user.save }.not_to raise_error # rubocop:disable Rails/SaveBang
end
end
@@ -111,7 +111,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'throws an error' do
- expect { saml_user.save }.to raise_error StandardError
+ expect { saml_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang
end
end
end
@@ -120,7 +120,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'are defined' do
it 'marks the user as external' do
stub_saml_group_config(%w(Freelancers))
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_truthy
end
@@ -129,7 +129,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'are defined but the user does not belong there' do
it 'does not mark the user as external' do
stub_saml_group_config(%w(Interns))
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.external).to be_falsey
end
@@ -170,7 +170,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'and no account for the LDAP user' do
it 'creates a user with dual LDAP and SAML identities' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
@@ -230,7 +230,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
{ provider: id.provider, extern_uid: id.extern_uid }
end
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -259,7 +259,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'adds the omniauth identity to the LDAP account' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
@@ -271,9 +271,9 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'saves successfully on subsequent tries, when both identities are present' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
local_saml_user = described_class.new(auth_hash)
- local_saml_user.save
+ local_saml_user.save # rubocop:disable Rails/SaveBang
expect(local_saml_user.gl_user).to be_valid
expect(local_saml_user.gl_user).to be_persisted
@@ -289,7 +289,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
local_hash = OmniAuth::AuthHash.new(uid: dn, provider: provider, info: info_hash)
local_saml_user = described_class.new(local_hash)
- local_saml_user.save
+ local_saml_user.save # rubocop:disable Rails/SaveBang
local_gl_user = local_saml_user.gl_user
expect(local_gl_user).to be_valid
@@ -309,7 +309,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'creates the user' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -321,7 +321,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'creates and confirms the user anyway' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
expect(gl_user).to be_confirmed
@@ -334,7 +334,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'creates the user' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
@@ -353,7 +353,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'does not block the user' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -365,7 +365,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it 'blocks user' do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).to be_blocked
end
@@ -374,7 +374,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
context 'sign-in' do
before do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
saml_user.gl_user.activate
end
@@ -384,7 +384,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
@@ -396,7 +396,7 @@ RSpec.describe Gitlab::Auth::Saml::User do
end
it do
- saml_user.save
+ saml_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_valid
expect(gl_user).not_to be_blocked
end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 611c70d73a1..706344831b8 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -10,29 +10,29 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
describe 'constants' do
it 'API_SCOPES contains all scopes for API access' do
- expect(subject::API_SCOPES).to eq %i[api read_user read_api]
+ expect(subject::API_SCOPES).to match_array %i[api read_user read_api]
end
it 'ADMIN_SCOPES contains all scopes for ADMIN access' do
- expect(subject::ADMIN_SCOPES).to eq %i[sudo]
+ expect(subject::ADMIN_SCOPES).to match_array %i[sudo]
end
it 'REPOSITORY_SCOPES contains all scopes for REPOSITORY access' do
- expect(subject::REPOSITORY_SCOPES).to eq %i[read_repository write_repository]
+ expect(subject::REPOSITORY_SCOPES).to match_array %i[read_repository write_repository]
end
it 'OPENID_SCOPES contains all scopes for OpenID Connect' do
- expect(subject::OPENID_SCOPES).to eq [:openid]
+ expect(subject::OPENID_SCOPES).to match_array [:openid]
end
it 'DEFAULT_SCOPES contains all default scopes' do
- expect(subject::DEFAULT_SCOPES).to eq [:api]
+ expect(subject::DEFAULT_SCOPES).to match_array [:api]
end
it 'optional_scopes contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.optional_scopes).to eq %i[read_user read_api read_repository write_repository read_registry write_registry sudo openid profile email]
+ expect(subject.optional_scopes).to match_array %i[read_user read_api read_repository write_repository read_registry write_registry sudo openid profile email]
end
end
@@ -40,21 +40,21 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.all_available_scopes).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
+ expect(subject.all_available_scopes).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
end
it 'contains for non-admin user all non-default scopes without ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: false)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry]
+ expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry]
end
it 'contains for admin user all non-default scopes with ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: true)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
+ expect(subject.available_scopes_for(user)).to match_array %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
end
context 'registry_scopes' do
@@ -156,21 +156,36 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
let(:username) { 'gitlab-ci-token' }
context 'for running build' do
- let!(:build) { create(:ci_build, :running) }
- let(:project) { build.project }
+ let!(:group) { create(:group) }
+ let!(:project) { create(:project, group: group) }
+ let!(:build) { create(:ci_build, :running, project: project) }
it 'recognises user-less build' do
expect(subject).to have_attributes(actor: nil, project: build.project, type: :ci, authentication_abilities: described_class.build_authentication_abilities)
end
it 'recognises user token' do
- build.update(user: create(:user))
+ build.update!(user: create(:user))
+
+ expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities)
+ end
+
+ it 'recognises project level bot access token' do
+ build.update!(user: create(:user, :project_bot))
+ project.add_maintainer(build.user)
+
+ expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities)
+ end
+
+ it 'recognises group level bot access token' do
+ build.update!(user: create(:user, :project_bot))
+ group.add_maintainer(build.user)
expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities)
end
it 'fails with blocked user token' do
- build.update(user: create(:user, :blocked))
+ build.update!(user: create(:user, :blocked))
expect(subject).to have_attributes(auth_failure)
end
@@ -198,7 +213,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'recognizes other ci services' do
project.create_drone_ci_integration(active: true)
- project.drone_ci_integration.update(token: 'token')
+ project.drone_ci_integration.update!(token: 'token', drone_url: generate(:url))
expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to have_attributes(actor: nil, project: project, type: :ci, authentication_abilities: described_class.build_authentication_abilities)
end
@@ -311,7 +326,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
context 'orphaned token' do
before do
- user.destroy
+ user.destroy!
end
it_behaves_like 'an oauth failure'
@@ -888,7 +903,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'resets failed_attempts when true and password is correct' do
user.failed_attempts = 2
- user.save
+ user.save!
expect do
gl_auth.find_with_user_password(username, password, increment_failed_attempts: true)
@@ -917,7 +932,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'does not reset failed_attempts when true and password is correct' do
user.failed_attempts = 2
- user.save
+ user.save!
expect do
gl_auth.find_with_user_password(username, password, increment_failed_attempts: true)
diff --git a/spec/lib/gitlab/authorized_keys_spec.rb b/spec/lib/gitlab/authorized_keys_spec.rb
index 1053ae2e325..073cee96ede 100644
--- a/spec/lib/gitlab/authorized_keys_spec.rb
+++ b/spec/lib/gitlab/authorized_keys_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::AuthorizedKeys do
end
describe '#create' do
- subject { authorized_keys.create }
+ subject { authorized_keys.create } # rubocop:disable Rails/SaveBang
context 'authorized_keys file exists' do
before do
diff --git a/spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb b/spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb
new file mode 100644
index 00000000000..1aac5970a77
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb
@@ -0,0 +1,244 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillCiQueuingTables, :migration, schema: 20220208115439 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:ci_cd_settings) { table(:project_ci_cd_settings) }
+ let(:builds) { table(:ci_builds) }
+ let(:queuing_entries) { table(:ci_pending_builds) }
+ let(:tags) { table(:tags) }
+ let(:taggings) { table(:taggings) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ let!(:namespace) do
+ namespaces.create!(
+ id: 10,
+ name: 'namespace10',
+ path: 'namespace10',
+ traversal_ids: [10])
+ end
+
+ let!(:other_namespace) do
+ namespaces.create!(
+ id: 11,
+ name: 'namespace11',
+ path: 'namespace11',
+ traversal_ids: [11])
+ end
+
+ let!(:project) do
+ projects.create!(id: 5, namespace_id: 10, name: 'test1', path: 'test1')
+ end
+
+ let!(:ci_cd_setting) do
+ ci_cd_settings.create!(id: 5, project_id: 5, group_runners_enabled: true)
+ end
+
+ let!(:other_project) do
+ projects.create!(id: 7, namespace_id: 11, name: 'test2', path: 'test2')
+ end
+
+ let!(:other_ci_cd_setting) do
+ ci_cd_settings.create!(id: 7, project_id: 7, group_runners_enabled: false)
+ end
+
+ let!(:another_project) do
+ projects.create!(id: 9, namespace_id: 10, name: 'test3', path: 'test3', shared_runners_enabled: false)
+ end
+
+ let!(:ruby_tag) do
+ tags.create!(id: 22, name: 'ruby')
+ end
+
+ let!(:postgres_tag) do
+ tags.create!(id: 23, name: 'postgres')
+ end
+
+ it 'creates ci_pending_builds for all pending builds in range' do
+ builds.create!(id: 50, status: :pending, name: 'test1', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 51, status: :created, name: 'test2', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 52, status: :pending, name: 'test3', project_id: 5, protected: true, type: 'Ci::Build')
+
+ taggings.create!(taggable_id: 52, taggable_type: 'CommitStatus', tag_id: 22)
+ taggings.create!(taggable_id: 52, taggable_type: 'CommitStatus', tag_id: 23)
+
+ builds.create!(id: 60, status: :pending, name: 'test1', project_id: 7, type: 'Ci::Build')
+ builds.create!(id: 61, status: :running, name: 'test2', project_id: 7, protected: true, type: 'Ci::Build')
+ builds.create!(id: 62, status: :pending, name: 'test3', project_id: 7, type: 'Ci::Build')
+
+ taggings.create!(taggable_id: 60, taggable_type: 'CommitStatus', tag_id: 23)
+ taggings.create!(taggable_id: 62, taggable_type: 'CommitStatus', tag_id: 22)
+
+ builds.create!(id: 70, status: :pending, name: 'test1', project_id: 9, protected: true, type: 'Ci::Build')
+ builds.create!(id: 71, status: :failed, name: 'test2', project_id: 9, type: 'Ci::Build')
+ builds.create!(id: 72, status: :pending, name: 'test3', project_id: 9, type: 'Ci::Build')
+
+ taggings.create!(taggable_id: 71, taggable_type: 'CommitStatus', tag_id: 22)
+
+ subject.perform(1, 100)
+
+ expect(queuing_entries.all).to contain_exactly(
+ an_object_having_attributes(
+ build_id: 50,
+ project_id: 5,
+ namespace_id: 10,
+ protected: false,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: [],
+ namespace_traversal_ids: [10]),
+ an_object_having_attributes(
+ build_id: 52,
+ project_id: 5,
+ namespace_id: 10,
+ protected: true,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: match_array([22, 23]),
+ namespace_traversal_ids: [10]),
+ an_object_having_attributes(
+ build_id: 60,
+ project_id: 7,
+ namespace_id: 11,
+ protected: false,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: [23],
+ namespace_traversal_ids: []),
+ an_object_having_attributes(
+ build_id: 62,
+ project_id: 7,
+ namespace_id: 11,
+ protected: false,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: [22],
+ namespace_traversal_ids: []),
+ an_object_having_attributes(
+ build_id: 70,
+ project_id: 9,
+ namespace_id: 10,
+ protected: true,
+ instance_runners_enabled: false,
+ minutes_exceeded: false,
+ tag_ids: [],
+ namespace_traversal_ids: []),
+ an_object_having_attributes(
+ build_id: 72,
+ project_id: 9,
+ namespace_id: 10,
+ protected: false,
+ instance_runners_enabled: false,
+ minutes_exceeded: false,
+ tag_ids: [],
+ namespace_traversal_ids: [])
+ )
+ end
+
+ it 'skips builds that already have ci_pending_builds' do
+ builds.create!(id: 50, status: :pending, name: 'test1', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 51, status: :created, name: 'test2', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 52, status: :pending, name: 'test3', project_id: 5, protected: true, type: 'Ci::Build')
+
+ taggings.create!(taggable_id: 50, taggable_type: 'CommitStatus', tag_id: 22)
+ taggings.create!(taggable_id: 52, taggable_type: 'CommitStatus', tag_id: 23)
+
+ queuing_entries.create!(build_id: 50, project_id: 5, namespace_id: 10)
+
+ subject.perform(1, 100)
+
+ expect(queuing_entries.all).to contain_exactly(
+ an_object_having_attributes(
+ build_id: 50,
+ project_id: 5,
+ namespace_id: 10,
+ protected: false,
+ instance_runners_enabled: false,
+ minutes_exceeded: false,
+ tag_ids: [],
+ namespace_traversal_ids: []),
+ an_object_having_attributes(
+ build_id: 52,
+ project_id: 5,
+ namespace_id: 10,
+ protected: true,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: [23],
+ namespace_traversal_ids: [10])
+ )
+ end
+
+ it 'upserts values in case of conflicts' do
+ builds.create!(id: 50, status: :pending, name: 'test1', project_id: 5, type: 'Ci::Build')
+ queuing_entries.create!(build_id: 50, project_id: 5, namespace_id: 10)
+
+ build = described_class::Ci::Build.find(50)
+ described_class::Ci::PendingBuild.upsert_from_build!(build)
+
+ expect(queuing_entries.all).to contain_exactly(
+ an_object_having_attributes(
+ build_id: 50,
+ project_id: 5,
+ namespace_id: 10,
+ protected: false,
+ instance_runners_enabled: true,
+ minutes_exceeded: false,
+ tag_ids: [],
+ namespace_traversal_ids: [10])
+ )
+ end
+ end
+
+ context 'Ci::Build' do
+ describe '.each_batch' do
+ let(:model) { described_class::Ci::Build }
+
+ before do
+ builds.create!(id: 1, status: :pending, name: 'test1', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 2, status: :pending, name: 'test2', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 3, status: :pending, name: 'test3', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 4, status: :pending, name: 'test4', project_id: 5, type: 'Ci::Build')
+ builds.create!(id: 5, status: :pending, name: 'test5', project_id: 5, type: 'Ci::Build')
+ end
+
+ it 'yields an ActiveRecord::Relation when a block is given' do
+ model.each_batch do |relation|
+ expect(relation).to be_a_kind_of(ActiveRecord::Relation)
+ end
+ end
+
+ it 'yields a batch index as the second argument' do
+ model.each_batch do |_, index|
+ expect(index).to eq(1)
+ end
+ end
+
+ it 'accepts a custom batch size' do
+ amount = 0
+
+ model.each_batch(of: 1) { amount += 1 }
+
+ expect(amount).to eq(5)
+ end
+
+ it 'does not include ORDER BYs in the yielded relations' do
+ model.each_batch do |relation|
+ expect(relation.to_sql).not_to include('ORDER BY')
+ end
+ end
+
+ it 'orders ascending' do
+ ids = []
+
+ model.each_batch(of: 1) { |rel| ids.concat(rel.ids) }
+
+ expect(ids).to eq(ids.sort)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb
deleted file mode 100644
index c4013d002b2..00000000000
--- a/spec/lib/gitlab/background_migration/backfill_legacy_project_repositories_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::BackfillLegacyProjectRepositories do
- it_behaves_like 'backfill migration for project repositories', :legacy
-end
diff --git a/spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb b/spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb
new file mode 100644
index 00000000000..b821efcadb0
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillNamespaceIdForNamespaceRoute, :migration, schema: 20220120123800 do
+ let(:migration) { described_class.new }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:routes_table) { table(:routes) }
+
+ let(:table_name) { 'routes' }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 200 }
+ let(:pause_ms) { 0 }
+
+ let(:namespace1) { namespaces_table.create!(name: 'namespace1', path: 'namespace1', type: 'User') }
+ let(:namespace2) { namespaces_table.create!(name: 'namespace2', path: 'namespace2', type: 'Group') }
+ let(:namespace3) { namespaces_table.create!(name: 'namespace3', path: 'namespace3', type: 'Group') }
+ let(:namespace4) { namespaces_table.create!(name: 'namespace4', path: 'namespace4', type: 'Group') }
+ let(:project1) { projects_table.create!(name: 'project1', namespace_id: namespace1.id) }
+
+ subject(:perform_migration) { migration.perform(1, 10, table_name, batch_column, sub_batch_size, pause_ms) }
+
+ before do
+ routes_table.create!(id: 1, name: 'test1', path: 'test1', source_id: namespace1.id,
+ source_type: namespace1.class.sti_name)
+ routes_table.create!(id: 2, name: 'test2', path: 'test2', source_id: namespace2.id,
+ source_type: namespace2.class.sti_name)
+ routes_table.create!(id: 5, name: 'test3', path: 'test3', source_id: project1.id,
+ source_type: project1.class.sti_name) # should be ignored - project route
+ routes_table.create!(id: 6, name: 'test4', path: 'test4', source_id: non_existing_record_id,
+ source_type: namespace3.class.sti_name) # should be ignored - invalid source_id
+ routes_table.create!(id: 10, name: 'test5', path: 'test5', source_id: namespace3.id,
+ source_type: namespace3.class.sti_name)
+ routes_table.create!(id: 11, name: 'test6', path: 'test6', source_id: namespace4.id,
+ source_type: namespace4.class.sti_name) # should be ignored - outside the scope
+ end
+
+ it 'backfills `type` for the selected records', :aggregate_failures do
+ perform_migration
+
+ expect(routes_table.where.not(namespace_id: nil).pluck(:id)).to match_array([1, 2, 10])
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_updated_at_after_repository_storage_move_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_updated_at_after_repository_storage_move_spec.rb
deleted file mode 100644
index ed44b819a97..00000000000
--- a/spec/lib/gitlab/background_migration/backfill_project_updated_at_after_repository_storage_move_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::BackfillProjectUpdatedAtAfterRepositoryStorageMove, :migration, schema: 20210301200959 do
- let(:projects) { table(:projects) }
- let(:project_repository_storage_moves) { table(:project_repository_storage_moves) }
- let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
-
- subject { described_class.new }
-
- describe '#perform' do
- it 'updates project updated_at column if they were moved to a different repository storage' do
- freeze_time do
- project_1 = projects.create!(id: 1, namespace_id: namespace.id, updated_at: 1.day.ago)
- project_2 = projects.create!(id: 2, namespace_id: namespace.id, updated_at: Time.current)
- original_project_3_updated_at = 2.minutes.from_now
- project_3 = projects.create!(id: 3, namespace_id: namespace.id, updated_at: original_project_3_updated_at)
- original_project_4_updated_at = 10.days.ago
- project_4 = projects.create!(id: 4, namespace_id: namespace.id, updated_at: original_project_4_updated_at)
-
- repository_storage_move_1 = project_repository_storage_moves.create!(project_id: project_1.id, updated_at: 2.hours.ago, source_storage_name: 'default', destination_storage_name: 'default')
- repository_storage_move_2 = project_repository_storage_moves.create!(project_id: project_2.id, updated_at: Time.current, source_storage_name: 'default', destination_storage_name: 'default')
- project_repository_storage_moves.create!(project_id: project_3.id, updated_at: Time.current, source_storage_name: 'default', destination_storage_name: 'default')
-
- subject.perform([1, 2, 3, 4, non_existing_record_id])
-
- expect(project_1.reload.updated_at).to eq(repository_storage_move_1.updated_at + 1.second)
- expect(project_2.reload.updated_at).to eq(repository_storage_move_2.updated_at + 1.second)
- expect(project_3.reload.updated_at).to eq(original_project_3_updated_at)
- expect(project_4.reload.updated_at).to eq(original_project_4_updated_at)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_namespace_per_group_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_namespace_per_group_batching_strategy_spec.rb
new file mode 100644
index 00000000000..7b8a466b37c
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_namespace_per_group_batching_strategy_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectNamespacePerGroupBatchingStrategy, '#next_batch' do
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:background_migrations) { table(:batched_background_migrations) }
+
+ let!(:namespace1) { namespaces.create!(name: 'batchtest1', type: 'Group', path: 'batch-test1') }
+ let!(:namespace2) { namespaces.create!(name: 'batchtest2', type: 'Group', parent_id: namespace1.id, path: 'batch-test2') }
+ let!(:namespace3) { namespaces.create!(name: 'batchtest3', type: 'Group', parent_id: namespace2.id, path: 'batch-test3') }
+
+ let!(:project1) { projects.create!(name: 'project1', path: 'project1', namespace_id: namespace1.id, visibility_level: 20) }
+ let!(:project2) { projects.create!(name: 'project2', path: 'project2', namespace_id: namespace2.id, visibility_level: 20) }
+ let!(:project3) { projects.create!(name: 'project3', path: 'project3', namespace_id: namespace3.id, visibility_level: 20) }
+ let!(:project4) { projects.create!(name: 'project4', path: 'project4', namespace_id: namespace3.id, visibility_level: 20) }
+ let!(:batching_strategy) { described_class.new }
+
+ let(:job_arguments) { [namespace1.id, 'up'] }
+
+ context 'when starting on the first batch' do
+ it 'returns the bounds of the next batch' do
+ batch_bounds = batching_strategy.next_batch(:projects, :id, batch_min_value: project1.id, batch_size: 3, job_arguments: job_arguments)
+
+ expect(batch_bounds).to match_array([project1.id, project3.id])
+ end
+ end
+
+ context 'when additional batches remain' do
+ it 'returns the bounds of the next batch' do
+ batch_bounds = batching_strategy.next_batch(:projects, :id, batch_min_value: project2.id, batch_size: 3, job_arguments: job_arguments)
+
+ expect(batch_bounds).to match_array([project2.id, project4.id])
+ end
+ end
+
+ context 'when on the final batch' do
+ it 'returns the bounds of the next batch' do
+ batch_bounds = batching_strategy.next_batch(:projects, :id, batch_min_value: project4.id, batch_size: 3, job_arguments: job_arguments)
+
+ expect(batch_bounds).to match_array([project4.id, project4.id])
+ end
+ end
+
+ context 'when no additional batches remain' do
+ it 'returns nil' do
+ batch_bounds = batching_strategy.next_batch(:projects, :id, batch_min_value: project4.id + 1, batch_size: 1, job_arguments: job_arguments)
+
+ expect(batch_bounds).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
index 8febe850e04..39030039125 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
context 'when starting on the first batch' do
it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace1.id, batch_size: 3)
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace1.id, batch_size: 3, job_arguments: nil)
expect(batch_bounds).to eq([namespace1.id, namespace3.id])
end
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
context 'when additional batches remain' do
it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace2.id, batch_size: 3)
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace2.id, batch_size: 3, job_arguments: nil)
expect(batch_bounds).to eq([namespace2.id, namespace4.id])
end
@@ -29,7 +29,7 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
context 'when on the final batch' do
it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3)
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3, job_arguments: nil)
expect(batch_bounds).to eq([namespace4.id, namespace4.id])
end
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
context 'when no additional batches remain' do
it 'returns nil' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id + 1, batch_size: 1)
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id + 1, batch_size: 1, job_arguments: nil)
expect(batch_bounds).to be_nil
end
diff --git a/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb b/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
index d4fc24d0559..90d9bbb42c3 100644
--- a/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
+++ b/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
@@ -7,13 +7,14 @@ RSpec.describe Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJo
let(:test_table) { table(table_name) }
let(:sub_batch_size) { 1000 }
let(:pause_ms) { 0 }
+ let(:connection) { ApplicationRecord.connection }
let(:helpers) do
ActiveRecord::Migration.new.extend(Gitlab::Database::MigrationHelpers)
end
before do
- ActiveRecord::Base.connection.execute(<<~SQL)
+ connection.execute(<<~SQL)
CREATE TABLE #{table_name}
(
id integer NOT NULL,
@@ -34,12 +35,14 @@ RSpec.describe Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJo
after do
# Make sure that the temp table we created is dropped (it is not removed by the database_cleaner)
- ActiveRecord::Base.connection.execute(<<~SQL)
+ connection.execute(<<~SQL)
DROP TABLE IF EXISTS #{table_name};
SQL
end
- subject(:copy_columns) { described_class.new }
+ subject(:copy_columns) { described_class.new(connection: connection) }
+
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BaseJob }
describe '#perform' do
let(:migration_class) { described_class.name }
diff --git a/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb b/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb
deleted file mode 100644
index 68fe8f39f59..00000000000
--- a/spec/lib/gitlab/background_migration/populate_finding_uuid_for_vulnerability_feedback_spec.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::PopulateFindingUuidForVulnerabilityFeedback, schema: 20210301200959 do
- let(:namespaces) { table(:namespaces) }
- let(:projects) { table(:projects) }
- let(:users) { table(:users) }
- let(:scanners) { table(:vulnerability_scanners) }
- let(:identifiers) { table(:vulnerability_identifiers) }
- let(:findings) { table(:vulnerability_occurrences) }
- let(:vulnerability_feedback) { table(:vulnerability_feedback) }
-
- let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
- let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo') }
- let(:user) { users.create!(username: 'john.doe', projects_limit: 5) }
- let(:scanner) { scanners.create!(project_id: project.id, external_id: 'foo', name: 'bar') }
- let(:identifier) { identifiers.create!(project_id: project.id, fingerprint: 'foo', external_type: 'bar', external_id: 'zoo', name: 'baz') }
- let(:sast_report) { 0 }
- let(:dependency_scanning_report) { 1 }
- let(:dast_report) { 3 }
- let(:secret_detection_report) { 4 }
- let(:project_fingerprint) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
- let(:location_fingerprint_1) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
- let(:location_fingerprint_2) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
- let(:location_fingerprint_3) { Digest::SHA1.hexdigest(SecureRandom.uuid) }
- let(:finding_1) { finding_creator.call(sast_report, location_fingerprint_1) }
- let(:finding_2) { finding_creator.call(dast_report, location_fingerprint_2) }
- let(:finding_3) { finding_creator.call(secret_detection_report, location_fingerprint_3) }
- let(:expected_uuid_1) do
- Security::VulnerabilityUUID.generate(
- report_type: 'sast',
- primary_identifier_fingerprint: identifier.fingerprint,
- location_fingerprint: location_fingerprint_1,
- project_id: project.id
- )
- end
-
- let(:expected_uuid_2) do
- Security::VulnerabilityUUID.generate(
- report_type: 'dast',
- primary_identifier_fingerprint: identifier.fingerprint,
- location_fingerprint: location_fingerprint_2,
- project_id: project.id
- )
- end
-
- let(:expected_uuid_3) do
- Security::VulnerabilityUUID.generate(
- report_type: 'secret_detection',
- primary_identifier_fingerprint: identifier.fingerprint,
- location_fingerprint: location_fingerprint_3,
- project_id: project.id
- )
- end
-
- let(:finding_creator) do
- -> (report_type, location_fingerprint) do
- findings.create!(
- project_id: project.id,
- primary_identifier_id: identifier.id,
- scanner_id: scanner.id,
- report_type: report_type,
- uuid: SecureRandom.uuid,
- name: 'Foo',
- location_fingerprint: Gitlab::Database::ShaAttribute.serialize(location_fingerprint),
- project_fingerprint: Gitlab::Database::ShaAttribute.serialize(project_fingerprint),
- metadata_version: '1',
- severity: 0,
- confidence: 5,
- raw_metadata: '{}'
- )
- end
- end
-
- let(:feedback_creator) do
- -> (category, project_fingerprint) do
- vulnerability_feedback.create!(
- project_id: project.id,
- author_id: user.id,
- feedback_type: 0,
- category: category,
- project_fingerprint: project_fingerprint
- )
- end
- end
-
- let!(:feedback_1) { feedback_creator.call(finding_1.report_type, project_fingerprint) }
- let!(:feedback_2) { feedback_creator.call(finding_2.report_type, project_fingerprint) }
- let!(:feedback_3) { feedback_creator.call(finding_3.report_type, project_fingerprint) }
- let!(:feedback_4) { feedback_creator.call(finding_1.report_type, 'foo') }
- let!(:feedback_5) { feedback_creator.call(dependency_scanning_report, project_fingerprint) }
-
- subject(:populate_finding_uuids) { described_class.new.perform(feedback_1.id, feedback_5.id) }
-
- before do
- allow(Gitlab::BackgroundMigration::Logger).to receive(:info)
- end
-
- describe '#perform' do
- it 'updates the `finding_uuid` attributes of the feedback records' do
- expect { populate_finding_uuids }.to change { feedback_1.reload.finding_uuid }.from(nil).to(expected_uuid_1)
- .and change { feedback_2.reload.finding_uuid }.from(nil).to(expected_uuid_2)
- .and change { feedback_3.reload.finding_uuid }.from(nil).to(expected_uuid_3)
- .and not_change { feedback_4.reload.finding_uuid }
- .and not_change { feedback_5.reload.finding_uuid }
-
- expect(Gitlab::BackgroundMigration::Logger).to have_received(:info).once
- end
-
- it 'preloads the finding and identifier records to prevent N+1 queries' do
- # Load feedback records(1), load findings(2), load identifiers(3) and finally update feedback records one by one(6)
- expect { populate_finding_uuids }.not_to exceed_query_limit(6)
- end
-
- context 'when setting the `finding_uuid` attribute of a feedback record fails' do
- let(:expected_error) { RuntimeError.new }
-
- before do
- allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
-
- allow_next_found_instance_of(described_class::VulnerabilityFeedback) do |feedback|
- allow(feedback).to receive(:update_column).and_raise(expected_error)
- end
- end
-
- it 'captures the errors and does not crash entirely' do
- expect { populate_finding_uuids }.not_to raise_error
-
- expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(expected_error).exactly(3).times
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/background_migration/populate_issue_email_participants_spec.rb b/spec/lib/gitlab/background_migration/populate_issue_email_participants_spec.rb
deleted file mode 100644
index b00eb185b34..00000000000
--- a/spec/lib/gitlab/background_migration/populate_issue_email_participants_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::PopulateIssueEmailParticipants, schema: 20210301200959 do
- let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let!(:project) { table(:projects).create!(id: 1, namespace_id: namespace.id) }
- let!(:issue1) { table(:issues).create!(id: 1, project_id: project.id, service_desk_reply_to: "a@gitlab.com") }
- let!(:issue2) { table(:issues).create!(id: 2, project_id: project.id, service_desk_reply_to: "b@gitlab.com") }
- let(:issue_email_participants) { table(:issue_email_participants) }
-
- describe '#perform' do
- it 'migrates email addresses from service desk issues', :aggregate_failures do
- expect { subject.perform(1, 2) }.to change { issue_email_participants.count }.by(2)
-
- expect(issue_email_participants.find_by(issue_id: 1).email).to eq("a@gitlab.com")
- expect(issue_email_participants.find_by(issue_id: 2).email).to eq("b@gitlab.com")
- end
- end
-end
diff --git a/spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb b/spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb
new file mode 100644
index 00000000000..e72e3392210
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::PopulateTopicsNonPrivateProjectsCount, schema: 20220125122640 do
+ it 'correctly populates the non private projects counters' do
+ namespaces = table(:namespaces)
+ projects = table(:projects)
+ topics = table(:topics)
+ project_topics = table(:project_topics)
+
+ group = namespaces.create!(name: 'group', path: 'group')
+ project_public = projects.create!(namespace_id: group.id, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project_internal = projects.create!(namespace_id: group.id, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ project_private = projects.create!(namespace_id: group.id, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ topic_1 = topics.create!(name: 'Topic1')
+ topic_2 = topics.create!(name: 'Topic2')
+ topic_3 = topics.create!(name: 'Topic3')
+ topic_4 = topics.create!(name: 'Topic4')
+ topic_5 = topics.create!(name: 'Topic5')
+ topic_6 = topics.create!(name: 'Topic6')
+ topic_7 = topics.create!(name: 'Topic7')
+ topic_8 = topics.create!(name: 'Topic8')
+
+ project_topics.create!(topic_id: topic_1.id, project_id: project_public.id)
+ project_topics.create!(topic_id: topic_2.id, project_id: project_internal.id)
+ project_topics.create!(topic_id: topic_3.id, project_id: project_private.id)
+ project_topics.create!(topic_id: topic_4.id, project_id: project_public.id)
+ project_topics.create!(topic_id: topic_4.id, project_id: project_internal.id)
+ project_topics.create!(topic_id: topic_5.id, project_id: project_public.id)
+ project_topics.create!(topic_id: topic_5.id, project_id: project_private.id)
+ project_topics.create!(topic_id: topic_6.id, project_id: project_internal.id)
+ project_topics.create!(topic_id: topic_6.id, project_id: project_private.id)
+ project_topics.create!(topic_id: topic_7.id, project_id: project_public.id)
+ project_topics.create!(topic_id: topic_7.id, project_id: project_internal.id)
+ project_topics.create!(topic_id: topic_7.id, project_id: project_private.id)
+ project_topics.create!(topic_id: topic_8.id, project_id: project_public.id)
+
+ subject.perform(topic_1.id, topic_7.id)
+
+ expect(topic_1.reload.non_private_projects_count).to eq(1)
+ expect(topic_2.reload.non_private_projects_count).to eq(1)
+ expect(topic_3.reload.non_private_projects_count).to eq(0)
+ expect(topic_4.reload.non_private_projects_count).to eq(2)
+ expect(topic_5.reload.non_private_projects_count).to eq(1)
+ expect(topic_6.reload.non_private_projects_count).to eq(1)
+ expect(topic_7.reload.non_private_projects_count).to eq(2)
+ expect(topic_8.reload.non_private_projects_count).to eq(0)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb b/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb
new file mode 100644
index 00000000000..a265fa95b23
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::PopulateVulnerabilityReads do
+ let(:vulnerabilities) { table(:vulnerabilities) }
+ let(:vulnerability_reads) { table(:vulnerability_reads) }
+ let(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let(:vulnerability_issue_links) { table(:vulnerability_issue_links) }
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:user) { table(:users).create!(email: 'author@example.com', username: 'author', projects_limit: 10) }
+ let(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let(:scanner) { table(:vulnerability_scanners).create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let(:sub_batch_size) { 1000 }
+
+ before do
+ vulnerabilities_findings.connection.execute 'ALTER TABLE vulnerability_occurrences DISABLE TRIGGER "trigger_insert_or_update_vulnerability_reads_from_occurrences"'
+ vulnerabilities.connection.execute 'ALTER TABLE vulnerabilities DISABLE TRIGGER "trigger_update_vulnerability_reads_on_vulnerability_update"'
+ vulnerability_issue_links.connection.execute 'ALTER TABLE vulnerability_issue_links DISABLE TRIGGER "trigger_update_has_issues_on_vulnerability_issue_links_update"'
+
+ 10.times.each do |x|
+ vulnerability = create_vulnerability!(
+ project_id: project.id,
+ report_type: 7,
+ author_id: user.id
+ )
+ identifier = table(:vulnerability_identifiers).create!(
+ project_id: project.id,
+ external_type: 'uuid-v5',
+ external_id: 'uuid-v5',
+ fingerprint: Digest::SHA1.hexdigest("#{vulnerability.id}"),
+ name: 'Identifier for UUIDv5')
+
+ create_finding!(
+ vulnerability_id: vulnerability.id,
+ project_id: project.id,
+ scanner_id: scanner.id,
+ primary_identifier_id: identifier.id
+ )
+ end
+ end
+
+ it 'creates vulnerability_reads for the given records' do
+ described_class.new.perform(vulnerabilities.first.id, vulnerabilities.last.id, sub_batch_size)
+
+ expect(vulnerability_reads.count).to eq(10)
+ end
+
+ it 'does not create new records when records already exists' do
+ described_class.new.perform(vulnerabilities.first.id, vulnerabilities.last.id, sub_batch_size)
+ described_class.new.perform(vulnerabilities.first.id, vulnerabilities.last.id, sub_batch_size)
+
+ expect(vulnerability_reads.count).to eq(10)
+ end
+
+ private
+
+ def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)
+ vulnerabilities.create!(
+ project_id: project_id,
+ author_id: author_id,
+ title: title,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type
+ )
+ end
+
+ # rubocop:disable Metrics/ParameterLists
+ def create_finding!(
+ vulnerability_id: nil, project_id:, scanner_id:, primary_identifier_id:,
+ name: "test", severity: 7, confidence: 7, report_type: 0,
+ project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, location_fingerprint: 'test',
+ metadata_version: 'test', raw_metadata: 'test', uuid: SecureRandom.uuid)
+ vulnerabilities_findings.create!(
+ vulnerability_id: vulnerability_id,
+ project_id: project_id,
+ name: name,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type,
+ project_fingerprint: project_fingerprint,
+ scanner_id: scanner_id,
+ primary_identifier_id: primary_identifier_id,
+ location: location,
+ location_fingerprint: location_fingerprint,
+ metadata_version: metadata_version,
+ raw_metadata: raw_metadata,
+ uuid: uuid
+ )
+ end
+ # rubocop:enable Metrics/ParameterLists
+end
diff --git a/spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb b/spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb
index 24259b06469..2c5de448fbc 100644
--- a/spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb
+++ b/spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = ::Project.minimum(:id)
end_id = ::Project.maximum(:id)
projects_count = ::Project.count
- batches_count = (projects_count / described_class::BATCH_SIZE.to_f).ceil
+ batches_count = (projects_count / described_class::SUB_BATCH_SIZE.to_f).ceil
project_namespaces_count = ::Namespace.where(type: 'Project').count
migration = described_class.new
@@ -39,7 +39,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
expect(migration).to receive(:batch_update_projects).exactly(batches_count).and_call_original
expect(migration).to receive(:batch_update_project_namespaces_traversal_ids).exactly(batches_count).and_call_original
- expect { migration.perform(start_id, end_id, nil, 'up') }.to change(Namespace.where(type: 'Project'), :count)
+ expect { migration.perform(start_id, end_id, nil, nil, nil, nil, nil, 'up') }.to change(Namespace.where(type: 'Project'), :count)
expect(projects_count).to eq(::Namespace.where(type: 'Project').count)
check_projects_in_sync_with(Namespace.where(type: 'Project'))
@@ -53,7 +53,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = backfilled_namespace_projects.minimum(:id)
end_id = backfilled_namespace_projects.maximum(:id)
group_projects_count = backfilled_namespace_projects.count
- batches_count = (group_projects_count / described_class::BATCH_SIZE.to_f).ceil
+ batches_count = (group_projects_count / described_class::SUB_BATCH_SIZE.to_f).ceil
project_namespaces_in_hierarchy = project_namespaces_in_hierarchy(base_ancestor(backfilled_namespace))
migration = described_class.new
@@ -66,7 +66,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
expect(group_projects_count).to eq(14)
expect(project_namespaces_in_hierarchy.count).to eq(0)
- migration.perform(start_id, end_id, backfilled_namespace.id, 'up')
+ migration.perform(start_id, end_id, nil, nil, nil, nil, backfilled_namespace.id, 'up')
expect(project_namespaces_in_hierarchy.count).to eq(14)
check_projects_in_sync_with(project_namespaces_in_hierarchy)
@@ -79,7 +79,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = hierarchy1_projects.minimum(:id)
end_id = hierarchy1_projects.maximum(:id)
- described_class.new.perform(start_id, end_id, parent_group1.id, 'up')
+ described_class.new.perform(start_id, end_id, nil, nil, nil, nil, parent_group1.id, 'up')
end
it 'does not duplicate project namespaces' do
@@ -87,7 +87,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
projects_count = ::Project.count
start_id = ::Project.minimum(:id)
end_id = ::Project.maximum(:id)
- batches_count = (projects_count / described_class::BATCH_SIZE.to_f).ceil
+ batches_count = (projects_count / described_class::SUB_BATCH_SIZE.to_f).ceil
project_namespaces = ::Namespace.where(type: 'Project')
migration = described_class.new
@@ -100,7 +100,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
expect(migration).to receive(:batch_update_projects).exactly(batches_count).and_call_original
expect(migration).to receive(:batch_update_project_namespaces_traversal_ids).exactly(batches_count).and_call_original
- expect { migration.perform(start_id, end_id, nil, 'up') }.to change(project_namespaces, :count).by(14)
+ expect { migration.perform(start_id, end_id, nil, nil, nil, nil, nil, 'up') }.to change(project_namespaces, :count).by(14)
expect(projects_count).to eq(project_namespaces.count)
end
@@ -125,7 +125,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
context 'back-fill project namespaces in batches' do
before do
- stub_const("#{described_class.name}::BATCH_SIZE", 2)
+ stub_const("#{described_class.name}::SUB_BATCH_SIZE", 2)
end
it_behaves_like 'back-fill project namespaces'
@@ -137,7 +137,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = ::Project.minimum(:id)
end_id = ::Project.maximum(:id)
# back-fill first
- described_class.new.perform(start_id, end_id, nil, 'up')
+ described_class.new.perform(start_id, end_id, nil, nil, nil, nil, nil, 'up')
end
shared_examples 'cleanup project namespaces' do
@@ -146,7 +146,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = ::Project.minimum(:id)
end_id = ::Project.maximum(:id)
migration = described_class.new
- batches_count = (projects_count / described_class::BATCH_SIZE.to_f).ceil
+ batches_count = (projects_count / described_class::SUB_BATCH_SIZE.to_f).ceil
expect(projects_count).to be > 0
expect(projects_count).to eq(::Namespace.where(type: 'Project').count)
@@ -154,7 +154,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
expect(migration).to receive(:nullify_project_namespaces_in_projects).exactly(batches_count).and_call_original
expect(migration).to receive(:delete_project_namespace_records).exactly(batches_count).and_call_original
- migration.perform(start_id, end_id, nil, 'down')
+ migration.perform(start_id, end_id, nil, nil, nil, nil, nil, 'down')
expect(::Project.count).to be > 0
expect(::Namespace.where(type: 'Project').count).to eq(0)
@@ -168,7 +168,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
start_id = backfilled_namespace_projects.minimum(:id)
end_id = backfilled_namespace_projects.maximum(:id)
group_projects_count = backfilled_namespace_projects.count
- batches_count = (group_projects_count / described_class::BATCH_SIZE.to_f).ceil
+ batches_count = (group_projects_count / described_class::SUB_BATCH_SIZE.to_f).ceil
project_namespaces_in_hierarchy = project_namespaces_in_hierarchy(base_ancestor(backfilled_namespace))
migration = described_class.new
@@ -176,7 +176,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
expect(migration).to receive(:nullify_project_namespaces_in_projects).exactly(batches_count).and_call_original
expect(migration).to receive(:delete_project_namespace_records).exactly(batches_count).and_call_original
- migration.perform(start_id, end_id, backfilled_namespace.id, 'down')
+ migration.perform(start_id, end_id, nil, nil, nil, nil, backfilled_namespace.id, 'down')
expect(::Namespace.where(type: 'Project').count).to be > 0
expect(project_namespaces_in_hierarchy.count).to eq(0)
@@ -190,7 +190,7 @@ RSpec.describe Gitlab::BackgroundMigration::ProjectNamespaces::BackfillProjectNa
context 'cleanup project namespaces in batches' do
before do
- stub_const("#{described_class.name}::BATCH_SIZE", 2)
+ stub_const("#{described_class.name}::SUB_BATCH_SIZE", 2)
end
it_behaves_like 'cleanup project namespaces'
diff --git a/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb b/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
index 7214225c32c..f6f4a3f6115 100644
--- a/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveDuplicateVulnerabilitiesFindin
let!(:unrelated_finding) do
create_finding!(
id: 9999999,
- uuid: "unreleated_finding",
+ uuid: Gitlab::UUID.v5(SecureRandom.hex),
vulnerability_id: nil,
report_type: 1,
location_fingerprint: 'random_location_fingerprint',
diff --git a/spec/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer_spec.rb b/spec/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer_spec.rb
deleted file mode 100644
index 17fe25c7f71..00000000000
--- a/spec/lib/gitlab/background_migration/wrongfully_confirmed_email_unconfirmer_spec.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer, schema: 20210301200959 do
- let(:users) { table(:users) }
- let(:emails) { table(:emails) }
- let(:user_synced_attributes_metadata) { table(:user_synced_attributes_metadata) }
- let(:confirmed_at_2_days_ago) { 2.days.ago }
- let(:confirmed_at_3_days_ago) { 3.days.ago }
- let(:one_year_ago) { 1.year.ago }
-
- let!(:user_needs_migration_1) { users.create!(name: 'user1', email: 'test1@test.com', state: 'active', projects_limit: 1, confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:user_needs_migration_2) { users.create!(name: 'user2', email: 'test2@test.com', unconfirmed_email: 'unconfirmed@test.com', state: 'active', projects_limit: 1, confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:user_does_not_need_migration) { users.create!(name: 'user3', email: 'test3@test.com', state: 'active', projects_limit: 1) }
- let!(:inactive_user) { users.create!(name: 'user4', email: 'test4@test.com', state: 'blocked', projects_limit: 1, confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:alert_bot_user) { users.create!(name: 'user5', email: 'test5@test.com', state: 'active', user_type: 2, projects_limit: 1, confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:user_has_synced_email) { users.create!(name: 'user6', email: 'test6@test.com', state: 'active', projects_limit: 1, confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:synced_attributes_metadata_for_user) { user_synced_attributes_metadata.create!(user_id: user_has_synced_email.id, email_synced: true) }
-
- let!(:bad_email_1) { emails.create!(user_id: user_needs_migration_1.id, email: 'other1@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:bad_email_2) { emails.create!(user_id: user_needs_migration_2.id, email: 'other2@test.com', confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:bad_email_3_inactive_user) { emails.create!(user_id: inactive_user.id, email: 'other-inactive@test.com', confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:bad_email_4_bot_user) { emails.create!(user_id: alert_bot_user.id, email: 'other-bot@test.com', confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
-
- let!(:good_email_1) { emails.create!(user_id: user_needs_migration_2.id, email: 'other3@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
- let!(:good_email_2) { emails.create!(user_id: user_needs_migration_2.id, email: 'other4@test.com', confirmed_at: nil) }
- let!(:good_email_3) { emails.create!(user_id: user_does_not_need_migration.id, email: 'other5@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
-
- let!(:second_email_for_user_with_synced_email) { emails.create!(user_id: user_has_synced_email.id, email: 'other6@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
-
- subject do
- email_ids = [bad_email_1, bad_email_2, good_email_1, good_email_2, good_email_3, second_email_for_user_with_synced_email].map(&:id)
-
- described_class.new.perform(email_ids.min, email_ids.max)
- end
-
- it 'does not change irrelevant email records' do
- subject
-
- expect(good_email_1.reload.confirmed_at).to be_within(1.second).of(confirmed_at_2_days_ago)
- expect(good_email_2.reload.confirmed_at).to be_nil
- expect(good_email_3.reload.confirmed_at).to be_within(1.second).of(confirmed_at_2_days_ago)
-
- expect(bad_email_3_inactive_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
- expect(bad_email_4_bot_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
-
- expect(good_email_1.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- expect(good_email_2.reload.confirmation_sent_at).to be_nil
- expect(good_email_3.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
-
- expect(bad_email_3_inactive_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- expect(bad_email_4_bot_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- end
-
- it 'clears the `unconfirmed_email` field' do
- subject
-
- user_needs_migration_2.reload
- expect(user_needs_migration_2.unconfirmed_email).to be_nil
- end
-
- it 'does not change irrelevant user records' do
- subject
-
- expect(user_does_not_need_migration.reload.confirmed_at).to be_nil
- expect(inactive_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
- expect(alert_bot_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
- expect(user_has_synced_email.reload.confirmed_at).to be_within(1.second).of(confirmed_at_2_days_ago)
-
- expect(user_does_not_need_migration.reload.confirmation_sent_at).to be_nil
- expect(inactive_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- expect(alert_bot_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- expect(user_has_synced_email.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
- end
-
- it 'updates confirmation_sent_at column' do
- subject
-
- expect(user_needs_migration_1.reload.confirmation_sent_at).to be_within(1.minute).of(Time.now)
- expect(user_needs_migration_2.reload.confirmation_sent_at).to be_within(1.minute).of(Time.now)
-
- expect(bad_email_1.reload.confirmation_sent_at).to be_within(1.minute).of(Time.now)
- expect(bad_email_2.reload.confirmation_sent_at).to be_within(1.minute).of(Time.now)
- end
-
- it 'unconfirms bad email records' do
- subject
-
- expect(bad_email_1.reload.confirmed_at).to be_nil
- expect(bad_email_2.reload.confirmed_at).to be_nil
-
- expect(bad_email_1.reload.confirmation_token).not_to be_nil
- expect(bad_email_2.reload.confirmation_token).not_to be_nil
- end
-
- it 'unconfirms user records' do
- subject
-
- expect(user_needs_migration_1.reload.confirmed_at).to be_nil
- expect(user_needs_migration_2.reload.confirmed_at).to be_nil
-
- expect(user_needs_migration_1.reload.confirmation_token).not_to be_nil
- expect(user_needs_migration_2.reload.confirmation_token).not_to be_nil
- end
-
- context 'enqueued jobs' do
- let(:user_1) { User.find(user_needs_migration_1.id) }
- let(:user_2) { User.find(user_needs_migration_2.id) }
-
- let(:email_1) { Email.find(bad_email_1.id) }
- let(:email_2) { Email.find(bad_email_2.id) }
-
- it 'enqueues the email confirmation and the unconfirm notification mailer jobs' do
- allow(DeviseMailer).to receive(:confirmation_instructions).and_call_original
- allow(Gitlab::BackgroundMigration::Mailers::UnconfirmMailer).to receive(:unconfirm_notification_email).and_call_original
-
- subject
-
- expect(DeviseMailer).to have_received(:confirmation_instructions).with(email_1, email_1.confirmation_token)
- expect(DeviseMailer).to have_received(:confirmation_instructions).with(email_2, email_2.confirmation_token)
-
- expect(Gitlab::BackgroundMigration::Mailers::UnconfirmMailer).to have_received(:unconfirm_notification_email).with(user_1)
- expect(DeviseMailer).to have_received(:confirmation_instructions).with(user_1, user_1.confirmation_token)
-
- expect(Gitlab::BackgroundMigration::Mailers::UnconfirmMailer).to have_received(:unconfirm_notification_email).with(user_2)
- expect(DeviseMailer).to have_received(:confirmation_instructions).with(user_2, user_2.confirmation_token)
- end
- end
-end
diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
index 0380ddd9a2e..d2abdb740f8 100644
--- a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
@@ -22,8 +22,8 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
data: { project_key: project_key, repo_slug: repo_slug },
credentials: { base_uri: import_url, user: bitbucket_user, password: password }
)
- data.save
- project.save
+ data.save!
+ project.save!
end
describe '#import_repository' do
diff --git a/spec/lib/gitlab/buffered_io_spec.rb b/spec/lib/gitlab/buffered_io_spec.rb
new file mode 100644
index 00000000000..f8896abd46e
--- /dev/null
+++ b/spec/lib/gitlab/buffered_io_spec.rb
@@ -0,0 +1,54 @@
+# rubocop:disable Style/FrozenStringLiteralComment
+require 'spec_helper'
+
+RSpec.describe Gitlab::BufferedIo do
+ describe '#readuntil' do
+ let(:never_ending_tcp_socket) do
+ Class.new do
+ def initialize(*_)
+ @read_counter = 0
+ end
+
+ def setsockopt(*_); end
+
+ def closed?
+ false
+ end
+
+ def close
+ true
+ end
+
+ def to_io
+ StringIO.new('Hello World!')
+ end
+
+ def write_nonblock(data, *_)
+ data.size
+ end
+
+ def read_nonblock(buffer_size, *_)
+ sleep 0.01
+ @read_counter += 1
+
+ raise 'Test did not raise HeaderReadTimeout' if @read_counter > 10
+
+ 'H' * buffer_size
+ end
+ end
+ end
+
+ before do
+ stub_const('Gitlab::BufferedIo::HEADER_READ_TIMEOUT', 0.1)
+ end
+
+ subject(:readuntil) do
+ Gitlab::BufferedIo.new(never_ending_tcp_socket.new).readuntil('a')
+ end
+
+ it 'raises a timeout error' do
+ expect { readuntil }.to raise_error(Gitlab::HTTP::HeaderReadTimeout, /Request timed out after reading headers for 0\.[0-9]+ seconds/)
+ end
+ end
+end
+# rubocop:enable Style/FrozenStringLiteralComment
diff --git a/spec/lib/gitlab/changelog/config_spec.rb b/spec/lib/gitlab/changelog/config_spec.rb
index c410ba4d116..600682d30ad 100644
--- a/spec/lib/gitlab/changelog/config_spec.rb
+++ b/spec/lib/gitlab/changelog/config_spec.rb
@@ -31,6 +31,20 @@ RSpec.describe Gitlab::Changelog::Config do
described_class.from_git(project)
end
+
+ context 'when changelog is empty' do
+ it 'returns the default configuration' do
+ allow(project.repository)
+ .to receive(:changelog_config)
+ .and_return("")
+
+ expect(described_class)
+ .to receive(:new)
+ .with(project)
+
+ described_class.from_git(project)
+ end
+ end
end
describe '.from_hash' do
diff --git a/spec/lib/gitlab/changelog/release_spec.rb b/spec/lib/gitlab/changelog/release_spec.rb
index d8434821640..defcec5aa65 100644
--- a/spec/lib/gitlab/changelog/release_spec.rb
+++ b/spec/lib/gitlab/changelog/release_spec.rb
@@ -139,6 +139,16 @@ RSpec.describe Gitlab::Changelog::Release do
OUT
end
end
+
+ context 'when template parser raises an error' do
+ before do
+ allow(config).to receive(:template).and_raise(Gitlab::TemplateParser::Error)
+ end
+
+ it 'raises a Changelog error' do
+ expect { release.to_markdown }.to raise_error(Gitlab::Changelog::Error)
+ end
+ end
end
describe '#header_start_position' do
diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb
index f503759f3f8..c06d26d1441 100644
--- a/spec/lib/gitlab/checks/branch_check_spec.rb
+++ b/spec/lib/gitlab/checks/branch_check_spec.rb
@@ -40,15 +40,6 @@ RSpec.describe Gitlab::Checks::BranchCheck do
expect { subject.validate! }.not_to raise_error
end
end
-
- context "the feature flag is disabled" do
- it "doesn't prohibit a 40-character hexadecimal branch name" do
- stub_feature_flags(prohibit_hexadecimal_branch_names: false)
- allow(subject).to receive(:branch_name).and_return("267208abfe40e546f5e847444276f7d43a39503e")
-
- expect { subject.validate! }.not_to raise_error
- end
- end
end
context 'protected branches check' do
diff --git a/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb b/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb
new file mode 100644
index 00000000000..36f9f4fb321
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/latest_release_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Badge::Release::LatestRelease do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_guest(user)
+ create(:release, project: project, released_at: 1.day.ago)
+ end
+
+ subject { described_class.new(project, user) }
+
+ describe '#entity' do
+ it 'describes latest release' do
+ expect(subject.entity).to eq 'Latest Release'
+ end
+ end
+
+ describe '#tag' do
+ it 'returns latest release tag for the project ordered using release_at' do
+ create(:release, tag: "v1.0.0", project: project, released_at: 1.hour.ago)
+ latest_release = create(:release, tag: "v2.0.0", project: project, released_at: Time.current)
+
+ expect(subject.tag).to eq latest_release.tag
+ end
+ end
+
+ describe '#metadata' do
+ it 'returns correct metadata' do
+ expect(subject.metadata.image_url).to include 'release.svg'
+ end
+ end
+
+ describe '#template' do
+ it 'returns correct template' do
+ expect(subject.template.key_text).to eq 'Latest Release'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/badge/release/metadata_spec.rb b/spec/lib/gitlab/ci/badge/release/metadata_spec.rb
new file mode 100644
index 00000000000..d68358f1458
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/metadata_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'lib/gitlab/ci/badge/shared/metadata'
+
+RSpec.describe Gitlab::Ci::Badge::Release::Metadata do
+ let(:project) { create(:project) }
+ let(:ref) { 'feature' }
+ let!(:release) { create(:release, tag: ref, project: project) }
+ let(:user) { create(:user) }
+ let(:badge) do
+ Gitlab::Ci::Badge::Release::LatestRelease.new(project, user)
+ end
+
+ let(:metadata) { described_class.new(badge) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like 'badge metadata'
+
+ describe '#title' do
+ it 'returns latest release title' do
+ expect(metadata.title).to eq 'Latest Release'
+ end
+ end
+
+ describe '#image_url' do
+ it 'returns valid url' do
+ expect(metadata.image_url).to include "/-/badges/release.svg"
+ end
+ end
+
+ describe '#link_url' do
+ it 'returns valid link' do
+ expect(metadata.link_url).to include "/-/releases"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/badge/release/template_spec.rb b/spec/lib/gitlab/ci/badge/release/template_spec.rb
new file mode 100644
index 00000000000..2b66c296a94
--- /dev/null
+++ b/spec/lib/gitlab/ci/badge/release/template_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Badge::Release::Template do
+ let(:project) { create(:project) }
+ let(:ref) { 'v1.2.3' }
+ let(:user) { create(:user) }
+ let!(:release) { create(:release, tag: ref, project: project) }
+ let(:badge) { Gitlab::Ci::Badge::Release::LatestRelease.new(project, user) }
+ let(:template) { described_class.new(badge) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ describe '#key_text' do
+ it 'defaults to latest release' do
+ expect(template.key_text).to eq 'Latest Release'
+ end
+
+ it 'returns custom key text' do
+ key_text = 'Test Release'
+ badge = Gitlab::Ci::Badge::Release::LatestRelease.new(project, user, opts: { key_text: key_text })
+
+ expect(described_class.new(badge).key_text).to eq key_text
+ end
+ end
+
+ describe '#value_text' do
+ context 'when a release exists' do
+ it 'returns the tag of the release' do
+ expect(template.value_text).to eq ref
+ end
+ end
+
+ context 'no releases exist' do
+ before do
+ allow(badge).to receive(:tag).and_return(nil)
+ end
+
+ it 'returns string that latest release is none' do
+ expect(template.value_text).to eq 'none'
+ end
+ end
+ end
+
+ describe '#key_width' do
+ it 'returns the default key width' do
+ expect(template.key_width).to eq 90
+ end
+
+ it 'returns custom key width' do
+ key_width = 100
+ badge = Gitlab::Ci::Badge::Release::LatestRelease.new(project, user, opts: { key_width: key_width })
+
+ expect(described_class.new(badge).key_width).to eq key_width
+ end
+ end
+
+ describe '#value_width' do
+ it 'has a fixed value width' do
+ expect(template.value_width).to eq 54
+ end
+ end
+
+ describe '#key_color' do
+ it 'always has the same color' do
+ expect(template.key_color).to eq '#555'
+ end
+ end
+
+ describe '#value_color' do
+ context 'when release exists' do
+ it 'is blue' do
+ expect(template.value_color).to eq '#3076af'
+ end
+ end
+
+ context 'when release does not exist' do
+ before do
+ allow(badge).to receive(:tag).and_return(nil)
+ end
+
+ it 'is red' do
+ expect(template.value_color).to eq '#e05d44'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
index 0e26a9fa571..889878cf3ef 100644
--- a/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/expire_in_parser_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
- describe '.validate_duration' do
+ describe '.validate_duration', :request_store do
subject { described_class.validate_duration(value) }
context 'with never' do
@@ -20,14 +20,33 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::ExpireInParser do
context 'with a duration' do
let(:value) { '1 Day' }
+ let(:other_value) { '30 seconds' }
it { is_expected.to be_truthy }
+
+ it 'caches data' do
+ expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+ expect(ChronicDuration).to receive(:parse).with(other_value).once.and_call_original
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to eq(86400)
+ expect(described_class.validate_duration(other_value)).to eq(30)
+ end
+ end
end
context 'without a duration' do
let(:value) { 'something' }
it { is_expected.to be_falsy }
+
+ it 'caches data' do
+ expect(ChronicDuration).to receive(:parse).with(value).once.and_call_original
+
+ 2.times do
+ expect(described_class.validate_duration(value)).to be_falsey
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
index 532c83f6768..4ac8bf61738 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
@@ -4,14 +4,23 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
describe '#satisfied_by?' do
+ subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+
it_behaves_like 'a glob matching rule' do
let(:pipeline) { build(:ci_pipeline) }
+ let(:context) {}
before do
allow(pipeline).to receive(:modified_paths).and_return(files.keys)
end
+ end
- subject { described_class.new(globs).satisfied_by?(pipeline, nil) }
+ context 'when pipeline is nil' do
+ let(:pipeline) {}
+ let(:context) {}
+ let(:globs) { [] }
+
+ it { is_expected.to be_truthy }
end
context 'when using variable expansion' do
@@ -20,8 +29,6 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
let(:globs) { ['$HELM_DIR/**/*'] }
let(:context) { double('context') }
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
-
before do
allow(pipeline).to receive(:modified_paths).and_return(modified_paths)
end
@@ -32,6 +39,12 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
it { is_expected.to be_falsey }
end
+ context 'when modified paths are nil' do
+ let(:modified_paths) {}
+
+ it { is_expected.to be_truthy }
+ end
+
context 'when context has the specified variables' do
let(:variables_hash) do
{ 'HELM_DIR' => 'helm' }
diff --git a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
index 0505b17ea91..e83d4974bb7 100644
--- a/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules::Rule do
let(:factory) do
diff --git a/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb b/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
index c255d6e9dd6..d5988dbbb58 100644
--- a/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include/rules_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Include::Rules do
let(:factory) do
diff --git a/spec/lib/gitlab/ci/config/entry/include_spec.rb b/spec/lib/gitlab/ci/config/entry/include_spec.rb
index 275cdcddeb0..fd7f85c9298 100644
--- a/spec/lib/gitlab/ci/config/entry/include_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/include_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require_dependency 'active_model'
RSpec.describe ::Gitlab::Ci::Config::Entry::Include do
subject(:include_entry) { described_class.new(config) }
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 9a2a67389fc..b03175cd80f 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -70,6 +70,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Jobs do
it 'reports error' do
expect(entry.errors).to include 'jobs rspec config should implement a script: or a trigger: keyword'
end
+
+ context 'when the job name cannot be cast directly to a symbol' do
+ let(:config) { { true => nil } }
+
+ it 'properly parses the job name without raising a NoMethodError' do
+ expect(entry.errors).to include 'jobs true config should implement a script: or a trigger: keyword'
+ end
+ end
end
context 'when no visible jobs present' do
diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
index 46800055dd9..e5de0fb38e3 100644
--- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/helpers/stub_feature_flags'
-require_dependency 'active_model'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
@@ -47,6 +45,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
end
context 'when using unsafe regexp' do
+ # When removed we could use `require 'fast_spec_helper'` again.
include StubFeatureFlags
let(:config) { ['/^(?!master).+/'] }
@@ -89,7 +88,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
describe '#errors' do
it 'saves errors' do
expect(entry.errors)
- .to include /policy config should be an array of strings or regexps/
+ .to include /policy config should be an array of strings or regular expressions/
end
end
end
@@ -107,6 +106,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Policy do
end
context 'when using unsafe regexp' do
+ # When removed we could use `require 'fast_spec_helper'` again.
include StubFeatureFlags
let(:config) { { refs: ['/^(?!master).+/'] } }
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 749d1386ed9..daf58aff116 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -55,13 +55,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
}
end
- context 'when deprecated types keyword is defined' do
+ context 'when deprecated types/type keywords are defined' do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:hash) do
{ types: %w(test deploy),
- rspec: { script: 'rspec' } }
+ rspec: { script: 'rspec', type: 'test' } }
end
before do
@@ -69,11 +69,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
it 'returns array of types as stages with a warning' do
+ expect(root.jobs_value[:rspec][:stage]).to eq 'test'
expect(root.stages_value).to eq %w[test deploy]
- expect(root.warnings).to match_array(["root `types` is deprecated in 9.0 and will be removed in 15.0."])
+ expect(root.warnings).to match_array([
+ "root `types` is deprecated in 9.0 and will be removed in 15.0.",
+ "jobs:rspec `type` is deprecated in 9.0 and will be removed in 15.0."
+ ])
end
- it 'logs usage of types keyword' do
+ it 'logs usage of keywords' do
expect(Gitlab::AppJsonLogger).to(
receive(:info)
.with(event: 'ci_used_deprecated_keyword',
@@ -350,9 +354,9 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
root.compose!
end
- context 'when before script is not an array' do
+ context 'when before script is a number' do
let(:hash) do
- { before_script: 'ls' }
+ { before_script: 123 }
end
describe '#valid?' do
@@ -364,7 +368,7 @@ RSpec.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 containing strings and arrays of strings'
+ .to include 'before_script config should be a string or a nested array of strings up to 10 levels deep'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb
deleted file mode 100644
index 1ddf7881e81..00000000000
--- a/spec/lib/gitlab/ci/config/entry/script_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Ci::Config::Entry::Script do
- let(:entry) { described_class.new(config) }
-
- describe 'validations' do
- context 'when entry config value is array of strings' do
- let(:config) { %w(ls pwd) }
-
- describe '#value' do
- it 'returns array of strings' do
- expect(entry.value).to eq config
- 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 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 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
-
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index cebe8984741..f8754d7e124 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -175,27 +175,35 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
end
end
- context "when duplicate 'include' is defined" do
+ context "when duplicate 'include's are defined" do
+ let(:values) do
+ { include: [
+ { 'local' => local_file },
+ { 'local' => local_file }
+ ],
+ image: 'ruby:2.7' }
+ end
+
+ it 'does not raise an exception' do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context 'when passing max number of files' do
let(:values) do
{ include: [
{ 'local' => local_file },
- { 'local' => local_file }
+ { 'remote' => remote_url }
],
image: 'ruby:2.7' }
end
- it 'raises an exception' do
- expect { subject }.to raise_error(described_class::DuplicateIncludesError)
+ before do
+ stub_const("#{described_class}::MAX_INCLUDES", 2)
end
- context 'when including multiple files from a project' do
- let(:values) do
- { include: { project: project.full_path, file: [local_file, local_file] } }
- end
-
- it 'raises an exception' do
- expect { subject }.to raise_error(described_class::DuplicateIncludesError)
- end
+ it 'does not raise an exception' do
+ expect { subject }.not_to raise_error
end
end
diff --git a/spec/lib/gitlab/ci/config/external/rules_spec.rb b/spec/lib/gitlab/ci/config/external/rules_spec.rb
index 091bd3b07e6..e2bb55f3854 100644
--- a/spec/lib/gitlab/ci/config/external/rules_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/rules_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::Ci::Config::External::Rules do
let(:context) { double(project: project, sha: project.repository.tree.sha, top_level_worktree_paths: ['Dockerfile']) }
before do
- project.repository.create_file(project.owner, 'Dockerfile', "commit", message: 'test', branch_name: "master")
+ project.repository.create_file(project.first_owner, 'Dockerfile', "commit", message: 'test', branch_name: "master")
end
it { is_expected.to eq(true) }
diff --git a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
index a29471706cc..1cc8b462224 100644
--- a/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'support/helpers/stubbed_feature'
-require 'support/helpers/stub_feature_flags'
RSpec.describe Gitlab::Ci::Config::Normalizer::MatrixStrategy do
- include StubFeatureFlags
-
describe '.applies_to?' do
subject { described_class.applies_to?(config) }
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 1b3e8a2ce4a..05ff1f3618b 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -462,7 +462,7 @@ RSpec.describe Gitlab::Ci::Config do
expect(project.repository).to receive(:blob_data_at)
.with('eeff1122', local_location)
- described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122', user: user)
+ described_class.new(gitlab_ci_yml, project: project, sha: 'eeff1122', user: user, pipeline: pipeline)
end
end
@@ -470,7 +470,7 @@ RSpec.describe Gitlab::Ci::Config do
it 'is using latest SHA on the default branch' do
expect(project.repository).to receive(:root_ref_sha)
- described_class.new(gitlab_ci_yml, project: project, sha: nil, user: user)
+ described_class.new(gitlab_ci_yml, project: project, sha: nil, user: user, pipeline: pipeline)
end
end
end
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 1e433d7854a..747ff13c840 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -7,9 +7,10 @@ RSpec.describe Gitlab::Ci::Lint do
let_it_be(:user) { create(:user) }
let(:lint) { described_class.new(project: project, current_user: user) }
+ let(:ref) { project.default_branch }
describe '#validate' do
- subject { lint.validate(content, dry_run: dry_run) }
+ subject { lint.validate(content, dry_run: dry_run, ref: ref) }
shared_examples 'content is valid' do
let(:content) do
@@ -251,6 +252,29 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
+ context 'when using a ref other than the default branch' do
+ let(:ref) { 'feature' }
+ let(:content) do
+ <<~YAML
+ build:
+ stage: build
+ script: echo 1
+ rules:
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
+ test:
+ stage: test
+ script: echo 2
+ rules:
+ - if: "$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH"
+ YAML
+ end
+
+ it 'includes only jobs that are excluded on the default branch' do
+ expect(subject.jobs.size).to eq(1)
+ expect(subject.jobs[0][:name]).to eq('test')
+ end
+ end
+
it_behaves_like 'sets merged yaml'
include_context 'advanced validations' do
@@ -298,4 +322,102 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
end
+
+ context 'pipeline logger' do
+ let(:counters) do
+ {
+ 'count' => a_kind_of(Numeric),
+ 'avg' => a_kind_of(Numeric),
+ 'max' => a_kind_of(Numeric),
+ 'min' => a_kind_of(Numeric)
+ }
+ end
+
+ let(:loggable_data) do
+ {
+ 'class' => 'Gitlab::Ci::Pipeline::Logger',
+ 'config_build_context_duration_s' => counters,
+ 'config_build_variables_duration_s' => counters,
+ 'config_compose_duration_s' => counters,
+ 'config_expand_duration_s' => counters,
+ 'config_external_process_duration_s' => counters,
+ 'config_stages_inject_duration_s' => counters,
+ 'config_tags_resolve_duration_s' => counters,
+ 'config_yaml_extend_duration_s' => counters,
+ 'config_yaml_load_duration_s' => counters,
+ 'pipeline_creation_caller' => 'Gitlab::Ci::Lint',
+ 'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
+ 'pipeline_persisted' => false,
+ 'pipeline_source' => 'unknown',
+ 'project_id' => project&.id,
+ 'yaml_process_duration_s' => counters
+ }
+ end
+
+ let(:content) do
+ <<~YAML
+ build:
+ script: echo
+ YAML
+ end
+
+ subject(:validate) { lint.validate(content, dry_run: false) }
+
+ before do
+ project&.add_developer(user)
+ end
+
+ context 'when the duration is under the threshold' do
+ it 'does not create a log entry' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ validate
+ end
+ end
+
+ context 'when the durations exceeds the threshold' do
+ let(:timer) do
+ proc do
+ @timer = @timer.to_i + 30
+ end
+ end
+
+ before do
+ allow(Gitlab::Ci::Pipeline::Logger)
+ .to receive(:current_monotonic_time) { timer.call }
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(loggable_data)
+
+ validate
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_pipeline_creation_logger: false)
+ end
+
+ it 'does not create a log entry' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:info)
+
+ validate
+ end
+ end
+
+ context 'when project is not provided' do
+ let(:project) { nil }
+
+ let(:project_nil_loggable_data) do
+ loggable_data.except('project_id')
+ end
+
+ it 'creates a log entry without project_id' do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with(project_nil_loggable_data)
+
+ validate
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index c49673f5a4a..7eec78ff186 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -40,60 +40,142 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
allow(validator_class).to receive(:new).and_call_original
end
- context 'when the validate flag is set as `false`' do
- let(:validate) { false }
+ context 'when enforce_security_report_validation is enabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
- it 'does not run the validation logic' do
- parse_report
+ context 'when the validate flag is set as `true`' do
+ let(:validate) { true }
- expect(validator_class).not_to have_received(:new)
- end
- end
+ it 'instantiates the validator with correct params' do
+ parse_report
- context 'when the validate flag is set as `true`' do
- let(:validate) { true }
- let(:valid?) { false }
+ expect(validator_class).to have_received(:new).with(report.type, {})
+ end
- before do
- allow_next_instance_of(validator_class) do |instance|
- allow(instance).to receive(:valid?).and_return(valid?)
- allow(instance).to receive(:errors).and_return(['foo'])
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return([])
+ end
+
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ end
+
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }.from([])
+ end
+
+ it 'adds the schema validation status to the report' do
+ parse_report
+
+ expect(report.schema_validation_status).to eq(:valid_schema)
+ end
+
+ it 'keeps the execution flow as normal' do
+ parse_report
+
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
+ end
end
- allow(parser).to receive_messages(create_scanner: true, create_scan: true)
- end
+ context 'when the report data is not valid according to the schema' do
+ let(:valid?) { false }
- it 'instantiates the validator with correct params' do
- parse_report
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(['foo'])
+ end
- expect(validator_class).to have_received(:new).with(report.type, {})
- end
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
+ end
+
+ it 'adds errors to the report' do
+ expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ end
+
+ it 'adds the schema validation status to the report' do
+ parse_report
- context 'when the report data is not valid according to the schema' do
- it 'adds errors to the report' do
- expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ expect(report.schema_validation_status).to eq(:invalid_schema)
+ end
+
+ it 'does not try to create report entities' do
+ parse_report
+
+ expect(parser).not_to have_received(:create_scanner)
+ expect(parser).not_to have_received(:create_scan)
+ end
end
+ end
+ end
+
+ context 'when enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ context 'when the validate flag is set as `false`' do
+ let(:validate) { false }
- it 'does not try to create report entities' do
+ it 'does not run the validation logic' do
parse_report
- expect(parser).not_to have_received(:create_scanner)
- expect(parser).not_to have_received(:create_scan)
+ expect(validator_class).not_to have_received(:new)
end
end
- context 'when the report data is valid according to the schema' do
- let(:valid?) { true }
+ context 'when the validate flag is set as `true`' do
+ let(:validate) { true }
+ let(:valid?) { false }
- it 'does not add errors to the report' do
- expect { parse_report }.not_to change { report.errors }.from([])
+ before do
+ allow_next_instance_of(validator_class) do |instance|
+ allow(instance).to receive(:valid?).and_return(valid?)
+ allow(instance).to receive(:errors).and_return(['foo'])
+ end
+
+ allow(parser).to receive_messages(create_scanner: true, create_scan: true)
end
- it 'keeps the execution flow as normal' do
+ it 'instantiates the validator with correct params' do
parse_report
- expect(parser).to have_received(:create_scanner)
- expect(parser).to have_received(:create_scan)
+ expect(validator_class).to have_received(:new).with(report.type, {})
+ end
+
+ context 'when the report data is not valid according to the schema' do
+ it 'adds errors to the report' do
+ expect { parse_report }.to change { report.errors }.from([]).to([{ message: 'foo', type: 'Schema' }])
+ end
+
+ it 'does not try to create report entities' do
+ parse_report
+
+ expect(parser).not_to have_received(:create_scanner)
+ expect(parser).not_to have_received(:create_scan)
+ end
+ end
+
+ context 'when the report data is valid according to the schema' do
+ let(:valid?) { true }
+
+ it 'does not add errors to the report' do
+ expect { parse_report }.not_to change { report.errors }.from([])
+ end
+
+ it 'keeps the execution flow as normal' do
+ parse_report
+
+ expect(parser).to have_received(:create_scanner)
+ expect(parser).to have_received(:create_scan)
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index 4ca8f74e57f..82fa11d5f98 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -99,6 +99,19 @@ RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
'Some failure'
end
+ context 'and has failure with no message but has system-err' do
+ let(:testcase_content) do
+ <<-EOF.strip_heredoc
+ <failure></failure>
+ <system-err>Some failure</system-err>
+ EOF
+ end
+
+ it_behaves_like '<testcase> XML parser',
+ ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED,
+ 'Some failure'
+ end
+
context 'and has error' do
let(:testcase_content) { '<error>Some error</error>' }
@@ -107,6 +120,19 @@ RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
'Some error'
end
+ context 'and has error with no message but has system-err' do
+ let(:testcase_content) do
+ <<-EOF.strip_heredoc
+ <error></error>
+ <system-err>Some error</system-err>
+ EOF
+ end
+
+ it_behaves_like '<testcase> XML parser',
+ ::Gitlab::Ci::Reports::TestCase::STATUS_ERROR,
+ 'Some error'
+ end
+
context 'and has skipped' do
let(:testcase_content) { '<skipped/>' }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
index 0a592395c3a..375841ce236 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
@@ -47,18 +47,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CreateDeployments do
expect(job.deployment).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create a deployment record' do
- expect { subject }.not_to change { Deployment.count }
-
- expect(job.deployment).to be_nil
- end
- end
end
context 'when a pipeline contains a teardown job' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
index 253928e1a19..6a7d9b58a05 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -57,18 +57,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
expect(job.persisted_environment).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create any environments' do
- expect { subject }.not_to change { Environment.count }
-
- expect(job.persisted_environment).to be_nil
- end
- end
end
context 'when a pipeline contains a teardown job' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
index 87df5a3e21b..571455d6279 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
@@ -60,18 +60,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureResourceGroups do
expect(job.resource_group).to be_nil
end
end
-
- context 'when create_deployment_in_separate_transaction feature flag is disabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- it 'does not create any resource groups' do
- expect { subject }.not_to change { Ci::ResourceGroup.count }
-
- expect(job.resource_group).to be_nil
- end
- end
end
context 'when a pipeline does not contain a job that requires a resource group' do
diff --git a/spec/lib/gitlab/ci/pipeline/logger_spec.rb b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
index a488bc184f8..f31361431f2 100644
--- a/spec/lib/gitlab/ci/pipeline/logger_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/logger_spec.rb
@@ -47,13 +47,15 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end
def loggable_data(count:, db_count: nil)
- keys = %w[
+ database_name = Ci::ApplicationRecord.connection.pool.db_config.name
+
+ keys = %W[
expensive_operation_duration_s
expensive_operation_db_count
expensive_operation_db_primary_count
expensive_operation_db_primary_duration_s
- expensive_operation_db_main_count
- expensive_operation_db_main_duration_s
+ expensive_operation_db_#{database_name}_count
+ expensive_operation_db_#{database_name}_duration_s
]
data = keys.each.with_object({}) do |key, accumulator|
@@ -75,7 +77,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
end
context 'with a single query' do
- let(:operation) { -> { Project.count } }
+ let(:operation) { -> { Ci::Pipeline.count } }
it { is_expected.to eq(operation.call) }
@@ -201,6 +203,35 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Logger do
expect(commit).to be_truthy
end
end
+
+ context 'when project is not passed and pipeline is not persisted' do
+ let(:project) {}
+ let(:pipeline) { build(:ci_pipeline) }
+
+ let(:loggable_data) do
+ {
+ 'class' => described_class.name.to_s,
+ 'pipeline_persisted' => false,
+ 'pipeline_creation_service_duration_s' => a_kind_of(Numeric),
+ 'pipeline_creation_caller' => 'source',
+ 'pipeline_save_duration_s' => {
+ 'avg' => 60, 'count' => 1, 'max' => 60, 'min' => 60
+ },
+ 'pipeline_creation_duration_s' => {
+ 'avg' => 20, 'count' => 2, 'max' => 30, 'min' => 10
+ }
+ }
+ end
+
+ it 'logs to application.json' do
+ expect(Gitlab::AppJsonLogger)
+ .to receive(:info)
+ .with(a_hash_including(loggable_data))
+ .and_call_original
+
+ expect(commit).to be_truthy
+ end
+ end
end
context 'when the feature flag is disabled' do
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 2f9fcd7caac..49505d397c2 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -411,171 +411,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
describe '#to_resource' do
subject { seed_build.to_resource }
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: false)
- end
-
- context 'when job is Ci::Build' do
- it { is_expected.to be_a(::Ci::Build) }
- it { is_expected.to be_valid }
-
- shared_examples_for 'deployment job' do
- it 'returns a job with deployment' do
- expect { subject }.to change { Environment.count }.by(1)
-
- expect(subject.deployment).not_to be_nil
- expect(subject.deployment.deployable).to eq(subject)
- expect(subject.deployment.environment.name).to eq(expected_environment_name)
- end
- end
-
- shared_examples_for 'non-deployment job' do
- it 'returns a job without deployment' do
- expect(subject.deployment).to be_nil
- end
- end
-
- shared_examples_for 'ensures environment existence' do
- it 'has environment' do
- expect { subject }.to change { Environment.count }.by(1)
-
- expect(subject).to be_has_environment
- expect(subject.environment).to eq(environment_name)
- expect(subject.metadata.expanded_environment_name).to eq(expected_environment_name)
- expect(Environment.exists?(name: expected_environment_name)).to eq(true)
- end
- end
-
- shared_examples_for 'ensures environment inexistence' do
- it 'does not have environment' do
- expect { subject }.not_to change { Environment.count }
-
- expect(subject).not_to be_has_environment
- expect(subject.environment).to be_nil
- expect(subject.metadata&.expanded_environment_name).to be_nil
- expect(Environment.exists?(name: expected_environment_name)).to eq(false)
- end
- end
-
- context 'when job deploys to production' do
- let(:environment_name) { 'production' }
- let(:expected_environment_name) { 'production' }
- let(:attributes) { { name: 'deploy', ref: 'master', environment: 'production' } }
-
- it_behaves_like 'deployment job'
- it_behaves_like 'ensures environment existence'
-
- context 'when create_deployment_in_separate_transaction feature flag is enabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: true)
- end
-
- it 'does not create any deployments nor environments' do
- expect(subject.deployment).to be_nil
- expect(Environment.count).to eq(0)
- expect(Deployment.count).to eq(0)
- end
- end
-
- context 'when the environment name is invalid' do
- let(:attributes) { { name: 'deploy', ref: 'master', environment: '!!!' } }
-
- it 'fails the job with a failure reason and does not create an environment' do
- expect(subject).to be_failed
- expect(subject).to be_environment_creation_failure
- expect(subject.metadata.expanded_environment_name).to be_nil
- expect(Environment.exists?(name: expected_environment_name)).to eq(false)
- end
- end
- end
-
- context 'when job starts a review app' do
- let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
- let(:expected_environment_name) { "review/#{pipeline.ref}" }
-
- let(:attributes) do
- {
- name: 'deploy', ref: 'master', environment: environment_name,
- options: { environment: { name: environment_name } }
- }
- end
-
- it_behaves_like 'deployment job'
- it_behaves_like 'ensures environment existence'
- end
-
- context 'when job stops a review app' do
- let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
- let(:expected_environment_name) { "review/#{pipeline.ref}" }
-
- let(:attributes) do
- {
- name: 'deploy', ref: 'master', environment: environment_name,
- options: { environment: { name: environment_name, action: 'stop' } }
- }
- end
-
- it 'returns a job without deployment' do
- expect(subject.deployment).to be_nil
- end
-
- it_behaves_like 'non-deployment job'
- it_behaves_like 'ensures environment existence'
- end
-
- context 'when job belongs to a resource group' do
- let(:resource_group) { 'iOS' }
- let(:attributes) { { name: 'rspec', ref: 'master', resource_group_key: resource_group, environment: 'production' }}
-
- it 'returns a job with resource group' do
- expect(subject.resource_group).not_to be_nil
- expect(subject.resource_group.key).to eq('iOS')
- expect(Ci::ResourceGroup.count).to eq(1)
- end
-
- context 'when create_deployment_in_separate_transaction feature flag is enabled' do
- before do
- stub_feature_flags(create_deployment_in_separate_transaction: true)
- end
-
- it 'does not create any resource groups' do
- expect(subject.resource_group).to be_nil
- expect(Ci::ResourceGroup.count).to eq(0)
- end
- end
-
- context 'when resource group has $CI_ENVIRONMENT_NAME in it' do
- let(:resource_group) { 'test/$CI_ENVIRONMENT_NAME' }
-
- it 'expands environment name' do
- expect(subject.resource_group.key).to eq('test/production')
- end
- end
- end
- end
-
- context 'when job is a bridge' do
- let(:base_attributes) do
- {
- name: 'rspec', ref: 'master', options: { trigger: 'my/project' }, scheduling_type: :stage
- }
- end
-
- let(:attributes) { base_attributes }
-
- it { is_expected.to be_a(::Ci::Bridge) }
- it { is_expected.to be_valid }
-
- context 'when job belongs to a resource group' do
- let(:attributes) { base_attributes.merge(resource_group_key: 'iOS') }
-
- it 'returns a job with resource group' do
- expect(subject.resource_group).not_to be_nil
- expect(subject.resource_group.key).to eq('iOS')
- end
- end
- end
-
it 'memoizes a resource object' do
expect(subject.object_id).to eq seed_build.to_resource.object_id
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
index 3b0eaffc54e..f4b47893805 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
@@ -85,6 +85,9 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
let(:info) { build(:codequality_degradation, :info) }
let(:major_2) { build(:codequality_degradation, :major) }
let(:critical) { build(:codequality_degradation, :critical) }
+ let(:uppercase_major) { build(:codequality_degradation, severity: 'MAJOR') }
+ let(:unknown) { build(:codequality_degradation, severity: 'unknown') }
+
let(:codequality_report) { described_class.new }
before do
@@ -94,6 +97,7 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
codequality_report.add_degradation(major_2)
codequality_report.add_degradation(info)
codequality_report.add_degradation(critical)
+ codequality_report.add_degradation(unknown)
codequality_report.sort_degradations!
end
@@ -105,8 +109,30 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
major,
major_2,
minor,
- info
+ info,
+ unknown
])
end
+
+ context 'with non-existence and uppercase severities' do
+ let(:other_report) { described_class.new }
+ let(:non_existent) { build(:codequality_degradation, severity: 'non-existent') }
+
+ before do
+ other_report.add_degradation(blocker)
+ other_report.add_degradation(uppercase_major)
+ other_report.add_degradation(minor)
+ other_report.add_degradation(non_existent)
+ end
+
+ it 'sorts unknown last' do
+ expect(other_report.degradations.values).to eq([
+ blocker,
+ uppercase_major,
+ minor,
+ non_existent
+ ])
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb b/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
index 784c1183320..2320f011cf5 100644
--- a/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/finding_key_spec.rb
@@ -6,36 +6,47 @@ RSpec.describe Gitlab::Ci::Reports::Security::FindingKey do
using RSpec::Parameterized::TableSyntax
describe '#==' do
- where(:location_fp_1, :location_fp_2, :identifier_fp_1, :identifier_fp_2, :equals?) do
- nil | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | nil | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'different location fp' | nil | 'different identifier fp' | false
- 'location fp' | 'different location fp' | 'identifier fp' | nil | false
- nil | nil | 'identifier fp' | 'identifier fp' | false
- 'location fp' | 'location fp' | nil | nil | false
- nil | nil | nil | nil | false
- 'location fp' | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'different location fp' | 'identifier fp' | 'identifier fp' | false
- 'location fp' | 'location fp' | 'identifier fp' | 'different identifier fp' | false
- 'location fp' | 'location fp' | 'identifier fp' | 'identifier fp' | true
- end
-
- with_them do
- let(:finding_key_1) do
- build(:ci_reports_security_finding_key,
- location_fingerprint: location_fp_1,
- identifier_fingerprint: identifier_fp_1)
+ context 'when the comparison is done between FindingKey instances' do
+ where(:location_fp_1, :location_fp_2, :identifier_fp_1, :identifier_fp_2, :equals?) do
+ nil | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | nil | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | nil | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | 'identifier fp' | nil | false
+ nil | nil | 'identifier fp' | 'identifier fp' | false
+ 'location fp' | 'location fp' | nil | nil | false
+ nil | nil | nil | nil | false
+ 'location fp' | 'different location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'different location fp' | 'identifier fp' | 'identifier fp' | false
+ 'location fp' | 'location fp' | 'identifier fp' | 'different identifier fp' | false
+ 'location fp' | 'location fp' | 'identifier fp' | 'identifier fp' | true
end
- let(:finding_key_2) do
- build(:ci_reports_security_finding_key,
- location_fingerprint: location_fp_2,
- identifier_fingerprint: identifier_fp_2)
+ with_them do
+ let(:finding_key_1) do
+ build(:ci_reports_security_finding_key,
+ location_fingerprint: location_fp_1,
+ identifier_fingerprint: identifier_fp_1)
+ end
+
+ let(:finding_key_2) do
+ build(:ci_reports_security_finding_key,
+ location_fingerprint: location_fp_2,
+ identifier_fingerprint: identifier_fp_2)
+ end
+
+ subject { finding_key_1 == finding_key_2 }
+
+ it { is_expected.to be(equals?) }
end
+ end
+
+ context 'when the comparison is not done between FindingKey instances' do
+ let(:finding_key) { build(:ci_reports_security_finding_key) }
+ let(:uuid) { SecureRandom.uuid }
- subject { finding_key_1 == finding_key_2 }
+ subject { finding_key == uuid }
- it { is_expected.to be(equals?) }
+ it { is_expected.to be_falsey }
end
end
end
diff --git a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
index f8df2266689..8204b104832 100644
--- a/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe '5-Minute-Production-App.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
diff --git a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
index ca6f6872f89..27de8324206 100644
--- a/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Deploy-ECS.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
index bd701aec8fc..21052f03cb8 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/build_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Build.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
index 64243f2d205..d88d9782021 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/code_quality_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Code-Quality.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
index 789f694b4b4..b657f73fa77 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project, refind: true) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
@@ -66,6 +66,11 @@ RSpec.describe 'Jobs/Deploy.gitlab-ci.yml' do
expect(build_names).not_to include('review')
end
+ it 'when CI_DEPLOY_FREEZE is present' do
+ create(:ci_variable, project: project, key: 'CI_DEPLOY_FREEZE', value: 'true')
+ expect(build_names).to eq %w(placeholder)
+ end
+
it 'when CANARY_ENABLED' do
create(:ci_variable, project: project, key: 'CANARY_ENABLED', value: 'true')
diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
index b9256ece78b..0f97bc06a4e 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'main' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
index db9d7496251..a92a8397e96 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs/Test.gitlab-ci.yml' do
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
index 4685d843ce0..5e9224cebd9 100644
--- a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Terraform/Base.gitlab-ci.yml' do
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
index e35f2eabe8e..0ab81f97f20 100644
--- a/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Terraform/base_latest_gitlab_ci_yaml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Terraform/Base.latest.gitlab-ci.yml' do
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
index 004261bc617..d6c7cd32f79 100644
--- a/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'Verify/Load-Performance-Testing.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_ref) { default_branch }
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
index 64ef6ecd7f8..6a4be1fa072 100644
--- a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
@@ -276,7 +276,7 @@ RSpec.describe 'Auto-DevOps.gitlab-ci.yml' do
with_them do
let(:project) { create(:project, :custom_repo, files: files) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: default_branch ) }
let(:pipeline) { service.execute(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
index 3d97b47473d..de94eec09fe 100644
--- a/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/flutter_gitlab_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Flutter.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { 'master' }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
index c7dbbea4622..ebf52e6d65a 100644
--- a/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Kaniko.gitlab-ci.yml' do
describe 'the created pipeline' do
let(:pipeline_branch) { 'master' }
let(:project) { create(:project, :custom_repo, files: { 'Dockerfile' => 'FROM alpine:latest' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/npm_spec.rb b/spec/lib/gitlab/ci/templates/npm_spec.rb
index ea954690133..d86a3a67823 100644
--- a/spec/lib/gitlab/ci/templates/npm_spec.rb
+++ b/spec/lib/gitlab/ci/templates/npm_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'npm.gitlab-ci.yml' do
let(:repo_files) { { 'package.json' => '{}', 'README.md' => '' } }
let(:modified_files) { %w[package.json] }
let(:project) { create(:project, :custom_repo, files: repo_files) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:pipeline_branch) { project.default_branch }
let(:pipeline_tag) { 'v1.2.1' }
let(:pipeline_ref) { pipeline_branch }
diff --git a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
index 936cd6ac8aa..346ab9f7af7 100644
--- a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
index fd5d5d6af7f..6c06403adff 100644
--- a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do
let(:default_branch) { project.default_branch_or_main }
let(:pipeline_branch) { default_branch }
let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
diff --git a/spec/lib/gitlab/ci/variables/builder/instance_spec.rb b/spec/lib/gitlab/ci/variables/builder/instance_spec.rb
new file mode 100644
index 00000000000..7abda2bd615
--- /dev/null
+++ b/spec/lib/gitlab/ci/variables/builder/instance_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Variables::Builder::Instance do
+ let_it_be(:variable) { create(:ci_instance_variable, protected: false) }
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+
+ let(:builder) { described_class.new }
+
+ describe '#secret_variables' do
+ let(:variable_item) { item(variable) }
+ let(:protected_variable_item) { item(protected_variable) }
+
+ subject do
+ builder.secret_variables(protected_ref: protected_ref)
+ end
+
+ context 'when the ref is protected' do
+ let(:protected_ref) { true }
+
+ it 'contains all the variables' do
+ is_expected.to contain_exactly(variable_item, protected_variable_item)
+ end
+ end
+
+ context 'when the ref is not protected' do
+ let(:protected_ref) { false }
+
+ it 'contains only unprotected variables' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+ end
+
+ def item(variable)
+ Gitlab::Ci::Variables::Collection::Item.fabricate(variable)
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/builder/project_spec.rb b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
new file mode 100644
index 00000000000..b64b6ea98e2
--- /dev/null
+++ b/spec/lib/gitlab/ci/variables/builder/project_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Variables::Builder::Project do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:builder) { described_class.new(project) }
+
+ describe '#secret_variables' do
+ let(:environment) { '*' }
+ let(:protected_ref) { false }
+
+ let_it_be(:variable) do
+ create(:ci_variable,
+ value: 'secret',
+ project: project)
+ end
+
+ let_it_be(:protected_variable) do
+ create(:ci_variable, :protected,
+ value: 'protected',
+ project: project)
+ end
+
+ let(:variable_item) { item(variable) }
+ let(:protected_variable_item) { item(protected_variable) }
+
+ subject do
+ builder.secret_variables(
+ environment: environment,
+ protected_ref: protected_ref)
+ end
+
+ context 'when the ref is protected' do
+ let(:protected_ref) { true }
+
+ it 'contains all the variables' do
+ is_expected.to contain_exactly(variable_item, protected_variable_item)
+ end
+ end
+
+ context 'when the ref is not protected' do
+ let(:protected_ref) { false }
+
+ it 'contains only the unprotected variables' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name is specified' do
+ let(:environment) { 'review/name' }
+
+ before do
+ Ci::Variable.update_all(environment_scope: environment_scope)
+ end
+
+ context 'when environment scope is exactly matched' do
+ let(:environment_scope) { 'review/name' }
+
+ it { is_expected.to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ let(:environment_scope) { 'review/*' }
+
+ it { is_expected.to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope does not match' do
+ let(:environment_scope) { 'review/*/special' }
+
+ it { is_expected.not_to contain_exactly(variable_item) }
+ end
+
+ context 'when environment scope has _' do
+ let(:environment_scope) { '*_*' }
+
+ it 'does not treat it as wildcard' do
+ is_expected.not_to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name contains underscore' do
+ let(:environment) { 'foo_bar/test' }
+ let(:environment_scope) { 'foo_bar/*' }
+
+ it 'matches literally for _' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+
+ # The environment name and scope cannot have % at the moment,
+ # but we're considering relaxing it and we should also make sure
+ # it doesn't break in case some data sneaked in somehow as we're
+ # not checking this integrity in database level.
+ context 'when environment scope has %' do
+ let(:environment_scope) { '*%*' }
+
+ it 'does not treat it as wildcard' do
+ is_expected.not_to contain_exactly(variable_item)
+ end
+ end
+
+ context 'when environment name contains a percent' do
+ let(:environment) { 'foo%bar/test' }
+ let(:environment_scope) { 'foo%bar/*' }
+
+ it 'matches literally for _' do
+ is_expected.to contain_exactly(variable_item)
+ end
+ end
+ end
+
+ context 'when variables with the same name have different environment scopes' do
+ let(:environment) { 'review/name' }
+
+ let_it_be(:partially_matched_variable) do
+ create(:ci_variable,
+ key: variable.key,
+ value: 'partial',
+ environment_scope: 'review/*',
+ project: project)
+ end
+
+ let_it_be(:perfectly_matched_variable) do
+ create(:ci_variable,
+ key: variable.key,
+ value: 'prefect',
+ environment_scope: 'review/name',
+ project: project)
+ end
+
+ it 'puts variables matching environment scope more in the end' do
+ variables_collection = Gitlab::Ci::Variables::Collection.new([
+ variable,
+ partially_matched_variable,
+ perfectly_matched_variable
+ ]).to_runner_variables
+
+ expect(subject.to_runner_variables).to eq(variables_collection)
+ end
+ end
+ end
+
+ def item(variable)
+ Gitlab::Ci::Variables::Collection::Item.fabricate(variable)
+ end
+end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 8a87cbe45c1..6e144d62ac0 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Variables::Builder do
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:user) { project.owner }
- let_it_be(:job) do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, namespace: group) }
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:job) do
create(:ci_build,
pipeline: pipeline,
user: user,
@@ -153,7 +154,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
before do
allow(builder).to receive(:predefined_variables) { [var('A', 1), var('B', 1)] }
- allow(project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
+ allow(pipeline.project).to receive(:predefined_variables) { [var('B', 2), var('C', 2)] }
allow(pipeline).to receive(:predefined_variables) { [var('C', 3), var('D', 3)] }
allow(job).to receive(:runner) { double(predefined_variables: [var('D', 4), var('E', 4)]) }
allow(builder).to receive(:kubernetes_variables) { [var('E', 5), var('F', 5)] }
@@ -201,4 +202,240 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
end
end
end
+
+ describe '#user_variables' do
+ context 'with user' do
+ subject { builder.user_variables(user).to_hash }
+
+ let(:expected_variables) do
+ {
+ 'GITLAB_USER_EMAIL' => user.email,
+ 'GITLAB_USER_ID' => user.id.to_s,
+ 'GITLAB_USER_LOGIN' => user.username,
+ 'GITLAB_USER_NAME' => user.name
+ }
+ end
+
+ it { is_expected.to eq(expected_variables) }
+ end
+
+ context 'without user' do
+ subject { builder.user_variables(nil).to_hash }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ describe '#kubernetes_variables' do
+ let(:service) { double(execute: template) }
+ let(:template) { double(to_yaml: 'example-kubeconfig', valid?: template_valid) }
+ let(:template_valid) { true }
+
+ subject { builder.kubernetes_variables(job) }
+
+ before do
+ allow(Ci::GenerateKubeconfigService).to receive(:new).with(job).and_return(service)
+ end
+
+ it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
+
+ context 'generated config is invalid' do
+ let(:template_valid) { false }
+
+ it { is_expected.not_to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
+ end
+ end
+
+ describe '#deployment_variables' do
+ let(:environment) { 'production' }
+ let(:kubernetes_namespace) { 'namespace' }
+ let(:project_variables) { double }
+
+ subject { builder.deployment_variables(environment: environment, job: job) }
+
+ before do
+ allow(job).to receive(:expanded_kubernetes_namespace)
+ .and_return(kubernetes_namespace)
+
+ allow(project).to receive(:deployment_variables)
+ .with(environment: environment, kubernetes_namespace: kubernetes_namespace)
+ .and_return(project_variables)
+ end
+
+ context 'environment is nil' do
+ let(:environment) { nil }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples "secret CI variables" do
+ context 'when ref is branch' do
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: job.ref, project: project)
+ end
+
+ it { is_expected.to contain_exactly(protected_variable_item, unprotected_variable_item) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+
+ context 'when ref is tag' do
+ before do
+ job.update!(ref: 'v1.1.0', tag: true)
+ pipeline.update!(ref: 'v1.1.0', tag: true)
+ end
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_tag, project: project, name: 'v*')
+ end
+
+ it { is_expected.to contain_exactly(protected_variable_item, unprotected_variable_item) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+
+ context 'when ref is merge request' do
+ let_it_be(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline, source_project: project) }
+ let_it_be(:pipeline) { merge_request.pipelines_for_merge_request.first }
+ let_it_be(:job) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project)
+ end
+
+ it 'does not return protected variables as it is not supported for merge request pipelines' do
+ is_expected.to contain_exactly(unprotected_variable_item)
+ end
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.to contain_exactly(unprotected_variable_item) }
+ end
+ end
+ end
+
+ describe '#secret_instance_variables' do
+ subject { builder.secret_instance_variables }
+
+ let_it_be(:protected_variable) { create(:ci_instance_variable, protected: true) }
+ let_it_be(:unprotected_variable) { create(:ci_instance_variable, protected: false) }
+
+ let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
+ let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_group_variables' do
+ subject { builder.secret_group_variables(ref: job.git_ref, environment: job.expanded_environment_name) }
+
+ let_it_be(:protected_variable) { create(:ci_group_variable, protected: true, group: group) }
+ let_it_be(:unprotected_variable) { create(:ci_group_variable, protected: false, group: group) }
+
+ let(:protected_variable_item) { protected_variable }
+ let(:unprotected_variable_item) { unprotected_variable }
+
+ include_examples "secret CI variables"
+ end
+
+ describe '#secret_project_variables' do
+ let_it_be(:protected_variable) { create(:ci_variable, protected: true, project: project) }
+ let_it_be(:unprotected_variable) { create(:ci_variable, protected: false, project: project) }
+
+ let(:ref) { job.git_ref }
+ let(:environment) { job.expanded_environment_name }
+
+ subject { builder.secret_project_variables(ref: ref, environment: environment) }
+
+ context 'with ci_variables_builder_memoize_secret_variables disabled' do
+ before do
+ stub_feature_flags(ci_variables_builder_memoize_secret_variables: false)
+ end
+
+ let(:protected_variable_item) { protected_variable }
+ let(:unprotected_variable_item) { unprotected_variable }
+
+ include_examples "secret CI variables"
+ end
+
+ context 'with ci_variables_builder_memoize_secret_variables enabled' do
+ before do
+ stub_feature_flags(ci_variables_builder_memoize_secret_variables: true)
+ end
+
+ let(:protected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(protected_variable) }
+ let(:unprotected_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(unprotected_variable) }
+
+ include_examples "secret CI variables"
+
+ context 'variables memoization' do
+ let_it_be(:scoped_variable) { create(:ci_variable, project: project, environment_scope: 'scoped') }
+
+ let(:scoped_variable_item) { Gitlab::Ci::Variables::Collection::Item.fabricate(scoped_variable) }
+
+ context 'with protected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(true)
+
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'production', protected_ref: true)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(ref: ref, environment: 'production'))
+ .to contain_exactly(unprotected_variable_item, protected_variable_item)
+ end
+ end
+ end
+
+ context 'with unprotected environments' do
+ it 'memoizes the result by environment' do
+ expect(pipeline.project)
+ .to receive(:protected_for?)
+ .with(pipeline.jobs_git_ref)
+ .once.and_return(false)
+
+ expect_next_instance_of(described_class::Project) do |project_variables_builder|
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: nil, protected_ref: false)
+ .once
+ .and_call_original
+
+ expect(project_variables_builder)
+ .to receive(:secret_variables)
+ .with(environment: 'scoped', protected_ref: false)
+ .once
+ .and_call_original
+ end
+
+ 2.times do
+ expect(builder.secret_project_variables(ref: 'other', environment: nil))
+ .to contain_exactly(unprotected_variable_item)
+
+ expect(builder.secret_project_variables(ref: 'other', environment: 'scoped'))
+ .to contain_exactly(unprotected_variable_item, scoped_variable_item)
+ end
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 20af84ce648..5f46607b042 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -9,6 +9,10 @@ module Gitlab
subject { described_class.new(config, user: nil).execute }
+ before do
+ stub_feature_flags(allow_unsafe_ruby_regexp: false)
+ end
+
shared_examples 'returns errors' do |error_message|
it 'adds a message when an error is encountered' do
expect(subject.errors).to include(error_message)
@@ -609,13 +613,13 @@ module Gitlab
context 'when it is an array of integers' do
let(:only) { [1, 1] }
- it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regular expressions using re2 syntax'
end
context 'when it is invalid regex' do
let(:only) { ["/*invalid/"] }
- it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:only config should be an array of strings or regular expressions using re2 syntax'
end
end
@@ -633,13 +637,13 @@ module Gitlab
context 'when it is an array of integers' do
let(:except) { [1, 1] }
- it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regular expressions using re2 syntax'
end
context 'when it is invalid regex' do
let(:except) { ["/*invalid/"] }
- it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regexps'
+ it_behaves_like 'returns errors', 'jobs:rspec:except config should be an array of strings or regular expressions using re2 syntax'
end
end
end
@@ -710,16 +714,16 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- before_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ before_script: [[["global script"], "echo 1"], "echo 2", ["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"])
+ expect(subject[:options][:before_script]).to eq(["global script", "echo 1", "echo 2", "ls", "pwd"])
end
end
end
@@ -737,15 +741,15 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- test: { script: [["script"], ["echo 1"], "ls"] }
+ test: { script: [[["script"], "echo 1", "echo 2"], "ls"] }
}
end
it "return commands with scripts concatenated" do
- expect(subject[:options][:script]).to eq(["script", "echo 1", "ls"])
+ expect(subject[:options][:script]).to eq(["script", "echo 1", "echo 2", "ls"])
end
end
end
@@ -790,16 +794,16 @@ module Gitlab
end
end
- context 'when script is array of arrays of strings' do
+ context 'when script is nested arrays of strings' do
let(:config) do
{
- after_script: [["global script", "echo 1"], ["ls"], "pwd"],
+ after_script: [[["global script"], "echo 1"], "echo 2", ["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"])
+ expect(subject[:options][:after_script]).to eq(["global script", "echo 1", "echo 2", "ls", "pwd"])
end
end
end
@@ -2469,40 +2473,16 @@ module Gitlab
it_behaves_like 'returns errors', 'jobs:rspec:tags config should be an array of strings'
end
- context 'returns errors if before_script parameter is invalid' do
- let(:config) { YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) }
-
- it_behaves_like 'returns errors', 'before_script config should be an array containing strings and arrays of strings'
- end
-
context 'returns errors if job before_script parameter is not an array of strings' do
let(:config) { YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if job before_script parameter is multi-level nested array of strings' do
- let(:config) { YAML.dump({ rspec: { script: "test", before_script: [["ls", ["pwd"]], "test"] } }) }
-
- it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if after_script parameter is invalid' do
- let(:config) { YAML.dump({ after_script: "bundle update", rspec: { script: "test" } }) }
-
- it_behaves_like 'returns errors', 'after_script config should be an array containing strings and arrays of strings'
+ it_behaves_like 'returns errors', 'jobs:rspec:before_script config should be a string or a nested array of strings up to 10 levels deep'
end
context 'returns errors if job after_script parameter is not an array of strings' do
let(:config) { YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be an array containing strings and arrays of strings'
- end
-
- context 'returns errors if job after_script parameter is multi-level nested array of strings' do
- let(:config) { YAML.dump({ rspec: { script: "test", after_script: [["ls", ["pwd"]], "test"] } }) }
-
- it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be an array containing strings and arrays of strings'
+ it_behaves_like 'returns errors', 'jobs:rspec:after_script config should be a string or a nested array of strings up to 10 levels deep'
end
context 'returns errors if image parameter is invalid' do
diff --git a/spec/lib/gitlab/cluster/lifecycle_events_spec.rb b/spec/lib/gitlab/cluster/lifecycle_events_spec.rb
index 4ed68d54680..5eea78acd98 100644
--- a/spec/lib/gitlab/cluster/lifecycle_events_spec.rb
+++ b/spec/lib/gitlab/cluster/lifecycle_events_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'rspec-parameterized'
+require 'spec_helper'
RSpec.describe Gitlab::Cluster::LifecycleEvents do
# we create a new instance to ensure that we do not touch existing hooks
diff --git a/spec/lib/gitlab/config/entry/factory_spec.rb b/spec/lib/gitlab/config/entry/factory_spec.rb
index 260b5cf0ade..be4dfd31651 100644
--- a/spec/lib/gitlab/config/entry/factory_spec.rb
+++ b/spec/lib/gitlab/config/entry/factory_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Config::Entry::Factory do
describe '#create!' do
before do
- stub_const('Script', Class.new(Gitlab::Config::Entry::Node))
- Script.class_eval do
+ stub_const('Commands', Class.new(Gitlab::Config::Entry::Node))
+ Commands.class_eval do
include Gitlab::Config::Entry::Validatable
validations do
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Config::Entry::Factory do
end
end
- let(:entry) { Script }
+ let(:entry) { Commands }
let(:factory) { described_class.new(entry) }
context 'when setting a concrete value' do
diff --git a/spec/lib/gitlab/console_spec.rb b/spec/lib/gitlab/console_spec.rb
new file mode 100644
index 00000000000..f043433b4c5
--- /dev/null
+++ b/spec/lib/gitlab/console_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Console do
+ describe '.welcome!' do
+ context 'when running in the Rails console' do
+ before do
+ allow(Gitlab::Runtime).to receive(:console?).and_return(true)
+ allow(Gitlab::Metrics::BootTimeTracker.instance).to receive(:startup_time).and_return(42)
+ end
+
+ shared_examples 'console messages' do
+ it 'prints system info' do
+ expect($stdout).to receive(:puts).ordered.with(include("--"))
+ expect($stdout).to receive(:puts).ordered.with(include("Ruby:"))
+ expect($stdout).to receive(:puts).ordered.with(include("GitLab:"))
+ expect($stdout).to receive(:puts).ordered.with(include("GitLab Shell:"))
+ expect($stdout).to receive(:puts).ordered.with(include("PostgreSQL:"))
+ expect($stdout).to receive(:puts).ordered.with(include("--"))
+ expect($stdout).not_to receive(:puts).ordered
+
+ described_class.welcome!
+ end
+ end
+
+ # This is to add line coverage, not to actually verify behavior on macOS.
+ context 'on darwin' do
+ before do
+ stub_const('RUBY_PLATFORM', 'x86_64-darwin-19')
+ end
+
+ it_behaves_like 'console messages'
+ end
+
+ it_behaves_like 'console messages'
+ end
+
+ context 'when not running in the Rails console' do
+ before do
+ allow(Gitlab::Runtime).to receive(:console?).and_return(false)
+ end
+
+ it 'does not print anything' do
+ expect($stdout).not_to receive(:puts)
+
+ described_class.welcome!
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 46c33d7b7b2..73540a9b0f3 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe Gitlab::CurrentSettings do
allow(Gitlab::Runtime).to receive(:rake?).and_return(true)
# For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues
# during the initialization phase of the test suite, so instead let's mock the internals of it
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false)
+ allow(ApplicationSetting.connection).to receive(:active?).and_return(false)
end
context 'and no settings in cache' do
@@ -150,8 +150,8 @@ RSpec.describe Gitlab::CurrentSettings do
it 'fetches the settings from cache' do
# For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues
# during the initialization phase of the test suite, so instead let's mock the internals of it
- expect(ActiveRecord::Base.connection).not_to receive(:active?)
- expect(ActiveRecord::Base.connection).not_to receive(:cached_table_exists?)
+ expect(ApplicationSetting.connection).not_to receive(:active?)
+ expect(ApplicationSetting.connection).not_to receive(:cached_table_exists?)
expect_any_instance_of(ActiveRecord::MigrationContext).not_to receive(:needs_migration?)
expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0)
end
@@ -159,8 +159,8 @@ RSpec.describe Gitlab::CurrentSettings do
context 'and no settings in cache' do
before do
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true)
- allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
+ allow(ApplicationSetting.connection).to receive(:active?).and_return(true)
+ allow(ApplicationSetting.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
end
context 'with RequestStore enabled', :request_store do
@@ -181,7 +181,7 @@ RSpec.describe Gitlab::CurrentSettings do
context 'when ApplicationSettings does not have a primary key' do
before do
- allow(ActiveRecord::Base.connection).to receive(:primary_key).with('application_settings').and_return(nil)
+ allow(ApplicationSetting.connection).to receive(:primary_key).with('application_settings').and_return(nil)
end
it 'raises an exception if ApplicationSettings does not have a primary key' do
diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
index 8053f5261c0..7173ea43450 100644
--- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb
@@ -15,6 +15,13 @@ RSpec.describe Gitlab::CycleAnalytics::StageSummary do
let(:stage_summary) { described_class.new(project, **args).data }
+ describe '#identifier' do
+ it 'returns identifiers for each metric' do
+ identifiers = stage_summary.pluck(:identifier)
+ expect(identifiers).to eq(%i[issues commits deploys deployment_frequency])
+ end
+ end
+
describe "#new_issues" do
subject { stage_summary.first }
diff --git a/spec/lib/gitlab/database/background_migration/batch_metrics_spec.rb b/spec/lib/gitlab/database/background_migration/batch_metrics_spec.rb
index e96862fbc2d..66983733411 100644
--- a/spec/lib/gitlab/database/background_migration/batch_metrics_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batch_metrics_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Database::BackgroundMigration::BatchMetrics do
let(:batch_metrics) { described_class.new }
diff --git a/spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb b/spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb
index 95863ce3765..c367f4a4493 100644
--- a/spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb
@@ -6,7 +6,11 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchOptimizer do
describe '#optimize' do
subject { described_class.new(migration, number_of_jobs: number_of_jobs, ema_alpha: ema_alpha).optimize! }
- let(:migration) { create(:batched_background_migration, batch_size: batch_size, sub_batch_size: 100, interval: 120) }
+ let(:migration_params) { {} }
+ let(:migration) do
+ params = { batch_size: batch_size, sub_batch_size: 100, interval: 120 }.merge(migration_params)
+ create(:batched_background_migration, params)
+ end
let(:batch_size) { 10_000 }
@@ -87,6 +91,17 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchOptimizer do
expect { subject }.to change { migration.reload.batch_size }.to(2_000_000)
end
+
+ context 'when max_batch_size is set' do
+ let(:max_batch_size) { 10000 }
+ let(:migration_params) { { max_batch_size: max_batch_size } }
+
+ it 'caps the batch size at max_batch_size' do
+ mock_efficiency(0.7)
+
+ expect { subject }.to change { migration.reload.batch_size }.to(max_batch_size)
+ end
+ end
end
context 'reaching the lower limit for the batch size' do
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
index c4364826ee2..7338ea657b9 100644
--- a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -7,18 +7,85 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
describe 'associations' do
it { is_expected.to belong_to(:batched_migration).with_foreign_key(:batched_background_migration_id) }
+ it { is_expected.to have_many(:batched_job_transition_logs).with_foreign_key(:batched_background_migration_job_id) }
+ end
+
+ describe 'state machine' do
+ let_it_be(:job) { create(:batched_background_migration_job, :failed) }
+
+ context 'when a job is running' do
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :running, previous_state: :failed } )
+
+ expect { job.run! }.to change(job, :started_at)
+ end
+ end
+
+ context 'when a job succeed' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :succeeded, previous_state: :running } )
+
+ job.succeed!
+ end
+
+ it 'updates the finished_at' do
+ expect { job.succeed! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.succeed!
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('succeeded')
+ expect(transition_log.exception_class).to be_nil
+ expect(transition_log.exception_message).to be_nil
+ end
+ end
+
+ context 'when a job fails' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :failed, previous_state: :running } )
+
+ job.failure!
+ end
+
+ it 'tracks the exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(RuntimeError, { batched_job_id: job.id } )
+
+ job.failure!(error: RuntimeError.new)
+ end
+
+ it 'updates the finished_at' do
+ expect { job.failure! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.failure!(error: RuntimeError.new)
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('failed')
+ expect(transition_log.exception_class).to eq('RuntimeError')
+ expect(transition_log.exception_message).to eq('RuntimeError')
+ end
+ end
end
describe 'scopes' do
let_it_be(:fixed_time) { Time.new(2021, 04, 27, 10, 00, 00, 00) }
- let_it_be(:pending_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time) }
- let_it_be(:running_job) { create(:batched_background_migration_job, status: :running, updated_at: fixed_time) }
- let_it_be(:stuck_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
- let_it_be(:failed_job) { create(:batched_background_migration_job, status: :failed, attempts: 1) }
+ let_it_be(:pending_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time) }
+ let_it_be(:running_job) { create(:batched_background_migration_job, :running, updated_at: fixed_time) }
+ let_it_be(:stuck_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
+ let_it_be(:failed_job) { create(:batched_background_migration_job, :failed, attempts: 1) }
- let!(:max_attempts_failed_job) { create(:batched_background_migration_job, status: :failed, attempts: described_class::MAX_ATTEMPTS) }
- let!(:succeeded_job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:max_attempts_failed_job) { create(:batched_background_migration_job, :failed, attempts: described_class::MAX_ATTEMPTS) }
+ let!(:succeeded_job) { create(:batched_background_migration_job, :succeeded) }
before do
travel_to fixed_time
@@ -82,10 +149,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
subject { job.time_efficiency }
let(:migration) { build(:batched_background_migration, interval: 120.seconds) }
- let(:job) { build(:batched_background_migration_job, status: :succeeded, batched_migration: migration) }
+ let(:job) { build(:batched_background_migration_job, :succeeded, batched_migration: migration) }
context 'when job has not yet succeeded' do
- let(:job) { build(:batched_background_migration_job, status: :running) }
+ let(:job) { build(:batched_background_migration_job, :running) }
it 'returns nil' do
expect(subject).to be_nil
@@ -130,7 +197,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
describe '#split_and_retry!' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
context 'when job can be split' do
before do
@@ -146,7 +213,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 6,
max_value: 10,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -160,7 +227,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 11,
max_value: 15,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -177,7 +244,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when job is not failed' do
- let!(:job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:job) { create(:batched_background_migration_job, :succeeded) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Only failed jobs can be split'
@@ -185,7 +252,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when batch size is already 1' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 1, status: :failed) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 1) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Job cannot be split further'
@@ -204,7 +271,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
expect(job.batch_size).to eq(5)
expect(job.attempts).to eq(0)
- expect(job.status).to eq('failed')
+ expect(job.status_name).to eq(:failed)
end
end
end
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_transition_log_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_transition_log_spec.rb
new file mode 100644
index 00000000000..c42a0fc5e05
--- /dev/null
+++ b/spec/lib/gitlab/database/background_migration/batched_job_transition_log_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJobTransitionLog, type: :model do
+ describe 'associations' do
+ it { is_expected.to belong_to(:batched_job).with_foreign_key(:batched_background_migration_job_id) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:previous_status) }
+ it { is_expected.to validate_presence_of(:next_status) }
+ it { is_expected.to validate_presence_of(:batched_job) }
+ it { is_expected.to validate_length_of(:exception_class).is_at_most(100) }
+ it { is_expected.to validate_length_of(:exception_message).is_at_most(1000) }
+ it { is_expected.to define_enum_for(:previous_status).with_values(%i(pending running failed succeeded)).with_prefix }
+ it { is_expected.to define_enum_for(:next_status).with_values(%i(pending running failed succeeded)).with_prefix }
+ end
+end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
index 04c18a98ee6..bb2c6b9a3ae 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
@@ -96,13 +96,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
end
let!(:previous_job) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :succeeded,
batched_migration: migration,
min_value: event1.id,
max_value: event2.id,
batch_size: 2,
- sub_batch_size: 1,
- status: :succeeded
+ sub_batch_size: 1
)
end
@@ -144,7 +143,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has failed jobs' do
before do
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'retries the failed job' do
@@ -172,7 +171,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'retries the stuck job' do
@@ -186,7 +185,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has possible stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'keeps the migration active' do
@@ -201,13 +200,13 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when the migration has batches to process and failed jobs' do
before do
migration.update!(max_value: event3.id)
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'runs next batch then retries the failed job' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(1)
@@ -264,12 +263,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
it 'runs all jobs inline until finishing the migration' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.first)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_entire_migration(migration) }.to change { job_relation.count }.by(2)
@@ -330,9 +329,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
pause_ms: 0
}
- create(:batched_background_migration_job, common_attributes.merge(status: :succeeded, min_value: 1, max_value: 2))
- create(:batched_background_migration_job, common_attributes.merge(status: :pending, min_value: 3, max_value: 4))
- create(:batched_background_migration_job, common_attributes.merge(status: :failed, min_value: 5, max_value: 6, attempts: 1))
+ create(:batched_background_migration_job, :succeeded, common_attributes.merge(min_value: 1, max_value: 2))
+ create(:batched_background_migration_job, :pending, common_attributes.merge(min_value: 3, max_value: 4))
+ create(:batched_background_migration_job, :failed, common_attributes.merge(min_value: 5, max_value: 6, attempts: 1))
end
it 'completes the migration' do
@@ -359,7 +358,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration fails to complete' do
it 'raises an error' do
- batched_migration.batched_jobs.failed.update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
+ batched_migration.batched_jobs.with_status(:failed).update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
expect do
runner.finalize(
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 01d61a525e6..ea4ba4dd137 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when there are failed jobs' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :failed) }
+ let!(:batched_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration) }
it 'raises an exception' do
expect { batched_migration.finished! }.to raise_error(ActiveRecord::RecordInvalid)
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when the jobs are completed' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :succeeded) }
+ let!(:batched_job) { create(:batched_background_migration_job, :succeeded, batched_migration: batched_migration) }
it 'finishes the migration' do
batched_migration.finished!
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
it 'returns the first active migration according to queue order' do
expect(described_class.active_migration).to eq(migration2)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
end
end
@@ -84,10 +84,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let!(:migration_without_jobs) { create(:batched_background_migration) }
before do
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 200, status: :failed)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 500, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 200, status: :running)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
+ create(:batched_background_migration_job, :failed, batched_migration: migration1, batch_size: 200)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration2, batch_size: 500)
+ create(:batched_background_migration_job, :running, batched_migration: migration2, batch_size: 200)
end
it 'returns totals from successful jobs' do
@@ -268,7 +268,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject(:retry_failed_jobs) { batched_migration.retry_failed_jobs! }
context 'when there are failed migration jobs' do
- let!(:batched_background_migration_job) { create(:batched_background_migration_job, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:batched_background_migration_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
before do
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
@@ -312,9 +312,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let(:batched_migration) { create(:batched_background_migration) }
before do
- create_list(:batched_background_migration_job, 5, status: :succeeded, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :running, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :failed, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 5, :succeeded, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :running, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :failed, batch_size: 1_000, batched_migration: batched_migration)
end
it 'sums the batch_size of succeeded jobs' do
@@ -347,7 +347,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let_it_be(:common_attrs) do
{
- status: :succeeded,
batched_migration: migration,
finished_at: end_time
}
@@ -357,7 +356,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: 10) }
it 'returns nil' do
- create_list(:batched_background_migration_job, 9, **common_attrs)
+ create_list(:batched_background_migration_job, 9, :succeeded, **common_attrs)
expect(subject).to be_nil
end
@@ -369,6 +368,8 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: number_of_jobs) }
+ let!(:jobs) { create_list(:batched_background_migration_job, number_of_jobs, :succeeded, **common_attrs.merge(batched_migration: migration)) }
+
before do
expect(migration).to receive_message_chain(:batched_jobs, :successful_in_execution_order, :reverse_order, :limit, :with_preloads)
.and_return(jobs)
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index c1183a15e37..4f5536d8771 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -35,8 +35,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(test_metrics)
- expect(job_record).to receive(:update!).with(hash_including(attempts: 1, status: :running)).and_call_original
-
freeze_time do
subject
@@ -51,11 +49,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
context 'when running a job that failed previously' do
let!(:job_record) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :failed,
batched_migration: active_migration,
pause_ms: pause_ms,
attempts: 1,
- status: :failed,
finished_at: 1.hour.ago,
metrics: { 'my_metrics' => 'some_value' }
)
@@ -67,10 +64,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
- expect(job_record).to receive(:update!).with(
- hash_including(attempts: 2, status: :running, finished_at: nil, metrics: {})
- ).and_call_original
-
freeze_time do
subject
@@ -201,4 +194,44 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
it_behaves_like 'an error is raised', RuntimeError.new('Something broke!')
it_behaves_like 'an error is raised', SignalException.new('SIGTERM')
end
+
+ context 'when the batched background migration does not inherit from BaseJob' do
+ let(:migration_class) { Class.new }
+
+ before do
+ stub_const('Gitlab::BackgroundMigration::Foo', migration_class)
+ end
+
+ let(:connection) { double(:connection) }
+ let(:active_migration) { create(:batched_background_migration, :active, job_class_name: 'Foo') }
+ let!(:job_record) { create(:batched_background_migration_job, batched_migration: active_migration) }
+
+ it 'does not pass any argument' do
+ expect(Gitlab::BackgroundMigration::Foo).to receive(:new).with(no_args).and_return(job_instance)
+
+ expect(job_instance).to receive(:perform)
+
+ described_class.new(connection: connection).perform(job_record)
+ end
+ end
+
+ context 'when the batched background migration inherits from BaseJob' do
+ let(:connection) { double(:connection) }
+ let(:active_migration) { create(:batched_background_migration, :active, job_class_name: 'Foo') }
+ let!(:job_record) { create(:batched_background_migration_job, batched_migration: active_migration) }
+
+ let(:migration_class) { Class.new(::Gitlab::BackgroundMigration::BaseJob) }
+
+ before do
+ stub_const('Gitlab::BackgroundMigration::Foo', migration_class)
+ end
+
+ it 'passes the correct connection' do
+ expect(Gitlab::BackgroundMigration::Foo).to receive(:new).with(connection: connection).and_return(job_instance)
+
+ expect(job_instance).to receive(:perform)
+
+ described_class.new(connection: connection).perform(job_record)
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb b/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb
index 0844616ee1c..31486240bfa 100644
--- a/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb
+++ b/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb
@@ -4,10 +4,11 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::DynamicModelHelpers do
let(:including_class) { Class.new.include(described_class) }
- let(:table_name) { 'projects' }
+ let(:table_name) { Project.table_name }
+ let(:connection) { Project.connection }
describe '#define_batchable_model' do
- subject { including_class.new.define_batchable_model(table_name) }
+ subject { including_class.new.define_batchable_model(table_name, connection: connection) }
it 'is an ActiveRecord model' do
expect(subject.ancestors).to include(ActiveRecord::Base)
@@ -40,7 +41,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do
it 'iterates table in batches' do
each_batch_size = ->(&block) do
- subject.each_batch(table_name, of: 1) do |batch|
+ subject.each_batch(table_name, connection: connection, of: 1) do |batch|
block.call(batch.size)
end
end
@@ -56,7 +57,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do
end
it 'raises an error' do
- expect { subject.each_batch(table_name, of: 1) { |batch| batch.size } }
+ expect { subject.each_batch(table_name, connection: connection, of: 1) { |batch| batch.size } }
.to raise_error(RuntimeError, /each_batch should not run inside a transaction/)
end
end
@@ -74,7 +75,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do
end
it 'iterates table in batch ranges' do
- expect { |b| subject.each_batch_range(table_name, of: 1, &b) }
+ expect { |b| subject.each_batch_range(table_name, connection: connection, of: 1, &b) }
.to yield_successive_args(
[first_project.id, first_project.id],
[second_project.id, second_project.id]
@@ -82,13 +83,13 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do
end
it 'yields only one batch if bigger than the table size' do
- expect { |b| subject.each_batch_range(table_name, of: 2, &b) }
+ expect { |b| subject.each_batch_range(table_name, connection: connection, of: 2, &b) }
.to yield_successive_args([first_project.id, second_project.id])
end
it 'makes it possible to apply a scope' do
each_batch_limited = ->(&b) do
- subject.each_batch_range(table_name, scope: ->(table) { table.limit(1) }, of: 1, &b)
+ subject.each_batch_range(table_name, connection: connection, scope: ->(table) { table.limit(1) }, of: 1, &b)
end
expect { |b| each_batch_limited.call(&b) }
@@ -102,7 +103,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do
end
it 'raises an error' do
- expect { subject.each_batch_range(table_name, of: 1) { 1 } }
+ expect { subject.each_batch_range(table_name, connection: connection, of: 1) { 1 } }
.to raise_error(RuntimeError, /each_batch should not run inside a transaction/)
end
end
diff --git a/spec/lib/gitlab/database/each_database_spec.rb b/spec/lib/gitlab/database/each_database_spec.rb
index 9327fc4ff78..d526b3bc1ac 100644
--- a/spec/lib/gitlab/database/each_database_spec.rb
+++ b/spec/lib/gitlab/database/each_database_spec.rb
@@ -4,45 +4,97 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::EachDatabase do
describe '.each_database_connection' do
- let(:expected_connections) do
- Gitlab::Database.database_base_models.map { |name, model| [model.connection, name] }
+ before do
+ allow(Gitlab::Database).to receive(:database_base_models)
+ .and_return({ main: ActiveRecord::Base, ci: Ci::ApplicationRecord }.with_indifferent_access)
end
- it 'yields each connection after connecting SharedModel' do
- expected_connections.each do |connection, _|
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(connection).and_yield
- end
+ it 'yields each connection after connecting SharedModel', :add_ci_connection do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection)
+ .with(ActiveRecord::Base.connection).ordered.and_yield
- yielded_connections = []
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection)
+ .with(Ci::ApplicationRecord.connection).ordered.and_yield
- described_class.each_database_connection do |connection, name|
- yielded_connections << [connection, name]
- end
-
- expect(yielded_connections).to match_array(expected_connections)
+ expect { |b| described_class.each_database_connection(&b) }
+ .to yield_successive_args(
+ [ActiveRecord::Base.connection, 'main'],
+ [Ci::ApplicationRecord.connection, 'ci']
+ )
end
end
describe '.each_model_connection' do
- let(:model1) { double(connection: double, table_name: 'table1') }
- let(:model2) { double(connection: double, table_name: 'table2') }
+ context 'when the model inherits from SharedModel', :add_ci_connection do
+ let(:model1) { Class.new(Gitlab::Database::SharedModel) }
+ let(:model2) { Class.new(Gitlab::Database::SharedModel) }
- before do
- allow(model1.connection).to receive_message_chain('pool.db_config.name').and_return('name1')
- allow(model2.connection).to receive_message_chain('pool.db_config.name').and_return('name2')
+ before do
+ allow(Gitlab::Database).to receive(:database_base_models)
+ .and_return({ main: ActiveRecord::Base, ci: Ci::ApplicationRecord }.with_indifferent_access)
+ end
+
+ it 'yields each model with SharedModel connected to each database connection' do
+ expect_yielded_models([model1, model2], [
+ { model: model1, connection: ActiveRecord::Base.connection, name: 'main' },
+ { model: model1, connection: Ci::ApplicationRecord.connection, name: 'ci' },
+ { model: model2, connection: ActiveRecord::Base.connection, name: 'main' },
+ { model: model2, connection: Ci::ApplicationRecord.connection, name: 'ci' }
+ ])
+ end
+
+ context 'when the model limits connection names' do
+ before do
+ model1.limit_connection_names = %i[main]
+ model2.limit_connection_names = %i[ci]
+ end
+
+ it 'only yields the model with SharedModel connected to the limited connections' do
+ expect_yielded_models([model1, model2], [
+ { model: model1, connection: ActiveRecord::Base.connection, name: 'main' },
+ { model: model2, connection: Ci::ApplicationRecord.connection, name: 'ci' }
+ ])
+ end
+ end
+ end
+
+ context 'when the model does not inherit from SharedModel' do
+ let(:main_model) { Class.new(ActiveRecord::Base) }
+ let(:ci_model) { Class.new(Ci::ApplicationRecord) }
+
+ let(:main_connection) { double(:connection) }
+ let(:ci_connection) { double(:connection) }
+
+ before do
+ allow(main_model).to receive(:connection).and_return(main_connection)
+ allow(ci_model).to receive(:connection).and_return(ci_connection)
+
+ allow(main_connection).to receive_message_chain('pool.db_config.name').and_return('main')
+ allow(ci_connection).to receive_message_chain('pool.db_config.name').and_return('ci')
+ end
+
+ it 'yields each model after connecting SharedModel' do
+ expect_yielded_models([main_model, ci_model], [
+ { model: main_model, connection: main_connection, name: 'main' },
+ { model: ci_model, connection: ci_connection, name: 'ci' }
+ ])
+ end
end
- it 'yields each model after connecting SharedModel' do
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(model1.connection).and_yield
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(model2.connection).and_yield
+ def expect_yielded_models(models_to_iterate, expected_values)
+ times_yielded = 0
+
+ described_class.each_model_connection(models_to_iterate) do |model, name|
+ expected = expected_values[times_yielded]
- yielded_models = []
+ expect(model).to be(expected[:model])
+ expect(model.connection).to be(expected[:connection])
+ expect(name).to eq(expected[:name])
- described_class.each_model_connection([model1, model2]) do |model, name|
- yielded_models << [model, name]
+ times_yielded += 1
end
- expect(yielded_models).to match_array([[model1, 'name1'], [model2, 'name2']])
+ expect(times_yielded).to eq(expected_values.size)
end
end
end
diff --git a/spec/lib/gitlab/database/gitlab_schema_spec.rb b/spec/lib/gitlab/database/gitlab_schema_spec.rb
index 255efc99ff6..a5a67c2c918 100644
--- a/spec/lib/gitlab/database/gitlab_schema_spec.rb
+++ b/spec/lib/gitlab/database/gitlab_schema_spec.rb
@@ -44,6 +44,8 @@ RSpec.describe Gitlab::Database::GitlabSchema do
'my_schema.ci_builds' | :gitlab_ci
'information_schema.columns' | :gitlab_shared
'audit_events_part_5fc467ac26' | :gitlab_main
+ '_test_gitlab_main_table' | :gitlab_main
+ '_test_gitlab_ci_table' | :gitlab_ci
'_test_my_table' | :gitlab_shared
'pg_attribute' | :gitlab_shared
'my_other_table' | :undefined_my_other_table
diff --git a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb b/spec/lib/gitlab/database/load_balancing/configuration_spec.rb
index 796c14c1038..e87c9c20707 100644
--- a/spec/lib/gitlab/database/load_balancing/configuration_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/configuration_spec.rb
@@ -2,11 +2,18 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
+RSpec.describe Gitlab::Database::LoadBalancing::Configuration, :request_store do
let(:configuration_hash) { {} }
let(:db_config) { ActiveRecord::DatabaseConfigurations::HashConfig.new('test', 'ci', configuration_hash) }
let(:model) { double(:model, connection_db_config: db_config) }
+ before do
+ # It's confusing to think about these specs with this enabled by default so
+ # we make it disabled by default and just write the specific spec for when
+ # it's enabled
+ stub_feature_flags(force_no_sharing_primary_model: false)
+ end
+
describe '.for_model' do
context 'when load balancing is not configured' do
it 'uses the default settings' do
@@ -233,11 +240,23 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
end
context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=main' do
- it 'the primary connection uses main connection' do
+ before do
stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', 'main')
+ end
+ it 'the primary connection uses main connection' do
expect(config.primary_connection_specification_name).to eq('ActiveRecord::Base')
end
+
+ context 'when force_no_sharing_primary_model feature flag is enabled' do
+ before do
+ stub_feature_flags(force_no_sharing_primary_model: true)
+ end
+
+ it 'the primary connection uses ci connection' do
+ expect(config.primary_connection_specification_name).to eq('Ci::ApplicationRecord')
+ end
+ end
end
context 'when GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci=unknown' do
diff --git a/spec/lib/gitlab/database/load_balancing/setup_spec.rb b/spec/lib/gitlab/database/load_balancing/setup_spec.rb
index 953d83d3b48..20519a759b2 100644
--- a/spec/lib/gitlab/database/load_balancing/setup_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/setup_spec.rb
@@ -130,6 +130,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: nil,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'ci' }
@@ -140,6 +141,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: false,
ff_use_model_load_balancing: nil,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'main' }
@@ -150,6 +152,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: nil,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
@@ -160,60 +163,77 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: false,
ff_use_model_load_balancing: nil,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
- "with FF disabled without RequestStore it uses main" => {
+ "with FF use_model_load_balancing disabled without RequestStore it uses main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: false,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
- "with FF enabled without RequestStore sticking of FF does not work, so it fallbacks to use main" => {
+ "with FF use_model_load_balancing enabled without RequestStore sticking of FF does not work, so it fallbacks to use main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: false,
ff_use_model_load_balancing: true,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
- "with FF disabled with RequestStore it uses main" => {
+ "with FF use_model_load_balancing disabled with RequestStore it uses main" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: true,
ff_use_model_load_balancing: false,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'main_replica', write: 'main' }
}
},
- "with FF enabled with RequestStore it sticks FF and uses CI connection" => {
+ "with FF use_model_load_balancing enabled with RequestStore it sticks FF and uses CI connection" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
request_store_active: true,
ff_use_model_load_balancing: true,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'ci' }
}
},
- "with re-use and FF enabled with RequestStore it sticks FF and uses CI connection for reads" => {
+ "with re-use and ff_use_model_load_balancing enabled and FF force_no_sharing_primary_model disabled with RequestStore it sticks FF and uses CI connection for reads" => {
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
request_store_active: true,
ff_use_model_load_balancing: true,
+ ff_force_no_sharing_primary_model: false,
expectations: {
main: { read: 'main_replica', write: 'main' },
ci: { read: 'ci_replica', write: 'main' }
}
+ },
+ "with re-use and ff_use_model_load_balancing enabled and FF force_no_sharing_primary_model enabled with RequestStore it sticks FF and uses CI connection for reads" => {
+ env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
+ env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
+ request_store_active: true,
+ ff_use_model_load_balancing: true,
+ ff_force_no_sharing_primary_model: true,
+ expectations: {
+ main: { read: 'main_replica', write: 'main' },
+ ci: { read: 'ci_replica', write: 'ci' }
+ }
}
}
end
@@ -243,6 +263,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
around do |example|
if request_store_active
Gitlab::WithRequestStore.with_request_store do
+ stub_feature_flags(force_no_sharing_primary_model: ff_force_no_sharing_primary_model)
RequestStore.clear!
example.run
diff --git a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
index 13f2d31bc32..ed11699e494 100644
--- a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
@@ -18,6 +18,45 @@ RSpec.describe Gitlab::Database::LooseForeignKeys do
))
end
+ context 'ensure keys are sorted' do
+ it 'does not have any keys that are out of order' do
+ parsed = YAML.parse_file(described_class.loose_foreign_keys_yaml_path)
+ mapping = parsed.children.first
+ table_names = mapping.children.select(&:scalar?).map(&:value)
+ expect(table_names).to eq(table_names.sort), "expected sorted table names in the YAML file"
+ end
+ end
+
+ context 'ensure no duplicates are found' do
+ it 'does not have duplicate tables defined' do
+ # since we use hash to detect duplicate hash keys we need to parse YAML document
+ parsed = YAML.parse_file(described_class.loose_foreign_keys_yaml_path)
+ expect(parsed).to be_document
+ expect(parsed.children).to be_one, "YAML has a single document"
+
+ # require hash
+ mapping = parsed.children.first
+ expect(mapping).to be_mapping, "YAML has a top-level hash"
+
+ # find all scalars with names
+ table_names = mapping.children.select(&:scalar?).map(&:value)
+ expect(table_names).not_to be_empty, "YAML has a non-zero tables defined"
+
+ # expect to not have duplicates
+ expect(table_names).to contain_exactly(*table_names.uniq)
+ end
+
+ it 'does not have duplicate column definitions' do
+ # ignore other modifiers
+ all_definitions = definitions.map do |definition|
+ { from_table: definition.from_table, to_table: definition.to_table, column: definition.column }
+ end
+
+ # expect to not have duplicates
+ expect(all_definitions).to contain_exactly(*all_definitions.uniq)
+ end
+ end
+
describe 'ensuring database integrity' do
def base_models_for(table)
parent_table_schema = Gitlab::Database::GitlabSchema.table_schema(table)
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 7e3de32b965..d71a4f81901 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -14,6 +14,54 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:puts)
end
+ describe 'overridden dynamic model helpers' do
+ let(:test_table) { '__test_batching_table' }
+
+ before do
+ model.connection.execute(<<~SQL)
+ CREATE TABLE #{test_table} (
+ id integer NOT NULL PRIMARY KEY,
+ name text NOT NULL
+ );
+
+ INSERT INTO #{test_table} (id, name)
+ VALUES (1, 'bob'), (2, 'mary'), (3, 'amy');
+ SQL
+ end
+
+ describe '#define_batchable_model' do
+ it 'defines a batchable model with the migration connection' do
+ expect(model.define_batchable_model(test_table).count).to eq(3)
+ end
+ end
+
+ describe '#each_batch' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
+ it 'calls each_batch with the migration connection' do
+ each_batch_name = ->(&block) do
+ model.each_batch(test_table, of: 2) do |batch|
+ block.call(batch.pluck(:name))
+ end
+ end
+
+ expect { |b| each_batch_name.call(&b) }.to yield_successive_args(%w[bob mary], %w[amy])
+ end
+ end
+
+ describe '#each_batch_range' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
+ it 'calls each_batch with the migration connection' do
+ expect { |b| model.each_batch_range(test_table, of: 2, &b) }.to yield_successive_args([1, 2], [3, 3])
+ end
+ end
+ end
+
describe '#remove_timestamps' do
it 'can remove the default timestamps' do
Gitlab::Database::MigrationHelpers::DEFAULT_TIMESTAMP_COLUMNS.each do |column_name|
@@ -442,6 +490,60 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
end
+ describe '#remove_foreign_key_if_exists' do
+ context 'when the foreign key does not exist' do
+ before do
+ allow(model).to receive(:foreign_key_exists?).and_return(false)
+ end
+
+ it 'does nothing' do
+ expect(model).not_to receive(:remove_foreign_key)
+
+ model.remove_foreign_key_if_exists(:projects, :users, column: :user_id)
+ end
+ end
+
+ context 'when the foreign key exists' do
+ before do
+ allow(model).to receive(:foreign_key_exists?).and_return(true)
+ end
+
+ it 'removes the foreign key' do
+ expect(model).to receive(:remove_foreign_key).with(:projects, :users, { column: :user_id })
+
+ model.remove_foreign_key_if_exists(:projects, :users, column: :user_id)
+ end
+
+ context 'when the target table is not given' do
+ it 'passes the options as the second parameter' do
+ expect(model).to receive(:remove_foreign_key).with(:projects, { column: :user_id })
+
+ model.remove_foreign_key_if_exists(:projects, column: :user_id)
+ end
+ end
+
+ context 'when the reverse_lock_order option is given' do
+ it 'requests for lock before removing the foreign key' do
+ expect(model).to receive(:transaction_open?).and_return(true)
+ expect(model).to receive(:execute).with(/LOCK TABLE users, projects/)
+ expect(model).not_to receive(:remove_foreign_key).with(:projects, :users)
+
+ model.remove_foreign_key_if_exists(:projects, :users, column: :user_id, reverse_lock_order: true)
+ end
+
+ context 'when not inside a transaction' do
+ it 'does not lock' do
+ expect(model).to receive(:transaction_open?).and_return(false)
+ expect(model).not_to receive(:execute).with(/LOCK TABLE users, projects/)
+ expect(model).to receive(:remove_foreign_key).with(:projects, :users, { column: :user_id })
+
+ model.remove_foreign_key_if_exists(:projects, :users, column: :user_id, reverse_lock_order: true)
+ end
+ end
+ end
+ end
+ end
+
describe '#add_concurrent_foreign_key' do
before do
allow(model).to receive(:foreign_key_exists?).and_return(false)
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index 0abb76b9f8a..96dc3a0fc28 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -299,7 +299,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
before do
allow(Gitlab::BackgroundMigration).to receive(:coordinator_for_database)
- .with('main').and_return(coordinator)
+ .with(tracking_database).and_return(coordinator)
expect(coordinator).to receive(:migration_class_for)
.with(job_class_name).at_least(:once) { job_class }
@@ -403,7 +403,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
context 'when a specific coordinator is given' do
- let(:coordinator) { Gitlab::BackgroundMigration::JobCoordinator.for_tracking_database('main') }
+ let(:coordinator) { Gitlab::BackgroundMigration::JobCoordinator.for_tracking_database(tracking_database) }
it 'uses that coordinator' do
expect(coordinator).to receive(:perform_in).with(10.minutes, 'Class', 'Hello', 'World').and_call_original
@@ -438,6 +438,16 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
it_behaves_like 'helpers that enqueue background migrations', BackgroundMigrationWorker, 'main'
end
+ context 'when the migration is running against the ci database', if: Gitlab::Database.has_config?(:ci) do
+ around do |example|
+ Gitlab::Database::SharedModel.using_connection(::Ci::ApplicationRecord.connection) do
+ example.run
+ end
+ end
+
+ it_behaves_like 'helpers that enqueue background migrations', BackgroundMigration::CiDatabaseWorker, 'ci'
+ end
+
describe '#delete_job_tracking' do
let!(:job_class_name) { 'TestJob' }
diff --git a/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
index c45149d67bf..37efff165c7 100644
--- a/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
@@ -59,6 +59,7 @@ RSpec.describe Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers d
batch_max_value: 1000,
batch_class_name: 'MyBatchClass',
batch_size: 100,
+ max_batch_size: 10000,
sub_batch_size: 10)
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
@@ -71,6 +72,7 @@ RSpec.describe Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers d
max_value: 1000,
batch_class_name: 'MyBatchClass',
batch_size: 100,
+ max_batch_size: 10000,
sub_batch_size: 10,
job_arguments: %w[],
status: 'active',
diff --git a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
index 902d8e13a63..fd8303c379c 100644
--- a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
+++ b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb
@@ -66,55 +66,43 @@ RSpec.describe Gitlab::Database::Migrations::Instrumentation do
context 'on successful execution' do
subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name, connection: connection) {} }
- it 'records walltime' do
+ it 'records a valid observation', :aggregate_failures do
expect(subject.walltime).not_to be_nil
- end
-
- it 'records success' do
expect(subject.success).to be_truthy
- end
-
- it 'records the migration version' do
expect(subject.version).to eq(migration_version)
- end
-
- it 'records the migration name' do
expect(subject.name).to eq(migration_name)
end
end
context 'upon failure' do
- subject { described_class.new(result_dir: result_dir).observe(version: migration_version, name: migration_name, connection: connection) { raise 'something went wrong' } }
-
- it 'raises the exception' do
- expect { subject }.to raise_error(/something went wrong/)
- end
-
- context 'retrieving observations' do
- subject { instance.observations.first }
-
- before do
- instance.observe(version: migration_version, name: migration_name, connection: connection) { raise 'something went wrong' }
- rescue StandardError
- # ignore
- end
+ where(exception: ['something went wrong', SystemStackError, Interrupt])
+ with_them do
let(:instance) { described_class.new(result_dir: result_dir) }
- it 'records walltime' do
- expect(subject.walltime).not_to be_nil
- end
-
- it 'records failure' do
- expect(subject.success).to be_falsey
- end
+ subject(:observe) { instance.observe(version: migration_version, name: migration_name, connection: connection) { raise exception } }
- it 'records the migration version' do
- expect(subject.version).to eq(migration_version)
+ it 'raises the exception' do
+ expect { observe }.to raise_error(exception)
end
- it 'records the migration name' do
- expect(subject.name).to eq(migration_name)
+ context 'retrieving observations' do
+ subject { instance.observations.first }
+
+ before do
+ observe
+ # rubocop:disable Lint/RescueException
+ rescue Exception
+ # rubocop:enable Lint/RescueException
+ # ignore (we expect this exception)
+ end
+
+ it 'records a valid observation', :aggregate_failures do
+ expect(subject.walltime).not_to be_nil
+ expect(subject.success).to be_falsey
+ expect(subject.version).to eq(migration_version)
+ expect(subject.name).to eq(migration_name)
+ end
end
end
end
diff --git a/spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb b/spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb
index 076fb9e8215..50ad77caaf1 100644
--- a/spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb
+++ b/spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::LockRetryMixin do
describe Gitlab::Database::Migrations::LockRetryMixin::ActiveRecordMigrationProxyLockRetries do
- let(:migration) { double }
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:migration) { double(connection: connection) }
let(:return_value) { double }
let(:class_def) do
Class.new do
@@ -40,6 +41,18 @@ RSpec.describe Gitlab::Database::Migrations::LockRetryMixin do
expect(result).to eq(return_value)
end
end
+
+ describe '#migration_connection' do
+ subject { class_def.new(migration).migration_connection }
+
+ it 'retrieves actual migration connection from #migration' do
+ expect(migration).to receive(:connection).and_return(return_value)
+
+ result = subject
+
+ expect(result).to eq(return_value)
+ end
+ end
end
describe Gitlab::Database::Migrations::LockRetryMixin::ActiveRecordMigratorLockRetries do
@@ -96,7 +109,8 @@ RSpec.describe Gitlab::Database::Migrations::LockRetryMixin do
context 'with transactions enabled and lock retries enabled' do
let(:receiver) { double('receiver', use_transaction?: true)}
- let(:migration) { double('migration', enable_lock_retries?: true) }
+ let(:migration) { double('migration', migration_connection: connection, enable_lock_retries?: true) }
+ let(:connection) { ActiveRecord::Base.connection }
it 'calls super method' do
p = proc { }
diff --git a/spec/lib/gitlab/database/migrations/observers/query_details_spec.rb b/spec/lib/gitlab/database/migrations/observers/query_details_spec.rb
index 5a19ae6581d..a757cac0a2a 100644
--- a/spec/lib/gitlab/database/migrations/observers/query_details_spec.rb
+++ b/spec/lib/gitlab/database/migrations/observers/query_details_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Gitlab::Database::Migrations::Observers::QueryDetails do
subject { described_class.new(observation, directory_path, connection) }
let(:connection) { ActiveRecord::Migration.connection }
- let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
+ let(:observation) { Gitlab::Database::Migrations::Observation.new(version: migration_version, name: migration_name) }
let(:query) { "select date_trunc('day', $1::timestamptz) + $2 * (interval '1 hour')" }
let(:query_binds) { [Time.current, 3] }
let(:directory_path) { Dir.mktmpdir }
diff --git a/spec/lib/gitlab/database/migrations/observers/query_log_spec.rb b/spec/lib/gitlab/database/migrations/observers/query_log_spec.rb
index 7b01e39f5f1..eb66972e5ab 100644
--- a/spec/lib/gitlab/database/migrations/observers/query_log_spec.rb
+++ b/spec/lib/gitlab/database/migrations/observers/query_log_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Observers::QueryLog do
subject { described_class.new(observation, directory_path, connection) }
- let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
+ let(:observation) { Gitlab::Database::Migrations::Observation.new(version: migration_version, name: migration_name) }
let(:connection) { ActiveRecord::Migration.connection }
let(:query) { 'select 1' }
let(:directory_path) { Dir.mktmpdir }
diff --git a/spec/lib/gitlab/database/migrations/observers/transaction_duration_spec.rb b/spec/lib/gitlab/database/migrations/observers/transaction_duration_spec.rb
index b26bb8fbe41..f433e25b2ba 100644
--- a/spec/lib/gitlab/database/migrations/observers/transaction_duration_spec.rb
+++ b/spec/lib/gitlab/database/migrations/observers/transaction_duration_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Gitlab::Database::Migrations::Observers::TransactionDuration do
subject(:transaction_duration_observer) { described_class.new(observation, directory_path, connection) }
let(:connection) { ActiveRecord::Migration.connection }
- let(:observation) { Gitlab::Database::Migrations::Observation.new(migration_version, migration_name) }
+ let(:observation) { Gitlab::Database::Migrations::Observation.new(version: migration_version, name: migration_name) }
let(:directory_path) { Dir.mktmpdir }
let(:log_file) { "#{directory_path}/#{migration_version}_#{migration_name}-transaction-duration.json" }
let(:transaction_duration) { Gitlab::Json.parse(File.read(log_file)) }
diff --git a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
index e5a8143fcc3..f94a40c93e1 100644
--- a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
@@ -3,59 +3,12 @@
require 'spec_helper'
RSpec.describe 'cross-database foreign keys' do
- # TODO: We are trying to empty out this list in
- # https://gitlab.com/groups/gitlab-org/-/epics/7249 . Once we are done we can
- # keep this test and assert that there are no cross-db foreign keys. We
- # should not be adding anything to this list but should instead only add new
- # loose foreign keys
- # https://docs.gitlab.com/ee/development/database/loose_foreign_keys.html .
+ # Since we don't expect to have any cross-database foreign keys
+ # this is empty. If we will have an entry like
+ # `ci_daily_build_group_report_results.project_id`
+ # should be added.
let(:allowed_cross_database_foreign_keys) do
- %w(
- ci_build_report_results.project_id
- ci_builds.project_id
- ci_builds_metadata.project_id
- ci_daily_build_group_report_results.group_id
- ci_daily_build_group_report_results.project_id
- ci_freeze_periods.project_id
- ci_job_artifacts.project_id
- ci_job_token_project_scope_links.added_by_id
- ci_job_token_project_scope_links.source_project_id
- ci_job_token_project_scope_links.target_project_id
- ci_pending_builds.namespace_id
- ci_pending_builds.project_id
- ci_pipeline_schedules.owner_id
- ci_pipeline_schedules.project_id
- ci_pipelines.merge_request_id
- ci_pipelines.project_id
- ci_project_monthly_usages.project_id
- ci_refs.project_id
- ci_resource_groups.project_id
- ci_runner_namespaces.namespace_id
- ci_runner_projects.project_id
- ci_running_builds.project_id
- ci_sources_pipelines.project_id
- ci_sources_pipelines.source_project_id
- ci_sources_projects.source_project_id
- ci_stages.project_id
- ci_subscriptions_projects.downstream_project_id
- ci_subscriptions_projects.upstream_project_id
- ci_triggers.owner_id
- ci_triggers.project_id
- ci_unit_tests.project_id
- ci_variables.project_id
- dast_profiles_pipelines.ci_pipeline_id
- dast_scanner_profiles_builds.ci_build_id
- dast_site_profiles_builds.ci_build_id
- dast_site_profiles_pipelines.ci_pipeline_id
- external_pull_requests.project_id
- merge_requests.head_pipeline_id
- merge_trains.pipeline_id
- requirements_management_test_reports.build_id
- security_scans.build_id
- vulnerability_feedback.pipeline_id
- vulnerability_occurrence_pipelines.pipeline_id
- vulnerability_statistics.latest_pipeline_id
- ).freeze
+ %w[].freeze
end
def foreign_keys_for(table_name)
diff --git a/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb b/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb
index c41b4eeea10..22a70dc7df0 100644
--- a/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb
@@ -14,23 +14,41 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio
Gitlab::Database::QueryAnalyzer.instance.within { example.run }
end
- shared_examples 'successful examples' do
+ describe 'context and suppress key names' do
+ describe '.context_key' do
+ it 'contains class name' do
+ expect(described_class.context_key)
+ .to eq 'analyzer_prevent_cross_database_modification_context'.to_sym
+ end
+ end
+
+ describe '.suppress_key' do
+ it 'contains class name' do
+ expect(described_class.suppress_key)
+ .to eq 'analyzer_prevent_cross_database_modification_suppressed'.to_sym
+ end
+ end
+ end
+
+ shared_examples 'successful examples' do |model:|
+ let(:model) { model }
+
context 'outside transaction' do
it { expect { run_queries }.not_to raise_error }
end
- context 'within transaction' do
+ context "within #{model} transaction" do
it do
- Project.transaction do
+ model.transaction do
expect { run_queries }.not_to raise_error
end
end
end
- context 'within nested transaction' do
+ context "within nested #{model} transaction" do
it do
- Project.transaction(requires_new: true) do
- Project.transaction(requires_new: true) do
+ model.transaction(requires_new: true) do
+ model.transaction(requires_new: true) do
expect { run_queries }.not_to raise_error
end
end
@@ -38,13 +56,26 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio
end
end
+ shared_examples 'cross-database modification errors' do |model:|
+ let(:model) { model }
+
+ context "within #{model} transaction" do
+ it 'raises error' do
+ model.transaction do
+ expect { run_queries }.to raise_error /Cross-database data modification/
+ end
+ end
+ end
+ end
+
context 'when CI and other tables are read in a transaction' do
def run_queries
pipeline.reload
project.reload
end
- include_examples 'successful examples'
+ include_examples 'successful examples', model: Project
+ include_examples 'successful examples', model: Ci::Pipeline
end
context 'when only CI data is modified' do
@@ -53,7 +84,9 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio
project.reload
end
- include_examples 'successful examples'
+ include_examples 'successful examples', model: Ci::Pipeline
+
+ include_examples 'cross-database modification errors', model: Project
end
context 'when other data is modified' do
@@ -62,7 +95,9 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio
project.touch
end
- include_examples 'successful examples'
+ include_examples 'successful examples', model: Project
+
+ include_examples 'cross-database modification errors', model: Ci::Pipeline
end
context 'when both CI and other data is modified' do
@@ -144,7 +179,9 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModificatio
project.save!
end
- include_examples 'successful examples'
+ include_examples 'successful examples', model: Ci::Pipeline
+
+ include_examples 'cross-database modification errors', model: Project
end
describe '.allow_cross_database_modification_within_transaction' do
diff --git a/spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb b/spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb
index 0282a7af0df..6c32fb3ca17 100644
--- a/spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb
+++ b/spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
let(:env) { {} }
let(:logger) { Gitlab::Database::WithLockRetries::NULL_LOGGER }
- let(:subject) { described_class.new(env: env, logger: logger, timing_configuration: timing_configuration) }
+ let(:subject) { described_class.new(connection: connection, env: env, logger: logger, timing_configuration: timing_configuration) }
+ let(:connection) { ActiveRecord::Base.retrieve_connection }
let(:timing_configuration) do
[
@@ -67,7 +68,7 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
WHERE t.relkind = 'r' AND l.mode = 'ExclusiveLock' AND t.relname = '#{Project.table_name}'
"""
- expect(ActiveRecord::Base.connection.execute(check_exclusive_lock_query).to_a).to be_present
+ expect(connection.execute(check_exclusive_lock_query).to_a).to be_present
end
end
@@ -96,8 +97,8 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
lock_fiber.resume
end
- ActiveRecord::Base.transaction do
- ActiveRecord::Base.connection.execute("LOCK TABLE #{Project.table_name} in exclusive mode")
+ connection.transaction do
+ connection.execute("LOCK TABLE #{Project.table_name} in exclusive mode")
lock_acquired = true
end
end
@@ -115,7 +116,7 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
context 'setting the idle transaction timeout' do
context 'when there is no outer transaction: disable_ddl_transaction! is set in the migration' do
it 'does not disable the idle transaction timeout' do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false)
+ allow(connection).to receive(:transaction_open?).and_return(false)
allow(subject).to receive(:run_block_with_lock_timeout).once.and_raise(ActiveRecord::LockWaitTimeout)
allow(subject).to receive(:run_block_with_lock_timeout).once
@@ -127,7 +128,7 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
context 'when there is outer transaction: disable_ddl_transaction! is not set in the migration' do
it 'disables the idle transaction timeout so the code can sleep and retry' do
- allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(true)
+ allow(connection).to receive(:transaction_open?).and_return(true)
n = 0
allow(subject).to receive(:run_block_with_lock_timeout).twice do
@@ -184,8 +185,8 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
subject.run(raise_on_exhaustion: true) do
lock_attempts += 1
- ActiveRecord::Base.transaction do
- ActiveRecord::Base.connection.execute("LOCK TABLE #{Project.table_name} in exclusive mode")
+ connection.transaction do
+ connection.execute("LOCK TABLE #{Project.table_name} in exclusive mode")
lock_acquired = true
end
end
@@ -199,11 +200,11 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
context 'when statement timeout is reached' do
it 'raises StatementInvalid error' do
lock_acquired = false
- ActiveRecord::Base.connection.execute("SET statement_timeout='100ms'")
+ connection.execute("SET statement_timeout='100ms'")
expect do
subject.run do
- ActiveRecord::Base.connection.execute("SELECT 1 FROM pg_sleep(0.11)") # 110ms
+ connection.execute("SELECT 1 FROM pg_sleep(0.11)") # 110ms
lock_acquired = true
end
end.to raise_error(ActiveRecord::StatementInvalid)
@@ -216,11 +217,11 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
context 'restore local database variables' do
it do
- expect { subject.run {} }.not_to change { ActiveRecord::Base.connection.execute("SHOW lock_timeout").to_a }
+ expect { subject.run {} }.not_to change { connection.execute("SHOW lock_timeout").to_a }
end
it do
- expect { subject.run {} }.not_to change { ActiveRecord::Base.connection.execute("SHOW idle_in_transaction_session_timeout").to_a }
+ expect { subject.run {} }.not_to change { connection.execute("SHOW idle_in_transaction_session_timeout").to_a }
end
end
@@ -228,8 +229,8 @@ RSpec.describe Gitlab::Database::WithLockRetriesOutsideTransaction do
let(:timing_configuration) { [[0.015.seconds, 0.025.seconds], [0.015.seconds, 0.025.seconds]] } # 15ms, 25ms
it 'executes `SET lock_timeout` using the configured timeout value in milliseconds' do
- expect(ActiveRecord::Base.connection).to receive(:execute).with('RESET idle_in_transaction_session_timeout; RESET lock_timeout').and_call_original
- expect(ActiveRecord::Base.connection).to receive(:execute).with("SET lock_timeout TO '15ms'").and_call_original
+ expect(connection).to receive(:execute).with('RESET idle_in_transaction_session_timeout; RESET lock_timeout').and_call_original
+ expect(connection).to receive(:execute).with("SET lock_timeout TO '15ms'").and_call_original
subject.run { }
end
diff --git a/spec/lib/gitlab/database/with_lock_retries_spec.rb b/spec/lib/gitlab/database/with_lock_retries_spec.rb
index c2c818aa106..6b35ccafabc 100644
--- a/spec/lib/gitlab/database/with_lock_retries_spec.rb
+++ b/spec/lib/gitlab/database/with_lock_retries_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::WithLockRetries do
let(:env) { {} }
let(:logger) { Gitlab::Database::WithLockRetries::NULL_LOGGER }
- let(:subject) { described_class.new(env: env, logger: logger, allow_savepoints: allow_savepoints, timing_configuration: timing_configuration) }
+ let(:subject) { described_class.new(connection: connection, env: env, logger: logger, allow_savepoints: allow_savepoints, timing_configuration: timing_configuration) }
let(:allow_savepoints) { true }
let(:connection) { ActiveRecord::Base.retrieve_connection }
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 f5ea660ee1e..6601b6658d5 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
@@ -60,7 +60,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- application_setting.update(allow_local_requests_from_web_hooks_and_services: true)
+ application_setting.update!(allow_local_requests_from_web_hooks_and_services: true)
end
shared_examples 'has prometheus integration' do |server_address|
@@ -181,7 +181,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
let(:existing_project) { create(:project, namespace: existing_group) }
before do
- application_setting.update(instance_administrators_group_id: existing_group.id,
+ application_setting.update!(instance_administrators_group_id: existing_group.id,
self_monitoring_project_id: existing_project.id)
end
@@ -195,7 +195,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
context 'when local requests from hooks and integrations are not allowed' do
before do
- application_setting.update(allow_local_requests_from_web_hooks_and_services: false)
+ application_setting.update!(allow_local_requests_from_web_hooks_and_services: false)
end
it_behaves_like 'has prometheus integration', 'http://localhost:9090'
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 5ec7c338a2a..b3b7c81e9e7 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -104,6 +104,34 @@ RSpec.describe Gitlab::Database do
end
end
+ describe '.check_for_non_superuser' do
+ subject { described_class.check_for_non_superuser }
+
+ let(:non_superuser) { Gitlab::Database::PgUser.new(usename: 'foo', usesuper: false ) }
+ let(:superuser) { Gitlab::Database::PgUser.new(usename: 'bar', usesuper: true) }
+
+ it 'prints user details if not superuser' do
+ allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_return(non_superuser)
+
+ expect(Gitlab::AppLogger).to receive(:info).with("Account details: User: \"foo\", UseSuper: (false)")
+
+ subject
+ end
+
+ it 'raises an exception if superuser' do
+ allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_return(superuser)
+
+ expect(Gitlab::AppLogger).to receive(:info).with("Account details: User: \"bar\", UseSuper: (true)")
+ expect { subject }.to raise_error('Error: detected superuser')
+ end
+
+ it 'catches exception if find_by fails' do
+ allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_raise(ActiveRecord::StatementInvalid)
+
+ expect { subject }.to raise_error('User CURRENT_USER not found')
+ end
+ end
+
describe '.check_postgres_version_and_print_warning' do
let(:reflect) { instance_spy(Gitlab::Database::Reflection) }
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 45a49a36fe2..7c1a8f4c3c8 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::Diff::File do
def create_file(file_name, content)
Files::CreateService.new(
project,
- project.owner,
+ project.first_owner,
commit_message: 'Update',
start_branch: branch_name,
branch_name: branch_name,
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Diff::File do
def update_file(file_name, content)
Files::UpdateService.new(
project,
- project.owner,
+ project.first_owner,
commit_message: 'Update',
start_branch: branch_name,
branch_name: branch_name,
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::Diff::File do
def delete_file(file_name)
Files::DeleteService.new(
project,
- project.owner,
+ project.first_owner,
commit_message: 'Update',
start_branch: branch_name,
branch_name: branch_name,
diff --git a/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb b/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb
index 7dceb64b776..1414056ad6a 100644
--- a/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::Diff::PositionTracer::ImageStrategy do
include PositionTracerHelpers
let(:project) { create(:project, :repository) }
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
let(:file_name) { 'test-file' }
let(:new_file_name) { "#{file_name}-new" }
let(:second_file_name) { "#{file_name}-2" }
diff --git a/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb b/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
index c46f476899e..ea56a87dec2 100644
--- a/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Diff::PositionTracer::LineStrategy, :clean_gitlab_redis_c
include PositionTracerHelpers
let(:project) { create(:project, :repository) }
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
let(:repository) { project.repository }
let(:file_name) { "test-file" }
let(:new_file_name) { "#{file_name}-new" }
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index fc649812b0a..9b0ea892f91 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe Gitlab::Diff::PositionTracer do
describe 'diffs methods' do
let(:project) { create(:project, :repository) }
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
let(:old_diff_refs) do
diff_refs(
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index c0ac40e3249..59b87c5d8e7 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
include_context :email_shared_context
- let_it_be(:user) { create(:user) }
+ let_it_be(:user) { create(:user, email: 'jake@adventuretime.ooo') }
let_it_be(:project) { create(:project, :public, :repository) }
let(:noteable) { note.noteable }
@@ -39,6 +39,43 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
end
end
+ context 'when the incoming email is from a different email address' do
+ before do
+ SentNotification.find_by(reply_key: mail_key).update!(recipient: original_recipient)
+ end
+
+ context 'when the issue is not a Service Desk issue' do
+ let(:original_recipient) { create(:user, email: 'john@somethingelse.com') }
+
+ context 'with only one email address' do
+ it 'raises a UserNotFoundError' do
+ expect { receiver.execute }.to raise_error(Gitlab::Email::UserNotFoundError)
+ end
+ end
+
+ context 'with a secondary verified email address' do
+ let(:verified_email) { 'alan@adventuretime.ooo'}
+ let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub('jake@adventuretime.ooo', verified_email) }
+
+ before do
+ create(:email, :confirmed, user: original_recipient, email: verified_email)
+ end
+
+ it 'does not raise a UserNotFoundError' do
+ expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError)
+ end
+ end
+ end
+
+ context 'when the issue is a Service Desk issue' do
+ let(:original_recipient) { User.support_bot }
+
+ it 'does not raise a UserNotFoundError' do
+ expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError)
+ end
+ end
+ end
+
context 'when no sent notification for the mail key could be found' do
let(:email_raw) { fixture_file('emails/wrong_mail_key.eml') }
diff --git a/spec/lib/gitlab/endpoint_attributes_spec.rb b/spec/lib/gitlab/endpoint_attributes_spec.rb
index 4d4cfed57fa..53f5b302f05 100644
--- a/spec/lib/gitlab/endpoint_attributes_spec.rb
+++ b/spec/lib/gitlab/endpoint_attributes_spec.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require_relative "../../support/matchers/be_request_urgency"
-require_relative "../../../lib/gitlab/endpoint_attributes"
+require_relative '../../support/matchers/be_request_urgency'
+require_relative '../../../lib/gitlab/endpoint_attributes/config'
+require_relative '../../../lib/gitlab/endpoint_attributes'
RSpec.describe Gitlab::EndpointAttributes do
let(:base_controller) do
diff --git a/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb b/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb
index 0e72dd7ec5e..38745fe0cde 100644
--- a/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb
+++ b/spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'rspec-parameterized'
+require 'spec_helper'
RSpec.describe Gitlab::ErrorTracking::ContextPayloadGenerator do
subject(:generator) { described_class.new }
diff --git a/spec/lib/gitlab/error_tracking/log_formatter_spec.rb b/spec/lib/gitlab/error_tracking/log_formatter_spec.rb
index 188ccd000a1..15d201401f4 100644
--- a/spec/lib/gitlab/error_tracking/log_formatter_spec.rb
+++ b/spec/lib/gitlab/error_tracking/log_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::ErrorTracking::LogFormatter do
let(:exception) { StandardError.new('boom') }
diff --git a/spec/lib/gitlab/event_store/store_spec.rb b/spec/lib/gitlab/event_store/store_spec.rb
index 711e1d5b4d5..94e8f0ff2ff 100644
--- a/spec/lib/gitlab/event_store/store_spec.rb
+++ b/spec/lib/gitlab/event_store/store_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe Gitlab::EventStore::Store do
let(:worker) do
stub_const('EventSubscriber', Class.new).tap do |klass|
klass.class_eval do
- include ApplicationWorker
include Gitlab::EventStore::Subscriber
def handle_event(event)
@@ -23,7 +22,6 @@ RSpec.describe Gitlab::EventStore::Store do
let(:another_worker) do
stub_const('AnotherEventSubscriber', Class.new).tap do |klass|
klass.class_eval do
- include ApplicationWorker
include Gitlab::EventStore::Subscriber
end
end
@@ -32,7 +30,6 @@ RSpec.describe Gitlab::EventStore::Store do
let(:unrelated_worker) do
stub_const('UnrelatedEventSubscriber', Class.new).tap do |klass|
klass.class_eval do
- include ApplicationWorker
include Gitlab::EventStore::Subscriber
end
end
@@ -224,6 +221,26 @@ RSpec.describe Gitlab::EventStore::Store do
store.publish(event)
end
end
+
+ context 'when the event does not have any subscribers' do
+ let(:store) do
+ described_class.new do |s|
+ s.subscribe unrelated_worker, to: another_event_klass
+ end
+ end
+
+ let(:event) { event_klass.new(data: data) }
+
+ it 'returns successfully' do
+ expect { store.publish(event) }.not_to raise_error
+ end
+
+ it 'does not dispatch the event to another subscription' do
+ expect(unrelated_worker).not_to receive(:perform_async)
+
+ store.publish(event)
+ end
+ end
end
describe 'subscriber' do
@@ -233,6 +250,10 @@ RSpec.describe Gitlab::EventStore::Store do
subject { worker_instance.perform(event_name, data) }
+ it 'is a Sidekiq worker' do
+ expect(worker_instance).to be_a(ApplicationWorker)
+ end
+
it 'handles the event' do
expect(worker_instance).to receive(:handle_event).with(instance_of(event.class))
diff --git a/spec/lib/gitlab/experiment/rollout/feature_spec.rb b/spec/lib/gitlab/experiment/rollout/feature_spec.rb
new file mode 100644
index 00000000000..d73757be79b
--- /dev/null
+++ b/spec/lib/gitlab/experiment/rollout/feature_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment do
+ subject { described_class.new.for(subject_experiment) }
+
+ let(:subject_experiment) { experiment('namespaced/stub') }
+
+ describe "#enabled?" do
+ before do
+ allow(Feature::Definition).to receive(:get).and_return('_instance_')
+ allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
+ allow(Feature).to receive(:get).and_return(double(state: :on))
+ end
+
+ it "is enabled when all criteria are met" do
+ expect(subject).to be_enabled
+ end
+
+ it "isn't enabled if the feature definition doesn't exist" do
+ expect(Feature::Definition).to receive(:get).with('namespaced_stub').and_return(nil)
+
+ expect(subject).not_to be_enabled
+ end
+
+ it "isn't enabled if we're not in dev or dotcom environments" do
+ expect(Gitlab).to receive(:dev_env_or_com?).and_return(false)
+
+ expect(subject).not_to be_enabled
+ end
+
+ it "isn't enabled if the feature flag state is :off" do
+ expect(Feature).to receive(:get).with('namespaced_stub').and_return(double(state: :off))
+
+ expect(subject).not_to be_enabled
+ end
+ end
+
+ describe "#execute_assignment" do
+ before do
+ allow(Feature).to receive(:enabled?).with('namespaced_stub', any_args).and_return(true)
+ end
+
+ it "uses the default value as specified in the yaml" do
+ expect(Feature).to receive(:enabled?).with(
+ 'namespaced_stub',
+ subject,
+ type: :experiment,
+ default_enabled: :yaml
+ ).and_return(false)
+
+ expect(subject.execute_assignment).to be_nil
+ end
+
+ it "returns an assigned name" do
+ allow(subject).to receive(:behavior_names).and_return([:variant1, :variant2])
+
+ expect(subject.execute_assignment).to eq(:variant2)
+ end
+ end
+
+ describe "#flipper_id" do
+ it "returns the expected flipper id if the experiment doesn't provide one" do
+ subject.instance_variable_set(:@experiment, double(id: '__id__'))
+ expect(subject.flipper_id).to eq('Experiment;__id__')
+ end
+
+ it "lets the experiment provide a flipper id so it can override the default" do
+ allow(subject_experiment).to receive(:flipper_id).and_return('_my_overridden_id_')
+
+ expect(subject.flipper_id).to eq('_my_overridden_id_')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/feature_categories_spec.rb b/spec/lib/gitlab/feature_categories_spec.rb
index daced154a69..477da900d0a 100644
--- a/spec/lib/gitlab/feature_categories_spec.rb
+++ b/spec/lib/gitlab/feature_categories_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::FeatureCategories do
let(:fake_categories) { %w(foo bar) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index f1b6a59abf9..ae6ca728573 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -2252,44 +2252,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
- describe '#clean_stale_repository_files' do
- let(:worktree_id) { 'rebase-1' }
- let(:gitlab_worktree_path) { File.join(repository_path, 'gitlab-worktree', worktree_id) }
- let(:admin_dir) { File.join(repository_path, 'worktrees') }
-
- it 'cleans up the files' do
- create_worktree = %W[git -C #{repository_path} worktree add --detach #{gitlab_worktree_path} master]
- raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
-
- FileUtils.touch(gitlab_worktree_path, mtime: Time.now - 8.hours)
- # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
- # but the HEAD must be 40 characters long or git will ignore it.
- File.write(File.join(admin_dir, worktree_id, 'HEAD'), Gitlab::Git::BLANK_SHA)
-
- expect(rev_list_all).to be(false)
- repository.clean_stale_repository_files
-
- expect(rev_list_all).to be(true)
- expect(File.exist?(gitlab_worktree_path)).to be_falsey
- end
-
- def rev_list_all
- system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
- end
-
- it 'increments a counter upon an error' do
- expect(repository.gitaly_repository_client).to receive(:cleanup).and_raise(Gitlab::Git::CommandError)
-
- counter = double(:counter)
-
- expect(counter).to receive(:increment)
- expect(Gitlab::Metrics).to receive(:counter).with(:failed_repository_cleanup_total,
- 'Number of failed repository cleanup events').and_return(counter)
-
- repository.clean_stale_repository_files
- end
- end
-
describe '#squash' do
let(:branch_name) { 'fix' }
let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index eb7deb08063..ee0c0e2708e 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::Git::Wiki do
using RSpec::Parameterized::TableSyntax
let(:project) { create(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
subject(:wiki) { project_wiki.wiki }
diff --git a/spec/lib/gitlab/git_access_design_spec.rb b/spec/lib/gitlab/git_access_design_spec.rb
index 9fd1f2dcb0c..c90d9802300 100644
--- a/spec/lib/gitlab/git_access_design_spec.rb
+++ b/spec/lib/gitlab/git_access_design_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Gitlab::GitAccessDesign do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let(:protocol) { 'web' }
let(:actor) { user }
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 4bf7994f4dd..d6ef1836ad9 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1008,11 +1008,6 @@ RSpec.describe Gitlab::GitAccess do
end
end
- it 'cleans up the files' do
- expect(project.repository).to receive(:clean_stale_repository_files).and_call_original
- expect { push_access_check }.not_to raise_error
- end
-
it 'avoids N+1 queries', :request_store do
# Run this once to establish a baseline. Cached queries should get
# cached, so that when we introduce another change we shouldn't see
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 27e7d446770..f0115aa6b2b 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -2,6 +2,9 @@
require 'spec_helper'
+require 'google/rpc/status_pb'
+require 'google/protobuf/well_known_types'
+
RSpec.describe Gitlab::GitalyClient::OperationService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -185,11 +188,16 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
context 'with an exception with the UserMergeBranchError' do
let(:permission_error) do
- GRPC::PermissionDenied.new(
+ new_detailed_error(
+ GRPC::Core::StatusCodes::PERMISSION_DENIED,
"GitLab: You are not allowed to push code to this project.",
- { "grpc-status-details-bin" =>
- "\b\a\x129GitLab: You are not allowed to push code to this project.\x1A\xDE\x01\n/type.googleapis.com/gitaly.UserMergeBranchError\x12\xAA\x01\n\xA7\x01\n1You are not allowed to push code to this project.\x12\x03web\x1A\auser-15\"df15b32277d2c55c6c595845a87109b09c913c556 5d6e0f935ad9240655f64e883cd98fad6f9a17ee refs/heads/master\n" }
- )
+ Gitaly::UserMergeBranchError.new(
+ access_check: Gitaly::AccessCheckError.new(
+ error_message: "You are not allowed to push code to this project.",
+ protocol: "web",
+ user_id: "user-15",
+ changes: "df15b32277d2c55c6c595845a87109b09c913c556 5d6e0f935ad9240655f64e883cd98fad6f9a17ee refs/heads/master\n"
+ )))
end
it 'raises PreRecieveError with the error message' do
@@ -217,6 +225,27 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
expect { subject }.to raise_error(GRPC::PermissionDenied)
end
end
+
+ context 'with ReferenceUpdateError' do
+ let(:reference_update_error) do
+ new_detailed_error(GRPC::Core::StatusCodes::FAILED_PRECONDITION,
+ "some ignored error message",
+ Gitaly::UserMergeBranchError.new(
+ reference_update: Gitaly::ReferenceUpdateError.new(
+ reference_name: "refs/heads/something",
+ old_oid: "1234",
+ new_oid: "6789"
+ )))
+ end
+
+ it 'returns nil' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_merge_branch).with(kind_of(Enumerator), kind_of(Hash))
+ .and_raise(reference_update_error)
+
+ expect(subject).to be_nil
+ end
+ end
end
describe '#user_ff_branch' do
@@ -478,4 +507,14 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
end
end
end
+
+ def new_detailed_error(error_code, error_message, details)
+ status_error = Google::Rpc::Status.new(
+ code: error_code,
+ message: error_message,
+ details: [Google::Protobuf::Any.pack(details)]
+ )
+
+ GRPC::BadStatus.new(error_code, error_message, { "grpc-status-details-bin" => Google::Rpc::Status.encode(status_error) })
+ end
end
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index e5502a883b5..353726b56f6 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -21,16 +21,6 @@ RSpec.describe Gitlab::GitalyClient::RepositoryService do
end
end
- describe '#cleanup' do
- it 'sends a cleanup message' do
- expect_any_instance_of(Gitaly::RepositoryService::Stub)
- .to receive(:cleanup)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
-
- client.cleanup
- end
- end
-
describe '#garbage_collect' do
it 'sends a garbage_collect message' do
expect_any_instance_of(Gitaly::RepositoryService::Stub)
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 a0e78186caa..c8e744ab262 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
@@ -119,123 +119,80 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
.and_return(discussion_id)
end
- context 'when github_importer_use_diff_note_with_suggestions is disabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: false)
+ it_behaves_like 'diff notes without suggestion'
+
+ context 'when the note has suggestions' do
+ let(:note_body) do
+ <<~EOB
+ Suggestion:
+ ```suggestion
+ what do you think to do it like this
+ ```
+ EOB
end
- it_behaves_like 'diff notes without suggestion'
+ before do
+ stub_user_finder(user.id, true)
+ end
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
+ it 'imports the note as diff note' do
+ expect { subject.execute }
+ .to change(DiffNote, :count)
+ .by(1)
+
+ note = project.notes.diff_notes.take
+ expect(note).to be_valid
+ expect(note.noteable_type).to eq('MergeRequest')
+ expect(note.noteable_id).to eq(merge_request.id)
+ expect(note.project_id).to eq(project.id)
+ expect(note.author_id).to eq(user.id)
+ expect(note.system).to eq(false)
+ expect(note.discussion_id).to eq(discussion_id)
+ expect(note.commit_id).to eq('original123abc')
+ expect(note.line_code).to eq(note_representation.line_code)
+ expect(note.type).to eq('DiffNote')
+ expect(note.created_at).to eq(created_at)
+ expect(note.updated_at).to eq(updated_at)
+ expect(note.position.to_h).to eq({
+ base_sha: merge_request.diffs.diff_refs.base_sha,
+ head_sha: merge_request.diffs.diff_refs.head_sha,
+ start_sha: merge_request.diffs.diff_refs.start_sha,
+ new_line: 15,
+ old_line: nil,
+ new_path: file_path,
+ old_path: file_path,
+ position_type: 'text',
+ line_range: nil
+ })
+ expect(note.note)
+ .to eq <<~NOTE
Suggestion:
- ```suggestion
+ ```suggestion:-0+0
what do you think to do it like this
```
- EOB
- end
-
- it 'imports the note' do
- stub_user_finder(user.id, true)
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
- end
- end
-
- context 'when github_importer_use_diff_note_with_suggestions is enabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: true)
+ NOTE
end
- it_behaves_like 'diff notes without suggestion'
+ context 'when the note diff file creation fails' do
+ it 'falls back to the LegacyDiffNote' do
+ exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
- Suggestion:
- ```suggestion
- what do you think to do it like this
- ```
- EOB
- end
+ expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
- before do
- stub_user_finder(user.id, true)
- end
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:warn)
+ .with(
+ message: 'Failed to create diff note file',
+ 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ )
- it 'imports the note as diff note' do
expect { subject.execute }
- .to change(DiffNote, :count)
- .by(1)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.noteable_type).to eq('MergeRequest')
- expect(note.noteable_id).to eq(merge_request.id)
- expect(note.project_id).to eq(project.id)
- expect(note.author_id).to eq(user.id)
- expect(note.system).to eq(false)
- expect(note.discussion_id).to eq(discussion_id)
- expect(note.commit_id).to eq('original123abc')
- expect(note.line_code).to eq(note_representation.line_code)
- expect(note.type).to eq('DiffNote')
- expect(note.created_at).to eq(created_at)
- expect(note.updated_at).to eq(updated_at)
- expect(note.position.to_h).to eq({
- base_sha: merge_request.diffs.diff_refs.base_sha,
- head_sha: merge_request.diffs.diff_refs.head_sha,
- start_sha: merge_request.diffs.diff_refs.start_sha,
- new_line: 15,
- old_line: nil,
- new_path: file_path,
- old_path: file_path,
- position_type: 'text',
- line_range: nil
- })
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
-
- context 'when the note diff file creation fails' do
- it 'falls back to the LegacyDiffNote' do
- exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
-
- expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
- expect(service)
- .to receive(:execute)
- .and_raise(exception)
- end
-
- expect(Gitlab::GithubImport::Logger)
- .to receive(:warn)
- .with(
- message: 'Failed to create diff note file',
- 'error.class': 'DiffNote::NoteDiffFileCreationError'
- )
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
- end
+ .to change(LegacyDiffNote, :count)
+ .and not_change(DiffNote, :count)
end
end
end
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 1a25824bc8a..6b3d18f20e9 100644
--- a/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/releases_importer_spec.rb
@@ -52,6 +52,12 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
expect { importer.execute }.to change { Release.count }.by(1)
end
+
+ it 'is idempotent' do
+ allow(importer).to receive(:each_release).and_return([github_release])
+ expect { importer.execute }.to change { Release.count }.by(1)
+ expect { importer.execute }.to change { Release.count }.by(0) # Idempotency check
+ end
end
describe '#build_releases' do
@@ -79,6 +85,24 @@ RSpec.describe Gitlab::GithubImport::Importer::ReleasesImporter do
expect(release[:description]).to eq('Release for tag 1.0')
end
+
+ it 'does not create releases that have a NULL tag' do
+ null_tag_release = double(
+ name: 'NULL Test',
+ tag_name: nil
+ )
+
+ expect(importer).to receive(:each_release).and_return([null_tag_release])
+ expect(importer.build_releases).to be_empty
+ end
+
+ it 'does not create duplicate release tags' do
+ expect(importer).to receive(:each_release).and_return([github_release, github_release])
+
+ releases = importer.build_releases
+ expect(releases.length).to eq(1)
+ expect(releases[0][:description]).to eq('This is my release')
+ end
end
describe '#build' 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 58a8fb1b7e4..f2730ba74ec 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -264,8 +264,8 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
it 'sets the timestamp for when the cloning process finished' do
freeze_time do
expect(project)
- .to receive(:update_column)
- .with(:last_repository_updated_at, Time.zone.now)
+ .to receive(:touch)
+ .with(:last_repository_updated_at)
importer.update_clone_time
end
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 63834cfdb94..fe3040c102b 100644
--- a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_redis_shared_state do
+RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_redis_cache do
let(:hunk) do
'@@ -1 +1 @@
-Hello
@@ -166,6 +166,23 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
expect(new_discussion_note.discussion_id)
.to eq('SECOND_DISCUSSION_ID')
end
+
+ context 'when cached value does not exist' do
+ it 'falls back to generating a new discussion_id' do
+ expect(Discussion)
+ .to receive(:discussion_id)
+ .and_return('NEW_DISCUSSION_ID')
+
+ reply_note = described_class.from_json_hash(
+ 'note_id' => note.note_id + 1,
+ 'in_reply_to_id' => note.note_id
+ )
+ reply_note.project = project
+ reply_note.merge_request = merge_request
+
+ expect(reply_note.discussion_id).to eq('NEW_DISCUSSION_ID')
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/gl_repository/identifier_spec.rb b/spec/lib/gitlab/gl_repository/identifier_spec.rb
index e0622e30e7a..0a8559dd800 100644
--- a/spec/lib/gitlab/gl_repository/identifier_spec.rb
+++ b/spec/lib/gitlab/gl_repository/identifier_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe Gitlab::GlRepository::Identifier do
let_it_be(:project) { create(:project) }
- let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
- let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) }
describe 'project repository' do
it_behaves_like 'parsing gl_repository identifier' do
diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
index 71a4c693f9d..0ec94563cbb 100644
--- a/spec/lib/gitlab/gl_repository/repo_type_spec.rb
+++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
RSpec.describe Gitlab::GlRepository::RepoType do
let_it_be(:project) { create(:project) }
- let_it_be(:personal_snippet) { create(:personal_snippet, author: project.owner) }
- let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.owner) }
+ let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) }
+ let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) }
let(:project_path) { project.repository.full_path }
let(:wiki_path) { project.wiki.repository.full_path }
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
index b8ed4cf608d..047873d8237 100644
--- a/spec/lib/gitlab/gon_helper_spec.rb
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -6,9 +6,41 @@ RSpec.describe Gitlab::GonHelper do
let(:helper) do
Class.new do
include Gitlab::GonHelper
+
+ def current_user
+ nil
+ end
end.new
end
+ describe '#add_gon_variables' do
+ let(:gon) { double('gon').as_null_object }
+ let(:https) { true }
+
+ before do
+ allow(helper).to receive(:gon).and_return(gon)
+ stub_config_setting(https: https)
+ end
+
+ context 'when HTTPS is enabled' do
+ it 'sets the secure flag to true' do
+ expect(gon).to receive(:secure=).with(true)
+
+ helper.add_gon_variables
+ end
+ end
+
+ context 'when HTTP is enabled' do
+ let(:https) { false }
+
+ it 'sets the secure flag to false' do
+ expect(gon).to receive(:secure=).with(false)
+
+ helper.add_gon_variables
+ end
+ end
+ end
+
describe '#push_frontend_feature_flag' do
before do
skip_feature_flags_yaml_validation
diff --git a/spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb b/spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb
index 73e25f23848..274cc83a6be 100644
--- a/spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe ::Gitlab::Graphql::Authorize::ObjectAuthorization do
describe '#ok?' do
diff --git a/spec/lib/gitlab/graphql/batch_key_spec.rb b/spec/lib/gitlab/graphql/batch_key_spec.rb
index 7b73b27f24b..43e248885c2 100644
--- a/spec/lib/gitlab/graphql/batch_key_spec.rb
+++ b/spec/lib/gitlab/graphql/batch_key_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'test_prof/recipes/rspec/let_it_be'
RSpec.describe ::Gitlab::Graphql::BatchKey do
- let_it_be(:rect) { Struct.new(:len, :width) }
- let_it_be(:circle) { Struct.new(:radius) }
-
+ let(:rect) { Struct.new(:len, :width) }
+ let(:circle) { Struct.new(:radius) }
let(:lookahead) { nil }
let(:object) { rect.new(2, 3) }
diff --git a/spec/lib/gitlab/graphql/markdown_field_spec.rb b/spec/lib/gitlab/graphql/markdown_field_spec.rb
index a3fb0bbbed8..c2253811e91 100644
--- a/spec/lib/gitlab/graphql/markdown_field_spec.rb
+++ b/spec/lib/gitlab/graphql/markdown_field_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe Gitlab::Graphql::MarkdownField do
end
it 'shows the reference to users that are allowed to see it' do
- context = GraphQL::Query::Context.new(query: query, values: { current_user: project.owner }, object: nil)
+ context = GraphQL::Query::Context.new(query: query, values: { current_user: project.first_owner }, object: nil)
type_instance = type_class.authorized_new(note, context)
expect(field.to_graphql.resolve(type_instance, {}, context)).to include(issue_path(issue))
diff --git a/spec/lib/gitlab/graphql/queries_spec.rb b/spec/lib/gitlab/graphql/queries_spec.rb
index 8b7f4ca7933..ad1aaac712e 100644
--- a/spec/lib/gitlab/graphql/queries_spec.rb
+++ b/spec/lib/gitlab/graphql/queries_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
-require 'fast_spec_helper'
-require "test_prof/recipes/rspec/let_it_be"
RSpec.describe Gitlab::Graphql::Queries do
shared_examples 'a valid GraphQL query for the blog schema' do
diff --git a/spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb b/spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb
index 6eff816b95a..264fe993197 100644
--- a/spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb
+++ b/spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require "fast_spec_helper"
-require "support/graphql/fake_tracer"
-require "support/graphql/fake_query_type"
+
+require 'spec_helper'
RSpec.describe Gitlab::Graphql::Tracers::ApplicationContextTracer do
let(:tracer_spy) { spy('tracer_spy') }
diff --git a/spec/lib/gitlab/hook_data/project_builder_spec.rb b/spec/lib/gitlab/hook_data/project_builder_spec.rb
index 672dbab918f..e86ac66b1ad 100644
--- a/spec/lib/gitlab/hook_data/project_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/project_builder_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::HookData::ProjectBuilder do
let(:attributes) do
[
:event_name, :created_at, :updated_at, :name, :path, :path_with_namespace, :project_id,
- :owner_name, :owner_email, :project_visibility
+ :owners, :owner_name, :owner_email, :project_visibility
]
end
@@ -30,6 +30,7 @@ RSpec.describe Gitlab::HookData::ProjectBuilder do
expect(data[:project_id]).to eq(project.id)
expect(data[:owner_name]).to eq('John')
expect(data[:owner_email]).to eq('john@example.com')
+ expect(data[:owners]).to contain_exactly({ name: 'John', email: 'john@example.com' })
expect(data[:project_visibility]).to eq('internal')
end
end
diff --git a/spec/lib/gitlab/http_connection_adapter_spec.rb b/spec/lib/gitlab/http_connection_adapter_spec.rb
index 7c57d162e9b..e9e517f1fe6 100644
--- a/spec/lib/gitlab/http_connection_adapter_spec.rb
+++ b/spec/lib/gitlab/http_connection_adapter_spec.rb
@@ -15,11 +15,33 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
stub_all_dns('https://example.org', ip_address: '93.184.216.34')
end
+ context 'with use_read_total_timeout option' do
+ let(:options) { { use_read_total_timeout: true } }
+
+ it 'sets up the connection using the Gitlab::NetHttpAdapter' do
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
+ expect(connection.address).to eq('93.184.216.34')
+ expect(connection.hostname_override).to eq('example.org')
+ expect(connection.addr_port).to eq('example.org')
+ expect(connection.port).to eq(443)
+ end
+ end
+
+ context 'with header_read_timeout_buffered_io feature disabled' do
+ before do
+ stub_feature_flags(header_read_timeout_buffered_io: false)
+ end
+
+ it 'uses the regular Net::HTTP class' do
+ expect(connection).to be_a(Net::HTTP)
+ end
+ end
+
context 'when local requests are allowed' do
let(:options) { { allow_local_requests: true } }
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('93.184.216.34')
expect(connection.hostname_override).to eq('example.org')
expect(connection.addr_port).to eq('example.org')
@@ -31,7 +53,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
let(:options) { { allow_local_requests: false } }
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('93.184.216.34')
expect(connection.hostname_override).to eq('example.org')
expect(connection.addr_port).to eq('example.org')
@@ -52,7 +74,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
let(:options) { { allow_local_requests: true } }
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('172.16.0.0')
expect(connection.hostname_override).to be(nil)
expect(connection.addr_port).to eq('172.16.0.0')
@@ -75,7 +97,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
let(:options) { { allow_local_requests: true } }
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('127.0.0.1')
expect(connection.hostname_override).to be(nil)
expect(connection.addr_port).to eq('127.0.0.1')
@@ -88,7 +110,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
let(:uri) { URI('https://example.org:8080') }
it 'sets up the addr_port accordingly' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('93.184.216.34')
expect(connection.hostname_override).to eq('example.org')
expect(connection.addr_port).to eq('example.org:8080')
@@ -103,7 +125,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
end
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('example.org')
expect(connection.hostname_override).to eq(nil)
expect(connection.addr_port).to eq('example.org')
@@ -117,7 +139,7 @@ RSpec.describe Gitlab::HTTPConnectionAdapter do
end
it 'sets up the connection' do
- expect(connection).to be_a(Net::HTTP)
+ expect(connection).to be_a(Gitlab::NetHttpAdapter)
expect(connection.address).to eq('example.org')
expect(connection.hostname_override).to eq(nil)
expect(connection.addr_port).to eq('example.org')
diff --git a/spec/lib/gitlab/http_spec.rb b/spec/lib/gitlab/http_spec.rb
index 7d459f2d88a..7dbd21e6914 100644
--- a/spec/lib/gitlab/http_spec.rb
+++ b/spec/lib/gitlab/http_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::HTTP do
end
context 'when reading the response is too slow' do
- before do
+ before(:all) do
# Override Net::HTTP to add a delay between sending each response chunk
mocked_http = Class.new(Net::HTTP) do
def request(*)
@@ -51,8 +51,17 @@ RSpec.describe Gitlab::HTTP do
end
@original_net_http = Net.send(:remove_const, :HTTP)
+ @webmock_net_http = WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get('@webMockNetHTTP')
+
Net.send(:const_set, :HTTP, mocked_http)
+ WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_set('@webMockNetHTTP', mocked_http)
+
+ # Reload Gitlab::NetHttpAdapter
+ Gitlab.send(:remove_const, :NetHttpAdapter)
+ load "#{Rails.root}/lib/gitlab/net_http_adapter.rb"
+ end
+ before do
stub_const("#{described_class}::DEFAULT_READ_TOTAL_TIMEOUT", 0.001.seconds)
WebMock.stub_request(:post, /.*/).to_return do |request|
@@ -60,9 +69,14 @@ RSpec.describe Gitlab::HTTP do
end
end
- after do
+ after(:all) do
Net.send(:remove_const, :HTTP)
Net.send(:const_set, :HTTP, @original_net_http)
+ WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_set('@webMockNetHTTP', @webmock_net_http)
+
+ # Reload Gitlab::NetHttpAdapter
+ Gitlab.send(:remove_const, :NetHttpAdapter)
+ load "#{Rails.root}/lib/gitlab/net_http_adapter.rb"
end
let(:options) { {} }
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index f4a112d35aa..ce13f405459 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -529,6 +529,7 @@ project:
- vulnerability_feedback
- vulnerability_identifiers
- vulnerability_scanners
+- dast_profiles
- dast_site_profiles
- dast_scanner_profiles
- dast_sites
@@ -605,6 +606,7 @@ project:
- ci_project_mirror
- sync_events
- secure_files
+- security_trainings
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/command_line_util_spec.rb b/spec/lib/gitlab/import_export/command_line_util_spec.rb
index 31512259bb1..738a76d3360 100644
--- a/spec/lib/gitlab/import_export/command_line_util_spec.rb
+++ b/spec/lib/gitlab/import_export/command_line_util_spec.rb
@@ -97,7 +97,7 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
include Gitlab::ImportExport::CommandLineUtil
end.new
- expect { klass.tar_cf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'System call failed')
+ expect { klass.tar_cf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'command exited with error code 1: Error')
end
end
end
@@ -125,14 +125,31 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do
end
context 'when something goes wrong' do
- it 'raises an error' do
+ before do
expect(Gitlab::Popen).to receive(:popen).and_return(['Error', 1])
+ end
+ it 'raises an error' do
klass = Class.new do
include Gitlab::ImportExport::CommandLineUtil
end.new
- expect { klass.untar_xf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'System call failed')
+ expect { klass.untar_xf(archive: 'test', dir: 'test') }.to raise_error(Gitlab::ImportExport::Error, 'command exited with error code 1: Error')
+ end
+
+ it 'returns false and includes error status' do
+ klass = Class.new do
+ include Gitlab::ImportExport::CommandLineUtil
+
+ attr_accessor :shared
+
+ def initialize
+ @shared = Gitlab::ImportExport::Shared.new(nil)
+ end
+ end.new
+
+ expect(klass.tar_czf(archive: 'test', dir: 'test')).to eq(false)
+ expect(klass.shared.errors).to eq(['command exited with error code 1: Error'])
end
end
end
diff --git a/spec/lib/gitlab/import_export/config_spec.rb b/spec/lib/gitlab/import_export/config_spec.rb
index 7ad5d3d846c..fcb48678b88 100644
--- a/spec/lib/gitlab/import_export/config_spec.rb
+++ b/spec/lib/gitlab/import_export/config_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'rspec-parameterized'
+require 'spec_helper'
RSpec.describe Gitlab::ImportExport::Config do
let(:yaml_file) { described_class.new }
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 adb613c3abc..ce888b71d5e 100644
--- a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
@@ -240,7 +240,7 @@ RSpec.describe Gitlab::ImportExport::FastHashSerializer do
merge_request = create(:merge_request, source_project: project, milestone: milestone)
ci_build = create(:ci_build, project: project, when: nil)
- ci_build.pipeline.update(project: project)
+ ci_build.pipeline.update!(project: project)
create(:commit_status, project: project, pipeline: ci_build.pipeline)
create_list(:ci_pipeline, 5, :success, project: project)
diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb
index 65c28a8b8a2..25c82588c13 100644
--- a/spec/lib/gitlab/import_export/fork_spec.rb
+++ b/spec/lib/gitlab/import_export/fork_spec.rb
@@ -38,8 +38,8 @@ RSpec.describe 'forked project import' do
allow(instance).to receive(:storage_path).and_return(export_path)
end
- saver.save
- repo_saver.save
+ saver.save # rubocop:disable Rails/SaveBang
+ repo_saver.save # rubocop:disable Rails/SaveBang
repo_restorer.restore
restorer.restore
diff --git a/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb b/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
index e075c5acfea..31d647f883a 100644
--- a/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
# ^ These are specific for the Group::LegacyTreeSaver
context 'JSON' do
let(:saved_group_json) do
- group_tree_saver.save
+ group_tree_saver.save # rubocop:disable Rails/SaveBang
group_json(group_tree_saver.full_path)
end
@@ -88,7 +88,7 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
end
before do
- user2.update(public_email: user2.email)
+ user2.update!(public_email: user2.email)
group.add_developer(user2)
end
diff --git a/spec/lib/gitlab/import_export/group/relation_factory_spec.rb b/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
index 63286fc0719..8e7fe8849d4 100644
--- a/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::ImportExport::Group::RelationFactory do
let(:importer_user) { admin }
let(:excluded_keys) { [] }
let(:created_object) do
- described_class.create(
+ described_class.create( # rubocop:disable Rails/SaveBang
relation_sym: relation_sym,
relation_hash: relation_hash,
relation_index: 1,
diff --git a/spec/lib/gitlab/import_export/group/tree_saver_spec.rb b/spec/lib/gitlab/import_export/group/tree_saver_spec.rb
index c52daa8ccfd..de4d193a21c 100644
--- a/spec/lib/gitlab/import_export/group/tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/group/tree_saver_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::ImportExport::Group::TreeSaver do
context 'exported files' do
before do
- group_tree_saver.save
+ group_tree_saver.save # rubocop:disable Rails/SaveBang
end
it 'has one group per line' do
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index 20f0f6af6f3..c9d559c992c 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Gitlab::ImportExport::Importer do
stub_uploads_object_storage(FileUploader)
FileUtils.mkdir_p(shared.export_path)
- ImportExportUpload.create(project: project, import_file: import_file)
+ ImportExportUpload.create!(project: project, import_file: import_file)
allow(FileUtils).to receive(:rm_rf).and_call_original
end
diff --git a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
index d69d775fffb..352af18c822 100644
--- a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
@@ -183,24 +183,8 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
end
describe '.batch_size' do
- context 'when export_reduce_relation_batch_size feature flag is enabled' do
- before do
- stub_feature_flags(export_reduce_relation_batch_size: true)
- end
-
- it 'returns 20' do
- expect(described_class.batch_size(exportable)).to eq(described_class::SMALLER_BATCH_SIZE)
- end
- end
-
- context 'when export_reduce_relation_batch_size feature flag is disabled' do
- before do
- stub_feature_flags(export_reduce_relation_batch_size: false)
- end
-
- it 'returns default batch size' do
- expect(described_class.batch_size(exportable)).to eq(described_class::BATCH_SIZE)
- end
+ it 'returns default batch size' do
+ expect(described_class.batch_size(exportable)).to eq(described_class::BATCH_SIZE)
end
end
end
diff --git a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
index 3b7ed7cb32b..0d372def8b0 100644
--- a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
@@ -8,35 +8,17 @@ RSpec.describe Gitlab::ImportExport::LegacyRelationTreeSaver do
let(:tree) { {} }
describe '#serialize' do
- shared_examples 'FastHashSerializer with batch size' do |batch_size|
- let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
+ let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
- it 'uses FastHashSerializer' do
- expect(Gitlab::ImportExport::FastHashSerializer)
- .to receive(:new)
- .with(exportable, tree, batch_size: batch_size)
- .and_return(serializer)
+ it 'uses FastHashSerializer' do
+ expect(Gitlab::ImportExport::FastHashSerializer)
+ .to receive(:new)
+ .with(exportable, tree, batch_size: Gitlab::ImportExport::Json::StreamingSerializer::BATCH_SIZE)
+ .and_return(serializer)
- expect(serializer).to receive(:execute)
+ expect(serializer).to receive(:execute)
- relation_tree_saver.serialize(exportable, tree)
- end
- end
-
- context 'when export_reduce_relation_batch_size feature flag is enabled' do
- before do
- stub_feature_flags(export_reduce_relation_batch_size: true)
- end
-
- include_examples 'FastHashSerializer with batch size', Gitlab::ImportExport::Json::StreamingSerializer::SMALLER_BATCH_SIZE
- end
-
- context 'when export_reduce_relation_batch_size feature flag is disabled' do
- before do
- stub_feature_flags(export_reduce_relation_batch_size: false)
- end
-
- include_examples 'FastHashSerializer with batch size', Gitlab::ImportExport::Json::StreamingSerializer::BATCH_SIZE
+ relation_tree_saver.serialize(exportable, tree)
end
end
end
diff --git a/spec/lib/gitlab/import_export/lfs_restorer_spec.rb b/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
index c8887b0ded1..fe064c50b9e 100644
--- a/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Gitlab::ImportExport::LfsRestorer do
)
end
- saver.save
+ saver.save # rubocop:disable Rails/SaveBang
project.lfs_objects.delete_all
end
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::ImportExport::LfsRestorer do
context 'when there is not an existing `LfsObject`' do
before do
- lfs_object.destroy
+ lfs_object.destroy!
end
it 'creates a new lfs object' do
diff --git a/spec/lib/gitlab/import_export/lfs_saver_spec.rb b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
index 55b4f7479b8..84bd782c467 100644
--- a/spec/lib/gitlab/import_export/lfs_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
@@ -34,13 +34,13 @@ RSpec.describe Gitlab::ImportExport::LfsSaver do
end
it 'does not cause errors' do
- saver.save
+ saver.save # rubocop:disable Rails/SaveBang
expect(shared.errors).to be_empty
end
it 'copies the file in the correct location when there is an lfs object' do
- saver.save
+ saver.save # rubocop:disable Rails/SaveBang
expect(File).to exist("#{shared.export_path}/lfs-objects/#{lfs_object.oid}")
end
@@ -61,7 +61,7 @@ RSpec.describe Gitlab::ImportExport::LfsSaver do
end
it 'saves a json file correctly' do
- saver.save
+ saver.save # rubocop:disable Rails/SaveBang
expect(File.exist?(lfs_json_file)).to eq(true)
expect(lfs_json).to eq(
@@ -96,7 +96,7 @@ RSpec.describe Gitlab::ImportExport::LfsSaver do
expect(fake_uri).to receive(:open).and_return(StringIO.new('LFS file content'))
expect(URI).to receive(:parse).with('http://my-object-storage.local').and_return(fake_uri)
- saver.save
+ saver.save # rubocop:disable Rails/SaveBang
expect(File.read(exported_file_path)).to eq('LFS file content')
end
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 8b9ca90a280..8d9bff9c610 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -243,7 +243,6 @@ RSpec.describe Gitlab::ImportExport::MembersMapper do
before do
group.add_users([user, user2], GroupMember::DEVELOPER)
- user.update(public_email: 'invite@test.com')
end
it 'maps the importer' do
diff --git a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb
index ea8b10675af..ffbbf9326ec 100644
--- a/spec/lib/gitlab/import_export/project/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/project/relation_factory_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationFactory, :use_clean_rails_
let(:importer_user) { admin }
let(:excluded_keys) { [] }
let(:created_object) do
- described_class.create(
+ described_class.create( # rubocop:disable Rails/SaveBang
relation_sym: relation_sym,
relation_hash: relation_hash,
relation_index: 1,
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 1d8b137c196..8884722254d 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -880,7 +880,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
before do
group = create(:group, visibility_level: group_visibility)
group.add_users([user], GroupMember::MAINTAINER)
- project.update(group: group)
+ project.update!(group: group)
end
context 'private group visibility' do
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 f68ec21039d..ba781ae78b7 100644
--- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
project_tree_saver = described_class.new(project: project, current_user: user, shared: shared)
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
end
@@ -305,14 +305,14 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
end
before do
- user2.update(public_email: user2.email)
+ user2.update!(public_email: user2.email)
group.add_developer(user2)
end
context 'when has no permission' do
before do
group.add_developer(user)
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
it 'does not export group members' do
@@ -324,7 +324,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
before do
group.add_maintainer(user)
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
it 'does not export group members' do
@@ -336,7 +336,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
before do
group.add_owner(user)
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
it 'exports group members as group owner' do
@@ -348,7 +348,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
let(:user) { create(:admin) }
before do
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
context 'when admin mode is enabled', :enable_admin_mode do
@@ -376,7 +376,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
let(:relation_name) { :projects }
before do
- project_tree_saver.save
+ project_tree_saver.save # rubocop:disable Rails/SaveBang
end
it { is_expected.to include({ 'description' => params[:description] }) }
@@ -471,7 +471,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
merge_request = create(:merge_request, source_project: project, milestone: milestone)
ci_build = create(:ci_build, project: project, when: nil)
- ci_build.pipeline.update(project: project)
+ ci_build.pipeline.update!(project: project)
create(:commit_status, project: project, pipeline: ci_build.pipeline)
create(:milestone, project: project)
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index 718a23f80a1..c0215ff5843 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
before do
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
end
after do
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
index 877474dd862..f5eed81f73c 100644
--- a/spec/lib/gitlab/import_export/saver_spec.rb
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -30,19 +30,63 @@ RSpec.describe Gitlab::ImportExport::Saver do
it 'saves the repo using object storage' do
stub_uploads_object_storage(ImportExportUploader)
- subject.save
+ subject.save # rubocop:disable Rails/SaveBang
expect(ImportExportUpload.find_by(project: project).export_file.url)
.to match(%r[\/uploads\/-\/system\/import_export_upload\/export_file.*])
end
+ it 'logs metrics after saving' do
+ stub_uploads_object_storage(ImportExportUploader)
+ expect(Gitlab::Export::Logger).to receive(:info).with(
+ hash_including(
+ message: 'Export archive saved',
+ exportable_class: 'Project',
+ 'correlation_id' => anything,
+ archive_file: anything,
+ compress_duration_s: anything
+ )).and_call_original
+
+ expect(Gitlab::Export::Logger).to receive(:info).with(
+ hash_including(
+ message: 'Export archive uploaded',
+ exportable_class: 'Project',
+ 'correlation_id' => anything,
+ archive_file: anything,
+ compress_duration_s: anything,
+ assign_duration_s: anything,
+ upload_duration_s: anything,
+ upload_bytes: anything
+ )).and_call_original
+
+ subject.save # rubocop:disable Rails/SaveBang
+ end
+
it 'removes archive path and keeps base path untouched' do
allow(shared).to receive(:archive_path).and_return(archive_path)
- subject.save
+ subject.save # rubocop:disable Rails/SaveBang
expect(FileUtils).not_to have_received(:rm_rf).with(base_path)
expect(FileUtils).to have_received(:rm_rf).with(archive_path)
expect(Dir.exist?(archive_path)).to eq(false)
end
+
+ context 'when save throws an exception' do
+ before do
+ expect(subject).to receive(:save_upload).and_raise(SocketError.new)
+ end
+
+ it 'logs a saver error' do
+ allow(Gitlab::Export::Logger).to receive(:info).with(anything).and_call_original
+ expect(Gitlab::Export::Logger).to receive(:info).with(
+ hash_including(
+ message: 'Export archive saver failed',
+ exportable_class: 'Project',
+ 'correlation_id' => anything
+ )).and_call_original
+
+ subject.save # rubocop:disable Rails/SaveBang
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb
index 7d719b6028f..2f39cb560d0 100644
--- a/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe Gitlab::ImportExport::SnippetRepoRestorer do
let!(:snippet_with_repo) { create(:project_snippet, :repository, project: project, author: user) }
let(:bundle_path) { ::Gitlab::ImportExport.snippets_repo_bundle_path(shared.export_path) }
let(:snippet_bundle_path) { File.join(bundle_path, "#{snippet_with_repo.hexdigest}.bundle") }
- let(:result) { exporter.save }
+ let(:result) { exporter.save } # rubocop:disable Rails/SaveBang
let(:repository) { snippet.repository }
before do
diff --git a/spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb b/spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb
index 9f3e8d2fa86..9a9f40b3d0c 100644
--- a/spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Gitlab::ImportExport::SnippetRepoSaver do
aggregate_failures do
expect(snippet.repository).not_to receive(:bundle_to_disk)
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
expect(Dir.empty?(bundle_path)).to be_truthy
end
diff --git a/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb b/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb
index 7ca365762b5..e529d36fd11 100644
--- a/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/snippets_repo_restorer_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::ImportExport::SnippetsRepoRestorer do
let!(:snippet2) { create(:project_snippet, project: project, author: user) }
before do
- exporter.save
+ exporter.save # rubocop:disable Rails/SaveBang
expect(File.exist?(bundle_path(snippet1))).to be true
expect(File.exist?(bundle_path(snippet2))).to be false
@@ -78,7 +78,7 @@ RSpec.describe Gitlab::ImportExport::SnippetsRepoRestorer do
let!(:snippet2) { create(:project_snippet, :repository, project: project, author: user) }
before do
- exporter.save
+ exporter.save # rubocop:disable Rails/SaveBang
expect(File.exist?(bundle_path(snippet1))).to be true
expect(File.exist?(bundle_path(snippet2))).to be true
diff --git a/spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb b/spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb
index aa284c60e73..eaa58c77aff 100644
--- a/spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::ImportExport::SnippetsRepoSaver do
snippets_dir = ::Gitlab::ImportExport.snippets_repo_bundle_path(shared.export_path)
expect(Dir.exist?(snippets_dir)).to be_falsey
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
expect(Dir.exist?(snippets_dir)).to be_truthy
end
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::ImportExport::SnippetsRepoSaver do
it 'does not perform any action' do
expect(Gitlab::ImportExport::SnippetRepoSaver).not_to receive(:new)
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
end
end
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::ImportExport::SnippetsRepoSaver do
allow(Gitlab::ImportExport::SnippetRepoSaver).to receive(:new).and_return(service)
expect(service).to receive(:save).and_return(true).twice
- bundler.save
+ bundler.save # rubocop:disable Rails/SaveBang
end
context 'when one snippet cannot be saved' do
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
index 8282ad9a070..0cfe3a69a09 100644
--- a/spec/lib/gitlab/import_export/uploads_manager_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -31,13 +31,13 @@ RSpec.describe Gitlab::ImportExport::UploadsManager do
let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) }
it 'does not cause errors' do
- manager.save
+ manager.save # rubocop:disable Rails/SaveBang
expect(shared.errors).to be_empty
end
it 'copies the file in the correct location when there is an upload' do
- manager.save
+ manager.save # rubocop:disable Rails/SaveBang
expect(File).to exist(exported_file_path)
end
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::ImportExport::UploadsManager do
end
it 'excludes orphaned upload files' do
- manager.save
+ manager.save # rubocop:disable Rails/SaveBang
expect(File).not_to exist(exported_orphan_path)
end
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::ImportExport::UploadsManager do
end
it 'does not cause errors' do
- manager.save
+ manager.save # rubocop:disable Rails/SaveBang
expect(shared.errors).to be_empty
end
@@ -84,7 +84,7 @@ RSpec.describe Gitlab::ImportExport::UploadsManager do
it 'ignores problematic upload and logs exception' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(instance_of(Errno::ENAMETOOLONG), project_id: project.id)
- manager.save
+ manager.save # rubocop:disable Rails/SaveBang
expect(shared.errors).to be_empty
expect(File).not_to exist(exported_file_path)
diff --git a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
index 535cce6aa04..c5288b9afbc 100644
--- a/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
+++ b/spec/lib/gitlab/incident_management/pager_duty/incident_issue_description_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription do
describe '#to_s' do
diff --git a/spec/lib/gitlab/json_spec.rb b/spec/lib/gitlab/json_spec.rb
index f9f57752b0a..5ffe736da54 100644
--- a/spec/lib/gitlab/json_spec.rb
+++ b/spec/lib/gitlab/json_spec.rb
@@ -317,36 +317,14 @@ RSpec.describe Gitlab::Json do
let(:env) { {} }
let(:result) { "{\"test\":true}" }
- context "grape_gitlab_json flag is enabled" do
- before do
- stub_feature_flags(grape_gitlab_json: true)
- end
-
- it "generates JSON" do
- expect(subject).to eq(result)
- end
-
- it "uses Gitlab::Json" do
- expect(Gitlab::Json).to receive(:dump).with(obj)
-
- subject
- end
+ it "generates JSON" do
+ expect(subject).to eq(result)
end
- context "grape_gitlab_json flag is disabled" do
- before do
- stub_feature_flags(grape_gitlab_json: false)
- end
+ it "uses Gitlab::Json" do
+ expect(Gitlab::Json).to receive(:dump).with(obj)
- it "generates JSON" do
- expect(subject).to eq(result)
- end
-
- it "uses Grape::Formatter::Json" do
- expect(Grape::Formatter::Json).to receive(:call).with(obj, env)
-
- subject
- end
+ subject
end
context "precompiled JSON" do
@@ -440,15 +418,5 @@ RSpec.describe Gitlab::Json do
expect(subject.size).to eq(10001)
end
end
-
- context 'when json_limited_encoder is disabled' do
- let(:obj) { [{ test: true }] * 1000 }
-
- it 'does not raise an error' do
- stub_feature_flags(json_limited_encoder: false)
-
- expect { subject }.not_to raise_error
- end
- end
end
end
diff --git a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
index 2e373613269..3028e0a13aa 100644
--- a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
@@ -2,6 +2,8 @@
require 'fast_spec_helper'
+require_relative '../../../../lib/gitlab/kubernetes/pod_cmd'
+
RSpec.describe Gitlab::Kubernetes::KubectlCmd do
describe '.delete' do
it 'constructs string properly' do
diff --git a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
index 73b35d3a4e7..cbd1a30c417 100644
--- a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
@@ -63,5 +63,13 @@ RSpec.describe Gitlab::LegacyGithubImport::ReleaseFormatter do
expect(release.valid?).to eq false
end
end
+
+ context 'when release has NULL tag' do
+ let(:raw_data) { double(base_data.merge(tag_name: '')) }
+
+ it 'returns false' do
+ expect(release.valid?).to eq false
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/boot_time_tracker_spec.rb b/spec/lib/gitlab/metrics/boot_time_tracker_spec.rb
new file mode 100644
index 00000000000..8a17fa8dd2e
--- /dev/null
+++ b/spec/lib/gitlab/metrics/boot_time_tracker_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Metrics::BootTimeTracker do
+ let(:logger) { double('logger') }
+ let(:gauge) { double('gauge') }
+
+ subject(:tracker) { described_class.instance }
+
+ before do
+ described_class.instance.reset!
+
+ allow(logger).to receive(:info)
+ allow(gauge).to receive(:set)
+ allow(Gitlab::Metrics).to receive(:gauge).and_return(gauge)
+ end
+
+ describe '#track_boot_time!' do
+ described_class::SUPPORTED_RUNTIMES.each do |runtime|
+ context "when called on #{runtime} for the first time" do
+ before do
+ expect(Gitlab::Runtime).to receive(:safe_identify).and_return(runtime)
+ end
+
+ it 'set the startup_time' do
+ tracker.track_boot_time!(logger: logger)
+
+ expect(tracker.startup_time).to be > 0
+ end
+
+ it 'records the current process runtime' do
+ expect(Gitlab::Metrics::System).to receive(:process_runtime_elapsed_seconds).once
+
+ tracker.track_boot_time!(logger: logger)
+ end
+
+ it 'logs the application boot time' do
+ expect(Gitlab::Metrics::System).to receive(:process_runtime_elapsed_seconds).and_return(42)
+ expect(logger).to receive(:info).with(message: 'Application boot finished', runtime: runtime.to_s, duration_s: 42)
+
+ tracker.track_boot_time!(logger: logger)
+ end
+
+ it 'tracks boot time in a prometheus gauge' do
+ expect(Gitlab::Metrics::System).to receive(:process_runtime_elapsed_seconds).and_return(42)
+ expect(gauge).to receive(:set).with({}, 42)
+
+ tracker.track_boot_time!(logger: logger)
+ end
+
+ context 'on subsequent calls' do
+ it 'does nothing' do
+ tracker.track_boot_time!(logger: logger)
+
+ expect(Gitlab::Metrics::System).not_to receive(:process_runtime_elapsed_seconds)
+ expect(logger).not_to receive(:info)
+ expect(gauge).not_to receive(:set)
+
+ tracker.track_boot_time!(logger: logger)
+ end
+ end
+ end
+ end
+
+ context 'when called on other runtimes' do
+ it 'does nothing' do
+ tracker.track_boot_time!(logger: logger)
+
+ expect(Gitlab::Metrics::System).not_to receive(:process_runtime_elapsed_seconds)
+ expect(logger).not_to receive(:info)
+ expect(gauge).not_to receive(:set)
+
+ tracker.track_boot_time!(logger: logger)
+ end
+ end
+ end
+
+ describe '#startup_time' do
+ it 'returns 0 when boot time not tracked' do
+ expect(tracker.startup_time).to eq(0)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb
deleted file mode 100644
index 0531bccf4b4..00000000000
--- a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Metrics::Exporter::WebExporter do
- let(:exporter) { described_class.new }
- 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', :prometheus 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
-
- it 'initializes request metrics' do
- expect(Gitlab::Metrics::RailsSlis).to receive(:initialize_request_slis_if_needed!).and_call_original
-
- http = Net::HTTP.new(exporter.server.config[:BindAddress], exporter.server.config[:Port])
- response = http.request(Net::HTTP::Get.new('/metrics'))
-
- expect(response.body).to include('gitlab_sli:rails_request_apdex')
- end
- end
-
- describe '#mark_as_not_running!' do
- it 'readiness probe returns a failure status', :prometheus do
- exporter.mark_as_not_running!
-
- 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/rails_slis_spec.rb b/spec/lib/gitlab/metrics/rails_slis_spec.rb
index a5ccf7fafa4..0c77dc9f582 100644
--- a/spec/lib/gitlab/metrics/rails_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/rails_slis_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
allow(Gitlab::Graphql::KnownOperations).to receive(:default).and_return(Gitlab::Graphql::KnownOperations.new(%w(foo bar)))
end
- describe '.initialize_request_slis_if_needed!' do
+ describe '.initialize_request_slis!' do
it "initializes the SLI for all possible endpoints if they weren't", :aggregate_failures do
possible_labels = [
{
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
expect(Gitlab::Metrics::Sli).to receive(:initialize_sli).with(:rails_request_apdex, array_including(*possible_labels)).and_call_original
expect(Gitlab::Metrics::Sli).to receive(:initialize_sli).with(:graphql_query_apdex, array_including(*possible_graphql_labels)).and_call_original
- described_class.initialize_request_slis_if_needed!
+ described_class.initialize_request_slis!
end
it 'does not initialize the SLI if they were initialized already', :aggregate_failures do
@@ -49,13 +49,13 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
expect(Gitlab::Metrics::Sli).to receive(:initialized?).with(:graphql_query_apdex) { true }
expect(Gitlab::Metrics::Sli).not_to receive(:initialize_sli)
- described_class.initialize_request_slis_if_needed!
+ described_class.initialize_request_slis!
end
end
describe '.request_apdex' do
it 'returns the initialized request apdex SLI object' do
- described_class.initialize_request_slis_if_needed!
+ described_class.initialize_request_slis!
expect(described_class.request_apdex).to be_initialized
end
@@ -63,7 +63,7 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
describe '.graphql_query_apdex' do
it 'returns the initialized request apdex SLI object' do
- described_class.initialize_request_slis_if_needed!
+ described_class.initialize_request_slis!
expect(described_class.graphql_query_apdex).to be_initialized
end
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index a4877208bcf..dfae5aa6784 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -18,6 +18,20 @@ RSpec.describe Gitlab::Metrics::Samplers::RubySampler do
expect(sampler.metrics[:process_start_time_seconds].get).to eq(Time.now.to_i)
end
end
+
+ context 'when not setting a prefix' do
+ it 'does not prepend metrics with that prefix' do
+ expect(sampler.metrics[:process_start_time_seconds].name).to eq(:ruby_process_start_time_seconds)
+ end
+ end
+
+ context 'when using custom prefix' do
+ let(:sampler) { described_class.new(prefix: 'custom') }
+
+ it 'prepends metrics with that prefix' do
+ expect(sampler.metrics[:process_start_time_seconds].name).to eq(:custom_ruby_process_start_time_seconds)
+ end
+ end
end
describe '#sample' do
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index 732aa553737..ce3caf8cdfe 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -4,6 +4,13 @@ require 'spec_helper'
RSpec.describe Gitlab::Metrics::System do
context 'when /proc files exist' do
+ # Modified column 22 to be 1000 (starttime ticks)
+ let(:proc_stat) do
+ <<~SNIP
+ 2095 (ruby) R 0 2095 2095 34818 2095 4194560 211267 7897 2 0 287 51 10 1 20 0 5 0 1000 566210560 80885 18446744073709551615 94736211292160 94736211292813 140720919612064 0 0 0 0 0 1107394127 0 0 0 17 3 0 0 0 0 0 94736211303768 94736211304544 94736226689024 140720919619473 140720919619513 140720919619513 140720919621604 0
+ SNIP
+ end
+
# Fixtures pulled from:
# Linux carbon 5.3.0-7648-generic #41~1586789791~19.10~9593806-Ubuntu SMP Mon Apr 13 17:50:40 UTC x86_64 x86_64 x86_64 GNU/Linux
let(:proc_status) do
@@ -97,6 +104,29 @@ RSpec.describe Gitlab::Metrics::System do
end
end
+ describe '.process_runtime_elapsed_seconds' do
+ it 'returns the seconds elapsed since the process was started' do
+ # sets process starttime ticks to 1000
+ mock_existing_proc_file('/proc/self/stat', proc_stat)
+ # system clock ticks/sec
+ expect(Etc).to receive(:sysconf).with(Etc::SC_CLK_TCK).and_return(100)
+ # system uptime in seconds
+ expect(::Process).to receive(:clock_gettime).and_return(15)
+
+ # uptime - (starttime_ticks / ticks_per_sec)
+ expect(described_class.process_runtime_elapsed_seconds).to eq(5)
+ end
+
+ context 'when inputs are not available' do
+ it 'returns 0' do
+ mock_missing_proc_file
+ expect(::Process).to receive(:clock_gettime).and_raise(NameError)
+
+ expect(described_class.process_runtime_elapsed_seconds).to eq(0)
+ end
+ end
+ end
+
describe '.summary' do
it 'contains a selection of the available fields' do
stub_const('RUBY_DESCRIPTION', 'ruby-3.0-patch1')
@@ -223,10 +253,10 @@ RSpec.describe Gitlab::Metrics::System do
end
def mock_existing_proc_file(path, content)
- allow(File).to receive(:foreach).with(path) { |_path, &block| content.each_line(&block) }
+ allow(File).to receive(:open).with(path) { |_path, &block| block.call(StringIO.new(content)) }
end
def mock_missing_proc_file
- allow(File).to receive(:foreach).and_raise(Errno::ENOENT)
+ allow(File).to receive(:open).and_raise(Errno::ENOENT)
end
end
diff --git a/spec/lib/gitlab/middleware/memory_report_spec.rb b/spec/lib/gitlab/middleware/memory_report_spec.rb
new file mode 100644
index 00000000000..e063866b056
--- /dev/null
+++ b/spec/lib/gitlab/middleware/memory_report_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'memory_profiler'
+
+RSpec.describe Gitlab::Middleware::MemoryReport do
+ let(:app) { proc { |env| [200, { 'Content-Type' => 'text/plain' }, ['Hello world!']] } }
+ let(:middleware) { described_class.new(app) }
+
+ describe '#call' do
+ shared_examples 'returns original response' do
+ it 'returns original response' do
+ expect(MemoryProfiler).not_to receive(:report)
+
+ status, headers, body = middleware.call(env)
+
+ expect(status).to eq(200)
+ expect(headers).to eq({ 'Content-Type' => 'text/plain' })
+ expect(body.first).to eq('Hello world!')
+ end
+
+ it 'does not call the MemoryProfiler' do
+ expect(MemoryProfiler).not_to receive(:report)
+
+ middleware.call(env)
+ end
+ end
+
+ context 'when the Rails environment is not development' do
+ let(:env) { Rack::MockRequest.env_for('/') }
+
+ it_behaves_like 'returns original response'
+ end
+
+ context 'when the Rails environment is development' do
+ before do
+ allow(Rails.env).to receive(:development?).and_return(true)
+ end
+
+ context 'when memory report is not requested' do
+ let(:env) { Rack::MockRequest.env_for('/') }
+
+ it_behaves_like 'returns original response'
+ end
+
+ context 'when memory report is requested' do
+ let(:env) { Rack::MockRequest.env_for('/', params: { 'performance_bar' => 'memory' }) }
+
+ before do
+ allow(env).to receive(:[]).and_call_original
+ allow(app).to receive(:call).and_return(empty_memory_report)
+ end
+
+ let(:empty_memory_report) do
+ report = MemoryProfiler::Results.new
+ report.register_results(MemoryProfiler::StatHash.new, MemoryProfiler::StatHash.new, 1)
+ end
+
+ it 'returns a memory report' do
+ expect(MemoryProfiler).to receive(:report).and_yield
+
+ status, headers, body = middleware.call(env)
+
+ expect(status).to eq(200)
+ expect(headers).to eq({ 'Content-Type' => 'text/plain' })
+ expect(body.first).to include('Total allocated: 0 B')
+ end
+
+ context 'when something goes wrong with creating the report' do
+ before do
+ expect(MemoryProfiler).to receive(:report).and_raise(StandardError, 'something went terribly wrong!')
+ end
+
+ it 'logs the error' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+
+ middleware.call(env)
+ end
+
+ it 'returns the error' do
+ status, headers, body = middleware.call(env)
+
+ expect(status).to eq(500)
+ expect(headers).to eq({ 'Content-Type' => 'text/plain' })
+ expect(body.first).to include('Could not generate memory report: something went terribly wrong!')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/net_http_adapter_spec.rb b/spec/lib/gitlab/net_http_adapter_spec.rb
new file mode 100644
index 00000000000..21c1a1ebe25
--- /dev/null
+++ b/spec/lib/gitlab/net_http_adapter_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::NetHttpAdapter do
+ describe '#connect' do
+ let(:url) { 'https://example.org' }
+ let(:net_http_adapter) { described_class.new(url) }
+
+ subject(:connect) { net_http_adapter.send(:connect) }
+
+ before do
+ allow(TCPSocket).to receive(:open).and_return(Socket.new(:INET, :STREAM))
+ end
+
+ it 'uses a Gitlab::BufferedIo instance as @socket' do
+ connect
+
+ expect(net_http_adapter.instance_variable_get(:@socket)).to be_a(Gitlab::BufferedIo)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb
index 577d15b8495..42ae5844b95 100644
--- a/spec/lib/gitlab/omniauth_initializer_spec.rb
+++ b/spec/lib/gitlab/omniauth_initializer_spec.rb
@@ -101,5 +101,19 @@ RSpec.describe Gitlab::OmniauthInitializer do
subject.execute([google_config])
end
+
+ it 'configures defaults for gitlab' do
+ conf = {
+ 'name' => 'gitlab',
+ "args" => {}
+ }
+
+ expect(devise_config).to receive(:omniauth).with(
+ :gitlab,
+ authorize_params: { gl_auth_type: 'login' }
+ )
+
+ subject.execute([conf])
+ end
end
end
diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/array_scope_columns_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/array_scope_columns_spec.rb
index 2cebf0d9473..087bfb197ec 100644
--- a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/array_scope_columns_spec.rb
+++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/array_scope_columns_spec.rb
@@ -16,4 +16,13 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::ArrayScopeCol
it { expect { array_scope_columns }.to raise_error /No array columns were given/ }
end
+
+ context 'when Arel AS node is given as input' do
+ let(:scope) { Issue.select(Issue.arel_table[:id].as('id'), :title) }
+ let(:columns) { scope.select_values }
+
+ it 'works with Arel AS nodes' do
+ expect(array_scope_columns.array_aggregated_column_names).to eq(%w[array_cte_id_array array_cte_title_array])
+ end
+ end
end
diff --git a/spec/lib/gitlab/pipeline_scope_counts_spec.rb b/spec/lib/gitlab/pipeline_scope_counts_spec.rb
new file mode 100644
index 00000000000..a9187ecfb54
--- /dev/null
+++ b/spec/lib/gitlab/pipeline_scope_counts_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::PipelineScopeCounts do
+ let(:current_user) { create(:user) }
+
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
+ let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) }
+ let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') }
+ let_it_be(:sha_pipeline) { create(:ci_pipeline, :running, project: project, sha: 'deadbeef') }
+ let_it_be(:on_demand_dast_scan) { create(:ci_pipeline, :success, project: project, source: 'ondemand_dast_scan') }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'has policy class' do
+ expect(described_class.declarative_policy_class).to be("Ci::ProjectPipelinesPolicy")
+ end
+
+ it 'has expected attributes' do
+ expect(described_class.new(current_user, project, {})).to have_attributes(
+ all: 6,
+ finished: 3,
+ pending: 2,
+ running: 1
+ )
+ end
+
+ describe 'with large amount of pipelines' do
+ it 'sets the PIPELINES_COUNT_LIMIT constant to a value of 1_000' do
+ expect(described_class::PIPELINES_COUNT_LIMIT).to eq(1_000)
+ end
+
+ context 'when there are more records than the limit' do
+ before do
+ stub_const('Gitlab::PipelineScopeCounts::PIPELINES_COUNT_LIMIT', 3)
+ end
+
+ it 'limits the found items' do
+ expect(described_class.new(current_user, project, {}).all).to eq(3)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 891482a5f17..8211806a809 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -40,6 +40,17 @@ RSpec.describe Gitlab::Popen do
it { expect(@output).to include('No such file or directory') }
end
+ context 'non-zero status with a kill' do
+ let(:cmd) { [Gem.ruby, "-e", "thr = Thread.new { sleep 5 }; Process.kill(9, Process.pid); thr.join"] }
+
+ before do
+ @output, @status = @klass.new.popen(cmd)
+ end
+
+ it { expect(@status).to eq(9) }
+ it { expect(@output).to be_empty }
+ end
+
context 'unsafe string command' do
it 'raises an error when it gets called with a string argument' do
expect { @klass.new.popen('ls', path) }.to raise_error(RuntimeError)
diff --git a/spec/lib/gitlab/process_memory_cache/helper_spec.rb b/spec/lib/gitlab/process_memory_cache/helper_spec.rb
index 27d7fd0bdcf..bad4f61282c 100644
--- a/spec/lib/gitlab/process_memory_cache/helper_spec.rb
+++ b/spec/lib/gitlab/process_memory_cache/helper_spec.rb
@@ -33,13 +33,20 @@ RSpec.describe Gitlab::ProcessMemoryCache::Helper, :use_clean_rails_memory_store
end
it 'resets the cache when the shared key is missing', :aggregate_failures do
- expect(Rails.cache).to receive(:read).with(:cached_content_instance_key).twice.and_return(nil)
+ allow(Rails.cache).to receive(:read).with(:cached_content_instance_key).and_return(nil)
is_expected.to receive(:expensive_computation).thrice.and_return(1, 2, 3)
3.times do |index|
expect(subject.cached_content).to eq(index + 1)
end
end
+
+ it 'does not set the shared timestamp if it is already present', :redis do
+ subject.clear_cached_content
+ is_expected.to receive(:expensive_computation).once.and_return(1)
+
+ expect { subject.cached_content }.not_to change { Rails.cache.read(:cached_content_instance_key) }
+ end
end
describe '.invalidate_memory_cache' do
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 16066934194..7852470196b 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -334,7 +334,7 @@ RSpec.describe Gitlab::ProjectAuthorizations do
let(:common_id) { non_existing_record_id }
let!(:group) { create(:group, id: common_id) }
let!(:unrelated_project) { create(:project, id: common_id) }
- let(:user) { unrelated_project.owner }
+ let(:user) { unrelated_project.first_owner }
it 'does not have access to group and its projects' do
mapping = map_access_levels(authorizations)
@@ -345,4 +345,76 @@ RSpec.describe Gitlab::ProjectAuthorizations do
end
end
end
+
+ context 'with pending memberships' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ subject(:mapping) { map_access_levels(authorizations) }
+
+ context 'group membership' do
+ let!(:group_project) { create(:project, namespace: group) }
+
+ before do
+ create(:group_member, :developer, :awaiting, user: user, group: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[group_project.id]).to be_nil
+ end
+ end
+
+ context 'inherited group membership' do
+ let!(:sub_group) { create(:group, parent: group) }
+ let!(:sub_group_project) { create(:project, namespace: sub_group) }
+
+ before do
+ create(:group_member, :developer, :awaiting, user: user, group: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[sub_group_project.id]).to be_nil
+ end
+ end
+
+ context 'project membership' do
+ let!(:group_project) { create(:project, namespace: group) }
+
+ before do
+ create(:project_member, :developer, :awaiting, user: user, project: group_project)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[group_project.id]).to be_nil
+ end
+ end
+
+ context 'shared group' do
+ let!(:shared_group) { create(:group) }
+ let!(:shared_group_project) { create(:project, namespace: shared_group) }
+
+ before do
+ create(:group_group_link, shared_group: shared_group, shared_with_group: group)
+ create(:group_member, :developer, :awaiting, user: user, group: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[shared_group_project.id]).to be_nil
+ end
+ end
+
+ context 'shared project' do
+ let!(:another_group) { create(:group) }
+ let!(:shared_project) { create(:project, namespace: another_group) }
+
+ before do
+ create(:project_group_link, group: group, project: shared_project)
+ create(:group_member, :developer, :awaiting, user: user, group: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[shared_project.id]).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/rack_attack/request_spec.rb b/spec/lib/gitlab/rack_attack/request_spec.rb
index ecdcc23e588..b8a26a64e5b 100644
--- a/spec/lib/gitlab/rack_attack/request_spec.rb
+++ b/spec/lib/gitlab/rack_attack/request_spec.rb
@@ -5,6 +5,20 @@ require 'spec_helper'
RSpec.describe Gitlab::RackAttack::Request do
using RSpec::Parameterized::TableSyntax
+ let(:path) { '/' }
+ let(:env) { {} }
+ let(:session) { {} }
+ let(:request) do
+ ::Rack::Attack::Request.new(
+ env.reverse_merge(
+ 'REQUEST_METHOD' => 'GET',
+ 'PATH_INFO' => Gitlab.config.gitlab.relative_url_root + path,
+ 'rack.input' => StringIO.new,
+ 'rack.session' => session
+ )
+ )
+ end
+
describe 'FILES_PATH_REGEX' do
subject { described_class::FILES_PATH_REGEX }
@@ -16,11 +30,249 @@ RSpec.describe Gitlab::RackAttack::Request do
it { is_expected.not_to match('/api/v4/projects/some/nested/repo/repository/files/README') }
end
+ describe '#api_request?' do
+ subject { request.api_request? }
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/foo/api' | false
+
+ '/api' | true
+ '/api/v4/groups/1' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#api_internal_request?' do
+ subject { request.api_internal_request? }
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/api' | false
+ '/api/v4/groups/1' | false
+ '/api/v4/internal' | false
+ '/foo/api/v4/internal' | false
+
+ '/api/v4/internal/' | true
+ '/api/v4/internal/foo' | true
+ '/api/v1/internal/foo' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#health_check_request?' do
+ subject { request.health_check_request? }
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/foo/-/health' | false
+
+ '/-/health' | true
+ '/-/liveness' | true
+ '/-/readiness' | true
+ '/-/metrics' | true
+ '/-/health/foo' | true
+ '/-/liveness/foo' | true
+ '/-/readiness/foo' | true
+ '/-/metrics/foo' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#container_registry_event?' do
+ subject { request.container_registry_event? }
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/api/v4/container_registry_event' | false
+ '/foo/api/v4/container_registry_event/' | false
+
+ '/api/v4/container_registry_event/' | true
+ '/api/v4/container_registry_event/foo' | true
+ '/api/v1/container_registry_event/foo' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#product_analytics_collector_request?' do
+ subject { request.product_analytics_collector_request? }
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/-/collector' | false
+ '/-/collector/foo' | false
+ '/foo/-/collector/i' | false
+
+ '/-/collector/i' | true
+ '/-/collector/ifoo' | true
+ '/-/collector/i/foo' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#should_be_skipped?' do
+ where(
+ api_internal_request: [true, false],
+ health_check_request: [true, false],
+ container_registry_event: [true, false]
+ )
+
+ with_them do
+ it 'returns true if any condition is true' do
+ allow(request).to receive(:api_internal_request?).and_return(api_internal_request)
+ allow(request).to receive(:health_check_request?).and_return(health_check_request)
+ allow(request).to receive(:container_registry_event?).and_return(container_registry_event)
+
+ expect(request.should_be_skipped?).to be(api_internal_request || health_check_request || container_registry_event)
+ end
+ end
+ end
+
+ describe '#web_request?' do
+ subject { request.web_request? }
+
+ where(:path, :expected) do
+ '/' | true
+ '/groups' | true
+ '/foo/api' | true
+
+ '/api' | false
+ '/api/v4/groups/1' | false
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#protected_path?' do
+ subject { request.protected_path? }
+
+ before do
+ stub_application_setting(protected_paths: [
+ '/protected',
+ '/secure'
+ ])
+ end
+
+ where(:path, :expected) do
+ '/' | false
+ '/groups' | false
+ '/foo/protected' | false
+ '/foo/secure' | false
+
+ '/protected' | true
+ '/secure' | true
+ '/secure/' | true
+ '/secure/foo' | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+ end
+
+ describe '#frontend_request?', :allow_forgery_protection do
+ subject { request.send(:frontend_request?) }
+
+ let(:path) { '/' }
+
+ # Define these as local variables so we can use them in the `where` block.
+ valid_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+ other_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+
+ where(:session, :env, :expected) do
+ {} | {} | false # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ {} | { 'HTTP_X_CSRF_TOKEN' => valid_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => other_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => valid_token } | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+ end
+ end
+
describe '#deprecated_api_request?' do
- let(:env) { { 'REQUEST_METHOD' => 'GET', 'rack.input' => StringIO.new, 'PATH_INFO' => path, 'QUERY_STRING' => query } }
- let(:request) { ::Rack::Attack::Request.new(env) }
+ subject { request.send(:deprecated_api_request?) }
- subject { !!request.__send__(:deprecated_api_request?) }
+ let(:env) { { 'QUERY_STRING' => query } }
where(:path, :query, :expected) do
'/' | '' | false
@@ -42,6 +294,14 @@ RSpec.describe Gitlab::RackAttack::Request do
with_them do
it { is_expected.to eq(expected) }
+
+ context 'when the application is mounted at a relative URL' do
+ before do
+ stub_config_setting(relative_url_root: '/gitlab/root')
+ end
+
+ it { is_expected.to eq(expected) }
+ end
end
end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 8d67350f0f3..54a0b282e99 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Regex do
shared_examples_for 'project/group name chars regex' do
diff --git a/spec/lib/gitlab/request_profiler/profile_spec.rb b/spec/lib/gitlab/request_profiler/profile_spec.rb
index 2e9c75dde87..30e23a99b22 100644
--- a/spec/lib/gitlab/request_profiler/profile_spec.rb
+++ b/spec/lib/gitlab/request_profiler/profile_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::RequestProfiler::Profile do
let(:profile) { described_class.new(filename) }
diff --git a/spec/lib/gitlab/runtime_spec.rb b/spec/lib/gitlab/runtime_spec.rb
index 4627a8db82e..402b72b9220 100644
--- a/spec/lib/gitlab/runtime_spec.rb
+++ b/spec/lib/gitlab/runtime_spec.rb
@@ -26,8 +26,16 @@ RSpec.describe Gitlab::Runtime do
end
context "when unknown" do
- it "raises an exception when trying to identify" do
- expect { subject.identify }.to raise_error(subject::UnknownProcessError)
+ describe '.identify' do
+ it "raises an exception when trying to identify" do
+ expect { subject.identify }.to raise_error(subject::UnknownProcessError)
+ end
+ end
+
+ describe '.safe_identify' do
+ it "returns nil" do
+ expect(subject.safe_identify).to be_nil
+ end
end
end
@@ -37,8 +45,16 @@ RSpec.describe Gitlab::Runtime do
stub_const('::Rails::Console', double)
end
- it "raises an exception when trying to identify" do
- expect { subject.identify }.to raise_error(subject::AmbiguousProcessError)
+ describe '.identify' do
+ it "raises an exception when trying to identify" do
+ expect { subject.identify }.to raise_error(subject::AmbiguousProcessError)
+ end
+ end
+
+ describe '.safe_identify' do
+ it "returns nil" do
+ expect(subject.safe_identify).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/security/scan_configuration_spec.rb b/spec/lib/gitlab/security/scan_configuration_spec.rb
index 0af029968e8..2e8a11dfda3 100644
--- a/spec/lib/gitlab/security/scan_configuration_spec.rb
+++ b/spec/lib/gitlab/security/scan_configuration_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe ::Gitlab::Security::ScanConfiguration do
+ using RSpec::Parameterized::TableSyntax
+
let_it_be(:project) { create(:project, :repository) }
let(:scan) { described_class.new(project: project, type: type, configured: configured) }
@@ -13,9 +15,11 @@ RSpec.describe ::Gitlab::Security::ScanConfiguration do
let(:configured) { true }
context 'with a core scanner' do
- let(:type) { :sast }
+ where(type: %i(sast sast_iac secret_detection))
- it { is_expected.to be_truthy }
+ with_them do
+ it { is_expected.to be_truthy }
+ end
end
context 'with custom scanner' do
@@ -38,27 +42,28 @@ RSpec.describe ::Gitlab::Security::ScanConfiguration do
subject { scan.configuration_path }
let(:configured) { true }
+ let(:type) { :sast }
- context 'with a non configurable scanner' do
- let(:type) { :secret_detection }
+ it { is_expected.to be_nil }
+ end
- it { is_expected.to be_nil }
- end
+ describe '#can_enable_by_merge_request?' do
+ subject { scan.can_enable_by_merge_request? }
- context 'with licensed scanner for FOSS environment' do
- let(:type) { :dast }
+ let(:configured) { true }
- before do
- stub_env('FOSS_ONLY', '1')
- end
+ context 'with a core scanner' do
+ where(type: %i(sast sast_iac secret_detection))
- it { is_expected.to be_nil }
+ with_them do
+ it { is_expected.to be_truthy }
+ end
end
- context 'with custom scanner' do
+ context 'with a custom scanner' do
let(:type) { :my_scanner }
- it { is_expected.to be_nil }
+ it { is_expected.to be_falsey }
end
end
end
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index 38486b313cb..cf5d2c3b455 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
end
where(:name) do
- [:rsa, :dsa, :ecdsa, :ed25519]
+ [:rsa, :dsa, :ecdsa, :ed25519, :ecdsa_sk, :ed25519_sk]
end
with_them do
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
describe '.supported_types' do
it 'returns array with the names of supported technologies' do
expect(described_class.supported_types).to eq(
- [:rsa, :dsa, :ecdsa, :ed25519]
+ [:rsa, :dsa, :ecdsa, :ed25519, :ecdsa_sk, :ed25519_sk]
)
end
end
@@ -35,7 +35,9 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
[:rsa, [1024, 2048, 3072, 4096]],
[:dsa, [1024, 2048, 3072]],
[:ecdsa, [256, 384, 521]],
- [:ed25519, [256]]
+ [:ed25519, [256]],
+ [:ecdsa_sk, [256]],
+ [:ed25519_sk, [256]]
]
end
@@ -53,6 +55,8 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
ssh-dss
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521
ssh-ed25519
+ sk-ecdsa-sha2-nistp256@openssh.com
+ sk-ssh-ed25519@openssh.com
)
)
end
@@ -64,7 +68,9 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
[:rsa, %w(ssh-rsa)],
[:dsa, %w(ssh-dss)],
[:ecdsa, %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521)],
- [:ed25519, %w(ssh-ed25519)]
+ [:ed25519, %w(ssh-ed25519)],
+ [:ecdsa_sk, %w(sk-ecdsa-sha2-nistp256@openssh.com)],
+ [:ed25519_sk, %w(sk-ssh-ed25519@openssh.com)]
]
end
@@ -122,13 +128,35 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
rsa_key_8192
dsa_key_2048
ecdsa_key_256
- ed25519_key_256)
+ ed25519_key_256
+ ecdsa_sk_key_256
+ ed25519_sk_key_256)
end
with_them do
let(:key) { attributes_for(factory)[:key] }
it { is_expected.to be_valid }
+
+ context 'when key begins with options' do
+ let(:key) { "restrict,command='dump /home' #{attributes_for(factory)[:key]}" }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when key is in known_hosts format' do
+ context "when key begins with 'example.com'" do
+ let(:key) { "example.com #{attributes_for(factory)[:key]}" }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when key begins with '@revoked other.example.com'" do
+ let(:key) { "@revoked other.example.com #{attributes_for(factory)[:key]}" }
+
+ it { is_expected.to be_valid }
+ end
+ end
end
end
@@ -137,6 +165,40 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
it { is_expected.not_to be_valid }
end
+
+ context 'when an unsupported SSH key algorithm' do
+ let(:key) { "unsupported-#{attributes_for(:rsa_key_2048)[:key]}" }
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ shared_examples 'raises error when the key is represented by a class that is not in the list of supported technologies' do
+ context 'when the key is represented by a class that is not in the list of supported technologies' do
+ it 'raises error' do
+ klass = Class.new
+ key = klass.new
+
+ allow(public_key).to receive(:key).and_return(key)
+
+ expect { subject }.to raise_error("Unsupported key type: #{key.class}")
+ end
+ end
+
+ context 'when the key is represented by a subclass of the class that is in the list of supported technologies' do
+ it 'raises error' do
+ rsa_subclass = Class.new(described_class.technology(:rsa).key_class) do
+ def initialize
+ end
+ end
+
+ key = rsa_subclass.new
+
+ allow(public_key).to receive(:key).and_return(key)
+
+ expect { subject }.to raise_error("Unsupported key type: #{key.class}")
+ end
+ end
end
describe '#type' do
@@ -147,7 +209,9 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
[:rsa_key_2048, :rsa],
[:dsa_key_2048, :dsa],
[:ecdsa_key_256, :ecdsa],
- [:ed25519_key_256, :ed25519]
+ [:ed25519_key_256, :ed25519],
+ [:ecdsa_sk_key_256, :ecdsa_sk],
+ [:ed25519_sk_key_256, :ed25519_sk]
]
end
@@ -162,6 +226,8 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
it { is_expected.to be_nil }
end
+
+ include_examples 'raises error when the key is represented by a class that is not in the list of supported technologies'
end
describe '#bits' do
@@ -175,7 +241,9 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
[:rsa_key_8192, 8192],
[:dsa_key_2048, 2048],
[:ecdsa_key_256, 256],
- [:ed25519_key_256, 256]
+ [:ed25519_key_256, 256],
+ [:ecdsa_sk_key_256, 256],
+ [:ed25519_sk_key_256, 256]
]
end
@@ -190,6 +258,8 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
it { is_expected.to be_nil }
end
+
+ include_examples 'raises error when the key is represented by a class that is not in the list of supported technologies'
end
describe '#fingerprint' do
@@ -203,7 +273,9 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
[:rsa_key_8192, 'fb:53:7f:e9:2f:f7:17:aa:c8:32:52:06:8e:05:e2:82'],
[:dsa_key_2048, 'c8:85:1e:df:44:0f:20:00:3c:66:57:2b:21:10:5a:27'],
[:ecdsa_key_256, '67:a3:a9:7d:b8:e1:15:d4:80:40:21:34:bb:ed:97:38'],
- [:ed25519_key_256, 'e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73']
+ [:ed25519_key_256, 'e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73'],
+ [:ecdsa_sk_key_256, '56:b9:bc:99:3d:2f:cf:63:6b:70:d8:f9:40:7e:09:4c'],
+ [:ed25519_sk_key_256, 'f9:a0:64:0b:4b:72:72:0e:62:92:d7:04:14:74:1c:c9']
]
end
@@ -220,18 +292,20 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
end
end
- describe '#fingerprint in SHA256 format' do
- subject { public_key.fingerprint("SHA256").gsub("SHA256:", "") if public_key.fingerprint("SHA256") }
+ describe '#fingerprint_sha256' do
+ subject { public_key.fingerprint_sha256 }
where(:factory, :fingerprint_sha256) do
[
- [:rsa_key_2048, 'GdtgO0eHbwLB+mK47zblkoXujkqKRZjgMQrHH6Kks3E'],
- [:rsa_key_4096, 'ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g'],
- [:rsa_key_5120, 'PCCupLbFHScm4AbEufbGDvhBU27IM0MVAor715qKQK8'],
- [:rsa_key_8192, 'CtHFQAS+9Hb8z4vrv4gVQPsHjNN0WIZhWODaB1mQLs4'],
- [:dsa_key_2048, '+a3DQ7cU5GM+gaYOfmc0VWNnykHQSuth3VRcCpWuYNI'],
- [:ecdsa_key_256, 'C+I5k3D+IGeM6k5iBR1ZsphqTKV+7uvL/XZ5hcrTr7g'],
- [:ed25519_key_256, 'DCKAjzxWrdOTjaGKBBjtCW8qY5++GaiAJflrHPmp6W0']
+ [:rsa_key_2048, 'SHA256:GdtgO0eHbwLB+mK47zblkoXujkqKRZjgMQrHH6Kks3E'],
+ [:rsa_key_4096, 'SHA256:ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g'],
+ [:rsa_key_5120, 'SHA256:PCCupLbFHScm4AbEufbGDvhBU27IM0MVAor715qKQK8'],
+ [:rsa_key_8192, 'SHA256:CtHFQAS+9Hb8z4vrv4gVQPsHjNN0WIZhWODaB1mQLs4'],
+ [:dsa_key_2048, 'SHA256:+a3DQ7cU5GM+gaYOfmc0VWNnykHQSuth3VRcCpWuYNI'],
+ [:ecdsa_key_256, 'SHA256:C+I5k3D+IGeM6k5iBR1ZsphqTKV+7uvL/XZ5hcrTr7g'],
+ [:ed25519_key_256, 'SHA256:DCKAjzxWrdOTjaGKBBjtCW8qY5++GaiAJflrHPmp6W0'],
+ [:ecdsa_sk_key_256, 'SHA256:N0sNKBgWKK8usPuPegtgzHQQA9vQ/dRhAEhwFDAnLA4'],
+ [:ed25519_sk_key_256, 'SHA256:U8IKRkIHed6vFMTflwweA3HhIf2DWgZ8EFTm9fgwOUk']
]
end
@@ -249,10 +323,19 @@ RSpec.describe Gitlab::SSHPublicKey, lib: true do
end
describe '#key_text' do
- let(:key) { 'this is not a key' }
+ where(:key_value) do
+ [
+ 'this is not a key',
+ nil
+ ]
+ end
- it 'carries the unmodified key data' do
- expect(public_key.key_text).to eq(key)
+ with_them do
+ let(:key) { key_value }
+
+ it 'carries the unmodified key data' do
+ expect(public_key.key_text).to eq(key)
+ end
end
end
end
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index 627d3bb42c7..fd3654afee0 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -61,7 +61,6 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
:subscriptions_more_minutes_url | 'https://customers.staging.gitlab.com/buy_pipeline_minutes'
:subscriptions_more_storage_url | 'https://customers.staging.gitlab.com/buy_storage'
:subscriptions_manage_url | 'https://customers.staging.gitlab.com/subscriptions'
- :subscriptions_plans_url | 'https://about.gitlab.com/pricing/'
:subscriptions_instance_review_url | 'https://customers.staging.gitlab.com/instance_review'
:subscriptions_gitlab_plans_url | 'https://customers.staging.gitlab.com/gitlab_plans'
:edit_account_url | 'https://customers.staging.gitlab.com/customers/edit'
diff --git a/spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb b/spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb
index 42fc84cf076..b021abc9f25 100644
--- a/spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb
+++ b/spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/shared_examples/lib/gitlab/malicious_regexp_shared_examples'
-require 'support/helpers/stub_feature_flags'
+require 'spec_helper'
RSpec.describe Gitlab::UntrustedRegexp::RubySyntax do
describe '.matches_syntax?' do
@@ -77,6 +75,7 @@ RSpec.describe Gitlab::UntrustedRegexp::RubySyntax do
include StubFeatureFlags
before do
+ # When removed we could use `require 'fast_spec_helper'` again.
stub_feature_flags(allow_unsafe_ruby_regexp: true)
allow(Gitlab::UntrustedRegexp).to receive(:new).and_raise(RegexpError)
diff --git a/spec/lib/gitlab/usage/service_ping_report_spec.rb b/spec/lib/gitlab/usage/service_ping_report_spec.rb
new file mode 100644
index 00000000000..9b9b24ad128
--- /dev/null
+++ b/spec/lib/gitlab/usage/service_ping_report_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_caching do
+ let(:usage_data) { { uuid: "1111" } }
+
+ context 'for output: :all_metrics_values' do
+ it 'generates the service ping' do
+ expect(Gitlab::UsageData).to receive(:data)
+
+ described_class.for(output: :all_metrics_values)
+ end
+ end
+
+ context 'for output: :metrics_queries' do
+ it 'generates the service ping' do
+ expect(Gitlab::UsageDataQueries).to receive(:data)
+
+ described_class.for(output: :metrics_queries)
+ end
+ end
+
+ context 'for output: :non_sql_metrics_values' do
+ it 'generates the service ping' do
+ expect(Gitlab::UsageDataNonSqlMetrics).to receive(:data)
+
+ described_class.for(output: :non_sql_metrics_values)
+ end
+ end
+
+ context 'when using cached' do
+ context 'for cached: true' do
+ let(:new_usage_data) { { uuid: "1112" } }
+
+ it 'caches the values' do
+ allow(Gitlab::UsageData).to receive(:data).and_return(usage_data, new_usage_data)
+
+ expect(described_class.for(output: :all_metrics_values)).to eq(usage_data)
+ expect(described_class.for(output: :all_metrics_values, cached: true)).to eq(usage_data)
+
+ expect(Rails.cache.fetch('usage_data')).to eq(usage_data)
+ end
+
+ it 'writes to cache and returns fresh data' do
+ allow(Gitlab::UsageData).to receive(:data).and_return(usage_data, new_usage_data)
+
+ expect(described_class.for(output: :all_metrics_values)).to eq(usage_data)
+ expect(described_class.for(output: :all_metrics_values)).to eq(new_usage_data)
+ expect(described_class.for(output: :all_metrics_values, cached: true)).to eq(new_usage_data)
+
+ expect(Rails.cache.fetch('usage_data')).to eq(new_usage_data)
+ end
+ end
+
+ context 'when no caching' do
+ let(:new_usage_data) { { uuid: "1112" } }
+
+ it 'returns fresh data' do
+ allow(Gitlab::UsageData).to receive(:data).and_return(usage_data, new_usage_data)
+
+ expect(described_class.for(output: :all_metrics_values)).to eq(usage_data)
+ expect(described_class.for(output: :all_metrics_values)).to eq(new_usage_data)
+
+ expect(Rails.cache.fetch('usage_data')).to eq(new_usage_data)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index f7ff68af8a2..5e74ea3293c 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -49,7 +49,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'secure',
'importer',
'network_policies',
- 'geo'
+ 'geo',
+ 'growth'
)
end
end
diff --git a/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb
new file mode 100644
index 00000000000..4169546edad
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter, :clean_gitlab_redis_shared_state do # rubocop:disable RSpec/FilePath
+ let(:user1) { build(:user, id: 1) }
+ let(:user2) { build(:user, id: 2) }
+ let(:time) { Time.current }
+ let(:action) { described_class::JETBRAINS_API_REQUEST_ACTION }
+ let(:user_agent) { { user_agent: 'gitlab-jetbrains-plugin/0.0.1 intellij-idea/2021.2.4 java/11.0.13 mac-os-x/aarch64/12.1' } }
+
+ context 'when tracking a jetbrains api request' do
+ it_behaves_like 'a request from an extension'
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/vscode_extenion_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/vscode_extenion_activity_unique_counter_spec.rb
deleted file mode 100644
index 7593d51fe76..00000000000
--- a/spec/lib/gitlab/usage_data_counters/vscode_extenion_activity_unique_counter_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.shared_examples 'a tracked vs code unique action' do |event|
- before do
- stub_application_setting(usage_ping_enabled: true)
- end
-
- def count_unique(date_from:, date_to:)
- Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
- end
-
- it 'tracks when the user agent is from vs code' do
- aggregate_failures do
- user_agent = { user_agent: 'vs-code-gitlab-workflow/3.11.1 VSCode/1.52.1 Node.js/12.14.1 (darwin; x64)' }
-
- expect(track_action(user: user1, **user_agent)).to be_truthy
- expect(track_action(user: user1, **user_agent)).to be_truthy
- expect(track_action(user: user2, **user_agent)).to be_truthy
-
- expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(2)
- end
- end
-
- it 'does not track when the user agent is not from vs code' do
- aggregate_failures do
- user_agent = { user_agent: 'normal_user_agent' }
-
- expect(track_action(user: user1, **user_agent)).to be_falsey
- expect(track_action(user: user1, **user_agent)).to be_falsey
- expect(track_action(user: user2, **user_agent)).to be_falsey
-
- expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(0)
- end
- end
-
- it 'does not track if user agent is not present' do
- expect(track_action(user: nil, user_agent: nil)).to be_nil
- end
-
- it 'does not track if user is not present' do
- user_agent = { user_agent: 'vs-code-gitlab-workflow/3.11.1 VSCode/1.52.1 Node.js/12.14.1 (darwin; x64)' }
-
- expect(track_action(user: nil, **user_agent)).to be_nil
- end
-end
-
-RSpec.describe Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter, :clean_gitlab_redis_shared_state do
- let(:user1) { build(:user, id: 1) }
- let(:user2) { build(:user, id: 2) }
- let(:time) { Time.current }
-
- context 'when tracking a vs code api request' do
- it_behaves_like 'a tracked vs code unique action' do
- let(:action) { described_class::VS_CODE_API_REQUEST_ACTION }
-
- def track_action(params)
- described_class.track_api_request_when_trackable(**params)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb
new file mode 100644
index 00000000000..640dadd8c0b
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter, :clean_gitlab_redis_shared_state do # rubocop:disable RSpec/FilePath
+ let(:user1) { build(:user, id: 1) }
+ let(:user2) { build(:user, id: 2) }
+ let(:time) { Time.current }
+ let(:action) { described_class::VS_CODE_API_REQUEST_ACTION }
+ let(:user_agent) { { user_agent: 'vs-code-gitlab-workflow/3.11.1 VSCode/1.52.1 Node.js/12.14.1 (darwin; x64)' } }
+
+ context 'when tracking a vs code api request' do
+ it_behaves_like 'a request from an extension'
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 427e8e67090..bea07dd9c43 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -12,8 +12,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
stub_database_flavor_check('Cloud SQL for PostgreSQL')
end
- describe '.uncached_data' do
- subject { described_class.uncached_data }
+ describe '.data' do
+ subject { described_class.data }
it 'includes basic top and second level keys' do
is_expected.to include(:counts)
@@ -556,8 +556,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:issues_created_from_alerts]).to eq(3)
expect(count_data[:issues_created_manually_from_alerts]).to eq(1)
expect(count_data[:alert_bot_incident_issues]).to eq(4)
- expect(count_data[:incident_labeled_issues]).to eq(3)
-
expect(count_data[:clusters_enabled]).to eq(6)
expect(count_data[:project_clusters_enabled]).to eq(4)
expect(count_data[:group_clusters_enabled]).to eq(1)
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index d756ec5ef83..ba6997adbf6 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -439,6 +439,23 @@ RSpec.describe Gitlab::Utils do
end
end
+ describe '.add_url_parameters' do
+ subject { described_class.add_url_parameters(url, params) }
+
+ where(:url, :params, :expected_url) do
+ nil | nil | ''
+ nil | { b: 3, a: 2 } | '?a=2&b=3'
+ 'https://gitlab.com' | nil | 'https://gitlab.com'
+ 'https://gitlab.com' | { b: 3, a: 2 } | 'https://gitlab.com?a=2&b=3'
+ 'https://gitlab.com?a=1#foo' | { b: 3, 'a': 2 } | 'https://gitlab.com?a=2&b=3#foo'
+ 'https://gitlab.com?a=1#foo' | [[:b, 3], [:a, 2]] | 'https://gitlab.com?a=2&b=3#foo'
+ end
+
+ with_them do
+ it { is_expected.to eq(expected_url) }
+ end
+ end
+
describe '.removes_sensitive_data_from_url' do
it 'returns string object' do
expect(described_class.removes_sensitive_data_from_url('http://gitlab.com')).to be_instance_of(String)
diff --git a/spec/lib/gitlab/web_ide/config/entry/global_spec.rb b/spec/lib/gitlab/web_ide/config/entry/global_spec.rb
index 9af21685c9e..66c9bb00ee9 100644
--- a/spec/lib/gitlab/web_ide/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/web_ide/config/entry/global_spec.rb
@@ -108,7 +108,7 @@ RSpec.describe Gitlab::WebIde::Config::Entry::Global do
describe '#errors' do
it 'reports errors about missing script' do
expect(global.errors)
- .to include "terminal:before_script config should be an array containing strings and arrays of strings"
+ .to include "terminal:before_script config should be a string or a nested array of strings up to 10 levels deep"
end
end
end
diff --git a/spec/lib/gitlab/web_ide/config_spec.rb b/spec/lib/gitlab/web_ide/config_spec.rb
index 7a9011d03c0..7ee9d40410c 100644
--- a/spec/lib/gitlab/web_ide/config_spec.rb
+++ b/spec/lib/gitlab/web_ide/config_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::WebIde::Config do
end
context 'when config logic is incorrect' do
- let(:yml) { 'terminal: { before_script: "ls" }' }
+ let(:yml) { 'terminal: { before_script: 123 }' }
describe '#valid?' do
it 'is not valid' do
diff --git a/spec/lib/gitlab/webpack/file_loader_spec.rb b/spec/lib/gitlab/webpack/file_loader_spec.rb
index 34d00b9f106..6475ef58611 100644
--- a/spec/lib/gitlab/webpack/file_loader_spec.rb
+++ b/spec/lib/gitlab/webpack/file_loader_spec.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'support/helpers/file_read_helpers'
-require 'support/webmock'
+require 'spec_helper'
RSpec.describe Gitlab::Webpack::FileLoader do
include FileReadHelpers
diff --git a/spec/lib/gitlab_edition_spec.rb b/spec/lib/gitlab_edition_spec.rb
index 2f1316819ec..6fc4312252d 100644
--- a/spec/lib/gitlab_edition_spec.rb
+++ b/spec/lib/gitlab_edition_spec.rb
@@ -3,18 +3,22 @@
require 'spec_helper'
RSpec.describe GitlabEdition do
+ def remove_instance_variable(ivar)
+ described_class.remove_instance_variable(ivar) if described_class.instance_variable_defined?(ivar)
+ end
+
before do
# Make sure the ENV is clean
stub_env('FOSS_ONLY', nil)
stub_env('EE_ONLY', nil)
- described_class.instance_variable_set(:@is_ee, nil)
- described_class.instance_variable_set(:@is_jh, nil)
+ remove_instance_variable(:@is_ee)
+ remove_instance_variable(:@is_jh)
end
after do
- described_class.instance_variable_set(:@is_ee, nil)
- described_class.instance_variable_set(:@is_jh, nil)
+ remove_instance_variable(:@is_ee)
+ remove_instance_variable(:@is_jh)
end
describe '.root' do
@@ -51,7 +55,7 @@ RSpec.describe GitlabEdition do
allow(described_class).to receive(:ee?).and_return(false)
end
- it 'returns the exyensions according to the current edition' do
+ it 'returns the extensions according to the current edition' do
expect(described_class.extensions).to be_empty
end
end
@@ -77,7 +81,7 @@ RSpec.describe GitlabEdition do
end
describe '.ee?' do
- context 'for EE' do
+ context 'when EE' do
before do
stub_path('ee/app/models/license.rb', exist?: true)
end
@@ -109,7 +113,7 @@ RSpec.describe GitlabEdition do
end
end
- context 'for CE' do
+ context 'when CE' do
before do
stub_path('ee/app/models/license.rb', exist?: false)
end
@@ -121,12 +125,9 @@ RSpec.describe GitlabEdition do
end
describe '.jh?' do
- context 'for JH' do
+ context 'when JH' do
before do
- stub_path(
- 'ee/app/models/license.rb',
- 'jh',
- exist?: true)
+ stub_path('ee/app/models/license.rb', 'jh', exist?: true)
end
context 'when using default FOSS_ONLY and EE_ONLY' do
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 49ba4debe31..57a4bdc9bb5 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -99,6 +99,13 @@ RSpec.describe Gitlab do
expect(described_class.com?).to eq true
end
+ it 'is true when on other gitlab subdomain with hyphen' do
+ url_with_subdomain = Gitlab::Saas.com_url.gsub('https://', 'https://test-example.')
+ stub_config_setting(url: url_with_subdomain)
+
+ expect(described_class.com?).to eq true
+ end
+
it 'is false when not on GitLab.com' do
stub_config_setting(url: 'http://example.com')
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 3284c9cd0d1..29e5445cfaa 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
let(:token) { 'token' }
let(:client) { described_class.new(token, nil) }
let(:user_agent_options) { client.instance_eval { user_agent_header } }
+ let(:gcp_project_id) { String('gcp_proj_id') }
+ let(:operation) { true }
describe '.session_key_for_redirect_uri' do
let(:state) { 'random_string' }
@@ -60,7 +62,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_cluster).with(any_args, options: user_agent_options)
- .and_return(gke_cluster)
+ .and_return(gke_cluster)
end
it { is_expected.to eq(gke_cluster) }
@@ -122,7 +124,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1beta1::ContainerService)
.to receive(:create_cluster).with(any_args)
- .and_return(operation)
+ .and_return(operation)
end
it 'sets corresponded parameters' do
@@ -172,7 +174,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_operation).with(any_args, options: user_agent_options)
- .and_return(operation)
+ .and_return(operation)
end
it { is_expected.to eq(operation) }
@@ -244,7 +246,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
let(:operation) { double('Service Account Key') }
- it 'class Google Api IamService#create_service_account_key' do
+ it 'calls Google Api IamService#create_service_account_key' do
expect_any_instance_of(Google::Apis::IamV1::IamService)
.to receive(:create_service_account_key)
.with(any_args)
@@ -252,4 +254,84 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
is_expected.to eq(operation)
end
end
+
+ describe 'grant_service_account_roles' do
+ subject { client.grant_service_account_roles(spy, spy) }
+
+ it 'calls Google Api CloudResourceManager#set_iam_policy' do
+ mock_gcp_id = 'mock-gcp-id'
+ mock_email = 'mock@email.com'
+ mock_policy = Struct.new(:bindings).new([])
+ mock_body = []
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/iam.serviceAccountUser', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/artifactregistry.admin', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/cloudbuild.builds.builder', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/run.admin', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/storage.admin', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/cloudsql.admin', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
+ .with({ 'role': 'roles/browser', 'members': ["serviceAccount:#{mock_email}"] })
+
+ expect(Google::Apis::CloudresourcemanagerV1::SetIamPolicyRequest).to receive(:new).and_return([])
+
+ expect_next_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService) do |instance|
+ expect(instance).to receive(:get_project_iam_policy)
+ .with(mock_gcp_id)
+ .and_return(mock_policy)
+ expect(instance).to receive(:set_project_iam_policy)
+ .with(mock_gcp_id, mock_body)
+ end
+
+ client.grant_service_account_roles(mock_gcp_id, mock_email)
+ end
+ end
+
+ describe '#enable_cloud_run' do
+ subject { client.enable_cloud_run(gcp_project_id) }
+
+ it 'calls Google Api IamService#create_service_account_key' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/run.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
+
+ describe '#enable_artifacts_registry' do
+ subject { client.enable_artifacts_registry(gcp_project_id) }
+
+ it 'calls Google Api IamService#create_service_account_key' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/artifactregistry.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
+
+ describe '#enable_cloud_build' do
+ subject { client.enable_cloud_build(gcp_project_id) }
+
+ it 'calls Google Api IamService#create_service_account_key' do
+ expect_any_instance_of(Google::Apis::ServiceusageV1::ServiceUsageService)
+ .to receive(:enable_service)
+ .with("projects/#{gcp_project_id}/services/cloudbuild.googleapis.com")
+ .and_return(operation)
+ is_expected.to eq(operation)
+ end
+ end
end
diff --git a/spec/lib/learn_gitlab/project_spec.rb b/spec/lib/learn_gitlab/project_spec.rb
index 523703761bf..5d649740c65 100644
--- a/spec/lib/learn_gitlab/project_spec.rb
+++ b/spec/lib/learn_gitlab/project_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe LearnGitlab::Project do
let_it_be(:current_user) { create(:user) }
let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME) }
+ let_it_be(:learn_gitlab_ultimate_trial_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME_ULTIMATE_TRIAL) }
let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: LearnGitlab::Project::BOARD_NAME) }
let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: LearnGitlab::Project::LABEL_NAME) }
@@ -45,6 +46,12 @@ RSpec.describe LearnGitlab::Project do
subject { described_class.new(current_user).project }
it { is_expected.to eq learn_gitlab_project }
+
+ context 'when it is created during trial signup' do
+ let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME_ULTIMATE_TRIAL) }
+
+ it { is_expected.to eq learn_gitlab_project }
+ end
end
describe '.board' do
diff --git a/spec/lib/peek/views/detailed_view_spec.rb b/spec/lib/peek/views/detailed_view_spec.rb
index 8d6d9a829ef..149685b243a 100644
--- a/spec/lib/peek/views/detailed_view_spec.rb
+++ b/spec/lib/peek/views/detailed_view_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Peek::Views::DetailedView, :request_store do
context 'when a class defines thresholds' do
diff --git a/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb
new file mode 100644
index 00000000000..38066e41c53
--- /dev/null
+++ b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb
@@ -0,0 +1,191 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do
+ subject(:result) { described_class.new(auto_devops_enabled, gitlab_ci_content).generate }
+
+ let(:params) { {} }
+
+ context 'with existing .gitlab-ci.yml' do
+ let(:auto_devops_enabled) { false }
+
+ context 'container_scanning has not been included' do
+ let(:expected_yml) do
+ <<-CI_YML.strip_heredoc
+ # You can override the included template(s) by including variable overrides
+ # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
+ # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
+ # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+ # Note that environment variables can be set in several places
+ # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
+
+ # container_scanning:
+ # variables:
+ # DOCKER_IMAGE: ...
+ # DOCKER_USER: ...
+ # DOCKER_PASSWORD: ...
+ stages:
+ - test
+ - security
+ variables:
+ RANDOM: make sure this persists
+ include:
+ - template: existing.yml
+ - template: Security/Container-Scanning.gitlab-ci.yml
+ CI_YML
+ end
+
+ context 'template includes are an array' do
+ let(:gitlab_ci_content) do
+ { "stages" => %w(test security),
+ "variables" => { "RANDOM" => "make sure this persists" },
+ "include" => [{ "template" => "existing.yml" }] }
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('update')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+
+ context 'template include is not an array' do
+ let(:gitlab_ci_content) do
+ { "stages" => %w(test security),
+ "variables" => { "RANDOM" => "make sure this persists" },
+ "include" => { "template" => "existing.yml" } }
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('update')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+ end
+
+ context 'container_scanning has been included' do
+ let(:expected_yml) do
+ <<-CI_YML.strip_heredoc
+ # You can override the included template(s) by including variable overrides
+ # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
+ # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
+ # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+ # Note that environment variables can be set in several places
+ # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
+
+ # container_scanning:
+ # variables:
+ # DOCKER_IMAGE: ...
+ # DOCKER_USER: ...
+ # DOCKER_PASSWORD: ...
+ stages:
+ - test
+ variables:
+ RANDOM: make sure this persists
+ include:
+ - template: Security/Container-Scanning.gitlab-ci.yml
+ CI_YML
+ end
+
+ context 'container_scanning template include are an array' do
+ let(:gitlab_ci_content) do
+ { "stages" => %w(test),
+ "variables" => { "RANDOM" => "make sure this persists" },
+ "include" => [{ "template" => "Security/Container-Scanning.gitlab-ci.yml" }] }
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('update')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+
+ context 'container_scanning template include is not an array' do
+ let(:gitlab_ci_content) do
+ { "stages" => %w(test),
+ "variables" => { "RANDOM" => "make sure this persists" },
+ "include" => { "template" => "Security/Container-Scanning.gitlab-ci.yml" } }
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('update')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+ end
+ end
+
+ context 'with no .gitlab-ci.yml' do
+ let(:gitlab_ci_content) { nil }
+
+ context 'autodevops disabled' do
+ let(:auto_devops_enabled) { false }
+ let(:expected_yml) do
+ <<-CI_YML.strip_heredoc
+ # You can override the included template(s) by including variable overrides
+ # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
+ # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
+ # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+ # Note that environment variables can be set in several places
+ # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
+
+ # container_scanning:
+ # variables:
+ # DOCKER_IMAGE: ...
+ # DOCKER_USER: ...
+ # DOCKER_PASSWORD: ...
+ include:
+ - template: Security/Container-Scanning.gitlab-ci.yml
+ CI_YML
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('create')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+
+ context 'with autodevops enabled' do
+ let(:auto_devops_enabled) { true }
+ let(:expected_yml) do
+ <<-CI_YML.strip_heredoc
+ # You can override the included template(s) by including variable overrides
+ # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
+ # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
+ # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
+ # Note that environment variables can be set in several places
+ # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
+
+ # container_scanning:
+ # variables:
+ # DOCKER_IMAGE: ...
+ # DOCKER_USER: ...
+ # DOCKER_PASSWORD: ...
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+ CI_YML
+ end
+
+ before do
+ allow_next_instance_of(described_class) do |secret_detection_build_actions|
+ allow(secret_detection_build_actions).to receive(:auto_devops_stages).and_return(fast_auto_devops_stages)
+ end
+ end
+
+ it 'generates the correct YML' do
+ expect(result[:action]).to eq('create')
+ expect(result[:content]).to eq(expected_yml)
+ end
+ end
+ end
+
+ # stubbing this method allows this spec file to use fast_spec_helper
+ def fast_auto_devops_stages
+ auto_devops_template = YAML.safe_load( File.read('lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml') )
+ auto_devops_template['stages']
+ end
+end
diff --git a/spec/lib/security/ci_configuration/sast_build_action_spec.rb b/spec/lib/security/ci_configuration/sast_build_action_spec.rb
index d93175249f5..6f702e51b73 100644
--- a/spec/lib/security/ci_configuration/sast_build_action_spec.rb
+++ b/spec/lib/security/ci_configuration/sast_build_action_spec.rb
@@ -324,6 +324,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -344,6 +345,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -361,6 +363,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -384,6 +387,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -420,6 +424,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -445,6 +450,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -468,6 +474,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -492,6 +499,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -516,6 +524,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
diff --git a/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb b/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb
index ecd1602dd9e..4c459058368 100644
--- a/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb
+++ b/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -64,6 +65,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -114,6 +116,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
include:
@@ -135,6 +138,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
include:
diff --git a/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb b/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb
index 146c60ffb6e..4d9860ca4a5 100644
--- a/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb
+++ b/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -64,6 +65,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages:
@@ -114,6 +116,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
include:
@@ -135,6 +138,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
+ # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
include:
diff --git a/spec/lib/serializers/json_spec.rb b/spec/lib/serializers/json_spec.rb
index 0c1801b34f9..96a57cde056 100644
--- a/spec/lib/serializers/json_spec.rb
+++ b/spec/lib/serializers/json_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'oj'
RSpec.describe Serializers::Json do
describe '.dump' do
diff --git a/spec/lib/serializers/symbolized_json_spec.rb b/spec/lib/serializers/symbolized_json_spec.rb
index b30fb074ddd..b9217854d9a 100644
--- a/spec/lib/serializers/symbolized_json_spec.rb
+++ b/spec/lib/serializers/symbolized_json_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'oj'
RSpec.describe Serializers::SymbolizedJson do
describe '.dump' do
diff --git a/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb b/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb
new file mode 100644
index 00000000000..2120341bf23
--- /dev/null
+++ b/spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Concerns::WorkItemHierarchy do
+ shared_examples 'hierarchy menu' do
+ let(:item_id) { :hierarchy }
+ specify { is_expected.not_to be_nil }
+ end
+
+ describe 'Project hierarchy menu item' do
+ let_it_be_with_reload(:project) { create(:project, :repository) }
+
+ let(:user) { project.owner }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
+
+ subject { Sidebars::Projects::Menus::ProjectInformationMenu.new(context).renderable_items.index { |e| e.item_id == item_id } }
+
+ it_behaves_like 'hierarchy menu'
+ end
+end
diff --git a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb
index 6f2ca719bc9..25a65015847 100644
--- a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu do
create(:user).tap { |u| project.add_guest(u) }
end
- let(:owner) { project.owner }
+ let(:owner) { project.first_owner }
let(:current_user) { owner }
let(:context) { Sidebars::Projects::Context.new(current_user: current_user, container: project, current_ref: project.repository.root_ref) }
diff --git a/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb
index dee2716e4c2..2ceb9dcada9 100644
--- a/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::CiCdMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:can_view_pipeline_editor) { true }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: 'master', can_view_pipeline_editor: can_view_pipeline_editor) }
diff --git a/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb b/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb
index e3ae3add4fd..836c6d26c6c 100644
--- a/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ConfluenceMenu do
let_it_be_with_refind(:project) { create(:project, has_external_wiki: true) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
index 3149c316c63..56eb082e101 100644
--- a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
let_it_be(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe '#render?' do
diff --git a/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb b/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb
index 0585eb2254c..2033d40897e 100644
--- a/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ExternalIssueTrackerMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:jira_issues_integration_active) { false }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, jira_issues_integration: jira_issues_integration_active) }
diff --git a/spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb b/spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb
index a8f4b039b8c..9cf2d19f85c 100644
--- a/spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ExternalWikiMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb
index 44013898721..e64b0de9c62 100644
--- a/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::HiddenMenu do
let_it_be(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: project.repository.root_ref) }
describe '#render?' do
diff --git a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
index 55281171634..0e415ec6014 100644
--- a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::InfrastructureMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: false) }
describe '#render?' do
diff --git a/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb b/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb
index df9b260d211..9838aa8c3e3 100644
--- a/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Sidebars::Projects::Menus::InviteTeamMembersMenu do
subject(:invite_menu) { described_class.new(context) }
context 'when the project is viewed by an owner of the group' do
- let(:owner) { project.owner }
+ let(:owner) { project.first_owner }
describe '#render?' do
it 'renders the Invite team members link' do
diff --git a/spec/lib/sidebars/projects/menus/issues_menu_spec.rb b/spec/lib/sidebars/projects/menus/issues_menu_spec.rb
index e5d486bbe8f..4c0016a77a1 100644
--- a/spec/lib/sidebars/projects/menus/issues_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/issues_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::IssuesMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb
index cef303fb068..45c49500e46 100644
--- a/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::MergeRequestsMenu do
let_it_be(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
index 77efe99aaa9..e8c6fb790c3 100644
--- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
let_it_be_with_refind(:project) { create(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:show_cluster_hint) { true }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_cluster_hint: show_cluster_hint) }
diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
index d6807451a25..afe0b2a8951 100644
--- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do
let_it_be(:project) { create(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb b/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
index 7e8d0ab0518..24625413ded 100644
--- a/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/project_information_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ProjectInformationMenu do
let_it_be_with_reload(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe '#container_html_options' do
@@ -59,5 +59,11 @@ RSpec.describe Sidebars::Projects::Menus::ProjectInformationMenu do
specify { is_expected.to be_nil }
end
end
+
+ describe 'Hierarchy' do
+ let(:item_id) { :hierarchy }
+
+ specify { is_expected.not_to be_nil }
+ end
end
end
diff --git a/spec/lib/sidebars/projects/menus/repository_menu_spec.rb b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
index 554a4e3f532..fc181947e60 100644
--- a/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/repository_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::RepositoryMenu do
let_it_be(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: 'master') }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/scope_menu_spec.rb b/spec/lib/sidebars/projects/menus/scope_menu_spec.rb
index 980ab2f7c71..4e87f3b8ead 100644
--- a/spec/lib/sidebars/projects/menus/scope_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/scope_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ScopeMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe '#container_html_options' do
diff --git a/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
index 6e84beeb274..41158bd58dc 100644
--- a/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::SecurityComplianceMenu do
let_it_be(:project) { create(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:show_promotions) { true }
let(:show_discover_project_security) { true }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, show_promotions: show_promotions, show_discover_project_security: show_discover_project_security) }
diff --git a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
index 1e5d41dfec4..d6136dddf40 100644
--- a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::SettingsMenu do
let_it_be(:project) { create(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb
index 534267a329e..e74647894fa 100644
--- a/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::ShimoMenu do
let_it_be_with_reload(:project) { create(:project) }
- let(:context) { Sidebars::Projects::Context.new(current_user: project.owner, container: project) }
+ let(:context) { Sidebars::Projects::Context.new(current_user: project.first_owner, container: project) }
subject(:shimo_menu) { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/snippets_menu_spec.rb b/spec/lib/sidebars/projects/menus/snippets_menu_spec.rb
index af219e4a742..04b8c128e3d 100644
--- a/spec/lib/sidebars/projects/menus/snippets_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/snippets_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::SnippetsMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/sidebars/projects/menus/wiki_menu_spec.rb b/spec/lib/sidebars/projects/menus/wiki_menu_spec.rb
index 41447ee24a9..362da3e7b50 100644
--- a/spec/lib/sidebars/projects/menus/wiki_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/wiki_menu_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::WikiMenu do
let(:project) { build(:project) }
- let(:user) { project.owner }
+ let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
diff --git a/spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb b/spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb
index d7a77a84472..9c4aebaedd8 100644
--- a/spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb
+++ b/spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
MAIL_ROOM_CONFIG_ENABLED_SAMPLE =
":mailboxes:\n"\