summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-09-19 23:18:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-19 23:18:09 +0000
commit6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch)
treedc4d20fe6064752c0bd323187252c77e0a89144b /spec/lib/gitlab
parent9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff)
downloadgitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'spec/lib/gitlab')
-rw-r--r--spec/lib/gitlab/alert_management/payload/base_spec.rb22
-rw-r--r--spec/lib/gitlab/alert_management/payload/generic_spec.rb36
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb2
-rw-r--r--spec/lib/gitlab/analytics/date_filler_spec.rb136
-rw-r--r--spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc/html5_converter_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb6
-rw-r--r--spec/lib/gitlab/audit/auditor_spec.rb9
-rw-r--r--spec/lib/gitlab/audit/null_target_spec.rb2
-rw-r--r--spec/lib/gitlab/audit/type/definition_spec.rb219
-rw-r--r--spec/lib/gitlab/auth/ldap/auth_hash_spec.rb14
-rw-r--r--spec/lib/gitlab/auth/ldap/config_spec.rb202
-rw-r--r--spec/lib/gitlab/auth/ldap/person_spec.rb12
-rw-r--r--spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb10
-rw-r--r--spec/lib/gitlab/auth/o_auth/provider_spec.rb18
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb107
-rw-r--r--spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb30
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb57
-rw-r--r--spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb42
-rw-r--r--spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb32
-rw-r--r--spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb24
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb135
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb133
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb112
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb23
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb7
-rw-r--r--spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb89
-rw-r--r--spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb102
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb114
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb59
-rw-r--r--spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb92
-rw-r--r--spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb31
-rw-r--r--spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb91
-rw-r--r--spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb21
-rw-r--r--spec/lib/gitlab/bullet/exclusions_spec.rb1
-rw-r--r--spec/lib/gitlab/cache/helpers_spec.rb9
-rw-r--r--spec/lib/gitlab/changes_list_spec.rb2
-rw-r--r--spec/lib/gitlab/chat/responder/base_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/result_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/path_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/build/policy_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/port_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb46
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb173
-rw-r--r--spec/lib/gitlab/ci/config/entry/port_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb149
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb25
-rw-r--r--spec/lib/gitlab/ci/config/entry/variable_spec.rb212
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/mask_secret_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb273
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb38
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb64
-rw-r--r--spec/lib/gitlab/ci/pipeline/duration_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/metrics_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb68
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb55
-rw-r--r--spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/project_config/repository_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/project_config/source_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/project_config_spec.rb177
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/coverage_report_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/component_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/report_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/source_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/reports/security/flag_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/link_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/scan_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/terraform_reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/extended_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/group/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/trace/archive_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/variables/collection/sort_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/variables/helpers_spec.rb113
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/dag_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb185
-rw-r--r--spec/lib/gitlab/ci_access_spec.rb2
-rw-r--r--spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb168
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb22
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/attributable_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/composable_hash_spec.rb9
-rw-r--r--spec/lib/gitlab/config/entry/simplifiable_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/undefined_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/unspecified_spec.rb2
-rw-r--r--spec/lib/gitlab/container_repository/tags/cache_spec.rb12
-rw-r--r--spec/lib/gitlab/cross_project_access/check_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access/class_methods_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/summary/value_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/issuable_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/note_spec.rb32
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb68
-rw-r--r--spec/lib/gitlab/database/batch_average_counter_spec.rb107
-rw-r--r--spec/lib/gitlab/database/batch_count_spec.rb177
-rw-r--r--spec/lib/gitlab/database/lock_writes_manager_spec.rb84
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb27
-rw-r--r--spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb246
-rw-r--r--spec/lib/gitlab/database/partitioning/partition_manager_spec.rb89
-rw-r--r--spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb15
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb6
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb27
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb70
-rw-r--r--spec/lib/gitlab/database/partitioning_spec.rb73
-rw-r--r--spec/lib/gitlab/database/postgres_constraint_spec.rb123
-rw-r--r--spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb78
-rw-r--r--spec/lib/gitlab/database/reindexing_spec.rb22
-rw-r--r--spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb41
-rw-r--r--spec/lib/gitlab/database/tables_truncate_spec.rb257
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb4
-rw-r--r--spec/lib/gitlab/database_spec.rb20
-rw-r--r--spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb2
-rw-r--r--spec/lib/gitlab/dependency_linker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_collection_sorter_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/highlight_cache_spec.rb110
-rw-r--r--spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/inline_diff_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/lines_unfolder_spec.rb16
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb3
-rw-r--r--spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb45
-rw-r--r--spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb (renamed from spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb)2
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb30
-rw-r--r--spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb8
-rw-r--r--spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb2
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb10
-rw-r--r--spec/lib/gitlab/etag_caching/router/graphql_spec.rb2
-rw-r--r--spec/lib/gitlab/experimentation/group_types_spec.rb2
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb2
-rw-r--r--spec/lib/gitlab/file_markdown_link_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb209
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb2
-rw-r--r--spec/lib/gitlab/git/changed_path_spec.rb2
-rw-r--r--spec/lib/gitlab/git/changes_spec.rb2
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb70
-rw-r--r--spec/lib/gitlab/git/commit_stats_spec.rb16
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb5
-rw-r--r--spec/lib/gitlab/git/conflict/file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/conflict/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/cross_repo_comparer_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb18
-rw-r--r--spec/lib/gitlab/git/gitmodules_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/lfs_pointer_file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb613
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb2
-rw-r--r--spec/lib/gitlab/git/util_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb63
-rw-r--r--spec/lib/gitlab/git_spec.rb12
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb19
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb79
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb107
-rw-r--r--spec/lib/gitlab/gitaly_client/server_service_spec.rb42
-rw-r--r--spec/lib/gitlab/gitaly_client/util_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/attachments_downloader_spec.rb97
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb62
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb55
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb48
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb49
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb101
-rw-r--r--spec/lib/gitlab/github_import/importer/events/closed_spec.rb80
-rw-r--r--spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb95
-rw-r--r--spec/lib/gitlab/github_import/importer/events/renamed_spec.rb50
-rw-r--r--spec/lib/gitlab/github_import/importer/events/reopened_spec.rb63
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb18
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb91
-rw-r--r--spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb225
-rw-r--r--spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb57
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb74
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb74
-rw-r--r--spec/lib/gitlab/github_import/markdown_text_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb32
-rw-r--r--spec/lib/gitlab/github_import/representation/issue_event_spec.rb56
-rw-r--r--spec/lib/gitlab/github_import/representation/lfs_object_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/note_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/protected_branch_spec.rb51
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/release_attachments_spec.rb49
-rw-r--r--spec/lib/gitlab/github_import/representation/to_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/user_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/user_finder_spec.rb12
-rw-r--r--spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/limit/field_call_count_spec.rb66
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb403
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb27
-rw-r--r--spec/lib/gitlab/health_checks/gitaly_check_spec.rb24
-rw-r--r--spec/lib/gitlab/health_checks/master_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/probes/collection_spec.rb20
-rw-r--r--spec/lib/gitlab/health_checks/redis/cache_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/queues_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/redis_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis_spec.rb26
-rw-r--r--spec/lib/gitlab/i18n/metadata_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/i18n/translation_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/import/merge_request_creator_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml30
-rw-r--r--spec/lib/gitlab/import_export/attribute_cleaner_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/attributes_finder_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/config_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group/object_builder_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/json/legacy_writer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb65
-rw-r--r--spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml29
-rw-r--r--spec/lib/gitlab/import_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb20
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb2
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb2
-rw-r--r--spec/lib/gitlab/instrumentation/redis_base_spec.rb19
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb40
-rw-r--r--spec/lib/gitlab/instrumentation/redis_spec.rb12
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb14
-rw-r--r--spec/lib/gitlab/internal_post_receive/response_spec.rb2
-rw-r--r--spec/lib/gitlab/jira/middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/jira_import/metadata_collector_spec.rb2
-rw-r--r--spec/lib/gitlab/jira_import_spec.rb6
-rw-r--r--spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb2
-rw-r--r--spec/lib/gitlab/lazy_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/client_spec.rb24
-rw-r--r--spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/project_creator_spec.rb24
-rw-r--r--spec/lib/gitlab/loop_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb47
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown_cache/redis/extension_spec.rb8
-rw-r--r--spec/lib/gitlab/markup_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/memory/jemalloc_spec.rb12
-rw-r--r--spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb23
-rw-r--r--spec/lib/gitlab/memory/watchdog_spec.rb318
-rw-r--r--spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/defaults_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb30
-rw-r--r--spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb38
-rw-r--r--spec/lib/gitlab/metrics/dashboard/validator_spec.rb24
-rw-r--r--spec/lib/gitlab/metrics/delta_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb22
-rw-r--r--spec/lib/gitlab/metrics/global_search_slis_spec.rb173
-rw-r--r--spec/lib/gitlab/metrics/rack_middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb10
-rw-r--r--spec/lib/gitlab/metrics/subscribers/action_view_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb37
-rw-r--r--spec/lib/gitlab/metrics/transaction_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/web_transaction_spec.rb4
-rw-r--r--spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb1
-rw-r--r--spec/lib/gitlab/middleware/release_env_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb2
-rw-r--r--spec/lib/gitlab/namespaced_session_store_spec.rb2
-rw-r--r--spec/lib/gitlab/nav/top_nav_menu_header_spec.rb16
-rw-r--r--spec/lib/gitlab/nav/top_nav_menu_item_spec.rb4
-rw-r--r--spec/lib/gitlab/net_http_adapter_spec.rb2
-rw-r--r--spec/lib/gitlab/null_request_store_spec.rb2
-rw-r--r--spec/lib/gitlab/omniauth_initializer_spec.rb12
-rw-r--r--spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb29
-rw-r--r--spec/lib/gitlab/phabricator_import/representation/task_spec.rb2
-rw-r--r--spec/lib/gitlab/phabricator_import/representation/user_spec.rb2
-rw-r--r--spec/lib/gitlab/popen/runner_spec.rb2
-rw-r--r--spec/lib/gitlab/push_options_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/substitution_definition_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb94
-rw-r--r--spec/lib/gitlab/redis/boolean_spec.rb2
-rw-r--r--spec/lib/gitlab/redis/cache_spec.rb4
-rw-r--r--spec/lib/gitlab/redis/duplicate_jobs_spec.rb2
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb44
-rw-r--r--spec/lib/gitlab/redis/sidekiq_status_spec.rb2
-rw-r--r--spec/lib/gitlab/render_timeout_spec.rb2
-rw-r--r--spec/lib/gitlab/seeder_spec.rb40
-rw-r--r--spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb43
-rw-r--r--spec/lib/gitlab/service_desk_email_spec.rb2
-rw-r--r--spec/lib/gitlab/session_spec.rb2
-rw-r--r--spec/lib/gitlab/setup_helper/workhorse_spec.rb2
-rw-r--r--spec/lib/gitlab/shell_spec.rb53
-rw-r--r--spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb4
-rw-r--r--spec/lib/gitlab/sidekiq_death_handler_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb48
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_queue_spec.rb10
-rw-r--r--spec/lib/gitlab/sidekiq_signals_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_versioning_spec.rb25
-rw-r--r--spec/lib/gitlab/slug/environment_spec.rb28
-rw-r--r--spec/lib/gitlab/spamcheck/client_spec.rb59
-rw-r--r--spec/lib/gitlab/string_placeholder_replacer_spec.rb2
-rw-r--r--spec/lib/gitlab/string_range_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/string_regex_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb15
-rw-r--r--spec/lib/gitlab/tcp_checker_spec.rb2
-rw-r--r--spec/lib/gitlab/tracking/incident_management_spec.rb2
-rw-r--r--spec/lib/gitlab/tracking_spec.rb44
-rw-r--r--spec/lib/gitlab/tree_summary_spec.rb16
-rw-r--r--spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb66
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb35
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb22
-rw-r--r--spec/lib/gitlab/usage_data/topology_spec.rb16
-rw-r--r--spec/lib/gitlab/usage_data_counters/base_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb68
-rw-r--r--spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb4
-rw-r--r--spec/lib/gitlab/usage_data_counters/note_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_metrics_spec.rb8
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb29
-rw-r--r--spec/lib/gitlab/utils/deep_size_spec.rb8
-rw-r--r--spec/lib/gitlab/utils/delegator_override_spec.rb1
-rw-r--r--spec/lib/gitlab/utils/execution_tracker_spec.rb24
-rw-r--r--spec/lib/gitlab/utils/json_size_estimator_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/markdown_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/merge_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/nokogiri_spec.rb3
-rw-r--r--spec/lib/gitlab/utils/sanitize_node_link_spec.rb4
-rw-r--r--spec/lib/gitlab/utils_spec.rb2
-rw-r--r--spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb23
-rw-r--r--spec/lib/gitlab/word_diff/chunk_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/line_processor_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/parser_spec.rb20
-rw-r--r--spec/lib/gitlab/word_diff/positions_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/chunk_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/newline_spec.rb2
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
392 files changed, 9063 insertions, 3156 deletions
diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb
index ad2a3c7b462..3e8d71ac673 100644
--- a/spec/lib/gitlab/alert_management/payload/base_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb
@@ -347,4 +347,26 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
it { is_expected.to be(true) }
end
+
+ describe '#source' do
+ subject { parsed_payload.source }
+
+ it { is_expected.to be_nil }
+
+ context 'with alerting integration provided' do
+ before do
+ parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
+ end
+
+ it { is_expected.to eq('INTEGRATION') }
+ end
+
+ context 'with monitoring tool defined in the raw payload' do
+ before do
+ allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
+ end
+
+ it { is_expected.to eq('TOOL') }
+ end
+ end
end
diff --git a/spec/lib/gitlab/alert_management/payload/generic_spec.rb b/spec/lib/gitlab/alert_management/payload/generic_spec.rb
index 59933f7459d..bc3b6edc638 100644
--- a/spec/lib/gitlab/alert_management/payload/generic_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload/generic_spec.rb
@@ -144,4 +144,40 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do
it { is_expected.to eq(value) }
end
end
+
+ describe '#resolved?' do
+ subject { parsed_payload.resolved? }
+
+ context 'without end time' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'with end time' do
+ let(:raw_payload) { { 'end_time' => Time.current.to_s } }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ describe '#source' do
+ subject { parsed_payload.source }
+
+ it { is_expected.to eq('Generic Alert Endpoint') }
+
+ context 'with alerting integration provided' do
+ before do
+ parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
+ end
+
+ it { is_expected.to eq('INTEGRATION') }
+ end
+
+ context 'with monitoring tool defined in the raw payload' do
+ before do
+ allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
+ end
+
+ it { is_expected.to eq('TOOL') }
+ end
+ end
end
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
index 6fc658ecade..1e0034e386e 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do
let(:instance) { described_class.new({}) }
diff --git a/spec/lib/gitlab/analytics/date_filler_spec.rb b/spec/lib/gitlab/analytics/date_filler_spec.rb
new file mode 100644
index 00000000000..3f547f667f2
--- /dev/null
+++ b/spec/lib/gitlab/analytics/date_filler_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Analytics::DateFiller do
+ let(:default_value) { 0 }
+ let(:formatter) { Gitlab::Analytics::DateFiller::DEFAULT_DATE_FORMATTER }
+
+ subject(:filler_result) do
+ described_class.new(data,
+ from: from,
+ to: to,
+ period: period,
+ default_value: default_value,
+ date_formatter: formatter).fill.to_a
+ end
+
+ context 'when unknown period is given' do
+ it 'raises error' do
+ input = { 3.days.ago.to_date => 10, Date.today => 5 }
+
+ expect do
+ described_class.new(input, from: 4.days.ago, to: Date.today, period: :unknown).fill
+ end.to raise_error(/Unknown period given/)
+ end
+ end
+
+ context 'when period=:day' do
+ let(:from) { Date.new(2021, 5, 25) }
+ let(:to) { Date.new(2021, 6, 5) }
+ let(:period) { :day }
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 25) => 1,
+ Date.new(2021, 5, 26) => default_value,
+ Date.new(2021, 5, 27) => default_value,
+ Date.new(2021, 5, 28) => default_value,
+ Date.new(2021, 5, 29) => default_value,
+ Date.new(2021, 5, 30) => default_value,
+ Date.new(2021, 5, 31) => default_value,
+ Date.new(2021, 6, 1) => default_value,
+ Date.new(2021, 6, 2) => default_value,
+ Date.new(2021, 6, 3) => 10,
+ Date.new(2021, 6, 4) => default_value,
+ Date.new(2021, 6, 5) => default_value
+ }
+ end
+
+ let(:data) do
+ {
+ Date.new(2021, 6, 3) => 10, # deliberatly not sorted
+ Date.new(2021, 5, 27) => nil,
+ Date.new(2021, 5, 25) => 1
+ }
+ end
+
+ it { is_expected.to eq(expected_result.to_a) }
+
+ context 'when a custom default value is given' do
+ let(:default_value) { 'MISSING' }
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when a custom date formatter is given' do
+ let(:formatter) { -> (date) { date.to_s } }
+
+ it do
+ expected_result.transform_keys!(&:to_s)
+
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when the data contains dates outside of the requested period' do
+ before do
+ data[Date.new(2022, 6, 1)] = 5
+ end
+
+ it 'raises error' do
+ expect { filler_result }.to raise_error(/Input contains values which doesn't/)
+ end
+ end
+ end
+
+ context 'when period=:week' do
+ let(:from) { Date.new(2021, 5, 16) }
+ let(:to) { Date.new(2021, 6, 7) }
+ let(:period) { :week }
+ let(:data) do
+ {
+ Date.new(2021, 5, 24) => nil,
+ Date.new(2021, 6, 7) => 10
+ }
+ end
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 10) => 0,
+ Date.new(2021, 5, 17) => 0,
+ Date.new(2021, 5, 24) => 0,
+ Date.new(2021, 5, 31) => 0,
+ Date.new(2021, 6, 7) => 10
+ }
+ end
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when period=:month' do
+ let(:from) { Date.new(2021, 5, 1) }
+ let(:to) { Date.new(2021, 7, 1) }
+ let(:period) { :month }
+ let(:data) do
+ {
+ Date.new(2021, 5, 1) => 100
+ }
+ end
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 1) => 100,
+ Date.new(2021, 6, 1) => 0,
+ Date.new(2021, 7, 1) => 0
+ }
+ end
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb b/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
index b34ac538b24..12679b51ce9 100644
--- a/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
+++ b/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ApplicationRateLimiter::BaseStrategy do
describe '#increment' do
diff --git a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
index 84c2cda496e..de1b3e2af71 100644
--- a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
+++ b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Asciidoc::Html5Converter do
describe 'convert AsciiDoc to HTML5' do
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index b2bce2076b0..8fec8bce23e 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -638,9 +638,9 @@ module Gitlab
context 'with project' do
let(:context) do
{
- commit: commit,
- project: project,
- ref: ref,
+ commit: commit,
+ project: project,
+ ref: ref,
requested_path: requested_path
}
end
diff --git a/spec/lib/gitlab/audit/auditor_spec.rb b/spec/lib/gitlab/audit/auditor_spec.rb
index fc5917ca583..f743515e616 100644
--- a/spec/lib/gitlab/audit/auditor_spec.rb
+++ b/spec/lib/gitlab/audit/auditor_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Audit::Auditor do
let(:name) { 'audit_operation' }
- let(:author) { create(:user) }
+ let(:author) { create(:user, :with_sign_ins) }
let(:group) { create(:group) }
let(:provider) { 'standard' }
let(:context) do
@@ -37,6 +37,13 @@ RSpec.describe Gitlab::Audit::Auditor do
).and_call_original
audit!
+
+ authentication_event = AuthenticationEvent.last
+
+ expect(authentication_event.user).to eq(author)
+ expect(authentication_event.user_name).to eq(author.name)
+ expect(authentication_event.ip_address).to eq(author.current_sign_in_ip)
+ expect(authentication_event.provider).to eq(provider)
end
it 'logs audit events to database', :aggregate_failures do
diff --git a/spec/lib/gitlab/audit/null_target_spec.rb b/spec/lib/gitlab/audit/null_target_spec.rb
index f192e0cd8db..9197b72afd0 100644
--- a/spec/lib/gitlab/audit/null_target_spec.rb
+++ b/spec/lib/gitlab/audit/null_target_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Audit::NullTarget do
subject { described_class.new }
diff --git a/spec/lib/gitlab/audit/type/definition_spec.rb b/spec/lib/gitlab/audit/type/definition_spec.rb
new file mode 100644
index 00000000000..9f4282a4ec0
--- /dev/null
+++ b/spec/lib/gitlab/audit/type/definition_spec.rb
@@ -0,0 +1,219 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Audit::Type::Definition do
+ let(:attributes) do
+ { name: 'group_deploy_token_destroyed',
+ description: 'Group deploy token is deleted',
+ introduced_by_issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/1',
+ introduced_by_mr: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1',
+ group: 'govern::compliance',
+ milestone: '15.4',
+ saved_to_database: true,
+ streamed: true }
+ end
+
+ let(:path) { File.join('types', 'group_deploy_token_destroyed.yml') }
+ let(:definition) { described_class.new(path, attributes) }
+ let(:yaml_content) { attributes.deep_stringify_keys.to_yaml }
+
+ describe '#key' do
+ subject { definition.key }
+
+ it 'returns a symbol from name' do
+ is_expected.to eq(:group_deploy_token_destroyed)
+ end
+ end
+
+ describe '#validate!', :aggregate_failures do
+ using RSpec::Parameterized::TableSyntax
+
+ # rubocop:disable Layout/LineLength
+ where(:param, :value, :result) do
+ :path | 'audit_event/types/invalid.yml' | /Audit event type 'group_deploy_token_destroyed' has an invalid path/
+ :name | nil | %r{property '/name' is not of type: string}
+ :description | nil | %r{property '/description' is not of type: string}
+ :introduced_by_issue | nil | %r{property '/introduced_by_issue' is not of type: string}
+ :introduced_by_mr | nil | %r{property '/introduced_by_mr' is not of type: string}
+ :group | nil | %r{property '/group' is not of type: string}
+ :milestone | nil | %r{property '/milestone' is not of type: string}
+ end
+ # rubocop:enable Layout/LineLength
+
+ with_them do
+ let(:params) { attributes.merge(path: path) }
+
+ before do
+ params[param] = value
+ end
+
+ it do
+ expect do
+ described_class.new(
+ params[:path], params.except(:path)
+ ).validate!
+ end.to raise_error(result)
+ end
+ end
+
+ context 'when both saved_to_database and streamed are false' do
+ let(:params) { attributes.merge({ path: path, saved_to_database: false, streamed: false }) }
+
+ it 'raises an exception' do
+ expect do
+ described_class.new(
+ params[:path], params.except(:path)
+ ).validate!
+ end.to raise_error(/root is invalid: error_type=not/)
+ end
+ end
+ end
+
+ describe '.paths' do
+ it 'returns at least one path' do
+ expect(described_class.paths).not_to be_empty
+ end
+ end
+
+ describe '.get' do
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition }
+ end
+ end
+
+ context 'when audit event type is not defined' do
+ let(:undefined_audit_event_type) { 'undefined_audit_event_type' }
+
+ it 'returns nil' do
+ expect(described_class.get(undefined_audit_event_type)).to be nil
+ end
+ end
+
+ context 'when audit event type is defined' do
+ let(:audit_event_type) { 'group_deploy_token_destroyed' }
+
+ it 'returns an instance of Gitlab::Audit::Type::Definition' do
+ expect(described_class.get(audit_event_type)).to be_an_instance_of(described_class)
+ end
+
+ it 'returns the properties as defined for that audit event type', :aggregate_failures do
+ audit_event_type_definition = described_class.get(audit_event_type)
+
+ expect(audit_event_type_definition.name).to eq "group_deploy_token_destroyed"
+ expect(audit_event_type_definition.description).to eq "Group deploy token is deleted"
+ expect(audit_event_type_definition.group).to eq "govern::compliance"
+ expect(audit_event_type_definition.milestone).to eq "15.4"
+ expect(audit_event_type_definition.saved_to_database).to be true
+ expect(audit_event_type_definition.streamed).to be true
+ end
+ end
+ end
+
+ describe '.load_from_file' do
+ it 'properly loads a definition from file' do
+ expect_file_read(path, content: yaml_content)
+
+ expect(described_class.send(:load_from_file, path).attributes)
+ .to eq(definition.attributes)
+ end
+
+ context 'for missing file' do
+ let(:path) { 'missing/audit_events/type/file.yml' }
+
+ it 'raises exception' do
+ expect do
+ described_class.send(:load_from_file, path)
+ end.to raise_error(/Invalid definition for/)
+ end
+ end
+
+ context 'for invalid definition' do
+ it 'raises exception' do
+ expect_file_read(path, content: '{}')
+
+ expect do
+ described_class.send(:load_from_file, path)
+ end.to raise_error(%r{property '/name' is not of type: string})
+ end
+ end
+ end
+
+ describe '.load_all!' do
+ let(:store1) { Dir.mktmpdir('path1') }
+ let(:store2) { Dir.mktmpdir('path2') }
+ let(:definitions) { {} }
+
+ before do
+ allow(described_class).to receive(:paths).and_return(
+ [
+ File.join(store1, '**', '*.yml'),
+ File.join(store2, '**', '*.yml')
+ ]
+ )
+ end
+
+ subject { described_class.send(:load_all!) }
+
+ after do
+ FileUtils.rm_rf(store1)
+ FileUtils.rm_rf(store2)
+ end
+
+ it "when there are no audit event types a list of definitions is empty" do
+ is_expected.to be_empty
+ end
+
+ it "when there's a single audit event type it properly loads them" do
+ write_audit_event_type(store1, path, yaml_content)
+
+ is_expected.to be_one
+ end
+
+ it "when the same audit event type is stored multiple times raises exception" do
+ write_audit_event_type(store1, path, yaml_content)
+ write_audit_event_type(store2, path, yaml_content)
+
+ expect { subject }
+ .to raise_error(/Audit event type 'group_deploy_token_destroyed' is already defined/)
+ end
+
+ it "when one of the YAMLs is invalid it does raise exception" do
+ write_audit_event_type(store1, path, '{}')
+
+ expect { subject }.to raise_error(/Invalid definition for .* '' must match the filename/)
+ end
+ end
+
+ describe '.definitions' do
+ let(:store1) { Dir.mktmpdir('path1') }
+
+ before do
+ allow(described_class).to receive(:paths).and_return(
+ [
+ File.join(store1, '**', '*.yml')
+ ]
+ )
+ end
+
+ subject { described_class.definitions }
+
+ after do
+ FileUtils.rm_rf(store1)
+ end
+
+ it "loads the definitions for all the audit event types" do
+ write_audit_event_type(store1, path, yaml_content)
+
+ is_expected.to be_one
+ end
+ end
+
+ def write_audit_event_type(store, path, content)
+ path = File.join(store, path)
+ dir = File.dirname(path)
+ FileUtils.mkdir_p(dir)
+ File.write(path, content)
+ end
+end
diff --git a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
index 9dff7f7b3dc..e8008aeaf57 100644
--- a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
@@ -20,17 +20,17 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:info) do
{
- name: 'Smith, J.',
- email: 'johnsmith@example.com',
+ name: 'Smith, J.',
+ email: 'johnsmith@example.com',
nickname: '123456'
}
end
let(:raw_info) do
{
- uid: ['123456'],
- email: ['johnsmith@example.com'],
- cn: ['Smith, J.'],
+ uid: ['123456'],
+ email: ['johnsmith@example.com'],
+ cn: ['Smith, J.'],
fullName: ['John Smith']
}
end
@@ -52,8 +52,8 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:attributes) do
{
- 'username' => %w(mail email),
- 'name' => 'fullName'
+ 'username' => %w(mail email),
+ 'name' => 'fullName'
}
end
diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb
index 3039fce6141..3be983857bc 100644
--- a/spec/lib/gitlab/auth/ldap/config_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/config_spec.rb
@@ -112,8 +112,8 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 386,
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
'encryption' => 'plain'
}
)
@@ -129,16 +129,16 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes failover hosts when set' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'hosts' => [
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'hosts' => [
['ldap1.example.com', 636],
['ldap2.example.com', 636]
],
- 'encryption' => 'simple_tls',
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -158,12 +158,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -179,9 +179,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to simple_tls when configured as simple_tls' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls'
}
)
@@ -191,9 +191,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to start_tls when configured as start_tls' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls'
}
)
@@ -203,12 +203,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => raw_cert,
- 'key' => raw_key
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
}
}
)
@@ -221,12 +221,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
allow(Gitlab::AppLogger).to receive(:error)
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => 'invalid cert',
- 'key' => 'invalid_key'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => 'invalid cert',
+ 'key' => 'invalid_key'
}
}
)
@@ -240,9 +240,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets tls_options to OpenSSL defaults' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@@ -255,9 +255,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets verify_mode to OpenSSL VERIFY_NONE' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@@ -274,11 +274,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ca_file' => '/etc/ca.pem'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
}
}
)
@@ -291,11 +291,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ca_file key to tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ca_file' => ' '
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ca_file' => ' '
}
}
)
@@ -308,11 +308,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ssl_version' => 'TLSv1_2'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
}
}
)
@@ -325,11 +325,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ssl_version key to tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ssl_version' => ' '
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ssl_version' => ' '
}
}
)
@@ -343,11 +343,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 386,
- 'base' => 'ou=users,dc=example,dc=com',
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
+ 'base' => 'ou=users,dc=example,dc=com',
'encryption' => 'plain',
- 'uid' => 'uid'
+ 'uid' => 'uid'
}
)
@@ -364,10 +364,10 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
- 'uid' => 'sAMAccountName',
+ 'uid' => 'sAMAccountName',
'user_filter' => '(memberOf=cn=group1,ou=groups,dc=example,dc=com)',
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -381,12 +381,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => raw_cert,
- 'key' => raw_key
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
}
}
)
@@ -399,9 +399,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as false' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@@ -414,9 +414,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as true' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@@ -429,12 +429,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ca_file' => '/etc/ca.pem'
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
}
}
)
@@ -447,12 +447,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ca_file option' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ca_file' => ' '
+ 'tls_options' => {
+ 'ca_file' => ' '
}
}
)
@@ -465,12 +465,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ssl_version' => 'TLSv1_2'
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
}
}
)
@@ -483,12 +483,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ssl_version option' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ssl_version' => ' '
+ 'tls_options' => {
+ 'ssl_version' => ' '
}
}
)
@@ -503,7 +503,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when password is set' do
stub_ldap_config(
options: {
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@@ -514,7 +514,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when bind_dn is set and password is empty' do
stub_ldap_config(
options: {
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => ''
}
)
@@ -539,15 +539,15 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
options: {
'attributes' => {
'username' => %w(sAMAccountName),
- 'email' => %w(userPrincipalName)
+ 'email' => %w(userPrincipalName)
}
}
)
expect(config.attributes).to include({
'username' => %w(sAMAccountName),
- 'email' => %w(userPrincipalName),
- 'name' => 'cn'
+ 'email' => %w(userPrincipalName),
+ 'name' => 'cn'
})
end
end
diff --git a/spec/lib/gitlab/auth/ldap/person_spec.rb b/spec/lib/gitlab/auth/ldap/person_spec.rb
index 6857b561370..f8268bb1666 100644
--- a/spec/lib/gitlab/auth/ldap/person_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/person_spec.rb
@@ -10,10 +10,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
before do
stub_ldap_config(
options: {
- 'uid' => 'uid',
+ 'uid' => 'uid',
'attributes' => {
- 'name' => 'cn',
- 'email' => %w(mail email userPrincipalName),
+ 'name' => 'cn',
+ 'email' => %w(mail email userPrincipalName),
'username' => username_attribute
}
}
@@ -53,10 +53,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
it 'returns a compact and unique array' do
stub_ldap_config(
options: {
- 'uid' => nil,
+ 'uid' => nil,
'attributes' => {
- 'name' => 'cn',
- 'email' => 'mail',
+ 'name' => 'cn',
+ 'email' => 'mail',
'username' => %w(uid mail),
'first_name' => ''
}
diff --git a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
index a044094179c..c94f962ee93 100644
--- a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
@@ -40,12 +40,12 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
let(:info_hash) do
{
- email: email_ascii,
+ email: email_ascii,
first_name: first_name_ascii,
- last_name: last_name_ascii,
- name: name_ascii,
- nickname: nickname_ascii,
- uid: uid_ascii,
+ last_name: last_name_ascii,
+ name: name_ascii,
+ nickname: nickname_ascii,
+ uid: uid_ascii,
address: {
locality: 'some locality',
country: 'some country'
diff --git a/spec/lib/gitlab/auth/o_auth/provider_spec.rb b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
index c1b96819176..96a31c50989 100644
--- a/spec/lib/gitlab/auth/o_auth/provider_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
@@ -90,6 +90,24 @@ RSpec.describe Gitlab::Auth::OAuth::Provider do
end
end
end
+
+ context 'for an OpenID Connect provider' do
+ before do
+ provider = ActiveSupport::InheritableOptions.new(
+ name: 'openid_connect',
+ args: ActiveSupport::InheritableOptions.new(name: 'custom_oidc')
+ )
+ allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
+ end
+
+ context 'when the provider exists' do
+ subject { described_class.config_for('custom_oidc') }
+
+ it 'returns the config' do
+ expect(subject).to be_a(ActiveSupport::InheritableOptions)
+ end
+ end
+ end
end
describe '.label_for' do
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
index 57ee53a452e..61e17ad2424 100644
--- a/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
@@ -49,10 +49,10 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiTokenCloud do
stub_request(:post, otp_verification_url)
.with(body: JSON(otp_verification_request_body),
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Bearer #{access_token}"
- })
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Bearer #{access_token}"
+ })
.to_return(status: otp_verification_response_status, body: '', headers: {})
end
diff --git a/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..3aab0cdf54b
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillClusterAgentsHasVulnerabilities, :migration do # rubocop:disable Layout/LineLength
+ let(:migration) do
+ described_class.new(start_id: 1, end_id: 10,
+ batch_table: table_name, batch_column: batch_column,
+ sub_batch_size: sub_batch_size, pause_ms: pause_ms,
+ connection: ApplicationRecord.connection)
+ end
+
+ let(:users_table) { table(:users) }
+ let(:vulnerability_reads_table) { table(:vulnerability_reads) }
+ let(:vulnerability_scanners_table) { table(:vulnerability_scanners) }
+ let(:vulnerabilities_table) { table(:vulnerabilities) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:cluster_agents_table) { table(:cluster_agents) }
+
+ let(:table_name) { 'cluster_agents' }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 100 }
+ let(:pause_ms) { 0 }
+
+ subject(:perform_migration) { migration.perform }
+
+ before do
+ users_table.create!(id: 1, name: 'John Doe', email: 'test@example.com', projects_limit: 5)
+
+ namespaces_table.create!(id: 1, name: 'Namespace 1', path: 'namespace-1')
+ namespaces_table.create!(id: 2, name: 'Namespace 2', path: 'namespace-2')
+ namespaces_table.create!(id: 3, name: 'Namespace 3', path: 'namespace-3')
+
+ projects_table.create!(id: 1, namespace_id: 1, name: 'Project 1', path: 'project-1', project_namespace_id: 1)
+ projects_table.create!(id: 2, namespace_id: 2, name: 'Project 2', path: 'project-2', project_namespace_id: 2)
+ projects_table.create!(id: 3, namespace_id: 2, name: 'Project 3', path: 'project-3', project_namespace_id: 3)
+
+ cluster_agents_table.create!(id: 1, name: 'Agent 1', project_id: 1)
+ cluster_agents_table.create!(id: 2, name: 'Agent 2', project_id: 2)
+ cluster_agents_table.create!(id: 3, name: 'Agent 3', project_id: 1)
+ cluster_agents_table.create!(id: 4, name: 'Agent 4', project_id: 1)
+ cluster_agents_table.create!(id: 5, name: 'Agent 5', project_id: 1)
+ cluster_agents_table.create!(id: 6, name: 'Agent 6', project_id: 1)
+ cluster_agents_table.create!(id: 7, name: 'Agent 7', project_id: 3)
+ cluster_agents_table.create!(id: 8, name: 'Agent 8', project_id: 1)
+ cluster_agents_table.create!(id: 9, name: 'Agent 9', project_id: 1)
+ cluster_agents_table.create!(id: 10, name: 'Agent 10', project_id: 3)
+ cluster_agents_table.create!(id: 11, name: 'Agent 11', project_id: 1)
+
+ vulnerability_scanners_table.create!(id: 1, project_id: 1, external_id: 'starboard', name: 'Starboard')
+ vulnerability_scanners_table.create!(id: 2, project_id: 2, external_id: 'starboard', name: 'Starboard')
+ vulnerability_scanners_table.create!(id: 3, project_id: 3, external_id: 'starboard', name: 'Starboard')
+
+ add_vulnerability_read!(1, project_id: 1, cluster_agent_id: 1, report_type: 7)
+ add_vulnerability_read!(2, project_id: 1, cluster_agent_id: nil, report_type: 7)
+ add_vulnerability_read!(3, project_id: 1, cluster_agent_id: 3, report_type: 7)
+ add_vulnerability_read!(4, project_id: 1, cluster_agent_id: nil, report_type: 7)
+ add_vulnerability_read!(5, project_id: 2, cluster_agent_id: 5, report_type: 5)
+ add_vulnerability_read!(7, project_id: 2, cluster_agent_id: 7, report_type: 7)
+ add_vulnerability_read!(9, project_id: 3, cluster_agent_id: 9, report_type: 7)
+ add_vulnerability_read!(10, project_id: 1, cluster_agent_id: 10, report_type: 7)
+ add_vulnerability_read!(11, project_id: 2, cluster_agent_id: 11, report_type: 7)
+ end
+
+ it 'backfills `has_vulnerabilities` for the selected records', :aggregate_failures do
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(3)
+ expect(cluster_agents_table.where(has_vulnerabilities: true).count).to eq 2
+ expect(cluster_agents_table.where(has_vulnerabilities: true).pluck(:id)).to match_array([1, 3])
+ 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
+
+ private
+
+ def add_vulnerability_read!(id, project_id:, cluster_agent_id:, report_type:)
+ vulnerabilities_table.create!(
+ id: id,
+ project_id: project_id,
+ author_id: 1,
+ title: "Vulnerability #{id}",
+ severity: 5,
+ confidence: 5,
+ report_type: report_type
+ )
+
+ vulnerability_reads_table.create!(
+ id: id,
+ uuid: SecureRandom.uuid,
+ severity: 5,
+ state: 1,
+ vulnerability_id: id,
+ scanner_id: project_id,
+ casted_cluster_agent_id: cluster_agent_id,
+ project_id: project_id,
+ report_type: report_type
+ )
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb b/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
index e363a5a6b20..8947262ae9e 100644
--- a/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb
@@ -14,10 +14,10 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillImportedIssueSearchData,
table(:projects)
.create!(
namespace_id: namespace.id,
- creator_id: user.id,
- name: 'projecty',
- path: 'path',
- project_namespace_id: namespace.id)
+ creator_id: user.id,
+ name: 'projecty',
+ path: 'path',
+ project_namespace_id: namespace.id)
end
let!(:issue) do
diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
index b3825a7c4ea..3c0b7766871 100644
--- a/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
@@ -9,25 +9,35 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerific
before do
integrations.create!(id: 1, type_new: 'Integrations::Bamboo') # unaffected integration
integrations.create!(id: 2, type_new: 'Integrations::DroneCi') # no properties
- integrations.create!(id: 3, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 3, type_new: 'Integrations::DroneCi',
properties: {}) # no URL
- integrations.create!(id: 4, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 4, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => '' }) # blank URL
- integrations.create!(id: 5, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 5, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com:foo' }) # invalid URL
- integrations.create!(id: 6, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 6, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com' }) # unknown URL
- integrations.create!(id: 7, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 7, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'http://cloud.drone.io' }) # no HTTPS
- integrations.create!(id: 8, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 8, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://cloud.drone.io' }) # known URL
- integrations.create!(id: 9, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 9, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://example.com' }) # unknown URL
- integrations.create!(id: 10, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 10, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }) # unknown URL
- integrations.create!(id: 11, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 11, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://teamcity.com' }) # unknown URL
- integrations.create!(id: 12, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 12, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://customer.teamcity.com' }) # known URL
end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
new file mode 100644
index 00000000000..29833074109
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+# todo: this will need to specify schema version once we introduce the not null constraint on issues#namespace_id
+# https://gitlab.com/gitlab-org/gitlab/-/issues/367835
+RSpec.describe Gitlab::BackgroundMigration::BackfillProjectNamespaceOnIssues do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:issues) { table(:issues) }
+
+ let(:namespace1) { namespaces.create!(name: 'batchtest1', type: 'Group', path: 'space1') }
+ let(:namespace2) { namespaces.create!(name: 'batchtest2', type: 'Group', parent_id: namespace1.id, path: 'space2') }
+
+ let(:proj_namespace1) { namespaces.create!(name: 'proj1', path: 'proj1', type: 'Project', parent_id: namespace1.id) }
+ let(:proj_namespace2) { namespaces.create!(name: 'proj2', path: 'proj2', type: 'Project', parent_id: namespace2.id) }
+
+ # rubocop:disable Layout/LineLength
+ let(:proj1) { projects.create!(name: 'proj1', path: 'proj1', namespace_id: namespace1.id, project_namespace_id: proj_namespace1.id) }
+ let(:proj2) { projects.create!(name: 'proj2', path: 'proj2', namespace_id: namespace2.id, project_namespace_id: proj_namespace2.id) }
+
+ let!(:proj1_issue_with_namespace) { issues.create!(title: 'issue1', project_id: proj1.id, namespace_id: proj_namespace1.id) }
+ let!(:proj1_issue_without_namespace1) { issues.create!(title: 'issue2', project_id: proj1.id) }
+ let!(:proj1_issue_without_namespace2) { issues.create!(title: 'issue3', project_id: proj1.id) }
+ let!(:proj2_issue_with_namespace) { issues.create!(title: 'issue4', project_id: proj2.id, namespace_id: proj_namespace2.id) }
+ let!(:proj2_issue_without_namespace1) { issues.create!(title: 'issue5', project_id: proj2.id) }
+ let!(:proj2_issue_without_namespace2) { issues.create!(title: 'issue6', project_id: proj2.id) }
+ # rubocop:enable Layout/LineLength
+
+ let(:migration) do
+ described_class.new(
+ start_id: proj1_issue_with_namespace.id,
+ end_id: proj2_issue_without_namespace2.id,
+ batch_table: :issues,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 2,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:perform_migration) { migration.perform }
+
+ it 'backfills namespace_id for the selected records', :aggregate_failures do
+ perform_migration
+
+ expected_namespaces = [proj_namespace1.id, proj_namespace2.id]
+
+ expect(issues.where.not(namespace_id: nil).count).to eq(6)
+ expect(issues.where.not(namespace_id: nil).pluck(:namespace_id).uniq).to match_array(expected_namespaces)
+ 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_snippet_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
index 6f75d3faef3..1c2e0e991d9 100644
--- a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
@@ -14,23 +14,23 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let!(:user) do
users.create!(id: 1,
- email: 'user@example.com',
- projects_limit: 10,
- username: 'test',
- name: user_name,
- state: user_state,
- last_activity_on: 1.minute.ago,
- user_type: user_type,
- confirmed_at: 1.day.ago)
+ email: 'user@example.com',
+ projects_limit: 10,
+ username: 'test',
+ name: user_name,
+ state: user_state,
+ last_activity_on: 1.minute.ago,
+ user_type: user_type,
+ confirmed_at: 1.day.ago)
end
let!(:migration_bot) do
users.create!(id: 100,
- email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
- user_type: HasUserType::USER_TYPES[:migration_bot],
- name: 'GitLab Migration Bot',
- projects_limit: 10,
- username: 'bot')
+ email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
+ user_type: HasUserType::USER_TYPES[:migration_bot],
+ name: 'GitLab Migration Bot',
+ projects_limit: 10,
+ username: 'bot')
end
let!(:snippet_with_repo) { snippets.create!(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
@@ -260,14 +260,14 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let(:user_name) { '.' }
let!(:other_user) do
users.create!(id: 2,
- email: 'user2@example.com',
- projects_limit: 10,
- username: 'test2',
- name: 'Test2',
- state: user_state,
- last_activity_on: 1.minute.ago,
- user_type: user_type,
- confirmed_at: 1.day.ago)
+ email: 'user2@example.com',
+ projects_limit: 10,
+ username: 'test2',
+ name: 'Test2',
+ state: user_state,
+ last_activity_on: 1.minute.ago,
+ user_type: user_type,
+ confirmed_at: 1.day.ago)
end
let!(:invalid_snippet) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: '.', content: content) }
diff --git a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
index 79699375a8d..f642ec8c20d 100644
--- a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
@@ -23,8 +23,6 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge
let(:sub_batch_size) { 1_000 }
let(:pause_ms) { 0 }
- subject(:perform_migration) { migration.perform }
-
before do
users_table.create!(id: 1, name: 'John Doe', email: 'test@example.com', projects_limit: 5)
@@ -49,20 +47,30 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge
add_vulnerability_read!(11, project_id: 1, cluster_agent_id: 1, report_type: 7)
end
- it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do
- queries = ActiveRecord::QueryRecorder.new do
- perform_migration
+ describe '#filter_batch' do
+ it 'pick only vulnerability reads where report_type = 7' do
+ expect(migration.filter_batch(vulnerability_reads_table).pluck(:id)).to contain_exactly(1, 3, 7, 9, 10, 11)
end
-
- expect(queries.count).to eq(3)
- expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3
- expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 10])
end
- it 'tracks timings of queries' do
- expect(migration.batch_metrics.timings).to be_empty
+ describe '#perform' do
+ subject(:perform_migration) { migration.perform }
- expect { perform_migration }.to change { migration.batch_metrics.timings }
+ it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(3)
+ expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3
+ expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 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
private
diff --git a/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb b/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
index 8d82c533d20..6ef474ad7f9 100644
--- a/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb
@@ -2,12 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :migration, schema: 20220326161803 do
- subject(:migrate) { migration.perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, issue_type_enum[:issue], issue_type.id) }
-
- let(:migration) { described_class.new }
-
- let(:batch_table) { 'issues' }
+RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :migration, schema: 20220825142324 do
let(:batch_column) { 'id' }
let(:sub_batch_size) { 2 }
let(:pause_ms) { 0 }
@@ -15,7 +10,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :mi
# let_it_be can't be used in migration specs because all tables but `work_item_types` are deleted after each spec
let(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
let(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let(:project) { table(:projects).create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
let(:issues_table) { table(:issues) }
let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_type_enum[:issue]) }
@@ -32,6 +27,21 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :mi
let(:all_issues) { [issue1, issue2, issue3, incident1, test_case1, requirement1] }
+ let(:migration) do
+ described_class.new(
+ start_id: start_id,
+ end_id: end_id,
+ batch_table: :issues,
+ batch_column: :id,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ job_arguments: [issue_type_enum[:issue], issue_type.id],
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:migrate) { migration.perform }
+
it 'sets work_item_type_id only for the given type' do
expect(all_issues).to all(have_attributes(work_item_type_id: nil))
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb
deleted file mode 100644
index 3cba99bfe51..00000000000
--- a/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillIssueWorkItemTypeBatchingStrategy, '#next_batch', schema: 20220326161803 do # rubocop:disable Layout/LineLength
- # let! can't be used in migration specs because all tables but `work_item_types` are deleted after each spec
- let!(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
- let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let!(:issues_table) { table(:issues) }
- let!(:task_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_type_enum[:task]) }
-
- let!(:issue1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:task1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
- let!(:issue2) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:issue3) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:task2) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
- let!(:incident1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:incident]) }
- # test_case is EE only, but enum values exist on the FOSS model
- let!(:test_case1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:test_case]) }
-
- let!(:task3) do
- issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task], work_item_type_id: task_type.id)
- end
-
- let!(:task4) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
-
- let!(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
-
- context 'when issue_type is issue' do
- let(:job_arguments) { [issue_type_enum[:issue], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue1.id, 2)
-
- expect(batch_bounds).to match_array([issue1.id, issue2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue2.id, 2)
-
- expect(batch_bounds).to match_array([issue2.id, issue3.id])
- end
- end
-
- context 'when on the final batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue3.id, 2)
-
- expect(batch_bounds).to match_array([issue3.id, issue3.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = next_batch(issue3.id + 1, 1)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- context 'when issue_type is incident' do
- let(:job_arguments) { [issue_type_enum[:incident], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch with only one element' do
- batch_bounds = next_batch(incident1.id, 2)
-
- expect(batch_bounds).to match_array([incident1.id, incident1.id])
- end
- end
- end
-
- context 'when issue_type is requirement and there are no matching records' do
- let(:job_arguments) { [issue_type_enum[:requirement], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns nil' do
- batch_bounds = next_batch(1, 2)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- context 'when issue_type is task' do
- let(:job_arguments) { [issue_type_enum[:task], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(task1.id, 2)
-
- expect(batch_bounds).to match_array([task1.id, task2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch, does not skip records where FK is already set' do
- batch_bounds = next_batch(task2.id, 2)
-
- expect(batch_bounds).to match_array([task2.id, task3.id])
- end
- end
-
- context 'when on the final batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(task4.id, 2)
-
- expect(batch_bounds).to match_array([task4.id, task4.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = next_batch(task4.id + 1, 1)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- def next_batch(min_value, batch_size)
- batching_strategy.next_batch(
- :issues,
- :id,
- batch_min_value: min_value,
- batch_size: batch_size,
- job_arguments: job_arguments
- )
- end
-end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
index 94e9bcf9207..7076e82ea34 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
@@ -2,137 +2,6 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy, '#next_batch' do # rubocop:disable Layout/LineLength
- let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
- let(:namespace) { table(:namespaces) }
- let(:project) { table(:projects) }
- let(:container_repositories) { table(:container_repositories) }
-
- let!(:group) do
- namespace.create!(
- name: 'namespace1', type: 'Group', path: 'space1'
- )
- end
-
- let!(:proj_namespace1) do
- namespace.create!(
- name: 'proj1', path: 'proj1', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj_namespace2) do
- namespace.create!(
- name: 'proj2', path: 'proj2', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj_namespace3) do
- namespace.create!(
- name: 'proj3', path: 'proj3', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj1) do
- project.create!(
- name: 'proj1', path: 'proj1', namespace_id: group.id, project_namespace_id: proj_namespace1.id
- )
- end
-
- let!(:proj2) do
- project.create!(
- name: 'proj2', path: 'proj2', namespace_id: group.id, project_namespace_id: proj_namespace2.id
- )
- end
-
- let!(:proj3) do
- project.create!(
- name: 'proj3', path: 'proj3', namespace_id: group.id, project_namespace_id: proj_namespace3.id
- )
- end
-
- let!(:con1) do
- container_repositories.create!(
- project_id: proj1.id,
- name: "ContReg_#{proj1.id}:1",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con2) do
- container_repositories.create!(
- project_id: proj1.id,
- name: "ContReg_#{proj1.id}:2",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con3) do
- container_repositories.create!(
- project_id: proj2.id,
- name: "ContReg_#{proj2.id}:1",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con4) do
- container_repositories.create!(
- project_id: proj3.id,
- name: "ContReg_#{proj3.id}:1",
- migration_state: 'default',
- created_at: Date.new(2022, 02, 20)
- )
- end
-
- let!(:con5) do
- container_repositories.create!(
- project_id: proj3.id,
- name: "ContReg_#{proj3.id}:2",
- migration_state: 'default',
- created_at: Date.new(2022, 02, 20)
- )
- end
-
+RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy do # rubocop:disable Layout/LineLength
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :container_repositories,
- :project_id,
- batch_min_value: con1.project_id,
- batch_size: 3,
- job_arguments: []
- )
- expect(batch_bounds).to eq([con1.project_id, con4.project_id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :container_repositories,
- :project_id,
- batch_min_value: con3.project_id,
- batch_size: 3,
- job_arguments: []
- )
-
- expect(batch_bounds).to eq([con3.project_id, con5.project_id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = batching_strategy.next_batch(:container_repositories,
- :project_id,
- batch_min_value: con5.project_id + 1,
- batch_size: 1, job_arguments: []
- )
-
- expect(batch_bounds).to be_nil
- end
- end
end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
index f96c7de50f2..e4bef81e0bd 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
@@ -3,117 +3,5 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::DismissedVulnerabilitiesStrategy, '#next_batch' do
- let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
- let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let(:users) { table(:users) }
- let(:user) { create_user! }
- let(:project) do
- table(:projects).create!(
- namespace_id: namespace.id,
- project_namespace_id: namespace.id,
- packages_enabled: false)
- end
-
- let(:vulnerabilities) { table(:vulnerabilities) }
-
- let!(:vulnerability1) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability2) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability3) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability4) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: nil
- )
- end
-
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability1.id,
- batch_size: 2,
- job_arguments: []
- )
- expect(batch_bounds).to eq([vulnerability1.id, vulnerability2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch and skips the records that do not have `dismissed_at` set' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability3.id,
- batch_size: 2,
- job_arguments: []
- )
-
- expect(batch_bounds).to eq([vulnerability3.id, vulnerability3.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability4.id + 1,
- batch_size: 1,
- job_arguments: []
- )
-
- expect(batch_bounds).to be_nil
- end
- end
-
- private
-
- def create_vulnerability!(
- project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0, state: 1, dismissed_at: nil
- )
- vulnerabilities.create!(
- project_id: project_id,
- author_id: author_id,
- title: title,
- severity: severity,
- confidence: confidence,
- report_type: report_type,
- state: state,
- dismissed_at: dismissed_at
- )
- end
-
- def create_user!(name: "Example User", email: "user@example.com", user_type: nil)
- users.create!(
- name: name,
- email: email,
- username: name,
- projects_limit: 10
- )
- 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 9fdd7bf8adc..37fdd209622 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
@@ -60,26 +60,21 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
expect(batch_bounds).to eq([namespace4.id, namespace4.id])
end
- end
-
- context 'additional filters' do
- let(:strategy_with_filters) do
- Class.new(described_class) do
- def apply_additional_filters(relation, job_arguments:, job_class: nil)
- min_id = job_arguments.first
- relation.where.not(type: 'Project').where('id >= ?', min_id)
+ context 'when scope has a join which makes the column name ambiguous' do
+ let(:job_class) do
+ Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) do
+ scope_to ->(r) { r.joins('LEFT JOIN users ON users.id = namespaces.owner_id') }
end
end
- end
- let(:batching_strategy) { strategy_with_filters.new(connection: ActiveRecord::Base.connection) }
- let!(:namespace5) { namespaces.create!(name: 'batchtest5', path: 'batch-test5', type: 'Project') }
+ it 'executes the correct query' do
+ expect(job_class).to receive(:generic_instance).and_call_original
- it 'applies additional filters' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3, job_arguments: [1])
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3, job_arguments: [], job_class: job_class)
- expect(batch_bounds).to eq([namespace4.id, namespace4.id])
+ expect(batch_bounds).to eq([namespace4.id, namespace4.id])
+ end
end
end
end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb
new file mode 100644
index 00000000000..e296a46ea2f
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::RemoveBackfilledJobArtifactsExpireAtBatchingStrategy do # rubocop:disable Layout/LineLength
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
+end
diff --git a/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb b/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb
new file mode 100644
index 00000000000..76a9ea82c76
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DestroyInvalidGroupMembers, :migration, schema: 20220809002011 do
+ # rubocop: disable Layout/LineLength
+ # rubocop: disable RSpec/ScatteredLet
+ let!(:migration_attrs) do
+ {
+ start_id: 1,
+ end_id: 1000,
+ batch_table: :members,
+ batch_column: :id,
+ sub_batch_size: 100,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ }
+ end
+
+ let!(:migration) { described_class.new(**migration_attrs) }
+
+ subject(:perform_migration) { migration.perform }
+
+ let(:users_table) { table(:users) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:members_table) { table(:members) }
+ let(:projects_table) { table(:projects) }
+
+ let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 5) }
+ let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 5) }
+ let(:user3) { users_table.create!(name: 'user3', email: 'user3@example.com', projects_limit: 5) }
+ let(:user4) { users_table.create!(name: 'user4', email: 'user4@example.com', projects_limit: 5) }
+ let(:user5) { users_table.create!(name: 'user5', email: 'user5@example.com', projects_limit: 5) }
+ let(:user6) { users_table.create!(name: 'user6', email: 'user6@example.com', projects_limit: 5) }
+
+ let!(:group1) { namespaces_table.create!(name: 'marvellous group 1', path: 'group-path-1', type: 'Group') }
+
+ let!(:group2) { namespaces_table.create!(name: 'outstanding group 2', path: 'group-path-2', type: 'Group') }
+
+ # create group member records, a mix of both valid and invalid
+ # project members will have already been filtered out.
+ let!(:group_member1) { create_invalid_group_member(id: 1, user_id: user1.id) }
+
+ let!(:group_member4) { create_valid_group_member(id: 4, user_id: user2.id, group_id: group1.id) }
+
+ let!(:group_member5) { create_valid_group_member(id: 5, user_id: user3.id, group_id: group2.id) }
+
+ let!(:group_member6) { create_invalid_group_member(id: 6, user_id: user4.id) }
+
+ let!(:group_member7) { create_valid_group_member(id: 7, user_id: user5.id, group_id: group1.id) }
+
+ let!(:group_member8) { create_invalid_group_member(id: 8, user_id: user6.id) }
+
+ it 'removes invalid memberships but keeps valid ones', :aggregate_failures do
+ expect(members_table.where(type: 'GroupMember').count).to eq 6
+
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(4)
+ expect(members_table.where(type: 'GroupMember').pluck(:id)).to match_array([group_member4, group_member5, group_member7].map(&:id))
+ 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
+
+ it 'logs IDs of deleted records' do
+ expect(Gitlab::AppLogger).to receive(:info).with({ message: 'Removing invalid group member records',
+ deleted_count: 3, ids: [group_member1, group_member6, group_member8].map(&:id) })
+
+ perform_migration
+ end
+
+ def create_invalid_group_member(id:, user_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "GroupMember", source_type: "Namespace", notification_level: 3, member_namespace_id: nil)
+ end
+
+ def create_valid_group_member(id:, user_id:, group_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: group_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "GroupMember", source_type: "Namespace", member_namespace_id: group_id, notification_level: 3)
+ end
+ # rubocop: enable Layout/LineLength
+ # rubocop: enable RSpec/ScatteredLet
+end
diff --git a/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb b/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb
new file mode 100644
index 00000000000..029a6adf831
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DestroyInvalidProjectMembers, :migration, schema: 20220901035725 do
+ # rubocop: disable Layout/LineLength
+ # rubocop: disable RSpec/ScatteredLet
+ let!(:migration_attrs) do
+ {
+ start_id: 1,
+ end_id: 1000,
+ batch_table: :members,
+ batch_column: :id,
+ sub_batch_size: 100,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ }
+ end
+
+ let!(:migration) { described_class.new(**migration_attrs) }
+
+ subject(:perform_migration) { migration.perform }
+
+ let(:users_table) { table(:users) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:members_table) { table(:members) }
+ let(:projects_table) { table(:projects) }
+
+ let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 5) }
+ let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 5) }
+ let(:user3) { users_table.create!(name: 'user3', email: 'user3@example.com', projects_limit: 5) }
+ let(:user4) { users_table.create!(name: 'user4', email: 'user4@example.com', projects_limit: 5) }
+ let(:user5) { users_table.create!(name: 'user5', email: 'user5@example.com', projects_limit: 5) }
+ let(:user6) { users_table.create!(name: 'user6', email: 'user6@example.com', projects_limit: 5) }
+
+ let!(:group1) { namespaces_table.create!(name: 'marvellous group 1', path: 'group-path-1', type: 'Group') }
+
+ let!(:project_namespace1) do
+ namespaces_table.create!(name: 'fabulous project', path: 'project-path-1', type: 'ProjectNamespace',
+ parent_id: group1.id)
+ end
+
+ let!(:project1) do
+ projects_table.create!(name: 'fabulous project', path: 'project-path-1', project_namespace_id: project_namespace1.id,
+ namespace_id: group1.id)
+ end
+
+ let!(:project_namespace2) do
+ namespaces_table.create!(name: 'splendiferous project', path: 'project-path-2', type: 'ProjectNamespace',
+ parent_id: group1.id)
+ end
+
+ let!(:project2) do
+ projects_table.create!(name: 'splendiferous project', path: 'project-path-2', project_namespace_id: project_namespace2.id,
+ namespace_id: group1.id)
+ end
+
+ # create project member records, a mix of both valid and invalid
+ # group members will have already been filtered out.
+ let!(:project_member1) { create_invalid_project_member(id: 1, user_id: user1.id) }
+ let!(:project_member2) { create_valid_project_member(id: 4, user_id: user2.id, project: project1) }
+ let!(:project_member3) { create_valid_project_member(id: 5, user_id: user3.id, project: project2) }
+ let!(:project_member4) { create_invalid_project_member(id: 6, user_id: user4.id) }
+ let!(:project_member5) { create_valid_project_member(id: 7, user_id: user5.id, project: project2) }
+ let!(:project_member6) { create_invalid_project_member(id: 8, user_id: user6.id) }
+
+ it 'removes invalid memberships but keeps valid ones', :aggregate_failures do
+ expect(members_table.where(type: 'ProjectMember').count).to eq 6
+
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(4)
+ expect(members_table.where(type: 'ProjectMember')).to match_array([project_member2, project_member3, project_member5])
+ 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
+
+ it 'logs IDs of deleted records' do
+ expect(Gitlab::AppLogger).to receive(:info).with({ message: 'Removing invalid project member records',
+ deleted_count: 3, ids: [project_member1, project_member4, project_member6].map(&:id) })
+
+ perform_migration
+ end
+
+ def create_invalid_project_member(id:, user_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "ProjectMember", source_type: "Project", notification_level: 3, member_namespace_id: nil)
+ end
+
+ def create_valid_project_member(id:, user_id:, project:)
+ members_table.create!(id: id, user_id: user_id, source_id: project.id, access_level: Gitlab::Access::MAINTAINER,
+ type: "ProjectMember", source_type: "Project", member_namespace_id: project.project_namespace_id, notification_level: 3)
+ end
+ # rubocop: enable Layout/LineLength
+ # rubocop: enable RSpec/ScatteredLet
+end
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
new file mode 100644
index 00000000000..7edba8cf524
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenceForRecentPublicProjects, :migration do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:namespace_1) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-1') }
+ let(:project_namespace_2) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-2', type: 'Project') }
+ let(:project_namespace_3) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-3', type: 'Project') }
+ let(:project_namespace_4) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-4', type: 'Project') }
+ let(:project_namespace_5) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-5', type: 'Project') }
+ let(:project_namespace_6) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-6', type: 'Project') }
+ let(:project_namespace_7) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-7', type: 'Project') }
+ let(:project_namespace_8) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-8', type: 'Project') }
+
+ let(:project_1) do
+ projects_table
+ .create!(
+ name: 'proj-1', path: 'path-1', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_2.id, visibility_level: 0
+ )
+ end
+
+ let(:project_2) do
+ projects_table
+ .create!(
+ name: 'proj-2', path: 'path-2', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_3.id, visibility_level: 10, created_at: '2022-02-22'
+ )
+ end
+
+ let(:project_3) do
+ projects_table
+ .create!(
+ name: 'proj-3', path: 'path-3', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_4.id, visibility_level: 20, created_at: '2022-02-17 09:00:01'
+ )
+ end
+
+ let(:project_4) do
+ projects_table
+ .create!(
+ name: 'proj-4', path: 'path-4', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_5.id, visibility_level: 20, created_at: '2022-02-01'
+ )
+ end
+
+ let(:project_5) do
+ projects_table
+ .create!(
+ name: 'proj-5', path: 'path-5', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_6.id, visibility_level: 20, created_at: '2022-01-04'
+ )
+ end
+
+ let(:project_6) do
+ projects_table
+ .create!(
+ name: 'proj-6', path: 'path-6', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_7.id, visibility_level: 20, created_at: '2022-02-17 08:59:59'
+ )
+ end
+
+ let(:project_7) do
+ projects_table
+ .create!(
+ name: 'proj-7', path: 'path-7', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_8.id, visibility_level: 20, created_at: '2022-02-17 09:00:00'
+ )
+ end
+
+ let(:projects_table) { table(:projects) }
+ let(:project_settings_table) { table(:project_settings) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: projects_table.minimum(:id),
+ end_id: projects_table.maximum(:id),
+ batch_table: :projects,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ before do
+ project_settings_table.create!(project_id: project_1.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_2.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_3.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_4.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_5.id, legacy_open_source_license_available: false)
+ project_settings_table.create!(project_id: project_6.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_7.id, legacy_open_source_license_available: true)
+ end
+
+ it 'sets `legacy_open_source_license_available` attribute to false for public projects created after threshold time',
+ :aggregate_failures do
+ record = ActiveRecord::QueryRecorder.new do
+ expect { perform_migration }
+ .to not_change { migrated_attribute(project_1.id) }.from(true)
+ .and not_change { migrated_attribute(project_2.id) }.from(true)
+ .and change { migrated_attribute(project_3.id) }.from(true).to(false)
+ .and not_change { migrated_attribute(project_4.id) }.from(true)
+ .and not_change { migrated_attribute(project_5.id) }.from(false)
+ .and not_change { migrated_attribute(project_6.id) }.from(true)
+ .and change { migrated_attribute(project_7.id) }.from(true).to(false)
+ end
+ expect(record.count).to eq(19)
+ end
+
+ def migrated_attribute(project_id)
+ project_settings_table.find(project_id).legacy_open_source_license_available
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
new file mode 100644
index 00000000000..205350f9df4
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb,
+ :migration,
+ schema: 20220906074449 do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:project_settings_table) { table(:project_settings) }
+ let(:project_statistics_table) { table(:project_statistics) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: project_settings_table.minimum(:project_id),
+ end_id: project_settings_table.maximum(:project_id),
+ batch_table: :project_settings,
+ batch_column: :project_id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ it 'sets `legacy_open_source_license_available` to false only for projects less than 1 MB',
+ :aggregate_failures do
+ project_setting_1_mb = create_legacy_license_project_setting(repo_size: 1)
+ project_setting_2_mb = create_legacy_license_project_setting(repo_size: 2)
+ project_setting_quarter_mb = create_legacy_license_project_setting(repo_size: 0.25)
+ project_setting_half_mb = create_legacy_license_project_setting(repo_size: 0.5)
+
+ queries = ActiveRecord::QueryRecorder.new { perform_migration }
+
+ expect(queries.count).to eq(7)
+ expect(migrated_attribute(project_setting_1_mb)).to be_truthy
+ expect(migrated_attribute(project_setting_2_mb)).to be_truthy
+ expect(migrated_attribute(project_setting_quarter_mb)).to be_falsey
+ expect(migrated_attribute(project_setting_half_mb)).to be_falsey
+ end
+
+ private
+
+ # @param repo_size: Repo size in MB
+ def create_legacy_license_project_setting(repo_size:)
+ path = "path-for-repo-size-#{repo_size}"
+ namespace = namespaces_table.create!(name: "namespace-#{path}", path: "namespace-#{path}")
+ project_namespace =
+ namespaces_table.create!(name: "-project-namespace-#{path}", path: "project-namespace-#{path}", type: 'Project')
+ project = projects_table
+ .create!(name: path, path: path, namespace_id: namespace.id, project_namespace_id: project_namespace.id)
+
+ size_in_bytes = 1.megabyte * repo_size
+ project_statistics_table.create!(project_id: project.id, namespace_id: namespace.id, repository_size: size_in_bytes)
+ project_settings_table.create!(project_id: project.id, legacy_open_source_license_available: true)
+ end
+
+ def migrated_attribute(project_setting)
+ project_settings_table.find(project_setting.project_id).legacy_open_source_license_available
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb b/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
index 38e8b159e63..c788b701d79 100644
--- a/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
+++ b/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
@@ -40,10 +40,10 @@ RSpec.describe Gitlab::BackgroundMigration::EncryptIntegrationProperties, schema
expect(integrations.count).to eq(4)
expect(props).to match(
- no_properties.id => both(be_nil),
+ no_properties.id => both(be_nil),
with_plaintext_1.id => both(eq some_props(1)),
with_plaintext_2.id => both(eq some_props(2)),
- with_encrypted.id => match([be_nil, eq(some_props(3))])
+ with_encrypted.id => match([be_nil, eq(some_props(3))])
)
end
diff --git a/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb b/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
new file mode 100644
index 00000000000..41266cb24da
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RemoveBackfilledJobArtifactsExpireAt do
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchedMigrationJob }
+
+ describe '#perform' do
+ let(:job_artifact) { table(:ci_job_artifacts, database: :ci) }
+
+ let(:test_worker) do
+ described_class.new(
+ start_id: 1,
+ end_id: 100,
+ batch_table: :ci_job_artifacts,
+ batch_column: :id,
+ sub_batch_size: 10,
+ pause_ms: 0,
+ connection: Ci::ApplicationRecord.connection
+ )
+ end
+
+ let_it_be(:namespace) { table(:namespaces).create!(id: 1, name: 'user', path: 'user') }
+ let_it_be(:project) do
+ table(:projects).create!(
+ id: 1,
+ name: 'gitlab1',
+ path: 'gitlab1',
+ project_namespace_id: 1,
+ namespace_id: namespace.id
+ )
+ end
+
+ subject { test_worker.perform }
+
+ context 'with artifacts that has backfilled expire_at' do
+ let!(:created_on_00_30_45_minutes_on_21_22_23) do
+ create_job_artifact(id: 1, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:00:00.000'))
+ create_job_artifact(id: 2, file_type: 1, expire_at: Time.zone.parse('2022-01-21 01:30:00.000'))
+ create_job_artifact(id: 3, file_type: 1, expire_at: Time.zone.parse('2022-01-22 12:00:00.000'))
+ create_job_artifact(id: 4, file_type: 1, expire_at: Time.zone.parse('2022-01-22 12:30:00.000'))
+ create_job_artifact(id: 5, file_type: 1, expire_at: Time.zone.parse('2022-01-23 23:00:00.000'))
+ create_job_artifact(id: 6, file_type: 1, expire_at: Time.zone.parse('2022-01-23 23:30:00.000'))
+ create_job_artifact(id: 7, file_type: 1, expire_at: Time.zone.parse('2022-01-23 06:45:00.000'))
+ end
+
+ let!(:created_close_to_00_or_30_minutes) do
+ create_job_artifact(id: 8, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:00:00.001'))
+ create_job_artifact(id: 9, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:30:00.999'))
+ end
+
+ let!(:created_on_00_or_30_minutes_on_other_dates) do
+ create_job_artifact(id: 10, file_type: 1, expire_at: Time.zone.parse('2022-01-01 00:00:00.000'))
+ create_job_artifact(id: 11, file_type: 1, expire_at: Time.zone.parse('2022-01-19 12:00:00.000'))
+ create_job_artifact(id: 12, file_type: 1, expire_at: Time.zone.parse('2022-01-24 23:30:00.000'))
+ end
+
+ let!(:created_at_other_times) do
+ create_job_artifact(id: 13, file_type: 1, expire_at: Time.zone.parse('2022-01-19 00:00:00.000'))
+ create_job_artifact(id: 14, file_type: 1, expire_at: Time.zone.parse('2022-01-19 00:30:00.000'))
+ create_job_artifact(id: 15, file_type: 1, expire_at: Time.zone.parse('2022-01-24 00:00:00.000'))
+ create_job_artifact(id: 16, file_type: 1, expire_at: Time.zone.parse('2022-01-24 00:30:00.000'))
+ end
+
+ it 'removes expire_at on job artifacts that have expire_at on 00, 30 or 45 minute of 21, 22, 23 of the month' do
+ expect { subject }.to change { job_artifact.where(expire_at: nil).count }.from(0).to(7)
+ end
+
+ it 'keeps expire_at on other job artifacts' do
+ expect { subject }.to change { job_artifact.where.not(expire_at: nil).count }.from(16).to(9)
+ end
+ end
+
+ context 'with trace artifacts that has backfilled expire_at' do
+ let!(:trace_artifacts) do
+ create_job_artifact(id: 1, file_type: 3, expire_at: Time.zone.parse('2022-01-01 00:00:00.000'))
+ create_job_artifact(id: 2, file_type: 3, expire_at: Time.zone.parse('2022-01-21 00:00:00.000'))
+ end
+
+ it 'removes expire_at on trace job artifacts' do
+ expect { subject }.to change { job_artifact.where(expire_at: nil).count }.from(0).to(2)
+ end
+ end
+
+ private
+
+ def create_job_artifact(id:, file_type:, expire_at:)
+ job = table(:ci_builds, database: :ci).create!(id: id)
+ job_artifact.create!(id: id, job_id: job.id, expire_at: expire_at, project_id: project.id, file_type: file_type)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb b/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb
new file mode 100644
index 00000000000..81927100562
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RemoveSelfManagedWikiNotes, :migration, schema: 20220601110011 do
+ let(:notes) { table(:notes) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: 1,
+ end_id: 30,
+ batch_table: :notes,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ it 'removes all wiki notes' do
+ notes.create!(id: 2, note: 'Commit note', noteable_type: 'Commit')
+ notes.create!(id: 10, note: 'Issue note', noteable_type: 'Issue')
+ notes.create!(id: 20, note: 'Wiki note', noteable_type: 'Wiki')
+ notes.create!(id: 30, note: 'MergeRequest note', noteable_type: 'MergeRequest')
+
+ expect(notes.where(noteable_type: 'Wiki').size).to eq(1)
+
+ expect { perform_migration }.to change(notes, :count).by(-1)
+
+ expect(notes.where(noteable_type: 'Wiki').size).to eq(0)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
new file mode 100644
index 00000000000..45932defaf9
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RenameTaskSystemNoteToChecklistItem do
+ let(:notes) { table(:notes) }
+ let(:projects) { table(:projects) }
+
+ let(:namespace) { table(:namespaces).create!(name: 'batchtest1', type: 'Group', path: 'space1') }
+ let(:project) { table(:projects).create!(name: 'proj1', path: 'proj1', namespace_id: namespace.id) }
+ let(:issue) { table(:issues).create!(title: 'Test issue') }
+
+ let!(:note1) do
+ notes.create!(
+ note: 'marked the task **Task 1** as complete', noteable_type: 'Issue', noteable_id: issue.id, system: true
+ )
+ end
+
+ let!(:note2) do
+ notes.create!(
+ note: 'NO_MATCH marked the task **Task 2** as complete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:note3) do
+ notes.create!(
+ note: 'marked the task **Task 3** as incomplete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:note4) do
+ notes.create!(
+ note: 'marked the task **Task 4** as incomplete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:metadata1) { table(:system_note_metadata).create!(note_id: note1.id, action: :task) }
+ let!(:metadata2) { table(:system_note_metadata).create!(note_id: note2.id, action: :task) }
+ let!(:metadata3) { table(:system_note_metadata).create!(note_id: note3.id, action: :task) }
+ let!(:metadata4) { table(:system_note_metadata).create!(note_id: note4.id, action: :not_task) }
+
+ let(:migration) do
+ described_class.new(
+ start_id: metadata1.id,
+ end_id: metadata4.id,
+ batch_table: :system_note_metadata,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 2,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:perform_migration) { migration.perform }
+
+ it 'renames task to checklist item in task system notes that match', :aggregate_failures do
+ expect do
+ perform_migration
+
+ note1.reload
+ note2.reload
+ note3.reload
+ note4.reload
+ end.to change(note1, :note).to('marked the checklist item **Task 1** as complete').and(
+ not_change(note2, :note).from('NO_MATCH marked the task **Task 2** as complete')
+ ).and(
+ change(note3, :note).to('marked the checklist item **Task 3** as incomplete')
+ ).and(
+ not_change(note4, :note).from('marked the task **Task 4** as incomplete')
+ )
+ end
+
+ it 'updates in batches' do
+ expect { perform_migration }.to make_queries_matching(/UPDATE notes/, 2)
+ 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/set_correct_vulnerability_state_spec.rb b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
index d5b98e49a31..2372ce21c4c 100644
--- a/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
+++ b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do
let(:detected_state) { 1 }
let(:dismissed_state) { 2 }
- subject(:perform_migration) do
+ let(:migration_job) do
described_class.new(start_id: vulnerability_with_dismissed_at.id,
end_id: vulnerability_without_dismissed_at.id,
batch_table: :vulnerabilities,
@@ -42,15 +42,24 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do
sub_batch_size: 1,
pause_ms: 0,
connection: ActiveRecord::Base.connection)
- .perform
end
- it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do
- expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state)
+ describe '#filter_batch' do
+ it 'filters out vulnerabilities where dismissed_at is null' do
+ expect(migration_job.filter_batch(vulnerabilities)).to contain_exactly(vulnerability_with_dismissed_at)
+ end
end
- it 'does not change the state when dismissed_at is nil' do
- expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state }
+ describe '#perform' do
+ subject(:perform_migration) { migration_job.perform }
+
+ it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do
+ expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state)
+ end
+
+ it 'does not change the state when dismissed_at is nil' do
+ expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state }
+ end
end
private
diff --git a/spec/lib/gitlab/bullet/exclusions_spec.rb b/spec/lib/gitlab/bullet/exclusions_spec.rb
index ba42156b0c4..325b0167f58 100644
--- a/spec/lib/gitlab/bullet/exclusions_spec.rb
+++ b/spec/lib/gitlab/bullet/exclusions_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tempfile'
RSpec.describe Gitlab::Bullet::Exclusions do
let(:config_file) do
diff --git a/spec/lib/gitlab/cache/helpers_spec.rb b/spec/lib/gitlab/cache/helpers_spec.rb
index 08e0d7729bd..39d37e979b4 100644
--- a/spec/lib/gitlab/cache/helpers_spec.rb
+++ b/spec/lib/gitlab/cache/helpers_spec.rb
@@ -18,10 +18,7 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
end
describe "#render_cached" do
- subject do
- instance.render_cached(presentable, **kwargs)
- end
-
+ let(:method) { :render_cached }
let(:kwargs) do
{
with: presenter,
@@ -29,6 +26,10 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
}
end
+ subject do
+ instance.public_send(method, presentable, **kwargs)
+ end
+
context 'single object' do
let_it_be(:presentable) { create(:merge_request, source_project: project, source_branch: 'wip') }
diff --git a/spec/lib/gitlab/changes_list_spec.rb b/spec/lib/gitlab/changes_list_spec.rb
index 8292764f561..762a121340e 100644
--- a/spec/lib/gitlab/changes_list_spec.rb
+++ b/spec/lib/gitlab/changes_list_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::ChangesList do
let(:valid_changes_string) { "\n000000 570e7b2 refs/heads/my_branch\nd14d6c 6fd24d refs/heads/master" }
diff --git a/spec/lib/gitlab/chat/responder/base_spec.rb b/spec/lib/gitlab/chat/responder/base_spec.rb
index 667228cbab4..2a253449678 100644
--- a/spec/lib/gitlab/chat/responder/base_spec.rb
+++ b/spec/lib/gitlab/chat/responder/base_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Chat::Responder::Base do
let(:project) { double(:project) }
diff --git a/spec/lib/gitlab/ci/ansi2json/parser_spec.rb b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
index cf93ebe0721..b11002e8e93 100644
--- a/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
# The rest of the specs for this class are covered in style_spec.rb
RSpec.describe Gitlab::Ci::Ansi2json::Parser do
diff --git a/spec/lib/gitlab/ci/ansi2json/result_spec.rb b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
index b7b4d6de8b9..14e2a9625fe 100644
--- a/spec/lib/gitlab/ci/ansi2json/result_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Ansi2json::Result do
let(:stream) { StringIO.new('hello') }
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
deleted file mode 100644
index 2c236ba3726..00000000000
--- a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Ci::Build::Artifacts::Adapters::ZipStream do
- let(:file_name) { 'single_file.zip' }
- let(:fixture_path) { "lib/gitlab/ci/build/artifacts/adapters/zip_stream/#{file_name}" }
- let(:stream) { File.open(expand_fixture_path(fixture_path), 'rb') }
-
- describe '#initialize' do
- it 'initializes when stream is passed' do
- expect { described_class.new(stream) }.not_to raise_error
- end
-
- context 'when stream is not passed' do
- let(:stream) { nil }
-
- it 'raises an error' do
- expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
- end
- end
- end
-
- describe '#each_blob' do
- let(:adapter) { described_class.new(stream) }
-
- context 'when stream is a zip file' do
- it 'iterates file content when zip file contains one file' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_with_args("file 1 content\n")
- end
-
- context 'when zip file contains multiple files' do
- let(:file_name) { 'multiple_files.zip' }
-
- it 'iterates content of all files' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_successive_args("file 1 content\n", "file 2 content\n")
- end
- end
-
- context 'when zip file includes files in a directory' do
- let(:file_name) { 'with_directory.zip' }
-
- it 'iterates contents from files only' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_successive_args("file 1 content\n", "file 2 content\n")
- end
- end
-
- context 'when zip contains a file which decompresses beyond the size limit' do
- let(:file_name) { '200_mb_decompressed.zip' }
-
- it 'does not read the file' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- end
- end
-
- context 'when the zip contains too many files' do
- let(:file_name) { '100_files.zip' }
-
- it 'stops processing when the limit is reached' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_control.exactly(described_class::MAX_FILES_PROCESSED).times
- end
- end
-
- context 'when stream is a zipbomb' do
- let(:file_name) { 'zipbomb.zip' }
-
- it 'does not read the file' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- end
- end
- end
-
- context 'when stream is not a zip file' do
- let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
-
- it 'does not yield any data' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- expect { adapter.each_blob { |b| b } }.not_to raise_error
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
index 27b7dac2ae4..773eaf4b3fc 100644
--- a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Artifacts::Path do
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 436ad59bdf7..e560f1c2b5a 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -108,7 +108,8 @@ RSpec.describe Gitlab::Ci::Build::Policy::Variables do
project: project,
ref: 'master',
stage: 'review',
- environment: 'test/$CI_JOB_STAGE/1')
+ environment: 'test/$CI_JOB_STAGE/1',
+ ci_stage: build(:ci_stage, name: 'review', project: project, pipeline: pipeline))
end
before do
diff --git a/spec/lib/gitlab/ci/build/policy_spec.rb b/spec/lib/gitlab/ci/build/policy_spec.rb
index b85b093fd03..4baf4a1b4c4 100644
--- a/spec/lib/gitlab/ci/build/policy_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Policy do
let(:policy) { spy('policy specification') }
diff --git a/spec/lib/gitlab/ci/build/port_spec.rb b/spec/lib/gitlab/ci/build/port_spec.rb
index 480418e0851..51820c1ab2c 100644
--- a/spec/lib/gitlab/ci/build/port_spec.rb
+++ b/spec/lib/gitlab/ci/build/port_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Port do
subject { described_class.new(port) }
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
index f192862c1c4..f9ebab149a5 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
@@ -3,44 +3,54 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
- shared_examples 'an exists rule with a context' do
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+ describe '#satisfied_by?' do
+ shared_examples 'an exists rule with a context' do
+ it_behaves_like 'a glob matching rule' do
+ let(:project) { create(:project, :custom_repo, files: files) }
+ end
- it_behaves_like 'a glob matching rule' do
- let(:project) { create(:project, :custom_repo, files: files) }
- end
+ context 'after pattern comparision limit is reached' do
+ let(:globs) { ['*definitely_not_a_matching_glob*'] }
+ let(:project) { create(:project, :repository) }
- context 'after pattern comparision limit is reached' do
- let(:globs) { ['*definitely_not_a_matching_glob*'] }
- let(:project) { create(:project, :repository) }
+ before do
+ stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
+ expect(File).to receive(:fnmatch?).twice.and_call_original
+ end
- before do
- stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
- expect(File).to receive(:fnmatch?).twice.and_call_original
+ it { is_expected.to be_truthy }
end
-
- it { is_expected.to be_truthy }
end
- end
- describe '#satisfied_by?' do
- let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.head_commit.sha) }
+ subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) }
context 'when context is Build::Context::Build' do
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: 'abc1234') }
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
+ let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: project.repository.commit.sha) }
end
end
context 'when context is Build::Context::Global' do
it_behaves_like 'an exists rule with a context' do
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
let(:context) { Gitlab::Ci::Build::Context::Global.new(pipeline, yaml_variables: {}) }
end
end
context 'when context is Config::External::Context' do
+ let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: sha) }
+
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: project.repository.tree.sha) }
+ let(:sha) { project.repository.commit.sha }
+ end
+
+ context 'when context has no project' do
+ let(:globs) { ['Dockerfile'] }
+ let(:project) {}
+ let(:sha) { 'abc1234' }
+
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index 36c26c8ee4f..3562706ff33 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -230,12 +230,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
end
end
- context 'when auto_stop_in is invalid format' do
- let(:auto_stop_in) { 'invalid' }
+ context 'when variables are used for auto_stop_in' do
+ let(:auto_stop_in) { '$TTL' }
- it 'becomes invalid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include 'environment auto stop in should be a duration'
+ it 'becomes valid' do
+ expect(entry).to be_valid
+ expect(entry.auto_stop_in).to eq(auto_stop_in)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index 6121c28070f..b37498ba10a 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -4,8 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Image do
before do
- stub_feature_flags(ci_docker_image_pull_policy: true)
-
entry.compose!
end
@@ -129,18 +127,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do
it 'is valid' do
expect(entry).to be_valid
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include('image config contains unknown keys: pull_policy')
- end
- end
end
describe '#value' do
@@ -150,19 +136,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do
pull_policy: ['if-not-present']
)
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry.value).to eq(
- name: 'image:1.0'
- )
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index ca336c3ecaa..75ac2ca87ab 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -605,8 +605,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
let(:deps) do
double('deps',
'default_entry' => default,
- 'workflow_entry' => workflow,
- 'variables_value' => nil)
+ 'workflow_entry' => workflow)
end
context 'when job config overrides default config' do
diff --git a/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb b/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
new file mode 100644
index 00000000000..e9edec9a0a4
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::LegacyVariables do
+ let(:config) { {} }
+ let(:metadata) { {} }
+
+ subject(:entry) { described_class.new(config, **metadata) }
+
+ before do
+ entry.compose!
+ end
+
+ shared_examples 'valid config' do
+ describe '#value' do
+ it 'returns hash with key value strings' do
+ expect(entry.value).to eq result
+ 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
+
+ shared_examples 'invalid config' do |error_message|
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include(error_message)
+ end
+ end
+ end
+
+ context 'when entry config value has key-value pairs' do
+ let(:config) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
+ end
+
+ context 'with numeric keys and values in the config' do
+ let(:config) { { 10 => 20 } }
+ let(:result) do
+ { '10' => '20' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when key is an array' do
+ let(:config) { { ['VAR1'] => 'val1' } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when value is a symbol' do
+ let(:config) { { 'VAR1' => :val1 } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when value is a boolean' do
+ let(:config) { { 'VAR1' => true } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when entry config value has key-value pair and hash' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+
+ context 'when metadata has use_value_data: true' do
+ let(:metadata) { { use_value_data: true } }
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
+ end
+ end
+
+ context 'when entry value is an array' do
+ let(:config) { [:VAR, 'test'] }
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when metadata has use_value_data: true' do
+ let(:metadata) { { use_value_data: true } }
+
+ context 'when entry value has hash with other key-pairs' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', hello: 'variable 1' },
+ 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs, value can be a hash/
+ end
+
+ context 'when entry config value has hash with nil description' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', description: nil } }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs, value can be a hash/
+ end
+
+ context 'when entry config value has hash without description' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1' } }
+ end
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/port_spec.rb b/spec/lib/gitlab/ci/config/entry/port_spec.rb
index e2840c07f6b..77f846f95f0 100644
--- a/spec/lib/gitlab/ci/config/entry/port_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/port_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Port do
let(:config) do
{ number: 80,
protocol: 'http',
- name: 'foobar' }
+ name: 'foobar' }
end
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 714b0a3b6aa..5f42a8c49a7 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -197,6 +197,34 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
end
end
end
+
+ context 'when a variable has an invalid data attribute' do
+ let(:config) do
+ {
+ script: 'echo',
+ variables: { 'VAR1' => 'val 1', 'VAR2' => { value: 'val 2', description: 'hello var 2' } }
+ }
+ end
+
+ it 'reports error about variable' do
+ expect(entry.errors)
+ .to include 'variables:var2 config must be a string'
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { node_class.new(config, name: :rspec) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'reports error about variable' do
+ expect(entry_without_ff.errors)
+ .to include /config should be a hash of key value pairs/
+ end
+ end
+ end
end
end
@@ -212,13 +240,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
let(:unspecified) { double('unspecified', 'specified?' => false) }
let(:default) { double('default', '[]' => unspecified) }
let(:workflow) { double('workflow', 'has_rules?' => false) }
- let(:variables) {}
let(:deps) do
double('deps',
default_entry: default,
- workflow_entry: workflow,
- variables_value: variables)
+ workflow_entry: workflow)
end
context 'with workflow rules' do
diff --git a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
index ff44a235ea5..394d91466bf 100644
--- a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
require_dependency 'active_model'
RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 55ad119ea21..3d19987a0be 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -117,49 +117,49 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
expect(root.jobs_value.keys).to eq([:rspec, :spinach, :release])
expect(root.jobs_value[:rspec]).to eq(
{ name: :rspec,
- script: %w[rspec ls],
- before_script: %w(ls pwd),
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ script: %w[rspec ls],
+ before_script: %w(ls pwd),
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
expect(root.jobs_value[:spinach]).to eq(
{ name: :spinach,
- before_script: [],
- script: %w[spinach],
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ before_script: [],
+ script: %w[spinach],
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
expect(root.jobs_value[:release]).to eq(
{ name: :release,
- stage: 'release',
- before_script: [],
- script: ["make changelog | tee release_changelog.txt"],
- release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
- image: { name: "image:1.0" },
- services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
- cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
- only: { refs: %w(branches tags) },
- job_variables: { 'VAR' => 'job' },
- root_variables_inheritance: true,
- after_script: [],
- ignore: false,
- scheduling_type: :stage }
+ stage: 'release',
+ before_script: [],
+ script: ["make changelog | tee release_changelog.txt"],
+ release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
+ image: { name: "image:1.0" },
+ services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
+ cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
+ only: { refs: %w(branches tags) },
+ job_variables: { 'VAR' => { value: 'job' } },
+ root_variables_inheritance: true,
+ after_script: [],
+ ignore: false,
+ scheduling_type: :stage }
)
end
end
@@ -196,31 +196,31 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
it 'returns jobs configuration' do
expect(root.jobs_value).to eq(
rspec: { name: :rspec,
- script: %w[rspec ls],
- before_script: %w(ls pwd),
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage },
+ script: %w[rspec ls],
+ before_script: %w(ls pwd),
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage },
spinach: { name: :spinach,
- before_script: [],
- script: %w[spinach],
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: { 'VAR' => 'job' },
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ before_script: [],
+ script: %w[spinach],
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: { 'VAR' => { value: 'job' } },
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
end
end
@@ -350,6 +350,33 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
end
end
+
+ context 'when a variable has an invalid data key' do
+ let(:hash) do
+ { variables: { VAR1: { invalid: 'hello' } }, rspec: { script: 'hello' } }
+ end
+
+ describe '#errors' do
+ it 'reports errors about the invalid variable' do
+ expect(root.errors)
+ .to include /var1 config uses invalid data keys: invalid/
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:root_without_ff) { described_class.new(hash, user: user, project: project) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ root_without_ff.compose!
+ end
+
+ it 'reports errors about the invalid variable' do
+ expect(root_without_ff.errors)
+ .to include /variables config should be a hash of key value pairs, value can be a hash/
+ end
+ end
+ end
+ end
end
context 'when value is not a hash' do
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index c85fe366da6..303d825c591 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
require 'gitlab_chronic_duration'
-require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
let(:factory) do
@@ -363,7 +362,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
it { is_expected.not_to be_valid }
it 'returns an error about invalid variables:' do
- expect(subject.errors).to include(/variables config should be a hash of key value pairs/)
+ expect(subject.errors).to include(/variables config should be a hash/)
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { factory.create! }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'returns an error about invalid variables:' do
+ expect(subject.errors).to include(/variables config should be a hash/)
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
index 821ab442d61..e36484bb0ae 100644
--- a/spec/lib/gitlab/ci/config/entry/service_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Service do
before do
- stub_feature_flags(ci_docker_image_pull_policy: true)
entry.compose!
end
@@ -149,18 +148,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do
it 'is valid' do
expect(entry).to be_valid
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include('service config contains unknown keys: pull_policy')
- end
- end
end
describe '#value' do
@@ -170,18 +157,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do
pull_policy: ['if-not-present']
)
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it 'is not valid' do
- expect(entry.value).to eq(
- name: 'postgresql:9.5'
- )
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
new file mode 100644
index 00000000000..744a89d4509
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
@@ -0,0 +1,212 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::Variable do
+ let(:config) { {} }
+ let(:metadata) { {} }
+
+ subject(:entry) do
+ described_class.new(config, **metadata).tap do |entry|
+ entry.key = 'VAR1' # composable_hash requires key to be set
+ end
+ end
+
+ before do
+ entry.compose!
+ end
+
+ describe 'SimpleVariable' do
+ context 'when config is a string' do
+ let(:config) { 'value' }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+ end
+
+ context 'when config is an integer' do
+ let(:config) { 1 }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('1') }
+ end
+ end
+
+ context 'when config is an array' do
+ let(:config) { [] }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'variable definition must be either a string or a hash' }
+ end
+ end
+ end
+
+ describe 'ComplexVariable' do
+ context 'when config is a hash with description' do
+ let(:config) { { value: 'value', description: 'description' } }
+
+ context 'when metadata allowed_value_data is not provided' do
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config must be a string' }
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, description)' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: 'description') }
+ end
+
+ context 'when config value is a symbol' do
+ let(:config) { { value: :value, description: 'description' } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: 'description') }
+ end
+ end
+
+ context 'when config value is an integer' do
+ let(:config) { { value: 123, description: 'description' } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('123') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: '123', description: 'description') }
+ end
+ end
+
+ context 'when config value is an array' do
+ let(:config) { { value: ['value'], description: 'description' } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config value must be an alphanumeric string' }
+ end
+ end
+
+ context 'when config description is a symbol' do
+ let(:config) { { value: 'value', description: :description } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: :description) }
+ end
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, xyz)' do
+ let(:metadata) { { allowed_value_data: %i[value xyz] } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config uses invalid data keys: description' }
+ end
+ end
+ end
+
+ context 'when config is a hash without description' do
+ let(:config) { { value: 'value' } }
+
+ context 'when metadata allowed_value_data is not provided' do
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config must be a string' }
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, description)' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value') }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
index 78d37e228df..ad7290d0589 100644
--- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -3,41 +3,46 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Variables do
+ let(:config) { {} }
let(:metadata) { {} }
- subject { described_class.new(config, **metadata) }
+ subject(:entry) { described_class.new(config, **metadata) }
+
+ before do
+ entry.compose!
+ end
shared_examples 'valid config' do
describe '#value' do
it 'returns hash with key value strings' do
- expect(subject.value).to eq result
+ expect(entry.value).to eq result
end
end
describe '#errors' do
it 'does not append errors' do
- expect(subject.errors).to be_empty
+ expect(entry.errors).to be_empty
end
end
describe '#valid?' do
it 'is valid' do
- expect(subject).to be_valid
+ expect(entry).to be_valid
end
end
end
- shared_examples 'invalid config' do
+ shared_examples 'invalid config' do |error_message|
describe '#valid?' do
it 'is not valid' do
- expect(subject).not_to be_valid
+ expect(entry).not_to be_valid
end
end
describe '#errors' do
it 'saves errors' do
- expect(subject.errors)
- .to include /should be a hash of key value pairs/
+ expect(entry.errors)
+ .to include(error_message)
end
end
end
@@ -52,6 +57,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
end
it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
end
context 'with numeric keys and values in the config' do
@@ -63,33 +77,63 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'valid config'
end
+ context 'when key is an array' do
+ let(:config) { { ['VAR1'] => 'val1' } }
+
+ it_behaves_like 'invalid config', /must be an alphanumeric string/
+ end
+
+ context 'when value is a symbol' do
+ let(:config) { { 'VAR1' => :val1 } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when value is a boolean' do
+ let(:config) { { 'VAR1' => true } }
+
+ it_behaves_like 'invalid config', /must be either a string or a hash/
+ end
+
context 'when entry config value has key-value pair and hash' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
'VARIABLE_2' => 'value 2' }
end
- let(:result) do
- { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
- end
+ it_behaves_like 'invalid config', /variable_1 config must be a string/
- it_behaves_like 'invalid config'
+ context 'when metadata has allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
- context 'when metadata has use_value_data' do
- let(:metadata) { { use_value_data: true } }
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
end
end
context 'when entry value is an array' do
let(:config) { [:VAR, 'test'] }
- it_behaves_like 'invalid config'
+ it_behaves_like 'invalid config', /variables config should be a hash/
end
- context 'when metadata has use_value_data' do
- let(:metadata) { { use_value_data: true } }
+ context 'when metadata has allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
context 'when entry value has hash with other key-pairs' do
let(:config) do
@@ -97,7 +141,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
'VARIABLE_2' => 'value 2' }
end
- it_behaves_like 'invalid config'
+ it_behaves_like 'invalid config', /variable_1 config uses invalid data keys: hello/
end
context 'when entry config value has hash with nil description' do
@@ -105,7 +149,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
{ 'VARIABLE_1' => { value: 'value 1', description: nil } }
end
- it_behaves_like 'invalid config'
+ it_behaves_like 'invalid config', /variable_1 config description must be an alphanumeric string/
end
context 'when entry config value has hash without description' do
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 45dfea636f3..c22afb32756 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -219,4 +219,43 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
)
}
end
+
+ describe '#to_hash' do
+ subject(:to_hash) { remote_file.to_hash }
+
+ before do
+ stub_full_request(location).to_return(body: remote_file_content)
+ end
+
+ context 'with a valid remote file' do
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ before_script: ["apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs",
+ "ruby -v",
+ "which ruby",
+ "bundle install --jobs $(nproc) \"${FLAGS[@]}\""]
+ )
+ end
+ end
+
+ context 'when it has `include` with rules:exists' do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+ HEREDOC
+ end
+
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ include: [
+ { local: 'another-file.yml',
+ rules: [{ exists: ['Dockerfile'] }] }
+ ]
+ )
+ 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 e74fdc2071b..9eaba12f388 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -210,7 +210,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
{ 'local' => local_file },
{ 'local' => local_file }
],
- image: 'image:1.0' }
+ image: 'image:1.0' }
end
it 'does not raise an exception' do
@@ -427,7 +427,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
{ 'local' => 'hello/secret-file1.yml' },
{ 'local' => 'hello/secret-file2.yml' }
],
- image: 'ruby:2.7' }
+ image: 'ruby:2.7' }
end
it 'has expanset with two' do
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 841a46e197d..b1dff6f9723 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -94,6 +94,36 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
end
end
+ context 'when the remote file has `include` with rules:exists' do
+ let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) { { include: remote_file, image: 'image:1.0' } }
+ let(:external_file_content) do
+ <<-HEREDOC
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+
+ rspec:
+ script:
+ - bundle exec rspec
+ HEREDOC
+ end
+
+ before do
+ stub_full_request(remote_file).to_return(body: external_file_content)
+ end
+
+ it 'evaluates the rule as false' do
+ output = processor.perform
+ expect(output.keys).to match_array([:image, :rspec])
+ end
+
+ it "removes the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
context 'with a valid local external file is defined' do
let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'image:1.0' } }
let(:local_file_content) do
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 7e0b2b5aa8e..3d46d266c13 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -341,9 +341,9 @@ RSpec.describe Gitlab::Ci::Lint do
let(:counters) do
{
'count' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
- 'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
+ 'avg' => a_kind_of(Numeric),
+ 'max' => a_kind_of(Numeric),
+ 'min' => a_kind_of(Numeric)
}
end
diff --git a/spec/lib/gitlab/ci/mask_secret_spec.rb b/spec/lib/gitlab/ci/mask_secret_spec.rb
index 7d950c86700..ffe36e69a8f 100644
--- a/spec/lib/gitlab/ci/mask_secret_spec.rb
+++ b/spec/lib/gitlab/ci/mask_secret_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::MaskSecret do
subject { described_class }
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
index c99cfa94aa6..38b229e0dd8 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
subject(:parse_source) { described_class.parse_source(properties) }
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
index 431fe9f3591..f3636106b98 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -102,11 +102,11 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
it 'adds each component, ignoring unused attributes' do
expect(report).to receive(:add_component)
- .with({ "name" => "activesupport", "version" => "5.1.4", "type" => "library" })
+ .with(an_object_having_attributes(name: "activesupport", version: "5.1.4", component_type: "library"))
expect(report).to receive(:add_component)
- .with({ "name" => "byebug", "version" => "10.0.0", "type" => "library" })
+ .with(an_object_having_attributes(name: "byebug", version: "10.0.0", component_type: "library"))
expect(report).to receive(:add_component)
- .with({ "name" => "minimal-component", "type" => "library" })
+ .with(an_object_having_attributes(name: "minimal-component", version: nil, component_type: "library"))
parse!
end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
index 30114b17cac..7222ebc3cb8 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
subject { described_class.source(property_data) }
@@ -17,11 +17,11 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
end
it 'returns expected source data' do
- is_expected.to eq({
- 'type' => :dependency_scanning,
- 'data' => property_data,
- 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
- })
+ is_expected.to have_attributes(
+ source_type: :dependency_scanning,
+ data: property_data,
+ fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ )
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 6495d1f654b..297ef1f5bb9 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
}
end
- where(vulnerability_finding_signatures_enabled: [true, false])
+ where(signatures_enabled: [true, false])
with_them do
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
let(:validator_class) { Gitlab::Ci::Parsers::Security::Validators::SchemaValidator }
let(:data) { {}.merge(scanner_data) }
let(:json_data) { data.to_json }
- let(:parser) { described_class.new(json_data, report, vulnerability_finding_signatures_enabled, validate: validate) }
+ let(:parser) { described_class.new(json_data, report, signatures_enabled: signatures_enabled, validate: validate) }
subject(:parse_report) { parser.parse! }
@@ -191,7 +191,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
context 'report parsing' do
before do
- artifact.each_blob { |blob| described_class.parse!(blob, report, vulnerability_finding_signatures_enabled) }
+ artifact.each_blob { |blob| described_class.parse!(blob, report, signatures_enabled: signatures_enabled) }
end
describe 'parsing finding.name' do
@@ -262,7 +262,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe 'top-level scanner' do
it 'is the primary scanner' do
expect(report.primary_scanner.external_id).to eq('gemnasium')
- expect(report.primary_scanner.name).to eq('Gemnasium')
+ expect(report.primary_scanner.name).to eq('Gemnasium top-level')
expect(report.primary_scanner.vendor).to eq('GitLab')
expect(report.primary_scanner.version).to eq('2.18.0')
end
@@ -278,9 +278,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe 'parsing scanners' do
subject(:scanner) { report.findings.first.scanner }
- context 'when vendor is not missing in scanner' do
- it 'returns scanner with parsed vendor value' do
- expect(scanner.vendor).to eq('GitLab')
+ context 'when the report contains top-level scanner' do
+ it 'sets the scanner of finding as top-level scanner' do
+ expect(scanner.name).to eq('Gemnasium top-level')
+ end
+ end
+
+ context 'when the report does not contain top-level scanner' do
+ let(:artifact) { build(:ci_job_artifact, :common_security_report_without_top_level_scanner) }
+
+ it 'sets the scanner of finding as `vulnerabilities[].scanner`' do
+ expect(scanner.name).to eq('Gemnasium')
end
end
end
@@ -465,7 +473,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
finding = report.findings.first
highest_signature = finding.signatures.max_by(&:priority)
- identifiers = if vulnerability_finding_signatures_enabled
+ identifiers = if signatures_enabled
"#{finding.report_type}-#{finding.primary_identifier.fingerprint}-#{highest_signature.signature_hex}-#{report.project_id}"
else
"#{finding.report_type}-#{finding.primary_identifier.fingerprint}-#{finding.location.fingerprint}-#{report.project_id}"
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index 7828aa99f6a..e730afc72b5 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -19,8 +19,72 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:report_data) do
+ {
+ 'scan' => {
+ 'analyzer' => {
+ 'id' => 'my-dast-analyzer',
+ 'name' => 'My DAST analyzer',
+ 'version' => '0.1.0',
+ 'vendor' => { 'name' => 'A DAST analyzer' }
+ },
+ 'end_time' => '2020-01-28T03:26:02',
+ 'scanned_resources' => [],
+ 'scanner' => {
+ 'id' => 'my-dast-scanner',
+ 'name' => 'My DAST scanner',
+ 'version' => '0.2.0',
+ 'vendor' => { 'name' => 'A DAST scanner' }
+ },
+ 'start_time' => '2020-01-28T03:26:01',
+ 'status' => 'success',
+ 'type' => 'dast'
+ },
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
let(:validator) { described_class.new(report_type, report_data, report_version, project: project, scanner: scanner) }
+ shared_examples 'report is valid' do
+ context 'and the report is valid' do
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ shared_examples 'logs related information' do
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: security_report_failure,
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
+ end
+
+ shared_examples 'report is invalid' do
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:security_report_failure) { 'schema_validation_fails' }
+
+ it { is_expected.to be_falsey }
+
+ it_behaves_like 'logs related information'
+ end
+ end
+
describe 'SUPPORTED_VERSIONS' do
schema_path = Rails.root.join("lib", "gitlab", "ci", "parsers", "security", "validators", "schemas")
@@ -75,80 +139,16 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
(latest_vendored_version[0...2] << "34").join(".")
end
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'and the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_falsey }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
- end
+ it_behaves_like 'report is valid'
+ it_behaves_like 'report is invalid'
end
context 'when given a supported schema version' do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'and the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_falsey }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
- end
+ it_behaves_like 'report is valid'
+ it_behaves_like 'report is invalid'
end
context 'when given a deprecated schema version' do
@@ -173,21 +173,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'using_deprecated_schema_version' }
+
it { is_expected.to be_truthy }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_deprecated_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
context 'and the report does not pass schema validation' do
@@ -213,21 +203,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'using_unsupported_schema_version' }
+
it { is_expected.to be_falsey }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
context 'and the report is invalid' do
@@ -282,6 +262,16 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
+ shared_examples 'report is valid with no error' do
+ context 'and the report is valid' do
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples 'report with expected errors' do
+ it { is_expected.to match_array(expected_errors) }
+ end
+
describe '#errors' do
subject { validator.errors }
@@ -289,16 +279,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no error'
context 'and the report is invalid' do
let(:report_data) do
@@ -309,11 +290,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
- 'root is missing required keys: vulnerabilities'
+ 'root is missing required keys: scan, vulnerabilities'
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -331,16 +312,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'and the report passes schema validation' do
- let(:report_data) do
- {
- 'version' => '10.0.0',
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no error'
context 'and the report does not pass schema validation' do
let(:report_data) do
@@ -356,7 +328,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -383,7 +355,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
context 'and the report is invalid' do
@@ -400,7 +372,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -426,10 +398,27 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
+ shared_examples 'report is valid with no warning' do
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples 'report with expected warnings' do
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+
describe '#deprecation_warnings' do
subject { validator.deprecation_warnings }
@@ -491,7 +480,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
context 'and the report does not pass schema validation' do
@@ -501,7 +490,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
end
@@ -516,7 +505,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
end
@@ -561,21 +550,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'schema_validation_fails' }
+
it { is_expected.to match_array([message]) }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
end
@@ -583,16 +562,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no warning'
context 'and the report is invalid' do
let(:report_data) do
@@ -644,16 +614,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no warning'
context 'and the report is invalid' do
let(:report_data) do
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index 82fa11d5f98..821a5057d2e 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -4,11 +4,13 @@ require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
describe '#parse!' do
- subject { described_class.new.parse!(junit, test_suite, job: job) }
+ subject { described_class.new.parse!(junit, test_report, job: job) }
- let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases) }
+
+ let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
+ let(:test_suite) { test_report.get_suite(job.test_suite_name) }
let(:test_cases) { flattened_test_cases(test_suite) }
- let(:job) { double(max_test_cases_per_report: max_test_cases) }
let(:max_test_cases) { 0 }
context 'when data is JUnit style XML' do
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
index c9891c06507..a9adff4fce3 100644
--- a/spec/lib/gitlab/ci/parsers_spec.rb
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers do
describe '.fabricate!' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
new file mode 100644
index 00000000000..15df5b2f68c
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Pipeline::Chain::AssignPartition do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
+ end
+
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:step) { described_class.new(pipeline, command) }
+ let(:current_partition_id) { 123 }
+
+ describe '#perform!' do
+ before do
+ allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
+ end
+
+ subject { step.perform! }
+
+ it 'assigns partition_id to pipeline' do
+ expect { subject }.to change(pipeline, :partition_id).to(current_partition_id)
+ end
+
+ context 'with parent-child pipelines' do
+ let(:bridge) do
+ instance_double(Ci::Bridge,
+ triggers_child_pipeline?: true,
+ parent_pipeline: instance_double(Ci::Pipeline, partition_id: 125))
+ end
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ bridge: bridge)
+ end
+
+ it 'assigns partition_id to pipeline' do
+ expect { subject }.to change(pipeline, :partition_id).to(125)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index de43e759193..6e8b6e40928 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -302,13 +302,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
context 'when bridge is present' do
context 'when bridge triggers a child pipeline' do
- let(:bridge) { double(:bridge, triggers_child_pipeline?: true) }
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: true) }
it { is_expected.to be_truthy }
end
context 'when bridge triggers a multi-project pipeline' do
- let(:bridge) { double(:bridge, triggers_child_pipeline?: false) }
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) }
it { is_expected.to be_falsey }
end
@@ -321,6 +321,38 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
end
end
+ describe '#parent_pipeline_partition_id' do
+ let(:command) { described_class.new(bridge: bridge) }
+
+ subject { command.parent_pipeline_partition_id }
+
+ context 'when bridge is present' do
+ context 'when bridge triggers a child pipeline' do
+ let(:pipeline) { instance_double(Ci::Pipeline, partition_id: 123) }
+
+ let(:bridge) do
+ instance_double(Ci::Bridge,
+ triggers_child_pipeline?: true,
+ parent_pipeline: pipeline)
+ end
+
+ it { is_expected.to eq(123) }
+ end
+
+ context 'when bridge triggers a multi-project pipeline' do
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ context 'when bridge is not present' do
+ let(:bridge) { nil }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
describe '#increment_pipeline_failure_reason_counter' do
let(:command) { described_class.new }
let(:reason) { :size_limit_exceeded }
@@ -345,7 +377,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
describe '#observe_step_duration' do
context 'when ci_pipeline_creation_step_duration_tracking is enabled' do
it 'adds the duration to the step duration histogram' do
- histogram = double(:histogram)
+ histogram = instance_double(Prometheus::Client::Histogram)
duration = 1.hour
expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_step_duration_histogram)
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
index e0d656f456e..f451bd6bfef 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -11,7 +11,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
subject { described_class.new(pipeline, command) }
- describe '#perform!' do
+ # TODO: change this to `describe` and remove rubocop-disable
+ # when removing the FF ci_project_pipeline_config_refactoring
+ shared_context '#perform!' do # rubocop:disable RSpec/ContextWording
context 'when bridge job is passed in as parameter' do
let(:ci_config_path) { nil }
let(:bridge) { create(:ci_bridge) }
@@ -201,4 +203,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
end
end
end
+
+ it_behaves_like '#perform!'
+
+ context 'when the FF ci_project_pipeline_config_refactoring is disabled' do
+ before do
+ stub_feature_flags(ci_project_pipeline_config_refactoring: false)
+ end
+
+ it_behaves_like '#perform!'
+ end
end
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 e07a3ca9033..7fb5b0b4200 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -2,11 +2,13 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
+RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failures do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:environment) { project.environments.find_by_name('review/master') }
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
@@ -24,12 +26,26 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
context 'when a pipeline contains a deployment job' do
let!(:job) { build(:ci_build, :start_review_app, project: project) }
- it 'ensures environment existence for the job' do
- expect { subject }.to change { Environment.count }.by(1)
+ context 'and the environment does not exist' do
+ it 'creates the environment specified by the job' do
+ expect { subject }.to change { Environment.count }.by(1)
- expect(project.environments.find_by_name('review/master')).to be_present
- expect(job.persisted_environment.name).to eq('review/master')
- expect(job.metadata.expanded_environment_name).to eq('review/master')
+ expect(environment).to be_present
+ expect(job.persisted_environment.name).to eq('review/master')
+ expect(job.metadata.expanded_environment_name).to eq('review/master')
+ end
+
+ context 'and the pipeline is for a merge request' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
+ end
+
+ it 'associates the environment with the merge request' do
+ expect { subject }.to change { Environment.count }.by(1)
+
+ expect(environment.merge_request).to eq(merge_request)
+ end
+ end
end
context 'when an environment has already been existed' do
@@ -40,10 +56,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.not_to change { Environment.count }
- expect(project.environments.find_by_name('review/master')).to be_present
+ expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
+
+ context 'and the pipeline is for a merge request' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user, merge_request: merge_request)
+ end
+
+ it 'does not associate the environment with the merge request' do
+ expect { subject }.not_to change { Environment.count }
+
+ expect(environment.merge_request).to be_nil
+ end
+ end
end
context 'when an environment name contains an invalid character' do
@@ -65,7 +93,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.to change { Environment.count }.by(1)
- expect(project.environments.find_by_name('review/master')).to be_present
+ expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index f7774e199fb..8c4f7af0ef4 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -277,7 +277,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
non_handled_sql_queries = 2
# 1. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache`
- # 2. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore`
extra_jobs * non_handled_sql_queries
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index e8eb3333b88..ee32661f267 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -83,19 +83,36 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Sequence do
.with({ source: 'push' }, 0)
end
- it 'records active jobs by pipeline plan in a histogram' do
- allow(command.metrics)
- .to receive(:active_jobs_histogram)
- .and_return(histogram)
+ describe 'active jobs by pipeline plan histogram' do
+ before do
+ allow(command.metrics)
+ .to receive(:active_jobs_histogram)
+ .and_return(histogram)
+
+ pipeline = create(:ci_pipeline, :running, project: project)
+ create_list(:ci_build, 3, pipeline: pipeline)
+ create(:ci_bridge, pipeline: pipeline)
+ end
- pipeline = create(:ci_pipeline, project: project, status: :running)
- create(:ci_build, :finished, project: project, pipeline: pipeline)
- create(:ci_build, :failed, project: project, pipeline: pipeline)
- create(:ci_build, :running, project: project, pipeline: pipeline)
- subject.build!
+ it 'counts all the active jobs' do
+ subject.build!
- expect(histogram).to have_received(:observe)
- .with(hash_including(plan: project.actual_plan_name), 3)
+ expect(histogram).to have_received(:observe)
+ .with(hash_including(plan: project.actual_plan_name), 4)
+ end
+
+ context 'when feature flag ci_limit_active_jobs_early is disabled' do
+ before do
+ stub_feature_flags(ci_limit_active_jobs_early: false)
+ end
+
+ it 'counts all the active builds' do
+ subject.build!
+
+ expect(histogram).to have_received(:observe)
+ .with(hash_including(plan: project.actual_plan_name), 3)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
index fb1a360a4b7..52a00e0d501 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
@@ -179,6 +179,70 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
perform!
end
end
+
+ describe 'credit_card' do
+ context 'with no registered credit_card' do
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(0)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(0)
+ end
+
+ perform!
+ end
+ end
+
+ context 'with a registered credit card' do
+ let!(:credit_card) { create(:credit_card_validation, last_digits: 10, holder_name: 'Alice', user: user) }
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(1)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
+ end
+
+ perform!
+ end
+
+ context 'with similar credit cards registered by other users' do
+ before do
+ create(:credit_card_validation, last_digits: 10, holder_name: 'Bob')
+ end
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(2)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
+ end
+
+ perform!
+ end
+ end
+
+ context 'with similar holder names registered by other users' do
+ before do
+ create(:credit_card_validation, last_digits: 11, holder_name: 'Alice')
+ end
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(1)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(2)
+ end
+
+ perform!
+ end
+ end
+ end
+ end
end
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do
diff --git a/spec/lib/gitlab/ci/pipeline/duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
index e0b4928d7f7..46c7072ad8e 100644
--- a/spec/lib/gitlab/ci/pipeline/duration_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Duration do
let(:calculated_duration) { calculate(data) }
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
index 49686d1a9bd..3ca6fd9143f 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Null do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
index c6d0d2534a5..b224fca6011 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::String do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
index 3e10ca686ba..b030bd22aa1 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Variable do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/metrics_spec.rb b/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
index 83b969ff3c4..8df3c67beaa 100644
--- a/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe ::Gitlab::Ci::Pipeline::Metrics do
describe '.pipeline_creation_step_duration_histogram' do
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 890ba51157a..75f6a773c2d 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -97,15 +97,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' },
+ { key: 'VAR2', value: 'var 2' }],
rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
end
it do
- is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' }])
end
end
@@ -114,13 +114,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
{
name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
+ job_variables: [{ key: 'VARIABLE', value: 'value' }],
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
}
end
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
- it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
+ it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
end
context 'with cache:key' do
@@ -257,19 +257,19 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
- job_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
+ yaml_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
+ job_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
root_variables_inheritance: root_variables_inheritance }
end
context 'when the pipeline has variables' do
let(:root_variables) do
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var pipeline 2', public: true },
- { key: 'VAR3', value: 'var pipeline 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var pipeline 2' },
+ { key: 'VAR3', value: 'var pipeline 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
end
context 'when root_variables_inheritance is true' do
@@ -277,10 +277,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
)
end
end
@@ -290,8 +290,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns job variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -301,9 +301,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -314,8 +314,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns seed yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }])
end
end
end
@@ -324,8 +324,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
+ yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' }],
root_variables_inheritance: root_variables_inheritance,
rules: rules }
end
@@ -338,14 +338,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'new var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'new var 2' })
end
end
context 'when the rules use root variables' do
let(:root_variables) do
- [{ key: 'VAR2', value: 'var pipeline 2', public: true }]
+ [{ key: 'VAR2', value: 'var pipeline 2' }]
end
let(:rules) do
@@ -353,15 +353,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'overridden var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'overridden var 2' })
end
context 'when the root_variables_inheritance is false' do
let(:root_variables_inheritance) { false }
it 'does not recalculate the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
index 51185be3e74..6569ce937ac 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
@@ -6,8 +6,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do
let_it_be(:project, refind: true) { create(:project, :repository) }
let(:pipeline) do
- create(:ci_pipeline, project: project,
- sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
let(:job) { build(:ci_build, project: project, pipeline: pipeline) }
diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
index ad89f1f5cda..2b9d8127886 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
let_it_be(:project) { create(:project) }
- let(:job) { build(:ci_build, project: project) }
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:job) { build(:ci_build, project: project, pipeline: pipeline) }
let(:seed) { described_class.new(job) }
let(:attributes) { {} }
@@ -87,6 +89,28 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
+
+ context 'and job environment has an auto_stop_in variable attribute' do
+ let(:environment_auto_stop_in) { '10 minutes' }
+ let(:expected_auto_stop_in) { '10 minutes' }
+
+ let(:attributes) do
+ {
+ environment: environment_name,
+ options: {
+ environment: {
+ name: environment_name,
+ auto_stop_in: '$TTL'
+ }
+ },
+ yaml_variables: [
+ { key: "TTL", value: environment_auto_stop_in, public: true }
+ ]
+ }
+ end
+
+ it_behaves_like 'returning a correct environment'
+ end
end
context 'when job has deployment tier attribute' do
@@ -167,5 +191,34 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
+
+ context 'when merge_request is provided' do
+ let(:environment_name) { 'development' }
+ let(:attributes) { { environment: environment_name, options: { environment: { name: environment_name } } } }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:seed) { described_class.new(job, merge_request: merge_request) }
+
+ context 'and environment does not exist' do
+ let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
+
+ it 'creates an environment associated with the merge request' do
+ expect { subject }.to change { Environment.count }.by(1)
+
+ expect(subject.merge_request).to eq(merge_request)
+ end
+ end
+
+ context 'and environment already exists' do
+ before do
+ create(:environment, project: project, name: environment_name)
+ end
+
+ it 'does not change the merge request associated with the environment' do
+ expect { subject }.not_to change { Environment.count }
+
+ expect(subject.merge_request).to be_nil
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
new file mode 100644
index 00000000000..a844ce6486b
--- /dev/null
+++ b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProcessableObjectHierarchy do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master') }
+
+ let_it_be(:job1) { create(:ci_build, :created, pipeline: pipeline, name: 'job1') }
+ let_it_be(:job2) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job2', needed: job1) }
+ let_it_be(:job3) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job3', needed: job1) }
+ let_it_be(:job4) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job4', needed: job2) }
+ let_it_be(:job5) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job5', needed: job3) }
+ let_it_be(:job6) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job6', needed: job4) }
+
+ describe '#base_and_ancestors' do
+ it 'includes the base and its ancestors' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_ancestors
+
+ expect(relation).to eq([job2, job1])
+ end
+
+ it 'can find ancestors upto a certain level' do
+ relation = described_class.new(::Ci::Processable.where(id: job4.id)).base_and_ancestors(upto: job1.name)
+
+ expect(relation).to eq([job4, job2])
+ end
+
+ describe 'hierarchy_order option' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job4.id)).base_and_ancestors(hierarchy_order: hierarchy_order)
+ end
+
+ context 'for :asc' do
+ let(:hierarchy_order) { :asc }
+
+ it 'orders by child to ancestor' do
+ expect(relation).to eq([job4, job2, job1])
+ end
+ end
+
+ context 'for :desc' do
+ let(:hierarchy_order) { :desc }
+
+ it 'orders by ancestor to child' do
+ expect(relation).to eq([job1, job2, job4])
+ end
+ end
+ end
+ end
+
+ describe '#base_and_descendants' do
+ it 'includes the base and its descendants' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_descendants
+
+ expect(relation).to contain_exactly(job2, job4, job6)
+ end
+
+ context 'when with_depth is true' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job1.id)).base_and_descendants(with_depth: true)
+ end
+
+ it 'includes depth in the results' do
+ object_depths = {
+ job1.id => 1,
+ job2.id => 2,
+ job3.id => 2,
+ job4.id => 3,
+ job5.id => 3,
+ job6.id => 4
+ }
+
+ relation.each do |object|
+ expect(object.depth).to eq(object_depths[object.id])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config/repository_spec.rb b/spec/lib/gitlab/ci/project_config/repository_spec.rb
new file mode 100644
index 00000000000..2105b691d9e
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config/repository_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig::Repository do
+ let(:project) { create(:project, :custom_repo, files: files) }
+ let(:sha) { project.repository.head_commit.sha }
+ let(:files) { { 'README.md' => 'hello' } }
+
+ subject(:config) { described_class.new(project, sha, nil, nil, nil) }
+
+ describe '#content' do
+ subject(:content) { config.content }
+
+ context 'when file is in repository' do
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ CICONFIG
+ end
+
+ let(:files) { { '.gitlab-ci.yml' => 'content' } }
+
+ it { is_expected.to eq(config_content_result) }
+ end
+
+ context 'when file is not in repository' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when Gitaly raises error' do
+ before do
+ allow(project.repository).to receive(:gitlab_ci_yml_for).and_raise(GRPC::Internal)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#source' do
+ subject { config.source }
+
+ it { is_expected.to eq(:repository_source) }
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config/source_spec.rb b/spec/lib/gitlab/ci/project_config/source_spec.rb
new file mode 100644
index 00000000000..dda5c7cdce8
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config/source_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig::Source do
+ let_it_be(:custom_config_class) { Class.new(described_class) }
+ let_it_be(:project) { build_stubbed(:project) }
+ let_it_be(:sha) { '123456' }
+
+ subject(:custom_config) { custom_config_class.new(project, sha, nil, nil, nil) }
+
+ describe '#content' do
+ subject(:content) { custom_config.content }
+
+ it { expect { content }.to raise_error(NotImplementedError) }
+ end
+
+ describe '#source' do
+ subject(:source) { custom_config.source }
+
+ it { expect { source }.to raise_error(NotImplementedError) }
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config_spec.rb b/spec/lib/gitlab/ci/project_config_spec.rb
new file mode 100644
index 00000000000..c4b179c9ef5
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig do
+ let(:project) { create(:project, :empty_repo, ci_config_path: ci_config_path) }
+ let(:sha) { '123456' }
+ let(:content) { nil }
+ let(:source) { :push }
+ let(:bridge) { nil }
+
+ subject(:config) do
+ described_class.new(project: project, sha: sha,
+ custom_content: content, pipeline_source: source, pipeline_source_bridge: bridge)
+ end
+
+ context 'when bridge job is passed in as parameter' do
+ let(:ci_config_path) { nil }
+ let(:bridge) { create(:ci_bridge) }
+
+ before do
+ allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
+ end
+
+ it 'returns the content already available in command' do
+ expect(config.source).to eq(:bridge_source)
+ expect(config.content).to eq('the-yaml')
+ end
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: #{ci_config_path}
+ CICONFIG
+ end
+
+ before do
+ allow(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'returns root config including the local custom file' do
+ expect(config.source).to eq(:repository_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - remote: #{ci_config_path}
+ CICONFIG
+ end
+
+ it 'returns root config including the remote config' do
+ expect(config.source).to eq(:remote_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is defined in a separate repository' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ CICONFIG
+ end
+
+ it 'returns root config including the path to another repository' do
+ expect(config.source).to eq(:external_project_source)
+ expect(config.content).to eq(config_content_result)
+ end
+
+ context 'when path specifies a refname' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo:refname' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ ref: refname
+ CICONFIG
+ end
+
+ it 'returns root config including the path and refname to another repository' do
+ expect(config.source).to eq(:external_project_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ CICONFIG
+ end
+
+ before do
+ allow(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'returns root config including the canonical CI config file' do
+ expect(config.source).to eq(:repository_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+ CICONFIG
+ end
+
+ before do
+ allow(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'returns root config including the auto-devops template' do
+ expect(config.source).to eq(:auto_devops_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is passed as a parameter' do
+ let(:source) { :ondemand_dast_scan }
+ let(:ci_config_path) { nil }
+ let(:content) do
+ <<~CICONFIG
+ ---
+ stages:
+ - dast
+ CICONFIG
+ end
+
+ it 'returns the parameter content' do
+ expect(config.source).to eq(:parameter_source)
+ expect(config.content).to eq(content)
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ allow(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'returns nil' do
+ expect(config.source).to be_nil
+ expect(config.content).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
index ade0e36cf1e..ad8f1dc11f8 100644
--- a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
let(:comparer) { described_class.new(base_report, head_report) }
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
index 8c35b2a34cf..af6844491ca 100644
--- a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do
let(:accessibility_report) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/coverage_report_spec.rb b/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
index 53646f7dfc0..23361a0c768 100644
--- a/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::CoverageReport do
let(:coverage_report) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
index 672117c311f..06ea3433ef0 100644
--- a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
@@ -1,23 +1,23 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Component do
let(:attributes) do
{
- 'type' => 'library',
- 'name' => 'component-name',
- 'version' => 'v0.0.1'
+ type: 'library',
+ name: 'component-name',
+ version: 'v0.0.1'
}
end
- subject { described_class.new(attributes) }
+ subject { described_class.new(**attributes) }
it 'has correct attributes' do
expect(subject).to have_attributes(
- component_type: 'library',
- name: 'component-name',
- version: 'v0.0.1'
+ component_type: attributes[:type],
+ name: attributes[:name],
+ version: attributes[:version]
)
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
index d7a285ab13c..6ffa93e5fc8 100644
--- a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
@@ -15,40 +15,22 @@ RSpec.describe Gitlab::Ci::Reports::Sbom::Report do
end
describe '#set_source' do
- let_it_be(:source) do
- {
- 'type' => :dependency_scanning,
- 'data' => {
- 'input_file' => { 'path' => 'package-lock.json' },
- 'source_file' => { 'path' => 'package.json' },
- 'package_manager' => { 'name' => 'npm' },
- 'language' => { 'name' => 'JavaScript' }
- },
- 'fingerprint' => 'c01df1dc736c1148717e053edbde56cb3a55d3e31f87cea955945b6f67c17d42'
- }
- end
+ let_it_be(:source) { create(:ci_reports_sbom_source) }
it 'stores the source' do
report.set_source(source)
- expect(report.source).to be_a(Gitlab::Ci::Reports::Sbom::Source)
+ expect(report.source).to eq(source)
end
end
describe '#add_component' do
- let_it_be(:components) do
- [
- { 'type' => 'library', 'name' => 'component1', 'version' => 'v0.0.1' },
- { 'type' => 'library', 'name' => 'component2', 'version' => 'v0.0.2' },
- { 'type' => 'library', 'name' => 'component2' }
- ]
- end
+ let_it_be(:components) { create_list(:ci_reports_sbom_component, 3) }
it 'appends components to a list' do
components.each { |component| report.add_component(component) }
- expect(report.components.size).to eq(3)
- expect(report.components).to all(be_a(Gitlab::Ci::Reports::Sbom::Component))
+ expect(report.components).to match_array(components)
end
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
index 97d8d7abb33..75ea91251eb 100644
--- a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Reports do
subject(:reports_list) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
index 2d6434534a0..cb30bd721dd 100644
--- a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
@@ -1,29 +1,29 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
let(:attributes) do
{
- 'type' => :dependency_scanning,
- 'data' => {
+ type: :dependency_scanning,
+ data: {
'category' => 'development',
'input_file' => { 'path' => 'package-lock.json' },
'source_file' => { 'path' => 'package.json' },
'package_manager' => { 'name' => 'npm' },
'language' => { 'name' => 'JavaScript' }
},
- 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
}
end
- subject { described_class.new(attributes) }
+ subject { described_class.new(**attributes) }
it 'has correct attributes' do
expect(subject).to have_attributes(
- source_type: attributes['type'],
- data: attributes['data'],
- fingerprint: attributes['fingerprint']
+ source_type: attributes[:type],
+ data: attributes[:data],
+ fingerprint: attributes[:fingerprint]
)
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/flag_spec.rb b/spec/lib/gitlab/ci/reports/security/flag_spec.rb
index d677425a8da..6ee074f7aeb 100644
--- a/spec/lib/gitlab/ci/reports/security/flag_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/flag_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Flag do
subject(:security_flag) { described_class.new(type: 'flagged-as-likely-false-positive', origin: 'post analyzer X', description: 'static string to sink') }
diff --git a/spec/lib/gitlab/ci/reports/security/link_spec.rb b/spec/lib/gitlab/ci/reports/security/link_spec.rb
index 7b55af27f4d..0e1cdc93f6c 100644
--- a/spec/lib/gitlab/ci/reports/security/link_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/link_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Link do
subject(:security_link) { described_class.new(name: 'CVE-2020-0202', url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0202') }
diff --git a/spec/lib/gitlab/ci/reports/security/scan_spec.rb b/spec/lib/gitlab/ci/reports/security/scan_spec.rb
index b4968ff3a6e..23427e8608c 100644
--- a/spec/lib/gitlab/ci/reports/security/scan_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scan_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Scan do
describe '#initialize' do
diff --git a/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb b/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
index e9daa05e8b9..74a5344f79e 100644
--- a/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::ScannedResource do
let(:url) { 'http://example.com:3001/1?foo=bar' }
diff --git a/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
index 5e94fe2bb3d..f754786d071 100644
--- a/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::TerraformReports do
it 'initializes plans with and empty hash' do
diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb
index 3e1004754ba..e81c7b0f6be 100644
--- a/spec/lib/gitlab/ci/status/extended_spec.rb
+++ b/spec/lib/gitlab/ci/status/extended_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Status::Extended do
it 'requires subclass to implement matcher' do
diff --git a/spec/lib/gitlab/ci/status/group/factory_spec.rb b/spec/lib/gitlab/ci/status/group/factory_spec.rb
index c67c7ff8271..38aa1ba4ebb 100644
--- a/spec/lib/gitlab/ci/status/group/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/group/factory_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Status::Group::Factory do
it 'inherits from the core factory' do
diff --git a/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..5a62324da74
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Katalon.gitlab-ci.yml' do
+ subject(:template) do
+ <<~YAML
+ include:
+ - template: 'Katalon.gitlab-ci.yml'
+
+ katalon_tests_placeholder:
+ extends: .katalon_tests
+ stage: test
+ script:
+ - echo "katalon tests"
+
+ katalon_tests_with_artifacts_placeholder:
+ extends: .katalon_tests_with_artifacts
+ stage: test
+ script:
+ - echo "katalon tests with artifacts"
+ YAML
+ end
+
+ describe 'the created pipeline' do
+ let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
+ let(:user) { project.first_owner }
+
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: 'master' ) }
+ let(:pipeline) { service.execute!(:push).payload }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template)
+ end
+
+ it 'create katalon tests jobs' do
+ expect(build_names).to match_array(%w[katalon_tests_placeholder katalon_tests_with_artifacts_placeholder])
+
+ expect(pipeline.builds.find_by(name: 'katalon_tests_placeholder').options).to include(
+ image: { name: 'katalonstudio/katalon' },
+ services: [{ name: 'docker:dind' }]
+ )
+
+ expect(pipeline.builds.find_by(name: 'katalon_tests_with_artifacts_placeholder').options).to include(
+ image: { name: 'katalonstudio/katalon' },
+ services: [{ name: 'docker:dind' }],
+ artifacts: { when: 'always', paths: ['Reports/'], reports: { junit: ['Reports/*/*/*/*.xml'] } }
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace/archive_spec.rb b/spec/lib/gitlab/ci/trace/archive_spec.rb
index 3ae0e5d1f0e..f91cb03883a 100644
--- a/spec/lib/gitlab/ci/trace/archive_spec.rb
+++ b/spec/lib/gitlab/ci/trace/archive_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Trace::Archive do
context 'with transactional fixtures' do
- let_it_be(:job) { create(:ci_build, :success, :trace_live) }
+ let_it_be_with_reload(:job) { create(:ci_build, :success, :trace_live) }
let_it_be_with_reload(:trace_metadata) { create(:ci_build_trace_metadata, build: job) }
let_it_be(:src_checksum) do
job.trace.read { |stream| Digest::MD5.hexdigest(stream.raw) }
diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb
index 888ceb7ff9a..3043c8c5467 100644
--- a/spec/lib/gitlab/ci/trace_spec.rb
+++ b/spec/lib/gitlab/ci/trace_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state, factory_defa
describe "associations" do
it { expect(trace).to respond_to(:job) }
- it { expect(trace).to delegate_method(:old_trace).to(:job) }
end
context 'when trace is migrated to object storage' do
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 6ab2089cce8..4833ccf9093 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Variables::Builder do
+RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
include Ci::TemplateHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
@@ -26,13 +26,13 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
{ key: 'CI_JOB_NAME',
value: job.name },
{ key: 'CI_JOB_STAGE',
- value: job.stage },
+ value: job.stage_name },
{ key: 'CI_NODE_TOTAL',
value: '1' },
{ key: 'CI_BUILD_NAME',
value: job.name },
{ key: 'CI_BUILD_STAGE',
- value: job.stage },
+ value: job.stage_name },
{ key: 'CI',
value: 'true' },
{ key: 'GITLAB_CI',
@@ -138,11 +138,11 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
{ key: 'GITLAB_USER_ID',
value: user.id.to_s },
{ key: 'GITLAB_USER_EMAIL',
- value: user.email },
+ value: user.email },
{ key: 'GITLAB_USER_LOGIN',
- value: user.username },
+ value: user.username },
{ key: 'GITLAB_USER_NAME',
- value: user.name }
+ value: user.name }
].map { |var| var.merge(public: true, masked: false) }
end
diff --git a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
index 7e4e9602a92..57171e5be69 100644
--- a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
@@ -2,6 +2,7 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
+require 'tsort'
RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
describe '#initialize with non-Collection value' do
diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb
index fc1055751bd..fb1e66bd605 100644
--- a/spec/lib/gitlab/ci/variables/helpers_spec.rb
+++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb
@@ -15,21 +15,21 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
end
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.merge_variables(current_variables, new_variables) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when new variables is a hash' do
let(:new_variables) do
{ 'key2' => 'value22', 'key3' => 'value3' }
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when new variables is a hash with symbol keys' do
@@ -37,79 +37,68 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
{ key2: 'value22', key3: 'value3' }
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when new variables is nil' do
let(:new_variables) {}
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
- describe '.transform_to_yaml_variables' do
- let(:variables) do
- { 'key1' => 'value1', 'key2' => 'value2' }
- end
-
- let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
-
- subject { described_class.transform_to_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
+ describe '.transform_to_array' do
+ subject { described_class.transform_to_array(variables) }
- context 'when variables is nil' do
- let(:variables) {}
-
- it { is_expected.to eq([]) }
- end
- end
+ context 'when values are strings' do
+ let(:variables) do
+ { 'key1' => 'value1', 'key2' => 'value2' }
+ end
- describe '.transform_from_yaml_variables' do
- let(:variables) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- let(:result) do
- { 'key1' => 'value1', 'key2' => 'value2' }
+ it { is_expected.to match_array(result) }
end
- subject { described_class.transform_from_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
-
context 'when variables is nil' do
let(:variables) {}
- it { is_expected.to eq({}) }
+ it { is_expected.to match_array([]) }
end
- context 'when variables is a hash' do
+ context 'when values are hashes' do
let(:variables) do
- { key1: 'value1', 'key2' => 'value2' }
+ { 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } }
end
- it { is_expected.to eq(result) }
- end
-
- context 'when variables contain integers and symbols' do
- let(:variables) do
- { key1: 1, key2: :value2 }
+ let(:result) do
+ [{ key: 'key1', value: 'value1', description: 'var 1' },
+ { key: 'key2', value: 'value2' }]
end
- let(:result1) do
- { 'key1' => '1', 'key2' => 'value2' }
- end
+ it { is_expected.to match_array(result) }
+
+ context 'when a value data has `key` as a key' do
+ let(:variables) do
+ { 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
+ end
+
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- it { is_expected.to eq(result1) }
+ it 'ignores the key set with "key"' do
+ is_expected.to match_array(result)
+ end
+ end
end
end
@@ -127,35 +116,35 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
let(:inheritance) { true }
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when inheritance is false' do
let(:inheritance) { false }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when inheritance is array' do
let(:inheritance) { ['key2'] }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
index f815f56543c..082febacbd7 100644
--- a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tsort'
RSpec.describe Gitlab::Ci::YamlProcessor::Dag do
let(:nodes) { {} }
diff --git a/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb b/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
index 0bd9563d191..77346f328ca 100644
--- a/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::YamlProcessor::FeatureFlags do
let(:feature_flag) { :my_feature_flag }
@@ -48,20 +48,32 @@ RSpec.describe Gitlab::Ci::YamlProcessor::FeatureFlags do
end
context 'when feature flag is checked outside the "with_actor" block' do
- it 'raises an error on dev/test environment' do
- expect { described_class.enabled?(feature_flag) }.to raise_error(described_class::NoActorError)
- end
+ context 'when yaml_processor_feature_flag_corectness is used', :yaml_processor_feature_flag_corectness do
+ it 'raises an error on dev/test environment' do
+ expect { described_class.enabled?(feature_flag) }.to raise_error(described_class::NoActorError)
+ end
+
+ context 'when on production' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
- context 'when on production' do
- before do
- allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ it 'checks the feature flag without actor' do
+ expect(Feature).to receive(:enabled?).with(feature_flag, nil)
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_and_raise_for_dev_exception)
+ .and_call_original
+
+ described_class.enabled?(feature_flag)
+ end
end
+ end
+ context 'when yaml_processor_feature_flag_corectness is not used' do
it 'checks the feature flag without actor' do
expect(Feature).to receive(:enabled?).with(feature_flag, nil)
expect(Gitlab::ErrorTracking)
- .to receive(:track_and_raise_for_dev_exception)
- .and_call_original
+ .to receive(:track_exception)
described_class.enabled?(feature_flag)
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 8416501e949..f7a0905d9da 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -72,8 +72,8 @@ module Gitlab
it 'returns calculated variables with root and job variables' do
is_expected.to match_array([
- { key: 'VAR1', value: 'value 11', public: true },
- { key: 'VAR2', value: 'value 2', public: true }
+ { key: 'VAR1', value: 'value 11' },
+ { key: 'VAR2', value: 'value 2' }
])
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 35af9ae6201..cc327f5b5f1 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -298,8 +298,8 @@ module Gitlab
context 'when delayed is defined' do
let(:config) do
YAML.dump(rspec: {
- script: 'rollout 10%',
- when: 'delayed',
+ script: 'rollout 10%',
+ when: 'delayed',
start_in: '1 day'
})
end
@@ -315,7 +315,7 @@ module Gitlab
context 'when resource group is defined' do
let(:config) do
YAML.dump(rspec: {
- script: 'test',
+ script: 'test',
resource_group: 'iOS'
})
end
@@ -448,7 +448,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -490,7 +490,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -997,18 +997,6 @@ module Gitlab
scheduling_type: :stage
})
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it { is_expected.not_to be_valid }
-
- it "returns no job" do
- expect(processor.jobs).to eq({})
- end
- end
end
context 'when a service has pull_policy' do
@@ -1042,39 +1030,29 @@ module Gitlab
scheduling_type: :stage
})
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it { is_expected.not_to be_valid }
-
- it "returns no job" do
- expect(processor.jobs).to eq({})
- end
- end
end
end
- describe 'Variables' do
- subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
+ # Change this to a `describe` block when removing the FF ci_variables_refactoring_to_variable
+ shared_examples 'Variables' do
+ subject(:execute) { described_class.new(config).execute }
- let(:build) { subject.builds.first }
+ let(:build) { execute.builds.first }
let(:job_variables) { build[:job_variables] }
let(:root_variables_inheritance) { build[:root_variables_inheritance] }
context 'when global variables are defined' do
- let(:variables) do
- { 'VAR1' => 'value1', 'VAR2' => 'value2' }
- end
-
let(:config) do
- {
- variables: variables,
- before_script: ['pwd'],
- rspec: { script: 'rspec' }
- }
+ <<~YAML
+ variables:
+ VAR1: value1
+ VAR2: value2
+
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ YAML
end
it 'returns global variables' do
@@ -1084,22 +1062,23 @@ module Gitlab
end
context 'when job variables are defined' do
- let(:config) do
- {
- before_script: ['pwd'],
- rspec: { script: 'rspec', variables: variables }
- }
- end
-
context 'when syntax is correct' do
- let(:variables) do
- { 'VAR1' => 'value1', 'VAR2' => 'value2' }
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables:
+ VAR1: value1
+ VAR2: value2
+ YAML
end
it 'returns job variables' do
expect(job_variables).to contain_exactly(
- { key: 'VAR1', value: 'value1', public: true },
- { key: 'VAR2', value: 'value2', public: true }
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' }
)
expect(root_variables_inheritance).to eq(true)
end
@@ -1107,16 +1086,28 @@ module Gitlab
context 'when syntax is incorrect' do
context 'when variables defined but invalid' do
- let(:variables) do
- %w(VAR1 value1 VAR2 value2)
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables: [VAR1 value1 VAR2 value2]
+ YAML
end
- it_behaves_like 'returns errors', /jobs:rspec:variables config should be a hash of key value pairs/
+ it_behaves_like 'returns errors', /jobs:rspec:variables config should be a hash/
end
context 'when variables key defined but value not specified' do
- let(:variables) do
- nil
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables: null
+ YAML
end
it 'returns empty array' do
@@ -1133,10 +1124,12 @@ module Gitlab
context 'when job variables are not defined' do
let(:config) do
- {
- before_script: ['pwd'],
- rspec: { script: 'rspec' }
- }
+ <<~YAML
+ before_script: ['pwd']
+
+ rspec:
+ script: rspec
+ YAML
end
it 'returns empty array' do
@@ -1144,6 +1137,42 @@ module Gitlab
expect(root_variables_inheritance).to eq(true)
end
end
+
+ context 'when variables have different type of values' do
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ variables:
+ VAR1: value1
+ VAR2: :value2
+ VAR3: 123
+ script: rspec
+ YAML
+ end
+
+ it 'returns job variables' do
+ expect(job_variables).to contain_exactly(
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' },
+ { key: 'VAR3', value: '123' }
+ )
+ expect(root_variables_inheritance).to eq(true)
+ end
+ end
+ end
+
+ context 'when ci_variables_refactoring_to_variable is enabled' do
+ it_behaves_like 'Variables'
+ end
+
+ context 'when ci_variables_refactoring_to_variable is disabled' do
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ end
+
+ it_behaves_like 'Variables'
end
context 'when using `extends`' do
@@ -1203,21 +1232,21 @@ module Gitlab
expect(config_processor.builds[0]).to include(
name: 'test1',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'test1 var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'test1 var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[1]).to include(
name: 'test2',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[2]).to include(
name: 'test3',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' }]
)
expect(config_processor.builds[3]).to include(
@@ -1647,10 +1676,10 @@ module Gitlab
describe "Artifacts" do
it "returns artifacts when defined" do
config = YAML.dump({
- image: "image:1.0",
- services: ["mysql"],
+ image: "image:1.0",
+ services: ["mysql"],
before_script: ["pwd"],
- rspec: {
+ rspec: {
artifacts: {
paths: ["logs/", "binaries/"],
expose_as: "Exposed artifacts",
@@ -1906,7 +1935,7 @@ module Gitlab
let(:config) do
{
deploy_to_production: {
- stage: 'deploy',
+ stage: 'deploy',
script: 'test'
}
}
@@ -2275,15 +2304,15 @@ module Gitlab
let(:config) do
{
- var_default: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null' }] },
- var_when: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', when: 'always' }] },
+ var_default: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null' }] },
+ var_when: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', when: 'always' }] },
var_and_changes: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', changes: %w[README], when: 'always' }] },
changes_not_var: { stage: 'test', script: 'test', rules: [{ if: '$VAR != null', changes: %w[README] }] },
var_not_changes: { stage: 'test', script: 'test', rules: [{ if: '$VAR == null', changes: %w[other/file.rb], when: 'always' }] },
- nothing: { stage: 'test', script: 'test', rules: [{ when: 'manual' }] },
- var_never: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'never' }] },
- var_delayed: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] },
- two_rules: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'on_success' }, { changes: %w[README], when: 'manual' }] }
+ nothing: { stage: 'test', script: 'test', rules: [{ when: 'manual' }] },
+ var_never: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'never' }] },
+ var_delayed: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] },
+ two_rules: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'on_success' }, { changes: %w[README], when: 'manual' }] }
}
end
@@ -2729,13 +2758,13 @@ module Gitlab
context 'returns errors if variables is not a map' do
let(:config) { YAML.dump({ variables: "test", rspec: { script: "test" } }) }
- it_behaves_like 'returns errors', 'variables config should be a hash of key value pairs, value can be a hash'
+ it_behaves_like 'returns errors', 'variables config should be a hash'
end
context 'returns errors if variables is not a map of key-value strings' do
let(:config) { YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) }
- it_behaves_like 'returns errors', 'variables config should be a hash of key value pairs, value can be a hash'
+ it_behaves_like 'returns errors', 'variable definition must be either a string or a hash'
end
context 'returns errors if job when is not on_success, on_failure or always' do
diff --git a/spec/lib/gitlab/ci_access_spec.rb b/spec/lib/gitlab/ci_access_spec.rb
index 9b573c6eb7a..e41b666abda 100644
--- a/spec/lib/gitlab/ci_access_spec.rb
+++ b/spec/lib/gitlab/ci_access_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CiAccess do
let(:access) { described_class.new }
diff --git a/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb
new file mode 100644
index 00000000000..36c5d0e9b0c
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Cleanup::PersonalAccessTokens do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+
+ let(:group_full_path) { group.full_path }
+ let(:logger) { instance_double(Gitlab::AppJsonLogger, info: nil, warn: nil) }
+ let(:last_used_at) { 1.month.ago.beginning_of_hour }
+ let!(:unused_token) { create(:personal_access_token) }
+
+ let!(:old_unused_token) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_actively_used_token) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute, last_used_at: 1.day.ago)
+ end
+
+ let!(:old_unused_token_for_non_group_member) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_unused_token_for_subgroup_member) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_unused_project_access_token) do
+ create(:personal_access_token, user: project_bot, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_formerly_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: last_used_at - 1.minute
+ )
+ end
+
+ before do
+ group.add_member(old_formerly_used_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(old_actively_used_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(unused_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(old_unused_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(project_bot, Gitlab::Access::MAINTAINER)
+
+ subgroup.add_member(old_unused_token_for_subgroup_member.user, Gitlab::Access::DEVELOPER)
+ end
+
+ subject do
+ described_class.new(
+ logger: logger,
+ cut_off_date: last_used_at,
+ group_full_path: group_full_path
+ )
+ end
+
+ context 'when initialized with an invalid logger' do
+ let(:logger) { "not a logger" }
+
+ it 'raises error' do
+ expect do
+ subject.run!
+ end.to raise_error('Invalid logger: not a logger')
+ end
+ end
+
+ describe '#run!' do
+ context 'when invalid group path passed' do
+ let(:group_full_path) { 'notagroup' }
+
+ it 'raises error' do
+ expect do
+ subject.run!
+ end.to raise_error("Group with full_path notagroup not found")
+ end
+ end
+
+ context 'in a real run' do
+ let(:args) { { dry_run: false } }
+
+ context 'when revoking unused tokens' do
+ it 'revokes human-owned tokens created and last used over 1 year ago' do
+ subject.run!(**args)
+
+ expect(PersonalAccessToken.active).to contain_exactly(
+ unused_token,
+ old_actively_used_token,
+ old_unused_project_access_token,
+ old_unused_token_for_non_group_member,
+ old_unused_token_for_subgroup_member
+ )
+ expect(PersonalAccessToken.revoked).to contain_exactly(
+ old_unused_token,
+ old_formerly_used_token
+ )
+ end
+ end
+
+ context 'when revoking used and unused tokens' do
+ let(:args) { { dry_run: false, revoke_active_tokens: true } }
+
+ it 'revokes human-owned tokens created over 1 year ago' do
+ subject.run!(**args)
+
+ expect(PersonalAccessToken.active).to contain_exactly(
+ unused_token,
+ old_unused_project_access_token,
+ old_unused_token_for_non_group_member,
+ old_unused_token_for_subgroup_member
+ )
+ expect(PersonalAccessToken.revoked).to contain_exactly(
+ old_unused_token,
+ old_actively_used_token,
+ old_formerly_used_token
+ )
+ end
+ end
+
+ it 'updates updated_at' do
+ expect do
+ subject.run!(**args)
+ end.to change {
+ old_unused_token.reload.updated_at
+ }
+ end
+
+ it 'logs action as done' do
+ message = {
+ dry_run: false,
+ token_count: 2,
+ updated_count: 2,
+ tokens: instance_of(Array),
+ group_full_path: group_full_path
+ }
+ expect(logger).to receive(:info).with(include(message))
+ subject.run!(**args)
+ end
+ end
+
+ context 'in a dry run' do
+ # Dry run is the default
+ let(:args) { {} }
+
+ it 'does not revoke any tokens' do
+ expect do
+ subject.run!(**args)
+ end.to not_change {
+ PersonalAccessToken.active.count
+ }
+ end
+
+ it 'logs what could be revoked' do
+ message = {
+ dry_run: true,
+ token_count: 2,
+ updated_count: 0,
+ tokens: instance_of(Array),
+ group_full_path: group_full_path
+ }
+ expect(logger).to receive(:info).with(include(message))
+ subject.run!(**args)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 279486aa2a1..1422f83c629 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::ClosingIssueExtractor do
let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:project2) { create(:project) }
+ let_it_be_with_reload(:project2) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project2) }
@@ -335,6 +335,17 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
end
+ context 'when target project has autoclose issues disabled' do
+ before do
+ project2.update!(autoclose_referenced_issues: false)
+ end
+
+ it 'omits the issue reference' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to be_empty
+ end
+ end
+
context "with an invalid URL" do
it do
message = "Closes https://google.com#{urls.project_issue_path(issue2.project, issue2)}"
@@ -443,14 +454,19 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
context "with autoclose referenced issues disabled" do
- before do
+ before_all do
project.update!(autoclose_referenced_issues: false)
end
- it do
+ it 'excludes same project references' do
message = "Awesome commit (Closes #{reference})"
expect(subject.closed_by_message(message)).to eq([])
end
+
+ it 'includes issues from other projects with autoclose enabled' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to eq([issue2])
+ end
end
end
diff --git a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
index 948de161235..cf532cf7be6 100644
--- a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
+++ b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Cluster::PumaWorkerKillerObserver do
let(:counter) { Gitlab::Metrics::NullMetric.instance }
diff --git a/spec/lib/gitlab/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb
index 1e7880ed898..8a207bddaae 100644
--- a/spec/lib/gitlab/config/entry/attributable_spec.rb
+++ b/spec/lib/gitlab/config/entry/attributable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Attributable do
let(:node) do
diff --git a/spec/lib/gitlab/config/entry/composable_hash_spec.rb b/spec/lib/gitlab/config/entry/composable_hash_spec.rb
index f64b39231a3..331c9efc741 100644
--- a/spec/lib/gitlab/config/entry/composable_hash_spec.rb
+++ b/spec/lib/gitlab/config/entry/composable_hash_spec.rb
@@ -6,7 +6,8 @@ RSpec.describe Gitlab::Config::Entry::ComposableHash, :aggregate_failures do
let(:valid_config) do
{
DATABASE_SECRET: 'passw0rd',
- API_TOKEN: 'passw0rd2'
+ API_TOKEN: 'passw0rd2',
+ ACCEPT_PASSWORD: false
}
end
@@ -55,6 +56,12 @@ RSpec.describe Gitlab::Config::Entry::ComposableHash, :aggregate_failures do
expect(entry[:API_TOKEN].metadata).to eq(name: :API_TOKEN)
expect(entry[:API_TOKEN].parent.class).to eq(Gitlab::Config::Entry::ComposableHash)
expect(entry[:API_TOKEN].value).to eq('passw0rd2')
+ expect(entry[:ACCEPT_PASSWORD]).to be_a(Gitlab::Config::Entry::Node)
+ expect(entry[:ACCEPT_PASSWORD].description).to eq('ACCEPT_PASSWORD node definition')
+ expect(entry[:ACCEPT_PASSWORD].key).to eq(:ACCEPT_PASSWORD)
+ expect(entry[:ACCEPT_PASSWORD].metadata).to eq(name: :ACCEPT_PASSWORD)
+ expect(entry[:ACCEPT_PASSWORD].parent.class).to eq(Gitlab::Config::Entry::ComposableHash)
+ expect(entry[:ACCEPT_PASSWORD].value).to eq(false)
end
describe '#descendants' do
diff --git a/spec/lib/gitlab/config/entry/simplifiable_spec.rb b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
index f9088130037..fbbc9571eb0 100644
--- a/spec/lib/gitlab/config/entry/simplifiable_spec.rb
+++ b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Simplifiable do
describe '.strategy' do
diff --git a/spec/lib/gitlab/config/entry/undefined_spec.rb b/spec/lib/gitlab/config/entry/undefined_spec.rb
index 31e0f9487aa..faa9b9b8a7c 100644
--- a/spec/lib/gitlab/config/entry/undefined_spec.rb
+++ b/spec/lib/gitlab/config/entry/undefined_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Undefined do
let(:entry) { described_class.new }
diff --git a/spec/lib/gitlab/config/entry/unspecified_spec.rb b/spec/lib/gitlab/config/entry/unspecified_spec.rb
index 35ba992f62a..8fc0889367f 100644
--- a/spec/lib/gitlab/config/entry/unspecified_spec.rb
+++ b/spec/lib/gitlab/config/entry/unspecified_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Unspecified do
let(:unspecified) { described_class.new(entry) }
diff --git a/spec/lib/gitlab/container_repository/tags/cache_spec.rb b/spec/lib/gitlab/container_repository/tags/cache_spec.rb
index f84c1ce173f..fcfc8e7a348 100644
--- a/spec/lib/gitlab/container_repository/tags/cache_spec.rb
+++ b/spec/lib/gitlab/container_repository/tags/cache_spec.rb
@@ -79,10 +79,14 @@ RSpec.describe ::Gitlab::ContainerRepository::Tags::Cache, :clean_gitlab_redis_c
it 'inserts values in redis' do
::Gitlab::Redis::Cache.with do |redis|
- expect(redis)
- .to receive(:set)
- .with(cache_key(tag), rfc3339(tag.created_at), ex: ttl.to_i)
- .and_call_original
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline)
+ .to receive(:set)
+ .with(cache_key(tag), rfc3339(tag.created_at), ex: ttl.to_i)
+ .and_call_original
+ end
end
subject
diff --git a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
index 178188f5555..a75c943aaf6 100644
--- a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::CheckCollection do
subject(:collection) { described_class.new }
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
index 5327030daf0..7cf2309a1f8 100644
--- a/spec/lib/gitlab/cross_project_access/check_info_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::CheckInfo do
let(:dummy_controller) { double }
diff --git a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
index afc45c86362..3a6e528c9b0 100644
--- a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::ClassMethods do
let(:dummy_class) do
diff --git a/spec/lib/gitlab/cross_project_access_spec.rb b/spec/lib/gitlab/cross_project_access_spec.rb
index fb72b85f161..e45c734a003 100644
--- a/spec/lib/gitlab/cross_project_access_spec.rb
+++ b/spec/lib/gitlab/cross_project_access_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess do
let(:super_class) { Class.new }
diff --git a/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb b/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
index c955b288500..41b0604bee0 100644
--- a/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CycleAnalytics::Summary::Value do
describe Gitlab::CycleAnalytics::Summary::Value::None do
diff --git a/spec/lib/gitlab/data_builder/issuable_spec.rb b/spec/lib/gitlab/data_builder/issuable_spec.rb
index f0802f335f4..455800a3f7d 100644
--- a/spec/lib/gitlab/data_builder/issuable_spec.rb
+++ b/spec/lib/gitlab/data_builder/issuable_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe Gitlab::DataBuilder::Issuable do
},
assignees: {
previous: [],
- current: [{
+ current: [{
name: "Foo Bar",
username: "foobar",
avatar_url: "http://www.example.com/my-avatar.jpg"
diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb
index 3fa535dd800..8e8b8ce6681 100644
--- a/spec/lib/gitlab/data_builder/note_spec.rb
+++ b/spec/lib/gitlab/data_builder/note_spec.rb
@@ -49,8 +49,7 @@ RSpec.describe Gitlab::DataBuilder::Note do
let(:label) { create(:label, project: project) }
let(:issue) do
- create(:labeled_issue, created_at: fixed_time, updated_at: fixed_time,
- project: project, labels: [label])
+ create(:labeled_issue, created_at: fixed_time, updated_at: fixed_time, project: project, labels: [label])
end
let(:note) do
@@ -84,15 +83,15 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on merge request' do
let(:label) { create(:label, project: project) }
let(:merge_request) do
- create(:labeled_merge_request, created_at: fixed_time,
- updated_at: fixed_time,
- source_project: project,
- labels: [label])
+ create(:labeled_merge_request,
+ created_at: fixed_time,
+ updated_at: fixed_time,
+ source_project: project,
+ labels: [label])
end
let(:note) do
- create(:note_on_merge_request, noteable: merge_request,
- project: project)
+ create(:note_on_merge_request, noteable: merge_request, project: project)
end
it_behaves_like 'includes general data'
@@ -112,14 +111,15 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on merge request diff' do
let(:label) { create(:label, project: project) }
let(:merge_request) do
- create(:labeled_merge_request, created_at: fixed_time, updated_at: fixed_time,
- source_project: project,
- labels: [label])
+ create(:labeled_merge_request,
+ created_at: fixed_time,
+ updated_at: fixed_time,
+ source_project:
+ project, labels: [label])
end
let(:note) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- project: project)
+ create(:diff_note_on_merge_request, noteable: merge_request, project: project)
end
it_behaves_like 'includes general data'
@@ -138,13 +138,11 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on project snippet' do
let!(:snippet) do
- create(:project_snippet, created_at: fixed_time, updated_at: fixed_time,
- project: project)
+ create(:project_snippet, created_at: fixed_time, updated_at: fixed_time, project: project)
end
let!(:note) do
- create(:note_on_project_snippet, noteable: snippet,
- project: project)
+ create(:note_on_project_snippet, noteable: snippet, project: project)
end
it_behaves_like 'includes general data'
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 06c2bc32db3..3daed2508a2 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -59,6 +59,50 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '#pause!' do
+ context 'when an invalid transition is applied' do
+ %i[finished failed finalizing].each do |state|
+ it 'raises an exception' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect { batched_migration.pause! }.to raise_error(StateMachines::InvalidTransition, /Cannot transition status/)
+ end
+ end
+ end
+
+ context 'when a valid transition is applied' do
+ %i[active paused].each do |state|
+ it 'moves to pause' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect(batched_migration.pause!).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe '#execute!' do
+ context 'when an invalid transition is applied' do
+ %i[finished finalizing].each do |state|
+ it 'raises an exception' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect { batched_migration.execute! }.to raise_error(StateMachines::InvalidTransition, /Cannot transition status/)
+ end
+ end
+ end
+
+ context 'when a valid transition is applied' do
+ %i[active paused failed].each do |state|
+ it 'moves to active' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect(batched_migration.execute!).to be_truthy
+ end
+ end
+ end
+ end
+
describe '.valid_status' do
valid_status = [:paused, :active, :finished, :failed, :finalizing]
@@ -77,6 +121,16 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '.ordered_by_created_at_desc' do
+ let!(:migration_1) { create(:batched_background_migration, created_at: Time.zone.now - 2) }
+ let!(:migration_2) { create(:batched_background_migration, created_at: Time.zone.now - 1) }
+ let!(:migration_3) { create(:batched_background_migration, created_at: Time.zone.now - 3) }
+
+ it 'returns batched migrations ordered by created_at (DESC)' do
+ expect(described_class.ordered_by_created_at_desc).to eq([migration_2, migration_1, migration_3])
+ end
+ end
+
describe '.active_migration' do
let(:connection) { Gitlab::Database.database_base_models[:main].connection }
let!(:migration1) { create(:batched_background_migration, :finished) }
@@ -620,7 +674,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
describe '#progress' do
subject { migration.progress }
- context 'when the migration is finished' do
+ context 'when the migration is completed' do
let(:migration) do
create(:batched_background_migration, :finished, total_tuple_count: 1).tap do |record|
create(:batched_background_migration_job, :succeeded, batched_migration: record, batch_size: 1)
@@ -632,6 +686,18 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ context 'when the status is finished' do
+ let(:migration) do
+ create(:batched_background_migration, :finished, total_tuple_count: 100).tap do |record|
+ create(:batched_background_migration_job, :succeeded, batched_migration: record, batch_size: 5)
+ end
+ end
+
+ it 'returns 100' do
+ expect(subject).to be 100
+ end
+ end
+
context 'when the migration does not have jobs' do
let(:migration) { create(:batched_background_migration, :active) }
diff --git a/spec/lib/gitlab/database/batch_average_counter_spec.rb b/spec/lib/gitlab/database/batch_average_counter_spec.rb
new file mode 100644
index 00000000000..43c7a1554f7
--- /dev/null
+++ b/spec/lib/gitlab/database/batch_average_counter_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BatchAverageCounter do
+ let(:model) { Issue }
+ let(:column) { :weight }
+
+ let(:in_transaction) { false }
+
+ before do
+ allow(model.connection).to receive(:transaction_open?).and_return(in_transaction)
+ end
+
+ describe '#count' do
+ before do
+ create_list(:issue, 2, weight: 4)
+ create_list(:issue, 2, weight: 2)
+ create_list(:issue, 2, weight: 3)
+ end
+
+ subject(:batch_average_counter) { described_class.new(model, column) }
+
+ it 'returns correct average of weights' do
+ expect(subject.count).to eq(3.0)
+ end
+
+ it 'does no raise an exception if transaction is not open' do
+ expect { subject.count }.not_to raise_error
+ end
+
+ context 'when transaction is open' do
+ let(:in_transaction) { true }
+
+ it 'raises an error' do
+ expect { subject.count }.to raise_error('BatchAverageCounter can not be run inside a transaction')
+ end
+ end
+
+ context 'when batch size is small' do
+ let(:batch_size) { 2 }
+
+ it 'returns correct average of weights' do
+ expect(subject.count(batch_size: batch_size)).to eq(3.0)
+ end
+ end
+
+ context 'when column passed is an Arel attribute' do
+ let(:column) { model.arel_table[:weight] }
+
+ it 'returns correct average of weights' do
+ expect(subject.count).to eq(3.0)
+ end
+ end
+
+ context 'when column has total count of zero' do
+ before do
+ Issue.update_all(weight: nil)
+ end
+
+ it 'returns the fallback value' do
+ expect(subject.count).to eq(-1)
+ end
+ end
+
+ context 'when one batch has nil weights (no average)' do
+ before do
+ issues = Issue.where(weight: 4)
+ issues.update_all(weight: nil)
+ end
+
+ let(:batch_size) { 2 }
+
+ it 'calculates average of weights with no errors' do
+ expect(subject.count(batch_size: batch_size)).to eq(2.5)
+ end
+ end
+
+ context 'when batch fetch query is cancelled' do
+ let(:batch_size) { 22_000 }
+ let(:relation) { instance_double(ActiveRecord::Relation, to_sql: batch_average_query) }
+
+ context 'when all retries fail' do
+ let(:batch_average_query) { 'SELECT AVG(weight) FROM issues WHERE weight BETWEEN 2 and 5' }
+ let(:query_timed_out_exception) { ActiveRecord::QueryCanceled.new('query timed out') }
+
+ before do
+ allow(model).to receive(:where).and_return(relation)
+ allow(relation).to receive(:pick).and_raise(query_timed_out_exception)
+ end
+
+ it 'logs failing query' do
+ expect(Gitlab::AppJsonLogger).to receive(:error).with(
+ event: 'batch_count',
+ relation: model.table_name,
+ operation: 'average',
+ start: 2,
+ query: batch_average_query,
+ message: 'Query has been canceled with message: query timed out'
+ )
+
+ expect(subject.count(batch_size: batch_size)).to eq(-1)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb
index 811d4fad95c..a87b0c1a3a8 100644
--- a/spec/lib/gitlab/database/batch_count_spec.rb
+++ b/spec/lib/gitlab/database/batch_count_spec.rb
@@ -86,48 +86,48 @@ RSpec.describe Gitlab::Database::BatchCount do
query: batch_count_query,
message: 'Query has been canceled with message: query timed out'
)
- expect(subject.call(model, column, batch_size: batch_size, start: 0)).to eq(-1)
+ expect(subject.call(model, column, batch_size: batch_size, start: 0)).to eq(fallback)
end
end
end
describe '#batch_count' do
it 'counts table' do
- expect(described_class.batch_count(model)).to eq(5)
+ expect(described_class.batch_count(model)).to eq(model.count)
end
it 'counts with :id field' do
- expect(described_class.batch_count(model, :id)).to eq(5)
+ expect(described_class.batch_count(model, :id)).to eq(model.count)
end
it 'counts with "id" field' do
- expect(described_class.batch_count(model, 'id')).to eq(5)
+ expect(described_class.batch_count(model, 'id')).to eq(model.count)
end
it 'counts with table.id field' do
- expect(described_class.batch_count(model, "#{model.table_name}.id")).to eq(5)
+ expect(described_class.batch_count(model, "#{model.table_name}.id")).to eq(model.count)
end
it 'counts with Arel column' do
- expect(described_class.batch_count(model, model.arel_table[:id])).to eq(5)
+ expect(described_class.batch_count(model, model.arel_table[:id])).to eq(model.count)
end
it 'counts table with batch_size 50K' do
- expect(described_class.batch_count(model, batch_size: 50_000)).to eq(5)
+ expect(described_class.batch_count(model, batch_size: 50_000)).to eq(model.count)
end
it 'will not count table with a batch size less than allowed' do
expect(described_class.batch_count(model, batch_size: small_batch_size)).to eq(fallback)
end
- it 'counts with a small edge case batch_sizes than result' do
+ it 'produces the same result with different batch sizes' do
stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
- [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(5) }
+ [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(model.count) }
end
it 'counts with a start and finish' do
- expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(5)
+ expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(model.count)
end
it 'stops counting when finish value is reached' do
@@ -217,6 +217,113 @@ RSpec.describe Gitlab::Database::BatchCount do
end
end
+ describe '#batch_count_with_timeout' do
+ it 'counts table' do
+ expect(described_class.batch_count_with_timeout(model)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with :id field' do
+ expect(described_class.batch_count_with_timeout(model, :id)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with "id" field' do
+ expect(described_class.batch_count_with_timeout(model, 'id')).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with table.id field' do
+ expect(described_class.batch_count_with_timeout(model, "#{model.table_name}.id")).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with Arel column' do
+ expect(described_class.batch_count_with_timeout(model, model.arel_table[:id])).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts table with batch_size 50K' do
+ expect(described_class.batch_count_with_timeout(model, batch_size: 50_000)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'will not count table with a batch size less than allowed' do
+ expect(described_class.batch_count_with_timeout(model, batch_size: small_batch_size)).to eq({ status: :bad_config })
+ end
+
+ it 'produces the same result with different batch sizes' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count_with_timeout(model, batch_size: i)).to eq({ status: :completed, count: model.count }) }
+ end
+
+ it 'counts with a start and finish' do
+ expect(described_class.batch_count_with_timeout(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'stops counting when finish value is reached' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(described_class.batch_count_with_timeout(model,
+ start: model.minimum(:id),
+ finish: model.maximum(:id) - 1, # Do not count the last record
+ batch_size: model.count - 2 # Ensure there are multiple batches
+ )).to eq({ status: :completed, count: model.count - 1 })
+ end
+
+ it 'returns a partial count when timeout elapses' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(::Gitlab::Metrics::System).to receive(:monotonic_time).and_return(1, 10, 300)
+
+ expect(
+ described_class.batch_count_with_timeout(model, batch_size: 1, timeout: 250.seconds)
+ ).to eq({ status: :timeout, partial_results: 1, continue_from: model.minimum(:id) + 1 })
+ end
+
+ it 'starts counting from a given partial result' do
+ expect(described_class.batch_count_with_timeout(model, partial_results: 3)).to eq({ status: :completed, count: 3 + model.count })
+ end
+
+ it_behaves_like 'when a transaction is open' do
+ subject { described_class.batch_count_with_timeout(model) }
+ end
+
+ it_behaves_like 'when batch fetch query is canceled' do
+ let(:mode) { :itself }
+ let(:operation) { :count }
+ let(:operation_args) { nil }
+ let(:column) { nil }
+ let(:fallback) { { status: :cancelled } }
+
+ subject { described_class.method(:batch_count_with_timeout) }
+ end
+
+ context 'disallowed_configurations' do
+ include_examples 'disallowed configurations', :batch_count do
+ let(:args) { [Issue] }
+ let(:default_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_BATCH_SIZE }
+ end
+
+ it 'raises an error if distinct count is requested' do
+ expect { described_class.batch_count_with_timeout(model.distinct(column)) }.to raise_error 'Use distinct count for optimized distinct counting'
+ end
+ end
+
+ context 'when a relation is grouped' do
+ let!(:one_more_issue) { create(:issue, author: user, project: model.first.project) }
+
+ before do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 1)
+ end
+
+ context 'count by default column' do
+ let(:count) do
+ described_class.batch_count_with_timeout(model.group(column), batch_size: 2)
+ end
+
+ it 'counts grouped records' do
+ expect(count).to eq({ status: :completed, count: { user.id => 4, another_user.id => 2 } })
+ end
+ end
+ end
+ end
+
describe '#batch_distinct_count' do
it 'counts with column field' do
expect(described_class.batch_distinct_count(model, column)).to eq(2)
@@ -242,7 +349,7 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_distinct_count(model, column, batch_size: small_batch_size)).to eq(fallback)
end
- it 'counts with a small edge case batch_sizes than result' do
+ it 'produces the same result with different batch sizes' do
stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
[1, 2, 4, 5, 6].each { |i| expect(described_class.batch_distinct_count(model, column, batch_size: i)).to eq(2) }
@@ -386,56 +493,18 @@ RSpec.describe Gitlab::Database::BatchCount do
end
describe '#batch_average' do
- let(:model) { Issue }
let(:column) { :weight }
before do
- Issue.update_all(weight: 2)
- end
-
- it 'returns the average of values in the given column' do
- expect(described_class.batch_average(model, column)).to eq(2)
- end
-
- it 'works when given an Arel column' do
- expect(described_class.batch_average(model, model.arel_table[column])).to eq(2)
- end
-
- it 'works with a batch size of 50K' do
- expect(described_class.batch_average(model, column, batch_size: 50_000)).to eq(2)
- end
-
- it 'works with start and finish provided' do
- expect(described_class.batch_average(model, column, start: model.minimum(:id), finish: model.maximum(:id))).to eq(2)
+ allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance|
+ allow(instance).to receive(:count).and_return
+ end
end
- it "defaults the batch size to #{Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE}" do
- min_id = model.minimum(:id)
- relation = instance_double(ActiveRecord::Relation)
- allow(model).to receive_message_chain(:select, public_send: relation)
- batch_end_id = min_id + calculate_batch_size(Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE)
-
- expect(relation).to receive(:where).with("id" => min_id..batch_end_id).and_return(double(send: 1))
+ it 'calls BatchAverageCounter' do
+ expect(Gitlab::Database::BatchAverageCounter).to receive(:new).with(model, column).and_call_original
described_class.batch_average(model, column)
end
-
- it_behaves_like 'when a transaction is open' do
- subject { described_class.batch_average(model, column) }
- end
-
- it_behaves_like 'disallowed configurations', :batch_average do
- let(:args) { [model, column] }
- let(:default_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE }
- let(:small_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE - 1 }
- end
-
- it_behaves_like 'when batch fetch query is canceled' do
- let(:mode) { :itself }
- let(:operation) { :average }
- let(:operation_args) { [column] }
-
- subject { described_class.method(:batch_average) }
- end
end
end
diff --git a/spec/lib/gitlab/database/lock_writes_manager_spec.rb b/spec/lib/gitlab/database/lock_writes_manager_spec.rb
index eb527d492cf..b1cc8add55a 100644
--- a/spec/lib/gitlab/database/lock_writes_manager_spec.rb
+++ b/spec/lib/gitlab/database/lock_writes_manager_spec.rb
@@ -6,13 +6,15 @@ RSpec.describe Gitlab::Database::LockWritesManager do
let(:connection) { ApplicationRecord.connection }
let(:test_table) { '_test_table' }
let(:logger) { instance_double(Logger) }
+ let(:dry_run) { false }
subject(:lock_writes_manager) do
described_class.new(
table_name: test_table,
connection: connection,
database_name: 'main',
- logger: logger
+ logger: logger,
+ dry_run: dry_run
)
end
@@ -27,6 +29,16 @@ RSpec.describe Gitlab::Database::LockWritesManager do
SQL
end
+ describe "#table_locked_for_writes?" do
+ it 'returns false for a table that is not locked for writes' do
+ expect(subject.table_locked_for_writes?(test_table)).to eq(false)
+ end
+
+ it 'returns true for a table that is locked for writes' do
+ expect { subject.lock_writes }.to change { subject.table_locked_for_writes?(test_table) }.from(false).to(true)
+ end
+ end
+
describe '#lock_writes' do
it 'prevents any writes on the table' do
subject.lock_writes
@@ -84,11 +96,57 @@ RSpec.describe Gitlab::Database::LockWritesManager do
subject.lock_writes
end.to raise_error(ActiveRecord::QueryCanceled)
end
+
+ it 'skips the operation if the table is already locked for writes' do
+ subject.lock_writes
+
+ expect(logger).to receive(:info).with("Skipping lock_writes, because #{test_table} is already locked for writes")
+ expect(connection).not_to receive(:execute).with(/CREATE TRIGGER/)
+
+ expect do
+ subject.lock_writes
+ end.not_to change {
+ number_of_triggers_on(connection, test_table)
+ }
+ end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'prints the sql statement to the logger' do
+ expect(logger).to receive(:info).with("Database: 'main', Table: '#{test_table}': Lock Writes")
+ expected_sql_statement = <<~SQL
+ CREATE TRIGGER gitlab_schema_write_trigger_for_#{test_table}
+ BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
+ ON #{test_table}
+ FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write();
+ SQL
+ expect(logger).to receive(:info).with(expected_sql_statement)
+
+ subject.lock_writes
+ end
+
+ it 'does not lock the tables for writes' do
+ subject.lock_writes
+
+ expect do
+ connection.execute("delete from #{test_table}")
+ connection.execute("truncate #{test_table}")
+ end.not_to raise_error
+ end
+ end
end
describe '#unlock_writes' do
before do
- subject.lock_writes
+ # Locking the table without the considering the value of dry_run
+ described_class.new(
+ table_name: test_table,
+ connection: connection,
+ database_name: 'main',
+ logger: logger,
+ dry_run: false
+ ).lock_writes
end
it 'allows writing on the table again' do
@@ -114,6 +172,28 @@ RSpec.describe Gitlab::Database::LockWritesManager do
subject.unlock_writes
end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'prints the sql statement to the logger' do
+ expect(logger).to receive(:info).with("Database: 'main', Table: '#{test_table}': Allow Writes")
+ expected_sql_statement = <<~SQL
+ DROP TRIGGER IF EXISTS gitlab_schema_write_trigger_for_#{test_table} ON #{test_table};
+ SQL
+ expect(logger).to receive(:info).with(expected_sql_statement)
+
+ subject.unlock_writes
+ end
+
+ it 'does not unlock the tables for writes' do
+ subject.unlock_writes
+
+ expect do
+ connection.execute("delete from #{test_table}")
+ end.to raise_error(ActiveRecord::StatementInvalid, /Table: "#{test_table}" is write protected/)
+ end
+ end
end
def number_of_triggers_on(connection, table_name)
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index dd5ad40d8ef..d73b478ee7c 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -667,7 +667,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
column: :user_id,
on_delete: :cascade,
name: name,
- primary_key: :id).and_return(true)
+ primary_key: :id).and_return(true)
expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/)
expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/)
diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
index 9451a6bd34a..3ac483c8ab7 100644
--- a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
connection.execute(<<~SQL)
CREATE TABLE #{table_name} (
id bigint primary key not null,
- data bigint
+ data bigint default 0
);
insert into #{table_name} (id) select i from generate_series(1, 1000) g(i);
@@ -40,10 +40,12 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
:id, :data,
batch_size: 100,
job_interval: 5.minutes) # job_interval is skipped when testing
- described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute)
- unmigrated_row_count = define_batchable_model(table_name).where('id != data').count
- expect(unmigrated_row_count).to eq(0)
+ # Expect that running sampling for this migration processes some of the rows. Sampling doesn't run
+ # over every row in the table, so this does not completely migrate the table.
+ expect { described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute) }
+ .to change { define_batchable_model(table_name).where('id IS DISTINCT FROM data').count }
+ .by_at_most(-1)
end
end
@@ -62,7 +64,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 3.minutes)
- expect(calls.count).to eq(10) # 1000 rows / batch size 100 = 10
+ expect(calls).not_to be_empty
end
context 'with multiple jobs to run' do
@@ -92,4 +94,19 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
end
end
end
+
+ context 'choosing uniform batches to run' do
+ subject { described_class.new(result_dir: result_dir, connection: connection) }
+
+ describe '#uniform_fractions' do
+ it 'generates evenly distributed sequences of fractions' do
+ received = subject.uniform_fractions.take(9)
+ expected = [0, 1, 1.0 / 2, 1.0 / 4, 3.0 / 4, 1.0 / 8, 3.0 / 8, 5.0 / 8, 7.0 / 8]
+
+ # All the fraction numerators are small integers, and all denominators are powers of 2, so these
+ # fit perfectly into floating point numbers with zero loss of precision
+ expect(received).to eq(expected)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
new file mode 100644
index 00000000000..af7d751a404
--- /dev/null
+++ b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition do
+ include Gitlab::Database::DynamicModelHelpers
+ include Database::TableSchemaHelpers
+
+ let(:migration_context) { Gitlab::Database::Migration[2.0].new }
+
+ let(:connection) { migration_context.connection }
+ let(:table_name) { '_test_table_to_partition' }
+ let(:table_identifier) { "#{connection.current_schema}.#{table_name}" }
+ let(:partitioning_column) { :partition_number }
+ let(:partitioning_default) { 1 }
+ let(:referenced_table_name) { '_test_referenced_table' }
+ let(:other_referenced_table_name) { '_test_other_referenced_table' }
+ let(:parent_table_name) { "#{table_name}_parent" }
+
+ let(:model) { define_batchable_model(table_name, connection: connection) }
+
+ let(:parent_model) { define_batchable_model(parent_table_name, connection: connection) }
+
+ let(:converter) do
+ described_class.new(
+ migration_context: migration_context,
+ table_name: table_name,
+ partitioning_column: partitioning_column,
+ parent_table_name: parent_table_name,
+ zero_partition_value: partitioning_default
+ )
+ end
+
+ before do
+ # Suppress printing migration progress
+ allow(migration_context).to receive(:puts)
+ allow(migration_context.connection).to receive(:transaction_open?).and_return(false)
+
+ connection.execute(<<~SQL)
+ create table #{referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{other_referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{referenced_table_name} default values;
+ insert into #{other_referenced_table_name} default values;
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{table_name} (
+ id bigserial not null,
+ #{partitioning_column} bigint not null default #{partitioning_default},
+ referenced_id bigint not null references #{referenced_table_name} (id) on delete cascade,
+ other_referenced_id bigint not null references #{other_referenced_table_name} (id) on delete set null,
+ primary key (id, #{partitioning_column})
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{table_name} (referenced_id, other_referenced_id)
+ select #{referenced_table_name}.id, #{other_referenced_table_name}.id
+ from #{referenced_table_name}, #{other_referenced_table_name};
+ SQL
+ end
+
+ describe "#prepare_for_partitioning" do
+ subject(:prepare) { converter.prepare_for_partitioning }
+
+ it 'adds a check constraint' do
+ expect { prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.from(0).to(1)
+ end
+ end
+
+ describe '#revert_prepare_for_partitioning' do
+ before do
+ converter.prepare_for_partitioning
+ end
+
+ subject(:revert_prepare) { converter.revert_preparation_for_partitioning }
+
+ it 'removes a check constraint' do
+ expect { revert_prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier("#{connection.current_schema}.#{table_name}")
+ .count
+ }.from(1).to(0)
+ end
+ end
+
+ describe "#convert_to_zero_partition" do
+ subject(:partition) { converter.partition }
+
+ before do
+ converter.prepare_for_partitioning
+ end
+
+ context 'when the primary key is incorrect' do
+ before do
+ connection.execute(<<~SQL)
+ alter table #{table_name} drop constraint #{table_name}_pkey;
+ alter table #{table_name} add constraint #{table_name}_pkey PRIMARY KEY (id);
+ SQL
+ end
+
+ it 'throws a reasonable error message' do
+ expect { partition }.to raise_error(described_class::UnableToPartition, /#{partitioning_column}/)
+ end
+ end
+
+ context 'when there is not a supporting check constraint' do
+ before do
+ connection.execute(<<~SQL)
+ alter table #{table_name} drop constraint partitioning_constraint;
+ SQL
+ end
+
+ it 'throws a reasonable error message' do
+ expect { partition }.to raise_error(described_class::UnableToPartition, /constraint /)
+ end
+ end
+
+ it 'migrates the table to a partitioned table' do
+ fks_before = migration_context.foreign_keys(table_name)
+
+ partition
+
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1)
+ expect(migration_context.foreign_keys(parent_table_name).map(&:options)).to match_array(fks_before.map(&:options))
+
+ connection.execute(<<~SQL)
+ insert into #{table_name} (referenced_id, other_referenced_id) select #{referenced_table_name}.id, #{other_referenced_table_name}.id from #{referenced_table_name}, #{other_referenced_table_name};
+ SQL
+
+ # Create a second partition
+ connection.execute(<<~SQL)
+ create table #{table_name}2 partition of #{parent_table_name} FOR VALUES IN (2)
+ SQL
+
+ parent_model.create!(partitioning_column => 2, :referenced_id => 1, :other_referenced_id => 1)
+ expect(parent_model.pluck(:id)).to match_array([1, 2, 3])
+ end
+
+ context 'when an error occurs during the conversion' do
+ def fail_first_time
+ # We can't directly use a boolean here, as we need something that will be passed by-reference to the proc
+ fault_status = { faulted: false }
+ proc do |m, *args, **kwargs|
+ next m.call(*args, **kwargs) if fault_status[:faulted]
+
+ fault_status[:faulted] = true
+ raise 'fault!'
+ end
+ end
+
+ def fail_sql_matching(regex)
+ proc do
+ allow(migration_context.connection).to receive(:execute).and_call_original
+ allow(migration_context.connection).to receive(:execute).with(regex).and_wrap_original(&fail_first_time)
+ end
+ end
+
+ def fail_adding_fk(from_table, to_table)
+ proc do
+ allow(migration_context.connection).to receive(:add_foreign_key).and_call_original
+ expect(migration_context.connection).to receive(:add_foreign_key).with(from_table, to_table, any_args)
+ .and_wrap_original(&fail_first_time)
+ end
+ end
+
+ where(:case_name, :fault) do
+ [
+ ["creating parent table", lazy { fail_sql_matching(/CREATE/i) }],
+ ["adding the first foreign key", lazy { fail_adding_fk(parent_table_name, referenced_table_name) }],
+ ["adding the second foreign key", lazy { fail_adding_fk(parent_table_name, other_referenced_table_name) }],
+ ["attaching table", lazy { fail_sql_matching(/ATTACH/i) }]
+ ]
+ end
+
+ before do
+ # Set up the fault that we'd like to inject
+ fault.call
+ end
+
+ with_them do
+ it 'recovers from a fault', :aggregate_failures do
+ expect { converter.partition }.to raise_error(/fault/)
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(0)
+
+ expect { converter.partition }.not_to raise_error
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1)
+ end
+ end
+ end
+ end
+
+ describe '#revert_conversion_to_zero_partition' do
+ before do
+ converter.prepare_for_partitioning
+ converter.partition
+ end
+
+ subject(:revert_conversion) { converter.revert_partitioning }
+
+ it 'detaches the partition' do
+ expect { revert_conversion }.to change {
+ Gitlab::Database::PostgresPartition
+ .for_parent_table(parent_table_name).count
+ }.from(1).to(0)
+ end
+
+ it 'does not drop the child partition' do
+ expect { revert_conversion }.not_to change { table_oid(table_name) }
+ end
+
+ it 'removes the parent table' do
+ expect { revert_conversion }.to change { table_oid(parent_table_name).present? }.from(true).to(false)
+ end
+
+ it 're-adds the check constraint' do
+ expect { revert_conversion }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.by(1)
+ end
+
+ it 'moves sequences back to the original table' do
+ expect { revert_conversion }.to change { converter.send(:sequences_owned_by, table_name).count }.from(0)
+ .and change { converter.send(:sequences_owned_by, parent_table_name).count }.to(0)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
index dca4548a0a3..8027990a546 100644
--- a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
@@ -21,20 +21,11 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
let(:model) { double(partitioning_strategy: partitioning_strategy, table_name: table, connection: connection) }
let(:connection) { ActiveRecord::Base.connection }
- let(:table) { "issues" }
+ let(:table) { "my_model_example_table" }
let(:partitioning_strategy) do
double(missing_partitions: partitions, extra_partitions: [], after_adding_partitions: nil)
end
- before do
- allow(connection).to receive(:table_exists?).and_call_original
- allow(connection).to receive(:table_exists?).with(table).and_return(true)
- allow(connection).to receive(:execute).and_call_original
- expect(partitioning_strategy).to receive(:validate_and_fix)
-
- stub_exclusive_lease(described_class::MANAGEMENT_LEASE_KEY % table, timeout: described_class::LEASE_TIMEOUT)
- end
-
let(:partitions) do
[
instance_double(Gitlab::Database::Partitioning::TimePartition, table: 'bar', partition_name: 'foo', to_sql: "SELECT 1"),
@@ -42,19 +33,63 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
]
end
- it 'creates the partition' do
- expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
- expect(connection).to receive(:execute).with(partitions.first.to_sql)
- expect(connection).to receive(:execute).with(partitions.second.to_sql)
+ context 'when the given table is partitioned' do
+ before do
+ create_partitioned_table(connection, table)
- sync_partitions
+ allow(connection).to receive(:table_exists?).and_call_original
+ allow(connection).to receive(:table_exists?).with(table).and_return(true)
+ allow(connection).to receive(:execute).and_call_original
+ expect(partitioning_strategy).to receive(:validate_and_fix)
+
+ stub_exclusive_lease(described_class::MANAGEMENT_LEASE_KEY % table, timeout: described_class::LEASE_TIMEOUT)
+ end
+
+ it 'creates the partition' do
+ expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(connection).to receive(:execute).with(partitions.first.to_sql)
+ expect(connection).to receive(:execute).with(partitions.second.to_sql)
+
+ sync_partitions
+ end
+
+ context 'with eplicitly provided connection' do
+ let(:connection) { Ci::ApplicationRecord.connection }
+
+ it 'uses the explicitly provided connection when any' do
+ skip_if_multiple_databases_not_setup
+
+ expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(connection).to receive(:execute).with(partitions.first.to_sql)
+ expect(connection).to receive(:execute).with(partitions.second.to_sql)
+
+ described_class.new(model, connection: connection).sync_partitions
+ end
+ end
+
+ context 'when an error occurs during partition management' do
+ it 'does not raise an error' do
+ expect(partitioning_strategy).to receive(:missing_partitions).and_raise('this should never happen (tm)')
+
+ expect { sync_partitions }.not_to raise_error
+ end
+ end
end
- context 'when an error occurs during partition management' do
- it 'does not raise an error' do
- expect(partitioning_strategy).to receive(:missing_partitions).and_raise('this should never happen (tm)')
+ context 'when the table is not partitioned' do
+ let(:table) { 'this_does_not_need_to_be_real_table' }
+
+ it 'does not try creating the partitions' do
+ expect(connection).not_to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ {
+ message: 'Skipping synching partitions',
+ table_name: table,
+ connection_name: 'main'
+ }
+ )
- expect { sync_partitions }.not_to raise_error
+ sync_partitions
end
end
end
@@ -74,11 +109,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
before do
- connection.execute(<<~SQL)
- CREATE TABLE my_model_example_table
- (id serial not null, created_at timestamptz not null, primary key (id, created_at))
- PARTITION BY RANGE (created_at);
- SQL
+ create_partitioned_table(connection, 'my_model_example_table')
end
it 'creates partitions' do
@@ -98,6 +129,8 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
before do
+ create_partitioned_table(connection, table)
+
allow(connection).to receive(:table_exists?).and_call_original
allow(connection).to receive(:table_exists?).with(table).and_return(true)
expect(partitioning_strategy).to receive(:validate_and_fix)
@@ -260,4 +293,12 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
expect { described_class.new(my_model).sync_partitions }.to change { has_partition(my_model, 2.months.ago.beginning_of_month) }.from(true).to(false).and(change { num_partitions(my_model) }.by(0))
end
end
+
+ def create_partitioned_table(connection, table)
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table}
+ (id serial not null, created_at timestamptz not null, primary key (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
end
diff --git a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
index 04b9fba5b2f..07c2c6606d8 100644
--- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
end
context 'when some partitions are true for detach_partition_if' do
- let(:detach_partition_if) { ->(p) { p != 5 } }
+ let(:detach_partition_if) { ->(p) { p.value != 5 } }
it 'is the leading set of partitions before that value' do
# should not contain partition 2 since it's the default value for the partition column
@@ -181,9 +181,10 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
Class.new(ApplicationRecord) do
include PartitionedTable
- partitioned_by :partition, strategy: :sliding_list,
- next_partition_if: proc { false },
- detach_partition_if: proc { false }
+ partitioned_by :partition,
+ strategy: :sliding_list,
+ next_partition_if: proc { false },
+ detach_partition_if: proc { false }
end
end.to raise_error(/ignored_columns/)
end
@@ -195,7 +196,8 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
self.ignored_columns = [:partition]
- partitioned_by :partition, strategy: :sliding_list,
+ partitioned_by :partition,
+ strategy: :sliding_list,
next_partition_if: proc { false },
detach_partition_if: proc { false }
end
@@ -221,7 +223,8 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
def self.detach_partition_if_wrapper(...)
detach_partition?(...)
end
- partitioned_by :partition, strategy: :sliding_list,
+ partitioned_by :partition,
+ strategy: :sliding_list,
next_partition_if: method(:next_partition_if_wrapper),
detach_partition_if: method(:detach_partition_if_wrapper)
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
index 3072c413246..1885e84ac4c 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
@@ -97,7 +97,8 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::BackfillPartition
end
it 'marks each job record as succeeded after processing' do
- create(:background_migration_job, class_name: "::#{described_class.name}",
+ create(:background_migration_job,
+ class_name: "::#{described_class.name}",
arguments: [source1.id, source3.id, source_table, destination_table, unique_key])
expect(::Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded).and_call_original
@@ -108,7 +109,8 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::BackfillPartition
end
it 'returns the number of job records marked as succeeded' do
- create(:background_migration_job, class_name: "::#{described_class.name}",
+ create(:background_migration_job,
+ class_name: "::#{described_class.name}",
arguments: [source1.id, source3.id, source_table, destination_table, unique_key])
jobs_updated = backfill_job.perform(source1.id, source3.id, source_table, destination_table, unique_key)
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
index edb8ae36c45..7465f69b87c 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
CREATE TABLE #{table_name} (
id serial NOT NULL,
created_at timestamptz NOT NULL,
+ updated_at timestamptz NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
@@ -204,4 +205,30 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end
end
end
+
+ describe '#find_duplicate_indexes' do
+ context 'when duplicate and non-duplicate indexes exist' do
+ let(:nonduplicate_column_name) { 'updated_at' }
+ let(:nonduplicate_index_name) { 'updated_at_idx' }
+ let(:duplicate_column_name) { 'created_at' }
+ let(:duplicate_index_name1) { 'created_at_idx' }
+ let(:duplicate_index_name2) { 'index_on_created_at' }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE INDEX #{nonduplicate_index_name} ON #{table_name} (#{nonduplicate_column_name});
+ CREATE INDEX #{duplicate_index_name1} ON #{table_name} (#{duplicate_column_name});
+ CREATE INDEX #{duplicate_index_name2} ON #{table_name} (#{duplicate_column_name});
+ SQL
+ end
+
+ subject do
+ migration.find_duplicate_indexes(table_name)
+ end
+
+ it 'finds the duplicate index' do
+ expect(subject).to match_array([match_array([duplicate_index_name1, duplicate_index_name2])])
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
index 1026b4370a5..8bb9ad2737a 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
@@ -41,6 +41,76 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe
allow(migration).to receive(:assert_table_is_allowed)
end
+ context 'list partitioning conversion helpers' do
+ shared_examples_for 'delegates to ConvertTableToFirstListPartition' do
+ it 'throws an error if in a transaction' do
+ allow(migration).to receive(:transaction_open?).and_return(true)
+ expect { migrate }.to raise_error(/cannot be run inside a transaction/)
+ end
+
+ it 'delegates to a method on ConvertTableToFirstListPartition' do
+ expect_next_instance_of(Gitlab::Database::Partitioning::ConvertTableToFirstListPartition,
+ migration_context: migration,
+ table_name: source_table,
+ parent_table_name: partitioned_table,
+ partitioning_column: partition_column,
+ zero_partition_value: min_date) do |converter|
+ expect(converter).to receive(expected_method)
+ end
+
+ migrate
+ end
+ end
+
+ describe '#convert_table_to_first_list_partition' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :partition }
+ let(:migrate) do
+ migration.convert_table_to_first_list_partition(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#revert_converting_table_to_first_list_partition' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :revert_partitioning }
+ let(:migrate) do
+ migration.revert_converting_table_to_first_list_partition(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#prepare_constraint_for_list_partitioning' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :prepare_for_partitioning }
+ let(:migrate) do
+ migration.prepare_constraint_for_list_partitioning(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#revert_preparing_constraint_for_list_partitioning' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :revert_preparation_for_partitioning }
+ let(:migrate) do
+ migration.revert_preparing_constraint_for_list_partitioning(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+ end
+
describe '#partition_table_by_date' do
let(:partition_column) { 'created_at' }
let(:old_primary_key) { 'id' }
diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb
index 36c8b0811fe..94cdbfb2328 100644
--- a/spec/lib/gitlab/database/partitioning_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::Database::Partitioning do
end
it 'does not call sync_partitions' do
- expect(described_class).to receive(:sync_partitions).never
+ expect(described_class).not_to receive(:sync_partitions)
described_class.sync_partitions_ignore_db_error
end
@@ -64,6 +64,7 @@ RSpec.describe Gitlab::Database::Partitioning do
end
describe '.sync_partitions' do
+ let(:ci_connection) { Ci::ApplicationRecord.connection }
let(:table_names) { %w[partitioning_test1 partitioning_test2] }
let(:models) do
table_names.map do |table_name|
@@ -94,6 +95,38 @@ RSpec.describe Gitlab::Database::Partitioning do
.and change { find_partitions(table_names.last).size }.from(0)
end
+ context 'with multiple databases' do
+ before do
+ table_names.each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+
+ ci_connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial not null,
+ created_at timestamptz not null,
+ PRIMARY KEY (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
+ end
+
+ after do
+ table_names.each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+ end
+ end
+
+ it 'creates partitions in each database' do
+ skip_if_multiple_databases_not_setup
+
+ expect { described_class.sync_partitions(models) }
+ .to change { find_partitions(table_names.first, conn: connection).size }.from(0)
+ .and change { find_partitions(table_names.last, conn: connection).size }.from(0)
+ .and change { find_partitions(table_names.first, conn: ci_connection).size }.from(0)
+ .and change { find_partitions(table_names.last, conn: ci_connection).size }.from(0)
+ end
+ end
+
context 'when no partitioned models are given' do
it 'manages partitions for each registered model' do
described_class.register_models([models.first])
@@ -111,16 +144,44 @@ RSpec.describe Gitlab::Database::Partitioning do
end
context 'when only a specific database is requested' do
+ let(:ci_model) do
+ Class.new(Ci::ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = 'partitioning_test3'
+ partitioned_by :created_at, strategy: :monthly
+ end
+ end
+
before do
- allow(models.first).to receive_message_chain('connection_db_config.name').and_return('main')
- allow(models.last).to receive_message_chain('connection_db_config.name').and_return('ci')
+ (table_names + ['partitioning_test3']).each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+
+ ci_connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial not null,
+ created_at timestamptz not null,
+ PRIMARY KEY (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
+ end
+
+ after do
+ (table_names + ['partitioning_test3']).each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+ end
end
it 'manages partitions for models for the given database', :aggregate_failures do
- expect { described_class.sync_partitions(models, only_on: 'ci') }
- .to change { find_partitions(table_names.last).size }.from(0)
+ skip_if_multiple_databases_not_setup
+
+ expect { described_class.sync_partitions([models.first, ci_model], only_on: 'ci') }
+ .to change { find_partitions(ci_model.table_name, conn: ci_connection).size }.from(0)
- expect(find_partitions(table_names.first).size).to eq(0)
+ expect(find_partitions(models.first.table_name).size).to eq(0)
+ expect(find_partitions(models.first.table_name, conn: ci_connection).size).to eq(0)
+ expect(find_partitions(ci_model.table_name).size).to eq(0)
end
end
end
diff --git a/spec/lib/gitlab/database/postgres_constraint_spec.rb b/spec/lib/gitlab/database/postgres_constraint_spec.rb
new file mode 100644
index 00000000000..75084a69115
--- /dev/null
+++ b/spec/lib/gitlab/database/postgres_constraint_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do
+ # PostgresConstraint does not `behaves_like 'a postgres model'` because it does not correspond 1-1 with a single entry
+ # in pg_class
+ let(:schema) { ActiveRecord::Base.connection.current_schema }
+ let(:table_name) { '_test_table' }
+ let(:table_identifier) { "#{schema}.#{table_name}" }
+ let(:referenced_name) { '_test_referenced' }
+ let(:check_constraint_a_positive) { 'check_constraint_a_positive' }
+ let(:check_constraint_a_gt_b) { 'check_constraint_a_gt_b' }
+ let(:invalid_constraint_a) { 'check_constraint_b_positive_invalid' }
+ let(:unique_constraint_a) { "#{table_name}_a_key" }
+
+ before do
+ ActiveRecord::Base.connection.execute(<<~SQL)
+ create table #{referenced_name} (
+ id bigserial primary key not null
+ );
+
+ create table #{table_name} (
+ id bigserial not null,
+ referenced_id bigint not null references #{referenced_name}(id),
+ a integer unique,
+ b integer,
+ primary key (id, referenced_id),
+ constraint #{check_constraint_a_positive} check (a > 0),
+ constraint #{check_constraint_a_gt_b} check (a > b)
+ );
+
+ alter table #{table_name} add constraint #{invalid_constraint_a} CHECK (a > 1) NOT VALID;
+ SQL
+ end
+
+ describe '#by_table_identifier' do
+ subject(:constraints_for_table) { described_class.by_table_identifier(table_identifier) }
+
+ it 'includes all constraints on the table' do
+ all_constraints_for_table = described_class.all.to_a.select { |c| c.table_identifier == table_identifier }
+ expect(all_constraints_for_table.map(&:oid)).to match_array(constraints_for_table.pluck(:oid))
+ end
+
+ it 'throws an error if the format is incorrect' do
+ expect { described_class.by_table_identifier('not-an-identifier') }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe '#check_constraints' do
+ subject(:check_constraints) { described_class.check_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds check constraints for the table' do
+ expect(check_constraints.map(&:name)).to contain_exactly(check_constraint_a_positive,
+ check_constraint_a_gt_b,
+ invalid_constraint_a)
+ end
+
+ it 'includes columns for the check constraints', :aggregate_failures do
+ expect(check_constraints.find_by(name: check_constraint_a_positive).column_names).to contain_exactly('a')
+ expect(check_constraints.find_by(name: check_constraint_a_gt_b).column_names).to contain_exactly('a', 'b')
+ end
+ end
+
+ describe "#valid" do
+ subject(:valid_constraint_names) { described_class.valid.by_table_identifier(table_identifier).pluck(:name) }
+
+ let(:all_constraint_names) { described_class.by_table_identifier(table_identifier).pluck(:name) }
+
+ it 'excludes invalid constraints' do
+ expect(valid_constraint_names).not_to include(invalid_constraint_a)
+ expect(valid_constraint_names).to match_array(all_constraint_names - [invalid_constraint_a])
+ end
+ end
+
+ describe '#primary_key_constraints' do
+ subject(:pk_constraints) { described_class.primary_key_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds the primary key constraint for the table' do
+ expect(pk_constraints.count).to eq(1)
+ expect(pk_constraints.first.constraint_type).to eq('p')
+ end
+
+ it 'finds the columns in the primary key constraint' do
+ constraint = pk_constraints.first
+ expect(constraint.column_names).to contain_exactly('id', 'referenced_id')
+ end
+ end
+
+ describe '#unique_constraints' do
+ subject(:unique_constraints) { described_class.unique_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds the unique constraints for the table' do
+ expect(unique_constraints.pluck(:name)).to contain_exactly(unique_constraint_a)
+ end
+ end
+
+ describe '#primary_or_unique_constraints' do
+ subject(:pk_or_unique_constraints) do
+ described_class.primary_or_unique_constraints.by_table_identifier(table_identifier)
+ end
+
+ it 'finds primary and unique constraints' do
+ expect(pk_or_unique_constraints.pluck(:name)).to contain_exactly("#{table_name}_pkey", unique_constraint_a)
+ end
+ end
+
+ describe '#including_column' do
+ it 'only matches constraints on the given column' do
+ constraints_on_a = described_class.by_table_identifier(table_identifier).including_column('a').map(&:name)
+ expect(constraints_on_a).to contain_exactly(check_constraint_a_positive, check_constraint_a_gt_b,
+ unique_constraint_a, invalid_constraint_a)
+ end
+ end
+
+ describe '#not_including_column' do
+ it 'only matches constraints not including the given column' do
+ constraints_not_on_a = described_class.by_table_identifier(table_identifier).not_including_column('a').map(&:name)
+
+ expect(constraints_not_on_a).to contain_exactly("#{table_name}_pkey", "#{table_name}_referenced_id_fkey")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb
new file mode 100644
index 00000000000..ef7c7965c09
--- /dev/null
+++ b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::QueryAnalyzers::Ci::PartitioningAnalyzer, query_analyzers: false do
+ let(:analyzer) { described_class }
+
+ before do
+ allow(Gitlab::Database::QueryAnalyzer.instance).to receive(:all_analyzers).and_return([analyzer])
+ end
+
+ context 'when ci_partitioning_analyze_queries is disabled' do
+ before do
+ stub_feature_flags(ci_partitioning_analyze_queries: false)
+ end
+
+ it 'does not analyze the query' do
+ expect(analyzer).not_to receive(:analyze)
+
+ process_sql(Ci::BuildMetadata, "SELECT 1 FROM ci_builds_metadata")
+ end
+ end
+
+ context 'when ci_partitioning_analyze_queries is enabled' do
+ context 'when analyzing targeted tables' do
+ described_class::ENABLED_TABLES.each do |enabled_table|
+ context 'when querying a non routing table' do
+ it 'tracks exception' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ process_sql(Ci::ApplicationRecord, "SELECT 1 FROM #{enabled_table}")
+ end
+
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::ApplicationRecord, "SELECT 1 FROM #{enabled_table}") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+ end
+
+ context 'when updating a record' do
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::BuildMetadata, "UPDATE ci_builds_metadata SET id = 1") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+
+ context 'when inserting a record' do
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::BuildMetadata, "INSERT INTO ci_builds_metadata (id) VALUES(1)") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+ end
+
+ context 'when analyzing non targeted table' do
+ it 'does not raise error' do
+ expect { process_sql(Ci::BuildMetadata, "SELECT 1 FROM projects") }
+ .not_to raise_error
+ end
+ end
+
+ context 'when querying a routing table' do
+ it 'does not raise error' do
+ expect { process_sql(Ci::BuildMetadata, "SELECT 1 FROM p_ci_builds_metadata") }
+ .not_to raise_error
+ end
+ end
+ end
+
+ private
+
+ def process_sql(model, sql)
+ Gitlab::Database::QueryAnalyzer.instance.within do
+ # Skip load balancer and retrieve connection assigned to model
+ Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index 495e953f993..4c98185e780 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -46,25 +46,11 @@ RSpec.describe Gitlab::Database::Reindexing do
end
end
- context 'when async index destruction is enabled' do
- it 'executes async index destruction prior to any reindexing actions' do
- stub_feature_flags(database_async_index_destruction: true)
+ it 'executes async index destruction prior to any reindexing actions' do
+ expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
+ expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
- expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
- expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
-
- described_class.invoke
- end
- end
-
- context 'when async index destruction is disabled' do
- it 'does not execute async index destruction' do
- stub_feature_flags(database_async_index_destruction: false)
-
- expect(Gitlab::Database::AsyncIndexes).not_to receive(:drop_pending_indexes!)
-
- described_class.invoke
- end
+ described_class.invoke
end
context 'calls automatic reindexing' do
diff --git a/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb b/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
new file mode 100644
index 00000000000..97abd6d23bd
--- /dev/null
+++ b/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
+ let(:connection) { ApplicationRecord.connection }
+ let(:tables) { %w[_test_gitlab_main_items _test_gitlab_main_references] }
+
+ subject do
+ described_class.new(connection, tables).execute
+ end
+
+ before do
+ statement = <<~SQL
+ CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_main_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ );
+ SQL
+ connection.execute(statement)
+ end
+
+ describe '#execute' do
+ it 'returns the tables sorted by the foreign keys dependency' do
+ expect(subject).to eq([['_test_gitlab_main_references'], ['_test_gitlab_main_items']])
+ end
+
+ it 'returns both tables together if they are strongly connected' do
+ statement = <<~SQL
+ ALTER TABLE _test_gitlab_main_items ADD COLUMN reference_id BIGINT
+ REFERENCES _test_gitlab_main_references(id)
+ SQL
+ connection.execute(statement)
+
+ expect(subject).to eq([tables])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb
new file mode 100644
index 00000000000..01af9efd782
--- /dev/null
+++ b/spec/lib/gitlab/database/tables_truncate_spec.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_base,
+ :suppress_gitlab_schemas_validate_connection do
+ include MigrationsHelpers
+
+ let(:logger) { instance_double(Logger) }
+ let(:dry_run) { false }
+ let(:until_table) { nil }
+ let(:min_batch_size) { 1 }
+ let(:main_connection) { ApplicationRecord.connection }
+ let(:ci_connection) { Ci::ApplicationRecord.connection }
+ let(:test_gitlab_main_table) { '_test_gitlab_main_table' }
+ let(:test_gitlab_ci_table) { '_test_gitlab_ci_table' }
+
+ # Main Database
+ let(:main_db_main_item_model) { table("_test_gitlab_main_items", database: "main") }
+ let(:main_db_main_reference_model) { table("_test_gitlab_main_references", database: "main") }
+ let(:main_db_ci_item_model) { table("_test_gitlab_ci_items", database: "main") }
+ let(:main_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "main") }
+ let(:main_db_shared_item_model) { table("_test_gitlab_shared_items", database: "main") }
+ # CI Database
+ let(:ci_db_main_item_model) { table("_test_gitlab_main_items", database: "ci") }
+ let(:ci_db_main_reference_model) { table("_test_gitlab_main_references", database: "ci") }
+ let(:ci_db_ci_item_model) { table("_test_gitlab_ci_items", database: "ci") }
+ let(:ci_db_ci_reference_model) { table("_test_gitlab_ci_references", database: "ci") }
+ let(:ci_db_shared_item_model) { table("_test_gitlab_shared_items", database: "ci") }
+
+ subject(:truncate_legacy_tables) do
+ described_class.new(
+ database_name: database_name,
+ min_batch_size: min_batch_size,
+ logger: logger,
+ dry_run: dry_run,
+ until_table: until_table
+ ).execute
+ end
+
+ shared_examples 'truncating legacy tables on a database' do
+ before do
+ skip_if_multiple_databases_not_setup
+
+ # Creating some test tables on the main database
+ main_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_main_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ );
+ SQL
+
+ main_connection.execute(main_tables_sql)
+ ci_connection.execute(main_tables_sql)
+
+ ci_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_ci_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_ci_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_ci_items(id)
+ );
+ SQL
+
+ main_connection.execute(ci_tables_sql)
+ ci_connection.execute(ci_tables_sql)
+
+ internal_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_shared_items (id serial NOT NULL PRIMARY KEY);
+ SQL
+
+ main_connection.execute(internal_tables_sql)
+ ci_connection.execute(internal_tables_sql)
+
+ # Filling the tables
+ 5.times do |i|
+ # Main Database
+ main_db_main_item_model.create!(id: i)
+ main_db_main_reference_model.create!(item_id: i)
+ main_db_ci_item_model.create!(id: i)
+ main_db_ci_reference_model.create!(item_id: i)
+ main_db_shared_item_model.create!(id: i)
+ # CI Database
+ ci_db_main_item_model.create!(id: i)
+ ci_db_main_reference_model.create!(item_id: i)
+ ci_db_ci_item_model.create!(id: i)
+ ci_db_ci_reference_model.create!(item_id: i)
+ ci_db_shared_item_model.create!(id: i)
+ end
+
+ allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
+ {
+ "_test_gitlab_main_items" => :gitlab_main,
+ "_test_gitlab_main_references" => :gitlab_main,
+ "_test_gitlab_ci_items" => :gitlab_ci,
+ "_test_gitlab_ci_references" => :gitlab_ci,
+ "_test_gitlab_shared_items" => :gitlab_shared,
+ "_test_gitlab_geo_items" => :gitlab_geo
+ }
+ )
+
+ allow(logger).to receive(:info).with(any_args)
+ end
+
+ context 'when the truncated tables are not locked for writes' do
+ it 'raises an error that the tables are not locked for writes' do
+ error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/
+ expect { truncate_legacy_tables }.to raise_error(error_message)
+ end
+ end
+
+ context 'when the truncated tables are locked for writes' do
+ before do
+ legacy_tables_models.map(&:table_name).each do |table|
+ Gitlab::Database::LockWritesManager.new(
+ table_name: table,
+ connection: connection,
+ database_name: database_name
+ ).lock_writes
+ end
+ end
+
+ it 'truncates the legacy tables' do
+ old_counts = legacy_tables_models.map(&:count)
+ expect do
+ truncate_legacy_tables
+ end.to change { legacy_tables_models.map(&:count) }.from(old_counts).to([0] * legacy_tables_models.length)
+ end
+
+ it 'does not affect the other tables' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { other_tables_models.map(&:count) }
+ end
+
+ it 'logs the sql statements to the logger' do
+ expect(logger).to receive(:info).with("SET LOCAL lock_timeout = 0")
+ expect(logger).to receive(:info).with("SET LOCAL statement_timeout = 0")
+ expect(logger).to receive(:info)
+ .with(/TRUNCATE TABLE #{legacy_tables_models.map(&:table_name).sort.join(', ')} RESTRICT/)
+ truncate_legacy_tables
+ end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'does not truncate the legacy tables if running in dry run mode' do
+ legacy_tables_models = [main_db_ci_reference_model, main_db_ci_reference_model]
+ expect do
+ truncate_legacy_tables
+ end.not_to change { legacy_tables_models.map(&:count) }
+ end
+ end
+
+ context 'when passing until_table parameter' do
+ context 'with a table that exists' do
+ let(:until_table) { referencing_table_model.table_name }
+
+ it 'only truncates until the table specified' do
+ expect do
+ truncate_legacy_tables
+ end.to change(referencing_table_model, :count).by(-5)
+ .and change(referenced_table_model, :count).by(0)
+ end
+ end
+
+ context 'with a table that does not exist' do
+ let(:until_table) { 'foobar' }
+
+ it 'raises an error if the specified table does not exist' do
+ expect do
+ truncate_legacy_tables
+ end.to raise_error(/The table 'foobar' is not within the truncated tables/)
+ end
+ end
+ end
+
+ context 'with geo configured' do
+ let(:geo_connection) { Gitlab::Database.database_base_models[:geo].connection }
+
+ before do
+ skip unless geo_configured?
+ geo_connection.execute('CREATE TABLE _test_gitlab_geo_items (id serial NOT NULL PRIMARY KEY)')
+ geo_connection.execute('INSERT INTO _test_gitlab_geo_items VALUES(generate_series(1, 50))')
+ end
+
+ it 'does not truncate gitlab_geo tables' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { geo_connection.select_value("select count(*) from _test_gitlab_geo_items") }
+ end
+ end
+ end
+ end
+
+ context 'when truncating gitlab_ci tables on the main database' do
+ let(:connection) { ApplicationRecord.connection }
+ let(:database_name) { "main" }
+ let(:legacy_tables_models) { [main_db_ci_item_model, main_db_ci_reference_model] }
+ let(:referencing_table_model) { main_db_ci_reference_model }
+ let(:referenced_table_model) { main_db_ci_item_model }
+ let(:other_tables_models) do
+ [
+ main_db_main_item_model, main_db_main_reference_model,
+ ci_db_ci_item_model, ci_db_ci_reference_model,
+ ci_db_main_item_model, ci_db_main_reference_model,
+ main_db_shared_item_model, ci_db_shared_item_model
+ ]
+ end
+
+ it_behaves_like 'truncating legacy tables on a database'
+ end
+
+ context 'when truncating gitlab_main tables on the ci database' do
+ let(:connection) { Ci::ApplicationRecord.connection }
+ let(:database_name) { "ci" }
+ let(:legacy_tables_models) { [ci_db_main_item_model, ci_db_main_reference_model] }
+ let(:referencing_table_model) { ci_db_main_reference_model }
+ let(:referenced_table_model) { ci_db_main_item_model }
+ let(:other_tables_models) do
+ [
+ main_db_main_item_model, main_db_main_reference_model,
+ ci_db_ci_item_model, ci_db_ci_reference_model,
+ main_db_ci_item_model, main_db_ci_reference_model,
+ main_db_shared_item_model, ci_db_shared_item_model
+ ]
+ end
+
+ it_behaves_like 'truncating legacy tables on a database'
+ end
+
+ context 'when running in a single database mode' do
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'raises an error when truncating the main database that it is a single database setup' do
+ expect do
+ described_class.new(database_name: 'main', min_batch_size: min_batch_size).execute
+ end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
+ end
+
+ it 'raises an error when truncating the ci database that it is a single database setup' do
+ expect do
+ described_class.new(database_name: 'ci', min_batch_size: min_batch_size).execute
+ end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
+ end
+ end
+
+ def geo_configured?
+ !!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo')
+ end
+end
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 6601b6658d5..ad91320c6eb 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
@@ -181,8 +181,8 @@ 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,
- self_monitoring_project_id: existing_project.id)
+ application_setting.update!(
+ instance_administrators_group_id: existing_group.id, self_monitoring_project_id: existing_project.id)
end
it 'returns success' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 452a662bdcb..c893bca9e62 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -237,6 +237,26 @@ RSpec.describe Gitlab::Database do
end
end
+ it 'does return a valid schema for a replica connection' do
+ with_replica_pool_for(ActiveRecord::Base) do |main_replica_pool|
+ expect(described_class.gitlab_schemas_for_connection(main_replica_pool.connection)).to include(:gitlab_main, :gitlab_shared)
+ end
+
+ with_replica_pool_for(Ci::ApplicationRecord) do |ci_replica_pool|
+ expect(described_class.gitlab_schemas_for_connection(ci_replica_pool.connection)).to include(:gitlab_ci, :gitlab_shared)
+ end
+ end
+
+ def with_replica_pool_for(base_model)
+ config = Gitlab::Database::LoadBalancing::Configuration.new(base_model, [base_model.connection_pool.db_config.host])
+ lb = Gitlab::Database::LoadBalancing::LoadBalancer.new(config)
+ pool = lb.create_replica_connection_pool(1)
+
+ yield pool
+ ensure
+ pool&.disconnect!
+ end
+
context "when there's CI connection", :request_store do
before do
skip_if_multiple_databases_not_setup
diff --git a/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
index 15f580a3a60..47d09e7a165 100644
--- a/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::DependencyLinker::Parser::Gemfile do
describe '#parse' do
diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb
index 2daa8df815d..8feab0f8017 100644
--- a/spec/lib/gitlab/dependency_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::DependencyLinker do
describe '.link' do
diff --git a/spec/lib/gitlab/diff/file_collection_sorter_spec.rb b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
index 9ba9271cefc..ca9c156c1ad 100644
--- a/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::FileCollectionSorter do
let(:diffs) do
diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb
index 1d1ffc8c275..53e74748234 100644
--- a/spec/lib/gitlab/diff/highlight_cache_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb
@@ -9,33 +9,33 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
{ ".gitignore-false-false-false" =>
[{ line_code: nil, rich_text: nil, text: "@@ -17,3 +17,4 @@ rerun.txt", type: "match", index: 0, old_pos: 17, new_pos: 17 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_17_17",
- rich_text: " <span id=\"LC17\" class=\"line\" lang=\"plaintext\">pickle-email-*.html</span>\n",
- text: " pickle-email-*.html",
- type: nil,
- index: 1,
- old_pos: 17,
- new_pos: 17 },
+ rich_text: " <span id=\"LC17\" class=\"line\" lang=\"plaintext\">pickle-email-*.html</span>\n",
+ text: " pickle-email-*.html",
+ type: nil,
+ index: 1,
+ old_pos: 17,
+ new_pos: 17 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_18_18",
- rich_text: " <span id=\"LC18\" class=\"line\" lang=\"plaintext\">.project</span>\n",
- text: " .project",
- type: nil,
- index: 2,
- old_pos: 18,
- new_pos: 18 },
+ rich_text: " <span id=\"LC18\" class=\"line\" lang=\"plaintext\">.project</span>\n",
+ text: " .project",
+ type: nil,
+ index: 2,
+ old_pos: 18,
+ new_pos: 18 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_19_19",
- rich_text: " <span id=\"LC19\" class=\"line\" lang=\"plaintext\">config/initializers/secret_token.rb</span>\n",
- text: " config/initializers/secret_token.rb",
- type: nil,
- index: 3,
- old_pos: 19,
- new_pos: 19 },
+ rich_text: " <span id=\"LC19\" class=\"line\" lang=\"plaintext\">config/initializers/secret_token.rb</span>\n",
+ text: " config/initializers/secret_token.rb",
+ type: nil,
+ index: 3,
+ old_pos: 19,
+ new_pos: 19 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_20_20",
- rich_text: "+<span id=\"LC20\" class=\"line\" lang=\"plaintext\">.DS_Store</span>",
- text: "+.DS_Store",
- type: "new",
- index: 4,
- old_pos: 20,
- new_pos: 20 }] }
+ rich_text: "+<span id=\"LC20\" class=\"line\" lang=\"plaintext\">.DS_Store</span>",
+ text: "+.DS_Store",
+ type: "new",
+ index: 4,
+ old_pos: 20,
+ new_pos: 20 }] }
end
let(:cache_key) { cache.key }
@@ -109,23 +109,59 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
end
shared_examples 'caches missing entries' do
- it 'filters the key/value list of entries to be caches for each invocation' do
- expect(cache).to receive(:write_to_redis_hash)
- .with(hash_including(*paths))
- .once
- .and_call_original
-
- Gitlab::Redis::Cache.with do |redis|
- expect(redis).to receive(:expire).with(cache.key, described_class::EXPIRATION)
+ where(:expiration_period, :renewable_expiration_ff, :short_renewable_expiration_ff) do
+ [
+ [1.day, false, true],
+ [1.day, false, false],
+ [1.hour, true, true],
+ [8.hours, true, false]
+ ]
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(
+ highlight_diffs_renewable_expiration: renewable_expiration_ff,
+ highlight_diffs_short_renewable_expiration: short_renewable_expiration_ff
+ )
end
- 2.times { cache.write_if_empty }
- end
+ it 'filters the key/value list of entries to be caches for each invocation' do
+ expect(cache).to receive(:write_to_redis_hash)
+ .with(hash_including(*paths))
+ .once
+ .and_call_original
- it 'reads from cache once' do
- expect(cache).to receive(:read_cache).once.and_call_original
+ 2.times { cache.write_if_empty }
+ end
- cache.write_if_empty
+ it 'reads from cache once' do
+ expect(cache).to receive(:read_cache).once.and_call_original
+
+ cache.write_if_empty
+ end
+
+ it 'refreshes TTL of the key on read' do
+ cache.write_if_empty
+
+ time_until_expire = 30.minutes
+
+ Gitlab::Redis::Cache.with do |redis|
+ # Emulate that a key is going to expire soon
+ redis.expire(cache.key, time_until_expire)
+
+ expect(redis.ttl(cache.key)).to be <= time_until_expire
+
+ cache.send(:read_cache)
+
+ if renewable_expiration_ff
+ expect(redis.ttl(cache.key)).to be > time_until_expire
+ expect(redis.ttl(cache.key)).to be_within(1.minute).of(expiration_period)
+ else
+ expect(redis.ttl(cache.key)).to be <= time_until_expire
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
index 3670074cc21..87d47e36f6a 100644
--- a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::InlineDiffMarkdownMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
index 6820a7df95e..8ab2a7b64dd 100644
--- a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::InlineDiffMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/diff/lines_unfolder_spec.rb b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
index f0e710be2e4..98f0c4df204 100644
--- a/spec/lib/gitlab/diff/lines_unfolder_spec.rb
+++ b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
@@ -189,14 +189,14 @@ RSpec.describe Gitlab::Diff::LinesUnfolder do
let(:diff) do
Gitlab::Git::Diff.new({ diff: raw_diff,
- new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
- old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
- a_mode: "100644",
- b_mode: "100644",
- new_file: false,
- renamed_file: false,
- deleted_file: false,
- too_large: false })
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ a_mode: "100644",
+ b_mode: "100644",
+ new_file: false,
+ renamed_file: false,
+ deleted_file: false,
+ too_large: false })
end
let(:diff_file) do
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index bb3522eb579..00a468bfef6 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -684,7 +684,7 @@ RSpec.describe Gitlab::Diff::Position do
"old_line" => 18,
"new_line" => 18
},
- "end" => {
+ "end" => {
"line_code" => end_line_code,
"type" => nil,
"old_line" => end_old_line,
diff --git a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
index b5137f9db6b..e1135f4d546 100644
--- a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
+++ b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
include RepoHelpers
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
+
let(:commit) { project.commit("5d6ed1503801ca9dc28e95eeb85a7cf863527aee") }
let(:diffs) { commit.raw_diffs.to_a }
let(:diff) { diffs.first }
diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb
new file mode 100644
index 00000000000..df17d92bb0c
--- /dev/null
+++ b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::DoorkeeperSecretStoring::Secret::Pbkdf2Sha512 do
+ describe '.transform_secret' do
+ let(:plaintext_secret) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' }
+
+ it 'generates a PBKDF2+SHA512 hashed value in the correct format' do
+ expect(described_class.transform_secret(plaintext_secret))
+ .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength
+ end
+
+ context 'when hash_oauth_secrets is disabled' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
+ it 'returns a plaintext secret' do
+ expect(described_class.transform_secret(plaintext_secret)).to eq(plaintext_secret)
+ end
+ end
+ end
+
+ describe 'STRETCHES' do
+ it 'is 20_000' do
+ expect(described_class::STRETCHES).to eq(20_000)
+ end
+ end
+
+ describe 'SALT' do
+ it 'is empty' do
+ expect(described_class::SALT).to be_empty
+ end
+ end
+
+ describe '.secret_matches?' do
+ it "match by hashing the input if the stored value is hashed" do
+ stub_feature_flags(hash_oauth_secrets: false)
+ plain_secret = 'plain_secret'
+ stored_value = '$pbkdf2-sha512$20000$$/BwQRdwSpL16xkQhstavh7nvA5avCP7.4n9LLKe9AupgJDeA7M5xOAvG3N3E5XbRyGWWBbbr.BsojPVWzd1Sqg' # rubocop:disable Layout/LineLength
+ expect(described_class.secret_matches?(plain_secret, stored_value)).to be true
+ end
+ end
+end
diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb
index e953733c997..c73744cd481 100644
--- a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb
+++ b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512 do
+RSpec.describe Gitlab::DoorkeeperSecretStoring::Token::Pbkdf2Sha512 do
describe '.transform_secret' do
let(:plaintext_token) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' }
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index b0c67cdafe1..690396d4dbc 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -98,6 +98,36 @@ RSpec.describe Gitlab::EncodingHelper do
end
end
+ describe '#encode_utf8_with_escaping!' do
+ where(:input, :expected) do
+ "abcd" | "abcd"
+ "DzDzDz" | "DzDzDz"
+ "\xC7\xB2\xC7DzDzDz" | "Dz%C7DzDzDz"
+ "🐤🐤🐤🐤\xF0\x9F\x90" | "🐤🐤🐤🐤%F0%9F%90"
+ "\xD0\x9F\xD1\x80 \x90" | "Пр %90"
+ "\x41" | "A"
+ end
+
+ with_them do
+ it 'escapes invalid UTF-8' do
+ expect(ext_class.encode_utf8_with_escaping!(input.dup.force_encoding(Encoding::ASCII_8BIT))).to eq(expected)
+ expect(ext_class.encode_utf8_with_escaping!(input)).to eq(expected)
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(escape_gitaly_refs: false)
+ end
+
+ it 'uses #encode! method' do
+ expect(ext_class).to receive(:encode!).with('String')
+
+ ext_class.encode_utf8_with_escaping!('String')
+ end
+ end
+ end
+
describe '#encode_utf8' do
[
["nil", nil, nil],
diff --git a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
index 210829056c8..c9b632b50e1 100644
--- a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
+++ b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
@@ -38,10 +38,10 @@ RSpec.describe Gitlab::ErrorTracking::Processor::ContextPayloadProcessor do
expect(result_hash[:tags])
.to include(priority: 'high',
- locale: 'en',
- program: 'test',
- feature_category: 'feature_a',
- correlation_id: 'cid')
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a',
+ correlation_id: 'cid')
expect(result_hash[:extra])
.to include(some_info: 'info',
diff --git a/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
index 577d59798da..3d23249d00d 100644
--- a/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
+++ b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do
'function' => 'print',
'lineNo' => 3,
'filename' => 'hello_world.php',
- 'context' => [
+ 'context' => [
[1, '<span id="LC1" class="line" lang="hack"><span class="c1">// PHP/Hack example</span></span>'],
[2, '<span id="LC1" class="line" lang="hack"><span class="cp">&lt;?php</span></span>'],
[3, '<span id="LC1" class="line" lang="hack"><span class="k">echo</span> <span class="s1">\'Hello, World!\'</span><span class="p">;</span></span>']
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 8228f95dd5e..da5eaf2e4ab 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -119,11 +119,11 @@ RSpec.describe Gitlab::EtagCaching::Middleware, :clean_gitlab_redis_shared_state
let(:expected_items) do
{
etag_route: endpoint,
- params: {},
- format: :html,
- method: 'GET',
- path: enabled_path,
- status: status_code
+ params: {},
+ format: :html,
+ method: 'GET',
+ path: enabled_path,
+ status: status_code
}
end
diff --git a/spec/lib/gitlab/etag_caching/router/graphql_spec.rb b/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
index 9a6787e3640..792f02f8cda 100644
--- a/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::EtagCaching::Router::Graphql do
def match_route(path, header)
described_class.match(
double(path_info: path,
- headers: { 'X-GITLAB-GRAPHQL-RESOURCE-ETAG' => header }))
+ headers: { 'X-GITLAB-GRAPHQL-RESOURCE-ETAG' => header }))
end
describe '.cache_key' do
diff --git a/spec/lib/gitlab/experimentation/group_types_spec.rb b/spec/lib/gitlab/experimentation/group_types_spec.rb
index 599ad08f706..2b118d76fa4 100644
--- a/spec/lib/gitlab/experimentation/group_types_spec.rb
+++ b/spec/lib/gitlab/experimentation/group_types_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Experimentation::GroupTypes do
it 'defines a GROUP_CONTROL constant' do
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index 8c0c56ea2c3..208acf28cc4 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::FileDetector do
describe '.types_in_paths' do
diff --git a/spec/lib/gitlab/file_markdown_link_builder_spec.rb b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
index d684beaaaca..54dfde9fc45 100644
--- a/spec/lib/gitlab/file_markdown_link_builder_spec.rb
+++ b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::FileMarkdownLinkBuilder do
let(:custom_class) do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 763e6f1b5f4..a16f96a7d11 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
end
let(:text) do
- "Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
+ "Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}".freeze # rubocop:disable Style/RedundantFreeze
end
def referenced_files(text, project)
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 0da7aa7dad0..d35d288050a 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -2,11 +2,9 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Blob, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:rugged) do
- Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
- end
+RSpec.describe Gitlab::Git::Blob do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
@@ -44,7 +42,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
shared_examples '.find' do
context 'nil path' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], nil) }
it { expect(blob).to eq(nil) }
end
@@ -56,30 +54,30 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'blank path' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], '') }
it { expect(blob).to eq(nil) }
end
context 'file in subdir' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb") }
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
end
context 'file in root' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, ".gitignore") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], ".gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
@@ -87,25 +85,25 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file in root with leading slash' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "/.gitignore") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "/.gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
end
context 'non-exist file' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "missing.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "missing.rb") }
it { expect(blob).to be_nil }
end
context 'six submodule' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'six') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'six') }
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
it { expect(blob.data).to eq('') }
@@ -121,7 +119,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'large file' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg') }
let(:blob_size) { 111803 }
let(:stub_limit) { 1000 }
@@ -159,10 +157,10 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.find with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
allow_next_instance_of(Rugged) do |instance|
- allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ allow(instance).to receive(:rev_parse).with(TestEnv::BRANCH_SHA['master']).and_call_original
end
- described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
+ described_class.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg')
end
it_behaves_like '.find'
@@ -177,40 +175,13 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(raw_blob.size).to eq(669) }
it { expect(raw_blob.truncated?).to be_falsey }
it { expect(bad_blob).to be_nil }
-
- context 'large file' do
- it 'limits the size of a large file' do
- blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
- buffer = Array.new(blob_size, 0)
- rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
- blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
-
- expect(blob.size).to eq(blob_size)
- expect(blob.loaded_size).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
- expect(blob.data.length).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
- expect(blob.truncated?).to be_truthy
-
- blob.load_all_data!(repository)
- expect(blob.loaded_size).to eq(blob_size)
- end
- end
-
- context 'when sha references a tree' do
- it 'returns nil' do
- tree = rugged.rev_parse('master^{tree}')
-
- blob = Gitlab::Git::Blob.raw(repository, tree.oid)
-
- expect(blob).to be_nil
- end
- end
end
describe '.batch' do
let(:blob_references) do
[
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
@@ -224,7 +195,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
@@ -273,21 +244,21 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
context 'when large number of blobs requested' do
let(:first_batch) do
[
- [SeedRepo::Commit::ID, 'files/ruby/popen.rb'],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], 'files/ruby/popen.rb'],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
let(:second_batch) do
[
- [SeedRepo::Commit::ID, 'some'],
- [SeedRepo::Commit::ID, 'other']
+ [TestEnv::BRANCH_SHA['master'], 'some'],
+ [TestEnv::BRANCH_SHA['master'], 'other']
]
end
let(:third_batch) do
[
- [SeedRepo::Commit::ID, 'files']
+ [TestEnv::BRANCH_SHA['master'], 'files']
]
end
@@ -315,8 +286,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.batch_metadata' do
let(:blob_references) do
[
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
@@ -333,8 +304,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
describe '.batch_lfs_pointers' do
- let(:tree_object) { rugged.rev_parse('master^{tree}') }
-
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
repository,
@@ -346,8 +315,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:lfs_blob) do
Gitlab::Git::Blob.find(
repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/image.jpg'
+ TestEnv::BRANCH_SHA['master'],
+ 'files/lfs/lfs_object.iso'
)
end
@@ -374,12 +343,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
expect(blobs_2).to eq([])
end
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
-
- expect(blobs).to eq([])
- end
-
it 'silently ignores non lfs objects' do
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
@@ -398,7 +361,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe 'encoding', :aggregate_failures do
context 'file with russian text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/russian.rb") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("russian.rb")
@@ -412,7 +375,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with Japanese text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/テスト.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("テスト.txt")
@@ -424,12 +387,12 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with ISO-8859 text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::LastCommit::ID, "encoding/iso8859.txt") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/iso8859.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("iso8859.txt")
- expect(blob.loaded_size).to eq(4)
- expect(blob.size).to eq(4)
+ expect(blob.loaded_size).to eq(3)
+ expect(blob.size).to eq(3)
expect(blob.mode).to eq("100644")
expect(blob.truncated?).to be_falsey
end
@@ -441,7 +404,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
+ TestEnv::BRANCH_SHA['master'],
'files/ruby/regex.rb'
)
end
@@ -456,14 +419,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
+ TestEnv::BRANCH_SHA['with-executables'],
'files/executables/ls'
)
end
it { expect(blob.name).to eq('ls') }
it { expect(blob.path).to eq('files/executables/ls') }
- it { expect(blob.size).to eq(110080) }
+ it { expect(blob.size).to eq(23) }
it { expect(blob.mode).to eq("100755") }
end
@@ -471,29 +434,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
- 'files/links/ruby-style-guide.md'
+ '88ce9520c07b7067f589b7f83a30b6250883115c',
+ 'symlink'
)
end
- it { expect(blob.name).to eq('ruby-style-guide.md') }
- it { expect(blob.path).to eq('files/links/ruby-style-guide.md') }
- it { expect(blob.size).to eq(31) }
- it { expect(blob.mode).to eq("120000") }
- end
-
- context 'file symlink to binary' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
- 'files/links/touch'
- )
- end
-
- it { expect(blob.name).to eq('touch') }
- it { expect(blob.path).to eq('files/links/touch') }
- it { expect(blob.size).to eq(20) }
+ it { expect(blob.name).to eq('symlink') }
+ it { expect(blob.path).to eq('symlink') }
+ it { expect(blob.size).to eq(6) }
it { expect(blob.mode).to eq("120000") }
end
end
@@ -503,79 +451,20 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/image.jpg'
+ TestEnv::BRANCH_SHA['png-lfs'],
+ 'files/images/emoji.png'
)
end
it { expect(blob.lfs_pointer?).to eq(true) }
- it { expect(blob.lfs_oid).to eq("4206f951d2691c78aac4c0ce9f2b23580b2c92cdcc4336e1028742c0274938e0") }
- it { expect(blob.lfs_size).to eq(19548) }
- it { expect(blob.id).to eq("f4d76af13003d1106be7ac8c5a2a3d37ddf32c2a") }
- it { expect(blob.name).to eq("image.jpg") }
- it { expect(blob.path).to eq("files/lfs/image.jpg") }
- it { expect(blob.size).to eq(130) }
+ it { expect(blob.lfs_oid).to eq("96f74c6fe7a2979eefb9ec74a5dfc6888fb25543cf99b77586b79afea1da6f97") }
+ it { expect(blob.lfs_size).to eq(1219696) }
+ it { expect(blob.id).to eq("ff0ab3afd1616ff78d0331865d922df103b64cf0") }
+ it { expect(blob.name).to eq("emoji.png") }
+ it { expect(blob.path).to eq("files/images/emoji.png") }
+ it { expect(blob.size).to eq(132) }
it { expect(blob.mode).to eq("100644") }
end
-
- describe 'file an invalid lfs pointer' do
- context 'with correct version header but incorrect size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/archive-invalid.tar'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(nil) }
- it { expect(blob.id).to eq("f8a898db217a5a85ed8b3d25b34c1df1d1094c46") }
- it { expect(blob.name).to eq("archive-invalid.tar") }
- it { expect(blob.path).to eq("files/lfs/archive-invalid.tar") }
- it { expect(blob.size).to eq(43) }
- it { expect(blob.mode).to eq("100644") }
- end
-
- context 'with correct version header and size but incorrect size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/picture-invalid.png'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(1575078) }
- it { expect(blob.id).to eq("5ae35296e1f95c1ef9feda1241477ed29a448572") }
- it { expect(blob.name).to eq("picture-invalid.png") }
- it { expect(blob.path).to eq("files/lfs/picture-invalid.png") }
- it { expect(blob.size).to eq(57) }
- it { expect(blob.mode).to eq("100644") }
- end
-
- context 'with correct version header and size but invalid size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/file-invalid.zip'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(nil) }
- it { expect(blob.id).to eq("d831981bd876732b85a1bcc6cc01210c9f36248f") }
- it { expect(blob.name).to eq("file-invalid.zip") }
- it { expect(blob.path).to eq("files/lfs/file-invalid.zip") }
- it { expect(blob.size).to eq(60) }
- it { expect(blob.mode).to eq("100644") }
- end
- end
end
describe '#load_all_data!' do
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index feaa1f6595c..95cc833390f 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe Gitlab::Git::Branch do
end
def create_commit
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'HEAD',
message: 'commit message',
diff --git a/spec/lib/gitlab/git/changed_path_spec.rb b/spec/lib/gitlab/git/changed_path_spec.rb
index 93db107ad5c..ef51021ba4c 100644
--- a/spec/lib/gitlab/git/changed_path_spec.rb
+++ b/spec/lib/gitlab/git/changed_path_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::ChangedPath do
subject(:changed_path) { described_class.new(path: path, status: status) }
diff --git a/spec/lib/gitlab/git/changes_spec.rb b/spec/lib/gitlab/git/changes_spec.rb
index 310be7a3731..7cded9740ee 100644
--- a/spec/lib/gitlab/git/changes_spec.rb
+++ b/spec/lib/gitlab/git/changes_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Changes do
let(:changes) { described_class.new }
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 95b49186d0f..d873151421d 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Commit, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Commit do
+ let(:repository) { create(:project, :repository).repository.raw }
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
describe "Commit info from gitaly commit" do
@@ -121,14 +121,6 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
it "returns nil for id containing NULL" do
expect(described_class.find(repository, "HE\x00AD")).to be_nil
end
-
- context 'with broken repo' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_BROKEN_REPO_PATH, '', 'group/project') }
-
- it 'returns nil' do
- expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_nil
- end
- end
end
describe '.find with Gitaly enabled' do
@@ -154,7 +146,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
describe '#id' do
subject { super().id }
- it { is_expected.to eq(SeedRepo::LastCommit::ID) }
+ it { is_expected.to eq(TestEnv::BRANCH_SHA['master']) }
end
end
@@ -223,7 +215,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
expect(subject.size).to eq(10)
end
- it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
+ it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
end
context 'path is nil' do
@@ -242,28 +234,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
expect(subject.size).to eq(10)
end
- it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
- end
-
- context 'ref is branch name' do
- subject do
- commits = described_class.where(
- repo: repository,
- ref: 'master',
- path: 'files',
- limit: 3,
- offset: 1
- )
-
- commits.map { |c| c.id }
- end
-
- it 'has 3 elements' do
- expect(subject.size).to eq(3)
- end
-
- it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
- it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
+ it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
end
context 'ref is commit id' do
@@ -323,13 +294,12 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
context 'requesting a commit range' do
let(:from) { 'v1.0.0' }
- let(:to) { 'v1.2.0' }
+ let(:to) { 'v1.1.0' }
let(:commits_in_range) do
%w[
570e7b2abdd848b95f2f578043fc23bd6f6fd24d
5937ac0a7beb003549fc5fd26fc247adbce4a52e
- eb49186cfa5c4338011f5f590fac11bd66c5c631
]
end
@@ -338,9 +308,9 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
end
context 'limited' do
- let(:limit) { 2 }
+ let(:limit) { 1 }
- it { expect(commit_ids).to eq(commits_in_range.last(2)) }
+ it { expect(commit_ids).to eq(commits_in_range.last(1)) }
end
end
end
@@ -383,16 +353,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
commits.map(&:id)
end
- it 'has 34 elements' do
- expect(subject.size).to eq(34)
- end
-
- it 'includes the expected commits' do
- expect(subject).to include(
- SeedRepo::Commit::ID,
- SeedRepo::Commit::PARENT_ID,
- SeedRepo::FirstCommit::ID
- )
+ it 'has maximum elements' do
+ expect(subject.size).to eq(50)
end
end
@@ -408,13 +370,13 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
commits.map(&:id)
end
- it 'has 24 elements' do
- expect(subject.size).to eq(24)
+ it 'has 36 elements' do
+ expect(subject.size).to eq(36)
end
it 'includes the expected commits' do
expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID)
- expect(subject).not_to include(SeedRepo::LastCommit::ID)
+ expect(subject).not_to include(TestEnv::BRANCH_SHA['master'])
end
end
end
@@ -650,8 +612,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
subject { commit.ref_names(repository) }
- it 'has 2 element' do
- expect(subject.size).to eq(2)
+ it 'has 3 elements' do
+ expect(subject.size).to eq(3)
end
it { is_expected.to include("master") }
@@ -681,6 +643,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
end
it 'gets messages in one batch', :request_store do
+ repository # preload repository so that the project factory does not pollute request counts
+
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end
diff --git a/spec/lib/gitlab/git/commit_stats_spec.rb b/spec/lib/gitlab/git/commit_stats_spec.rb
index 29d3909efec..81d9dda4b8f 100644
--- a/spec/lib/gitlab/git/commit_stats_spec.rb
+++ b/spec/lib/gitlab/git/commit_stats_spec.rb
@@ -2,17 +2,19 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::CommitStats, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:commit) { Gitlab::Git::Commit.find(repository, SeedRepo::Commit::ID) }
+RSpec.describe Gitlab::Git::CommitStats do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
+
+ let(:commit) { Gitlab::Git::Commit.find(repository, TestEnv::BRANCH_SHA['feature']) }
def verify_stats!
stats = described_class.new(repository, commit)
expect(stats).to have_attributes(
- additions: eq(11),
- deletions: eq(6),
- total: eq(17)
+ additions: eq(5),
+ deletions: eq(0),
+ total: eq(5)
)
end
@@ -21,7 +23,7 @@ RSpec.describe Gitlab::Git::CommitStats, :seed_helper do
verify_stats!
- expect(Rails.cache.fetch("commit_stats:group/project:#{commit.id}")).to eq([11, 6])
+ expect(Rails.cache.fetch("commit_stats:#{repository.gl_project_path}:#{commit.id}")).to eq([5, 0])
expect(repository.gitaly_commit_client).not_to receive(:commit_stats)
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index 51043355ede..e8c683cf8aa 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -2,8 +2,9 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Compare, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Compare do
+ let_it_be(:repository) { create(:project, :repository).repository.raw }
+
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) }
diff --git a/spec/lib/gitlab/git/conflict/file_spec.rb b/spec/lib/gitlab/git/conflict/file_spec.rb
index 6eb7a7e394e..fb1bec0a554 100644
--- a/spec/lib/gitlab/git/conflict/file_spec.rb
+++ b/spec/lib/gitlab/git/conflict/file_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Conflict::File do
let(:conflict) { { ancestor: { path: 'ancestor' }, theirs: { path: 'foo', mode: 33188 }, ours: { path: 'foo', mode: 33188 } } }
diff --git a/spec/lib/gitlab/git/conflict/parser_spec.rb b/spec/lib/gitlab/git/conflict/parser_spec.rb
index 7d81af92412..67f288e0299 100644
--- a/spec/lib/gitlab/git/conflict/parser_spec.rb
+++ b/spec/lib/gitlab/git/conflict/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Conflict::Parser do
describe '.parse' do
diff --git a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb b/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
index 1c49486b7b1..7888e224d59 100644
--- a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
+++ b/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Gitlab::Git::CrossRepoComparer do
def create_commit(user, repo, branch)
action = { action: :create, file_path: '/FILE', content: 'content' }
- result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
+ result = repo.commit_files(user, branch_name: branch, message: 'Commit', actions: [action])
result.newrev
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 0e3e92e03cf..7fa5bd8a92b 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
+RSpec.describe Gitlab::Git::DiffCollection do
before do
stub_const('MutatingConstantIterator', Class.new)
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 2c931a999f1..6745c700b92 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -2,8 +2,10 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Diff, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Diff do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository }
+
let(:gitaly_diff) do
Gitlab::GitalyClient::Diff.new(
from_path: '.gitmodules',
@@ -190,16 +192,6 @@ EOT
expect(binary_diff(project).diff).not_to be_empty
end
end
-
- context 'when convert_diff_to_utf8_with_replacement_symbol feature flag is disabled' do
- before do
- stub_feature_flags(convert_diff_to_utf8_with_replacement_symbol: false)
- end
-
- it 'will not try to convert invalid characters' do
- expect(Gitlab::EncodingHelper).not_to receive(:encode_utf8_with_replacement_character?)
- end
- end
end
context 'when replace_invalid_utf8_chars is false' do
@@ -218,7 +210,7 @@ EOT
let(:diffs) { described_class.between(repository, 'feature', 'master', options) }
it 'has the correct size' do
- expect(diffs.size).to eq(24)
+ expect(diffs.size).to eq(21)
end
context 'diff' do
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
index 0e386c7f3d1..33268b4edcb 100644
--- a/spec/lib/gitlab/git/gitmodules_parser_spec.rb
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::GitmodulesParser do
it 'parses a .gitmodules file correctly' do
diff --git a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
index f45c7cccca0..b210c86c3d1 100644
--- a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
+++ b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::LfsPointerFile do
let(:data) { "1234\n" }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index a1fb8b70bd7..9a87911b6e8 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Repository, :seed_helper do
+RSpec.describe Gitlab::Git::Repository do
include Gitlab::EncodingHelper
include RepoHelpers
using RSpec::Parameterized::TableSyntax
@@ -21,13 +21,11 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
- let(:mutable_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '', 'group/project') }
- let(:mutable_repository_path) { File.join(TestEnv.repos_path, mutable_repository.relative_path) }
- let(:mutable_repository_rugged) { Rugged::Repository.new(mutable_repository_path) }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) }
- let(:repository_rugged) { Rugged::Repository.new(repository_path) }
- let(:storage_path) { TestEnv.repos_path }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
+
+ let(:mutable_project) { create(:project, :repository) }
+ let(:mutable_repository) { mutable_project.repository.raw }
let(:user) { build(:user) }
describe "Respond to" do
@@ -61,8 +59,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#branch_names' do
subject { repository.branch_names }
- it 'has SeedRepo::Repo::BRANCHES.size elements' do
- expect(subject.size).to eq(SeedRepo::Repo::BRANCHES.size)
+ it 'has TestRepo::BRANCH_SHA.size elements' do
+ expect(subject.size).to eq(TestEnv::BRANCH_SHA.size)
end
it 'returns UTF-8' do
@@ -85,8 +83,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it { is_expected.to be_kind_of Array }
- it 'has SeedRepo::Repo::TAGS.size elements' do
- expect(subject.size).to eq(SeedRepo::Repo::TAGS.size)
+ it 'has some elements' do
+ expect(subject.size).to be >= 1
end
it 'returns UTF-8' do
@@ -96,63 +94,24 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#last' do
subject { super().last }
- it { is_expected.to eq("v1.2.1") }
+ it { is_expected.to eq("v1.1.1") }
end
+
it { is_expected.to include("v1.0.0") }
it { is_expected.not_to include("v5.0.0") }
- it 'gets the tag names from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:tag_names)
- subject
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end
describe '#tags' do
subject { repository.tags }
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags)
- end
-
- subject
- end
-
- context 'with sorting option' do
- subject { repository.tags(sort_by: 'name_asc') }
-
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags).with(sort_by: 'name_asc', pagination_params: nil)
- end
-
- subject
- end
- end
-
- context 'with pagination option' do
- subject { repository.tags(pagination_params: { limit: 5, page_token: 'refs/tags/v1.0.0' }) }
-
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags).with(
- sort_by: nil,
- pagination_params: { limit: 5, page_token: 'refs/tags/v1.0.0' }
- )
- end
-
- subject
- end
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tags
end
describe '#archive_metadata' do
let(:storage_path) { '/tmp' }
- let(:cache_key) { File.join(repository.gl_repository, SeedRepo::LastCommit::ID) }
+ let(:cache_key) { File.join(repository.gl_repository, TestEnv::BRANCH_SHA['master']) }
let(:append_sha) { true }
let(:ref) { 'master' }
@@ -162,12 +121,12 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_extension) { 'tar.gz' }
let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" }
let(:expected_path) { File.join(storage_path, cache_key, "@v2", expected_filename) }
- let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" }
+ let(:expected_prefix) { "gitlab-git-test-#{ref}-#{TestEnv::BRANCH_SHA['master']}" }
subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha, path: path) }
it 'sets CommitId to the commit SHA' do
- expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID)
+ expect(metadata['CommitId']).to start_with(TestEnv::BRANCH_SHA['master'])
end
it 'sets ArchivePrefix to the expected prefix' do
@@ -175,7 +134,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
it 'sets ArchivePath to the expected globally-unique path' do
- expect(expected_path).to include(File.join(repository.gl_repository, SeedRepo::LastCommit::ID))
+ expect(expected_path).to include(File.join(repository.gl_repository, TestEnv::BRANCH_SHA['master']))
expect(metadata['ArchivePath']).to eq(expected_path)
end
@@ -190,7 +149,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'append_sha varies archive path and filename' do
where(:append_sha, :ref, :expected_prefix) do
- sha = SeedRepo::LastCommit::ID
+ sha = TestEnv::BRANCH_SHA['master']
true | 'master' | "gitlab-git-test-master-#{sha}"
true | sha | "gitlab-git-test-#{sha}-#{sha}"
@@ -224,13 +183,13 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#size' do
subject { repository.size }
- it { is_expected.to be < 2 }
+ it { is_expected.to be > 0 }
end
describe '#to_s' do
subject { repository.to_s }
- it { is_expected.to eq("<Gitlab::Git::Repository: group/project>") }
+ it { is_expected.to eq("<Gitlab::Git::Repository: #{project.full_path}>") }
end
describe '#object_directory_size' do
@@ -259,26 +218,25 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#first' do
subject { super().first }
- it { is_expected.to eq('feature') }
+ it { is_expected.to eq(TestEnv::BRANCH_SHA.keys.min) }
end
describe '#last' do
subject { super().last }
- it { is_expected.to eq('v1.2.1') }
+ it { is_expected.to eq('v1.1.1') }
end
end
describe '#submodule_url_for' do
- let(:ref) { 'master' }
+ let(:ref) { 'submodule_inside_folder' }
def submodule_url(path)
repository.submodule_url_for(ref, path)
end
it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') }
- it { expect(submodule_url('nested/six')).to eq('git://github.com/randx/six.git') }
- it { expect(submodule_url('deeper/nested/six')).to eq('git://github.com/randx/six.git') }
+ it { expect(submodule_url('test_inside_folder/another_folder/six')).to eq('git://github.com/randx/six.git') }
it { expect(submodule_url('invalid/path')).to eq(nil) }
context 'uncommitted submodule dir' do
@@ -288,7 +246,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'tags' do
- let(:ref) { 'v1.2.1' }
+ let(:ref) { 'v1.1.1' }
it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') }
end
@@ -313,17 +271,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
urls = repository.submodule_urls_for(ref)
expect(urls).to eq({
- "deeper/nested/six" => "git://github.com/randx/six.git",
- "gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
- "gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
- "nested/six" => "git://github.com/randx/six.git",
+ "gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
+ "gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
"six" => "git://github.com/randx/six.git"
})
end
end
describe '#commit_count' do
- it { expect(repository.commit_count("master")).to eq(25) }
+ it { expect(repository.commit_count("master")).to eq(37) }
it { expect(repository.commit_count("feature")).to eq(9) }
it { expect(repository.commit_count("does-not-exist")).to eq(0) }
@@ -353,7 +309,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('right-branch')
left.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'left-branch',
message: 'some more content for a',
@@ -366,7 +322,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
right.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'right-branch',
message: 'some more content for b',
@@ -411,7 +367,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('right-branch')
left.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'left-branch',
message: 'some more content for a',
@@ -424,7 +380,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
right.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'right-branch',
message: 'some more content for b',
@@ -461,47 +417,32 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#has_local_branches?' do
context 'check for local branches' do
it { expect(repository.has_local_branches?).to eq(true) }
+ end
+ end
- context 'mutable' do
- let(:repository) { mutable_repository }
+ describe '#delete_branch' do
+ let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
+ it 'deletes a branch' do
+ expect(repository.find_branch('feature')).not_to be_nil
- it 'returns false when there are no branches' do
- # Sanity check
- expect(repository.has_local_branches?).to eq(true)
+ repository.delete_branch('feature')
- FileUtils.rm_rf(File.join(repository_path, 'packed-refs'))
- heads_dir = File.join(repository_path, 'refs/heads')
- FileUtils.rm_rf(heads_dir)
- FileUtils.mkdir_p(heads_dir)
+ expect(repository.find_branch('feature')).to be_nil
+ end
- repository.expire_has_local_branches_cache
- expect(repository.has_local_branches?).to eq(false)
- end
- end
+ it 'deletes a fully qualified branch' do
+ expect(repository.find_branch('feature')).not_to be_nil
- context 'memoizes the value' do
- it 'returns true' do
- expect(repository).to receive(:uncached_has_local_branches?).once.and_call_original
+ repository.delete_branch('refs/heads/feature')
- 2.times do
- expect(repository.has_local_branches?).to eq(true)
- end
- end
- end
+ expect(repository.find_branch('feature')).to be_nil
end
end
describe '#delete_refs' do
let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
it 'deletes the ref' do
repository.delete_refs('refs/heads/feature')
@@ -548,9 +489,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
subject { repository.refs_hash }
it "has as many entries as branches and tags" do
- expected_refs = SeedRepo::Repo::BRANCHES + SeedRepo::Repo::TAGS
# We flatten in case a commit is pointed at by more than one branch and/or tag
- expect(subject.values.flatten.size).to eq(expected_refs.size)
+ expect(subject.values.flatten.size).to be > 0
end
it 'has valid commit ids as keys' do
@@ -598,7 +538,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
before do
repository.create_branch(ref)
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: ref,
message: 'committing something',
@@ -608,7 +548,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: content
}]
)
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: ref,
message: 'committing something',
@@ -620,10 +560,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
)
end
- after do
- ensure_seeds
- end
-
subject do
repository.search_files_by_content(content, ref)
end
@@ -647,8 +583,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:filter) { 'files\/.*\/.*\.rb' }
it 'returns matched files' do
- expect(result).to contain_exactly('files/links/regex.rb',
- 'files/ruby/popen.rb',
+ expect(result).to contain_exactly('files/ruby/popen.rb',
'files/ruby/regex.rb',
'files/ruby/version_info.rb')
end
@@ -673,6 +608,61 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#search_files_by_name' do
+ let(:ref) { 'master' }
+
+ subject(:result) { mutable_repository.search_files_by_name(query, ref) }
+
+ context 'when sending a valid name' do
+ let(:query) { 'files/ruby/popen.rb' }
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('files/ruby/popen.rb')
+ end
+ end
+
+ context 'when sending a name with space' do
+ let(:query) { 'file with space.md' }
+
+ before do
+ mutable_repository.commit_files(
+ user,
+ actions: [{ action: :create, file_path: "file with space.md", content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('file with space.md')
+ end
+ end
+
+ context 'when sending a name with special ASCII characters' do
+ let(:file_name) { 'Hello !@#$%^&*()' }
+ let(:query) { file_name }
+
+ before do
+ mutable_repository.commit_files(
+ user,
+ actions: [{ action: :create, file_path: file_name, content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly(file_name)
+ end
+ end
+
+ context 'when sending a non-existing name' do
+ let(:query) { 'please do not exist.md' }
+
+ it 'raises error' do
+ expect(result).to eql([])
+ end
+ end
+ end
+
describe '#find_remote_root_ref' do
it 'gets the remote root ref from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
@@ -720,7 +710,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
before do
# Add new commits so that there's a renamed file in the commit history
- @commit_with_old_name_id = repository.multi_action(
+ @commit_with_old_name_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Update CHANGELOG',
@@ -730,7 +720,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: 'CHANGELOG'
}]
).newrev
- @rename_commit_id = repository.multi_action(
+ @rename_commit_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Move CHANGELOG to encoding/',
@@ -741,7 +731,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: 'CHANGELOG'
}]
).newrev
- @commit_with_new_name_id = repository.multi_action(
+ @commit_with_new_name_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Edit encoding/CHANGELOG',
@@ -755,7 +745,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
after do
# Erase our commits so other tests get the original repo
- repository.write_ref(repository.root_ref, SeedRepo::LastCommit::ID)
+ repository.write_ref(repository.root_ref, TestEnv::BRANCH_SHA['master'])
end
context "where 'follow' == true" do
@@ -908,16 +898,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
expect(log_commits).not_to include(commit_with_new_name)
end
end
-
- context "and 'path' includes a directory that used to be a file" do
- let(:log_commits) do
- repository.log(options.merge(ref: "refs/heads/fix-blob-path", path: "files/testdir/file.txt"))
- end
-
- it "returns a list of commits" do
- expect(log_commits.size).to eq(1)
- end
- end
end
context "where provides 'after' timestamp" do
@@ -981,7 +961,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns a list of commits' do
commits = repository.log({ all: true, limit: 50 })
- expect(commits.size).to eq(37)
+ expect(commits.size).to eq(50)
end
end
end
@@ -992,7 +972,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#blobs' do
- let_it_be(:commit_oid) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let_it_be(:commit_oid) { TestEnv::BRANCH_SHA['master'] }
shared_examples 'a blob enumeration' do
it 'enumerates blobs' do
@@ -1008,7 +988,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'single revision' do
let(:revisions) { [commit_oid] }
- let(:expected_blobs) { 53 }
+ let(:expected_blobs) { 52 }
it_behaves_like 'a blob enumeration'
end
@@ -1038,48 +1018,31 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it_behaves_like 'a blob enumeration'
end
-
- context 'partially blank revisions' do
- let(:revisions) { [::Gitlab::Git::BLANK_SHA, commit_oid] }
- let(:expected_blobs) { 53 }
-
- before do
- expect_next_instance_of(Gitlab::GitalyClient::BlobService) do |service|
- expect(service)
- .to receive(:list_blobs)
- .with([commit_oid], kind_of(Hash))
- .and_call_original
- end
- end
-
- it_behaves_like 'a blob enumeration'
- end
end
describe '#new_blobs' do
let(:repository) { mutable_repository }
- let(:repository_rugged) { mutable_repository_rugged }
- let(:blob) { create_blob('This is a new blob') }
- let(:commit) { create_commit('nested/new-blob.txt' => blob) }
-
- def create_blob(content)
- repository_rugged.write(content, :blob)
- end
+ let(:commit) { create_commit('nested/new-blob.txt' => 'This is a new blob') }
def create_commit(blobs)
- author = { name: 'Test User', email: 'mail@example.com', time: Time.now }
+ commit_result = repository.commit_files(
+ user,
+ branch_name: 'a-new-branch',
+ message: 'Add a file',
+ actions: blobs.map do |path, content|
+ {
+ action: :create,
+ file_path: path,
+ content: content
+ }
+ end
+ )
- index = repository_rugged.index
- blobs.each do |path, oid|
- index.add(path: path, oid: oid, mode: 0100644)
- end
+ # new_blobs only returns unreferenced blobs because it is used for hooks.
+ # Gitaly does not allow us to create loose objects via the RPC.
+ repository.delete_branch('a-new-branch')
- Rugged::Commit.create(repository_rugged,
- author: author,
- committer: author,
- message: "Message",
- parents: [],
- tree: index.write_tree(repository_rugged))
+ commit_result.newrev
end
subject { repository.new_blobs(newrevs).to_a }
@@ -1112,7 +1075,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:newrevs) { commit }
let(:expected_newrevs) { ['--not', '--all', '--not', newrevs] }
let(:expected_blobs) do
- [have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)]
+ [have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)]
end
it_behaves_like '#new_blobs with revisions'
@@ -1122,20 +1085,19 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:newrevs) { [commit] }
let(:expected_newrevs) { ['--not', '--all', '--not'] + newrevs }
let(:expected_blobs) do
- [have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)]
+ [have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)]
end
it_behaves_like '#new_blobs with revisions'
end
context 'with multiple revisions' do
- let(:another_blob) { create_blob('Another blob') }
- let(:newrevs) { [commit, create_commit('another_path.txt' => another_blob)] }
+ let(:newrevs) { [commit, create_commit('another_path.txt' => 'Another blob')] }
let(:expected_newrevs) { ['--not', '--all', '--not'] + newrevs.sort }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18),
- have_attributes(class: Gitlab::Git::Blob, id: another_blob, path: 'another_path.txt', size: 12)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18),
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'another_path.txt', size: 12)
]
end
@@ -1147,7 +1109,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_newrevs) { ['--not', '--all', '--not', commit] }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)
]
end
@@ -1159,7 +1121,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_newrevs) { ['--not', '--all', '--not', commit] }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)
]
end
@@ -1212,14 +1174,22 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#new_commits' do
let(:repository) { mutable_repository }
let(:new_commit) do
- author = { name: 'Test User', email: 'mail@example.com', time: Time.now }
+ commit_result = repository.commit_files(
+ user,
+ branch_name: 'a-new-branch',
+ message: 'Message',
+ actions: [{
+ action: :create,
+ file_path: 'some_file.txt',
+ content: 'This is a file'
+ }]
+ )
+
+ # new_commits only returns unreferenced commits because it is used for
+ # hooks. Gitaly does not allow us to create loose objects via the RPC.
+ repository.delete_branch('a-new-branch')
- Rugged::Commit.create(repository_rugged,
- author: author,
- committer: author,
- message: "Message",
- parents: [],
- tree: "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
+ commit_result.newrev
end
let(:expected_commits) { 1 }
@@ -1248,7 +1218,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#count_commits_between' do
subject { repository.count_commits_between('feature', 'master') }
- it { is_expected.to eq(17) }
+ it { is_expected.to eq(29) }
end
describe '#raw_changes_between' do
@@ -1275,26 +1245,26 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
- context 'with valid revs' do
- let(:old_rev) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' }
- let(:new_rev) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ context 'with valid revs', :aggregate_failures do
+ let(:old_rev) { TestEnv::BRANCH_SHA['feature'] }
+ let(:new_rev) { TestEnv::BRANCH_SHA['master'] }
it 'returns the changes' do
- expect(changes.size).to eq(9)
- expect(changes.first.operation).to eq(:modified)
- expect(changes.first.new_path).to eq('.gitmodules')
+ expect(changes.size).to eq(21)
+ expect(changes.first.operation).to eq(:deleted)
+ expect(changes.first.old_path).to eq('.DS_Store')
expect(changes.last.operation).to eq(:added)
- expect(changes.last.new_path).to eq('files/lfs/picture-invalid.png')
+ expect(changes.last.new_path).to eq('with space/README.md')
end
end
end
describe '#merge_base' do
where(:from, :to, :result) do
- '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | 'foobar' | nil
- 'foobar' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | nil
+ 'master' | 'feature' | 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f'
+ 'feature' | 'master' | 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f'
+ 'master' | 'foobar' | nil
+ 'foobar' | 'master' | nil
end
with_them do
@@ -1308,7 +1278,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns the number of commits after timestamp' do
options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') }
- expect(repository.count_commits(options)).to eq(25)
+ expect(repository.count_commits(options)).to eq(37)
end
end
@@ -1337,28 +1307,28 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with option :from and option :to' do
- it 'returns the number of commits ahead for fix-mode..fix-blob-path' do
- options = { from: 'fix-mode', to: 'fix-blob-path' }
+ it 'returns the number of commits ahead for master..feature' do
+ options = { from: 'master', to: 'feature' }
- expect(repository.count_commits(options)).to eq(2)
+ expect(repository.count_commits(options)).to eq(1)
end
- it 'returns the number of commits ahead for fix-blob-path..fix-mode' do
- options = { from: 'fix-blob-path', to: 'fix-mode' }
+ it 'returns the number of commits ahead for feature..master' do
+ options = { from: 'feature', to: 'master' }
- expect(repository.count_commits(options)).to eq(1)
+ expect(repository.count_commits(options)).to eq(29)
end
context 'with option :left_right' do
- it 'returns the number of commits for fix-mode...fix-blob-path' do
- options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true }
+ it 'returns the number of commits for master..feature' do
+ options = { from: 'master', to: 'feature', left_right: true }
- expect(repository.count_commits(options)).to eq([1, 2])
+ expect(repository.count_commits(options)).to eq([29, 1])
end
context 'with max_count' do
- it 'returns the number of commits with path' do
- options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 }
+ it 'returns the number of commits' do
+ options = { from: 'feature', to: 'master', left_right: true, max_count: 1 }
expect(repository.count_commits(options)).to eq([1, 1])
end
@@ -1378,7 +1348,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it "returns the number of commits in the whole repository" do
options = { all: true }
- expect(repository.count_commits(options)).to eq(34)
+ expect(repository.count_commits(options)).to eq(314)
end
end
@@ -1416,10 +1386,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the local and remote branches' do
expect(subject.any? { |b| b.name == 'joe/remote_branch' }).to eq(true)
expect(subject.any? { |b| b.name == 'local_branch' }).to eq(true)
@@ -1431,7 +1397,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#branch_count' do
it 'returns the number of branches' do
- expect(repository.branch_count).to eq(11)
+ expect(repository.branch_count).to eq(TestEnv::BRANCH_SHA.size)
end
context 'with local and remote branches' do
@@ -1442,10 +1408,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the count of local branches' do
expect(repository.branch_count).to eq(repository.local_branches.count)
end
@@ -1488,21 +1450,16 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'when no branch names are specified' do
+ let(:repository) { mutable_repository }
+
before do
repository.create_branch('identical')
end
- after do
- ensure_seeds
- end
-
it 'returns all merged branch names except for identical one' do
names = repository.merged_branch_names
- expect(names).to include('merge-test')
- expect(names).to include('fix-mode')
- expect(names).not_to include('feature')
- expect(names).not_to include('identical')
+ expect(names).to match_array(["'test'", "branch-merged", "flatten-dir", "improve/awesome", "merge-test"])
end
end
end
@@ -1556,24 +1513,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#find_changed_paths' do
- let(:commit_1) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' }
- let(:commit_2) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:commit_1) { TestEnv::BRANCH_SHA['with-executables'] }
+ let(:commit_2) { TestEnv::BRANCH_SHA['master'] }
let(:commit_3) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' }
let(:commit_1_files) do
- [
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/ls"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/touch"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/regex.rb"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/ruby-style-guide.md"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/touch"),
- Gitlab::Git::ChangedPath.new(status: :MODIFIED, path: ".gitmodules"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "deeper/nested/six"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "nested/six")
- ]
+ [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/ls")]
end
let(:commit_2_files) do
- [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "bin/executable")]
+ [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "bar/branch-test.txt")]
end
let(:commit_3_files) do
@@ -1621,7 +1569,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
it "read every file paths of master branch" do
- expect(master_file_paths.length).to equal(40)
+ expect(master_file_paths.length).to equal(38)
end
it "reads full file paths of master branch" do
@@ -1646,11 +1594,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe "#copy_gitattributes" do
- let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
-
- after do
- FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
- end
+ let(:repository) { mutable_repository }
it "raises an error with invalid ref" do
expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
@@ -1673,63 +1617,10 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository
end
end
-
- context "with no .gitattrbutes" do
- before do
- repository.copy_gitattributes("master")
- end
-
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
- end
-
- context "with .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- end
-
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
-
- it "has the same content in info/attributes as .gitattributes" do
- contents = File.open(attributes_path, "rb") { |f| f.read }
- expect(contents).to eq("*.md binary\n")
- end
- end
-
- context "with updated .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("gitattributes-updated")
- end
-
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
-
- it "has the updated content in info/attributes" do
- contents = File.read(attributes_path)
- expect(contents).to eq("*.txt binary\n")
- end
- end
-
- context "with no .gitattrbutes in HEAD but with previous info/attributes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("master")
- end
-
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
- end
end
describe '#gitattribute' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
context 'with gitattributes' do
before do
@@ -1808,10 +1699,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the local branches' do
expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
@@ -1880,7 +1767,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#languages' do
it 'returns exactly the expected results' do
- languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6')
+ languages = repository.languages(TestEnv::BRANCH_SHA['master'])
expect(languages).to match_array([
{ value: a_value_within(0.1).of(66.7), label: "Ruby", color: "#701516", highlight: "#701516" },
@@ -1918,18 +1805,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#fetch_source_branch!' do
let(:local_ref) { 'refs/merge-requests/1/head' }
+ let(:repository) { create(:project, :repository).repository.raw }
let(:source_repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
context 'when the branch exists' do
context 'when the commit does not exist locally' do
let(:source_branch) { 'new-branch-for-fetch-source-branch' }
let!(:new_oid) do
- source_repository.multi_action(
+ source_repository.commit_files(
user,
branch_name: source_branch,
message: 'Add a file',
@@ -1949,14 +1833,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'when the commit exists locally' do
let(:source_branch) { 'master' }
- let(:expected_oid) { SeedRepo::LastCommit::ID }
+ let(:expected_oid) { TestEnv::BRANCH_SHA['master'] }
it 'writes the ref' do
# Sanity check: the commit should already exist
expect(repository.commit(expected_oid)).not_to be_nil
expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(expected_oid)
+ expect(repository.commit(local_ref).sha).to start_with(expected_oid)
end
end
end
@@ -2012,9 +1896,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
it 'writes other refs' do
- repository.write_ref('refs/heads/feature', SeedRepo::Commit::ID)
+ repository.write_ref('refs/heads/feature', TestEnv::BRANCH_SHA['master'])
- expect(repository.commit('feature').sha).to eq(SeedRepo::Commit::ID)
+ expect(repository.commit('feature').sha).to start_with(TestEnv::BRANCH_SHA['master'])
end
end
@@ -2052,28 +1936,28 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns nil for an empty repo' do
project = create(:project)
- expect(project.repository.refs_by_oid(oid: SeedRepo::Commit::ID, limit: 0)).to be_nil
+ expect(project.repository.refs_by_oid(oid: TestEnv::BRANCH_SHA['master'], limit: 0)).to be_nil
end
end
describe '#set_full_path' do
+ let(:full_path) { 'some/path' }
+
before do
- repository.set_full_path(full_path: repository_path)
+ repository.set_full_path(full_path: full_path)
end
- context 'is given a path' do
- it 'writes it to disk' do
- repository.set_full_path(full_path: "not-the/real-path.git")
+ it 'writes full_path to gitaly' do
+ repository.set_full_path(full_path: "not-the/real-path.git")
- expect(repository.full_path).to eq('not-the/real-path.git')
- end
+ expect(repository.full_path).to eq('not-the/real-path.git')
end
context 'it is given an empty path' do
it 'does not write it to disk' do
repository.set_full_path(full_path: "")
- expect(repository.full_path).to eq(repository_path)
+ expect(repository.full_path).to eq(full_path)
end
end
@@ -2145,10 +2029,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch(target_branch, '6d394385cf567f80a8fd85055db1ab4c5295806f')
end
- after do
- ensure_seeds
- end
-
it 'can perform a merge' do
merge_commit_id = nil
result = repository.merge(user, source_sha, target_branch, 'Test merge') do |commit_id|
@@ -2185,10 +2065,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch(target_branch, branch_head)
end
- after do
- ensure_seeds
- end
-
subject { repository.ff_merge(user, source_sha, target_branch) }
shared_examples '#ff_merge' do
@@ -2242,14 +2118,10 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:repository) { mutable_repository }
before do
- repository.write_ref("refs/delete/a", "0b4bc9a49b562e85de7cc9e834518ea6828729b9")
- repository.write_ref("refs/also-delete/b", "12d65c8dd2b2676fa3ac47d955accc085a37a9c1")
- repository.write_ref("refs/keep/c", "6473c90867124755509e100d0d35ebdc85a0b6ae")
- repository.write_ref("refs/also-keep/d", "0b4bc9a49b562e85de7cc9e834518ea6828729b9")
- end
-
- after do
- ensure_seeds
+ repository.write_ref("refs/delete/a", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/also-delete/b", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/keep/c", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/also-keep/d", TestEnv::BRANCH_SHA['master'])
end
it 'deletes all refs except those with the specified prefixes' do
@@ -2272,11 +2144,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'saves a bundle to disk' do
repository.bundle_to_disk(save_path)
- success = system(
- *%W(#{Gitlab.config.git.bin_path} -C #{repository_path} bundle verify #{save_path}),
- [:out, :err] => '/dev/null'
- )
- expect(success).to be true
+ expect(File).to exist(save_path)
end
end
@@ -2326,41 +2194,22 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#checksum' do
it 'calculates the checksum for non-empty repo' do
- expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059'
- end
-
- it 'returns 0000000000000000000000000000000000000000 for an empty repo' do
- FileUtils.rm_rf(File.join(storage_path, 'empty-repo.git'))
-
- system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git),
- chdir: storage_path,
- out: '/dev/null',
- err: '/dev/null')
-
- empty_repo = described_class.new('default', 'empty-repo.git', '', 'group/empty-repo')
-
- expect(empty_repo.checksum).to eq '0000000000000000000000000000000000000000'
+ expect(repository.checksum.length).to be(40)
+ expect(Gitlab::Git.blank_ref?(repository.checksum)).to be false
end
- it 'raises Gitlab::Git::Repository::InvalidRepository error for non-valid git repo' do
- FileUtils.rm_rf(File.join(storage_path, 'non-valid.git'))
-
- system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} non-valid.git),
- chdir: SEED_STORAGE_PATH,
- out: '/dev/null',
- err: '/dev/null')
-
- File.truncate(File.join(storage_path, 'non-valid.git/HEAD'), 0)
+ it 'returns a blank sha for an empty repo' do
+ repository = create(:project, :empty_repo).repository
- non_valid = described_class.new('default', 'non-valid.git', '', 'a/non-valid')
-
- expect { non_valid.checksum }.to raise_error(Gitlab::Git::Repository::InvalidRepository)
+ expect(Gitlab::Git.blank_ref?(repository.checksum)).to be true
end
- it 'raises Gitlab::Git::Repository::NoRepository error when there is no repo' do
- broken_repo = described_class.new('default', 'a/path.git', '', 'a/path')
+ it 'raises NoRepository for a non-existent repo' do
+ repository = create(:project).repository
- expect { broken_repo.checksum }.to raise_error(Gitlab::Git::Repository::NoRepository)
+ expect do
+ repository.checksum
+ end.to raise_error(described_class::NoRepository)
end
end
@@ -2375,7 +2224,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#squash' do
let(:branch_name) { 'fix' }
- let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:start_sha) { TestEnv::BRANCH_SHA['master'] }
let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
subject do
@@ -2412,7 +2261,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'when the diff contains a rename' do
let(:end_sha) do
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Move CHANGELOG to encoding/',
@@ -2427,7 +2276,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
after do
# Erase our commits so other tests get the original repo
- repository.write_ref(repository.root_ref, SeedRepo::LastCommit::ID)
+ repository.write_ref(repository.root_ref, TestEnv::BRANCH_SHA['master'])
end
it 'does not include the renamed file in the sparse checkout' do
@@ -2480,9 +2329,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#disconnect_alternates' do
- let(:project) { create(:project, :repository) }
+ let(:project) { mutable_project }
+ let(:repository) { mutable_repository }
let(:pool_repository) { create(:pool_repository) }
- let(:repository) { project.repository }
let(:object_pool) { pool_repository.object_pool }
before do
@@ -2495,7 +2344,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'can still access objects in the object pool' do
object_pool.link(repository)
- new_commit_id = object_pool.repository.multi_action(
+ new_commit_id = object_pool.repository.commit_files(
project.owner,
branch_name: object_pool.repository.root_ref,
message: 'Add a file',
@@ -2515,8 +2364,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#rename' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
it 'moves the repository' do
checksum = repository.checksum
@@ -2531,15 +2379,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#remove' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
it 'removes the repository' do
expect(repository.exists?).to be true
repository.remove
- expect(repository.raw_repository.exists?).to be false
+ expect(repository.exists?).to be false
end
context 'when the repository does not exist' do
@@ -2550,15 +2397,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.remove
- expect(repository.raw_repository.exists?).to be false
+ expect(repository.exists?).to be false
end
end
end
describe '#import_repository' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:repository) { create(:project).repository }
- let(:repository) { project.repository }
let(:url) { 'http://invalid.invalid' }
it 'raises an error if a relative path is provided' do
@@ -2584,11 +2430,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#replicate' do
let(:new_repository) do
- Gitlab::Git::Repository.new('test_second_storage', TEST_REPO_PATH, '', 'group/project')
+ Gitlab::Git::Repository.new('test_second_storage', repository.relative_path, '', 'group/project')
end
- let(:new_repository_path) { File.join(TestEnv::SECOND_STORAGE_PATH, new_repository.relative_path) }
-
subject { new_repository.replicate(repository) }
before do
@@ -2622,7 +2466,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with keep-around refs' do
- let(:sha) { SeedRepo::Commit::ID }
+ let(:repository) { mutable_repository }
+ let(:sha) { TestEnv::BRANCH_SHA['master'] }
let(:keep_around_ref) { "refs/keep-around/#{sha}" }
let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
index 03d1c125e36..747611a59e6 100644
--- a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
+++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require 'json'
require 'tempfile'
-RSpec.describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
+RSpec.describe Gitlab::Git::RuggedImpl::UseRugged do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:feature_flag_name) { wrapper.rugged_feature_keys.first }
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 2e4520cd3a0..7c84c737c00 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:subdir_file) { entries.first }
# rubocop: enable Rails/FindBy
let!(:sha) do
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'HEAD',
message: "Create #{filename}",
diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb
index a0237c821b5..dd925a902ab 100644
--- a/spec/lib/gitlab/git/util_spec.rb
+++ b/spec/lib/gitlab/git/util_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Util do
describe '#count_lines' do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index dddcf8c40fc..05c7ac149e4 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -8,9 +8,15 @@ RSpec.describe Gitlab::Git::Wiki do
let(:project) { create(:project) }
let(:user) { project.first_owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
+ let(:repository) { project_wiki.repository }
+ let(:default_branch) { described_class.default_ref(project) }
subject(:wiki) { project_wiki.wiki }
+ before do
+ repository.create_if_not_exists(project_wiki.default_branch)
+ end
+
describe '#pages' do
before do
create_page('page1', 'content')
@@ -44,7 +50,7 @@ RSpec.describe Gitlab::Git::Wiki do
after do
destroy_page('page1')
- destroy_page('page1', 'foo')
+ destroy_page('foo/page1')
end
it 'returns the right page' do
@@ -71,20 +77,20 @@ RSpec.describe Gitlab::Git::Wiki do
end
describe '#preview_slug' do
- where(:title, :format, :expected_slug) do
- 'The Best Thing' | :markdown | 'The-Best-Thing'
- 'The Best Thing' | :md | 'The-Best-Thing'
- 'The Best Thing' | :txt | 'The-Best-Thing'
- 'A Subject/Title Here' | :txt | 'A-Subject/Title-Here'
- 'A subject' | :txt | 'A-subject'
- 'A 1/B 2/C 3' | :txt | 'A-1/B-2/C-3'
- 'subject/title' | :txt | 'subject/title'
- 'subject/title.md' | :txt | 'subject/title.md'
- 'foo<bar>+baz' | :txt | 'foo-bar--baz'
- 'foo%2Fbar' | :txt | 'foo%2Fbar'
- '' | :markdown | '.md'
- '' | :md | '.md'
- '' | :txt | '.txt'
+ where(:title, :file_extension, :format, :expected_slug) do
+ 'The Best Thing' | :md | :markdown | 'The-Best-Thing'
+ 'The Best Thing' | :md | :md | 'The-Best-Thing'
+ 'The Best Thing' | :txt | :txt | 'The-Best-Thing'
+ 'A Subject/Title Here' | :txt | :txt | 'A-Subject/Title-Here'
+ 'A subject' | :txt | :txt | 'A-subject'
+ 'A 1/B 2/C 3' | :txt | :txt | 'A-1/B-2/C-3'
+ 'subject/title' | :txt | :txt | 'subject/title'
+ 'subject/title.md' | :txt | :txt | 'subject/title.md'
+ 'foo<bar>+baz' | :txt | :txt | 'foo-bar--baz'
+ 'foo%2Fbar' | :txt | :txt | 'foo%2Fbar'
+ '' | :md | :markdown | '.md'
+ '' | :md | :md | '.md'
+ '' | :txt | :txt | '.txt'
end
with_them do
@@ -97,7 +103,7 @@ RSpec.describe Gitlab::Git::Wiki do
it 'matches the slug generated by gitaly' do
skip('Gitaly cannot generate a slug for an empty title') unless title.present?
- create_page(title, 'content', format: format)
+ create_page(title, 'content', file_extension)
gitaly_slug = wiki.list_pages.first.url_path
@@ -106,16 +112,23 @@ RSpec.describe Gitlab::Git::Wiki do
end
end
- def create_page(name, content, format: :markdown)
- wiki.write_page(name, format, content, commit_details(name))
- end
-
- def commit_details(name)
- Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}")
+ def create_page(name, content, extension = :md)
+ repository.create_file(
+ user, ::Wiki.sluggified_full_path(name, extension.to_s), content,
+ branch_name: default_branch,
+ message: "created page #{name}",
+ author_email: user.email,
+ author_name: user.name
+ )
end
- def destroy_page(title, dir = '')
- page = wiki.page(title: title, dir: dir)
- project_wiki.delete_page(page, "test commit")
+ def destroy_page(name, extension = :md)
+ repository.delete_file(
+ user, ::Wiki.sluggified_full_path(name, extension.to_s),
+ branch_name: described_class.default_ref(project),
+ message: "delete page #{name}",
+ author_email: user.email,
+ author_name: user.name
+ )
end
end
diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb
index f359679a930..0f6ef55b4b1 100644
--- a/spec/lib/gitlab/git_spec.rb
+++ b/spec/lib/gitlab/git_spec.rb
@@ -7,10 +7,18 @@ RSpec.describe Gitlab::Git do
let(:committer_name) { 'John Doe' }
describe '.ref_name' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5" }
+
it 'ensure ref is a valid UTF-8 string' do
- utf8_invalid_ref = Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5"
+ expect(described_class.ref_name(ref)).to eq("an_invalid_ref_%E5")
+ end
- expect(described_class.ref_name(utf8_invalid_ref)).to eq("an_invalid_ref_å")
+ context 'when ref contains characters \x80 - \xFF' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "\x90" }
+
+ it 'correctly converts it' do
+ expect(described_class.ref_name(ref)).to eq("%90")
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index ed6a87cda6f..ff3cade07c0 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -297,6 +297,11 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
describe '#list_commits' do
let(:revisions) { 'master' }
let(:reverse) { false }
+ let(:author) { nil }
+ let(:ignore_case) { nil }
+ let(:commit_message_patterns) { nil }
+ let(:before) { nil }
+ let(:after) { nil }
let(:pagination_params) { nil }
shared_examples 'a ListCommits request' do
@@ -309,13 +314,18 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
expected_request = gitaly_request_with_params(
Array.wrap(revisions),
reverse: reverse,
+ author: author,
+ ignore_case: ignore_case,
+ commit_message_patterns: commit_message_patterns,
+ before: before,
+ after: after,
pagination_params: pagination_params
)
expect(service).to receive(:list_commits).with(expected_request, kind_of(Hash)).and_return([])
end
- client.list_commits(revisions, reverse: reverse, pagination_params: pagination_params)
+ client.list_commits(revisions, { reverse: reverse, author: author, ignore_case: ignore_case, commit_message_patterns: commit_message_patterns, before: before, after: after, pagination_params: pagination_params })
end
end
@@ -333,7 +343,12 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
it_behaves_like 'a ListCommits request'
end
- context 'with pagination params' do
+ context 'with commit message, author, before and after' do
+ let(:author) { "Dmitriy" }
+ let(:before) { 1474828200 }
+ let(:after) { 1474828200 }
+ let(:commit_message_patterns) { "Initial commit" }
+ let(:ignore_case) { true }
let(:pagination_params) { { limit: 1, page_token: 'foo' } }
it_behaves_like 'a ListCommits request'
diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb
index 230322faecd..2c1f684c0c5 100644
--- a/spec/lib/gitlab/gitaly_client/diff_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::Diff do
let(:diff_fields) do
diff --git a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
index 54c84ddc56f..39fd752ef7f 100644
--- a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::DiffStitcher do
describe 'enumeration' do
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 5d854f0c9d1..7e8aaa3cdf4 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -56,6 +56,85 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
Gitlab::Git::PreReceiveError, "something failed")
end
end
+
+ context 'with structured errors' do
+ context 'with CustomHookError' do
+ let(:stdout) { nil }
+ let(:stderr) { nil }
+ let(:error_message) { "error_message" }
+
+ let(:custom_hook_error) do
+ new_detailed_error(
+ GRPC::Core::StatusCodes::PERMISSION_DENIED,
+ error_message,
+ Gitaly::UserCreateBranchError.new(
+ custom_hook: Gitaly::CustomHookError.new(
+ stdout: stdout,
+ stderr: stderr,
+ hook_type: Gitaly::CustomHookError::HookType::HOOK_TYPE_PRERECEIVE
+ )))
+ end
+
+ shared_examples 'failed branch creation' do
+ it 'raised a PreRecieveError' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_create_branch)
+ .and_raise(custom_hook_error)
+
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(Gitlab::Git::PreReceiveError)
+ expect(error.message).to eq(expected_message)
+ expect(error.raw_message).to eq(expected_raw_message)
+ end
+ end
+ end
+
+ context 'when details contain stderr without prefix' do
+ let(:stderr) { "something" }
+ let(:stdout) { "GL-HOOK-ERR: stdout is overridden by stderr" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { stderr }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stderr with prefix' do
+ let(:stderr) { "GL-HOOK-ERR: something" }
+ let(:stdout) { "GL-HOOK-ERR: stdout is overridden by stderr" }
+ let(:expected_message) { "something" }
+ let(:expected_raw_message) { stderr }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stdout without prefix' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "something" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { stdout }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stdout with prefix' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "GL-HOOK-ERR: something" }
+ let(:expected_message) { "something" }
+ let(:expected_raw_message) { stdout }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain no stderr or stdout' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "\n \n" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { "\n \n" }
+
+ it_behaves_like 'failed branch creation'
+ end
+ end
+ end
end
describe '#user_update_branch' do
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 277276bb1d3..b7c21516c77 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -156,35 +156,84 @@ RSpec.describe Gitlab::GitalyClient::RefService do
end
describe '#local_branches' do
- it 'sends a find_local_branches message' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
- .and_return([])
+ let(:remote_name) { 'my_remote' }
- client.local_branches
- end
+ shared_examples 'common examples' do
+ it 'sends a find_local_branches message' do
+ target_commits = create_list(:gitaly_commit, 4)
+ branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::FindLocalBranchResponse.new(
+ name: "#{remote_name}/#{i}",
+ commit: gitaly_commit,
+ commit_author: Gitaly::FindLocalBranchCommitAuthor.new(
+ name: gitaly_commit.author.name,
+ email: gitaly_commit.author.email,
+ date: gitaly_commit.author.date,
+ timezone: gitaly_commit.author.timezone
+ ),
+ commit_committer: Gitaly::FindLocalBranchCommitAuthor.new(
+ name: gitaly_commit.committer.name,
+ email: gitaly_commit.committer.email,
+ date: gitaly_commit.committer.date,
+ timezone: gitaly_commit.committer.timezone
+ )
+ )
+ end
+ local_branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit)
+ end
+ response = [
+ Gitaly::FindLocalBranchesResponse.new(branches: branches[0, 2], local_branches: local_branches[0, 2]),
+ Gitaly::FindLocalBranchesResponse.new(branches: branches[2, 2], local_branches: local_branches[2, 2])
+ ]
- it 'parses and sends the sort parameter' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
- .and_return([])
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(response)
+
+ subject = client.local_branches
+
+ expect(subject.length).to be(target_commits.length)
+ end
+
+ it 'parses and sends the sort parameter' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
+ .and_return([])
+
+ client.local_branches(sort_by: 'updated_desc')
+ end
+
+ it 'translates known mismatches on sort param values' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash))
+ .and_return([])
+
+ client.local_branches(sort_by: 'name_asc')
+ end
- client.local_branches(sort_by: 'updated_desc')
+ it 'raises an argument error if an invalid sort_by parameter is passed' do
+ expect { client.local_branches(sort_by: 'invalid_sort') }.to raise_error(ArgumentError)
+ end
end
- it 'translates known mismatches on sort param values' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash))
- .and_return([])
+ context 'when feature flag :gitaly_simplify_find_local_branches_response is enabled' do
+ before do
+ stub_feature_flags(gitaly_simplify_find_local_branches_response: true)
+ end
- client.local_branches(sort_by: 'name_asc')
+ it_behaves_like 'common examples'
end
- it 'raises an argument error if an invalid sort_by parameter is passed' do
- expect { client.local_branches(sort_by: 'invalid_sort') }.to raise_error(ArgumentError)
+ context 'when feature flag :gitaly_simplify_find_local_branches_response is disabled' do
+ before do
+ stub_feature_flags(gitaly_simplify_find_local_branches_response: false)
+ end
+
+ it_behaves_like 'common examples'
end
end
@@ -211,6 +260,22 @@ RSpec.describe Gitlab::GitalyClient::RefService do
client.tags(sort_by: 'name_asc')
end
+
+ context 'with semantic version sorting' do
+ it 'sends a correct find_all_tags message' do
+ expected_sort_by = Gitaly::FindAllTagsRequest::SortBy.new(
+ key: :VERSION_REFNAME,
+ direction: :ASCENDING
+ )
+
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_tags)
+ .with(gitaly_request_with_params(sort_by: expected_sort_by), kind_of(Hash))
+ .and_return([])
+
+ client.tags(sort_by: 'version_asc')
+ end
+ end
end
context 'with pagination option' do
diff --git a/spec/lib/gitlab/gitaly_client/server_service_spec.rb b/spec/lib/gitlab/gitaly_client/server_service_spec.rb
new file mode 100644
index 00000000000..615f2ce0c21
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/server_service_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GitalyClient::ServerService do
+ let(:storage) { 'default' }
+
+ describe '#readiness_check' do
+ before do
+ ::Gitlab::GitalyClient.clear_stubs!
+ end
+
+ let(:request) do
+ Gitaly::ReadinessCheckRequest.new(timeout: 30)
+ end
+
+ subject(:readiness_check) { described_class.new(storage).readiness_check }
+
+ it 'returns a positive success if no failures happened' do
+ expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
+ response = Gitaly::ReadinessCheckResponse.new(ok_response: Gitaly::ReadinessCheckResponse::Ok.new)
+ expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
+ end
+
+ expect(readiness_check[:success]).to eq(true)
+ end
+
+ it 'returns a negative success and a compiled message if at least one failure happened' do
+ failure1 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '1', error_message: 'msg 1')
+ failure2 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '2', error_message: 'msg 2')
+ failures = Gitaly::ReadinessCheckResponse::Failure.new(failed_checks: [failure1, failure2])
+ response = Gitaly::ReadinessCheckResponse.new(failure_response: failures)
+
+ expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
+ expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
+ end
+
+ expect(readiness_check[:success]).to eq(false)
+ expect(readiness_check[:message]).to eq("1: msg 1\n2: msg 2")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb
index b6589a08f7d..ae7c3789051 100644
--- a/spec/lib/gitlab/gitaly_client/util_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/util_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::Util do
describe '.repository' do
diff --git a/spec/lib/gitlab/github_import/attachments_downloader_spec.rb b/spec/lib/gitlab/github_import/attachments_downloader_spec.rb
new file mode 100644
index 00000000000..57391e06192
--- /dev/null
+++ b/spec/lib/gitlab/github_import/attachments_downloader_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::AttachmentsDownloader do
+ subject(:downloader) { described_class.new(file_url) }
+
+ let_it_be(:file_url) { 'https://example.com/avatar.png' }
+ let_it_be(:content_type) { 'application/octet-stream' }
+
+ let(:content_length) { 1000 }
+ let(:chunk_double) { instance_double(HTTParty::FragmentWithResponse, code: 200) }
+ let(:headers_double) do
+ instance_double(
+ HTTParty::Response,
+ code: 200,
+ success?: true,
+ parsed_response: {},
+ headers: {
+ 'content-length' => content_length,
+ 'content-type' => content_type
+ }
+ )
+ end
+
+ describe '#perform' do
+ before do
+ allow(Gitlab::HTTP).to receive(:perform_request)
+ .with(Net::HTTP::Get, file_url, stream_body: true).and_yield(chunk_double)
+ allow(Gitlab::HTTP).to receive(:perform_request)
+ .with(Net::HTTP::Head, file_url, {}).and_return(headers_double)
+ end
+
+ context 'when file valid' do
+ it 'downloads file' do
+ file = downloader.perform
+
+ expect(File.exist?(file.path)).to eq(true)
+ end
+ end
+
+ context 'when filename is malicious' do
+ let_it_be(:file_url) { 'https://example.com/ava%2F..%2Ftar.png' }
+
+ it 'raises expected exception' do
+ expect { downloader.perform }.to raise_exception(
+ Gitlab::Utils::PathTraversalAttackError,
+ 'Invalid path'
+ )
+ end
+ end
+
+ context 'when file size exceeds limit' do
+ let(:content_length) { 26.megabytes }
+
+ it 'raises expected exception' do
+ expect { downloader.perform }.to raise_exception(
+ Gitlab::GithubImport::AttachmentsDownloader::DownloadError,
+ 'File size 26 MB exceeds limit of 25 MB'
+ )
+ end
+ end
+
+ context 'when file name length exceeds limit' do
+ before do
+ stub_const('BulkImports::FileDownloads::FilenameFetch::FILENAME_SIZE_LIMIT', 2)
+ end
+
+ it 'chops filename' do
+ file = downloader.perform
+
+ expect(File.exist?(file.path)).to eq(true)
+ expect(File.basename(file)).to eq('av.png')
+ end
+ end
+ end
+
+ describe '#delete' do
+ let(:tmp_dir_path) { File.join(Dir.tmpdir, 'github_attachments_test') }
+ let(:file) do
+ downloader.mkdir_p(tmp_dir_path)
+ file = File.open("#{tmp_dir_path}/test.txt", 'wb')
+ file.write('foo')
+ file.close
+ file
+ end
+
+ before do
+ allow(downloader).to receive(:filepath).and_return(file.path)
+ end
+
+ it 'removes file with parent folder' do
+ downloader.delete
+ expect(Dir.exist?(tmp_dir_path)).to eq false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 2bd3910ad87..c88bb6de859 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -40,6 +40,22 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#repos' do
+ it 'returns the user\'s repositories as a hash' do
+ client = described_class.new('foo')
+
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/user/repos?page=1&page_length=10&per_page=100')
+ .to_return(status: 200, body: [{ id: 1 }, { id: 2 }].to_json, headers: { 'Content-Type' => 'application/json' })
+
+ repos = client.repos({ page: 1, page_length: 10 })
+
+ expect(repos).to match_array([{ id: 1 }, { id: 2 }])
+ end
+ end
+
describe '#repository' do
it 'returns the details of a repository' do
client = described_class.new('foo')
@@ -49,6 +65,20 @@ RSpec.describe Gitlab::GithubImport::Client do
client.repository('foo/bar')
end
+
+ it 'returns repository data as a hash' do
+ client = described_class.new('foo')
+
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/repos/foo/bar')
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: { 'Content-Type' => 'application/json' })
+
+ repository = client.repository('foo/bar')
+
+ expect(repository).to eq({ id: 1 })
+ end
end
describe '#pull_request' do
@@ -98,6 +128,30 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#branches' do
+ it 'returns the branches' do
+ client = described_class.new('foo')
+
+ expect(client)
+ .to receive(:each_object)
+ .with(:branches, 'foo/bar')
+
+ client.branches('foo/bar')
+ end
+ end
+
+ describe '#branch_protection' do
+ it 'returns the protection details for the given branch' do
+ client = described_class.new('foo')
+
+ expect(client.octokit)
+ .to receive(:branch_protection).with('org/repo', 'bar')
+ expect(client).to receive(:with_rate_limit).and_yield
+
+ client.branch_protection('org/repo', 'bar')
+ end
+ end
+
describe '#each_page' do
let(:client) { described_class.new('foo') }
let(:object1) { double(:object1) }
@@ -234,7 +288,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(client).to receive(:requests_remaining?).twice.and_return(true)
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.with_rate_limit(&block_to_rate_limit)).to be(true)
+ expect(client.with_rate_limit(&block_to_rate_limit)).to eq({})
end
it 'retries and does not succeed' do
@@ -255,7 +309,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.with_rate_limit(&block_to_rate_limit)).to be(true)
+ expect(client.with_rate_limit(&block_to_rate_limit)).to eq({})
end
it 'retries and does not succeed' do
@@ -559,7 +613,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.search_repos_by_name('test')).to be(true)
+ expect(client.search_repos_by_name('test')).to eq({})
end
it 'retries and does not succeed' do
@@ -599,7 +653,7 @@ RSpec.describe Gitlab::GithubImport::Client do
call_count = 0
allow(client.octokit).to receive(method) do
call_count += 1
- call_count > 1 ? true : raise(described_class::CLIENT_CONNECTION_ERROR, 'execution expired')
+ call_count > 1 ? {} : raise(described_class::CLIENT_CONNECTION_ERROR, 'execution expired')
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
index 2f6f727dc38..dbc72574ec2 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
@@ -6,31 +6,30 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
subject(:importer) { described_class.new(project, client) }
let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:author) { create(:user) }
let_it_be(:assignee) { create(:user) }
- let_it_be(:assigner) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
'id' => 6501124486,
- 'actor' => { 'id' => 4, 'login' => 'alice' },
+ 'actor' => { 'id' => author.id, 'login' => author.username },
'event' => event_type,
'commit_id' => nil,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'assigner' => { 'id' => assigner.id, 'login' => assigner.username },
'assignee' => { 'id' => assignee.id, 'login' => assignee.username },
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:note_attrs) do
{
- noteable_id: issue.id,
- noteable_type: Issue.name,
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
project_id: project.id,
- author_id: assigner.id,
+ author_id: author.id,
system: true,
created_at: issue_event.created_at,
updated_at: issue_event.created_at
@@ -45,12 +44,12 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
}.stringify_keys
end
- shared_examples 'new note' do
+ shared_examples 'create expected notes' do
it 'creates expected note' do
- expect { importer.execute(issue_event) }.to change { issue.notes.count }
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
.from(0).to(1)
- expect(issue.notes.last)
+ expect(issuable.notes.last)
.to have_attributes(expected_note_attrs)
end
@@ -67,29 +66,41 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
end
end
+ shared_examples 'process assigned & unassigned events' do
+ context 'when importing an assigned event' do
+ let(:event_type) { 'assigned' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "assigned to @#{assignee.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+
+ context 'when importing an unassigned event' do
+ let(:event_type) { 'unassigned' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assignee.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+ end
+
describe '#execute' do
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(author.id, author.username).and_return(author.id)
allow(finder).to receive(:find).with(assignee.id, assignee.username).and_return(assignee.id)
- allow(finder).to receive(:find).with(assigner.id, assigner.username).and_return(assigner.id)
end
end
- context 'when importing an assigned event' do
- let(:event_type) { 'assigned' }
- let(:expected_note_attrs) { note_attrs.merge(note: "assigned to @#{assignee.username}") }
-
- it_behaves_like 'new note'
+ context 'with Issue' do
+ it_behaves_like 'process assigned & unassigned events'
end
- context 'when importing an unassigned event' do
- let(:event_type) { 'unassigned' }
- let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assigner.username}") }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
- it_behaves_like 'new note'
+ it_behaves_like 'process assigned & unassigned events'
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
index e21672aa430..4476b4123ee 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let!(:label) { create(:label, project: project) }
let(:issue_event) do
@@ -19,16 +19,14 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
'event' => event_type,
'commit_id' => nil,
'label_title' => label.title,
- 'issue_db_id' => issue.id,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:event_attrs) do
{
user_id: user.id,
- issue_id: issue.id,
label_id: label.id,
created_at: issue_event.created_at
}.stringify_keys
@@ -36,9 +34,9 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
shared_examples 'new event' do
it 'creates a new label event' do
- expect { importer.execute(issue_event) }.to change { issue.resource_label_events.count }
+ expect { importer.execute(issue_event) }.to change { issuable.resource_label_events.count }
.from(0).to(1)
- expect(issue.resource_label_events.last)
+ expect(issuable.resource_label_events.last)
.to have_attributes(expected_event_attrs)
end
end
@@ -46,24 +44,44 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
before do
allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(label.id)
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- context 'when importing a labeled event' do
- let(:event_type) { 'labeled' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'add') }
+ context 'with Issue' do
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing an unlabeled event' do
- let(:event_type) { 'unlabeled' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'remove') }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
index 2687627fc23..bc14b81bd91 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let!(:milestone) { create(:milestone, project: project) }
let(:issue_event) do
@@ -19,16 +19,15 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
'event' => event_type,
'commit_id' => nil,
'milestone_title' => milestone.title,
- 'issue_db_id' => issue.id,
+ 'issue_db_id' => issuable.id,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:event_attrs) do
{
user_id: user.id,
- issue_id: issue.id,
milestone_id: milestone.id,
state: 'opened',
created_at: issue_event.created_at
@@ -37,9 +36,9 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
shared_examples 'new event' do
it 'creates a new milestone event' do
- expect { importer.execute(issue_event) }.to change { issue.resource_milestone_events.count }
+ expect { importer.execute(issue_event) }.to change { issuable.resource_milestone_events.count }
.from(0).to(1)
- expect(issue.resource_milestone_events.last)
+ expect(issuable.resource_milestone_events.last)
.to have_attributes(expected_event_attrs)
end
end
@@ -48,25 +47,45 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
before do
allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(milestone.id)
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- context 'when importing a milestoned event' do
- let(:event_type) { 'milestoned' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'add') }
+ context 'with Issue' do
+ context 'when importing a milestoned event' do
+ let(:event_type) { 'milestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing demilestoned event' do
+ let(:event_type) { 'demilestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing demilestoned event' do
- let(:event_type) { 'demilestoned' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'remove') }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ context 'when importing a milestoned event' do
+ let(:event_type) { 'milestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing demilestoned event' do
+ let(:event_type) { 'demilestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb
new file mode 100644
index 00000000000..ff813dd41d9
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedReviewer do
+ subject(:importer) { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:requested_reviewer) { create(:user) }
+ let_it_be(:review_requester) { create(:user) }
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ let(:issue_event) do
+ Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
+ 'id' => 6501124486,
+ 'actor' => { 'id' => 4, 'login' => 'alice' },
+ 'event' => event_type,
+ 'commit_id' => nil,
+ 'created_at' => '2022-04-26 18:30:53 UTC',
+ 'review_requester' => { 'id' => review_requester.id, 'login' => review_requester.username },
+ 'requested_reviewer' => { 'id' => requested_reviewer.id, 'login' => requested_reviewer.username },
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
+ )
+ end
+
+ let(:note_attrs) do
+ {
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
+ project_id: project.id,
+ author_id: review_requester.id,
+ system: true,
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ let(:expected_system_note_metadata_attrs) do
+ {
+ action: 'reviewer',
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ shared_examples 'create expected notes' do
+ it 'creates expected note' do
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
+ .from(0).to(1)
+
+ expect(issuable.notes.last)
+ .to have_attributes(expected_note_attrs)
+ end
+
+ it 'creates expected system note metadata' do
+ expect { importer.execute(issue_event) }.to change(SystemNoteMetadata, :count)
+ .from(0).to(1)
+
+ expect(SystemNoteMetadata.last)
+ .to have_attributes(
+ expected_system_note_metadata_attrs.merge(
+ note_id: Note.last.id
+ )
+ )
+ end
+ end
+
+ shared_examples 'process review_requested & review_request_removed MR events' do
+ context 'when importing a review_requested event' do
+ let(:event_type) { 'review_requested' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "requested review from @#{requested_reviewer.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+
+ context 'when importing a review_request_removed event' do
+ let(:event_type) { 'review_request_removed' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "removed review request for @#{requested_reviewer.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+ end
+
+ describe '#execute' do
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(requested_reviewer.id, requested_reviewer.username)
+ .and_return(requested_reviewer.id)
+ allow(finder).to receive(:find).with(review_requester.id, review_requester.username)
+ .and_return(review_requester.id)
+ end
+ end
+
+ it_behaves_like 'process review_requested & review_request_removed MR events'
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/events/closed_spec.rb b/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
index 9a49d80a8bb..f7e38f373c0 100644
--- a/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:commit_id) { nil }
let(:issue_event) do
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
'event' => 'closed',
'created_at' => '2022-04-26 18:30:53 UTC',
'commit_id' => commit_id,
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -29,54 +29,74 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
{
project_id: project.id,
author_id: user.id,
- target_id: issue.id,
- target_type: Issue.name,
+ target_id: issuable.id,
+ target_type: issuable.class.name,
action: 'closed',
created_at: issue_event.created_at,
updated_at: issue_event.created_at
}.stringify_keys
end
- let(:expected_state_event_attrs) do
- {
- user_id: user.id,
- issue_id: issue.id,
- state: 'closed',
- created_at: issue_event.created_at
- }.stringify_keys
- end
-
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ shared_examples 'new event' do
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
+
+ expect(issuable.events.count).to eq 1
+ expect(issuable.events[0].attributes)
+ .to include expected_event_attrs
+
+ expect(issuable.resource_state_events.count).to eq 1
+ expect(issuable.resource_state_events[0].attributes)
+ .to include expected_state_event_attrs
+ end
+
+ context 'when closed by commit' do
+ let!(:closing_commit) { create(:commit, project: project) }
+ let(:commit_id) { closing_commit.id }
- expect(issue.events.count).to eq 1
- expect(issue.events[0].attributes)
- .to include expected_event_attrs
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
- expect(issue.resource_state_events.count).to eq 1
- expect(issue.resource_state_events[0].attributes)
- .to include expected_state_event_attrs
+ expect(issuable.events.count).to eq 1
+ state_event = issuable.resource_state_events.last
+ expect(state_event.source_commit).to eq commit_id[0..40]
+ end
+ end
end
- context 'when closed by commit' do
- let!(:closing_commit) { create(:commit, project: project) }
- let(:commit_id) { closing_commit.id }
+ context 'with Issue' do
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ issue_id: issuable.id,
+ state: 'closed',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ it_behaves_like 'new event'
+ end
- expect(issue.events.count).to eq 1
- state_event = issue.resource_state_events.last
- expect(state_event.source_commit).to eq commit_id[0..40]
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ merge_request_id: issuable.id,
+ state: 'closed',
+ created_at: issue_event.created_at
+ }.stringify_keys
end
+
+ it_behaves_like 'new event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
index 68e001c7364..bf19147d4c8 100644
--- a/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
@@ -9,9 +9,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
-
let(:issue_iid) { 999 }
- let(:issue) { create(:issue, project: project, iid: issue_iid) }
+ let(:issuable) { create(:issue, project: project, iid: issue_iid) }
let(:referenced_in) { build_stubbed(:issue, project: project, iid: issue_iid + 1) }
let(:commit_id) { nil }
@@ -30,7 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
}
},
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -38,8 +37,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
let(:expected_note_attrs) do
{
system: true,
- noteable_type: Issue.name,
- noteable_id: issue.id,
+ noteable_type: issuable.class.name,
+ noteable_id: issuable.id,
project_id: project.id,
author_id: user.id,
note: expected_note_body,
@@ -47,58 +46,70 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
}.stringify_keys
end
- context 'when referenced in other issue' do
- let(:expected_note_body) { "mentioned in issue ##{referenced_in.iid}" }
-
- before do
- allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(referenced_in.iid)
- allow(finder).to receive(:database_id).and_return(issue.id)
- end
- allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
- allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ shared_examples 'import cross-referenced event' do
+ context 'when referenced in other issue' do
+ let(:expected_note_body) { "mentioned in issue ##{referenced_in.iid}" }
+
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(referenced_in.iid)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ end
end
- end
- it 'creates expected note' do
- importer.execute(issue_event)
+ it 'creates expected note' do
+ importer.execute(issue_event)
- expect(issue.notes.count).to eq 1
- expect(issue.notes[0]).to have_attributes expected_note_attrs
- expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ expect(issuable.notes.count).to eq 1
+ expect(issuable.notes[0]).to have_attributes expected_note_attrs
+ expect(issuable.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ end
end
- end
- context 'when referenced in pull request' do
- let(:referenced_in) { build_stubbed(:merge_request, project: project) }
- let(:pull_request_resource) { { 'id' => referenced_in.iid } }
+ context 'when referenced in pull request' do
+ let(:referenced_in) { build_stubbed(:merge_request, project: project) }
+ let(:pull_request_resource) { { 'id' => referenced_in.iid } }
- let(:expected_note_body) { "mentioned in merge request !#{referenced_in.iid}" }
+ let(:expected_note_body) { "mentioned in merge request !#{referenced_in.iid}" }
- before do
- allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(referenced_in.iid)
- allow(finder).to receive(:database_id).and_return(issue.id)
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(referenced_in.iid)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ end
end
- allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
- allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+
+ it 'creates expected note' do
+ importer.execute(issue_event)
+
+ expect(issuable.notes.count).to eq 1
+ expect(issuable.notes[0]).to have_attributes expected_note_attrs
+ expect(issuable.notes[0].system_note_metadata.action).to eq 'cross_reference'
end
end
- it 'creates expected note' do
- importer.execute(issue_event)
+ context 'when referenced in out of project issue/pull_request' do
+ it 'does not create expected note' do
+ importer.execute(issue_event)
- expect(issue.notes.count).to eq 1
- expect(issue.notes[0]).to have_attributes expected_note_attrs
- expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ expect(issuable.notes.count).to eq 0
+ end
end
end
- context 'when referenced in out of project issue/pull_request' do
- it 'does not create expected note' do
- importer.execute(issue_event)
+ context 'with Issue' do
+ it_behaves_like 'import cross-referenced event'
+ end
- expect(issue.notes.count).to eq 0
- end
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'import cross-referenced event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb b/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
index 316ea798965..29598cb4354 100644
--- a/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:issue_event) do
@@ -20,14 +20,14 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
'created_at' => '2022-04-26 18:30:53 UTC',
'old_title' => 'old title',
'new_title' => 'new title',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:expected_note_attrs) do
{
- noteable_id: issue.id,
- noteable_type: Issue.name,
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
project_id: project.id,
author_id: user.id,
note: "changed title from **{-old-} title** to **{+new+} title**",
@@ -48,31 +48,43 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
describe '#execute' do
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected note' do
- expect { importer.execute(issue_event) }.to change { issue.notes.count }
- .from(0).to(1)
+ shared_examples 'import renamed event' do
+ it 'creates expected note' do
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
+ .from(0).to(1)
- expect(issue.notes.last)
- .to have_attributes(expected_note_attrs)
- end
+ expect(issuable.notes.last)
+ .to have_attributes(expected_note_attrs)
+ end
- it 'creates expected system note metadata' do
- expect { importer.execute(issue_event) }.to change { SystemNoteMetadata.count }
- .from(0).to(1)
+ it 'creates expected system note metadata' do
+ expect { importer.execute(issue_event) }.to change { SystemNoteMetadata.count }
+ .from(0).to(1)
- expect(SystemNoteMetadata.last)
- .to have_attributes(
- expected_system_note_metadata_attrs.merge(
- note_id: Note.last.id
+ expect(SystemNoteMetadata.last)
+ .to have_attributes(
+ expected_system_note_metadata_attrs.merge(
+ note_id: Note.last.id
+ )
)
- )
+ end
+ end
+
+ context 'with Issue' do
+ it_behaves_like 'import renamed event'
+ end
+
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'import renamed event'
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb b/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
index 2461dbb9701..354003fc997 100644
--- a/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => 'reopened',
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -27,40 +27,61 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
{
project_id: project.id,
author_id: user.id,
- target_id: issue.id,
- target_type: Issue.name,
+ target_id: issuable.id,
+ target_type: issuable.class.name,
action: 'reopened',
created_at: issue_event.created_at,
updated_at: issue_event.created_at
}.stringify_keys
end
- let(:expected_state_event_attrs) do
- {
- user_id: user.id,
- state: 'reopened',
- created_at: issue_event.created_at
- }.stringify_keys
- end
-
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ shared_examples 'new event' do
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
- expect(issue.events.count).to eq 1
- expect(issue.events[0].attributes)
- .to include expected_event_attrs
+ expect(issuable.events.count).to eq 1
+ expect(issuable.events[0].attributes)
+ .to include expected_event_attrs
+
+ expect(issuable.resource_state_events.count).to eq 1
+ expect(issuable.resource_state_events[0].attributes)
+ .to include expected_state_event_attrs
+ end
+ end
+
+ context 'with Issue' do
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ issue_id: issuable.id,
+ state: 'reopened',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ it_behaves_like 'new event'
+ end
+
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ merge_request_id: issuable.id,
+ state: 'reopened',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
- expect(issue.resource_state_events.count).to eq 1
- expect(issue.resource_state_events[0].attributes)
- .to include expected_state_event_attrs
+ it_behaves_like 'new event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
index 49a76fb5e6b..d28640a4f07 100644
--- a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Importer::IssueAndLabelLinksImporter do
describe '#execute' do
diff --git a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
index 33d5fbf13a0..91121f3c3fc 100644
--- a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
@@ -42,10 +42,6 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventImporter, :clean_gitlab
end
describe '#execute' do
- before do
- issue_event.attributes[:issue_db_id] = issue.id
- end
-
context "when it's closed issue event" do
let(:event_name) { 'closed' }
@@ -116,6 +112,20 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventImporter, :clean_gitlab
Gitlab::GithubImport::Importer::Events::ChangedAssignee
end
+ context "when it's review_requested issue event" do
+ let(:event_name) { 'review_requested' }
+
+ it_behaves_like 'triggers specific event importer',
+ Gitlab::GithubImport::Importer::Events::ChangedReviewer
+ end
+
+ context "when it's review_request_removed issue event" do
+ let(:event_name) { 'review_request_removed' }
+
+ it_behaves_like 'triggers specific event importer',
+ Gitlab::GithubImport::Importer::Events::ChangedReviewer
+ end
+
context "when it's unknown issue event" do
let(:event_name) { 'fake' }
diff --git a/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
index 8d4c1b01e50..2c1af4f8948 100644
--- a/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventsImporter do
let(:parallel) { true }
let(:issue_event) do
struct = Struct.new(
- :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone,
- :source, :assignee, :assigner, :issue, :created_at, :performed_via_github_app,
+ :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone, :source,
+ :assignee, :assigner, :review_requester, :requested_reviewer, :issue, :created_at, :performed_via_github_app,
keyword_init: true
)
struct.new(id: rand(10), event: 'closed', created_at: '2022-04-26 18:30:53 UTC')
diff --git a/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb
new file mode 100644
index 00000000000..6dc6db739f4
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchImporter do
+ subject(:importer) { described_class.new(github_protected_branch, project, client) }
+
+ let(:allow_force_pushes_on_github) { true }
+ let(:github_protected_branch) do
+ Gitlab::GithubImport::Representation::ProtectedBranch.new(
+ id: 'protection',
+ allow_force_pushes: allow_force_pushes_on_github
+ )
+ end
+
+ let(:project) { create(:project, :repository) }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ describe '#execute' do
+ let(:create_service) { instance_double('ProtectedBranches::CreateService') }
+
+ shared_examples 'create branch protection by the strictest ruleset' do
+ let(:expected_ruleset) do
+ {
+ name: 'protection',
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ allow_force_push: expected_allow_force_push
+ }
+ end
+
+ it 'calls service with the correct arguments' do
+ expect(ProtectedBranches::CreateService).to receive(:new).with(
+ project,
+ project.creator,
+ expected_ruleset
+ ).and_return(create_service)
+
+ expect(create_service).to receive(:execute).with(skip_authorization: true)
+ importer.execute
+ end
+
+ it 'creates protected branch and access levels for given github rule' do
+ expect { importer.execute }.to change(ProtectedBranch, :count).by(1)
+ .and change(ProtectedBranch::PushAccessLevel, :count).by(1)
+ .and change(ProtectedBranch::MergeAccessLevel, :count).by(1)
+ end
+ end
+
+ context 'when branch is protected on GitLab' do
+ before do
+ create(
+ :protected_branch,
+ project: project,
+ name: 'protect*',
+ allow_force_push: allow_force_pushes_on_gitlab
+ )
+ end
+
+ context 'when branch protection rule on Gitlab is stricter than on Github' do
+ let(:allow_force_pushes_on_github) { true }
+ let(:allow_force_pushes_on_gitlab) { false }
+ let(:expected_allow_force_push) { false }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+
+ context 'when branch protection rule on Github is stricter than on Gitlab' do
+ let(:allow_force_pushes_on_github) { false }
+ let(:allow_force_pushes_on_gitlab) { true }
+ let(:expected_allow_force_push) { false }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+
+ context 'when branch protection rules on Github and Gitlab are the same' do
+ let(:allow_force_pushes_on_github) { true }
+ let(:allow_force_pushes_on_gitlab) { true }
+ let(:expected_allow_force_push) { true }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+ end
+
+ context 'when branch is not protected on GitLab' do
+ let(:expected_allow_force_push) { true }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
new file mode 100644
index 00000000000..4e9208be985
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
@@ -0,0 +1,225 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do
+ subject(:importer) { described_class.new(project, client, parallel: parallel) }
+
+ let(:project) { instance_double('Project', id: 4, import_source: 'foo/bar') }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:parallel) { true }
+
+ let(:branches) do
+ branch = Struct.new(:name, :protection, keyword_init: true)
+ protection = Struct.new(:enabled, keyword_init: true)
+
+ [
+ branch.new(name: 'main', protection: protection.new(enabled: false)),
+ branch.new(name: 'staging', protection: protection.new(enabled: true)),
+ branch.new(name: 'development', protection: nil) # when user has no admin right for this repo
+ ]
+ end
+
+ let(:github_protection_rule) do
+ response = Struct.new(:name, :url, :required_signatures, :enforce_admins, :required_linear_history,
+ :allow_force_pushes, :allow_deletion, :block_creations, :required_conversation_resolution,
+ keyword_init: true
+ )
+ required_signatures = Struct.new(:url, :enabled, keyword_init: true)
+ enforce_admins = Struct.new(:url, :enabled, keyword_init: true)
+ allow_option = Struct.new(:enabled, keyword_init: true)
+ response.new(
+ name: 'main',
+ url: 'https://example.com/branches/main/protection',
+ required_signatures: required_signatures.new(
+ url: 'https://example.com/branches/main/protection/required_signatures',
+ enabled: false
+ ),
+ enforce_admins: enforce_admins.new(
+ url: 'https://example.com/branches/main/protection/enforce_admins',
+ enabled: false
+ ),
+ required_linear_history: allow_option.new(
+ enabled: false
+ ),
+ allow_force_pushes: allow_option.new(
+ enabled: false
+ ),
+ allow_deletion: allow_option.new(
+ enabled: false
+ ),
+ block_creations: allow_option.new(
+ enabled: true
+ ),
+ required_conversation_resolution: allow_option.new(
+ enabled: false
+ )
+ )
+ end
+
+ describe '#parallel?' do
+ context 'when running in parallel mode' do
+ it { expect(importer).to be_parallel }
+ end
+
+ context 'when running in sequential mode' do
+ let(:parallel) { false }
+
+ it { expect(importer).not_to be_parallel }
+ end
+ end
+
+ describe '#execute' do
+ context 'when running in parallel mode' do
+ it 'imports protected branches in parallel' do
+ expect(importer).to receive(:parallel_import)
+
+ importer.execute
+ end
+ end
+
+ context 'when running in sequential mode' do
+ let(:parallel) { false }
+
+ it 'imports protected branches in sequence' do
+ expect(importer).to receive(:sequential_import)
+
+ importer.execute
+ end
+ end
+ end
+
+ describe '#sequential_import', :clean_gitlab_redis_cache do
+ let(:parallel) { false }
+
+ before do
+ allow(client).to receive(:branches).and_return(branches)
+ allow(client)
+ .to receive(:branch_protection)
+ .with(project.import_source, 'staging')
+ .and_return(github_protection_rule)
+ .once
+ end
+
+ it 'imports each protected branch in sequence' do
+ protected_branch_importer = instance_double('Gitlab::GithubImport::Importer::ProtectedBranchImporter')
+
+ expect(Gitlab::GithubImport::Importer::ProtectedBranchImporter)
+ .to receive(:new)
+ .with(
+ an_instance_of(Gitlab::GithubImport::Representation::ProtectedBranch),
+ project,
+ client
+ )
+ .and_return(protected_branch_importer)
+
+ expect(protected_branch_importer).to receive(:execute)
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment).with(project, :protected_branch, :fetched)
+
+ importer.sequential_import
+ end
+ end
+
+ describe '#parallel_import', :clean_gitlab_redis_cache do
+ before do
+ allow(client).to receive(:branches).and_return(branches)
+ allow(client)
+ .to receive(:branch_protection)
+ .with(project.import_source, 'staging')
+ .and_return(github_protection_rule)
+ .once
+ end
+
+ it 'imports each protected branch in parallel' do
+ expect(Gitlab::GithubImport::ImportProtectedBranchWorker)
+ .to receive(:bulk_perform_in)
+ .with(
+ 1.second,
+ [[project.id, an_instance_of(Hash), an_instance_of(String)]],
+ batch_delay: 1.minute,
+ batch_size: 1000
+ )
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment).with(project, :protected_branch, :fetched)
+
+ waiter = importer.parallel_import
+
+ expect(waiter).to be_an_instance_of(Gitlab::JobWaiter)
+ expect(waiter.jobs_remaining).to eq(1)
+ end
+ end
+
+ describe '#each_object_to_import', :clean_gitlab_redis_cache do
+ let(:branch_struct) { Struct.new(:protection, :name, :url, keyword_init: true) }
+ let(:protection_struct) { Struct.new(:enabled, keyword_init: true) }
+ let(:protected_branch) { branch_struct.new(name: 'main', protection: protection_struct.new(enabled: true)) }
+ let(:unprotected_branch) { branch_struct.new(name: 'staging', protection: protection_struct.new(enabled: false)) }
+ # when user has no admin rights on repo
+ let(:unknown_protection_branch) { branch_struct.new(name: 'development', protection: nil) }
+
+ let(:page_counter) { instance_double(Gitlab::GithubImport::PageCounter) }
+
+ before do
+ allow(client).to receive(:branches).with(project.import_source)
+ .and_return([protected_branch, unprotected_branch, unknown_protection_branch])
+ allow(client).to receive(:branch_protection)
+ .with(project.import_source, protected_branch.name).once
+ .and_return(github_protection_rule)
+ allow(Gitlab::GithubImport::ObjectCounter).to receive(:increment)
+ .with(project, :protected_branch, :fetched)
+ end
+
+ it 'imports each protected branch page by page' do
+ subject.each_object_to_import do |object|
+ expect(object).to eq github_protection_rule
+ end
+ expect(Gitlab::GithubImport::ObjectCounter).to have_received(:increment).once
+ end
+
+ context 'when protected branch is already processed' do
+ it "doesn't process this branch" do
+ subject.mark_as_imported(protected_branch)
+
+ subject.each_object_to_import {}
+ expect(Gitlab::GithubImport::ObjectCounter).not_to have_received(:increment)
+ end
+ end
+ end
+
+ describe '#importer_class' do
+ it { expect(importer.importer_class).to eq Gitlab::GithubImport::Importer::ProtectedBranchImporter }
+ end
+
+ describe '#representation_class' do
+ it { expect(importer.representation_class).to eq Gitlab::GithubImport::Representation::ProtectedBranch }
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(importer.sidekiq_worker_class).to eq Gitlab::GithubImport::ImportProtectedBranchWorker }
+ end
+
+ describe '#object_type' do
+ it { expect(importer.object_type).to eq :protected_branch }
+ end
+
+ describe '#collection_method' do
+ it { expect(importer.collection_method).to eq :protected_branches }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ it 'returns the ID of the given protected branch' do
+ expect(importer.id_for_already_imported_cache(github_protection_rule)).to eq('main')
+ end
+ end
+
+ describe '#collection_options' do
+ it 'returns an empty Hash' do
+ # For large projects (e.g. kubernetes/kubernetes) GitHub's API may produce
+ # HTTP 500 errors when using explicit sorting options, regardless of what
+ # order you sort in. Not using any sorting options at all allows us to
+ # work around this.
+ expect(importer.collection_options).to eq({})
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb b/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb
new file mode 100644
index 00000000000..4779f9c8982
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter do
+ subject(:importer) { described_class.new(release_attachments, project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:release) { create(:release, project: project, description: description) }
+ let(:release_attachments) do
+ Gitlab::GithubImport::Representation::ReleaseAttachments
+ .from_json_hash(release_db_id: release.id, description: release.description)
+ end
+
+ let(:doc_url) { 'https://github.com/nickname/public-test-repo/files/9020437/git-cheat-sheet.txt' }
+ let(:image_url) { 'https://user-images.githubusercontent.com/6833842/0cf366b61ef2.jpeg' }
+ let(:description) do
+ <<-TEXT.strip
+ Some text...
+
+ [special-doc](#{doc_url})
+ ![image.jpeg](#{image_url})
+ TEXT
+ end
+
+ describe '#execute' do
+ let(:downloader_stub) { instance_double(Gitlab::GithubImport::AttachmentsDownloader) }
+ let(:tmp_stub_doc) { Tempfile.create('attachment_download_test.txt') }
+ let(:tmp_stub_image) { Tempfile.create('image.jpeg') }
+
+ context 'when importing doc attachment' do
+ before do
+ allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(doc_url)
+ .and_return(downloader_stub)
+ allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(image_url)
+ .and_return(downloader_stub)
+ allow(downloader_stub).to receive(:perform).and_return(tmp_stub_doc, tmp_stub_image)
+ allow(downloader_stub).to receive(:delete).twice
+
+ allow(UploadService).to receive(:new)
+ .with(project, tmp_stub_doc, FileUploader).and_call_original
+ allow(UploadService).to receive(:new)
+ .with(project, tmp_stub_image, FileUploader).and_call_original
+ end
+
+ it 'updates release description with new attachment url' do
+ importer.execute
+
+ release.reload
+ expect(release.description).to start_with("Some text...\n\n [special-doc](/uploads/")
+ expect(release.description).to include('![image.jpeg](/uploads/')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb
new file mode 100644
index 00000000000..1aeb3462cd5
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter do
+ subject { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double(Gitlab::GithubImport::Client) }
+
+ describe '#each_object_to_import', :clean_gitlab_redis_cache do
+ let!(:release_1) { create(:release, project: project) }
+ let!(:release_2) { create(:release, project: project) }
+
+ it 'iterates each project release' do
+ list = []
+ subject.each_object_to_import do |object|
+ list << object
+ end
+ expect(list).to contain_exactly(release_1, release_2)
+ end
+
+ context 'when release is already processed' do
+ it "doesn't process this release" do
+ subject.mark_as_imported(release_1)
+
+ list = []
+ subject.each_object_to_import do |object|
+ list << object
+ end
+ expect(list).to contain_exactly(release_2)
+ end
+ end
+ end
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::ReleaseAttachments) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter) }
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(subject.sidekiq_worker_class).to eq(Gitlab::GithubImport::ImportReleaseAttachmentsWorker) }
+ end
+
+ describe '#collection_method' do
+ it { expect(subject.collection_method).to eq(:release_attachments) }
+ end
+
+ describe '#object_type' do
+ it { expect(subject.object_type).to eq(:release_attachment) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ let(:release) { build_stubbed(:release) }
+
+ it { expect(subject.id_for_already_imported_cache(release)).to eq(release.id) }
+ end
+
+ describe '#object_representation' do
+ let(:release) { build_stubbed(:release) }
+
+ it 'returns release attachments representation' do
+ representation = subject.object_representation(release)
+
+ expect(representation.class).to eq subject.representation_class
+ expect(representation.release_db_id).to eq release.id
+ expect(representation.description).to eq release.description
+ end
+ end
+end
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 f2730ba74ec..0b8b1922d94 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_wiki?' do
it 'returns true if the wiki should be imported' do
- repo = double(:repo, has_wiki: true)
+ repo = { has_wiki: true }
expect(client)
.to receive(:repository)
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'returns false if the GitHub wiki is disabled' do
- repo = double(:repo, has_wiki: false)
+ repo = { has_wiki: false }
expect(client)
.to receive(:repository)
@@ -78,7 +78,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'returns false if the wiki has already been imported' do
- repo = double(:repo, has_wiki: true)
+ repo = { has_wiki: true }
expect(client)
.to receive(:repository)
@@ -186,7 +186,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_repository' do
it 'imports the repository' do
- repo = double(:repo, default_branch: 'develop')
+ repo = { default_branch: 'develop' }
expect(client)
.to receive(:repository)
diff --git a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
index bb1ee79ad93..4ed01fd7e0b 100644
--- a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
@@ -6,7 +6,8 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
let(:client) { double }
let_it_be(:project) { create(:project, :import_started, import_source: 'http://somegithub.com') }
- let_it_be(:issue) { create(:issue, project: project) }
+
+ let!(:issuable) { create(:issue, project: project) }
subject { described_class.new(project, client, parallel: parallel) }
@@ -35,7 +36,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
end
describe '#page_counter_id' do
- it { expect(subject.page_counter_id(issue)).to eq("issues/#{issue.iid}/issue_timeline") }
+ it { expect(subject.page_counter_id(issuable)).to eq("issues/#{issuable.iid}/issue_timeline") }
end
describe '#id_for_already_imported_cache' do
@@ -51,6 +52,39 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
end
end
+ describe '#compose_associated_id!' do
+ let(:issuable) { build_stubbed(:issue, iid: 99) }
+ let(:event_resource) { Struct.new(:id, :event, :source, keyword_init: true) }
+
+ context 'when event type is cross-referenced' do
+ let(:event) do
+ source_resource = Struct.new(:issue, keyword_init: true)
+ issue_resource = Struct.new(:id, keyword_init: true)
+ event_resource.new(
+ id: nil,
+ event: 'cross-referenced',
+ source: source_resource.new(issue: issue_resource.new(id: '100500'))
+ )
+ end
+
+ it 'assigns event id' do
+ subject.compose_associated_id!(issuable, event)
+
+ expect(event.id).to eq 'cross-reference#99-in-100500'
+ end
+ end
+
+ context "when event type isn't cross-referenced" do
+ let(:event) { event_resource.new(id: nil, event: 'labeled') }
+
+ it "doesn't assign event id" do
+ subject.compose_associated_id!(issuable, event)
+
+ expect(event.id).to eq nil
+ end
+ end
+ end
+
describe '#each_object_to_import', :clean_gitlab_redis_cache do
let(:issue_event) do
struct = Struct.new(:id, :event, :created_at, :issue, keyword_init: true)
@@ -72,19 +106,37 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
.with(
:issue_timeline,
project.import_source,
- issue.iid,
+ issuable.iid,
{ state: 'all', sort: 'created', direction: 'asc', page: 1 }
).and_yield(page)
end
- it 'imports each issue event page by page' do
- counter = 0
- subject.each_object_to_import do |object|
- expect(object).to eq issue_event
- expect(issue_event.issue['number']).to eq issue.iid
- counter += 1
+ context 'with issues' do
+ it 'imports each issue event page by page' do
+ counter = 0
+ subject.each_object_to_import do |object|
+ expect(object).to eq issue_event
+ expect(issue_event.issue['number']).to eq issuable.iid
+ expect(issue_event.issue['pull_request']).to eq false
+ counter += 1
+ end
+ expect(counter).to eq 1
+ end
+ end
+
+ context 'with merge requests' do
+ let!(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it 'imports each merge request event page by page' do
+ counter = 0
+ subject.each_object_to_import do |object|
+ expect(object).to eq issue_event
+ expect(issue_event.issue['number']).to eq issuable.iid
+ expect(issue_event.issue['pull_request']).to eq true
+ counter += 1
+ end
+ expect(counter).to eq 1
end
- expect(counter).to eq 1
end
it 'triggers page number increment' do
@@ -103,7 +155,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
context 'when page is already processed' do
before do
page_counter = Gitlab::GithubImport::PageCounter.new(
- project, subject.page_counter_id(issue)
+ project, subject.page_counter_id(issuable)
)
page_counter.set(page.number)
end
diff --git a/spec/lib/gitlab/github_import/markdown_text_spec.rb b/spec/lib/gitlab/github_import/markdown_text_spec.rb
index ad45469a4c3..1da6bb06403 100644
--- a/spec/lib/gitlab/github_import/markdown_text_spec.rb
+++ b/spec/lib/gitlab/github_import/markdown_text_spec.rb
@@ -60,6 +60,34 @@ RSpec.describe Gitlab::GithubImport::MarkdownText do
end
end
+ describe '.fetch_attachment_urls' do
+ let(:image_extension) { described_class::MEDIA_TYPES.sample }
+ let(:image_attachment) do
+ "![special-image](https://user-images.githubusercontent.com/6833862/"\
+ "176685788-e7a93168-7ded-406a-82b5-eb1c56685a93.#{image_extension})"
+ end
+
+ let(:doc_extension) { described_class::DOC_TYPES.sample }
+ let(:doc_attachment) do
+ "[some-doc](https://github.com/nickname/public-test-repo/"\
+ "files/9020437/git-cheat-sheet.#{doc_extension})"
+ end
+
+ let(:text) do
+ <<-TEXT
+ Comment with an attachment
+ #{image_attachment}
+ #{FFaker::Lorem.sentence}
+ #{doc_attachment}
+ TEXT
+ end
+
+ it 'fetches attachment urls' do
+ expect(described_class.fetch_attachment_urls(text))
+ .to contain_exactly(image_attachment, doc_attachment)
+ end
+ end
+
describe '#to_s' do
it 'returns the text when the author was found' do
author = double(:author, login: 'Alice')
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index 738e7c88d7d..860bb60f3ed 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -15,6 +15,10 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
Class
end
+ def sidekiq_worker_class
+ Class
+ end
+
def object_type
:dummy
end
diff --git a/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb b/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
index bcb8575bdbf..5a24f929388 100644
--- a/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::DiffNotes::SuggestionFormatter do
it 'does nothing when there is any text before the suggestion tag' do
diff --git a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
index d40be0e841c..43f0198704f 100644
--- a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
@@ -1,21 +1,41 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::ExposeAttribute do
- it 'defines a getter method that returns an attribute value' do
- klass = Class.new do
+ let(:klass) do
+ Class.new do
include Gitlab::GithubImport::Representation::ExposeAttribute
expose_attribute :number
attr_reader :attributes
- def initialize
- @attributes = { number: 42 }
+ def initialize(attributes)
+ @attributes = attributes
+ end
+ end
+ end
+
+ it 'defines a getter method that returns an attribute value' do
+ expect(klass.new({ number: 42 }).number).to eq(42)
+ end
+
+ describe '#[]' do
+ it 'returns exposed attributes value using array notation' do
+ expect(klass.new({ number: 42 })[:number]).to eq(42)
+ end
+
+ context 'when attribute does not exist' do
+ it 'returns nil' do
+ expect(klass.new({})[:number]).to eq(nil)
end
end
- expect(klass.new.number).to eq(42)
+ context 'when attribute is not exposed' do
+ it 'returns nil' do
+ expect(klass.new({ not_exposed_attribute: 42 })[:not_exposed_attribute]).to eq(nil)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
index d3a98035e73..0256858ecf1 100644
--- a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_actor) { false }
it 'does not return such info' do
- expect(issue_event.actor).to eq nil
+ expect(issue_event.actor).to be_nil
end
end
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_label) { false }
it 'does not return such info' do
- expect(issue_event.label_title).to eq nil
+ expect(issue_event.label_title).to be_nil
end
end
@@ -72,8 +72,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { false }
it 'does not return such info' do
- expect(issue_event.old_title).to eq nil
- expect(issue_event.new_title).to eq nil
+ expect(issue_event.old_title).to be_nil
+ expect(issue_event.new_title).to be_nil
end
end
@@ -87,30 +87,47 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_milestone) { false }
it 'does not return such info' do
- expect(issue_event.milestone_title).to eq nil
+ expect(issue_event.milestone_title).to be_nil
end
end
- context 'when assignee and assigner data is present' do
- it 'includes assignee and assigner details' do
+ context 'when assignee data is present' do
+ it 'includes assignee details' do
expect(issue_event.assignee)
.to be_an_instance_of(Gitlab::GithubImport::Representation::User)
expect(issue_event.assignee.id).to eq(5)
expect(issue_event.assignee.login).to eq('tom')
+ end
+ end
+
+ context 'when assignee data is empty' do
+ let(:with_assignee) { false }
- expect(issue_event.assigner)
+ it 'does not return such info' do
+ expect(issue_event.assignee).to be_nil
+ end
+ end
+
+ context 'when requested_reviewer and review_requester data is present' do
+ it 'includes requested_reviewer and review_requester details' do
+ expect(issue_event.requested_reviewer)
.to be_an_instance_of(Gitlab::GithubImport::Representation::User)
- expect(issue_event.assigner.id).to eq(6)
- expect(issue_event.assigner.login).to eq('jerry')
+ expect(issue_event.requested_reviewer.id).to eq(6)
+ expect(issue_event.requested_reviewer.login).to eq('mickey')
+
+ expect(issue_event.review_requester)
+ .to be_an_instance_of(Gitlab::GithubImport::Representation::User)
+ expect(issue_event.review_requester.id).to eq(7)
+ expect(issue_event.review_requester.login).to eq('minnie')
end
end
- context 'when assignee and assigner data is empty' do
- let(:with_assignee) { false }
+ context 'when requested_reviewer and review_requester data is empty' do
+ let(:with_reviewer) { false }
it 'does not return such info' do
- expect(issue_event.assignee).to eq nil
- expect(issue_event.assigner).to eq nil
+ expect(issue_event.requested_reviewer).to be_nil
+ expect(issue_event.review_requester).to be_nil
end
end
@@ -148,7 +165,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:response) do
event_resource = Struct.new(
:id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone,
- :source, :assignee, :assigner, :issue, :created_at, :performed_via_github_app,
+ :source, :assignee, :requested_reviewer, :review_requester, :issue, :created_at,
+ :performed_via_github_app,
keyword_init: true
)
user_resource = Struct.new(:id, :login, keyword_init: true)
@@ -166,7 +184,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
milestone: with_milestone ? { title: 'milestone title' } : nil,
source: { type: 'issue', id: 123456 },
assignee: with_assignee ? user_resource.new(id: 5, login: 'tom') : nil,
- assigner: with_assignee ? user_resource.new(id: 6, login: 'jerry') : nil,
+ requested_reviewer: with_reviewer ? user_resource.new(id: 6, login: 'mickey') : nil,
+ review_requester: with_reviewer ? user_resource.new(id: 7, login: 'minnie') : nil,
issue: { 'number' => 2, 'pull_request' => pull_request },
created_at: '2022-04-26 18:30:53 UTC',
performed_via_github_app: nil
@@ -178,6 +197,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { true }
let(:with_milestone) { true }
let(:with_assignee) { true }
+ let(:with_reviewer) { true }
let(:pull_request) { nil }
it_behaves_like 'an IssueEvent' do
@@ -203,7 +223,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
'milestone_title' => (with_milestone ? 'milestone title' : nil),
'source' => { 'type' => 'issue', 'id' => 123456 },
'assignee' => (with_assignee ? { 'id' => 5, 'login' => 'tom' } : nil),
- 'assigner' => (with_assignee ? { 'id' => 6, 'login' => 'jerry' } : nil),
+ 'requested_reviewer' => (with_reviewer ? { 'id' => 6, 'login' => 'mickey' } : nil),
+ 'review_requester' => (with_reviewer ? { 'id' => 7, 'login' => 'minnie' } : nil),
'issue' => { 'number' => 2, 'pull_request' => pull_request },
'created_at' => '2022-04-26 18:30:53 UTC',
'performed_via_github_app' => nil
@@ -215,6 +236,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { true }
let(:with_milestone) { true }
let(:with_assignee) { true }
+ let(:with_reviewer) { true }
let(:pull_request) { nil }
let(:issue_event) { described_class.from_json_hash(hash) }
diff --git a/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb b/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
index b59ea513436..6663a7366a5 100644
--- a/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::LfsObject do
describe '#github_identifiers' do
diff --git a/spec/lib/gitlab/github_import/representation/note_spec.rb b/spec/lib/gitlab/github_import/representation/note_spec.rb
index 97addcc1c98..9f416eb3c02 100644
--- a/spec/lib/gitlab/github_import/representation/note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/note_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::Note do
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
diff --git a/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb b/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb
new file mode 100644
index 00000000000..e762dc469c1
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::ProtectedBranch do
+ shared_examples 'a ProtectedBranch rule' do
+ it 'returns an instance of ProtectedBranch' do
+ expect(protected_branch).to be_an_instance_of(described_class)
+ end
+
+ context 'with ProtectedBranch' do
+ it 'includes the protected branch ID (name)' do
+ expect(protected_branch.id).to eq 'main'
+ end
+
+ it 'includes the protected branch allow_force_pushes' do
+ expect(protected_branch.allow_force_pushes).to eq true
+ end
+ end
+ end
+
+ describe '.from_api_response' do
+ let(:response) do
+ response = Struct.new(:url, :allow_force_pushes, keyword_init: true)
+ allow_force_pushes = Struct.new(:enabled, keyword_init: true)
+ response.new(
+ url: 'https://example.com/branches/main/protection',
+ allow_force_pushes: allow_force_pushes.new(
+ enabled: true
+ )
+ )
+ end
+
+ it_behaves_like 'a ProtectedBranch rule' do
+ let(:protected_branch) { described_class.from_api_response(response) }
+ end
+ end
+
+ describe '.from_json_hash' do
+ it_behaves_like 'a ProtectedBranch rule' do
+ let(:hash) do
+ {
+ 'id' => 'main',
+ 'allow_force_pushes' => true
+ }
+ end
+
+ let(:protected_branch) { described_class.from_json_hash(hash) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb b/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
index f812fd85fbc..d6e7a8172f7 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::PullRequestReview do
let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc }
diff --git a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
index 925dba5b5a7..deb9535a845 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::PullRequest do
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
diff --git a/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb b/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb
new file mode 100644
index 00000000000..0ef9dad6a13
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::ReleaseAttachments do
+ shared_examples 'a Release attachments data' do
+ it 'returns an instance of ReleaseAttachments' do
+ expect(representation).to be_an_instance_of(described_class)
+ end
+
+ it 'includes release DB id' do
+ expect(representation.release_db_id).to eq 42
+ end
+
+ it 'includes release description' do
+ expect(representation.description).to eq 'Some text here..'
+ end
+ end
+
+ describe '.from_db_record' do
+ let(:release) { build_stubbed(:release, id: 42, description: 'Some text here..') }
+
+ it_behaves_like 'a Release attachments data' do
+ let(:representation) { described_class.from_db_record(release) }
+ end
+ end
+
+ describe '.from_json_hash' do
+ it_behaves_like 'a Release attachments data' do
+ let(:hash) do
+ {
+ 'release_db_id' => 42,
+ 'description' => 'Some text here..'
+ }
+ end
+
+ let(:representation) { described_class.from_json_hash(hash) }
+ end
+ end
+
+ describe '#github_identifiers' do
+ it 'returns a hash with needed identifiers' do
+ release_id = rand(100)
+ representation = described_class.new(release_db_id: release_id, description: 'text')
+
+ expect(representation.github_identifiers).to eq({ db_id: release_id })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
index 2770e5c5397..739c832025c 100644
--- a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::ToHash do
describe '#to_hash' do
diff --git a/spec/lib/gitlab/github_import/representation/user_spec.rb b/spec/lib/gitlab/github_import/representation/user_spec.rb
index 14204886e9b..d7219556ada 100644
--- a/spec/lib/gitlab/github_import/representation/user_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::User do
shared_examples 'a User' do
diff --git a/spec/lib/gitlab/github_import/representation_spec.rb b/spec/lib/gitlab/github_import/representation_spec.rb
index 58c10c4a775..9a0ef45fc1d 100644
--- a/spec/lib/gitlab/github_import/representation_spec.rb
+++ b/spec/lib/gitlab/github_import/representation_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation do
describe '.symbolize_hash' do
diff --git a/spec/lib/gitlab/github_import/user_finder_spec.rb b/spec/lib/gitlab/github_import/user_finder_spec.rb
index d85e298785c..8ebbff31f64 100644
--- a/spec/lib/gitlab/github_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/user_finder_spec.rb
@@ -68,10 +68,16 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
it_behaves_like 'user ID finder', :assignee
end
- context 'when the author_key parameter is :assigner' do
- let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', assigner: user) }
+ context 'when the author_key parameter is :requested_reviewer' do
+ let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', requested_reviewer: user) }
- it_behaves_like 'user ID finder', :assigner
+ it_behaves_like 'user ID finder', :requested_reviewer
+ end
+
+ context 'when the author_key parameter is :review_requester' do
+ let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', review_requester: user) }
+
+ it_behaves_like 'user ID finder', :review_requester
end
end
end
diff --git a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
index 487b19a98e0..5006d27c356 100644
--- a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
+++ b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp do
let(:log_entry) do
{
status: 200,
- time: {
+ time: {
total: 758.58,
db: 77.06,
view: 681.52
diff --git a/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
new file mode 100644
index 00000000000..5858986dfc8
--- /dev/null
+++ b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::Graphql::Limit::FieldCallCount do
+ include GraphqlHelpers
+
+ let(:field_args) { {} }
+ let(:owner) { fresh_object_type }
+ let(:field) do
+ ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner) do
+ extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
+ end
+ end
+
+ let(:query) do
+ GraphQL::Query.new(GitlabSchema)
+ end
+
+ def resolve_value
+ resolve_field(field, { value: 'foo' }, object_type: owner, query: query)
+ end
+
+ it 'allows the call' do
+ expect { resolve_value }.not_to raise_error
+ end
+
+ it 'executes the extension' do
+ expect(described_class).to receive(:new).and_call_original
+
+ resolve_value
+ end
+
+ it 'returns an error when the field is called multiple times' do
+ resolve_value
+
+ expect(resolve_value).to be_an_instance_of(Gitlab::Graphql::Errors::LimitError)
+ end
+
+ context 'when limit is not specified' do
+ let(:field) do
+ ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner) do
+ extension(::Gitlab::Graphql::Limit::FieldCallCount)
+ end
+ end
+
+ it 'returns an error' do
+ expect(resolve_value).to be_an_instance_of(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'when the field is not extended' do
+ let(:field) do
+ ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner)
+ end
+
+ it 'allows the call' do
+ expect { resolve_value }.not_to raise_error
+ end
+
+ it 'does not execute the extension' do
+ expect(described_class).not_to receive(:new)
+
+ resolve_value
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
index b54c618d8e0..bf09e98331f 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
@@ -49,6 +49,31 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
Gitlab::Json.parse(Base64Bp.urlsafe_decode64(cursor))
end
+ before do
+ stub_feature_flags(graphql_keyset_pagination_without_next_page_query: false)
+ end
+
+ it 'invokes an extra query for the next page check' do
+ arguments[:first] = 1
+
+ subject.nodes
+
+ count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
+ expect(count).to eq(1)
+ end
+
+ context 'when the relation is loaded' do
+ it 'invokes no extra query' do
+ allow(subject).to receive(:sliced_nodes).and_return(Project.all.to_a)
+ arguments[:first] = 1
+
+ subject.nodes
+
+ count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
+ expect(count).to eq(0)
+ end
+ end
+
describe "with generic keyset order support" do
let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
@@ -412,4 +437,382 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
end
end
end
+
+ # duplicated tests, remove with the removal of the graphql_keyset_pagination_without_next_page_query FF
+ context 'when the graphql_keyset_pagination_without_next_page_query is on' do
+ let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
+
+ before do
+ stub_feature_flags(graphql_keyset_pagination_without_next_page_query: true)
+ end
+
+ it 'does not invoke an extra query for the next page check' do
+ arguments[:first] = 1
+
+ subject.nodes
+
+ count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
+ expect(count).to eq(0)
+ end
+
+ it_behaves_like 'a connection with collection methods'
+
+ it_behaves_like 'a redactable connection' do
+ let_it_be(:projects) { create_list(:project, 2) }
+ let(:unwanted) { projects.second }
+ end
+
+ describe '#cursor_for' do
+ let(:project) { create(:project) }
+ let(:cursor) { connection.cursor_for(project) }
+
+ it 'returns an encoded ID' do
+ expect(decoded_cursor(cursor)).to eq('id' => project.id.to_s)
+ end
+
+ context 'when an order is specified' do
+ let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
+
+ it 'returns the encoded value of the order' do
+ expect(decoded_cursor(cursor)).to include('id' => project.id.to_s)
+ end
+ end
+
+ context 'when multiple orders are specified' do
+ let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_updated_at, column_order_created_at, column_order_id])) }
+
+ it 'returns the encoded value of the order' do
+ expect(decoded_cursor(cursor)).to include('updated_at' => project.updated_at.to_s(:inspect))
+ end
+ end
+ end
+
+ describe '#sliced_nodes' do
+ let(:projects) { create_list(:project, 4) }
+
+ context 'when before is passed' do
+ let(:arguments) { { before: encoded_cursor(projects[1]) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id_desc])) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..])
+ end
+ end
+ end
+
+ context 'when after is passed' do
+ let(:arguments) { { after: encoded_cursor(projects[1]) } }
+
+ it 'only returns the project before the selected one' do
+ expect(subject.sliced_nodes).to contain_exactly(*projects[2..])
+ end
+
+ context 'when the sort order is descending' do
+ let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id_desc])) }
+
+ it 'returns the correct nodes' do
+ expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ end
+ end
+ end
+
+ context 'when both before and after are passed' do
+ let(:arguments) do
+ {
+ after: encoded_cursor(projects[1]),
+ before: encoded_cursor(projects[3])
+ }
+ end
+
+ it 'returns the expected set' do
+ expect(subject.sliced_nodes).to contain_exactly(projects[2])
+ end
+ end
+
+ shared_examples 'nodes are in ascending order' do
+ context 'when no cursor is passed' do
+ let(:arguments) { {} }
+
+ it 'returns projects in ascending order' do
+ expect(subject.sliced_nodes).to eq(ascending_nodes)
+ end
+ end
+
+ context 'when before cursor value is not NULL' do
+ let(:arguments) { { before: encoded_cursor(ascending_nodes[2]) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq(ascending_nodes.first(2))
+ end
+ end
+
+ context 'when after cursor value is not NULL' do
+ let(:arguments) { { after: encoded_cursor(ascending_nodes[1]) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq(ascending_nodes.last(3))
+ end
+ end
+
+ context 'when before and after cursor' do
+ let(:arguments) { { before: encoded_cursor(ascending_nodes.last), after: encoded_cursor(ascending_nodes.first) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq(ascending_nodes[1..3])
+ end
+ end
+ end
+
+ shared_examples 'nodes are in descending order' do
+ context 'when no cursor is passed' do
+ let(:arguments) { {} }
+
+ it 'only returns projects in descending order' do
+ expect(subject.sliced_nodes).to eq(descending_nodes)
+ end
+ end
+
+ context 'when before cursor value is not NULL' do
+ let(:arguments) { { before: encoded_cursor(descending_nodes[2]) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq(descending_nodes.first(2))
+ end
+ end
+
+ context 'when after cursor value is not NULL' do
+ let(:arguments) { { after: encoded_cursor(descending_nodes[1]) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq(descending_nodes.last(3))
+ end
+ end
+
+ context 'when before and after cursor' do
+ let(:arguments) { { before: encoded_cursor(descending_nodes.last), after: encoded_cursor(descending_nodes.first) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq(descending_nodes[1..3])
+ end
+ end
+ end
+
+ context 'when multiple orders with nil values are defined' do
+ let_it_be(:project1) { create(:project, last_repository_check_at: 10.days.ago) } # Asc: project5 Desc: project3
+ let_it_be(:project2) { create(:project, last_repository_check_at: nil) } # Asc: project1 Desc: project1
+ let_it_be(:project3) { create(:project, last_repository_check_at: 5.days.ago) } # Asc: project3 Desc: project5
+ let_it_be(:project4) { create(:project, last_repository_check_at: nil) } # Asc: project2 Desc: project2
+ let_it_be(:project5) { create(:project, last_repository_check_at: 20.days.ago) } # Asc: project4 Desc: project4
+
+ context 'when ascending' do
+ let_it_be(:order) { Gitlab::Pagination::Keyset::Order.build([column_order_last_repo, column_order_id]) }
+ let_it_be(:nodes) { Project.order(order) }
+ let_it_be(:ascending_nodes) { [project5, project1, project3, project2, project4] }
+
+ it_behaves_like 'nodes are in ascending order'
+
+ context 'when before cursor value is NULL' do
+ let(:arguments) { { before: encoded_cursor(project4) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project5, project1, project3, project2])
+ end
+ end
+
+ context 'when after cursor value is NULL' do
+ let(:arguments) { { after: encoded_cursor(project2) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project4])
+ end
+ end
+ end
+
+ context 'when descending' do
+ let_it_be(:order) { Gitlab::Pagination::Keyset::Order.build([column_order_last_repo_desc, column_order_id]) }
+ let_it_be(:nodes) { Project.order(order) }
+ let_it_be(:descending_nodes) { [project3, project1, project5, project2, project4] }
+
+ it_behaves_like 'nodes are in descending order'
+
+ context 'when before cursor value is NULL' do
+ let(:arguments) { { before: encoded_cursor(project4) } }
+
+ it 'returns all projects before the cursor' do
+ expect(subject.sliced_nodes).to eq([project3, project1, project5, project2])
+ end
+ end
+
+ context 'when after cursor value is NULL' do
+ let(:arguments) { { after: encoded_cursor(project2) } }
+
+ it 'returns all projects after the cursor' do
+ expect(subject.sliced_nodes).to eq([project4])
+ end
+ end
+ end
+ end
+
+ context 'when ordering by similarity' do
+ let_it_be(:project1) { create(:project, name: 'test') }
+ let_it_be(:project2) { create(:project, name: 'testing') }
+ let_it_be(:project3) { create(:project, name: 'tests') }
+ let_it_be(:project4) { create(:project, name: 'testing stuff') }
+ let_it_be(:project5) { create(:project, name: 'test') }
+
+ let_it_be(:nodes) do
+ # Note: sorted_by_similarity_desc scope internally supports the generic keyset order.
+ Project.sorted_by_similarity_desc('test', include_in_select: true)
+ end
+
+ let_it_be(:descending_nodes) { nodes.to_a }
+
+ it_behaves_like 'nodes are in descending order'
+ end
+
+ context 'when an invalid cursor is provided' do
+ let(:arguments) { { before: Base64Bp.urlsafe_encode64('invalidcursor', padding: false) } }
+
+ it 'raises an error' do
+ expect { subject.sliced_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+ end
+
+ describe '#nodes' do
+ let_it_be(:all_nodes) { create_list(:project, 5) }
+
+ let(:paged_nodes) { subject.nodes }
+
+ it_behaves_like 'connection with paged nodes' do
+ let(:paged_nodes_size) { 3 }
+ end
+
+ context 'when both are passed' do
+ let(:arguments) { { first: 2, last: 2 } }
+
+ it 'raises an error' do
+ expect { paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'when primary key is not in original order' do
+ let(:nodes) { Project.order(last_repository_check_at: :desc) }
+
+ it 'is added to end' do
+ sliced = subject.sliced_nodes
+
+ order_sql = sliced.order_values.last.to_sql
+
+ expect(order_sql).to end_with(Project.arel_table[:id].desc.to_sql)
+ end
+ end
+
+ context 'when there is no primary key' do
+ before do
+ stub_const('NoPrimaryKey', Class.new(ActiveRecord::Base))
+ NoPrimaryKey.class_eval do
+ self.table_name = 'no_primary_key'
+ self.primary_key = nil
+ end
+ end
+
+ let(:nodes) { NoPrimaryKey.all }
+
+ it 'raises an error' do
+ expect(NoPrimaryKey.primary_key).to be_nil
+ expect { subject.sliced_nodes }.to raise_error(ArgumentError, 'Relation must have a primary key')
+ end
+ end
+ end
+
+ describe '#has_previous_page and #has_next_page' do
+ # using a list of 5 items with a max_page of 3
+ let_it_be(:project_list) { create_list(:project, 5) }
+ let_it_be(:nodes) { Project.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
+
+ context 'when default query' do
+ let(:arguments) { {} }
+
+ it 'has no previous, but a next' do
+ expect(subject.has_previous_page).to be_falsey
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ context 'when before is first item' do
+ let(:arguments) { { before: encoded_cursor(project_list.first) } }
+
+ it 'has no previous, but a next' do
+ expect(subject.has_previous_page).to be_falsey
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ describe 'using `before`' do
+ context 'when before is the last item' do
+ let(:arguments) { { before: encoded_cursor(project_list.last) } }
+
+ it 'has no previous, but a next' do
+ expect(subject.has_previous_page).to be_falsey
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ context 'when before and last specified' do
+ let(:arguments) { { before: encoded_cursor(project_list.last), last: 2 } }
+
+ it 'has a previous and a next' do
+ expect(subject.has_previous_page).to be_truthy
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ context 'when before and last does request all remaining nodes' do
+ let(:arguments) { { before: encoded_cursor(project_list[1]), last: 3 } }
+
+ it 'has a previous and a next' do
+ expect(subject.has_previous_page).to be_falsey
+ expect(subject.has_next_page).to be_truthy
+ expect(subject.nodes).to eq [project_list[0]]
+ end
+ end
+ end
+
+ describe 'using `after`' do
+ context 'when after is the first item' do
+ let(:arguments) { { after: encoded_cursor(project_list.first) } }
+
+ it 'has a previous, and a next' do
+ expect(subject.has_previous_page).to be_truthy
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ context 'when after and first specified' do
+ let(:arguments) { { after: encoded_cursor(project_list.first), first: 2 } }
+
+ it 'has a previous and a next' do
+ expect(subject.has_previous_page).to be_truthy
+ expect(subject.has_next_page).to be_truthy
+ end
+ end
+
+ context 'when before and last does request all remaining nodes' do
+ let(:arguments) { { after: encoded_cursor(project_list[2]), last: 3 } }
+
+ it 'has a previous but no next' do
+ expect(subject.has_previous_page).to be_truthy
+ expect(subject.has_next_page).to be_falsey
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
deleted file mode 100644
index 792cb03e8c7..00000000000
--- a/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Graphql::Pagination::Keyset::LastItems do
- let_it_be(:merge_request) { create(:merge_request) }
-
- let(:scope) { MergeRequest.order_merged_at_asc }
-
- subject { described_class.take_items(*args) }
-
- context 'when the `count` parameter is nil' do
- let(:args) { [scope, nil] }
-
- it 'returns a single record' do
- expect(subject).to eq(merge_request)
- end
- end
-
- context 'when the `count` parameter is given' do
- let(:args) { [scope, 1] }
-
- it 'returns an array' do
- expect(subject).to eq([merge_request])
- end
- end
-end
diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
index 7c346e3eb69..000b8eff661 100644
--- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::HealthChecks::GitalyCheck do
let(:result_class) { Gitlab::HealthChecks::Result }
@@ -14,20 +14,36 @@ RSpec.describe Gitlab::HealthChecks::GitalyCheck do
subject { described_class.readiness }
before do
- expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
+ expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(healthy_check)
end
context 'Gitaly server is up' do
- let(:gitaly_check) { double(check: { success: true }) }
+ before do
+ expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
+ end
+
+ let(:healthy_check) { double(check: { success: true }) }
+ let(:ready_check) { double(readiness_check: { success: true }) }
it { is_expected.to eq([result_class.new('gitaly_check', true, nil, shard: 'default')]) }
end
context 'Gitaly server is down' do
- let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
+ let(:healthy_check) { double(check: { success: false, message: 'Connection refused' }) }
it { is_expected.to eq([result_class.new('gitaly_check', false, 'Connection refused', shard: 'default')]) }
end
+
+ context 'Gitaly server is not ready' do
+ before do
+ expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
+ end
+
+ let(:healthy_check) { double(check: { success: true }) }
+ let(:ready_check) { double(readiness_check: { success: false, message: 'Clock is out of sync' }) }
+
+ it { is_expected.to match_array([result_class.new('gitaly_check', false, 'Clock is out of sync', shard: 'default')]) }
+ end
end
describe '#metrics' do
diff --git a/spec/lib/gitlab/health_checks/master_check_spec.rb b/spec/lib/gitlab/health_checks/master_check_spec.rb
index 287ebcec207..8a87b01c560 100644
--- a/spec/lib/gitlab/health_checks/master_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/master_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
require_relative './simple_check_shared'
RSpec.describe Gitlab::HealthChecks::MasterCheck do
diff --git a/spec/lib/gitlab/health_checks/probes/collection_spec.rb b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
index 741c45d953c..f1791375cea 100644
--- a/spec/lib/gitlab/health_checks/probes/collection_spec.rb
+++ b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
@@ -12,18 +12,16 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
let(:checks) do
[
Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::Redis::TraceChunksCheck,
- Gitlab::HealthChecks::Redis::RateLimitingCheck,
- Gitlab::HealthChecks::Redis::SessionsCheck,
+ *Gitlab::HealthChecks::Redis::ALL_INSTANCE_CHECKS,
Gitlab::HealthChecks::GitalyCheck
]
end
it 'responds with readiness checks data' do
+ expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
+ expect(service).to receive(:readiness_check).and_return({ success: true })
+ end
+
expect(subject.http_status).to eq(200)
expect(subject.json[:status]).to eq('ok')
@@ -37,8 +35,8 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
context 'when Redis fails' do
before do
- allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
- Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
+ allow(Gitlab::HealthChecks::Redis::SharedStateCheck).to receive(:readiness).and_return(
+ Gitlab::HealthChecks::Result.new('shared_state_check', false, "check error"))
end
it 'responds with failure' do
@@ -46,14 +44,14 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
expect(subject.json[:status]).to eq('failed')
expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
- expect(subject.json['redis_check']).to contain_exactly(
+ expect(subject.json['shared_state_check']).to contain_exactly(
status: 'failed', message: 'check error')
end
end
context 'when check raises exception not handled inside the check' do
before do
- expect(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_raise(
+ expect(Gitlab::HealthChecks::Redis::CacheCheck).to receive(:readiness).and_raise(
::Redis::CannotConnectError, 'Redis down')
end
diff --git a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
deleted file mode 100644
index c44bd2ed585..00000000000
--- a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::CacheCheck do
- include_examples 'simple_check', 'redis_cache_ping', 'RedisCache', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
deleted file mode 100644
index 3882e7db9d9..00000000000
--- a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::QueuesCheck do
- include_examples 'simple_check', 'redis_queues_ping', 'RedisQueues', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb b/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb
deleted file mode 100644
index 1521fc99cde..00000000000
--- a/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::RateLimitingCheck do
- include_examples 'simple_check', 'redis_rate_limiting_ping', 'RedisRateLimiting', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
deleted file mode 100644
index 145d573b6de..00000000000
--- a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::RedisCheck do
- include_examples 'simple_check', 'redis_ping', 'Redis', true
-end
diff --git a/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb b/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
deleted file mode 100644
index 82b3b33ec0a..00000000000
--- a/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::SessionsCheck do
- include_examples 'simple_check', 'redis_sessions_ping', 'RedisSessions', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb b/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
deleted file mode 100644
index 25917741a1c..00000000000
--- a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::SharedStateCheck do
- include_examples 'simple_check', 'redis_shared_state_ping', 'RedisSharedState', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb b/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb
deleted file mode 100644
index 5fb5232a4dd..00000000000
--- a/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::TraceChunksCheck do
- include_examples 'simple_check', 'redis_trace_chunks_ping', 'RedisTraceChunks', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis_spec.rb b/spec/lib/gitlab/health_checks/redis_spec.rb
new file mode 100644
index 00000000000..2460f57a9ec
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/redis_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+require 'spec_helper'
+require_relative './simple_check_shared'
+
+RSpec.describe Gitlab::HealthChecks::Redis do
+ describe "ALL_INSTANCE_CHECKS" do
+ subject { described_class::ALL_INSTANCE_CHECKS }
+
+ it { is_expected.to include(described_class::CacheCheck, described_class::QueuesCheck) }
+
+ it "contains a check for each redis instance" do
+ expect(subject.map(&:redis_instance_class_name)).to contain_exactly(*Gitlab::Redis::ALL_CLASSES)
+ end
+ end
+
+ describe 'all checks' do
+ described_class::ALL_INSTANCE_CHECKS.each do |check|
+ describe check do
+ include_examples 'simple_check',
+ "redis_#{check.redis_instance_class_name.store_name.underscore}_ping",
+ check.redis_instance_class_name.store_name,
+ 'PONG'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/i18n/metadata_entry_spec.rb b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
index 2f8816e62cc..fcdf3358570 100644
--- a/spec/lib/gitlab/i18n/metadata_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::I18n::MetadataEntry do
describe '#expected_forms' do
diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb
index f05346d07d3..df503e68cf1 100644
--- a/spec/lib/gitlab/i18n/translation_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#singular_translation' do
diff --git a/spec/lib/gitlab/import/merge_request_creator_spec.rb b/spec/lib/gitlab/import/merge_request_creator_spec.rb
index 9aedca40f1b..8f502216294 100644
--- a/spec/lib/gitlab/import/merge_request_creator_spec.rb
+++ b/spec/lib/gitlab/import/merge_request_creator_spec.rb
@@ -8,10 +8,13 @@ RSpec.describe Gitlab::Import::MergeRequestCreator do
subject { described_class.new(project) }
describe '#execute' do
+ let(:attributes) do
+ HashWithIndifferentAccess.new(merge_request.attributes.except('merge_params', 'suggested_reviewers'))
+ end
+
context 'merge request already exists' do
let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
let(:commits) { merge_request.merge_request_diffs.first.commits }
- let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
it 'updates the data' do
commits_count = commits.count
@@ -31,7 +34,6 @@ RSpec.describe Gitlab::Import::MergeRequestCreator do
context 'new merge request' do
let(:merge_request) { build(:merge_request, target_project: project, source_project: project) }
- let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
it 'creates a new merge request' do
attributes.delete(:id)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 9aec3271913..e270ca9ec6a 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -211,6 +211,8 @@ merge_requests:
- user_note_authors
- cleanup_schedule
- compliance_violations
+- created_environments
+- predictions
external_pull_requests:
- project
merge_request_diff:
@@ -315,6 +317,7 @@ statuses:
- user
- auto_canceled_by
- needs
+- ci_stage
variables:
- project
triggers:
@@ -654,11 +657,9 @@ search_data:
merge_request_assignees:
- merge_request
- assignee
-- updated_state_by
merge_request_reviewers:
- merge_request
- reviewer
-- updated_state_by
lfs_file_locks:
- user
project_badges:
@@ -821,3 +822,28 @@ service_desk_setting:
approvals:
- user
- merge_request
+resource_milestone_events:
+ - user
+ - issue
+ - merge_request
+ - milestone
+resource_state_events:
+ - user
+ - issue
+ - merge_request
+ - source_merge_request
+ - epic
+iteration:
+ - group
+ - iterations_cadence
+ - issues
+ - labels
+ - merge_requests
+resource_iteration_events:
+ - user
+ - issue
+ - merge_request
+ - iteration
+iterations_cadence:
+ - group
+ - iterations
diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
index 733be7fc226..272c2629b08 100644
--- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportExport::AttributeCleaner do
let(:relation_class) { double('relation_class').as_null_object }
diff --git a/spec/lib/gitlab/import_export/attributes_finder_spec.rb b/spec/lib/gitlab/import_export/attributes_finder_spec.rb
index 428d8d605ee..6536b895b2f 100644
--- a/spec/lib/gitlab/import_export/attributes_finder_spec.rb
+++ b/spec/lib/gitlab/import_export/attributes_finder_spec.rb
@@ -123,7 +123,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
is_expected.to match(
include: [{ merge_requests: {
include: [{ notes: { include: [{ author: { include: [] } }],
- preload: { author: nil } } }],
+ preload: { author: nil } } }],
preload: { notes: { author: nil } }
} }],
preload: { merge_requests: { notes: { author: nil } } }
@@ -132,7 +132,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with included attributes' do
setup_yaml(tree: { project: [:issues] },
- included_attributes: { issues: [:name, :description] })
+ included_attributes: { issues: [:name, :description] })
is_expected.to match(
include: [{ issues: { include: [],
@@ -143,7 +143,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with excluded attributes' do
setup_yaml(tree: { project: [:issues] },
- excluded_attributes: { issues: [:name] })
+ excluded_attributes: { issues: [:name] })
is_expected.to match(
include: [{ issues: { except: [:name],
@@ -154,8 +154,8 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with both excluded and included attributes' do
setup_yaml(tree: { project: [:issues] },
- excluded_attributes: { issues: [:name] },
- included_attributes: { issues: [:description] })
+ excluded_attributes: { issues: [:name] },
+ included_attributes: { issues: [:description] })
is_expected.to match(
include: [{ issues: { except: [:name],
@@ -167,7 +167,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with custom methods' do
setup_yaml(tree: { project: [:issues] },
- methods: { issues: [:name] })
+ methods: { issues: [:name] })
is_expected.to match(
include: [{ issues: { include: [],
diff --git a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
index 9f1b15aa049..4ee825c71b6 100644
--- a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
@@ -79,14 +79,14 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do
let(:relation_definition) { { 'notes' => {} } }
it 'saves valid subrelations and logs invalid subrelation' do
- expect(relation_object.notes).to receive(:<<).and_call_original
+ expect(relation_object.notes).to receive(:<<).twice.and_call_original
expect(Gitlab::Import::Logger)
.to receive(:info)
.with(
message: '[Project/Group Import] Invalid subrelation',
project_id: project.id,
relation_key: 'issues',
- error_messages: "Noteable can't be blank and Project does not match noteable project"
+ error_messages: "Project does not match noteable project"
)
saver.execute
@@ -94,9 +94,28 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do
issue = project.issues.last
import_failure = project.import_failures.last
+ expect(invalid_note.persisted?).to eq(false)
expect(issue.notes.count).to eq(5)
expect(import_failure.source).to eq('RelationObjectSaver#save!')
- expect(import_failure.exception_message).to eq("Noteable can't be blank and Project does not match noteable project")
+ expect(import_failure.exception_message).to eq('Project does not match noteable project')
+ end
+
+ context 'when invalid subrelation can still be persisted' do
+ let(:relation_key) { 'merge_requests' }
+ let(:relation_definition) { { 'approvals' => {} } }
+ let(:approval_1) { build(:approval, merge_request_id: nil, user: create(:user)) }
+ let(:approval_2) { build(:approval, merge_request_id: nil, user: create(:user)) }
+ let(:relation_object) { build(:merge_request, source_project: project, target_project: project, approvals: [approval_1, approval_2]) }
+
+ it 'saves the subrelation' do
+ expect(approval_1.valid?).to eq(false)
+ expect(Gitlab::Import::Logger).not_to receive(:info)
+
+ saver.execute
+
+ expect(project.merge_requests.first.approvals.count).to eq(2)
+ expect(project.merge_requests.first.approvals.first.persisted?).to eq(true)
+ end
end
context 'when importable is group' do
diff --git a/spec/lib/gitlab/import_export/config_spec.rb b/spec/lib/gitlab/import_export/config_spec.rb
index fcb48678b88..8f848af8bd3 100644
--- a/spec/lib/gitlab/import_export/config_spec.rb
+++ b/spec/lib/gitlab/import_export/config_spec.rb
@@ -21,10 +21,12 @@ RSpec.describe Gitlab::ImportExport::Config do
end
it 'parses default config' do
+ expected_keys = [:tree, :excluded_attributes, :included_attributes, :methods, :preloads, :export_reorders]
+ expected_keys << :include_if_exportable if ee
+
expect { subject }.not_to raise_error
expect(subject).to be_a(Hash)
- expect(subject.keys).to contain_exactly(
- :tree, :excluded_attributes, :included_attributes, :methods, :preloads, :export_reorders)
+ expect(subject.keys).to match_array(expected_keys)
end
end
end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 7b27f7183b0..5a75631ec4d 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -169,7 +169,7 @@ RSpec.describe Gitlab::ImportExport::FileImporter do
end
it 'skips validation' do
- expect(subject).to receive(:validate_decompressed_archive_size).never
+ expect(subject).not_to receive(:validate_decompressed_archive_size)
subject.import
end
diff --git a/spec/lib/gitlab/import_export/group/object_builder_spec.rb b/spec/lib/gitlab/import_export/group/object_builder_spec.rb
index 09f40199b31..25d9858dd4c 100644
--- a/spec/lib/gitlab/import_export/group/object_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/group/object_builder_spec.rb
@@ -6,9 +6,9 @@ RSpec.describe Gitlab::ImportExport::Group::ObjectBuilder do
let(:group) { create(:group) }
let(:base_attributes) do
{
- 'title' => 'title',
+ 'title' => 'title',
'description' => 'description',
- 'group' => group
+ 'group' => group
}
end
diff --git a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
index 2f1e2dd2db4..5e84284a060 100644
--- a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
@@ -33,15 +33,15 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer do
let(:relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Group::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Group::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: nil,
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Group::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Group::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: nil,
importable_attributes: attributes
)
end
diff --git a/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb b/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
index ab2c4cc2059..ed4368ba802 100644
--- a/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportExport::Json::LegacyWriter do
let(:path) { "#{Dir.tmpdir}/legacy_writer_spec/test.json" }
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 3088129a732..02ac8065c9f 100644
--- a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
@@ -32,18 +32,20 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
let(:hash) { { name: exportable.name, description: exportable.description }.stringify_keys }
let(:include) { [] }
let(:custom_orderer) { nil }
+ let(:include_if_exportable) { {} }
let(:relations_schema) do
{
only: [:name, :description],
include: include,
preload: { issues: nil },
- export_reorder: custom_orderer
+ export_reorder: custom_orderer,
+ include_if_exportable: include_if_exportable
}
end
subject do
- described_class.new(exportable, relations_schema, json_writer, exportable_path: exportable_path, logger: logger)
+ described_class.new(exportable, relations_schema, json_writer, exportable_path: exportable_path, logger: logger, current_user: user)
end
describe '#execute' do
@@ -210,11 +212,62 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
subject.execute
end
end
- end
- describe '.batch_size' do
- it 'returns default batch size' do
- expect(described_class.batch_size(exportable)).to eq(described_class::BATCH_SIZE)
+ describe 'conditional export of included associations' do
+ let(:include) do
+ [{ issues: { include: [{ label_links: { include: [:label] } }] } }]
+ end
+
+ let(:include_if_exportable) do
+ { issues: [:label_links] }
+ end
+
+ let_it_be(:label) { create(:label, project: exportable) }
+ let_it_be(:link) { create(:label_link, label: label, target: issue) }
+
+ context 'when association is exportable' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:exportable_association?).with(:label_links, current_user: user).and_return(true)
+ end
+ end
+
+ it 'includes exportable association' do
+ expected_issue = issue.to_json(include: [{ label_links: { include: [:label] } }])
+
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(expected_issue))
+
+ subject.execute
+ end
+ end
+
+ context 'when association is not exportable' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:exportable_association?).with(:label_links, current_user: user).and_return(false)
+ end
+ end
+
+ it 'filters out not exportable association' do
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(issue.to_json))
+
+ subject.execute
+ end
+ end
+
+ context 'when association does not respond to exportable_association?' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:respond_to?).with(:exportable_association?).and_return(false)
+ end
+ end
+
+ it 'filters out not exportable association' do
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(issue.to_json))
+
+ subject.execute
+ end
+ 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 0d372def8b0..c2c50751c3f 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
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::ImportExport::LegacyRelationTreeSaver do
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
- .with(exportable, tree, batch_size: Gitlab::ImportExport::Json::StreamingSerializer::BATCH_SIZE)
+ .with(exportable, tree)
.and_return(serializer)
expect(serializer).to receive(:execute)
diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
index b7b652005e9..ac646087a95 100644
--- a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
@@ -21,15 +21,15 @@ RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer do
let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) }
let(:relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Project::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: 'project',
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Project::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: 'project',
importable_attributes: attributes
)
end
diff --git a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
index 3dab84af744..d1fe9b80062 100644
--- a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
@@ -21,15 +21,15 @@ RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do
let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) }
let(:sample_data_relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: 'project',
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: 'project',
importable_attributes: attributes
)
end
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index 47d7555c8f4..299e107c881 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -192,10 +192,26 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
expect(Issue.find_by(title: 'Voluptatem').resource_label_events).not_to be_empty
end
+ it 'restores issue resource milestone events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores issue resource state events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_state_events).not_to be_empty
+ end
+
it 'restores merge requests resource label events' do
expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty
end
+ it 'restores merge request resource milestone events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores merge request resource state events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_state_events).not_to be_empty
+ end
+
it 'restores suggestion' do
note = Note.find_by("note LIKE 'Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum%'")
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 6cfc24a8996..e591cbd05a0 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -586,6 +586,7 @@ ProjectFeature:
- environments_access_level
- feature_flags_access_level
- releases_access_level
+- monitor_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
@@ -706,7 +707,7 @@ ProtectedEnvironment:
- name
- created_at
- updated_at
-ProtectedEnvironment::DeployAccessLevel:
+ProtectedEnvironments::DeployAccessLevel:
- id
- protected_environment_id
- access_level
@@ -917,3 +918,29 @@ Approval:
- user_id
- created_at
- updated_at
+ResourceMilestoneEvent:
+ - user_id
+ - action
+ - state
+ - created_at
+ResourceStateEvent:
+ - user_id
+ - created_at
+ - state
+ - source_commit
+ - close_after_error_tracking_resolve
+ - close_auto_resolve_prometheus_alert
+Iteration:
+ - created_at
+ - updated_at
+ - start_date
+ - due_date
+ - group_id
+ - iid
+ - description
+ResourceIterationEvent:
+ - user_id
+ - created_at
+ - action
+Iterations::Cadence:
+ - title
diff --git a/spec/lib/gitlab/import_formatter_spec.rb b/spec/lib/gitlab/import_formatter_spec.rb
index fbf00ab92d3..0feff61725b 100644
--- a/spec/lib/gitlab/import_formatter_spec.rb
+++ b/spec/lib/gitlab/import_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportFormatter do
let(:formatter) { Gitlab::ImportFormatter.new }
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index f42a109aa3a..41ffcece221 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -7,17 +7,17 @@ RSpec.describe Gitlab::ImportSources do
it 'returns a hash' do
expected =
{
- 'GitHub' => 'github',
- 'Bitbucket Cloud' => 'bitbucket',
- 'Bitbucket Server' => 'bitbucket_server',
- 'GitLab.com' => 'gitlab',
- 'Google Code' => 'google_code',
- 'FogBugz' => 'fogbugz',
+ 'GitHub' => 'github',
+ 'Bitbucket Cloud' => 'bitbucket',
+ 'Bitbucket Server' => 'bitbucket_server',
+ 'GitLab.com' => 'gitlab',
+ 'Google Code' => 'google_code',
+ 'FogBugz' => 'fogbugz',
'Repository by URL' => 'git',
- 'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea',
- 'Manifest file' => 'manifest',
- 'Phabricator' => 'phabricator'
+ 'GitLab export' => 'gitlab_project',
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest',
+ 'Phabricator' => 'phabricator'
}
expect(described_class.options).to eq(expected)
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index 72d201eed77..1545de6d8fd 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::IncomingEmail do
describe "self.enabled?" do
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
index 3a281574563..f2bf06236b9 100644
--- a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::InsecureKeyFingerprint do
let(:key) do
diff --git a/spec/lib/gitlab/instrumentation/redis_base_spec.rb b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
index a7e08b5a9bd..f9dd0c94c97 100644
--- a/spec/lib/gitlab/instrumentation/redis_base_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
@@ -65,6 +65,13 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
expect(instrumentation_class_b.get_request_count).to eq(2)
end
end
+
+ it 'increments by the given amount' do
+ instrumentation_class_a.increment_request_count(2)
+ instrumentation_class_a.increment_request_count(3)
+
+ expect(instrumentation_class_a.get_request_count).to eq(5)
+ end
end
describe '.increment_write_bytes' do
@@ -103,21 +110,21 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
context 'storage key overlapping' do
it 'keys do not overlap across storages' do
2.times do
- instrumentation_class_a.add_call_details(0.3, [:set])
- instrumentation_class_b.add_call_details(0.4, [:set])
+ instrumentation_class_a.add_call_details(0.3, [[:set]])
+ instrumentation_class_b.add_call_details(0.4, [[:set]])
end
expect(instrumentation_class_a.detail_store).to match(
[
- a_hash_including(cmd: :set, duration: 0.3, backtrace: an_instance_of(Array)),
- a_hash_including(cmd: :set, duration: 0.3, backtrace: an_instance_of(Array))
+ a_hash_including(commands: [[:set]], duration: 0.3, backtrace: an_instance_of(Array)),
+ a_hash_including(commands: [[:set]], duration: 0.3, backtrace: an_instance_of(Array))
]
)
expect(instrumentation_class_b.detail_store).to match(
[
- a_hash_including(cmd: :set, duration: 0.4, backtrace: an_instance_of(Array)),
- a_hash_including(cmd: :set, duration: 0.4, backtrace: an_instance_of(Array))
+ a_hash_including(commands: [[:set]], duration: 0.4, backtrace: an_instance_of(Array)),
+ a_hash_including(commands: [[:set]], duration: 0.4, backtrace: an_instance_of(Array))
]
)
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 09280402e2b..5b5516f100b 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -47,11 +47,22 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
let(:instrumentation_class) { Gitlab::Redis::SharedState.instrumentation_class }
it 'counts successful requests' do
- expect(instrumentation_class).to receive(:instance_count_request).and_call_original
+ expect(instrumentation_class).to receive(:instance_count_request).with(1).and_call_original
Gitlab::Redis::SharedState.with { |redis| redis.call(:get, 'foobar') }
end
+ it 'counts successful pipelined requests' do
+ expect(instrumentation_class).to receive(:instance_count_request).with(2).and_call_original
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foobar')
+ pipeline.call(:get, 'foobarbaz')
+ end
+ end
+ end
+
it 'counts exceptions' do
expect(instrumentation_class).to receive(:instance_count_exception)
.with(instance_of(Redis::CommandError)).and_call_original
@@ -84,6 +95,20 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
Gitlab::Redis::SharedState.with { |redis| redis.call(*command) }
end
end
+
+ context 'with pipelined commands' do
+ it 'measures requests that do not have blocking commands' do
+ expect(instrumentation_class).to receive(:instance_observe_duration).twice.with(a_value > 0)
+ .and_call_original
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foobar')
+ pipeline.call(:get, 'foobarbaz')
+ end
+ end
+ end
+ end
end
describe 'commands not in the apdex' do
@@ -109,6 +134,19 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
end
end
end
+
+ context 'with pipelined commands' do
+ it 'skips requests that have blocking commands', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373026' do
+ expect(instrumentation_class).not_to receive(:instance_observe_duration)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foo')
+ pipeline.call(:brpop, 'foobar', '0.01')
+ end
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_spec.rb b/spec/lib/gitlab/instrumentation/redis_spec.rb
index 900a079cdd2..c01d06c97b0 100644
--- a/spec/lib/gitlab/instrumentation/redis_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_spec.rb
@@ -71,14 +71,10 @@ RSpec.describe Gitlab::Instrumentation::Redis do
stub_storages(:detail_store, [details_row])
- expect(described_class.detail_store)
- .to contain_exactly(details_row.merge(storage: 'ActionCable'),
- details_row.merge(storage: 'Cache'),
- details_row.merge(storage: 'Queues'),
- details_row.merge(storage: 'SharedState'),
- details_row.merge(storage: 'TraceChunks'),
- details_row.merge(storage: 'RateLimiting'),
- details_row.merge(storage: 'Sessions'))
+ expected_detail_stores = Gitlab::Redis::ALL_CLASSES.map(&:store_name)
+ .map { |store_name| details_row.merge(storage: store_name) }
+ expected_detail_stores << details_row.merge(storage: 'ActionCable')
+ expect(described_class.detail_store).to contain_exactly(*expected_detail_stores)
end
end
end
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index 4fa9079144d..d5ff39767c4 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -140,13 +140,13 @@ RSpec.describe Gitlab::InstrumentationHelper do
subject
expect(payload).to include(db_replica_count: 0,
- db_replica_cached_count: 0,
- db_primary_count: 0,
- db_primary_cached_count: 0,
- db_primary_wal_count: 0,
- db_replica_wal_count: 0,
- db_primary_wal_cached_count: 0,
- db_replica_wal_cached_count: 0)
+ db_replica_cached_count: 0,
+ db_primary_count: 0,
+ db_primary_cached_count: 0,
+ db_primary_wal_count: 0,
+ db_replica_wal_count: 0,
+ db_primary_wal_cached_count: 0,
+ db_replica_wal_cached_count: 0)
end
context 'when replica caught up search was made' do
diff --git a/spec/lib/gitlab/internal_post_receive/response_spec.rb b/spec/lib/gitlab/internal_post_receive/response_spec.rb
index 135596c2de3..23ea5191486 100644
--- a/spec/lib/gitlab/internal_post_receive/response_spec.rb
+++ b/spec/lib/gitlab/internal_post_receive/response_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::InternalPostReceive::Response do
subject { described_class.new }
diff --git a/spec/lib/gitlab/jira/middleware_spec.rb b/spec/lib/gitlab/jira/middleware_spec.rb
index e7a79e40ac5..09cf67d0657 100644
--- a/spec/lib/gitlab/jira/middleware_spec.rb
+++ b/spec/lib/gitlab/jira/middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Jira::Middleware do
let(:app) { double(:app) }
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Jira::Middleware do
describe '#call' do
it 'adjusts HTTP_AUTHORIZATION env when request from Jira DVCS user agent' do
expect(app).to receive(:call).with({ 'HTTP_USER_AGENT' => jira_user_agent,
- 'HTTP_AUTHORIZATION' => 'Bearer hash-123' })
+ 'HTTP_AUTHORIZATION' => 'Bearer hash-123' })
middleware.call('HTTP_USER_AGENT' => jira_user_agent, 'HTTP_AUTHORIZATION' => 'token hash-123')
end
diff --git a/spec/lib/gitlab/jira_import/metadata_collector_spec.rb b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
index 51751c7b75f..d8e31d0ae22 100644
--- a/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
+++ b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::JiraImport::MetadataCollector do
describe '#execute' do
diff --git a/spec/lib/gitlab/jira_import_spec.rb b/spec/lib/gitlab/jira_import_spec.rb
index 972b0ab6ed1..c0c1a28b9ff 100644
--- a/spec/lib/gitlab/jira_import_spec.rb
+++ b/spec/lib/gitlab/jira_import_spec.rb
@@ -106,12 +106,6 @@ RSpec.describe Gitlab::JiraImport do
end
end
- describe '.jira_issue_cache_key' do
- it 'returns cache key for Jira issue imported to given project' do
- expect(described_class.jira_item_cache_key(project_id, 'DEMO-123', :issues)).to eq("jira-import/items-mapper/#{project_id}/issues/DEMO-123")
- end
- end
-
describe '.already_imported_cache_key' do
it 'returns cache key for already imported items' do
expect(described_class.already_imported_cache_key(:issues, project_id)).to eq("jira-importer/already-imported/#{project_id}/issues")
diff --git a/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb b/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
index a3f0fd9eb9b..698b88c9fa1 100644
--- a/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Helm::V2::Certificate do
describe '.generate_root' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
index 508808be1be..549fd862d2d 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::Cluster do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
index 43d4c46fda1..4734111a8ec 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::Context do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
index 3d6acc80823..9eb6ddcf30c 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::User do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
index 7d1f1aea291..869bba22a01 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Template do
let(:template) { described_class.new }
diff --git a/spec/lib/gitlab/lazy_spec.rb b/spec/lib/gitlab/lazy_spec.rb
index 3e929cf200a..92907081867 100644
--- a/spec/lib/gitlab/lazy_spec.rb
+++ b/spec/lib/gitlab/lazy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Lazy do
let(:dummy) { double(:dummy) }
diff --git a/spec/lib/gitlab/legacy_github_import/client_spec.rb b/spec/lib/gitlab/legacy_github_import/client_spec.rb
index 83ba5858d81..08679b7e9f1 100644
--- a/spec/lib/gitlab/legacy_github_import/client_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/client_spec.rb
@@ -98,6 +98,30 @@ RSpec.describe Gitlab::LegacyGithubImport::Client do
end
end
+ describe '#repository' do
+ it 'returns repository data as a hash' do
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/repositories/1')
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: { 'Content-Type' => 'application/json' })
+
+ expect(client.repository(1)).to eq({ id: 1 })
+ end
+ end
+
+ describe '#repos' do
+ it 'returns the user\'s repositories as a hash' do
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/user/repos')
+ .to_return(status: 200, body: [{ id: 1 }, { id: 2 }].to_json, headers: { 'Content-Type' => 'application/json' })
+
+ expect(client.repos).to match_array([{ id: 1 }, { id: 2 }])
+ end
+ end
+
context 'github rate limit' do
it 'does not raise error when rate limit is disabled' do
stub_request(:get, /api.github.com/)
diff --git a/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
index a5d2e00890b..a285a5820a2 100644
--- a/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::IssuableFormatter do
let(:raw_data) do
diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
index 68f1c214cef..17ecd183ac9 100644
--- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
@@ -7,15 +7,15 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
let(:namespace) { create(:group) }
let(:repo) do
- ActiveSupport::InheritableOptions.new(
+ {
login: 'vim',
name: 'vim',
full_name: 'asd/vim',
clone_url: 'https://gitlab.com/asd/vim.git'
- )
+ }
end
- subject(:service) { described_class.new(repo, repo.name, namespace, user, github_access_token: 'asdffg') }
+ subject(:service) { described_class.new(repo, repo[:name], namespace, user, github_access_token: 'asdffg') }
before do
namespace.add_owner(user)
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project is private' do
it 'sets project visibility to private' do
- repo.private = true
+ repo[:private] = true
project = service.execute
@@ -50,17 +50,19 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project is public' do
it 'sets project visibility to namespace visibility level' do
- repo.private = false
+ repo[:private] = false
+
project = service.execute
expect(project.visibility_level).to eq(namespace.visibility_level)
end
context 'when importing into a user namespace' do
- subject(:service) { described_class.new(repo, repo.name, user.namespace, user, github_access_token: 'asdffg') }
+ subject(:service) { described_class.new(repo, repo[:name], user.namespace, user, github_access_token: 'asdffg') }
it 'sets project visibility to user namespace visibility level' do
- repo.private = false
+ repo[:private] = false
+
project = service.execute
expect(project.visibility_level).to eq(user.namespace.visibility_level)
@@ -76,7 +78,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
end
it 'sets project visibility to the default project visibility' do
- repo.private = true
+ repo[:private] = true
project = service.execute
@@ -91,7 +93,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
end
it 'sets project visibility to the default project visibility' do
- repo.private = false
+ repo[:private] = false
project = service.execute
@@ -102,7 +104,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project has wiki' do
it 'does not create the wiki repository' do
- allow(repo).to receive(:has_wiki?).and_return(true)
+ repo[:has_wiki] = true
project = service.execute
@@ -112,7 +114,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project does not have wiki' do
it 'creates the wiki repository' do
- allow(repo).to receive(:has_wiki?).and_return(false)
+ repo[:has_wiki] = false
project = service.execute
diff --git a/spec/lib/gitlab/loop_helpers_spec.rb b/spec/lib/gitlab/loop_helpers_spec.rb
index 0535cb6068c..bb328e3dcce 100644
--- a/spec/lib/gitlab/loop_helpers_spec.rb
+++ b/spec/lib/gitlab/loop_helpers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::LoopHelpers do
let(:class_instance) { (Class.new { include ::Gitlab::LoopHelpers }).new }
diff --git a/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb b/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
index a2286415e96..4b9ea1c15a9 100644
--- a/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
+++ b/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
@@ -20,18 +20,43 @@ RSpec.describe Gitlab::Mailgun::WebhookProcessors::FailureLogger do
context 'on permanent failure' do
let(:processor) { described_class.new(base_payload.merge({ 'severity' => 'permanent' })) }
- it 'logs the failure immediately' do
- expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
- event: 'email_delivery_failure',
- mailgun_event_id: base_payload['id'],
- recipient: base_payload['recipient'],
- failure_type: 'permanent',
- failure_reason: base_payload['reason'],
- failure_code: base_payload['delivery-status']['code'],
- failure_message: base_payload['delivery-status']['message']
- )
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(permanent_email_failure: { threshold: 1, interval: 1.minute })
+ end
- processor.execute
+ context 'when threshold is not exceeded' do
+ it 'increments counter but does not log the failure' do
+ expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(
+ :permanent_email_failure, scope: 'recipient@gitlab.com'
+ ).and_call_original
+ expect(Gitlab::ErrorTracking::Logger).not_to receive(:error)
+
+ processor.execute
+ end
+ end
+
+ context 'when threshold is exceeded' do
+ before do
+ processor.execute
+ end
+
+ it 'increments counter and logs the failure' do
+ expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(
+ :permanent_email_failure, scope: 'recipient@gitlab.com'
+ ).and_call_original
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
+ event: 'email_delivery_failure',
+ mailgun_event_id: base_payload['id'],
+ recipient: base_payload['recipient'],
+ failure_type: 'permanent',
+ failure_reason: base_payload['reason'],
+ failure_code: base_payload['delivery-status']['code'],
+ failure_message: base_payload['delivery-status']['message']
+ )
+
+ processor.execute
+ end
end
end
diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
index 81910773dfa..57f2b1cfd96 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -174,8 +174,8 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
expect(thing).to receive(:update_columns)
.with({ "title_html" => updated_html,
- "description_html" => "",
- "cached_markdown_version" => cache_version })
+ "description_html" => "",
+ "cached_markdown_version" => cache_version })
thing.refresh_markdown_cache!
end
diff --git a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
index b5d458f15fc..8e75009099d 100644
--- a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
@@ -62,7 +62,13 @@ RSpec.describe Gitlab::MarkdownCache::Redis::Extension, :clean_gitlab_redis_cach
it 'does not preload the markdown twice' do
expect(Gitlab::MarkdownCache::Redis::Store).to receive(:bulk_read).and_call_original
- expect(Gitlab::Redis::Cache).to receive(:with).twice.and_call_original
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(:mapped_hmget).once.and_call_original
+ end
+ end
klass.preload_markdown_cache!([thing])
diff --git a/spec/lib/gitlab/markup_helper_spec.rb b/spec/lib/gitlab/markup_helper_spec.rb
index bf5415ba1d7..2bffd029568 100644
--- a/spec/lib/gitlab/markup_helper_spec.rb
+++ b/spec/lib/gitlab/markup_helper_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::MarkupHelper do
describe '#markup?' do
diff --git a/spec/lib/gitlab/memory/jemalloc_spec.rb b/spec/lib/gitlab/memory/jemalloc_spec.rb
index 482ac6e5802..414d6017534 100644
--- a/spec/lib/gitlab/memory/jemalloc_spec.rb
+++ b/spec/lib/gitlab/memory/jemalloc_spec.rb
@@ -1,12 +1,15 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tmpdir'
RSpec.describe Gitlab::Memory::Jemalloc do
let(:outdir) { Dir.mktmpdir }
+ let(:tmp_outdir) { Dir.mktmpdir }
after do
FileUtils.rm_f(outdir)
+ FileUtils.rm_f(tmp_outdir)
end
context 'when jemalloc is loaded' do
@@ -28,7 +31,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'writes stats JSON file' do
- file_path = described_class.dump_stats(path: outdir, format: format)
+ file_path = described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
file = Dir.entries(outdir).find { |e| e.match(/jemalloc_stats\.#{$$}\.\d+\.json$/) }
expect(file).not_to be_nil
@@ -55,7 +58,8 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
shared_examples 'writes stats text file' do |filename_label, filename_pattern|
it do
- described_class.dump_stats(path: outdir, format: format, filename_label: filename_label)
+ described_class.dump_stats(
+ path: outdir, tmp_dir: tmp_outdir, format: format, filename_label: filename_label)
file = Dir.entries(outdir).find { |e| e.match(filename_pattern) }
expect(file).not_to be_nil
@@ -87,7 +91,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'raises an error' do
expect do
- described_class.dump_stats(path: outdir, format: format)
+ described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
end.to raise_error(/format must be one of/)
end
end
@@ -109,7 +113,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
it 'does nothing' do
stub_env('LD_PRELOAD', nil)
- described_class.dump_stats(path: outdir)
+ described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir)
expect(Dir.empty?(outdir)).to be(true)
end
diff --git a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
index 53fae48776b..b327a40bc2c 100644
--- a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
+++ b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
@@ -3,14 +3,19 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reports::JemallocStats do
- let(:reports_dir) { '/empty-dir' }
- let(:jemalloc_stats) { described_class.new(reports_path: reports_dir) }
+ let_it_be(:outdir) { Dir.mktmpdir }
+
+ let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
+
+ after do
+ FileUtils.rm_f(outdir)
+ end
describe '.run' do
context 'when :report_jemalloc_stats ops FF is enabled' do
let(:worker_id) { 'puma_1' }
let(:report_name) { 'report.json' }
- let(:report_path) { File.join(reports_dir, report_name) }
+ let(:report_path) { File.join(outdir, report_name) }
before do
allow(Prometheus::PidProvider).to receive(:worker_id).and_return(worker_id)
@@ -18,14 +23,16 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
it 'invokes Jemalloc.dump_stats and returns file path' do
expect(Gitlab::Memory::Jemalloc)
- .to receive(:dump_stats).with(path: reports_dir, filename_label: worker_id).and_return(report_path)
+ .to receive(:dump_stats)
+ .with(path: outdir,
+ tmp_dir: File.join(outdir, '/tmp'),
+ filename_label: worker_id)
+ .and_return(report_path)
expect(jemalloc_stats.run).to eq(report_path)
end
describe 'reports cleanup' do
- let_it_be(:outdir) { Dir.mktmpdir }
-
let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
before do
@@ -33,10 +40,6 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
allow(Gitlab::Memory::Jemalloc).to receive(:dump_stats)
end
- after do
- FileUtils.rm_f(outdir)
- end
-
context 'when number of reports exceeds `max_reports_stored`' do
let_it_be(:reports) do
now = Time.current
diff --git a/spec/lib/gitlab/memory/watchdog_spec.rb b/spec/lib/gitlab/memory/watchdog_spec.rb
index 010f6884df3..beb49660022 100644
--- a/spec/lib/gitlab/memory/watchdog_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
+require_relative '../../../../lib/gitlab/cluster/lifecycle_events'
RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'watchdog' do
@@ -8,23 +9,31 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
let(:handler) { instance_double(described_class::NullHandler) }
let(:heap_frag_limit_gauge) { instance_double(::Prometheus::Client::Gauge) }
- let(:heap_frag_violations_counter) { instance_double(::Prometheus::Client::Counter) }
- let(:heap_frag_violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:violations_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
let(:sleep_time) { 0.1 }
let(:max_heap_fragmentation) { 0.2 }
+ let(:max_mem_growth) { 2 }
+
+ # Defaults that will not trigger any events.
+ let(:fragmentation) { 0 }
+ let(:worker_memory) { 0 }
+ let(:primary_memory) { 0 }
+ let(:max_strikes) { 0 }
# Tests should set this to control the number of loop iterations in `call`.
let(:watchdog_iterations) { 1 }
subject(:watchdog) do
described_class.new(handler: handler, logger: logger, sleep_time_seconds: sleep_time,
- max_strikes: max_strikes, max_heap_fragmentation: max_heap_fragmentation).tap do |instance|
+ max_strikes: max_strikes, max_mem_growth: max_mem_growth,
+ max_heap_fragmentation: max_heap_fragmentation).tap do |instance|
# We need to defuse `sleep` and stop the internal loop after N iterations.
iterations = 0
- expect(instance).to receive(:sleep) do
- instance.stop if (iterations += 1) >= watchdog_iterations
- end.at_most(watchdog_iterations)
+ allow(instance).to receive(:sleep) do
+ instance.stop if (iterations += 1) > watchdog_iterations
+ end
end
end
@@ -33,34 +42,35 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
.with(:gitlab_memwd_heap_frag_limit, anything)
.and_return(heap_frag_limit_gauge)
allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_heap_frag_violations_total, anything, anything)
- .and_return(heap_frag_violations_counter)
+ .with(:gitlab_memwd_violations_total, anything, anything)
+ .and_return(violations_counter)
allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_heap_frag_violations_handled_total, anything, anything)
- .and_return(heap_frag_violations_handled_counter)
+ .with(:gitlab_memwd_violations_handled_total, anything, anything)
+ .and_return(violations_handled_counter)
allow(heap_frag_limit_gauge).to receive(:set)
- allow(heap_frag_violations_counter).to receive(:increment)
- allow(heap_frag_violations_handled_counter).to receive(:increment)
+ allow(violations_counter).to receive(:increment)
+ allow(violations_handled_counter).to receive(:increment)
end
before do
stub_prometheus_metrics
- allow(handler).to receive(:on_high_heap_fragmentation).and_return(true)
+ allow(handler).to receive(:call).and_return(true)
allow(logger).to receive(:warn)
allow(logger).to receive(:info)
allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(fragmentation)
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return({ uss: worker_memory })
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
+ pid: Gitlab::Cluster::PRIMARY_PID
+ ).and_return({ uss: primary_memory })
allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
end
context 'when created' do
- let(:fragmentation) { 0 }
- let(:max_strikes) { 0 }
-
it 'sets the heap fragmentation limit gauge' do
expect(heap_frag_limit_gauge).to receive(:set).with({}, max_heap_fragmentation)
@@ -71,7 +81,8 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
it 'initializes with defaults' do
watchdog = described_class.new(handler: handler, logger: logger)
- expect(watchdog.max_heap_fragmentation).to eq(described_class::DEFAULT_HEAP_FRAG_THRESHOLD)
+ expect(watchdog.max_heap_fragmentation).to eq(described_class::DEFAULT_MAX_HEAP_FRAG)
+ expect(watchdog.max_mem_growth).to eq(described_class::DEFAULT_MAX_MEM_GROWTH)
expect(watchdog.max_strikes).to eq(described_class::DEFAULT_MAX_STRIKES)
expect(watchdog.sleep_time_seconds).to eq(described_class::DEFAULT_SLEEP_TIME_SECONDS)
end
@@ -82,6 +93,7 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 1)
stub_env('GITLAB_MEMWD_MAX_STRIKES', 2)
stub_env('GITLAB_MEMWD_SLEEP_TIME_SEC', 3)
+ stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 4)
end
it 'initializes with these settings' do
@@ -90,30 +102,17 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
expect(watchdog.max_heap_fragmentation).to eq(1)
expect(watchdog.max_strikes).to eq(2)
expect(watchdog.sleep_time_seconds).to eq(3)
+ expect(watchdog.max_mem_growth).to eq(4)
end
end
end
- context 'when process does not exceed heap fragmentation threshold' do
- let(:fragmentation) { max_heap_fragmentation - 0.1 }
- let(:max_strikes) { 0 } # To rule out that we were granting too many strikes.
-
- it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
-
- watchdog.call
- end
- end
-
- context 'when process exceeds heap fragmentation threshold permanently' do
- let(:fragmentation) { max_heap_fragmentation + 0.1 }
- let(:max_strikes) { 3 }
-
+ shared_examples 'has strikes left' do |stat|
context 'when process has not exceeded allowed number of strikes' do
let(:watchdog_iterations) { max_strikes }
it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
+ expect(handler).not_to receive(:call)
watchdog.call
end
@@ -125,119 +124,228 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
end
it 'increments the violations counter' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
+ expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
watchdog.call
end
it 'does not increment violations handled counter' do
- expect(heap_frag_violations_handled_counter).not_to receive(:increment)
+ expect(violations_handled_counter).not_to receive(:increment)
watchdog.call
end
end
+ end
+
+ shared_examples 'no strikes left' do |stat|
+ it 'signals the handler and resets strike counter' do
+ expect(handler).to receive(:call).and_return(true)
+
+ watchdog.call
+
+ expect(watchdog.strikes(stat.to_sym)).to eq(0)
+ end
+
+ it 'increments both the violations and violations handled counters' do
+ expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
+ expect(violations_handled_counter).to receive(:increment).with(reason: stat)
+
+ watchdog.call
+ end
- context 'when process exceeds the allowed number of strikes' do
- let(:watchdog_iterations) { max_strikes + 1 }
+ context 'when enforce_memory_watchdog ops toggle is off' do
+ before do
+ stub_feature_flags(enforce_memory_watchdog: false)
+ end
- it 'signals the handler and resets strike counter' do
- expect(handler).to receive(:on_high_heap_fragmentation).and_return(true)
+ it 'always uses the NullHandler' do
+ expect(handler).not_to receive(:call)
+ expect(described_class::NullHandler.instance).to receive(:call).and_return(true)
watchdog.call
+ end
+ end
- expect(watchdog.strikes).to eq(0)
+ context 'when handler result is true' do
+ it 'considers the event handled and stops itself' do
+ expect(handler).to receive(:call).once.and_return(true)
+ expect(logger).to receive(:info).with(hash_including(message: 'stopped'))
+
+ watchdog.call
end
+ end
- it 'logs the event' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
- expect(logger).to receive(:warn).with({
- message: 'heap fragmentation limit exceeded',
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
- memwd_sleep_time_s: sleep_time,
- memwd_max_heap_frag: max_heap_fragmentation,
- memwd_cur_heap_frag: fragmentation,
- memwd_max_strikes: max_strikes,
- memwd_cur_strikes: max_strikes + 1,
- memwd_rss_bytes: 1024
- })
+ context 'when handler result is false' do
+ let(:max_strikes) { 0 } # to make sure the handler fires each iteration
+ let(:watchdog_iterations) { 3 }
+
+ it 'keeps running' do
+ expect(violations_counter).to receive(:increment).exactly(watchdog_iterations)
+ expect(violations_handled_counter).to receive(:increment).exactly(watchdog_iterations)
+ # Return true the third time to terminate the daemon.
+ expect(handler).to receive(:call).and_return(false, false, true)
watchdog.call
end
+ end
+ end
+
+ context 'when monitoring memory growth' do
+ let(:primary_memory) { 2048 }
- it 'increments both the violations and violations handled counters' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
- expect(heap_frag_violations_handled_counter).to receive(:increment)
+ context 'when process does not exceed threshold' do
+ let(:worker_memory) { max_mem_growth * primary_memory - 1 }
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
watchdog.call
end
+ end
- context 'when enforce_memory_watchdog ops toggle is off' do
- before do
- stub_feature_flags(enforce_memory_watchdog: false)
- end
+ context 'when process exceeds threshold permanently' do
+ let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:max_strikes) { 3 }
+
+ it_behaves_like 'has strikes left', 'mem_growth'
+
+ context 'when process exceeds the allowed number of strikes' do
+ let(:watchdog_iterations) { max_strikes + 1 }
- it 'always uses the NullHandler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
- expect(described_class::NullHandler.instance).to(
- receive(:on_high_heap_fragmentation).with(fragmentation).and_return(true)
- )
+ it_behaves_like 'no strikes left', 'mem_growth'
+
+ it 'only reads reference memory once' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss)
+ .with(pid: Gitlab::Cluster::PRIMARY_PID)
+ .once
watchdog.call
end
- end
- context 'when handler result is true' do
- it 'considers the event handled and stops itself' do
- expect(handler).to receive(:on_high_heap_fragmentation).once.and_return(true)
- expect(logger).to receive(:info).with(hash_including(message: 'stopped'))
+ it 'logs the event' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
+ expect(logger).to receive(:warn).with({
+ message: 'memory limit exceeded',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
+ memwd_sleep_time_s: sleep_time,
+ memwd_max_uss_bytes: max_mem_growth * primary_memory,
+ memwd_ref_uss_bytes: primary_memory,
+ memwd_uss_bytes: worker_memory,
+ memwd_rss_bytes: 1024,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: max_strikes + 1
+ })
watchdog.call
end
end
+ end
+
+ context 'when process exceeds threshold temporarily' do
+ let(:worker_memory) { max_mem_growth * primary_memory }
+ let(:max_strikes) { 1 }
+ let(:watchdog_iterations) { 4 }
+
+ before do
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return(
+ { uss: worker_memory - 0.1 },
+ { uss: worker_memory + 0.2 },
+ { uss: worker_memory - 0.1 },
+ { uss: worker_memory + 0.1 }
+ )
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
+ pid: Gitlab::Cluster::PRIMARY_PID
+ ).and_return({ uss: primary_memory })
+ end
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
+ end
+ end
+
+ context 'when monitoring heap fragmentation' do
+ context 'when process does not exceed threshold' do
+ let(:fragmentation) { max_heap_fragmentation - 0.1 }
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
+ end
+
+ context 'when process exceeds threshold permanently' do
+ let(:fragmentation) { max_heap_fragmentation + 0.1 }
+ let(:max_strikes) { 3 }
- context 'when handler result is false' do
- let(:max_strikes) { 0 } # to make sure the handler fires each iteration
- let(:watchdog_iterations) { 3 }
+ it_behaves_like 'has strikes left', 'heap_frag'
- it 'keeps running' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
- expect(heap_frag_violations_handled_counter).to receive(:increment).exactly(watchdog_iterations)
- # Return true the third time to terminate the daemon.
- expect(handler).to receive(:on_high_heap_fragmentation).and_return(false, false, true)
+ context 'when process exceeds the allowed number of strikes' do
+ let(:watchdog_iterations) { max_strikes + 1 }
+
+ it_behaves_like 'no strikes left', 'heap_frag'
+
+ it 'logs the event' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
+ expect(logger).to receive(:warn).with({
+ message: 'heap fragmentation limit exceeded',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
+ memwd_sleep_time_s: sleep_time,
+ memwd_max_heap_frag: max_heap_fragmentation,
+ memwd_cur_heap_frag: fragmentation,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: max_strikes + 1,
+ memwd_rss_bytes: 1024
+ })
watchdog.call
end
end
end
- end
- context 'when process exceeds heap fragmentation threshold temporarily' do
- let(:fragmentation) { max_heap_fragmentation }
- let(:max_strikes) { 1 }
- let(:watchdog_iterations) { 4 }
+ context 'when process exceeds threshold temporarily' do
+ let(:fragmentation) { max_heap_fragmentation }
+ let(:max_strikes) { 1 }
+ let(:watchdog_iterations) { 4 }
- before do
- allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(
- fragmentation - 0.1,
- fragmentation + 0.2,
- fragmentation - 0.1,
- fragmentation + 0.1
- )
+ before do
+ allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(
+ fragmentation - 0.1,
+ fragmentation + 0.2,
+ fragmentation - 0.1,
+ fragmentation + 0.1
+ )
+ end
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
end
+ end
- it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
+ context 'when both memory fragmentation and growth exceed thresholds' do
+ let(:fragmentation) { max_heap_fragmentation + 0.1 }
+ let(:primary_memory) { 2048 }
+ let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:watchdog_iterations) { max_strikes + 1 }
+
+ it 'only calls the handler once' do
+ expect(handler).to receive(:call).once.and_return(true)
watchdog.call
end
end
context 'when gitlab_memory_watchdog ops toggle is off' do
- let(:fragmentation) { 0 }
- let(:max_strikes) { 0 }
-
before do
stub_feature_flags(gitlab_memory_watchdog: false)
end
@@ -247,6 +355,12 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
watchdog.call
end
+
+ it 'does not monitor memory growth' do
+ expect(Gitlab::Metrics::System).not_to receive(:memory_usage_uss_pss)
+
+ watchdog.call
+ end
end
end
@@ -254,9 +368,9 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'NullHandler' do
subject(:handler) { described_class::NullHandler.instance }
- describe '#on_high_heap_fragmentation' do
+ describe '#call' do
it 'does nothing' do
- expect(handler.on_high_heap_fragmentation(1.0)).to be(false)
+ expect(handler.call).to be(false)
end
end
end
@@ -264,11 +378,11 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'TermProcessHandler' do
subject(:handler) { described_class::TermProcessHandler.new(42) }
- describe '#on_high_heap_fragmentation' do
+ describe '#call' do
it 'sends SIGTERM to the current process' do
expect(Process).to receive(:kill).with(:TERM, 42)
- expect(handler.on_high_heap_fragmentation(1.0)).to be(true)
+ expect(handler.call).to be(true)
end
end
end
@@ -286,12 +400,12 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
stub_const('::Puma::Cluster::WorkerHandle', puma_worker_handle_class)
end
- describe '#on_high_heap_fragmentation' do
+ describe '#call' do
it 'invokes orderly termination via Puma API' do
expect(puma_worker_handle_class).to receive(:new).and_return(puma_worker_handle)
expect(puma_worker_handle).to receive(:term)
- expect(handler.on_high_heap_fragmentation(1.0)).to be(true)
+ expect(handler.call).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb b/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
index ed11f8ea6bb..0e8b598730c 100644
--- a/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
+++ b/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::MergeRequests::Mergeability::ResultsStore do
subject(:results_store) { described_class.new(merge_request: merge_request, interface: interface) }
diff --git a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
index 1f306753c39..b8556829a59 100644
--- a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Dashboard::Defaults do
it { is_expected.to be_const_defined(:DEFAULT_PANEL_TYPE) }
diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
index c15e717b126..bc6cd383758 100644
--- a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
@@ -24,13 +24,13 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
context 'with existing metrics' do
let(:existing_metric_attributes) do
{
- project: project,
- identifier: 'metric_b',
- title: 'overwrite',
- y_label: 'overwrite',
- query: 'overwrite',
- unit: 'overwrite',
- legend: 'overwrite',
+ project: project,
+ identifier: 'metric_b',
+ title: 'overwrite',
+ y_label: 'overwrite',
+ query: 'overwrite',
+ unit: 'overwrite',
+ legend: 'overwrite',
dashboard_path: dashboard_path
}
end
@@ -43,11 +43,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject.execute
expect(existing_metric.reload.attributes.with_indifferent_access).to include({
- title: 'Super Chart B',
+ title: 'Super Chart B',
y_label: 'y_label',
- query: 'query',
- unit: 'unit',
- legend: 'Legend Label'
+ query: 'query',
+ unit: 'unit',
+ legend: 'Legend Label'
})
end
@@ -69,11 +69,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject.execute
expect(existing_metric.reload.attributes.with_indifferent_access).to include({
- title: 'Super Chart B',
+ title: 'Super Chart B',
y_label: 'y_label',
- query: 'query',
- unit: 'unit',
- legend: 'Legend Label'
+ query: 'query',
+ unit: 'unit',
+ legend: 'Legend Label'
})
end
diff --git a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
index fdbba6c31b5..a50c2a506cb 100644
--- a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
@@ -17,11 +17,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => 'schema',
- 'details' => details
+ 'type' => type,
+ 'schema' => 'schema',
+ 'details' => details
}
end
@@ -72,10 +72,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'pattern' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'pattern' => 'aa.*' }
+ 'type' => type,
+ 'schema' => { 'pattern' => 'aa.*' }
}
end
@@ -86,10 +86,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'format' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'format' => 'date-time' }
+ 'type' => type,
+ 'schema' => { 'format' => 'date-time' }
}
end
@@ -100,10 +100,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'const' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'const' => 'one' }
+ 'type' => type,
+ 'schema' => { 'const' => 'one' }
}
end
@@ -114,10 +114,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'enum' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'enum' => %w(one two) }
+ 'type' => type,
+ 'schema' => { 'enum' => %w(one two) }
}
end
@@ -128,10 +128,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'unknown' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => 'schema'
+ 'type' => type,
+ 'schema' => 'schema'
}
end
diff --git a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
index eb67ea2b7da..aaa9daf8fee 100644
--- a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
@@ -33,9 +33,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -45,9 +45,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
@@ -94,9 +94,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -106,9 +106,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
@@ -166,9 +166,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -178,9 +178,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
diff --git a/spec/lib/gitlab/metrics/delta_spec.rb b/spec/lib/gitlab/metrics/delta_spec.rb
index e768da875c2..fdbb5e4ce4d 100644
--- a/spec/lib/gitlab/metrics/delta_spec.rb
+++ b/spec/lib/gitlab/metrics/delta_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Delta do
let(:delta) { described_class.new }
diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
index dc5c7eb2e55..fa50adb4e4f 100644
--- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
+++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
@@ -10,11 +10,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
describe 'when exporter is enabled' do
before do
allow(::WEBrick::HTTPServer).to receive(:new).with(
- Port: anything,
- BindAddress: anything,
- Logger: anything,
- AccessLog: anything
- ).and_call_original
+ {
+ Port: anything,
+ BindAddress: anything,
+ Logger: anything,
+ AccessLog: anything
+ }).and_call_original
allow(settings).to receive(:enabled).and_return(true)
allow(settings).to receive(:port).and_return(0)
@@ -45,11 +46,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
it 'starts server with port and address from settings' do
expect(::WEBrick::HTTPServer).to receive(:new).with(
- Port: port,
- BindAddress: address,
- Logger: anything,
- AccessLog: anything
- ).and_wrap_original do |m, *args|
+ {
+ Port: port,
+ BindAddress: address,
+ Logger: anything,
+ AccessLog: anything
+ }).and_wrap_original do |m, *args|
m.call(DoNotListen: true, Logger: args.first[:Logger])
end
diff --git a/spec/lib/gitlab/metrics/global_search_slis_spec.rb b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
new file mode 100644
index 00000000000..28496eff2fc
--- /dev/null
+++ b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:apdex_feature_flag_enabled) { true }
+ let(:error_rate_feature_flag_enabled) { true }
+
+ before do
+ stub_feature_flags(global_search_custom_slis: apdex_feature_flag_enabled)
+ stub_feature_flags(global_search_error_rate_sli: error_rate_feature_flag_enabled)
+ end
+
+ describe '#initialize_slis!' do
+ context 'when global_search_custom_slis feature flag is enabled' do
+ let(:apdex_feature_flag_enabled) { true }
+
+ it 'initializes Apdex SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(
+ :global_search,
+ a_kind_of(Array)
+ )
+
+ described_class.initialize_slis!
+ end
+ end
+
+ context 'when global_search_error_rate_sli feature flag is enabled' do
+ let(:error_rate_feature_flag_enabled) { true }
+
+ it 'initializes ErrorRate SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(
+ :global_search,
+ a_kind_of(Array)
+ )
+
+ described_class.initialize_slis!
+ end
+ end
+
+ context 'when global_search_custom_slis feature flag is disabled' do
+ let(:apdex_feature_flag_enabled) { false }
+
+ it 'does not initialize the Apdex SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::Apdex).not_to receive(:initialize_sli)
+
+ described_class.initialize_slis!
+ end
+ end
+
+ context 'when global_search_error_rate_sli feature flag is disabled' do
+ let(:error_rate_feature_flag_enabled) { false }
+
+ it 'does not initialize the ErrorRate SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::ErrorRate).not_to receive(:initialize_sli)
+
+ described_class.initialize_slis!
+ end
+ end
+ end
+
+ describe '#record_apdex' do
+ context 'when global_search_custom_slis feature flag is enabled' do
+ let(:apdex_feature_flag_enabled) { true }
+
+ where(:search_type, :code_search, :duration_target) do
+ 'basic' | false | 7.031
+ 'basic' | true | 21.903
+ 'advanced' | false | 4.865
+ 'advanced' | true | 13.546
+ end
+
+ with_them do
+ before do
+ allow(::Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return('end')
+ end
+
+ let(:search_scope) { code_search ? 'blobs' : 'issues' }
+
+ it 'increments the global_search SLI as a success if the elapsed time is within the target' do
+ duration = duration_target - 0.1
+
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with(
+ labels: {
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope,
+ endpoint_id: 'end'
+ },
+ success: true
+ )
+
+ described_class.record_apdex(
+ elapsed: duration,
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope
+ )
+ end
+
+ it 'increments the global_search SLI as a failure if the elapsed time is not within the target' do
+ duration = duration_target + 0.1
+
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with(
+ labels: {
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope,
+ endpoint_id: 'end'
+ },
+ success: false
+ )
+
+ described_class.record_apdex(
+ elapsed: duration,
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope
+ )
+ end
+ end
+ end
+
+ context 'when global_search_custom_slis feature flag is disabled' do
+ let(:apdex_feature_flag_enabled) { false }
+
+ it 'does not call increment on the apdex SLI' do
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).not_to receive(:increment)
+
+ described_class.record_apdex(
+ elapsed: 1,
+ search_type: 'basic',
+ search_level: 'global',
+ search_scope: 'issues'
+ )
+ end
+ end
+ end
+
+ describe '#record_error_rate' do
+ context 'when global_search_error_rate_sli feature flag is enabled' do
+ let(:error_rate_feature_flag_enabled) { true }
+
+ it 'calls increment on the error rate SLI' do
+ expect(Gitlab::Metrics::Sli::ErrorRate[:global_search]).to receive(:increment)
+
+ described_class.record_error_rate(
+ error: true,
+ search_type: 'basic',
+ search_level: 'global',
+ search_scope: 'issues'
+ )
+ end
+ end
+
+ context 'when global_search_error_rate_sli feature flag is disabled' do
+ let(:error_rate_feature_flag_enabled) { false }
+
+ it 'does not call increment on the error rate SLI' do
+ expect(Gitlab::Metrics::Sli::ErrorRate[:global_search]).not_to receive(:increment)
+
+ described_class.record_error_rate(
+ error: true,
+ search_type: 'basic',
+ search_level: 'global',
+ search_scope: 'issues'
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index ab56f38f0c1..21028d18648 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::RackMiddleware do
let(:app) { double(:app) }
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index 3396de9b12c..ed78548ef62 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -194,9 +194,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
let(:endpoint) do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
- double(:endpoint, route: route,
- options: { for: api_handler, path: [":id/archive"] },
- namespace: "/projects")
+ double(:endpoint,
+ route: route, options: { for: api_handler, path: [":id/archive"] }, namespace: "/projects")
end
let(:env) { { 'api.endpoint' => endpoint, 'REQUEST_METHOD' => 'GET' } }
@@ -256,9 +255,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
context 'Grape API without expected duration' do
let(:endpoint) do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
- double(:endpoint, route: route,
- options: { for: api_handler, path: [":id/archive"] },
- namespace: "/projects")
+ double(:endpoint,
+ route: route, options: { for: api_handler, path: [":id/archive"] }, namespace: "/projects")
end
let(:env) { { 'api.endpoint' => endpoint, 'REQUEST_METHOD' => 'GET' } }
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index adbc474343f..67cd8630758 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionView do
root = Rails.root.to_s
double(:event, duration: 2.1,
- payload: { identifier: "#{root}/app/views/x.html.haml" })
+ payload: { identifier: "#{root}/app/views/x.html.haml" })
end
before do
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 28c3ef229ab..005c1ae2d0a 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -137,7 +137,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'transaction.active_record',
duration: 230,
- payload: { connection: connection }
+ payload: { connection: connection }
)
end
@@ -213,7 +213,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'sql.active_record',
duration: 2,
- payload: payload
+ payload: payload
)
end
@@ -278,7 +278,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'sql.active_record',
duration: 2,
- payload: payload
+ payload: payload
)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
index bc6effd0438..7f7efaffd9e 100644
--- a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store do
double(
:event,
name: 'load_balancing.caught_up_replica_pick',
- payload: payload
+ payload: payload
)
end
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store do
double(
:event,
name: 'load_balancing.web_transaction_completed',
- payload: {}
+ payload: {}
)
end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index ce3caf8cdfe..7739501dd95 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::System do
context 'when /proc files exist' do
@@ -72,10 +72,20 @@ RSpec.describe Gitlab::Metrics::System do
end
describe '.memory_usage_rss' do
- it "returns the process' resident set size (RSS) in bytes" do
- mock_existing_proc_file('/proc/self/status', proc_status)
+ context 'without PID' do
+ it "returns the current process' resident set size (RSS) in bytes" do
+ mock_existing_proc_file('/proc/self/status', proc_status)
+
+ expect(described_class.memory_usage_rss).to eq(2527232)
+ end
+ end
+
+ context 'with PID' do
+ it "returns the given process' resident set size (RSS) in bytes" do
+ mock_existing_proc_file('/proc/7/status', proc_status)
- expect(described_class.memory_usage_rss).to eq(2527232)
+ expect(described_class.memory_usage_rss(pid: 7)).to eq(2527232)
+ end
end
end
@@ -96,11 +106,22 @@ RSpec.describe Gitlab::Metrics::System do
end
describe '.memory_usage_uss_pss' do
- it "returns the process' unique and porportional set size (USS/PSS) in bytes" do
- mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup)
+ context 'without PID' do
+ it "returns the current process' unique and porportional set size (USS/PSS) in bytes" do
+ mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup)
+
+ # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
+ expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072)
+ end
+ end
+
+ context 'with PID' do
+ it "returns the given process' unique and porportional set size (USS/PSS) in bytes" do
+ mock_existing_proc_file('/proc/7/smaps_rollup', proc_smaps_rollup)
- # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
- expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072)
+ # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
+ expect(described_class.memory_usage_uss_pss(pid: 7)).to eq(uss: 475136, pss: 515072)
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index b1c15db5193..1a8538b5d6a 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Transaction do
describe '#run' do
diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb
index d6590efcf4f..dc59fa804c4 100644
--- a/spec/lib/gitlab/metrics/web_transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb
@@ -66,8 +66,8 @@ RSpec.describe Gitlab::Metrics::WebTransaction do
before do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
endpoint = double(:endpoint, route: route,
- options: { for: API::Projects, path: [":id/archive"] },
- namespace: "/projects")
+ options: { for: API::Projects, path: [":id/archive"] },
+ namespace: "/projects")
env['api.endpoint'] = endpoint
diff --git a/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb b/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
index b868207e67c..02c4ea4df27 100644
--- a/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
+++ b/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
@@ -2,6 +2,7 @@
require 'fast_spec_helper'
require 'rack'
+require 'tempfile'
RSpec.describe Gitlab::Middleware::RackMultipartTempfileFactory do
let(:app) do
diff --git a/spec/lib/gitlab/middleware/release_env_spec.rb b/spec/lib/gitlab/middleware/release_env_spec.rb
index ca0ec0b9d83..a5bda23b38b 100644
--- a/spec/lib/gitlab/middleware/release_env_spec.rb
+++ b/spec/lib/gitlab/middleware/release_env_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Middleware::ReleaseEnv do
let(:inner_app) { double(:app, call: 'yay') }
diff --git a/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb b/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
index 91c030a0f45..9fb56e45103 100644
--- a/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
+++ b/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Middleware::SidekiqWebStatic do
let(:app) { double(:app) }
diff --git a/spec/lib/gitlab/namespaced_session_store_spec.rb b/spec/lib/gitlab/namespaced_session_store_spec.rb
index a569c86960c..2c258ce3da6 100644
--- a/spec/lib/gitlab/namespaced_session_store_spec.rb
+++ b/spec/lib/gitlab/namespaced_session_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NamespacedSessionStore do
let(:key) { :some_key }
diff --git a/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb
new file mode 100644
index 00000000000..d9da3ba1e46
--- /dev/null
+++ b/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe ::Gitlab::Nav::TopNavMenuHeader do
+ describe '.build' do
+ it 'builds a hash from with the given header' do
+ title = 'Test Header'
+ expected = {
+ title: title,
+ type: :header
+ }
+ expect(described_class.build(title: title)).to eq(expected)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
index 966b23bf51a..d1d6ac80c40 100644
--- a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
+++ b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe ::Gitlab::Nav::TopNavMenuItem do
describe '.build' do
@@ -17,7 +17,7 @@ RSpec.describe ::Gitlab::Nav::TopNavMenuItem do
emoji: 'smile'
}
- expect(described_class.build(**item)).to eq(item)
+ expect(described_class.build(**item)).to eq(item.merge(type: :item))
end
end
end
diff --git a/spec/lib/gitlab/net_http_adapter_spec.rb b/spec/lib/gitlab/net_http_adapter_spec.rb
index 21c1a1ebe25..fdaf35be31e 100644
--- a/spec/lib/gitlab/net_http_adapter_spec.rb
+++ b/spec/lib/gitlab/net_http_adapter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NetHttpAdapter do
describe '#connect' do
diff --git a/spec/lib/gitlab/null_request_store_spec.rb b/spec/lib/gitlab/null_request_store_spec.rb
index 66700313c9a..f68f478c73e 100644
--- a/spec/lib/gitlab/null_request_store_spec.rb
+++ b/spec/lib/gitlab/null_request_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NullRequestStore do
let(:null_store) { described_class.new }
diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb
index c91b14a33ba..563c97fa2cb 100644
--- a/spec/lib/gitlab/omniauth_initializer_spec.rb
+++ b/spec/lib/gitlab/omniauth_initializer_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and an array of args' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => %w[one two three]
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and an array of args, and default values' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => %w[one two three]
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and a hash of args' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => { 'foo' => 100, 'bar' => 200, 'nested' => { 'value' => 300 } }
@@ -84,7 +84,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and a hash of args, and default arguments' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => { 'foo' => 100, 'bar' => 200, 'nested' => { 'value' => 300 } }
@@ -106,7 +106,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, no args, and default values' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2
}
@@ -127,7 +127,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there are args, of an unsupported type' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'args' => 1
}
end
diff --git a/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb b/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
index 778244677ef..100574cc75f 100644
--- a/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
+++ b/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
@@ -50,6 +50,20 @@ RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
it { expect(project_calculated_column).to be_ascending_order }
it { expect(project_calculated_column).not_to be_descending_order }
+ context 'when order expression is an Arel node with nulls_last' do
+ it 'can automatically determine the reversed expression' do
+ column_order_definition = described_class.new(
+ attribute_name: :name,
+ column_expression: Project.arel_table[:name],
+ order_expression: Project.arel_table[:name].asc.nulls_last,
+ nullable: :nulls_last,
+ distinct: false
+ )
+
+ expect(column_order_definition).to be_ascending_order
+ end
+ end
+
it 'raises error when order direction cannot be infered' do
expect do
described_class.new(
@@ -132,6 +146,21 @@ RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
expect(column_order_definition.reverse.order_expression).to eq('name desc')
end
end
+
+ context 'when order expression is an Arel node with nulls_last' do
+ it 'can automatically determine the reversed expression' do
+ column_order_definition = described_class.new(
+ attribute_name: :name,
+ column_expression: Project.arel_table[:name],
+ order_expression: Project.arel_table[:name].asc.nulls_last,
+ order_direction: :asc,
+ nullable: :nulls_last,
+ distinct: false
+ )
+
+ expect(column_order_definition.reverse.order_expression).to eq(Project.arel_table[:name].desc.nulls_first)
+ end
+ end
end
describe '#nullable' do
diff --git a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
index 25a52af3a7a..2b8570e4aff 100644
--- a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PhabricatorImport::Representation::Task do
subject(:task) do
diff --git a/spec/lib/gitlab/phabricator_import/representation/user_spec.rb b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
index f51be0f7d8d..6df26b905cc 100644
--- a/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PhabricatorImport::Representation::User do
subject(:user) do
diff --git a/spec/lib/gitlab/popen/runner_spec.rb b/spec/lib/gitlab/popen/runner_spec.rb
index c7b64e8108b..eacb63c8f8a 100644
--- a/spec/lib/gitlab/popen/runner_spec.rb
+++ b/spec/lib/gitlab/popen/runner_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Popen::Runner do
subject { described_class.new }
diff --git a/spec/lib/gitlab/push_options_spec.rb b/spec/lib/gitlab/push_options_spec.rb
index 8f43943e2d1..3ff1c8e9012 100644
--- a/spec/lib/gitlab/push_options_spec.rb
+++ b/spec/lib/gitlab/push_options_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PushOptions do
describe 'namespace and key validation' do
diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
index 8a4e9ab8bb7..08bb06150d4 100644
--- a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::QuickActions::SubstitutionDefinition do
let(:content) do
diff --git a/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb b/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb
new file mode 100644
index 00000000000..89fe19b8f60
--- /dev/null
+++ b/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::QuickActions::TimelineTextAndDateTimeSeparator do
+ subject(:timeline_text_and_datetime_separator) { described_class }
+
+ shared_examples 'arg line with invalid parameters' do
+ it 'returns nil' do
+ expect(timeline_text_and_datetime_separator.new(invalid_arg).execute).to eq(nil)
+ end
+ end
+
+ shared_examples 'arg line with valid parameters' do
+ it 'returns text and date time array' do
+ freeze_time do
+ expect(timeline_text_and_datetime_separator.new(valid_arg).execute).to eq(expected_response)
+ end
+ end
+ end
+
+ describe 'execute' do
+ context 'with invalid parameters in arg line' do
+ context 'with empty arg line' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { '' }
+ end
+ end
+
+ context 'with invalid date' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-13-13 09:30' }
+ end
+
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-09/09 09:30' }
+ end
+
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-09.09 09:30' }
+ end
+ end
+
+ context 'with invalid time' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-11-13 29:30' }
+ end
+ end
+
+ context 'when date is invalid in arg line' do
+ let(:invalid_arg) { 'timeline comment | wrong data type' }
+
+ it 'return current date' do
+ timeline_args = timeline_text_and_datetime_separator.new(invalid_arg).execute
+
+ expect(timeline_args).to be_an_instance_of(Array)
+ expect(timeline_args.first).to eq('timeline comment')
+ expect(timeline_args.second).to match(Gitlab::QuickActions::TimelineTextAndDateTimeSeparator::DATETIME_REGEX)
+ end
+ end
+ end
+
+ context 'with valid parameters' do
+ context 'when only timeline text present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:valid_arg) { timeline_text }
+ let(:date) { DateTime.current.strftime("%Y-%m-%d %H:%M:00 UTC") }
+ let(:expected_response) { [timeline_text, date] }
+ end
+ end
+
+ context 'when only timeline text and time present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:date) { '09:30' }
+ let(:valid_arg) { "#{timeline_text} | #{date}" }
+ let(:parsed_date) { DateTime.parse(date) }
+ let(:expected_response) { [timeline_text, parsed_date] }
+ end
+ end
+
+ context 'when timeline text and date is present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:date) { '2022-06-05 09:30' }
+ let(:valid_arg) { "#{timeline_text} | #{date}" }
+ let(:parsed_date) { DateTime.parse(date) }
+ let(:expected_response) { [timeline_text, parsed_date] }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/redis/boolean_spec.rb b/spec/lib/gitlab/redis/boolean_spec.rb
index 9c233ba089f..661261c79da 100644
--- a/spec/lib/gitlab/redis/boolean_spec.rb
+++ b/spec/lib/gitlab/redis/boolean_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::Redis::Boolean do
subject(:redis_boolean) { described_class.new(bool) }
diff --git a/spec/lib/gitlab/redis/cache_spec.rb b/spec/lib/gitlab/redis/cache_spec.rb
index 1f0ebbe107f..82ff8a26199 100644
--- a/spec/lib/gitlab/redis/cache_spec.rb
+++ b/spec/lib/gitlab/redis/cache_spec.rb
@@ -17,8 +17,8 @@ RSpec.describe Gitlab::Redis::Cache do
end
describe '.active_support_config' do
- it 'has a default ttl of 2 weeks' do
- expect(described_class.active_support_config[:expires_in]).to eq(2.weeks)
+ it 'has a default ttl of 8 hours' do
+ expect(described_class.active_support_config[:expires_in]).to eq(8.hours)
end
it 'allows configuring the TTL through an env variable' do
diff --git a/spec/lib/gitlab/redis/duplicate_jobs_spec.rb b/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
index 53e3d73d17e..be20e6dcdaf 100644
--- a/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
+++ b/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::Redis::DuplicateJobs do
expect(redis_instance.primary_store.connection[:id]).to eq("redis://test-host:6379/99")
expect(redis_instance.primary_store.connection[:namespace]).to be_nil
- expect(redis_instance.secondary_store.connection[:id]).to eq("redis:///path/to/redis.sock/0")
+ expect(redis_instance.secondary_store.connection[:id]).to eq("unix:///path/to/redis.sock/0")
expect(redis_instance.secondary_store.connection[:namespace]).to eq("resque:gitlab")
expect(redis_instance.instance_name).to eq('DuplicateJobs')
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index ef8549548d7..8b73b5e03c0 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -264,13 +264,20 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.send(name, *args)
+ multi_store.pipelined do |pipeline|
+ pipeline.send(name, *args)
end
end
- it 'is executed only 1 time on primary instance' do
- expect(primary_store).to receive(name).with(*args).once
+ it 'is executed only 1 time on primary and secondary instance' do
+ expect(primary_store).to receive(:pipelined).and_call_original
+ expect(secondary_store).to receive(:pipelined).and_call_original
+
+ 2.times do
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*args).once.and_call_original
+ end
+ end
subject
end
@@ -438,14 +445,21 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.send(name, *args)
+ multi_store.pipelined do |pipeline|
+ pipeline.send(name, *args)
end
end
it 'is executed only 1 time on each instance', :aggregate_errors do
- expect(primary_store).to receive(name).with(*expected_args).once
- expect(secondary_store).to receive(name).with(*expected_args).once
+ expect(primary_store).to receive(:pipelined).and_call_original
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ end
+
+ expect(secondary_store).to receive(:pipelined).and_call_original
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ end
subject
end
@@ -781,14 +795,20 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.incr(key)
+ multi_store.pipelined do |pipeline|
+ pipeline.incr(key)
end
end
it 'is executed only 1 time on each instance', :aggregate_errors do
- expect(primary_store).to receive(:incr).with(key).once
- expect(secondary_store).to receive(:incr).with(key).once
+ expect(primary_store).to receive(:pipelined).once.and_call_original
+ expect(secondary_store).to receive(:pipelined).once.and_call_original
+
+ 2.times do
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(:incr).with(key).once
+ end
+ end
subject
end
diff --git a/spec/lib/gitlab/redis/sidekiq_status_spec.rb b/spec/lib/gitlab/redis/sidekiq_status_spec.rb
index f641ea40efd..76d130d67f7 100644
--- a/spec/lib/gitlab/redis/sidekiq_status_spec.rb
+++ b/spec/lib/gitlab/redis/sidekiq_status_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Redis::SidekiqStatus do
expect(redis_instance).to be_instance_of(::Gitlab::Redis::MultiStore)
expect(redis_instance.primary_store.connection[:id]).to eq("redis://test-host:6379/99")
- expect(redis_instance.secondary_store.connection[:id]).to eq("redis:///path/to/redis.sock/0")
+ expect(redis_instance.secondary_store.connection[:id]).to eq("unix:///path/to/redis.sock/0")
expect(redis_instance.instance_name).to eq('SidekiqStatus')
end
diff --git a/spec/lib/gitlab/render_timeout_spec.rb b/spec/lib/gitlab/render_timeout_spec.rb
index f322d71867b..b1386855fd5 100644
--- a/spec/lib/gitlab/render_timeout_spec.rb
+++ b/spec/lib/gitlab/render_timeout_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::RenderTimeout do
def expect_timeout(period)
diff --git a/spec/lib/gitlab/seeder_spec.rb b/spec/lib/gitlab/seeder_spec.rb
index 0ad80323085..a94ae2bca7a 100644
--- a/spec/lib/gitlab/seeder_spec.rb
+++ b/spec/lib/gitlab/seeder_spec.rb
@@ -77,44 +77,4 @@ RSpec.describe Gitlab::Seeder do
end
end
end
-
- describe ::Gitlab::Seeder::Ci::DailyBuildGroupReportResult do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :repository, group: group) }
- let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
-
- subject(:build_report) do
- described_class.new(project)
- end
-
- describe '#seed' do
- it 'creates daily build results for the project' do
- expect { build_report.seed }.to change {
- Ci::DailyBuildGroupReportResult.count
- }.by(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
-
- it 'matches project data with last report' do
- build_report.seed
-
- report = project.daily_build_group_report_results.last
- reports_count = project.daily_build_group_report_results.count
-
- expect(build.group_name).to eq(report.group_name)
- expect(pipeline.source_ref_path).to eq(report.ref_path)
- expect(pipeline.default_branch?).to eq(report.default_branch)
- expect(reports_count).to eq(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
-
- it 'does not raise error on RecordNotUnique' do
- build_report.seed
- build_report.seed
-
- reports_count = project.daily_build_group_report_results.count
-
- expect(reports_count).to eq(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
- end
- end
end
diff --git a/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb b/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb
new file mode 100644
index 00000000000..4b41122d23c
--- /dev/null
+++ b/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Seeders::Ci::DailyBuildGroupReportResult do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ subject(:build_report) do
+ described_class.new(project)
+ end
+
+ describe '#seed' do
+ it 'creates daily build results for the project' do
+ expect { build_report.seed }.to change {
+ Ci::DailyBuildGroupReportResult.count
+ }.by(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+
+ it 'matches project data with last report' do
+ build_report.seed
+
+ report = project.daily_build_group_report_results.last
+ reports_count = project.daily_build_group_report_results.count
+
+ expect(build.group_name).to eq(report.group_name)
+ expect(pipeline.source_ref_path).to eq(report.ref_path)
+ expect(pipeline.default_branch?).to eq(report.default_branch)
+ expect(reports_count).to eq(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+
+ it 'does not raise error on RecordNotUnique' do
+ build_report.seed
+ build_report.seed
+
+ reports_count = project.daily_build_group_report_results.count
+
+ expect(reports_count).to eq(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/service_desk_email_spec.rb b/spec/lib/gitlab/service_desk_email_spec.rb
index 9847496e361..6667b61c02b 100644
--- a/spec/lib/gitlab/service_desk_email_spec.rb
+++ b/spec/lib/gitlab/service_desk_email_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ServiceDeskEmail do
describe '.enabled?' do
diff --git a/spec/lib/gitlab/session_spec.rb b/spec/lib/gitlab/session_spec.rb
index 67ad59f956d..171288da1d5 100644
--- a/spec/lib/gitlab/session_spec.rb
+++ b/spec/lib/gitlab/session_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Session do
it 'uses the current thread as a data store' do
diff --git a/spec/lib/gitlab/setup_helper/workhorse_spec.rb b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
index 18cb266bf4e..726b73a9dfe 100644
--- a/spec/lib/gitlab/setup_helper/workhorse_spec.rb
+++ b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SetupHelper::Workhorse do
describe '.make' do
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 891b3639709..785429aa3b0 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -9,9 +9,13 @@ RSpec.describe Gitlab::Shell do
let(:repository) { project.repository }
let(:gitlab_shell) { described_class.new }
+ before do
+ described_class.instance_variable_set(:@secret_token, nil)
+ end
+
it { is_expected.to respond_to :remove_repository }
- describe 'memoized secret_token' do
+ describe '.secret_token' do
let(:secret_file) { 'tmp/tests/.secret_shell_test' }
let(:link_file) { 'tmp/tests/shell-secret-test/.gitlab_shell_secret' }
@@ -19,7 +23,6 @@ RSpec.describe Gitlab::Shell do
allow(Gitlab.config.gitlab_shell).to receive(:secret_file).and_return(secret_file)
allow(Gitlab.config.gitlab_shell).to receive(:path).and_return('tmp/tests/shell-secret-test')
FileUtils.mkdir('tmp/tests/shell-secret-test')
- described_class.ensure_secret_token!
end
after do
@@ -27,13 +30,47 @@ RSpec.describe Gitlab::Shell do
FileUtils.rm_rf(secret_file)
end
- it 'creates and links the secret token file' do
- secret_token = described_class.secret_token
+ shared_examples 'creates and links the secret token file' do
+ it 'creates and links the secret token file' do
+ secret_token = described_class.secret_token
+
+ expect(File.exist?(secret_file)).to be(true)
+ expect(File.read(secret_file).chomp).to eq(secret_token)
+ expect(File.symlink?(link_file)).to be(true)
+ expect(File.readlink(link_file)).to eq(secret_file)
+ end
+ end
+
+ describe 'memoized secret_token' do
+ before do
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+ end
+
+ context 'when link_file is a broken symbolic link' do
+ before do
+ File.symlink('tmp/tests/non_existing_file', link_file)
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+ end
+
+ context 'when secret_file exists' do
+ let(:secret_token) { 'secret-token' }
- expect(File.exist?(secret_file)).to be(true)
- expect(File.read(secret_file).chomp).to eq(secret_token)
- expect(File.symlink?(link_file)).to be(true)
- expect(File.readlink(link_file)).to eq(secret_file)
+ before do
+ File.write(secret_file, 'secret-token')
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+
+ it 'reads the token from the existing file' do
+ expect(described_class.secret_token).to eq(secret_token)
+ end
end
end
diff --git a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
index 635f572daef..dff04a2e509 100644
--- a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
+++ b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
@@ -326,7 +326,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
class: described_class.to_s,
signal: signal,
pid: pid,
- message: "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})")
+ message: "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})")
expect(Process).to receive(:kill).with(signal, pid).ordered
subject
@@ -340,7 +340,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
class: described_class.to_s,
signal: signal,
pid: pid,
- message: "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})")
+ message: "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})")
expect(Process).to receive(:kill).with(signal, 0).ordered
subject
diff --git a/spec/lib/gitlab/sidekiq_death_handler_spec.rb b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
index e3f9f8277a0..434642bf3ef 100644
--- a/spec/lib/gitlab/sidekiq_death_handler_spec.rb
+++ b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
@@ -24,8 +24,8 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
expect(described_class.counter)
.to receive(:increment)
.with({ queue: 'test_queue', worker: 'TestWorker',
- urgency: 'low', external_dependencies: 'yes',
- feature_category: 'users', boundary: 'cpu' })
+ urgency: 'low', external_dependencies: 'yes',
+ feature_category: 'users', boundary: 'cpu' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
end
@@ -40,8 +40,8 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
expect(described_class.counter)
.to receive(:increment)
.with({ queue: 'test_queue', worker: 'TestWorker',
- urgency: '', external_dependencies: 'no',
- feature_category: '', boundary: '' })
+ urgency: '', external_dependencies: 'no',
+ feature_category: '', boundary: '' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
index 09548d21106..cc730e203f6 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
@@ -41,10 +41,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r
describe '#call' do
it 'removes the stored job from redis before execution' do
bare_job = { 'class' => 'TestDeduplicationWorker', 'args' => ['hello'] }
- job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'test_deduplication')
+ job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'default')
expect(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
- .to receive(:new).with(a_hash_including(bare_job), 'test_deduplication')
+ .to receive(:new).with(a_hash_including(bare_job), 'default')
.and_return(job_definition).twice # once in client middleware
expect(job_definition).to receive(:delete!).ordered.and_call_original
@@ -60,10 +60,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r
it 'removes the stored job from redis after execution' do
bare_job = { 'class' => 'TestDeduplicationWorker', 'args' => ['hello'] }
- job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'test_deduplication')
+ job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'default')
expect(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
- .to receive(:new).with(a_hash_including(bare_job), 'test_deduplication')
+ .to receive(:new).with(a_hash_including(bare_job), 'default')
.and_return(job_definition).twice # once in client middleware
expect(TestDeduplicationWorker).to receive(:work).ordered.and_call_original
diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
index d6d24ea3a24..52b50a143fc 100644
--- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
@@ -22,39 +22,39 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'merge',
- worker: 'MergeWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'source_code_management',
- boundary: '',
- job_status: 'done' })
+ worker: 'MergeWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'source_code_management',
+ boundary: '',
+ job_status: 'done' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'merge',
- worker: 'MergeWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'source_code_management',
- boundary: '',
- job_status: 'fail' })
+ worker: 'MergeWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'source_code_management',
+ boundary: '',
+ job_status: 'fail' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'default',
- worker: 'Ci::BuildFinishedWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'continuous_integration',
- boundary: 'cpu',
- job_status: 'done' })
+ worker: 'Ci::BuildFinishedWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'continuous_integration',
+ boundary: 'cpu',
+ job_status: 'done' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'default',
- worker: 'Ci::BuildFinishedWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'continuous_integration',
- boundary: 'cpu',
- job_status: 'fail' })
+ worker: 'Ci::BuildFinishedWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'continuous_integration',
+ boundary: 'cpu',
+ job_status: 'fail' })
described_class.initialize_process_metrics
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
index 91b8ef97ab4..12430313141 100644
--- a/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
# rubocop: disable RSpec/MultipleMemoizedHelpers
RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Server, :clean_gitlab_redis_queues do
diff --git a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
index d4391d3023a..a576cf3e2ab 100644
--- a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
+++ b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 3, migrated: 0)
expect(set_after.length).to eq(3)
- expect(set_after.map(&:first)).to all(include('queue' => 'authorized_projects',
+ expect(set_after.map(&:first)).to all(include('queue' => 'default',
'class' => 'AuthorizedProjectsWorker'))
end
end
@@ -62,7 +62,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
if item['class'] == 'AuthorizedProjectsWorker'
expect(item).to include('queue' => 'new_queue', 'args' => [i])
else
- expect(item).to include('queue' => 'post_receive', 'args' => [i])
+ expect(item).to include('queue' => 'default', 'args' => [i])
end
expect(score).to be_within(schedule_jitter).of(i.succ.hours.from_now.to_i)
@@ -116,7 +116,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 4, migrated: 0)
expect(set_after.length).to eq(3)
- expect(set_after.map(&:first)).to all(include('queue' => 'authorized_projects'))
+ expect(set_after.map(&:first)).to all(include('queue' => 'default'))
end
end
@@ -138,7 +138,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 4, migrated: 1)
expect(set_after.group_by { |job| job.first['queue'] }.transform_values(&:count))
- .to eq('authorized_projects' => 6, 'new_queue' => 1)
+ .to eq('default' => 6, 'new_queue' => 1)
end
it 'iterates through the entire set of jobs' do
diff --git a/spec/lib/gitlab/sidekiq_queue_spec.rb b/spec/lib/gitlab/sidekiq_queue_spec.rb
index 5e91282612e..93632848788 100644
--- a/spec/lib/gitlab/sidekiq_queue_spec.rb
+++ b/spec/lib/gitlab/sidekiq_queue_spec.rb
@@ -4,15 +4,15 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
around do |example|
- Sidekiq::Queue.new('default').clear
+ Sidekiq::Queue.new('foobar').clear
Sidekiq::Testing.disable!(&example)
- Sidekiq::Queue.new('default').clear
+ Sidekiq::Queue.new('foobar').clear
end
def add_job(args, user:, klass: 'AuthorizedProjectsWorker')
Sidekiq::Client.push(
'class' => klass,
- 'queue' => 'default',
+ 'queue' => 'foobar',
'args' => args,
'meta.user' => user.username
)
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
describe '#drop_jobs!' do
shared_examples 'queue processing' do
- let(:sidekiq_queue) { described_class.new('default') }
+ let(:sidekiq_queue) { described_class.new('foobar') }
let_it_be(:sidekiq_queue_user) { create(:user) }
before do
@@ -80,7 +80,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
it 'raises NoMetadataError' do
add_job([1], user: create(:user))
- expect { described_class.new('default').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
+ expect { described_class.new('foobar').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
.to raise_error(described_class::NoMetadataError)
end
end
diff --git a/spec/lib/gitlab/sidekiq_signals_spec.rb b/spec/lib/gitlab/sidekiq_signals_spec.rb
index 2f751839f6a..734b9e79088 100644
--- a/spec/lib/gitlab/sidekiq_signals_spec.rb
+++ b/spec/lib/gitlab/sidekiq_signals_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SidekiqSignals do
describe '.install' do
diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
index 5a0c4cbd1b5..c0fd88eab1b 100644
--- a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SidekiqStatus::ServerMiddleware do
describe '#call' do
diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb
index afafd04d87d..bdbba04e0c0 100644
--- a/spec/lib/gitlab/sidekiq_versioning_spec.rb
+++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb
@@ -2,30 +2,9 @@
require 'spec_helper'
-RSpec.describe Gitlab::SidekiqVersioning, :redis do
- let(:foo_worker) do
- Class.new do
- def self.name
- 'FooWorker'
- end
-
- include ApplicationWorker
- end
- end
-
- let(:bar_worker) do
- Class.new do
- def self.name
- 'BarWorker'
- end
-
- include ApplicationWorker
- end
- end
-
+RSpec.describe Gitlab::SidekiqVersioning, :clean_gitlab_redis_queues do
before do
- allow(Gitlab::SidekiqConfig).to receive(:workers).and_return([foo_worker, bar_worker])
- allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return([foo_worker.queue, bar_worker.queue])
+ allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return(%w[foo bar])
end
describe '.install!' do
diff --git a/spec/lib/gitlab/slug/environment_spec.rb b/spec/lib/gitlab/slug/environment_spec.rb
index f516322b937..e8f0fba27b2 100644
--- a/spec/lib/gitlab/slug/environment_spec.rb
+++ b/spec/lib/gitlab/slug/environment_spec.rb
@@ -1,27 +1,27 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Slug::Environment do
describe '#generate' do
{
"staging-12345678901234567" => "staging-123456789-q517sa",
"9-staging-123456789012345" => "env-9-staging-123-q517sa",
- "staging-1234567890123456" => "staging-1234567890123456",
+ "staging-1234567890123456" => "staging-1234567890123456",
"staging-1234567890123456-" => "staging-123456789-q517sa",
- "production" => "production",
- "PRODUCTION" => "production-q517sa",
- "review/1-foo" => "review-1-foo-q517sa",
- "1-foo" => "env-1-foo-q517sa",
- "1/foo" => "env-1-foo-q517sa",
- "foo-" => "foo",
- "foo--bar" => "foo-bar-q517sa",
- "foo**bar" => "foo-bar-q517sa",
- "*-foo" => "env-foo-q517sa",
- "staging-12345678-" => "staging-12345678",
+ "production" => "production",
+ "PRODUCTION" => "production-q517sa",
+ "review/1-foo" => "review-1-foo-q517sa",
+ "1-foo" => "env-1-foo-q517sa",
+ "1/foo" => "env-1-foo-q517sa",
+ "foo-" => "foo",
+ "foo--bar" => "foo-bar-q517sa",
+ "foo**bar" => "foo-bar-q517sa",
+ "*-foo" => "env-foo-q517sa",
+ "staging-12345678-" => "staging-12345678",
"staging-12345678-01234567" => "staging-12345678-q517sa",
- "" => "env-q517sa",
- nil => "env-q517sa"
+ "" => "env-q517sa",
+ nil => "env-q517sa"
}.each do |name, matcher|
before do
# ('a' * 64).to_i(16).to_s(36).last(6) gives 'q517sa'
diff --git a/spec/lib/gitlab/spamcheck/client_spec.rb b/spec/lib/gitlab/spamcheck/client_spec.rb
index 956ed2a976f..2fe978125c4 100644
--- a/spec/lib/gitlab/spamcheck/client_spec.rb
+++ b/spec/lib/gitlab/spamcheck/client_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
let_it_be(:issue) { create(:issue, description: 'Test issue description') }
+ let_it_be(:snippet) { create(:personal_snippet, :public, description: 'Test issue description') }
let(:response) do
verdict = ::Spamcheck::SpamVerdict.new
@@ -26,7 +27,7 @@ RSpec.describe Gitlab::Spamcheck::Client do
verdict
end
- subject { described_class.new.issue_spam?(spam_issue: issue, user: user) }
+ subject { described_class.new.spam?(spammable: issue, user: user) }
before do
stub_application_setting(spam_check_endpoint_url: endpoint)
@@ -56,10 +57,11 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
- describe '#issue_spam?' do
+ shared_examples 'check for spam' do
before do
allow_next_instance_of(::Spamcheck::SpamcheckService::Stub) do |instance|
allow(instance).to receive(:check_for_spam_issue).and_return(response)
+ allow(instance).to receive(:check_for_spam_snippet).and_return(response)
end
end
@@ -89,12 +91,26 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
- describe "#build_issue_protobuf", :aggregate_failures do
- it 'builds the expected protobuf object' do
+ describe "#spam?", :aggregate_failures do
+ describe 'issue' do
+ subject { described_class.new.spam?(spammable: issue, user: user) }
+
+ it_behaves_like "check for spam"
+ end
+
+ describe 'snippet' do
+ subject { described_class.new.spam?(spammable: snippet, user: user, extra_features: { files: [{ path: "file.rb" }] }) }
+
+ it_behaves_like "check for spam"
+ end
+ end
+
+ describe "#build_protobuf", :aggregate_failures do
+ it 'builds the expected issue protobuf object' do
cxt = { action: :create }
- issue_pb = described_class.new.send(:build_issue_protobuf,
- issue: issue, user: user,
- context: cxt)
+ issue_pb, _ = described_class.new.send(:build_protobuf,
+ spammable: issue, user: user,
+ context: cxt, extra_features: {})
expect(issue_pb.title).to eq issue.title
expect(issue_pb.description).to eq issue.description
expect(issue_pb.user_in_project).to be false
@@ -104,6 +120,22 @@ RSpec.describe Gitlab::Spamcheck::Client do
expect(issue_pb.action).to be ::Spamcheck::Action.lookup(::Spamcheck::Action::CREATE)
expect(issue_pb.user.username).to eq user.username
end
+
+ it 'builds the expected snippet protobuf object' do
+ cxt = { action: :create }
+ snippet_pb, _ = described_class.new.send(:build_protobuf,
+ spammable: snippet, user: user,
+ context: cxt, extra_features: { files: [{ path: 'first.rb' }, { path: 'second.rb' }] })
+ expect(snippet_pb.title).to eq snippet.title
+ expect(snippet_pb.description).to eq snippet.description
+ expect(snippet_pb.created_at).to eq timestamp_to_protobuf_timestamp(snippet.created_at)
+ expect(snippet_pb.updated_at).to eq timestamp_to_protobuf_timestamp(snippet.updated_at)
+ expect(snippet_pb.action).to be ::Spamcheck::Action.lookup(::Spamcheck::Action::CREATE)
+ expect(snippet_pb.user.username).to eq user.username
+ expect(snippet_pb.user.username).to eq user.username
+ expect(snippet_pb.files.first.path).to eq 'first.rb'
+ expect(snippet_pb.files.last.path).to eq 'second.rb'
+ end
end
describe '#build_user_protobuf', :aggregate_failures do
@@ -143,6 +175,19 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
+ describe "#get_spammable_mappings", :aggregate_failures do
+ it 'is an expected spammable' do
+ protobuf_class, _ = described_class.new.send(:get_spammable_mappings, issue)
+ expect(protobuf_class).to eq ::Spamcheck::Issue
+ end
+
+ it 'is an unexpected spammable' do
+ expect { described_class.new.send(:get_spammable_mappings, 'spam') }.to raise_error(
+ ArgumentError, 'Not a spammable type: String'
+ )
+ end
+ end
+
private
def timestamp_to_protobuf_timestamp(timestamp)
diff --git a/spec/lib/gitlab/string_placeholder_replacer_spec.rb b/spec/lib/gitlab/string_placeholder_replacer_spec.rb
index 8f17bf64005..9f477998be2 100644
--- a/spec/lib/gitlab/string_placeholder_replacer_spec.rb
+++ b/spec/lib/gitlab/string_placeholder_replacer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringPlaceholderReplacer do
describe '.render_url' do
diff --git a/spec/lib/gitlab/string_range_marker_spec.rb b/spec/lib/gitlab/string_range_marker_spec.rb
index 6f63c8e2df4..2ababd6a938 100644
--- a/spec/lib/gitlab/string_range_marker_spec.rb
+++ b/spec/lib/gitlab/string_range_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringRangeMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/string_regex_marker_spec.rb b/spec/lib/gitlab/string_regex_marker_spec.rb
index 0cbe44eacf4..393bfea7c6b 100644
--- a/spec/lib/gitlab/string_regex_marker_spec.rb
+++ b/spec/lib/gitlab/string_regex_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringRegexMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index 098a58bff83..f93eb6f96cc 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -53,12 +53,13 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
it { is_expected.to match(link_match) }
end
- context 'url methods' do
+ describe 'class methods' do
where(:method_name, :result) do
:default_subscriptions_url | staging_customers_url
:payment_form_url | "#{staging_customers_url}/payment_forms/cc_validation"
:payment_validation_form_id | 'payment_method_validation'
:registration_validation_form_url | "#{staging_customers_url}/payment_forms/cc_registration_validation"
+ :registration_validation_form_id | 'cc_registration_validation'
:subscriptions_graphql_url | "#{staging_customers_url}/graphql"
:subscriptions_more_minutes_url | "#{staging_customers_url}/buy_pipeline_minutes"
:subscriptions_more_storage_url | "#{staging_customers_url}/buy_storage"
@@ -108,4 +109,16 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
is_expected.to eq(url)
end
end
+
+ describe 'constants' do
+ where(:constant_name, :result) do
+ 'REGISTRATION_VALIDATION_FORM_ID' | 'cc_registration_validation'
+ end
+
+ with_them do
+ subject { "#{described_class}::#{constant_name}".constantize }
+
+ it { is_expected.to eq(result) }
+ end
+ end
end
diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb
index 12149576de0..5f9960265ec 100644
--- a/spec/lib/gitlab/tcp_checker_spec.rb
+++ b/spec/lib/gitlab/tcp_checker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::TcpChecker, :permit_dns do
before do
diff --git a/spec/lib/gitlab/tracking/incident_management_spec.rb b/spec/lib/gitlab/tracking/incident_management_spec.rb
index ef7816aa0db..c27e2548526 100644
--- a/spec/lib/gitlab/tracking/incident_management_spec.rb
+++ b/spec/lib/gitlab/tracking/incident_management_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Tracking::IncidentManagement do
describe '.track_from_params' do
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index 028c985f3b3..e11175c776d 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -132,9 +132,36 @@ RSpec.describe Gitlab::Tracking do
expect(args[:context].last).to eq(other_context)
end
- described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5,
- context: [other_context], project: project, user: user, namespace: namespace,
- extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
+ described_class.event('category', 'action',
+ label: 'label',
+ property: 'property',
+ value: 1.5,
+ context: [other_context],
+ project: project,
+ user: user,
+ namespace: namespace,
+ extra_key_1: 'extra value 1',
+ extra_key_2: 'extra value 2')
+ end
+ end
+
+ context 'when the action is not passed in as a string' do
+ it 'allows symbols' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', :some_action)
+ end
+
+ it 'allows nil' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', nil)
+ end
+
+ it 'allows integers' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', 1)
end
end
@@ -197,8 +224,15 @@ RSpec.describe Gitlab::Tracking do
expect(args[:extra_key_1]).to eq('extra value 1')
end
- described_class.definition('filename', category: nil, action: nil, label: 'label', property: '...',
- project: project, user: user, namespace: namespace, extra_key_1: 'extra value 1')
+ described_class.definition('filename',
+ category: nil,
+ action: nil,
+ label: 'label',
+ property: '...',
+ project: project,
+ user: user,
+ namespace: namespace,
+ extra_key_1: 'extra value 1')
end
end
diff --git a/spec/lib/gitlab/tree_summary_spec.rb b/spec/lib/gitlab/tree_summary_spec.rb
index f45005fcc9b..42cc15a9033 100644
--- a/spec/lib/gitlab/tree_summary_spec.rb
+++ b/spec/lib/gitlab/tree_summary_spec.rb
@@ -25,6 +25,14 @@ RSpec.describe Gitlab::TreeSummary do
it 'defaults limit to 25' do
expect(summary.limit).to eq(25)
end
+
+ context 'when offset is larger than the maximum' do
+ let(:offset) { described_class::MAX_OFFSET + 1 }
+
+ it 'sets offset to the maximum' do
+ expect(subject.offset).to eq(described_class::MAX_OFFSET)
+ end
+ end
end
describe '#summarize' do
@@ -45,6 +53,14 @@ RSpec.describe Gitlab::TreeSummary do
end
end
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it 'returns an empty array' do
+ expect(entries).to eq([])
+ end
+ end
+
context 'with caching', :use_clean_rails_memory_store_caching do
subject { Rails.cache.fetch(key) }
diff --git a/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb b/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
index ece0a018d53..2405b6769b7 100644
--- a/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
+++ b/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UrlBlockers::DomainAllowlistEntry do
let(:domain) { 'www.example.com' }
diff --git a/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb b/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
index 110a6c17adb..8dcb402dfb2 100644
--- a/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
+++ b/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UrlBlockers::IpAllowlistEntry do
let(:ipv4) { IPAddr.new('192.168.1.1') }
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
index b85d5a3ebf9..ce15d44b1e1 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
@@ -5,15 +5,17 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitiesMetric do
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import_projects) do
- create_list(:bulk_import_entity, 3, source_type: 'project_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 2, source_type: 'project_entity', created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, source_type: 'project_entity', created_at: 3.weeks.ago, status: 0)
end
let_it_be(:bulk_import_groups) do
- create_list(:bulk_import_entity, 3, source_type: 'group_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 2, source_type: 'group_entity', created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, source_type: 'group_entity', created_at: 3.weeks.ago, status: 0)
end
let_it_be(:old_bulk_import_project) do
- create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago)
+ create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago, status: 2)
end
context 'with no source_type' do
@@ -103,4 +105,62 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitie
options: { source_type: 'group_entity' }
end
end
+
+ context 'with entity status' do
+ context 'with all time frame' do
+ let(:expected_value) { 5 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { status: 2 }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 4 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { status: 2 }
+ end
+ end
+
+ context 'with entity status and source_type' do
+ context 'with all time frame' do
+ let(:expected_value) { 3 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"source_type\" = 1 AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { status: 2, source_type: 'project_entity' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 2 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"source_type\" = 1 AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { status: 2, source_type: 'project_entity' }
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb
new file mode 100644
index 00000000000..2f49c427bd0
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUserAuthMetric do
+ context 'with all time frame' do
+ let(:expected_value) { 2 }
+
+ before do
+ user = create(:user)
+ user2 = create(:user)
+ create(:authentication_event, user: user, provider: :ldapmain, result: :success)
+ create(:authentication_event, user: user2, provider: :ldapsecondary, result: :success)
+ create(:authentication_event, user: user2, provider: :group_saml, result: :success)
+ create(:authentication_event, user: user2, provider: :group_saml, result: :success)
+ create(:authentication_event, user: user, provider: :group_saml, result: :failed)
+ end
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
+ end
+
+ context 'with 28d time frame' do
+ let(:expected_value) { 1 }
+
+ before do
+ user = create(:user)
+ user2 = create(:user)
+
+ create(:authentication_event, created_at: 1.year.ago, user: user, provider: :ldapmain, result: :success)
+ create(:authentication_event, created_at: 1.week.ago, user: user2, provider: :ldapsecondary, result: :success)
+ end
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', data_source: 'database' }
+ end
+end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
index 831f775ec9a..80ae5c6fd21 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
@@ -11,18 +11,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
let(:expected_value) { 4 }
- it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', prefix: 'source_code' } }
it 'raises an exception if event option is not present' do
- expect { described_class.new(counter_class: 'SourceCodeCounter') }.to raise_error(ArgumentError)
+ expect { described_class.new(prefix: 'source_code') }.to raise_error(ArgumentError)
end
- it 'raises an exception if counter_class option is not present' do
+ it 'raises an exception if prefix option is not present' do
expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError)
end
describe 'children classes' do
- let(:options) { { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ let(:options) { { event: 'pushes', prefix: 'source_code' } }
context 'availability not defined' do
subject { Class.new(described_class).new(time_frame: nil, options: options) }
@@ -44,4 +44,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
end
end
end
+
+ context "with usage prefix disabled" do
+ let(:expected_value) { 3 }
+
+ before do
+ 3.times do
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count
+ end
+ end
+
+ it_behaves_like 'a correct instrumented metric value', {
+ options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false }
+ }
+ end
end
diff --git a/spec/lib/gitlab/usage_data/topology_spec.rb b/spec/lib/gitlab/usage_data/topology_spec.rb
index 737580e3493..dfdf8eaabe8 100644
--- a/spec/lib/gitlab/usage_data/topology_spec.rb
+++ b/spec/lib/gitlab/usage_data/topology_spec.rb
@@ -187,7 +187,7 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:9100' },
- 'value' => [1000, '512']
+ 'value' => [1000, '512']
}
]
end
@@ -196,7 +196,7 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:9100' },
- 'value' => [1000, '0.35']
+ 'value' => [1000, '0.35']
}
]
end
@@ -224,23 +224,23 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:8080', 'job' => 'gitlab-rails' },
- 'value' => [1000, '10']
+ 'value' => [1000, '10']
},
{
'metric' => { 'instance' => '127.0.0.1:8090', 'job' => 'gitlab-sidekiq' },
- 'value' => [1000, '11']
+ 'value' => [1000, '11']
},
{
'metric' => { 'instance' => '0.0.0.0:9090', 'job' => 'prometheus' },
- 'value' => [1000, '12']
+ 'value' => [1000, '12']
},
{
'metric' => { 'instance' => '[::1]:1234', 'job' => 'redis' },
- 'value' => [1000, '13']
+ 'value' => [1000, '13']
},
{
'metric' => { 'instance' => '[::]:1234', 'job' => 'postgres' },
- 'value' => [1000, '14']
+ 'value' => [1000, '14']
}
]
end
@@ -640,7 +640,7 @@ RSpec.describe Gitlab::UsageData::Topology do
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
- 'value' => [1000, '300']
+ 'value' => [1000, '300']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
diff --git a/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
index 4a31191d75f..9cecaa01885 100644
--- a/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UsageDataCounters::BaseCounter do
describe '.fetch_supported_event' do
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 e0b334cb5af..3fb2532521a 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
@@ -107,10 +107,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'quickactions',
'pipeline_authoring',
'epics_usage',
- 'epic_boards_usage',
'secure',
'importer',
- 'network_policies',
'geo',
'growth',
'work_items',
diff --git a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
index 84a6f338282..032a5e78385 100644
--- a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
let(:time) { Time.zone.now }
context 'for Issue title edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TITLE_CHANGED }
def track_action(params)
@@ -25,7 +25,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue description edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESCRIPTION_CHANGED }
def track_action(params)
@@ -35,7 +35,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue assignee edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_ASSIGNEE_CHANGED }
def track_action(params)
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make confidential actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MADE_CONFIDENTIAL }
def track_action(params)
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make visible actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MADE_VISIBLE }
def track_action(params)
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue created actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CREATED }
def track_action(params)
@@ -75,7 +75,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue closed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CLOSED }
def track_action(params)
@@ -85,7 +85,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue reopened actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_REOPENED }
def track_action(params)
@@ -95,7 +95,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue label changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_LABEL_CHANGED }
def track_action(params)
@@ -104,8 +104,18 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
end
+ context 'for Issue label milestone actions' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
+ let(:action) { described_class::ISSUE_MILESTONE_CHANGED }
+
+ def track_action(params)
+ described_class.track_issue_milestone_changed_action(**params)
+ end
+ end
+ end
+
context 'for Issue cross-referenced actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CROSS_REFERENCED }
def track_action(params)
@@ -115,7 +125,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue moved actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MOVED }
def track_action(params)
@@ -135,7 +145,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue relate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_RELATED }
def track_action(params)
@@ -145,7 +155,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unrelate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_UNRELATED }
def track_action(params)
@@ -155,7 +165,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue marked as duplicate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MARKED_AS_DUPLICATE }
def track_action(params)
@@ -165,7 +175,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue locked actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_LOCKED }
def track_action(params)
@@ -175,7 +185,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unlocked actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_UNLOCKED }
def track_action(params)
@@ -185,7 +195,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs added actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_ADDED }
def track_action(params)
@@ -195,7 +205,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs modified actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_MODIFIED }
def track_action(params)
@@ -205,7 +215,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs removed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_REMOVED }
def track_action(params)
@@ -215,7 +225,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue due date changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DUE_DATE_CHANGED }
def track_action(params)
@@ -225,7 +235,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time estimate changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TIME_ESTIMATE_CHANGED }
def track_action(params)
@@ -235,7 +245,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time spent changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TIME_SPENT_CHANGED }
def track_action(params)
@@ -275,15 +285,15 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
it 'can return the count of actions per user deduplicated', :aggregate_failures do
- described_class.track_issue_title_changed_action(author: user1)
- described_class.track_issue_description_changed_action(author: user1)
- described_class.track_issue_assignee_changed_action(author: user1)
+ described_class.track_issue_title_changed_action(author: user1, project: project)
+ described_class.track_issue_description_changed_action(author: user1, project: project)
+ described_class.track_issue_assignee_changed_action(author: user1, project: project)
travel_to(2.days.ago) do
- described_class.track_issue_title_changed_action(author: user2)
- described_class.track_issue_title_changed_action(author: user3)
- described_class.track_issue_description_changed_action(author: user3)
- described_class.track_issue_assignee_changed_action(author: user3)
+ described_class.track_issue_title_changed_action(author: user2, project: project)
+ described_class.track_issue_title_changed_action(author: user3, project: project)
+ described_class.track_issue_description_changed_action(author: user3, project: project)
+ described_class.track_issue_assignee_changed_action(author: user3, project: project)
end
events = Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(described_class::ISSUE_CATEGORY)
diff --git a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
index 3f44cfdcf27..74e63d219bd 100644
--- a/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb
@@ -100,9 +100,9 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
subject
expect_snowplow_event(
- category: 'merge_requests',
+ category: 'merge_requests',
action: 'i_code_review_user_approve_mr',
- namespace: target_project.namespace,
+ namespace: target_project.namespace,
user: user,
project: target_project
)
diff --git a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
index 7e8f0172e06..687f8c2cd41 100644
--- a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::UsageDataCounters::NoteCounter, :clean_gitlab_redis_share
let(:expected_totals) do
{ snippet_comment: 3,
merge_request_comment: 4,
- commit_comment: 5 }
+ commit_comment: 5 }
end
before do
diff --git a/spec/lib/gitlab/usage_data_counters_spec.rb b/spec/lib/gitlab/usage_data_counters_spec.rb
index 0696b375eb5..040b5deca54 100644
--- a/spec/lib/gitlab/usage_data_counters_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UsageDataCounters do
describe '.usage_data_counters' do
diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb
index 485f2131d87..ed0eabf1b4d 100644
--- a/spec/lib/gitlab/usage_data_metrics_spec.rb
+++ b/spec/lib/gitlab/usage_data_metrics_spec.rb
@@ -16,6 +16,10 @@ RSpec.describe Gitlab::UsageDataMetrics do
allow_next_instance_of(Gitlab::Database::BatchCounter) do |batch_counter|
allow(batch_counter).to receive(:transaction_open?).and_return(false)
end
+
+ allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance|
+ allow(instance).to receive(:transaction_open?).and_return(false)
+ end
end
context 'with instrumentation_class' do
@@ -33,6 +37,10 @@ RSpec.describe Gitlab::UsageDataMetrics do
expect(subject[:usage_activity_by_stage][:plan]).to include(:issues)
end
+ it 'includes usage_activity_by_stage metrics' do
+ expect(subject[:usage_activity_by_stage][:manage]).to include(:count_user_auth)
+ end
+
it 'includes usage_activity_by_stage_monthly keys' do
expect(subject[:usage_activity_by_stage_monthly][:plan]).to include(:issues)
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 692b6483149..46ed4b57d3a 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -215,14 +215,28 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
groups: 2,
users_created: 10,
omniauth_providers: ['google_oauth2'],
- user_auth_by_provider: { 'group_saml' => 2, 'ldap' => 4, 'standard' => 0, 'two-factor' => 0, 'two-factor-via-u2f-device' => 0, "two-factor-via-webauthn-device" => 0 }
+ user_auth_by_provider: {
+ 'group_saml' => 2,
+ 'ldap' => 4,
+ 'standard' => 0,
+ 'two-factor' => 0,
+ 'two-factor-via-u2f-device' => 0,
+ "two-factor-via-webauthn-device" => 0
+ }
)
expect(described_class.usage_activity_by_stage_manage(described_class.monthly_time_range_db_params)).to include(
events: be_within(error_rate).percent_of(2),
groups: 1,
users_created: 6,
omniauth_providers: ['google_oauth2'],
- user_auth_by_provider: { 'group_saml' => 1, 'ldap' => 2, 'standard' => 0, 'two-factor' => 0, 'two-factor-via-u2f-device' => 0, "two-factor-via-webauthn-device" => 0 }
+ user_auth_by_provider: {
+ 'group_saml' => 1,
+ 'ldap' => 2,
+ 'standard' => 0,
+ 'two-factor' => 0,
+ 'two-factor-via-u2f-device' => 0,
+ "two-factor-via-webauthn-device" => 0
+ }
)
end
@@ -583,10 +597,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it 'gathers object store usage correctly' do
expect(subject[:object_store]).to eq(
{ artifacts: { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } },
- external_diffs: { enabled: false },
- lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
- uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
- packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } }
+ external_diffs: { enabled: false },
+ lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
+ uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } },
+ packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } }
)
end
@@ -749,9 +763,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it { is_expected.to include(:kubernetes_agent_gitops_sync) }
it { is_expected.to include(:kubernetes_agent_k8s_api_proxy_request) }
- it { is_expected.to include(:package_events_i_package_pull_package) }
- it { is_expected.to include(:package_events_i_package_delete_package_by_user) }
- it { is_expected.to include(:package_events_i_package_conan_push_package) }
end
describe '.usage_data_counters' do
diff --git a/spec/lib/gitlab/utils/deep_size_spec.rb b/spec/lib/gitlab/utils/deep_size_spec.rb
index 7595fb2c1b0..6b0be4590f1 100644
--- a/spec/lib/gitlab/utils/deep_size_spec.rb
+++ b/spec/lib/gitlab/utils/deep_size_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::DeepSize do
let(:data) do
@@ -58,10 +58,4 @@ RSpec.describe Gitlab::Utils::DeepSize do
it { is_expected.not_to be_valid }
end
end
-
- describe '.human_default_max_size' do
- it 'returns 1 MB' do
- expect(described_class.human_default_max_size).to eq('1 MB')
- end
- end
end
diff --git a/spec/lib/gitlab/utils/delegator_override_spec.rb b/spec/lib/gitlab/utils/delegator_override_spec.rb
index af4c7fa5d8e..2dafa75e344 100644
--- a/spec/lib/gitlab/utils/delegator_override_spec.rb
+++ b/spec/lib/gitlab/utils/delegator_override_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Gitlab::Utils::DelegatorOverride do
before do
stub_env('STATIC_VERIFICATION', 'true')
+ described_class.validators.clear
end
describe '.delegator_target' do
diff --git a/spec/lib/gitlab/utils/execution_tracker_spec.rb b/spec/lib/gitlab/utils/execution_tracker_spec.rb
new file mode 100644
index 00000000000..6c42863658c
--- /dev/null
+++ b/spec/lib/gitlab/utils/execution_tracker_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Utils::ExecutionTracker do
+ subject(:tracker) { described_class.new }
+
+ describe '#over_limit?' do
+ it 'is true when max runtime is exceeded' do
+ monotonic_time_before = 1 # this will be the start time
+ monotonic_time_after = described_class::MAX_RUNTIME.to_i + 1 # this will be returned when over_limit? is called
+
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+
+ tracker
+
+ expect(tracker).to be_over_limit
+ end
+
+ it 'is false when max runtime is not exceeded' do
+ expect(tracker).not_to be_over_limit
+ end
+ end
+end
diff --git a/spec/lib/gitlab/utils/json_size_estimator_spec.rb b/spec/lib/gitlab/utils/json_size_estimator_spec.rb
index 5fd66caa5e9..ba49cc3a847 100644
--- a/spec/lib/gitlab/utils/json_size_estimator_spec.rb
+++ b/spec/lib/gitlab/utils/json_size_estimator_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::JsonSizeEstimator do
RSpec::Matchers.define :match_json_bytesize_of do |expected|
diff --git a/spec/lib/gitlab/utils/markdown_spec.rb b/spec/lib/gitlab/utils/markdown_spec.rb
index acc5bd47c8c..0a7d1160bbc 100644
--- a/spec/lib/gitlab/utils/markdown_spec.rb
+++ b/spec/lib/gitlab/utils/markdown_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::Markdown do
let(:klass) do
diff --git a/spec/lib/gitlab/utils/merge_hash_spec.rb b/spec/lib/gitlab/utils/merge_hash_spec.rb
index 11daa05c9ee..4eec6e83be2 100644
--- a/spec/lib/gitlab/utils/merge_hash_spec.rb
+++ b/spec/lib/gitlab/utils/merge_hash_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::MergeHash do
describe '.crush' do
it 'can flatten a hash to each element' do
diff --git a/spec/lib/gitlab/utils/nokogiri_spec.rb b/spec/lib/gitlab/utils/nokogiri_spec.rb
index 90f137f53c8..7b4c63f9168 100644
--- a/spec/lib/gitlab/utils/nokogiri_spec.rb
+++ b/spec/lib/gitlab/utils/nokogiri_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'rspec-parameterized'
RSpec.describe Gitlab::Utils::Nokogiri do
describe '#css_to_xpath' do
diff --git a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
index 3ab592dfc62..1fc10bc3aa8 100644
--- a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
+++ b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'html/pipeline'
+require 'addressable'
RSpec.describe Gitlab::Utils::SanitizeNodeLink do
let(:klass) do
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index ad1a65ffae8..61323f0646b 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -174,7 +174,7 @@ RSpec.describe Gitlab::Utils do
{
'TEST' => 'test',
'project_with_underscores' => 'project-with-underscores',
- 'namespace/project' => 'namespace-project',
+ 'namespace/project' => 'namespace-project',
'a' * 70 => 'a' * 63,
'test_trailing_' => 'test-trailing'
}.each do |original, expected|
diff --git a/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb b/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
index 7d96adf95e8..8d4629bf48b 100644
--- a/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
+++ b/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
@@ -150,6 +150,29 @@ RSpec.describe Gitlab::WebIde::Config::Entry::Terminal do
}
)
end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { described_class.new(config, with_image_ports: true) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'returns correct value' do
+ expect(entry_without_ff.value)
+ .to eq(
+ tag_list: ['webide'],
+ job_variables: [{ key: 'KEY', value: 'value', public: true }],
+ options: {
+ image: { name: "image:1.0" },
+ services: [{ name: "mysql" }],
+ before_script: %w[ls pwd],
+ script: ['sleep 100']
+ }
+ )
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/word_diff/chunk_collection_spec.rb b/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
index 73e9ff3974a..f76c4213c19 100644
--- a/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
+++ b/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::ChunkCollection do
subject(:collection) { described_class.new }
diff --git a/spec/lib/gitlab/word_diff/line_processor_spec.rb b/spec/lib/gitlab/word_diff/line_processor_spec.rb
index f448f5b5eb6..7246ed772f8 100644
--- a/spec/lib/gitlab/word_diff/line_processor_spec.rb
+++ b/spec/lib/gitlab/word_diff/line_processor_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::LineProcessor do
subject(:line_processor) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/parser_spec.rb b/spec/lib/gitlab/word_diff/parser_spec.rb
index e793e44fd45..18109a8160b 100644
--- a/spec/lib/gitlab/word_diff/parser_spec.rb
+++ b/spec/lib/gitlab/word_diff/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Parser do
subject(:parser) { described_class.new }
@@ -42,18 +42,18 @@ RSpec.describe Gitlab::WordDiff::Parser do
{ index: 1, old_pos: 2, new_pos: 2, text: 'Unchanged line', type: nil, marker_ranges: [] },
{ index: 2, old_pos: 3, new_pos: 3, text: '', type: nil, marker_ranges: [] },
{ index: 3, old_pos: 4, new_pos: 4, text: 'Old changeNew addition unchanged content', type: nil,
- marker_ranges: [
- Gitlab::MarkerRange.new(0, 9, mode: :deletion),
- Gitlab::MarkerRange.new(10, 21, mode: :addition)
- ] },
+ marker_ranges: [
+ Gitlab::MarkerRange.new(0, 9, mode: :deletion),
+ Gitlab::MarkerRange.new(10, 21, mode: :addition)
+ ] },
{ index: 4, old_pos: 50, new_pos: 50, text: '@@ -50,14 +50,13 @@', type: 'match', marker_ranges: [] },
{ index: 5, old_pos: 50, new_pos: 50, text: 'First change same same same_removed_added_end of the line', type: nil,
- marker_ranges: [
- Gitlab::MarkerRange.new(0, 11, mode: :addition),
- Gitlab::MarkerRange.new(28, 35, mode: :deletion),
- Gitlab::MarkerRange.new(36, 41, mode: :addition)
- ] },
+ marker_ranges: [
+ Gitlab::MarkerRange.new(0, 11, mode: :addition),
+ Gitlab::MarkerRange.new(28, 35, mode: :deletion),
+ Gitlab::MarkerRange.new(36, 41, mode: :addition)
+ ] },
{ index: 6, old_pos: 51, new_pos: 51, text: '', type: nil, marker_ranges: [] }
]
diff --git a/spec/lib/gitlab/word_diff/positions_counter_spec.rb b/spec/lib/gitlab/word_diff/positions_counter_spec.rb
index e2c246f6801..32ce7c50591 100644
--- a/spec/lib/gitlab/word_diff/positions_counter_spec.rb
+++ b/spec/lib/gitlab/word_diff/positions_counter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::PositionsCounter do
subject(:counter) { described_class.new }
diff --git a/spec/lib/gitlab/word_diff/segments/chunk_spec.rb b/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
index 797cc42a03c..75c0e5b4a77 100644
--- a/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::Chunk do
subject(:chunk) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb b/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
index 5250e6d73c2..a65f55c716f 100644
--- a/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::DiffHunk do
subject(:diff_hunk) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/segments/newline_spec.rb b/spec/lib/gitlab/word_diff/segments/newline_spec.rb
index ed5054844f1..4c0cf0c5ee4 100644
--- a/spec/lib/gitlab/word_diff/segments/newline_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/newline_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::Newline do
subject(:newline) { described_class.new }
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 703a4b5399e..5c9a3cc0a24 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -365,7 +365,7 @@ RSpec.describe Gitlab::Workhorse do
it 'set and notify' do
expect(Gitlab::Redis::SharedState).to receive(:with).and_call_original
expect_any_instance_of(::Redis).to receive(:publish)
- .with(described_class::NOTIFICATION_CHANNEL, "test-key=test-value")
+ .with(described_class::NOTIFICATION_PREFIX + 'test-key', "test-value")
subject
end