summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-12-20 09:07:57 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-12-20 09:07:57 +0000
commit7881eb30eaa8b01dbcfe87faa09927c75c7d6e45 (patch)
tree298bc8d2c62b2f2c29cb8ecbcf3de3eaaa6466d9 /spec/lib
parent64b66e0cb6d1bfd27abf24e06653f00bddb60597 (diff)
downloadgitlab-ce-7881eb30eaa8b01dbcfe87faa09927c75c7d6e45.tar.gz
Add latest changes from gitlab-org/gitlab@12-6-stable-ee
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/api/entities/release_spec.rb40
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb56
-rw-r--r--spec/lib/api/helpers/related_resources_helpers_spec.rb1
-rw-r--r--spec/lib/api/support/git_access_actor_spec.rb15
-rw-r--r--spec/lib/backup/manager_spec.rb36
-rw-r--r--spec/lib/backup/repository_spec.rb9
-rw-r--r--spec/lib/backup/uploads_spec.rb1
-rw-r--r--spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb51
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb26
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb12
-rw-r--r--spec/lib/banzai/filter/reference_redactor_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb12
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb180
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb8
-rw-r--r--spec/lib/banzai/object_renderer_spec.rb8
-rw-r--r--spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb23
-rw-r--r--spec/lib/banzai/pipeline/wiki_pipeline_spec.rb34
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb25
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb1
-rw-r--r--spec/lib/banzai/reference_parser/mentioned_group_parser_spec.rb (renamed from spec/lib/banzai/reference_parser/mentioned_users_by_group_parser_spec.rb)2
-rw-r--r--spec/lib/banzai/reference_parser/mentioned_project_parser_spec.rb (renamed from spec/lib/banzai/reference_parser/mentioned_users_by_project_parser_spec.rb)2
-rw-r--r--spec/lib/banzai/reference_parser/snippet_parser_spec.rb1
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb20
-rw-r--r--spec/lib/banzai/reference_redactor_spec.rb10
-rw-r--r--spec/lib/bitbucket/connection_spec.rb16
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb2
-rw-r--r--spec/lib/constraints/user_url_constrainer_spec.rb2
-rw-r--r--spec/lib/container_registry/tag_spec.rb23
-rw-r--r--spec/lib/extracts_path_spec.rb4
-rw-r--r--spec/lib/gitaly/server_spec.rb22
-rw-r--r--spec/lib/gitlab/application_rate_limiter_spec.rb (renamed from spec/lib/gitlab/action_rate_limiter_spec.rb)32
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb3
-rw-r--r--spec/lib/gitlab/auth/auth_finders_spec.rb (renamed from spec/lib/gitlab/auth/user_auth_finders_spec.rb)162
-rw-r--r--spec/lib/gitlab/auth/current_user_mode_spec.rb115
-rw-r--r--spec/lib/gitlab/auth/ip_rate_limiter_spec.rb32
-rw-r--r--spec/lib/gitlab/auth/ldap/access_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/ldap/auth_hash_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/ldap/authentication_spec.rb15
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb25
-rw-r--r--spec/lib/gitlab/auth/request_authenticator_spec.rb24
-rw-r--r--spec/lib/gitlab/auth_spec.rb107
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb3
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb1
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb4
-rw-r--r--spec/lib/gitlab/chat/command_spec.rb2
-rw-r--r--spec/lib/gitlab/checks/branch_check_spec.rb4
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/ansi2json/result_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/ansi2json/style_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/build/context/build_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/context/global_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/credentials/factory_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb99
-rw-r--r--spec/lib/gitlab/ci/config/entry/default_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb53
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb68
-rw-r--r--spec/lib/gitlab/ci/config/entry/kubernetes_spec.rb56
-rw-r--r--spec/lib/gitlab/ci/config/entry/need_spec.rb168
-rw-r--r--spec/lib/gitlab/ci/config/entry/needs_spec.rb101
-rw-r--r--spec/lib/gitlab/ci/config/external/file/project_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/config/external/file/template_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/config/normalizer_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/cron_parser_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb221
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb103
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb42
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb110
-rw-r--r--spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/trace/chunked_io_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/trace/section_parser_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb193
-rw-r--r--spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb4
-rw-r--r--spec/lib/gitlab/cycle_analytics/usage_data_spec.rb4
-rw-r--r--spec/lib/gitlab/danger/changelog_spec.rb163
-rw-r--r--spec/lib/gitlab/danger/danger_spec_helper.rb17
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb17
-rw-r--r--spec/lib/gitlab/danger/teammate_spec.rb14
-rw-r--r--spec/lib/gitlab/data_builder/build_spec.rb7
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb1
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb199
-rw-r--r--spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb30
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb1
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb6
-rw-r--r--spec/lib/gitlab/database_spec.rb27
-rw-r--r--spec/lib/gitlab/diff/deprecated_highlight_cache_spec.rb70
-rw-r--r--spec/lib/gitlab/diff/diff_refs_spec.rb3
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb4
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb39
-rw-r--r--spec/lib/gitlab/diff/highlight_cache_spec.rb123
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb4
-rw-r--r--spec/lib/gitlab/diff/line_mapper_spec.rb1
-rw-r--r--spec/lib/gitlab/diff/line_spec.rb44
-rw-r--r--spec/lib/gitlab/diff/parallel_diff_spec.rb1
-rw-r--r--spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb9
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb5
-rw-r--r--spec/lib/gitlab/email/handler_spec.rb2
-rw-r--r--spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb58
-rw-r--r--spec/lib/gitlab/error_tracking_spec.rb172
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb16
-rw-r--r--spec/lib/gitlab/etag_caching/router_spec.rb9
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb4
-rw-r--r--spec/lib/gitlab/external_authorization/client_spec.rb1
-rw-r--r--spec/lib/gitlab/external_authorization/response_spec.rb1
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb16
-rw-r--r--spec/lib/gitlab/file_finder_spec.rb1
-rw-r--r--spec/lib/gitlab/fogbugz_import/client_spec.rb4
-rw-r--r--spec/lib/gitlab/fogbugz_import/importer_spec.rb73
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb6
-rw-r--r--spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/attributes_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blame_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb6
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb6
-rw-r--r--spec/lib/gitlab/git/bundle_file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb42
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb2
-rw-r--r--spec/lib/gitlab/git/conflict/file_spec.rb6
-rw-r--r--spec/lib/gitlab/git/conflict/parser_spec.rb4
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb3
-rw-r--r--spec/lib/gitlab/git/gitmodules_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/hook_env_spec.rb3
-rw-r--r--spec/lib/gitlab/git/lfs_changes_spec.rb2
-rw-r--r--spec/lib/gitlab/git/lfs_pointer_file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/merge_base_spec.rb1
-rw-r--r--spec/lib/gitlab/git/pre_receive_error_spec.rb2
-rw-r--r--spec/lib/gitlab/git/push_spec.rb2
-rw-r--r--spec/lib/gitlab/git/raw_diff_change_spec.rb2
-rw-r--r--spec/lib/gitlab/git/remote_mirror_spec.rb2
-rw-r--r--spec/lib/gitlab/git/remote_repository_spec.rb3
-rw-r--r--spec/lib/gitlab/git/repository_cleaner_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb3
-rw-r--r--spec/lib/gitlab/git/tag_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb6
-rw-r--r--spec/lib/gitlab/git/user_spec.rb4
-rw-r--r--spec/lib/gitlab/git/util_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb9
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb1
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb1
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb11
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/importer/labels_importer_spec.rb5
-rw-r--r--spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb5
-rw-r--r--spec/lib/gitlab/github_import/importer/note_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb5
-rw-r--r--spec/lib/gitlab/github_import/sequential_importer_spec.rb5
-rw-r--r--spec/lib/gitlab/gitlab_import/client_spec.rb14
-rw-r--r--spec/lib/gitlab/gitlab_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/gitlab_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/client_spec.rb3
-rw-r--r--spec/lib/gitlab/google_code_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/google_code_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/commit_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg_spec.rb28
-rw-r--r--spec/lib/gitlab/grafana_embed_usage_data_spec.rb70
-rw-r--r--spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb3
-rw-r--r--spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb1
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb21
-rw-r--r--spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb127
-rw-r--r--spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb2
-rw-r--r--spec/lib/gitlab/graphs/commits_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/db_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/gitaly_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/master_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/probes/collection_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/puma_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/redis/cache_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/redis/queues_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/redis/redis_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/simple_check_shared.rb2
-rw-r--r--spec/lib/gitlab/health_checks/unicorn_check_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/base_builder_spec.rb3
-rw-r--r--spec/lib/gitlab/hook_data/issuable_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/issue_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/http_io_spec.rb4
-rw-r--r--spec/lib/gitlab/i18n/metadata_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/i18n/po_linter_spec.rb2
-rw-r--r--spec/lib/gitlab/i18n/translation_entry_spec.rb3
-rw-r--r--spec/lib/gitlab/import/merge_request_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml16
-rw-r--r--spec/lib/gitlab/import_export/attribute_cleaner_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/attribute_configuration_spec.rb14
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb22
-rw-r--r--spec/lib/gitlab/import_export/fork_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/group_project_object_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/hash_util_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/import_export_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/lfs_restorer_spec.rb3
-rw-r--r--spec/lib/gitlab/import_export/lfs_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb15
-rw-r--r--spec/lib/gitlab/import_export/merge_request_parser_spec.rb10
-rw-r--r--spec/lib/gitlab/import_export/model_configuration_spec.rb13
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb70
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/reader_spec.rb21
-rw-r--r--spec/lib/gitlab/import_export/references_configuration_spec.rb48
-rw-r--r--spec/lib/gitlab/import_export/relation_factory_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb67
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/repo_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml38
-rw-r--r--spec/lib/gitlab/import_export/saver_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/shared_spec.rb24
-rw-r--r--spec/lib/gitlab/import_export/uploads_manager_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/uploads_restorer_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/version_checker_spec.rb14
-rw-r--r--spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/wiki_restorer_spec.rb2
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb9
-rw-r--r--spec/lib/gitlab/json_cache_spec.rb7
-rw-r--r--spec/lib/gitlab/kubernetes/config_map_spec.rb3
-rw-r--r--spec/lib/gitlab/kubernetes/helm/api_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb53
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb86
-rw-r--r--spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb218
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb16
-rw-r--r--spec/lib/gitlab/kubernetes/namespace_spec.rb3
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb1
-rw-r--r--spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/client_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/importer_spec.rb4
-rw-r--r--spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb3
-rw-r--r--spec/lib/gitlab/legacy_github_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/lets_encrypt/client_spec.rb2
-rw-r--r--spec/lib/gitlab/mail_room/mail_room_spec.rb106
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb5
-rw-r--r--spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/instrumentation_spec.rb8
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb1
-rw-r--r--spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb1
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb53
-rw-r--r--spec/lib/gitlab/pages_spec.rb4
-rw-r--r--spec/lib/gitlab/pagination/keyset/page_spec.rb66
-rw-r--r--spec/lib/gitlab/pagination/keyset/pager_spec.rb68
-rw-r--r--spec/lib/gitlab/pagination/keyset/request_context_spec.rb115
-rw-r--r--spec/lib/gitlab/pagination/keyset_spec.rb62
-rw-r--r--spec/lib/gitlab/phabricator_import/cache/map_spec.rb1
-rw-r--r--spec/lib/gitlab/phabricator_import/importer_spec.rb1
-rw-r--r--spec/lib/gitlab/phabricator_import/project_creator_spec.rb1
-rw-r--r--spec/lib/gitlab/phabricator_import/user_finder_spec.rb1
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb1
-rw-r--r--spec/lib/gitlab/project_template_spec.rb1
-rw-r--r--spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb2
-rw-r--r--spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb6
-rw-r--r--spec/lib/gitlab/prometheus/query_variables_spec.rb2
-rw-r--r--spec/lib/gitlab/prometheus_client_spec.rb1
-rw-r--r--spec/lib/gitlab/puma_logging/json_formatter_spec.rb11
-rw-r--r--spec/lib/gitlab/query_limiting/middleware_spec.rb5
-rw-r--r--spec/lib/gitlab/quick_actions/substitution_definition_spec.rb1
-rw-r--r--spec/lib/gitlab/regex_spec.rb6
-rw-r--r--spec/lib/gitlab/request_context_spec.rb4
-rw-r--r--spec/lib/gitlab/sanitizers/svg_spec.rb4
-rw-r--r--spec/lib/gitlab/sentry_spec.rb123
-rw-r--r--spec/lib/gitlab/sherlock/transaction_spec.rb5
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb4
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb251
-rw-r--r--spec/lib/gitlab/sidekiq_middleware_spec.rb143
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb2
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb1
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb1
-rw-r--r--spec/lib/gitlab/slash_commands/run_spec.rb35
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb10
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb28
-rw-r--r--spec/lib/gitlab/string_range_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/string_regex_marker_spec.rb4
-rw-r--r--spec/lib/gitlab/throttle_spec.rb87
-rw-r--r--spec/lib/gitlab/tracking_spec.rb4
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb20
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb25
-rw-r--r--spec/lib/gitlab/visibility_level_spec.rb24
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/lib/google_api/auth_spec.rb5
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb7
-rw-r--r--spec/lib/json_web_token/rsa_token_spec.rb1
-rw-r--r--spec/lib/marginalia_spec.rb173
-rw-r--r--spec/lib/omni_auth/strategies/saml_spec.rb4
-rw-r--r--spec/lib/quality/helm_client_spec.rb25
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb74
-rw-r--r--spec/lib/quality/test_level_spec.rb4
-rw-r--r--spec/lib/sentry/client/projects_spec.rb119
-rw-r--r--spec/lib/sentry/client_spec.rb253
-rw-r--r--spec/lib/sentry/pagination_parser_spec.rb63
324 files changed, 5852 insertions, 1424 deletions
diff --git a/spec/lib/api/entities/release_spec.rb b/spec/lib/api/entities/release_spec.rb
new file mode 100644
index 00000000000..729a69347cb
--- /dev/null
+++ b/spec/lib/api/entities/release_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::Entities::Release do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:release) { create(:release, :with_evidence, project: project) }
+ let(:user) { create(:user) }
+ let(:entity) { described_class.new(release, current_user: user) }
+
+ subject { entity.as_json }
+
+ describe 'evidence' do
+ context 'when the current user can download code' do
+ it 'exposes the evidence sha and the json path' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :download_code, project).and_return(true)
+
+ expect(subject[:evidence_sha]).to eq(release.evidence_sha)
+ expect(subject[:assets][:evidence_file_path]).to eq(
+ Gitlab::Routing.url_helpers.evidence_project_release_url(project,
+ release.tag,
+ format: :json)
+ )
+ end
+ end
+
+ context 'when the current user cannot download code' do
+ it 'does not expose any evidence data' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :download_code, project).and_return(false)
+
+ expect(subject.keys).not_to include(:evidence_sha)
+ expect(subject[:assets].keys).not_to include(:evidence_file_path)
+ end
+ end
+ end
+end
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index 040ff1a8ebe..2d5bec2e752 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -5,10 +5,16 @@ require 'spec_helper'
describe API::Helpers::Pagination do
subject { Class.new.include(described_class).new }
+ let(:expected_result) { double("result", to_a: double) }
+ let(:relation) { double("relation") }
+ let(:params) { {} }
+
+ before do
+ allow(subject).to receive(:params).and_return(params)
+ end
+
describe '#paginate' do
- let(:relation) { double("relation") }
let(:offset_pagination) { double("offset pagination") }
- let(:expected_result) { double("result") }
it 'delegates to OffsetPagination' do
expect(::Gitlab::Pagination::OffsetPagination).to receive(:new).with(subject).and_return(offset_pagination)
@@ -19,4 +25,50 @@ describe API::Helpers::Pagination do
expect(result).to eq(expected_result)
end
end
+
+ describe '#paginate_and_retrieve!' do
+ context 'for offset pagination' do
+ before do
+ allow(Gitlab::Pagination::Keyset).to receive(:available?).and_return(false)
+ end
+
+ it 'delegates to paginate' do
+ expect(subject).to receive(:paginate).with(relation).and_return(expected_result)
+
+ result = subject.paginate_and_retrieve!(relation)
+
+ expect(result).to eq(expected_result.to_a)
+ end
+ end
+
+ context 'for keyset pagination' do
+ let(:params) { { pagination: 'keyset' } }
+ let(:request_context) { double('request context') }
+
+ before do
+ allow(Gitlab::Pagination::Keyset::RequestContext).to receive(:new).with(subject).and_return(request_context)
+ end
+
+ context 'when keyset pagination is available' do
+ it 'delegates to KeysetPagination' do
+ expect(Gitlab::Pagination::Keyset).to receive(:available?).and_return(true)
+ expect(Gitlab::Pagination::Keyset).to receive(:paginate).with(request_context, relation).and_return(expected_result)
+
+ result = subject.paginate_and_retrieve!(relation)
+
+ expect(result).to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when keyset pagination is not available' do
+ it 'renders a 501 error if keyset pagination isnt available yet' do
+ expect(Gitlab::Pagination::Keyset).to receive(:available?).with(request_context, relation).and_return(false)
+ expect(Gitlab::Pagination::Keyset).not_to receive(:paginate)
+ expect(subject).to receive(:error!).with(/not yet available/, 405)
+
+ subject.paginate_and_retrieve!(relation)
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/api/helpers/related_resources_helpers_spec.rb b/spec/lib/api/helpers/related_resources_helpers_spec.rb
index fb26cc417e8..eeeb22abd10 100644
--- a/spec/lib/api/helpers/related_resources_helpers_spec.rb
+++ b/spec/lib/api/helpers/related_resources_helpers_spec.rb
@@ -43,6 +43,7 @@ describe API::Helpers::RelatedResourcesHelpers do
describe '#expose_url' do
let(:path) { '/api/v4/awesome_endpoint' }
+
subject(:url) { helpers.expose_url(path) }
def stub_default_url_options(protocol: 'http', host: 'example.com', port: nil, script_name: '')
diff --git a/spec/lib/api/support/git_access_actor_spec.rb b/spec/lib/api/support/git_access_actor_spec.rb
index 63f5966faea..69637947c79 100644
--- a/spec/lib/api/support/git_access_actor_spec.rb
+++ b/spec/lib/api/support/git_access_actor_spec.rb
@@ -9,17 +9,26 @@ describe API::Support::GitAccessActor do
subject { described_class.new(user: user, key: key) }
describe '.from_params' do
+ let(:key) { create(:key) }
+
context 'with params that are valid' do
it 'returns an instance of API::Support::GitAccessActor' do
- params = { key_id: create(:key).id }
+ params = { key_id: key.id }
expect(described_class.from_params(params)).to be_instance_of(described_class)
end
end
context 'with params that are invalid' do
- it 'returns nil' do
- expect(described_class.from_params({})).to be_nil
+ it "returns an instance of #{described_class}" do
+ expect(described_class.from_params({})).to be_instance_of(described_class)
+ end
+ end
+
+ context 'when passing an identifier used gitaly' do
+ it 'finds the user based on an identifier' do
+ expect(described_class).to receive(:identify).and_call_original
+ expect(described_class.from_params(identifier: "key-#{key.id}").user).to eq(key.user)
end
end
end
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 35594cd2fb8..06ad0557e37 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -326,7 +326,7 @@ describe Backup::Manager do
context 'target path' do
it 'uses the tar filename by default' do
expect_any_instance_of(Fog::Collection).to receive(:create)
- .with(hash_including(key: backup_filename))
+ .with(hash_including(key: backup_filename, public: false))
.and_return(true)
Dir.chdir(Gitlab.config.backup.path) do
@@ -338,7 +338,39 @@ describe Backup::Manager do
stub_env('DIRECTORY', 'daily')
expect_any_instance_of(Fog::Collection).to receive(:create)
- .with(hash_including(key: "daily/#{backup_filename}"))
+ .with(hash_including(key: "daily/#{backup_filename}", public: false))
+ .and_return(true)
+
+ Dir.chdir(Gitlab.config.backup.path) do
+ subject.upload
+ end
+ end
+ end
+
+ context 'with Google provider' do
+ before do
+ stub_backup_setting(
+ upload: {
+ connection: {
+ provider: 'Google',
+ google_storage_access_key_id: 'test-access-id',
+ google_storage_secret_access_key: 'secret'
+ },
+ remote_directory: 'directory',
+ multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
+ encryption: nil,
+ encryption_key: nil,
+ storage_class: nil
+ }
+ )
+
+ connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys)
+ connection.directories.create(key: Gitlab.config.backup.upload.remote_directory)
+ end
+
+ it 'does not attempt to set ACL' do
+ expect_any_instance_of(Fog::Collection).to receive(:create)
+ .with(hash_excluding(public: false))
.and_return(true)
Dir.chdir(Gitlab.config.backup.path) do
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index 5f120f258cd..32e718d4b3b 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Backup::Repository do
let(:progress) { StringIO.new }
let!(:project) { create(:project, :wiki_repo) }
+
subject { described_class.new(progress) }
before do
@@ -12,7 +13,9 @@ describe Backup::Repository do
allow(progress).to receive(:print)
allow(FileUtils).to receive(:mv).and_return(true)
- allow_any_instance_of(described_class).to receive(:progress).and_return(progress)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:progress).and_return(progress)
+ end
end
describe '#dump' do
@@ -47,7 +50,9 @@ describe Backup::Repository do
describe 'command failure' do
before do
- allow_any_instance_of(Gitlab::Shell).to receive(:create_repository).and_return(false)
+ allow_next_instance_of(Gitlab::Shell) do |instance|
+ allow(instance).to receive(:create_repository).and_return(false)
+ end
end
context 'hashed storage' do
diff --git a/spec/lib/backup/uploads_spec.rb b/spec/lib/backup/uploads_spec.rb
index 55b69f29812..1f49baeff69 100644
--- a/spec/lib/backup/uploads_spec.rb
+++ b/spec/lib/backup/uploads_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
describe Backup::Uploads do
let(:progress) { StringIO.new }
+
subject(:backup) { described_class.new(progress) }
describe '#initialize' do
diff --git a/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb b/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb
new file mode 100644
index 00000000000..317ac7ef854
--- /dev/null
+++ b/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::BroadcastMessageSanitizationFilter do
+ include FilterSpecHelper
+
+ it_behaves_like 'default whitelist'
+
+ describe 'custom whitelist' do
+ it_behaves_like 'XSS prevention'
+ it_behaves_like 'sanitize link'
+
+ subject { filter(exp).to_html }
+
+ context 'allows `a` elements' do
+ let(:exp) { %q{<a href="/">Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'allows `br` elements' do
+ let(:exp) { %q{Hello<br>World} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'when `a` elements have `style` attribute' do
+ let(:whitelisted_style) { 'color: red; border: blue; background: green; padding: 10px; margin: 10px; text-decoration: underline;' }
+
+ context 'allows specific properties' do
+ let(:exp) { %{<a href="#" style="#{whitelisted_style}">Stylish Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ it 'disallows other properties in `style` attribute on `a` elements' do
+ style = [whitelisted_style, 'position: fixed'].join(';')
+ doc = filter(%{<a href="#" style="#{style}">Stylish Link</a>})
+
+ expect(doc.at_css('a')['style']).to eq(whitelisted_style)
+ end
+ end
+
+ context 'allows `class` on `a` elements' do
+ let(:exp) { %q{<a href="#" class="btn">Button Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index 326703eea05..63ec597a0ba 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -60,7 +60,9 @@ describe Banzai::Filter::CommitReferenceFilter do
end
it 'escapes the title attribute' do
- allow_any_instance_of(Commit).to receive(:title).and_return(%{"></a>whatever<a title="})
+ allow_next_instance_of(Commit) do |instance|
+ allow(instance).to receive(:title).and_return(%{"></a>whatever<a title="})
+ end
doc = reference_filter("See #{reference}")
expect(doc.text).to eq "See #{commit.short_id}"
diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
index 745b9133529..e2615ea5069 100644
--- a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
@@ -55,11 +55,29 @@ describe Banzai::Filter::InlineMetricsRedactorFilter do
it_behaves_like 'a supported metrics dashboard url'
end
- context 'for an internal non-dashboard url' do
- let(:url) { urls.project_url(project) }
+ context 'the user has requisite permissions' do
+ let(:user) { create(:user) }
+ let(:doc) { filter(input, current_user: user) }
- it 'leaves the placeholder' do
- expect(doc.to_s).to be_empty
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'for an internal non-dashboard url' do
+ let(:url) { urls.project_url(project) }
+
+ it 'leaves the placeholder' do
+ expect(doc.to_s).to be_empty
+ end
+ end
+
+ context 'with over 100 embeds' do
+ let(:embed) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) }
+ let(:input) { embed * 150 }
+
+ it 'redacts ill-advised embeds' do
+ expect(doc.to_s.length).to eq(embed.length * 100)
+ end
end
end
end
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index 06df67facf9..d0a43564903 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -7,13 +7,17 @@ describe Banzai::Filter::MarkdownFilter do
describe 'markdown engine from context' do
it 'defaults to CommonMark' do
- expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
+ expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
+ expect(instance).to receive(:render).and_return('test')
+ end
filter('test')
end
it 'uses CommonMark' do
- expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
+ expect_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
+ expect(instance).to receive(:render).and_return('test')
+ end
filter('test', { markdown_engine: :common_mark })
end
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index ab0c2c383c5..2fe8c9074df 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -214,7 +214,9 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
it 'escapes the name attribute' do
- allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+ allow_next_instance_of(Milestone) do |instance|
+ allow(instance).to receive(:title).and_return(%{"></a>whatever<a title="})
+ end
doc = reference_filter("See #{reference}")
@@ -251,7 +253,9 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
it 'escapes the name attribute' do
- allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+ allow_next_instance_of(Milestone) do |instance|
+ allow(instance).to receive(:title).and_return(%{"></a>whatever<a title="})
+ end
doc = reference_filter("See #{reference}")
@@ -288,7 +292,9 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
it 'escapes the name attribute' do
- allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+ allow_next_instance_of(Milestone) do |instance|
+ allow(instance).to receive(:title).and_return(%{"></a>whatever<a title="})
+ end
doc = reference_filter("See #{reference}")
diff --git a/spec/lib/banzai/filter/reference_redactor_filter_spec.rb b/spec/lib/banzai/filter/reference_redactor_filter_spec.rb
index dc888a47988..9739afd3d57 100644
--- a/spec/lib/banzai/filter/reference_redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/reference_redactor_filter_spec.rb
@@ -42,7 +42,9 @@ describe Banzai::Filter::ReferenceRedactorFilter do
context 'valid projects' do
before do
- allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true)
+ allow_next_instance_of(Banzai::ReferenceParser::BaseParser) do |instance|
+ allow(instance).to receive(:can_read_reference?).and_return(true)
+ end
end
it 'allows permitted Project references' do
@@ -59,7 +61,9 @@ describe Banzai::Filter::ReferenceRedactorFilter do
context 'invalid projects' do
before do
- allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false)
+ allow_next_instance_of(Banzai::ReferenceParser::BaseParser) do |instance|
+ allow(instance).to receive(:can_read_reference?).and_return(false)
+ end
end
it 'removes unpermitted references' do
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 371c7a2347c..a17a645d4d0 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -96,21 +96,25 @@ describe Banzai::Filter::RelativeLinkFilter do
context 'with a project_wiki' do
let(:project_wiki) { double('ProjectWiki') }
+
include_examples :preserve_unchanged
end
context 'without a repository' do
let(:project) { create(:project) }
+
include_examples :preserve_unchanged
end
context 'with an empty repository' do
let(:project) { create(:project_empty_repo) }
+
include_examples :preserve_unchanged
end
context 'without project repository access' do
let(:project) { create(:project, :repository, repository_access_level: ProjectFeature::PRIVATE) }
+
include_examples :preserve_unchanged
end
@@ -269,6 +273,7 @@ describe Banzai::Filter::RelativeLinkFilter do
context 'when requested path is a file in the repo' do
let(:requested_path) { 'doc/api/README.md' }
+
it 'rebuilds URL relative to the containing directory' do
doc = filter(link('users.md'))
expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md"
@@ -277,6 +282,7 @@ describe Banzai::Filter::RelativeLinkFilter do
context 'when requested path is a directory in the repo' do
let(:requested_path) { 'doc/api/' }
+
it 'rebuilds URL relative to the directory' do
doc = filter(link('users.md'))
expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md"
@@ -287,6 +293,7 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:ref) { '100%branch' }
let(:commit) { project.commit('1b12f15a11fc6e62177bef08f47bc7b5ce50b141') }
let(:requested_path) { 'foo/bar/' }
+
it 'correctly escapes the ref' do
doc = filter(link('.gitkeep'))
expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/foo/bar/.gitkeep"
@@ -316,6 +323,7 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:ref) { 'master' }
let(:commit) { project.commit('38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') }
let(:requested_path) { 'with space/' }
+
it 'does not escape the space twice' do
doc = filter(link('README.md'))
expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/with%20space/README.md"
@@ -328,7 +336,9 @@ describe Banzai::Filter::RelativeLinkFilter do
end
context 'with a valid ref' do
- let(:commit) { nil } # force filter to use ref instead of commit
+ # force filter to use ref instead of commit
+ let(:commit) { nil }
+
include_examples :valid_repository
end
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index 8a4b819e4d6..607dc3fda47 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -5,48 +5,12 @@ require 'spec_helper'
describe Banzai::Filter::SanitizationFilter do
include FilterSpecHelper
- describe 'default whitelist' do
- it 'sanitizes tags that are not whitelisted' do
- act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>}
- exp = 'no inputs and no blinks'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes tag attributes' do
- act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>}
- exp = %q{<a href="http://example.com/bar.html">Text</a>}
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes javascript in attributes' do
- act = %q(<a href="javascript:alert('foo')">Text</a>)
- exp = '<a>Text</a>'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes mixed-cased javascript in attributes' do
- act = %q(<a href="javaScript:alert('foo')">Text</a>)
- exp = '<a>Text</a>'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'allows whitelisted HTML tags from the user' do
- exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes `class` attribute on any element' do
- act = %q{<strong class="foo">Strong</strong>}
- expect(filter(act).to_html).to eq %q{<strong>Strong</strong>}
- end
-
- it 'sanitizes `id` attribute on any element' do
- act = %q{<em id="foo">Emphasis</em>}
- expect(filter(act).to_html).to eq %q{<em>Emphasis</em>}
- end
- end
+ it_behaves_like 'default whitelist'
describe 'custom whitelist' do
+ it_behaves_like 'XSS prevention'
+ it_behaves_like 'sanitize link'
+
it 'customizes the whitelist only once' do
instance = described_class.new('Foo')
control_count = instance.whitelist[:transformers].size
@@ -167,142 +131,6 @@ describe Banzai::Filter::SanitizationFilter do
expect(filter(html).to_html).to eq(output)
end
- it 'removes `rel` attribute from `a` elements' do
- act = %q{<a href="#" rel="nofollow">Link</a>}
- exp = %q{<a href="#">Link</a>}
-
- expect(filter(act).to_html).to eq exp
- end
-
- # Adapted from the Sanitize test suite: http://git.io/vczrM
- protocols = {
- 'protocol-based JS injection: simple, no spaces' => {
- input: '<a href="javascript:alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces before' => {
- input: '<a href="javascript :alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces after' => {
- input: '<a href="javascript: alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces before and after' => {
- input: '<a href="javascript : alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: preceding colon' => {
- input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: UTF-8 encoding' => {
- input: '<a href="javascript&#58;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long UTF-8 encoding' => {
- input: '<a href="javascript&#0058;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long UTF-8 encoding without semicolons' => {
- input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: hex encoding' => {
- input: '<a href="javascript&#x3A;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long hex encoding' => {
- input: '<a href="javascript&#x003A;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: hex encoding without semicolons' => {
- input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: null char' => {
- input: "<a href=java\0script:alert(\"XSS\")>foo</a>",
- output: '<a href="java"></a>'
- },
-
- 'protocol-based JS injection: invalid URL char' => {
- input: '<img src=java\script:alert("XSS")>',
- output: '<img>'
- },
-
- 'protocol-based JS injection: Unicode' => {
- input: %Q(<a href="\u0001java\u0003script:alert('XSS')">foo</a>),
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: spaces and entities' => {
- input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
- output: '<a href="">foo</a>'
- },
-
- 'protocol whitespace' => {
- input: '<a href=" http://example.com/"></a>',
- output: '<a href="http://example.com/"></a>'
- }
- }
-
- protocols.each do |name, data|
- it "disallows #{name}" do
- doc = filter(data[:input])
-
- expect(doc.to_html).to eq data[:output]
- end
- end
-
- it 'disallows data links' do
- input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>XSS</a>'
- end
-
- it 'disallows vbscript links' do
- input = '<a href="vbscript:alert(document.domain)">XSS</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>XSS</a>'
- end
-
- it 'disallows invalid URIs' do
- expect(Addressable::URI).to receive(:parse).with('foo://example.com')
- .and_raise(Addressable::URI::InvalidURIError)
-
- input = '<a href="foo://example.com">Foo</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>Foo</a>'
- end
-
- it 'allows non-standard anchor schemes' do
- exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>}
- act = filter(exp)
-
- expect(act.to_html).to eq exp
- end
-
- it 'allows relative links' do
- exp = %q{<a href="foo/bar.md">foo/bar.md</a>}
- act = filter(exp)
-
- expect(act.to_html).to eq exp
- end
-
it 'allows the `data-sourcepos` attribute globally' do
exp = %q{<p data-sourcepos="1:1-1:10">foo/bar.md</p>}
act = filter(exp)
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index f220ccecee1..5a844fb61e3 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -92,7 +92,9 @@ describe Banzai::Filter::SyntaxHighlightFilter do
context "when Rouge lexing fails" do
before do
- allow_any_instance_of(Rouge::Lexers::Ruby).to receive(:stream_tokens).and_raise(StandardError)
+ allow_next_instance_of(Rouge::Lexers::Ruby) do |instance|
+ allow(instance).to receive(:stream_tokens).and_raise(StandardError)
+ end
end
it "highlights as plaintext" do
@@ -106,7 +108,9 @@ describe Banzai::Filter::SyntaxHighlightFilter do
context "when Rouge lexing fails after a retry" do
before do
- allow_any_instance_of(Rouge::Lexers::PlainText).to receive(:stream_tokens).and_raise(StandardError)
+ allow_next_instance_of(Rouge::Lexers::PlainText) do |instance|
+ allow(instance).to receive(:stream_tokens).and_raise(StandardError)
+ end
end
it "does not add highlighting classes" do
diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb
index a523608fa50..aef11775e60 100644
--- a/spec/lib/banzai/object_renderer_spec.rb
+++ b/spec/lib/banzai/object_renderer_spec.rb
@@ -25,7 +25,9 @@ describe Banzai::ObjectRenderer do
end
it 'calls Banzai::ReferenceRedactor to perform redaction' do
- expect_any_instance_of(Banzai::ReferenceRedactor).to receive(:redact).and_call_original
+ expect_next_instance_of(Banzai::ReferenceRedactor) do |instance|
+ expect(instance).to receive(:redact).and_call_original
+ end
renderer.render([object], :note)
end
@@ -85,7 +87,9 @@ describe Banzai::ObjectRenderer do
end
it 'calls Banzai::ReferenceRedactor to perform redaction' do
- expect_any_instance_of(Banzai::ReferenceRedactor).to receive(:redact).and_call_original
+ expect_next_instance_of(Banzai::ReferenceRedactor) do |instance|
+ expect(instance).to receive(:redact).and_call_original
+ end
renderer.render([cacheless_thing], :title)
end
diff --git a/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
new file mode 100644
index 00000000000..9832b132b58
--- /dev/null
+++ b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Pipeline::BroadcastMessagePipeline do
+ before do
+ stub_commonmark_sourcepos_disabled
+ end
+
+ subject { described_class.to_html(exp, project: spy) }
+
+ context "allows `a` elements" do
+ let(:exp) { "<a>Link</a>" }
+
+ it { is_expected.to eq("<p>#{exp}</p>") }
+ end
+
+ context "allows `br` elements" do
+ let(:exp) { "Hello<br>World" }
+
+ it { is_expected.to eq("<p>#{exp}</p>") }
+ end
+end
diff --git a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
index 26f2b0b0acf..e1814ea403e 100644
--- a/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/wiki_pipeline_spec.rb
@@ -72,14 +72,14 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "[Page](./page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page\"")
end
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](./page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page.md\"")
end
end
@@ -88,14 +88,14 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "[Link to Page](../page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/page\"")
end
it "rewrites file links to be at the scope of the parent directory" do
markdown = "[Link to Page](../page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/page.md\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/page.md\"")
end
end
@@ -104,14 +104,14 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "[Link to Page](./subdirectory/page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/subdirectory/page\"")
end
it "rewrites file links to be at the scope of the sub-directory" do
markdown = "[Link to Page](./subdirectory/page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/subdirectory/page.md\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/subdirectory/page.md\"")
end
end
@@ -120,35 +120,35 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "[Link to Page](page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page\"")
end
it 'rewrites non-file links (with spaces) to be at the scope of the wiki root' do
markdown = "[Link to Page](page slug)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page%20slug\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page%20slug\"")
end
it "rewrites file links to be at the scope of the current directory" do
markdown = "[Link to Page](page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/page.md\"")
end
it 'rewrites links with anchor' do
markdown = '[Link to Header](start-page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/start-page#title\"")
end
it 'rewrites links (with spaces) with anchor' do
markdown = '[Link to Header](start page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start%20page#title\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/start%20page#title\"")
end
end
@@ -157,14 +157,14 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "[Link to Page](/page)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page\"")
end
it 'rewrites file links to be at the scope of the wiki root' do
markdown = "[Link to Page](/page.md)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/page.md\"")
+ expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/-/wikis/page.md\"")
end
end
end
@@ -270,28 +270,28 @@ describe Banzai::Pipeline::WikiPipeline do
markdown = "![video_file](video_file_name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video_file_name.mp4"')
+ expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/video_file_name.mp4"')
end
it 'rewrites and replaces video links names with white spaces to %20' do
markdown = "![video file](video file name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video%20file%20name.mp4"')
+ expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/video%20file%20name.mp4"')
end
it 'generates audio html structure' do
markdown = "![audio_file](audio_file_name.wav)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio_file_name.wav"')
+ expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/audio_file_name.wav"')
end
it 'rewrites and replaces audio links names with white spaces to %20' do
markdown = "![audio file](audio file name.wav)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
- expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/audio%20file%20name.wav"')
+ expect(output).to include('<audio src="/wiki_link_ns/wiki_link_project/-/wikis/nested/twice/audio%20file%20name.wav"')
end
end
end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index eac1cf16a8f..7f7c750fe74 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -35,8 +35,9 @@ describe Banzai::ReferenceParser::CommitParser do
it 'returns an Array of commits' do
commit = double(:commit)
- allow_any_instance_of(Project).to receive(:valid_repo?)
- .and_return(true)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:valid_repo?).and_return(true)
+ end
expect(subject).to receive(:find_commits)
.with(project, ['123'])
@@ -46,8 +47,9 @@ describe Banzai::ReferenceParser::CommitParser do
end
it 'returns an empty Array when the commit could not be found' do
- allow_any_instance_of(Project).to receive(:valid_repo?)
- .and_return(true)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:valid_repo?).and_return(true)
+ end
expect(subject).to receive(:find_commits)
.with(project, ['123'])
@@ -57,8 +59,9 @@ describe Banzai::ReferenceParser::CommitParser do
end
it 'skips projects without valid repositories' do
- allow_any_instance_of(Project).to receive(:valid_repo?)
- .and_return(false)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:valid_repo?).and_return(false)
+ end
expect(subject.referenced_by([link])).to eq([])
end
@@ -66,8 +69,9 @@ describe Banzai::ReferenceParser::CommitParser do
context 'when the link does not have a data-commit attribute' do
it 'returns an empty Array' do
- allow_any_instance_of(Project).to receive(:valid_repo?)
- .and_return(true)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:valid_repo?).and_return(true)
+ end
expect(subject.referenced_by([link])).to eq([])
end
@@ -76,8 +80,9 @@ describe Banzai::ReferenceParser::CommitParser do
context 'when the link does not have a data-project attribute' do
it 'returns an empty Array' do
- allow_any_instance_of(Project).to receive(:valid_repo?)
- .and_return(true)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:valid_repo?).and_return(true)
+ end
expect(subject.referenced_by([link])).to eq([])
end
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index a925d294b1b..ac321aca5e9 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -9,6 +9,7 @@ describe Banzai::ReferenceParser::IssueParser do
let(:user) { create(:user) }
let(:issue) { create(:issue, project: project) }
let(:link) { empty_html_link }
+
subject { described_class.new(Banzai::RenderContext.new(project, user)) }
describe '#nodes_visible_to_user' do
diff --git a/spec/lib/banzai/reference_parser/mentioned_users_by_group_parser_spec.rb b/spec/lib/banzai/reference_parser/mentioned_group_parser_spec.rb
index 99d607629eb..30b99f3eda7 100644
--- a/spec/lib/banzai/reference_parser/mentioned_users_by_group_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/mentioned_group_parser_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Banzai::ReferenceParser::MentionedUsersByGroupParser do
+describe Banzai::ReferenceParser::MentionedGroupParser do
include ReferenceParserHelpers
let(:group) { create(:group, :private) }
diff --git a/spec/lib/banzai/reference_parser/mentioned_users_by_project_parser_spec.rb b/spec/lib/banzai/reference_parser/mentioned_project_parser_spec.rb
index 155f2189d9e..154f7c4dc36 100644
--- a/spec/lib/banzai/reference_parser/mentioned_users_by_project_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/mentioned_project_parser_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Banzai::ReferenceParser::MentionedUsersByProjectParser do
+describe Banzai::ReferenceParser::MentionedProjectParser do
include ReferenceParserHelpers
let(:group) { create(:group, :private) }
diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
index 05dc1cb4d2d..6581ed0d7c3 100644
--- a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
@@ -200,6 +200,7 @@ describe Banzai::ReferenceParser::SnippetParser do
describe '#referenced_by' do
let(:snippet) { create(:snippet, project: project) }
+
describe 'when the link has a data-snippet attribute' do
context 'using an existing snippet ID' do
it 'returns an Array of snippets' do
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 931fb1e3953..71d2e1de3b6 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -19,15 +19,23 @@ describe Banzai::ReferenceParser::UserParser do
link['data-group'] = project.group.id.to_s
end
- it 'returns the users of the group' do
- create(:group_member, group: group, user: user)
-
- expect(subject.referenced_by([link])).to eq([user])
- end
-
it 'returns an empty Array when the group has no users' do
expect(subject.referenced_by([link])).to eq([])
end
+
+ context 'when group has members' do
+ let!(:group_member) { create(:group_member, group: group, user: user) }
+
+ it 'returns the users of the group' do
+ expect(subject.referenced_by([link])).to eq([user])
+ end
+
+ it 'returns an empty Array when the group has mentions disabled' do
+ group.update!(mentions_disabled: true)
+
+ expect(subject.referenced_by([link])).to eq([])
+ end
+ end
end
context 'using a non-existing group ID' do
diff --git a/spec/lib/banzai/reference_redactor_spec.rb b/spec/lib/banzai/reference_redactor_spec.rb
index c30a194a0b3..0dec6395fb3 100644
--- a/spec/lib/banzai/reference_redactor_spec.rb
+++ b/spec/lib/banzai/reference_redactor_spec.rb
@@ -36,6 +36,7 @@ describe Banzai::ReferenceRedactor do
context 'when data-original attribute provided' do
let(:original_content) { '<code>foo</code>' }
+
it 'replaces redacted reference with original content' do
doc = Nokogiri::HTML.fragment("<a class='gfm' href='https://www.gitlab.com' data-reference-type='issue' data-original='#{original_content}'>bar</a>")
redactor.redact([doc])
@@ -173,10 +174,11 @@ describe Banzai::ReferenceRedactor do
doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>')
node = doc.children[0]
- expect_any_instance_of(Banzai::ReferenceParser::IssueParser)
- .to receive(:nodes_visible_to_user)
- .with(user, [node])
- .and_return([node])
+ expect_next_instance_of(Banzai::ReferenceParser::IssueParser) do |instance|
+ expect(instance).to receive(:nodes_visible_to_user)
+ .with(user, [node])
+ .and_return([node])
+ end
expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node]))
end
diff --git a/spec/lib/bitbucket/connection_spec.rb b/spec/lib/bitbucket/connection_spec.rb
index ec8eac232cd..5aca93767dc 100644
--- a/spec/lib/bitbucket/connection_spec.rb
+++ b/spec/lib/bitbucket/connection_spec.rb
@@ -4,12 +4,16 @@ require 'spec_helper'
describe Bitbucket::Connection do
before do
- allow_any_instance_of(described_class).to receive(:provider).and_return(double(app_id: '', app_secret: ''))
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:provider).and_return(double(app_id: '', app_secret: ''))
+ end
end
describe '#get' do
it 'calls OAuth2::AccessToken::get' do
- expect_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(double(parsed: true))
+ expect_next_instance_of(OAuth2::AccessToken) do |instance|
+ expect(instance).to receive(:get).and_return(double(parsed: true))
+ end
connection = described_class.new({})
@@ -19,7 +23,9 @@ describe Bitbucket::Connection do
describe '#expired?' do
it 'calls connection.expired?' do
- expect_any_instance_of(OAuth2::AccessToken).to receive(:expired?).and_return(true)
+ expect_next_instance_of(OAuth2::AccessToken) do |instance|
+ expect(instance).to receive(:expired?).and_return(true)
+ end
expect(described_class.new({}).expired?).to be_truthy
end
@@ -29,7 +35,9 @@ describe Bitbucket::Connection do
it 'calls connection.refresh!' do
response = double(token: nil, expires_at: nil, expires_in: nil, refresh_token: nil)
- expect_any_instance_of(OAuth2::AccessToken).to receive(:refresh!).and_return(response)
+ expect_next_instance_of(OAuth2::AccessToken) do |instance|
+ expect(instance).to receive(:refresh!).and_return(response)
+ end
described_class.new({}).refresh!
end
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
index ac3221ecab7..963e1d5b8e0 100644
--- a/spec/lib/constraints/project_url_constrainer_spec.rb
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -37,11 +37,13 @@ describe Constraints::ProjectUrlConstrainer do
context 'and is a GET request' do
let(:request) { build_request(namespace.full_path, old_project_path) }
+
it { expect(subject.matches?(request)).to be_truthy }
end
context 'and is NOT a GET request' do
let(:request) { build_request(namespace.full_path, old_project_path, 'POST') }
+
it { expect(subject.matches?(request)).to be_falsey }
end
end
diff --git a/spec/lib/constraints/user_url_constrainer_spec.rb b/spec/lib/constraints/user_url_constrainer_spec.rb
index 15ef930420c..4f665def3bf 100644
--- a/spec/lib/constraints/user_url_constrainer_spec.rb
+++ b/spec/lib/constraints/user_url_constrainer_spec.rb
@@ -24,11 +24,13 @@ describe Constraints::UserUrlConstrainer do
context 'and is a GET request' do
let(:request) { build_request(redirect_route.path) }
+
it { expect(subject.matches?(request)).to be_truthy }
end
context 'and is NOT a GET request' do
let(:request) { build_request(redirect_route.path, 'POST') }
+
it { expect(subject.matches?(request)).to be_falsey }
end
end
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index 3115dfe852f..9447112e4a8 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -97,6 +97,29 @@ describe ContainerRegistry::Tag do
end
end
+ context 'image is a helm chart' do
+ before do
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
+ .with(headers: headers)
+ .to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest_helm.json'),
+ headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
+
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:65a07b841ece031e6d0ec5eb948eacb17aa6d7294cdeb01d5348e86242951487')
+ .with(headers: { 'Accept' => 'application/vnd.cncf.helm.config.v1+json' })
+ .to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob_helm.json'))
+ end
+
+ context '#created_at' do
+ subject { tag.created_at }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
context 'schema v2' do
before do
stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index ffe7584a019..861ef79b2f8 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -88,7 +88,9 @@ describe ExtractsPath do
context 'subclass overrides get_id' do
it 'uses ref returned by get_id' do
- allow_any_instance_of(self.class).to receive(:get_id) { '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' }
+ allow_next_instance_of(self.class) do |instance|
+ allow(instance).to receive(:get_id) { '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' }
+ end
assign_ref_vars
diff --git a/spec/lib/gitaly/server_spec.rb b/spec/lib/gitaly/server_spec.rb
index 12dfad6698d..184d049d1fb 100644
--- a/spec/lib/gitaly/server_spec.rb
+++ b/spec/lib/gitaly/server_spec.rb
@@ -65,4 +65,26 @@ describe Gitaly::Server do
end
end
end
+
+ describe '#expected_version?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:expected_version, :server_version, :result) do
+ '1.1.1' | '1.1.1' | true
+ '1.1.2' | '1.1.1' | false
+ '1.73.0' | '1.73.0-18-gf756ebe2' | false
+ '594c3ea3e0e5540e5915bd1c49713a0381459dd6' | '1.55.6-45-g594c3ea3' | true
+ '594c3ea3e0e5540e5915bd1c49713a0381459dd6' | '1.55.6-46-gabc123ff' | false
+ '594c3ea3e0e5540e5915bd1c49713a0381459dd6' | '1.55.6' | false
+ end
+
+ with_them do
+ it do
+ allow(Gitlab::GitalyClient).to receive(:expected_server_version).and_return(expected_version)
+ allow(server).to receive(:server_version).and_return(server_version)
+
+ expect(server.expected_version?).to eq(result)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/action_rate_limiter_spec.rb b/spec/lib/gitlab/application_rate_limiter_spec.rb
index 8b510a475d2..f1a0163d91c 100644
--- a/spec/lib/gitlab/action_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/application_rate_limiter_spec.rb
@@ -2,30 +2,40 @@
require 'spec_helper'
-describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do
+describe Gitlab::ApplicationRateLimiter, :clean_gitlab_redis_cache do
let(:redis) { double('redis') }
let(:user) { create(:user) }
let(:project) { create(:project) }
+ let(:rate_limits) do
+ {
+ test_action: {
+ threshold: 1,
+ interval: 2.minutes
+ }
+ }
+ end
+ let(:key) { rate_limits.keys[0] }
- subject { described_class.new(action: :test_action, expiry_time: 100) }
+ subject { described_class }
before do
allow(Gitlab::Redis::Cache).to receive(:with).and_yield(redis)
+ allow(described_class).to receive(:rate_limits).and_return(rate_limits)
end
shared_examples 'action rate limiter' do
it 'increases the throttle count and sets the expiration time' do
expect(redis).to receive(:incr).with(cache_key).and_return(1)
- expect(redis).to receive(:expire).with(cache_key, 100)
+ expect(redis).to receive(:expire).with(cache_key, 120)
- expect(subject.throttled?(key, 1)).to be_falsy
+ expect(subject.throttled?(key, scope: scope)).to be_falsy
end
it 'returns true if the key is throttled' do
expect(redis).to receive(:incr).with(cache_key).and_return(2)
expect(redis).not_to receive(:expire)
- expect(subject.throttled?(key, 1)).to be_truthy
+ expect(subject.throttled?(key, scope: scope)).to be_truthy
end
context 'when throttling is disabled' do
@@ -33,16 +43,16 @@ describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do
expect(redis).not_to receive(:incr)
expect(redis).not_to receive(:expire)
- expect(subject.throttled?(key, 0)).to be_falsy
+ expect(subject.throttled?(key, scope: scope, threshold: 0)).to be_falsy
end
end
end
context 'when the key is an array of only ActiveRecord models' do
- let(:key) { [user, project] }
+ let(:scope) { [user, project] }
let(:cache_key) do
- "action_rate_limiter:test_action:user:#{user.id}:project:#{project.id}"
+ "application_rate_limiter:test_action:user:#{user.id}:project:#{project.id}"
end
it_behaves_like 'action rate limiter'
@@ -52,10 +62,10 @@ describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do
let(:project) { create(:project, :public, :repository) }
let(:commit) { project.repository.commit }
let(:path) { 'app/controllers/groups_controller.rb' }
- let(:key) { [project, commit, path] }
+ let(:scope) { [project, commit, path] }
let(:cache_key) do
- "action_rate_limiter:test_action:project:#{project.id}:commit:#{commit.sha}:#{path}"
+ "application_rate_limiter:test_action:project:#{project.id}:commit:#{commit.sha}:#{path}"
end
it_behaves_like 'action rate limiter'
@@ -72,7 +82,7 @@ describe Gitlab::ActionRateLimiter, :clean_gitlab_redis_cache do
let(:base_attributes) do
{
- message: 'Action_Rate_Limiter_Request',
+ message: 'Application_Rate_Limiter_Request',
env: type,
remote_ip: '127.0.0.1',
request_method: 'GET',
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 415a6e62374..38ec04ebe81 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -451,6 +451,7 @@ module Gitlab
context 'with path to a binary file' do
let(:blob) { fake_blob(path: 'dk.png', binary: true) }
+
include_examples :invalid_include
end
@@ -500,6 +501,7 @@ module Gitlab
context 'without a commit (only ref)' do
let(:commit) { nil }
+
include_examples :valid_include
end
end
@@ -511,6 +513,7 @@ module Gitlab
context 'without a commit (only ref)' do
let(:commit) { nil }
+
include_examples :valid_include
end
end
diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb
index dd8070c1240..82ff8e7f76c 100644
--- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb
+++ b/spec/lib/gitlab/auth/auth_finders_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Gitlab::Auth::UserAuthFinders do
+describe Gitlab::Auth::AuthFinders do
include described_class
let(:user) { create(:user) }
@@ -116,9 +116,9 @@ describe Gitlab::Auth::UserAuthFinders do
end
describe '#find_user_from_static_object_token' do
- context 'when request format is archive' do
+ shared_examples 'static object request' do
before do
- env['SCRIPT_NAME'] = 'project/-/archive/master.zip'
+ env['SCRIPT_NAME'] = path
end
context 'when token header param is present' do
@@ -126,7 +126,7 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns the user' do
request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token
- expect(find_user_from_static_object_token(:archive)).to eq(user)
+ expect(find_user_from_static_object_token(format)).to eq(user)
end
end
@@ -134,7 +134,7 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns the user' do
request.headers['X-Gitlab-Static-Object-Token'] = 'foobar'
- expect { find_user_from_static_object_token(:archive) }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ expect { find_user_from_static_object_token(format) }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
end
@@ -144,7 +144,7 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns the user' do
set_param(:token, user.static_object_token)
- expect(find_user_from_static_object_token(:archive)).to eq(user)
+ expect(find_user_from_static_object_token(format)).to eq(user)
end
end
@@ -152,13 +152,27 @@ describe Gitlab::Auth::UserAuthFinders do
it 'returns the user' do
set_param(:token, 'foobar')
- expect { find_user_from_static_object_token(:archive) }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ expect { find_user_from_static_object_token(format) }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
end
end
- context 'when request format is not archive' do
+ context 'when request format is archive' do
+ it_behaves_like 'static object request' do
+ let_it_be(:path) { 'project/-/archive/master.zip' }
+ let_it_be(:format) { :archive }
+ end
+ end
+
+ context 'when request format is blob' do
+ it_behaves_like 'static object request' do
+ let_it_be(:path) { 'project/raw/master/README.md' }
+ let_it_be(:format) { :blob }
+ end
+ end
+
+ context 'when request format is not archive nor blob' do
before do
env['script_name'] = 'url'
end
@@ -182,13 +196,13 @@ describe Gitlab::Auth::UserAuthFinders do
context 'when validate_access_token! returns valid' do
it 'returns user' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_user_from_access_token).to eq user
end
it 'returns exception if token has no user' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(PersonalAccessToken).to receive(:user).and_return(nil)
expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
@@ -214,7 +228,7 @@ describe Gitlab::Auth::UserAuthFinders do
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'returns exception if token has no user' do
@@ -265,7 +279,7 @@ describe Gitlab::Auth::UserAuthFinders do
context 'passed as header' do
it 'returns token if valid personal_access_token' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
+ env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_personal_access_token).to eq personal_access_token
end
@@ -273,7 +287,7 @@ describe Gitlab::Auth::UserAuthFinders do
context 'passed as param' do
it 'returns token if valid personal_access_token' do
- set_param(Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_PARAM, personal_access_token.token)
+ set_param(described_class::PRIVATE_TOKEN_PARAM, personal_access_token.token)
expect(find_personal_access_token).to eq personal_access_token
end
@@ -284,7 +298,7 @@ describe Gitlab::Auth::UserAuthFinders do
end
it 'returns exception if invalid personal_access_token' do
- env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid_token'
+ env[described_class::PRIVATE_TOKEN_HEADER] = 'invalid_token'
expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
@@ -321,6 +335,72 @@ describe Gitlab::Auth::UserAuthFinders do
end
end
+ describe '#find_user_from_basic_auth_job' do
+ def basic_http_auth(username, password)
+ ActionController::HttpAuthentication::Basic.encode_credentials(username, password)
+ end
+
+ def set_auth(username, password)
+ env['HTTP_AUTHORIZATION'] = basic_http_auth(username, password)
+ end
+
+ subject { find_user_from_basic_auth_job }
+
+ context 'when the request does not have AUTHORIZATION header' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'with wrong credentials' do
+ it 'returns nil without user and password' do
+ set_auth(nil, nil)
+
+ is_expected.to be_nil
+ end
+
+ it 'returns nil without password' do
+ set_auth('some-user', nil)
+
+ is_expected.to be_nil
+ end
+
+ it 'returns nil without user' do
+ set_auth(nil, 'password')
+
+ is_expected.to be_nil
+ end
+
+ it 'returns nil without CI username' do
+ set_auth('user', 'password')
+
+ is_expected.to be_nil
+ end
+ end
+
+ context 'with CI username' do
+ let(:username) { ::Ci::Build::CI_REGISTRY_USER }
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, user: user) }
+
+ it 'returns nil without password' do
+ set_auth(username, nil)
+
+ is_expected.to be_nil
+ end
+
+ it 'returns user with valid token' do
+ set_auth(username, build.token)
+
+ is_expected.to eq user
+ end
+
+ it 'raises error with invalid token' do
+ set_auth(username, 'token')
+
+ expect { subject }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ end
+ end
+ end
+
describe '#validate_access_token!' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
@@ -365,4 +445,58 @@ describe Gitlab::Auth::UserAuthFinders do
end
end
end
+
+ describe '#find_runner_from_token' do
+ let(:runner) { create(:ci_runner) }
+
+ context 'with API requests' do
+ before do
+ env['SCRIPT_NAME'] = '/api/endpoint'
+ end
+
+ it 'returns the runner if token is valid' do
+ set_param(:token, runner.token)
+
+ expect(find_runner_from_token).to eq(runner)
+ end
+
+ it 'returns nil if token is not present' do
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if token is blank' do
+ set_param(:token, '')
+
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns exception if invalid token' do
+ set_param(:token, 'invalid_token')
+
+ expect { find_runner_from_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
+ end
+ end
+
+ context 'without API requests' do
+ before do
+ env['SCRIPT_NAME'] = 'url.ics'
+ end
+
+ it 'returns nil if token is valid' do
+ set_param(:token, runner.token)
+
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if token is blank' do
+ expect(find_runner_from_token).to be_nil
+ end
+
+ it 'returns nil if invalid token' do
+ set_param(:token, 'invalid_token')
+
+ expect(find_runner_from_token).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb
index b93d460cf48..3b3db0f7315 100644
--- a/spec/lib/gitlab/auth/current_user_mode_spec.rb
+++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb
@@ -62,69 +62,90 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
context 'when the user is an admin' do
let(:user) { build(:user, :admin) }
- it 'is false by default' do
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'cannot be enabled with an invalid password' do
- subject.enable_admin_mode!(password: nil)
-
- expect(subject.admin_mode?).to be(false)
- end
+ context 'when admin mode not requested' do
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
- it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password)
+ it 'raises exception if we try to enable it' do
+ expect do
+ subject.enable_admin_mode!(password: user.password)
+ end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
- expect(subject.admin_mode?).to be(true)
+ expect(subject.admin_mode?).to be(false)
+ end
end
- it 'can be disabled' do
- subject.enable_admin_mode!(password: user.password)
- subject.disable_admin_mode!
-
- expect(subject.admin_mode?).to be(false)
- end
+ context 'when admin mode requested first' do
+ before do
+ subject.request_admin_mode!
+ end
- it 'will expire in the future' do
- subject.enable_admin_mode!(password: user.password)
- expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
- Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
- # in the future this will be a new request, simulate by clearing the RequestStore
- Gitlab::SafeRequestStore.clear!
+ it 'cannot be enabled with an invalid password' do
+ subject.enable_admin_mode!(password: nil)
- expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
+ expect(subject.admin_mode?).to be(false)
end
- end
- context 'skipping password validation' do
it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+ subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
end
- it 'can be enabled with an invalid password' do
- subject.enable_admin_mode!(skip_password_validation: true)
+ it 'can be disabled' do
+ subject.enable_admin_mode!(password: user.password)
+ subject.disable_admin_mode!
- expect(subject.admin_mode?).to be(true)
+ expect(subject.admin_mode?).to be(false)
end
- end
- context 'with two independent sessions' do
- let(:another_session) { {} }
- let(:another_subject) { described_class.new(user) }
+ it 'will expire in the future' do
+ subject.enable_admin_mode!(password: user.password)
+ expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
- before do
- allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
+ Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
+ # in the future this will be a new request, simulate by clearing the RequestStore
+ Gitlab::SafeRequestStore.clear!
+
+ expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
+ end
end
- it 'can be enabled in one and seen in the other' do
- Gitlab::Session.with_session(another_session) do
- another_subject.enable_admin_mode!(password: user.password)
+ context 'skipping password validation' do
+ it 'can be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
end
- expect(subject.admin_mode?).to be(true)
+ it 'can be enabled with an invalid password' do
+ subject.enable_admin_mode!(skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ context 'with two independent sessions' do
+ let(:another_session) { {} }
+ let(:another_subject) { described_class.new(user) }
+
+ before do
+ allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
+ end
+
+ it 'can be enabled in one and seen in the other' do
+ Gitlab::Session.with_session(another_session) do
+ another_subject.request_admin_mode!
+ another_subject.enable_admin_mode!(password: user.password)
+ end
+
+ expect(subject.admin_mode?).to be(true)
+ end
end
end
end
@@ -134,16 +155,28 @@ describe Gitlab::Auth::CurrentUserMode, :do_not_mock_admin_mode do
let(:user) { build(:user, :admin) }
it 'creates a timestamp in the session' do
+ subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of Time.now))
end
end
+ describe '#enable_sessionless_admin_mode!' do
+ let(:user) { build(:user, :admin) }
+
+ it 'enabled admin mode without password' do
+ subject.enable_sessionless_admin_mode!
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'sets the session timestamp to nil' do
+ subject.request_admin_mode!
subject.disable_admin_mode!
expect(session).to include(expected_session_entry(be_nil))
diff --git a/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb b/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
index 8d6bf45ab30..aea1b2921b6 100644
--- a/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
@@ -62,4 +62,36 @@ describe Gitlab::Auth::IpRateLimiter, :use_clean_rails_memory_store_caching do
it_behaves_like 'whitelisted IPs'
end
end
+
+ shared_examples 'skips the rate limiter' do
+ it 'does not call Rack::Attack::Allow2Ban.reset!' do
+ expect(Rack::Attack::Allow2Ban).not_to receive(:reset!)
+
+ subject.reset!
+ end
+
+ it 'does not call Rack::Attack::Allow2Ban.banned?' do
+ expect(Rack::Attack::Allow2Ban).not_to receive(:banned?)
+
+ subject.banned?
+ end
+
+ it 'does not call Rack::Attack::Allow2Ban.filter' do
+ expect(Rack::Attack::Allow2Ban).not_to receive(:filter)
+
+ subject.register_fail!
+ end
+ end
+
+ context 'when IP is whitlisted' do
+ let(:ip) { '127.0.0.1' }
+
+ it_behaves_like 'skips the rate limiter'
+ end
+
+ context 'when rate limiter is disabled' do
+ let(:options) { { enabled: false } }
+
+ it_behaves_like 'skips the rate limiter'
+ end
end
diff --git a/spec/lib/gitlab/auth/ldap/access_spec.rb b/spec/lib/gitlab/auth/ldap/access_spec.rb
index ecdd5b29986..f9eb4a30190 100644
--- a/spec/lib/gitlab/auth/ldap/access_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/access_spec.rb
@@ -136,7 +136,9 @@ describe Gitlab::Auth::LDAP::Access do
context 'without ActiveDirectory enabled' do
before do
allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive(:active_directory).and_return(false)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive(:active_directory).and_return(false)
+ end
end
it 'returns true' do
diff --git a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
index adb8e138ca7..f1050b9f830 100644
--- a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
@@ -58,7 +58,9 @@ describe Gitlab::Auth::LDAP::AuthHash do
end
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive(:attributes).and_return(attributes)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive(:attributes).and_return(attributes)
+ end
end
it "has the correct username" do
diff --git a/spec/lib/gitlab/auth/ldap/authentication_spec.rb b/spec/lib/gitlab/auth/ldap/authentication_spec.rb
index e68e83e4617..ebaf8383ce5 100644
--- a/spec/lib/gitlab/auth/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/authentication_spec.rb
@@ -18,8 +18,9 @@ describe Gitlab::Auth::LDAP::Authentication do
# try only to fake the LDAP call
adapter = double('adapter', dn: dn).as_null_object
- allow_any_instance_of(described_class)
- .to receive(:adapter).and_return(adapter)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:adapter).and_return(adapter)
+ end
expect(described_class.login(login, password)).to be_truthy
end
@@ -27,8 +28,9 @@ describe Gitlab::Auth::LDAP::Authentication do
it "is false if the user does not exist" do
# try only to fake the LDAP call
adapter = double('adapter', dn: dn).as_null_object
- allow_any_instance_of(described_class)
- .to receive(:adapter).and_return(adapter)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:adapter).and_return(adapter)
+ end
expect(described_class.login(login, password)).to be_falsey
end
@@ -38,8 +40,9 @@ describe Gitlab::Auth::LDAP::Authentication do
# try only to fake the LDAP call
adapter = double('adapter', bind_as: nil).as_null_object
- allow_any_instance_of(described_class)
- .to receive(:adapter).and_return(adapter)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:adapter).and_return(adapter)
+ end
expect(described_class.login(login, password)).to be_falsey
end
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index 1e3da4f7c2d..c621c0aa935 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -253,6 +253,7 @@ describe Gitlab::Auth::OAuth::User do
context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, name: 'John Doe', email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') }
+
it "adds the omniauth identity to the LDAP account" do
allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
@@ -396,7 +397,9 @@ describe Gitlab::Auth::OAuth::User do
context "and no account for the LDAP user" do
context 'dont block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: false)
+ end
end
it do
@@ -408,7 +411,9 @@ describe Gitlab::Auth::OAuth::User do
context 'block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: true)
+ end
end
it do
@@ -424,7 +429,9 @@ describe Gitlab::Auth::OAuth::User do
context 'dont block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: false)
+ end
end
it do
@@ -436,7 +443,9 @@ describe Gitlab::Auth::OAuth::User do
context 'block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: true)
+ end
end
it do
@@ -480,7 +489,9 @@ describe Gitlab::Auth::OAuth::User do
context 'dont block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: false)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: false)
+ end
end
it do
@@ -492,7 +503,9 @@ describe Gitlab::Auth::OAuth::User do
context 'block on create (LDAP)' do
before do
- allow_any_instance_of(Gitlab::Auth::LDAP::Config).to receive_messages(block_auto_created_users: true)
+ allow_next_instance_of(Gitlab::Auth::LDAP::Config) do |instance|
+ allow(instance).to receive_messages(block_auto_created_users: true)
+ end
end
it do
diff --git a/spec/lib/gitlab/auth/request_authenticator_spec.rb b/spec/lib/gitlab/auth/request_authenticator_spec.rb
index f7fff389d88..4dbcd0df302 100644
--- a/spec/lib/gitlab/auth/request_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/request_authenticator_spec.rb
@@ -66,4 +66,28 @@ describe Gitlab::Auth::RequestAuthenticator do
expect(subject.find_sessionless_user([:api])).to be_blank
end
end
+
+ describe '#runner' do
+ let!(:runner) { build(:ci_runner) }
+
+ it 'returns the runner using #find_runner_from_token' do
+ expect_any_instance_of(described_class)
+ .to receive(:find_runner_from_token)
+ .and_return(runner)
+
+ expect(subject.runner).to eq runner
+ end
+
+ it 'returns nil if no runner is found' do
+ expect(subject.runner).to be_blank
+ end
+
+ it 'rescue Gitlab::Auth::AuthenticationError exceptions' do
+ expect_any_instance_of(described_class)
+ .to receive(:find_runner_from_token)
+ .and_raise(Gitlab::Auth::UnauthorizedError)
+
+ expect(subject.runner).to be_blank
+ end
+ end
end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index dc4b0b5b1b6..311cbd4dd7e 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-describe Gitlab::Auth do
+describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
let(:gl_auth) { described_class }
+
set(:project) { create(:project) }
describe 'constants' do
@@ -79,6 +80,66 @@ describe Gitlab::Auth do
end
describe 'find_for_git_client' do
+ describe 'rate limiting' do
+ before do
+ stub_rack_attack_setting(enabled: true, ip_whitelist: [])
+ end
+
+ context 'when IP is already banned' do
+ subject { gl_auth.find_for_git_client('username', 'password', project: nil, ip: 'ip') }
+
+ before do
+ expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
+ expect(rate_limiter).to receive(:banned?).and_return(true)
+ end
+ end
+
+ it 'raises an IpBlacklisted exception' do
+ expect { subject }.to raise_error(Gitlab::Auth::IpBlacklisted)
+ end
+ end
+
+ context 'for CI registry user' do
+ let_it_be(:build) { create(:ci_build, :running) }
+
+ it 'skips rate limiting for successful auth' do
+ expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
+ expect(rate_limiter).not_to receive(:reset!)
+ end
+
+ gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: build.project, ip: 'ip')
+ end
+
+ it 'skips rate limiting for failed auth' do
+ expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
+ expect(rate_limiter).not_to receive(:register_fail!)
+ end
+
+ gl_auth.find_for_git_client('gitlab-ci-token', 'wrong_token', project: build.project, ip: 'ip')
+ end
+ end
+
+ context 'for other users' do
+ let_it_be(:user) { create(:user) }
+
+ it 'resets rate limit for successful auth' do
+ expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
+ expect(rate_limiter).to receive(:reset!)
+ end
+
+ gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip')
+ end
+
+ it 'registers failure for failed auth' do
+ expect_next_instance_of(Gitlab::Auth::IpRateLimiter) do |rate_limiter|
+ expect(rate_limiter).to receive(:register_fail!)
+ end
+
+ gl_auth.find_for_git_client(user.username, 'wrong_password', project: nil, ip: 'ip')
+ end
+ end
+ end
+
context 'build token' do
subject { gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: project, ip: 'ip') }
@@ -86,10 +147,6 @@ describe Gitlab::Auth do
let!(:build) { create(:ci_build, :running) }
let(:project) { build.project }
- before do
- expect(gl_auth).not_to receive(:rate_limit!).with('ip', success: true, login: 'gitlab-ci-token')
- end
-
it 'recognises user-less build' do
expect(subject).to eq(Gitlab::Auth::Result.new(nil, build.project, :ci, described_class.build_authentication_abilities))
end
@@ -106,10 +163,6 @@ describe Gitlab::Auth do
let!(:build) { create(:ci_build, status: build_status) }
let(:project) { build.project }
- before do
- expect(gl_auth).not_to receive(:rate_limit!).with('ip', success: false, login: 'gitlab-ci-token')
- end
-
it 'denies authentication' do
expect(subject).to eq(Gitlab::Auth::Result.new)
end
@@ -121,14 +174,12 @@ describe Gitlab::Auth do
project.create_drone_ci_service(active: true)
project.drone_ci_service.update(token: 'token')
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'drone-ci-token')
expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, project, :ci, described_class.build_authentication_abilities))
end
it 'recognizes master passwords' do
user = create(:user, password: 'password')
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, described_class.full_authentication_abilities))
end
@@ -145,7 +196,6 @@ describe Gitlab::Auth do
user = create(:user)
token = Gitlab::LfsToken.new(user).token
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, described_class.read_write_project_authentication_abilities))
end
@@ -153,7 +203,6 @@ describe Gitlab::Auth do
key = create(:deploy_key)
token = Gitlab::LfsToken.new(key).token
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, described_class.read_only_authentication_abilities))
end
@@ -171,7 +220,6 @@ describe Gitlab::Auth do
create(:deploy_keys_project, :write_access, deploy_key: key, project: project)
token = Gitlab::LfsToken.new(key).token
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, described_class.read_write_authentication_abilities))
end
@@ -179,7 +227,6 @@ describe Gitlab::Auth do
key = create(:deploy_key)
token = Gitlab::LfsToken.new(key).token
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, described_class.read_only_authentication_abilities))
end
end
@@ -190,14 +237,12 @@ describe Gitlab::Auth do
let(:application) { Doorkeeper::Application.create!(name: 'MyApp', redirect_uri: 'https://app.com', owner: user) }
it 'succeeds for OAuth tokens with the `api` scope' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'oauth2')
expect(gl_auth.find_for_git_client("oauth2", token_w_api_scope.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, described_class.full_authentication_abilities))
end
it 'fails for OAuth tokens with other scopes' do
token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: 'read_user')
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'oauth2')
expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil))
end
@@ -257,7 +302,7 @@ describe Gitlab::Auth do
end
context 'while using regular user and password' do
- it 'falls through lfs authentication' do
+ it 'goes through lfs authentication' do
user = create(
:user,
username: 'normal_user',
@@ -268,7 +313,7 @@ describe Gitlab::Auth do
.to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, described_class.full_authentication_abilities))
end
- it 'fails through oauth authentication when the username is oauth2' do
+ it 'goes through oauth authentication when the username is oauth2' do
user = create(
:user,
username: 'oauth2',
@@ -283,7 +328,6 @@ describe Gitlab::Auth do
it 'returns double nil for invalid credentials' do
login = 'foo'
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new)
end
@@ -301,10 +345,6 @@ describe Gitlab::Auth do
let(:user) { create(:user, username: username, password: 'my-secret') }
let(:deploy_token) { create(:deploy_token, username: username, read_registry: false, projects: [project]) }
- before do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: username)
- end
-
it 'succeeds for the token' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
@@ -328,13 +368,11 @@ describe Gitlab::Auth do
it 'succeeds for the right token' do
auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
it 'fails for the wrong token' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deployer')
expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
@@ -347,13 +385,11 @@ describe Gitlab::Auth do
it 'succeeds for the right token' do
auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
it 'fails for the wrong token' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deployer')
expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
@@ -367,7 +403,6 @@ describe Gitlab::Auth do
it 'succeeds when login and token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
@@ -376,32 +411,27 @@ describe Gitlab::Auth do
deploy_token = create(:deploy_token, username: 'deployer', read_registry: false, projects: [project])
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
expect(gl_auth.find_for_git_client('deployer', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
it 'fails when login is not valid' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login')
expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails when token is not valid' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is nil' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, nil, project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is not related to project' do
another_deploy_token = create(:deploy_token)
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, another_deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
@@ -410,7 +440,6 @@ describe Gitlab::Auth do
deploy_token.revoke!
expect(deploy_token.revoked?).to be_truthy
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deploy-token')
expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
@@ -428,31 +457,26 @@ describe Gitlab::Auth do
it 'succeeds when login and token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:read_container_image])
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_success)
end
it 'fails when login is not valid' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login')
expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails when token is not valid' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is nil' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, nil, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is not related to project' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, 'abcdef', project: nil, ip: 'ip'))
.to eq(auth_failure)
end
@@ -461,7 +485,6 @@ describe Gitlab::Auth do
deploy_token.revoke!
expect(deploy_token.revoked?).to be_truthy
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deploy-token')
expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
@@ -473,7 +496,6 @@ describe Gitlab::Auth do
end
it 'fails when login and token are valid' do
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
@@ -586,7 +608,6 @@ describe Gitlab::Auth do
private
def expect_results_with_abilities(personal_access_token, abilities, success = true)
- expect(gl_auth).to receive(:rate_limit!).with('ip', success: success, login: '')
expect(gl_auth.find_for_git_client('', personal_access_token&.token, project: nil, ip: 'ip'))
.to eq(Gitlab::Auth::Result.new(personal_access_token&.user, nil, personal_access_token.nil? ? nil : :personal_access_token, abilities))
end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
index c66d7cd6148..5cad479ff05 100644
--- a/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_project_fullpath_in_repo_config_spec.rb
@@ -10,6 +10,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectFullpathInRepoConfig, :migr
describe described_class::Storage::HashedProject do
let(:project) { double(id: 555) }
+
subject(:project_storage) { described_class.new(project) }
it 'has the correct disk_path' do
@@ -19,6 +20,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectFullpathInRepoConfig, :migr
describe described_class::Storage::LegacyProject do
let(:project) { double(full_path: 'this/is/the/full/path') }
+
subject(:project_storage) { described_class.new(project) }
it 'has the correct disk_path' do
@@ -28,6 +30,7 @@ describe Gitlab::BackgroundMigration::BackfillProjectFullpathInRepoConfig, :migr
describe described_class::Project do
let(:project_record) { projects.create!(namespace_id: subgroup.id, name: 'baz', path: 'baz') }
+
subject(:project) { described_class.find(project_record.id) }
describe '#full_path' do
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index ddb1d3cea21..75a23d4f49e 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -75,7 +75,9 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
end
it 'does not schedule an import' do
- expect_any_instance_of(Project).not_to receive(:import_schedule)
+ expect_next_instance_of(Project) do |instance|
+ expect(instance).not_to receive(:import_schedule)
+ end
importer.create_project_if_needed
end
diff --git a/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb b/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb
index 1e969542975..a27f14cd621 100644
--- a/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb
+++ b/spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb
@@ -49,6 +49,7 @@ describe Gitlab::BranchPushMergeCommitAnalyzer do
context 'when relevant_commit_ids is provided' do
let(:relevant_commit_id) { '8a994512e8c8f0dfcf22bb16df6e876be7a61036' }
+
subject { described_class.new(commits, relevant_commit_ids: [relevant_commit_id]) }
it 'returns correct merge commit' do
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index 91e7edaf704..c2816f35cec 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -9,7 +9,9 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
describe '.load_for_project' do
it "loads the status" do
- expect_any_instance_of(described_class).to receive(:load_status)
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:load_status)
+ end
described_class.load_for_project(project)
end
diff --git a/spec/lib/gitlab/chat/command_spec.rb b/spec/lib/gitlab/chat/command_spec.rb
index 46d23ab2b62..f7f344bf786 100644
--- a/spec/lib/gitlab/chat/command_spec.rb
+++ b/spec/lib/gitlab/chat/command_spec.rb
@@ -44,7 +44,7 @@ describe Gitlab::Chat::Command do
let(:pipeline) { command.create_pipeline }
before do
- stub_repository_ci_yaml_file(sha: project.commit.id)
+ stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
project.add_developer(chat_name.user)
end
diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb
index 71b64a3b9df..7cc1722dfd4 100644
--- a/spec/lib/gitlab/checks/branch_check_spec.rb
+++ b/spec/lib/gitlab/checks/branch_check_spec.rb
@@ -32,7 +32,9 @@ describe Gitlab::Checks::BranchCheck do
end
it 'raises an error if the user is not allowed to merge to protected branches' do
- expect_any_instance_of(Gitlab::Checks::MatchingMergeRequest).to receive(:match?).and_return(true)
+ expect_next_instance_of(Gitlab::Checks::MatchingMergeRequest) do |instance|
+ expect(instance).to receive(:match?).and_return(true)
+ end
expect(user_access).to receive(:can_merge_to_branch?).and_return(false)
expect(user_access).to receive(:can_push_to_branch?).and_return(false)
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 3a8e8f67e16..dfc8c59fd74 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -14,31 +14,41 @@ describe Gitlab::Checks::ChangeAccess do
end
it 'calls pushes checks' do
- expect_any_instance_of(Gitlab::Checks::PushCheck).to receive(:validate!)
+ expect_next_instance_of(Gitlab::Checks::PushCheck) do |instance|
+ expect(instance).to receive(:validate!)
+ end
subject.exec
end
it 'calls branches checks' do
- expect_any_instance_of(Gitlab::Checks::BranchCheck).to receive(:validate!)
+ expect_next_instance_of(Gitlab::Checks::BranchCheck) do |instance|
+ expect(instance).to receive(:validate!)
+ end
subject.exec
end
it 'calls tags checks' do
- expect_any_instance_of(Gitlab::Checks::TagCheck).to receive(:validate!)
+ expect_next_instance_of(Gitlab::Checks::TagCheck) do |instance|
+ expect(instance).to receive(:validate!)
+ end
subject.exec
end
it 'calls lfs checks' do
- expect_any_instance_of(Gitlab::Checks::LfsCheck).to receive(:validate!)
+ expect_next_instance_of(Gitlab::Checks::LfsCheck) do |instance|
+ expect(instance).to receive(:validate!)
+ end
subject.exec
end
it 'calls diff checks' do
- expect_any_instance_of(Gitlab::Checks::DiffCheck).to receive(:validate!)
+ expect_next_instance_of(Gitlab::Checks::DiffCheck) do |instance|
+ expect(instance).to receive(:validate!)
+ end
subject.exec
end
diff --git a/spec/lib/gitlab/ci/ansi2json/result_spec.rb b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
new file mode 100644
index 00000000000..5b7b5481400
--- /dev/null
+++ b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Ansi2json::Result do
+ let(:stream) { StringIO.new('hello') }
+ let(:state) { Gitlab::Ci::Ansi2json::State.new(nil, stream.size) }
+ let(:offset) { 0 }
+ let(:params) do
+ { lines: [], state: state, append: false, truncated: false, offset: offset, stream: stream }
+ end
+
+ subject { described_class.new(params) }
+
+ describe '#size' do
+ before do
+ stream.seek(5) # move stream cursor to the end
+ end
+
+ context 'when offset is at the start' do
+ let(:offset) { 0 }
+
+ it 'returns the full size' do
+ expect(subject.size).to eq(5)
+ end
+ end
+
+ context 'when offset is not zero' do
+ let(:offset) { 2 }
+
+ it 'returns the remaining size' do
+ expect(subject.size).to eq(3)
+ end
+ end
+ end
+
+ describe '#total' do
+ it 'returns size of stread' do
+ expect(subject.total).to eq(5)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2json/style_spec.rb b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
index 5110c215415..ad05aa03e83 100644
--- a/spec/lib/gitlab/ci/ansi2json/style_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
@@ -147,6 +147,10 @@ describe Gitlab::Ci::Ansi2json::Style do
[%w[1], %w[0], '', 'resets style from format bold'],
[%w[1 3], %w[0], '', 'resets style from format bold and italic'],
[%w[1 3 term-fg-l-red term-bg-yellow], %w[0], '', 'resets all formats and colors'],
+ # default foreground
+ [%w[31 42], %w[39], 'term-bg-green', 'set foreground from red to default leaving background unchanged'],
+ # default background
+ [%w[31 42], %w[49], 'term-fg-red', 'set background from green to default leaving foreground unchanged'],
# misc
[[], %w[1 30 42 3], 'term-fg-l-black term-bg-green term-bold term-italic', 'adds fg color, bg color and formats from no style'],
[%w[3 31], %w[23 1 43], 'term-fg-l-red term-bg-yellow term-bold', 'replaces format italic with bold and adds a yellow background']
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
index 73c3cad88bc..243c6f06324 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -152,6 +152,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
describe '#blob' do
let(:file_entry) { |example| path(example) }
+
subject { file_entry.blob }
it 'returns a blob representing the entry data' do
diff --git a/spec/lib/gitlab/ci/build/context/build_spec.rb b/spec/lib/gitlab/ci/build/context/build_spec.rb
index 3adde213f59..1b73b9a083d 100644
--- a/spec/lib/gitlab/ci/build/context/build_spec.rb
+++ b/spec/lib/gitlab/ci/build/context/build_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Build::Context::Build do
diff --git a/spec/lib/gitlab/ci/build/context/global_spec.rb b/spec/lib/gitlab/ci/build/context/global_spec.rb
index 6bc8f862779..65cc41ed3f9 100644
--- a/spec/lib/gitlab/ci/build/context/global_spec.rb
+++ b/spec/lib/gitlab/ci/build/context/global_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Ci::Build::Context::Global do
diff --git a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb
index 9148c0d579e..848adb2e6e5 100644
--- a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb
+++ b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb
@@ -12,12 +12,16 @@ describe Gitlab::Ci::Build::Credentials::Factory do
end
before do
- allow_any_instance_of(described_class).to receive(:providers).and_return([TestProvider])
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:providers).and_return([TestProvider])
+ end
end
context 'when provider is valid' do
before do
- allow_any_instance_of(TestProvider).to receive(:valid?).and_return(true)
+ allow_next_instance_of(TestProvider) do |instance|
+ allow(instance).to receive(:valid?).and_return(true)
+ end
end
it 'generates an array of credentials objects' do
@@ -29,7 +33,9 @@ describe Gitlab::Ci::Build::Credentials::Factory do
context 'when provider is not valid' do
before do
- allow_any_instance_of(TestProvider).to receive(:valid?).and_return(false)
+ allow_next_instance_of(TestProvider) do |instance|
+ allow(instance).to receive(:valid?).and_return(false)
+ end
end
it 'generates an array without specific credential object' do
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
index c7a5ac783b3..2493855f851 100644
--- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -38,13 +38,45 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
.and_return(double(execute: kubernetes_namespace))
end
- it { is_expected.to be_falsey }
-
- context 'and the service_account_token is blank' do
- let(:kubernetes_namespace) { instance_double(Clusters::KubernetesNamespace, service_account_token: nil) }
+ context 'and the knative-serving namespace is missing' do
+ before do
+ allow(Clusters::KnativeServingNamespaceFinder).to receive(:new)
+ .and_return(double(execute: false))
+ end
it { is_expected.to be_truthy }
end
+
+ context 'and the knative-serving namespace exists' do
+ before do
+ allow(Clusters::KnativeServingNamespaceFinder).to receive(:new)
+ .and_return(double(execute: true))
+ end
+
+ context 'and the knative version role binding is missing' do
+ before do
+ allow(Clusters::KnativeVersionRoleBindingFinder).to receive(:new)
+ .and_return(double(execute: nil))
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'and the knative version role binding already exists' do
+ before do
+ allow(Clusters::KnativeVersionRoleBindingFinder).to receive(:new)
+ .and_return(double(execute: true))
+ end
+
+ it { is_expected.to be_falsey }
+
+ context 'and the service_account_token is blank' do
+ let(:kubernetes_namespace) { instance_double(Clusters::KubernetesNamespace, service_account_token: nil) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
end
end
@@ -96,6 +128,47 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
subject
end
+
+ context 'the build has a namespace configured via CI template' do
+ let(:kubernetes_namespace) { double(namespace: existing_namespace) }
+
+ before do
+ allow(build).to receive(:expanded_kubernetes_namespace)
+ .and_return(requested_namespace)
+ end
+
+ context 'the requested namespace matches the default' do
+ let(:requested_namespace) { 'production' }
+ let(:existing_namespace) { requested_namespace }
+
+ it 'creates a namespace' do
+ expect(Clusters::BuildKubernetesNamespaceService)
+ .to receive(:new)
+ .with(cluster, environment: deployment.environment)
+ .and_return(namespace_builder)
+
+ expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
+ .to receive(:new)
+ .with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
+ .and_return(service)
+
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'the requested namespace differs from the default' do
+ let(:requested_namespace) { 'production' }
+ let(:existing_namespace) { 'other-namespace' }
+
+ it 'does not create a namespace' do
+ expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
end
context 'kubernetes namespace exists (but has no service_account_token)' do
@@ -115,6 +188,24 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
subject
end
end
+
+ context 'knative version role binding is missing' do
+ before do
+ allow(Clusters::KubernetesNamespaceFinder).to receive(:new)
+ .and_return(double(execute: kubernetes_namespace))
+ allow(Clusters::KnativeVersionRoleBindingFinder).to receive(:new)
+ .and_return(double(execute: nil))
+ end
+
+ it 'creates the knative version role binding' do
+ expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
+ .to receive(:new)
+ .with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
+ .and_return(service)
+
+ subject
+ end
+ end
end
context 'completion is not required' do
diff --git a/spec/lib/gitlab/ci/config/entry/default_spec.rb b/spec/lib/gitlab/ci/config/entry/default_spec.rb
index dad4f408e50..23c62bbf92a 100644
--- a/spec/lib/gitlab/ci/config/entry/default_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/default_spec.rb
@@ -26,7 +26,8 @@ describe Gitlab::Ci::Config::Entry::Default do
it 'contains the expected node names' do
expect(described_class.nodes.keys)
.to match_array(%i[before_script image services
- after_script cache interruptible])
+ after_script cache interruptible
+ timeout retry tags artifacts])
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 7b72b45fd8d..8c21d5342cc 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -206,6 +206,35 @@ describe Gitlab::Ci::Config::Entry::Environment do
end
end
+ context 'when auto_stop_in is specified' do
+ let(:config) do
+ {
+ name: 'review/$CI_COMMIT_REF_NAME',
+ url: 'https://$CI_COMMIT_REF_NAME.review.gitlab.com',
+ on_stop: 'stop_review',
+ auto_stop_in: auto_stop_in
+ }
+ end
+
+ context 'when auto_stop_in is correct format' do
+ let(:auto_stop_in) { '2 days' }
+
+ it 'becomes valid' do
+ expect(entry).to be_valid
+ expect(entry.auto_stop_in).to eq(auto_stop_in)
+ end
+ end
+
+ context 'when auto_stop_in is invalid format' do
+ let(:auto_stop_in) { 'invalid' }
+
+ it 'becomes invalid' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'environment auto stop in should be a duration'
+ end
+ end
+ end
+
context 'when configuration is invalid' do
context 'when configuration is an array' do
let(:config) { ['env'] }
@@ -241,4 +270,28 @@ describe Gitlab::Ci::Config::Entry::Environment do
end
end
end
+
+ describe 'kubernetes' do
+ let(:config) do
+ { name: 'production', kubernetes: kubernetes_config }
+ end
+
+ context 'is a string' do
+ let(:kubernetes_config) { 'production' }
+
+ it { expect(entry).not_to be_valid }
+ end
+
+ context 'is a hash' do
+ let(:kubernetes_config) { Hash(namespace: 'production') }
+
+ it { expect(entry).to be_valid }
+ end
+
+ context 'is nil' do
+ let(:kubernetes_config) { nil }
+
+ it { expect(entry).to be_valid }
+ 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 fe83171c57a..cc1ee63ff04 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:result) do
%i[before_script script stage type after_script cache
image services only except rules needs variables artifacts
- environment coverage retry interruptible]
+ environment coverage retry interruptible timeout tags]
end
it { is_expected.to match_array result }
@@ -93,7 +93,7 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when delayed job' do
context 'when start_in is specified' do
- let(:config) { { script: 'echo', when: 'delayed', start_in: '1 day' } }
+ let(:config) { { script: 'echo', when: 'delayed', start_in: '1 week' } }
it { expect(entry).to be_valid }
end
@@ -232,11 +232,9 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when delayed job' do
context 'when start_in is specified' do
- let(:config) { { script: 'echo', when: 'delayed', start_in: '1 day' } }
+ let(:config) { { script: 'echo', when: 'delayed', start_in: '1 week' } }
- it 'returns error about invalid type' do
- expect(entry).to be_valid
- end
+ it { expect(entry).to be_valid }
end
context 'when start_in is empty' do
@@ -257,8 +255,8 @@ describe Gitlab::Ci::Config::Entry::Job do
end
end
- context 'when start_in is longer than one day' do
- let(:config) { { when: 'delayed', start_in: '2 days' } }
+ context 'when start_in is longer than one week' do
+ let(:config) { { when: 'delayed', start_in: '8 days' } }
it 'returns error about exceeding the limit' do
expect(entry).not_to be_valid
@@ -417,21 +415,21 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when timeout value is not correct' do
context 'when it is higher than instance wide timeout' do
- let(:config) { { timeout: '3 months' } }
+ let(:config) { { timeout: '3 months', script: 'test' } }
it 'returns error about value too high' do
expect(entry).not_to be_valid
expect(entry.errors)
- .to include "job timeout should not exceed the limit"
+ .to include "timeout config should not exceed the limit"
end
end
context 'when it is not a duration' do
- let(:config) { { timeout: 100 } }
+ let(:config) { { timeout: 100, script: 'test' } }
it 'returns error about wrong value' do
expect(entry).not_to be_valid
- expect(entry.errors).to include 'job timeout should be a duration'
+ expect(entry.errors).to include 'timeout config should be a duration'
end
end
end
@@ -463,7 +461,8 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:unspecified) { double('unspecified', 'specified?' => false) }
let(:default) { double('default', '[]' => unspecified) }
- let(:deps) { double('deps', 'default' => default, '[]' => unspecified) }
+ let(:workflow) { double('workflow', 'has_rules?' => false) }
+ let(:deps) { double('deps', 'default' => default, '[]' => unspecified, 'workflow' => workflow) }
context 'when job config overrides default config' do
before do
@@ -494,6 +493,49 @@ describe Gitlab::Ci::Config::Entry::Job do
expect(entry[:cache].value).to eq(key: 'test', policy: 'pull-push')
end
end
+
+ context 'with workflow rules' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:name, :has_workflow_rules?, :only, :rules, :result) do
+ "uses default only" | false | nil | nil | { refs: %w[branches tags] }
+ "uses user only" | false | %w[branches] | nil | { refs: %w[branches] }
+ "does not define only" | false | nil | [] | nil
+ "does not define only" | true | nil | nil | nil
+ "uses user only" | true | %w[branches] | nil | { refs: %w[branches] }
+ "does not define only" | true | nil | [] | nil
+ end
+
+ with_them do
+ let(:config) { { script: 'ls', rules: rules, only: only }.compact }
+
+ it "#{name}" do
+ expect(workflow).to receive(:has_rules?) { has_workflow_rules? }
+
+ entry.compose!(deps)
+
+ expect(entry.only_value).to eq(result)
+ end
+ end
+ end
+
+ context 'when workflow rules is used' do
+ context 'when rules are used' do
+ let(:config) { { script: 'ls', cache: { key: 'test' }, rules: [] } }
+
+ it 'does not define only' do
+ expect(entry).not_to be_only_defined
+ end
+ end
+
+ context 'when rules are not used' do
+ let(:config) { { script: 'ls', cache: { key: 'test' }, only: [] } }
+
+ it 'does not define only' do
+ expect(entry).not_to be_only_defined
+ end
+ end
+ end
end
context 'when composed' do
diff --git a/spec/lib/gitlab/ci/config/entry/kubernetes_spec.rb b/spec/lib/gitlab/ci/config/entry/kubernetes_spec.rb
new file mode 100644
index 00000000000..468e83ec506
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/kubernetes_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Kubernetes do
+ subject { described_class.new(config) }
+
+ describe 'attributes' do
+ it { is_expected.to respond_to(:namespace) }
+ it { is_expected.to respond_to(:has_namespace?) }
+ end
+
+ describe 'validations' do
+ describe 'config' do
+ context 'is a hash containing known keys' do
+ let(:config) { Hash(namespace: 'namespace') }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'is a hash containing an unknown key' do
+ let(:config) { Hash(unknown: 'attribute') }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'is a string' do
+ let(:config) { 'config' }
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+
+ describe 'namespace' do
+ let(:config) { Hash(namespace: namespace) }
+
+ context 'is a string' do
+ let(:namespace) { 'namespace' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'is a hash' do
+ let(:namespace) { Hash(key: 'namespace') }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'is not present' do
+ let(:namespace) { '' }
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb
index d119e604900..92b71c5f6cc 100644
--- a/spec/lib/gitlab/ci/config/entry/need_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/need_spec.rb
@@ -5,31 +5,177 @@ require 'spec_helper'
describe ::Gitlab::Ci::Config::Entry::Need do
subject(:need) { described_class.new(config) }
- context 'when job is specified' do
- let(:config) { 'job_name' }
+ shared_examples 'job type' do
+ describe '#type' do
+ subject(:need_type) { need.type }
- describe '#valid?' do
- it { is_expected.to be_valid }
+ it { is_expected.to eq(:job) }
+ end
+ end
+
+ context 'with simple config' do
+ context 'when job is specified' do
+ let(:config) { 'job_name' }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name', artifacts: true)
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'when need is empty' do
+ let(:config) { '' }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about an empty config' do
+ expect(need.errors)
+ .to contain_exactly("job string config can't be blank")
+ end
+ end
+
+ it_behaves_like 'job type'
end
+ end
+
+ context 'with complex config' do
+ context 'with job name and artifacts true' do
+ let(:config) { { job: 'job_name', artifacts: true } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name', artifacts: true)
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'with job name and artifacts false' do
+ let(:config) { { job: 'job_name', artifacts: false } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name', artifacts: false)
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'with job name and artifacts nil' do
+ let(:config) { { job: 'job_name', artifacts: nil } }
- describe '#value' do
- it 'returns job needs configuration' do
- expect(need.value).to eq(name: 'job_name')
+ describe '#valid?' do
+ it { is_expected.to be_valid }
end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name', artifacts: true)
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'without artifacts key' do
+ let(:config) { { job: 'job_name' } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name', artifacts: true)
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'when job name is empty' do
+ let(:config) { { job: '', artifacts: true } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about an empty config' do
+ expect(need.errors)
+ .to contain_exactly("job hash job can't be blank")
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'when job name is not a string' do
+ let(:config) { { job: :job_name, artifacts: false } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about job type' do
+ expect(need.errors)
+ .to contain_exactly('job hash job should be a string')
+ end
+ end
+
+ it_behaves_like 'job type'
+ end
+
+ context 'when job has unknown keys' do
+ let(:config) { { job: 'job_name', artifacts: false, some: :key } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about job type' do
+ expect(need.errors)
+ .to contain_exactly('job hash config contains unknown keys: some')
+ end
+ end
+
+ it_behaves_like 'job type'
end
end
- context 'when need is empty' do
- let(:config) { '' }
+ context 'when need config is not a string or a hash' do
+ let(:config) { :job_name }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
- it 'is returns an error about an empty config' do
+ it 'is returns an error about job type' do
expect(need.errors)
- .to contain_exactly("job config can't be blank")
+ .to contain_exactly('unknown strategy has an unsupported type')
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
index f4a76b52d30..b8b84b5efd2 100644
--- a/spec/lib/gitlab/ci/config/entry/needs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
@@ -51,9 +51,34 @@ describe ::Gitlab::Ci::Config::Entry::Needs do
end
end
end
+
+ context 'when wrong needs type is used' do
+ let(:config) { [{ job: 'job_name', artifacts: true, some: :key }] }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors).to contain_exactly(
+ 'need config contains unknown keys: some')
+ end
+ end
+ end
end
describe '.compose!' do
+ shared_examples 'entry with descendant nodes' do
+ describe '#descendants' do
+ it 'creates valid descendant nodes' do
+ expect(needs.descendants.count).to eq 2
+ expect(needs.descendants)
+ .to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
+ end
+ end
+ end
+
context 'when valid job entries composed' do
let(:config) { %w[first_job_name second_job_name] }
@@ -65,18 +90,80 @@ describe ::Gitlab::Ci::Config::Entry::Needs do
it 'returns key value' do
expect(needs.value).to eq(
job: [
- { name: 'first_job_name' },
- { name: 'second_job_name' }
+ { name: 'first_job_name', artifacts: true },
+ { name: 'second_job_name', artifacts: true }
]
)
end
end
- describe '#descendants' do
- it 'creates valid descendant nodes' do
- expect(needs.descendants.count).to eq 2
- expect(needs.descendants)
- .to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
+ it_behaves_like 'entry with descendant nodes'
+ end
+
+ context 'with complex job entries composed' do
+ let(:config) do
+ [
+ { job: 'first_job_name', artifacts: true },
+ { job: 'second_job_name', artifacts: false }
+ ]
+ end
+
+ before do
+ needs.compose!
+ end
+
+ describe '#value' do
+ it 'returns key value' do
+ expect(needs.value).to eq(
+ job: [
+ { name: 'first_job_name', artifacts: true },
+ { name: 'second_job_name', artifacts: false }
+ ]
+ )
+ end
+ end
+
+ it_behaves_like 'entry with descendant nodes'
+ end
+
+ context 'with mixed job entries composed' do
+ let(:config) do
+ [
+ 'first_job_name',
+ { job: 'second_job_name', artifacts: false }
+ ]
+ end
+
+ before do
+ needs.compose!
+ end
+
+ describe '#value' do
+ it 'returns key value' do
+ expect(needs.value).to eq(
+ job: [
+ { name: 'first_job_name', artifacts: true },
+ { name: 'second_job_name', artifacts: false }
+ ]
+ )
+ end
+ end
+
+ it_behaves_like 'entry with descendant nodes'
+ end
+
+ context 'with empty config' do
+ let(:config) do
+ []
+ end
+
+ before do
+ needs.compose!
+ end
+
+ describe '#value' do
+ it 'returns empty value' do
+ expect(needs.value).to eq({})
end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index dd869c227a1..0aea3a59b33 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -15,8 +15,9 @@ describe Gitlab::Ci::Config::External::File::Project do
before do
project.add_developer(user)
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!)
+ end
end
describe '#matching?' do
@@ -159,8 +160,8 @@ describe Gitlab::Ci::Config::External::File::Project do
private
def stub_project_blob(ref, path)
- allow_any_instance_of(Repository)
- .to receive(:blob_data_at)
- .with(ref, path) { yield }
+ allow_next_instance_of(Repository) do |instance|
+ allow(instance).to receive(:blob_data_at).with(ref, path) { yield }
+ end
end
end
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 08db00dda9d..a23cce9b757 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -21,8 +21,9 @@ describe Gitlab::Ci::Config::External::File::Remote do
end
before do
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!)
+ end
end
describe '#matching?' do
diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
index 164b5800abf..ee1660e4dfd 100644
--- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
@@ -14,8 +14,9 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:template_file) { described_class.new(params, context) }
before do
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!)
+ end
end
describe '#matching?' do
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 8d09aa47f12..2a5f62f7e74 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -23,8 +23,9 @@ describe Gitlab::Ci::Config::External::Mapper do
before do
stub_full_request(remote_url).to_return(body: file_content)
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!)
+ end
end
describe '#process' do
diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb
index bf880478387..db62fb7524d 100644
--- a/spec/lib/gitlab/ci/config/normalizer_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb
@@ -105,7 +105,7 @@ describe Gitlab::Ci::Config::Normalizer do
context 'for needs' do
let(:expanded_job_attributes) do
expanded_job_names.map do |job_name|
- { name: job_name }
+ { name: job_name, extra: :key }
end
end
@@ -117,7 +117,7 @@ describe Gitlab::Ci::Config::Normalizer do
script: 'echo 1',
needs: {
job: [
- { name: job_name.to_s }
+ { name: job_name.to_s, extra: :key }
]
}
}
@@ -140,8 +140,8 @@ describe Gitlab::Ci::Config::Normalizer do
script: 'echo 1',
needs: {
job: [
- { name: job_name.to_s },
- { name: "other_job" }
+ { name: job_name.to_s, extra: :key },
+ { name: "other_job", extra: :key }
]
}
}
@@ -153,7 +153,7 @@ describe Gitlab::Ci::Config::Normalizer do
end
it "includes the regular job in dependencies" do
- expect(subject.dig(:final_job, :needs, :job)).to include(name: 'other_job')
+ expect(subject.dig(:final_job, :needs, :job)).to include(name: 'other_job', extra: :key)
end
end
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index b254f9af2f1..63a36995284 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -8,8 +8,9 @@ describe Gitlab::Ci::Config do
set(:user) { create(:user) }
before do
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!)
+ end
end
let(:config) do
@@ -156,7 +157,7 @@ describe Gitlab::Ci::Config do
describe '.new' do
it 'raises error' do
- expect(Gitlab::Sentry).to receive(:track_exception)
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
expect { config }.to raise_error(
described_class::ConfigError,
@@ -358,22 +359,15 @@ describe Gitlab::Ci::Config do
context "when it takes too long to evaluate includes" do
before do
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
- .and_call_original
-
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:set_deadline)
- .with(described_class::TIMEOUT_SECONDS)
- .and_call_original
-
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:execution_expired?)
- .and_return(true)
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!).and_call_original
+ allow(instance).to receive(:set_deadline).with(described_class::TIMEOUT_SECONDS).and_call_original
+ allow(instance).to receive(:execution_expired?).and_return(true)
+ end
end
it 'raises error TimeoutError' do
- expect(Gitlab::Sentry).to receive(:track_exception)
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
expect { config }.to raise_error(
described_class::ConfigError,
@@ -384,9 +378,9 @@ describe Gitlab::Ci::Config do
context 'when context expansion timeout is disabled' do
before do
- allow_any_instance_of(Gitlab::Ci::Config::External::Context)
- .to receive(:check_execution_time!)
- .and_call_original
+ allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
+ allow(instance).to receive(:check_execution_time!).and_call_original
+ end
allow(Feature)
.to receive(:enabled?)
diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb
index af4e9d687c4..385df72fa41 100644
--- a/spec/lib/gitlab/ci/cron_parser_spec.rb
+++ b/spec/lib/gitlab/ci/cron_parser_spec.rb
@@ -152,6 +152,22 @@ describe Gitlab::Ci::CronParser do
end
end
end
+
+ context 'when time crosses a Daylight Savings boundary' do
+ let(:cron) { '* 0 1 12 *'}
+
+ # Note this previously only failed if the time zone is set
+ # to a zone that observes Daylight Savings
+ # (e.g. America/Chicago) at the start of the test. Stubbing
+ # TZ doesn't appear to be enough.
+ it 'generates day without TZInfo::AmbiguousTime error' do
+ Timecop.freeze(Time.utc(2020, 1, 1)) do
+ expect(subject.year).to eq(2020)
+ expect(subject.month).to eq(12)
+ expect(subject.day).to eq(1)
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index a631cd2777b..b81094f8b4a 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
let(:step) { described_class.new(pipeline, command) }
before do
- stub_repository_ci_yaml_file(sha: anything)
+ stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
end
it 'never breaks the chain' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
new file mode 100644
index 00000000000..7ebe5842fd0
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -0,0 +1,221 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Config::Content do
+ let(:project) { create(:project, ci_config_path: ci_config_path) }
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new(project: project) }
+
+ subject { described_class.new(pipeline, command) }
+
+ describe '#perform!' do
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(ci_root_config_content: false)
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'returns the content of the YAML file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq('the-content')
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+
+ it 'does not support URLs and default to AutoDevops' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ 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' }
+
+ it 'does not support YAML from external repository and default to AutoDevops' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'returns the content of the canonical config file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq('the-content')
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'returns the content of AutoDevops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq('unknown_source')
+ expect(command.config_content).to be_nil
+ expect(pipeline.errors.full_messages).to include('Missing CI config file')
+ end
+ end
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'builds root config including the local custom file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - local: #{ci_config_path}
+ EOY
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+
+ it 'builds root config including the remote config' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'remote_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - remote: #{ci_config_path}
+ EOY
+ 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' }
+
+ it 'builds root config including the path to another repository' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'external_project_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ EOY
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(pipeline.sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'builds root config including the canonical CI config file' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'repository_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ EOY
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ expect(command.config_content).to eq(<<~EOY)
+ ---
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+ EOY
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ expect(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'builds root config including the auto-devops template' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq('unknown_source')
+ expect(command.config_content).to be_nil
+ expect(pipeline.errors.full_messages).to include('Missing CI config file')
+ 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
new file mode 100644
index 00000000000..f2a0b93ef28
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Validate::External do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:pipeline) { build(:ci_empty_pipeline, user: user, project: project) }
+ let!(:step) { described_class.new(pipeline, command) }
+
+ let(:ci_yaml) do
+ <<-CI_YAML
+ stages:
+ - first_stage
+ - second_stage
+
+ first_stage_job_name:
+ stage: first_stage
+ image: hello_world
+ script:
+ - echo 'hello'
+
+ second_stage_job_name:
+ stage: second_stage
+ services:
+ - postgres
+ before_script:
+ - echo 'first hello'
+ script:
+ - echo 'second hello'
+ CI_YAML
+ end
+
+ let(:yaml_processor) do
+ ::Gitlab::Ci::YamlProcessor.new(
+ ci_yaml, {
+ project: project,
+ sha: pipeline.sha,
+ user: user
+ }
+ )
+ end
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, config_processor: yaml_processor
+ )
+ end
+
+ describe '#perform!' do
+ subject(:perform!) { step.perform! }
+
+ context 'when validation returns true' do
+ before do
+ allow(step).to receive(:validate_external).and_return(true)
+ end
+
+ it 'does not drop the pipeline' do
+ perform!
+
+ expect(pipeline.status).not_to eq('failed')
+ expect(pipeline.errors).to be_empty
+ end
+
+ it 'does not break the chain' do
+ perform!
+
+ expect(step.break?).to be false
+ end
+ end
+
+ context 'when validation return false' do
+ before do
+ allow(step).to receive(:validate_external).and_return(false)
+ end
+
+ it 'drops the pipeline' do
+ perform!
+
+ expect(pipeline.status).to eq('failed')
+ expect(pipeline.errors.to_a).to include('External validation failed')
+ end
+
+ it 'breaks the chain' do
+ perform!
+
+ expect(step.break?).to be true
+ end
+ end
+ end
+
+ describe '#validation_service_payload' do
+ subject(:validation_service_payload) { step.send(:validation_service_payload, pipeline, command.config_processor.stages_attributes) }
+
+ it 'respects the defined schema' do
+ expect(validation_service_payload).to match_schema('/external_validation')
+ end
+
+ it 'does not fire sql queries' do
+ expect { validation_service_payload }.not_to exceed_query_limit(1)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 53dcb6359fe..2ae513aea1b 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -852,7 +852,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
it "returns an error" do
expect(subject.errors).to contain_exactly(
- "rspec: one job can only need 5 others, but you have listed 6. See needs keyword documentation for more details")
+ "rspec: one job can only need 10 others, but you have listed 11. See needs keyword documentation for more details")
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index a13335f63d5..a978084876f 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -81,7 +81,9 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
context 'when a ref is protected' do
before do
- allow_any_instance_of(Project).to receive(:protected_for?).and_return(true)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:protected_for?).and_return(true)
+ end
end
it 'returns protected builds' do
@@ -91,7 +93,9 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
context 'when a ref is not protected' do
before do
- allow_any_instance_of(Project).to receive(:protected_for?).and_return(false)
+ allow_next_instance_of(Project) do |instance|
+ allow(instance).to receive(:protected_for?).and_return(false)
+ end
end
it 'returns unprotected builds' do
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index de489fa4664..11be17bfc53 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -31,10 +31,10 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'passed'
+ expect(status.text).to eq s_('CiStatusText|passed')
expect(status.icon).to eq 'status_success'
expect(status.favicon).to eq 'favicon_status_success'
- expect(status.label).to eq 'passed'
+ expect(status.label).to eq s_('CiStatusLabel|passed')
expect(status).to have_details
expect(status).to have_action
end
@@ -58,10 +58,10 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'passed'
+ expect(status.text).to eq s_('CiStatusText|passed')
expect(status.icon).to eq 'status_success'
expect(status.favicon).to eq 'favicon_status_success'
- expect(status.label).to eq 'passed'
+ expect(status.label).to eq s_('CiStatusLabel|passed')
expect(status).to have_details
expect(status).to have_action
end
@@ -86,11 +86,11 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'failed'
+ expect(status.text).to eq s_('CiStatusText|failed')
expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
- expect(status.label).to eq 'failed'
- expect(status.status_tooltip).to eq 'failed - (unknown failure)'
+ expect(status.label).to eq s_('CiStatusLabel|failed')
+ expect(status.status_tooltip).to eq "#{s_('CiStatusText|failed')} - (unknown failure)"
expect(status).to have_details
expect(status).to have_action
end
@@ -115,7 +115,7 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'failed'
+ expect(status.text).to eq s_('CiStatusText|failed')
expect(status.icon).to eq 'status_warning'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed (allowed to fail)'
@@ -144,10 +144,10 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'failed'
+ expect(status.text).to eq s_('CiStatusText|failed')
expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
- expect(status.label).to eq 'failed'
+ expect(status.label).to eq s_('CiStatusLabel|failed')
expect(status).to have_details
expect(status).to have_action
expect(status.action_title).to include 'Retry'
@@ -173,11 +173,11 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'canceled'
+ expect(status.text).to eq s_('CiStatusText|canceled')
expect(status.icon).to eq 'status_canceled'
expect(status.favicon).to eq 'favicon_status_canceled'
expect(status.illustration).to include(:image, :size, :title)
- expect(status.label).to eq 'canceled'
+ expect(status.label).to eq s_('CiStatusLabel|canceled')
expect(status).to have_details
expect(status).to have_action
end
@@ -200,10 +200,10 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'running'
+ expect(status.text).to eq s_('CiStatus|running')
expect(status.icon).to eq 'status_running'
expect(status.favicon).to eq 'favicon_status_running'
- expect(status.label).to eq 'running'
+ expect(status.label).to eq s_('CiStatus|running')
expect(status).to have_details
expect(status).to have_action
end
@@ -226,11 +226,11 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'pending'
+ expect(status.text).to eq s_('CiStatusText|pending')
expect(status.icon).to eq 'status_pending'
expect(status.favicon).to eq 'favicon_status_pending'
expect(status.illustration).to include(:image, :size, :title, :content)
- expect(status.label).to eq 'pending'
+ expect(status.label).to eq s_('CiStatusLabel|pending')
expect(status).to have_details
expect(status).to have_action
end
@@ -252,11 +252,11 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'skipped'
+ expect(status.text).to eq s_('CiStatusText|skipped')
expect(status.icon).to eq 'status_skipped'
expect(status.favicon).to eq 'favicon_status_skipped'
expect(status.illustration).to include(:image, :size, :title)
- expect(status.label).to eq 'skipped'
+ expect(status.label).to eq s_('CiStatusLabel|skipped')
expect(status).to have_details
expect(status).not_to have_action
end
@@ -282,7 +282,7 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'manual'
+ expect(status.text).to eq s_('CiStatusText|manual')
expect(status.group).to eq 'manual'
expect(status.icon).to eq 'status_manual'
expect(status.favicon).to eq 'favicon_status_manual'
@@ -339,7 +339,7 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'manual'
+ expect(status.text).to eq s_('CiStatusText|manual')
expect(status.group).to eq 'manual'
expect(status.icon).to eq 'status_manual'
expect(status.favicon).to eq 'favicon_status_manual'
@@ -370,7 +370,7 @@ describe Gitlab::Ci::Status::Build::Factory do
end
it 'fabricates status with correct details' do
- expect(status.text).to eq 'delayed'
+ expect(status.text).to eq s_('CiStatusText|delayed')
expect(status.group).to eq 'scheduled'
expect(status.icon).to eq 'status_scheduled'
expect(status.favicon).to eq 'favicon_status_scheduled'
diff --git a/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb
index 876ba712d05..664915ba552 100644
--- a/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/blocked_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::Ci::Status::Pipeline::Blocked do
describe '.matches?' do
let(:user) { double('user') }
+
subject { described_class.matches?(pipeline, user) }
context 'when pipeline is blocked' do
diff --git a/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
index 90b797965b3..aba403de712 100644
--- a/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
+++ b/spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::Ci::Status::Pipeline::Delayed do
describe '.matches?' do
let(:user) { double('user') }
+
subject { described_class.matches?(pipeline, user) }
context 'when pipeline is scheduled' do
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..c2f9930056a
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Auto-DevOps.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps') }
+
+ describe 'the created pipeline' do
+ let(:user) { create(:admin) }
+ let(:default_branch) { 'master' }
+ let(:pipeline_branch) { default_branch }
+ let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ it 'creates a build and a test job' do
+ expect(build_names).to include('build', 'test')
+ end
+
+ context 'when the project has no active cluster' do
+ it 'only creates a build and a test stage' do
+ expect(pipeline.stages_names).to eq(%w(build test))
+ end
+
+ it 'does not create any deployment-related builds' do
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+
+ context 'when the project has an active cluster' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+
+ before do
+ allow(cluster).to receive(:active?).and_return(true)
+ end
+
+ describe 'deployment-related builds' do
+ context 'on default branch' do
+ it 'does not include rollout jobs besides production' do
+ expect(build_names).to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+
+ context 'when STAGING_ENABLED=1' do
+ before do
+ create(:ci_variable, project: project, key: 'STAGING_ENABLED', value: '1')
+ end
+
+ it 'includes a staging job and a production_manual job' do
+ expect(build_names).not_to include('production')
+ expect(build_names).to include('production_manual')
+ expect(build_names).to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+
+ context 'when CANARY_ENABLED=1' do
+ before do
+ create(:ci_variable, project: project, key: 'CANARY_ENABLED', value: '1')
+ end
+
+ it 'includes a canary job and a production_manual job' do
+ expect(build_names).not_to include('production')
+ expect(build_names).to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+ end
+
+ context 'outside of default branch' do
+ let(:pipeline_branch) { 'patch-1' }
+
+ before do
+ project.repository.create_branch(pipeline_branch)
+ end
+
+ it 'does not include rollout jobs besides review' do
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..2a6314755ef
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Managed-Cluster-Applications.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Managed-Cluster-Applications') }
+
+ describe 'the created pipeline' do
+ let_it_be(:user) { create(:user) }
+
+ let(:project) { create(:project, :custom_repo, namespace: user.namespace, files: { 'README.md' => '' }) }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+ let(:pipeline_branch) { 'master' }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ end
+
+ context 'for a default branch' do
+ it 'creates a apply job' do
+ expect(build_names).to match_array('apply')
+ end
+ end
+
+ context 'outside of default branch' do
+ let(:pipeline_branch) { 'a_branch' }
+
+ before do
+ project.repository.create_branch(pipeline_branch)
+ end
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
index e0077a5280a..795e8e51276 100644
--- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
+++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
@@ -112,8 +112,9 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
it 'calls get_chunk only once' do
- expect_any_instance_of(Gitlab::Ci::Trace::ChunkedIO)
- .to receive(:current_chunk).once.and_call_original
+ expect_next_instance_of(Gitlab::Ci::Trace::ChunkedIO) do |instance|
+ expect(instance).to receive(:current_chunk).once.and_call_original
+ end
chunked_io.each_line { |line| }
end
diff --git a/spec/lib/gitlab/ci/trace/section_parser_spec.rb b/spec/lib/gitlab/ci/trace/section_parser_spec.rb
index 5e2efe083be..6e8504a1584 100644
--- a/spec/lib/gitlab/ci/trace/section_parser_spec.rb
+++ b/spec/lib/gitlab/ci/trace/section_parser_spec.rb
@@ -21,6 +21,7 @@ describe Gitlab::Ci::Trace::SectionParser do
end
let(:lines) { build_lines('') }
+
subject { described_class.new(lines) }
describe '#sections' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 4b1c7483b11..8f9c5c74260 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -28,6 +28,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"]
@@ -120,6 +121,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: { script: ["rspec"] },
interruptible: true,
allow_failure: false,
@@ -149,6 +151,28 @@ module Gitlab
expect(subject[:options]).not_to have_key(:retry)
end
end
+
+ context 'when retry count is specified by default' do
+ let(:config) do
+ YAML.dump(default: { retry: { max: 1 } },
+ rspec: { script: 'rspec' })
+ end
+
+ it 'does use the default value' do
+ expect(subject[:options]).to include(retry: { max: 1 })
+ end
+ end
+
+ context 'when retry count default value is overridden' do
+ let(:config) do
+ YAML.dump(default: { retry: { max: 1 } },
+ rspec: { script: 'rspec', retry: { max: 2 } })
+ end
+
+ it 'does use the job value' do
+ expect(subject[:options]).to include(retry: { max: 2 })
+ end
+ end
end
describe 'allow failure entry' do
@@ -244,8 +268,7 @@ module Gitlab
when: "on_success",
yaml_variables: [],
options: { script: ["rspec"] },
- only: { refs: ["branches"] },
- except: {} }] },
+ only: { refs: ["branches"] } }] },
{ name: "deploy",
index: 3,
builds:
@@ -256,8 +279,7 @@ module Gitlab
when: "on_success",
yaml_variables: [],
options: { script: ["cap prod"] },
- only: { refs: ["tags"] },
- except: {} }] },
+ only: { refs: ["tags"] } }] },
{ name: ".post",
index: 4,
builds: [] }]
@@ -594,6 +616,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"],
@@ -625,6 +648,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"],
@@ -654,6 +678,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"],
@@ -679,6 +704,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"],
@@ -1193,6 +1219,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "rspec",
+ only: { refs: %w[branches tags] },
options: {
before_script: ["pwd"],
script: ["rspec"],
@@ -1375,7 +1402,7 @@ module Gitlab
end
it 'raises an error for invalid number' do
- expect { builds }.to raise_error('jobs:deploy_to_production timeout should be a duration')
+ expect { builds }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:deploy_to_production:timeout config should be a duration')
end
end
@@ -1490,6 +1517,7 @@ module Gitlab
stage: "build",
stage_idx: 1,
name: "build1",
+ only: { refs: %w[branches tags] },
options: {
script: ["test"]
},
@@ -1501,10 +1529,53 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "test1",
+ only: { refs: %w[branches tags] },
options: { script: ["test"] },
needs_attributes: [
- { name: "build1" },
- { name: "build2" }
+ { name: "build1", artifacts: true },
+ { name: "build2", artifacts: true }
+ ],
+ when: "on_success",
+ allow_failure: false,
+ yaml_variables: []
+ )
+ end
+ end
+
+ context 'needs two builds' do
+ let(:needs) do
+ [
+ { job: 'parallel', artifacts: false },
+ { job: 'build1', artifacts: true },
+ 'build2'
+ ]
+ end
+
+ it "does create jobs with valid specification" do
+ expect(subject.builds.size).to eq(7)
+ expect(subject.builds[0]).to eq(
+ stage: "build",
+ stage_idx: 1,
+ name: "build1",
+ only: { refs: %w[branches tags] },
+ options: {
+ script: ["test"]
+ },
+ when: "on_success",
+ allow_failure: false,
+ yaml_variables: []
+ )
+ expect(subject.builds[4]).to eq(
+ stage: "test",
+ stage_idx: 2,
+ name: "test1",
+ only: { refs: %w[branches tags] },
+ options: { script: ["test"] },
+ needs_attributes: [
+ { name: "parallel 1/2", artifacts: false },
+ { name: "parallel 2/2", artifacts: false },
+ { name: "build1", artifacts: true },
+ { name: "build2", artifacts: true }
],
when: "on_success",
allow_failure: false,
@@ -1522,10 +1593,41 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "test1",
+ only: { refs: %w[branches tags] },
options: { script: ["test"] },
needs_attributes: [
- { name: "parallel 1/2" },
- { name: "parallel 2/2" }
+ { name: "parallel 1/2", artifacts: true },
+ { name: "parallel 2/2", artifacts: true }
+ ],
+ when: "on_success",
+ allow_failure: false,
+ yaml_variables: []
+ )
+ end
+ end
+
+ context 'needs dependencies artifacts' do
+ let(:needs) do
+ [
+ "build1",
+ { job: "build2" },
+ { job: "parallel", artifacts: true }
+ ]
+ end
+
+ it "does create jobs with valid specification" do
+ expect(subject.builds.size).to eq(7)
+ expect(subject.builds[4]).to eq(
+ stage: "test",
+ stage_idx: 2,
+ name: "test1",
+ only: { refs: %w[branches tags] },
+ options: { script: ["test"] },
+ needs_attributes: [
+ { name: "build1", artifacts: true },
+ { name: "build2", artifacts: true },
+ { name: "parallel 1/2", artifacts: true },
+ { name: "parallel 2/2", artifacts: true }
],
when: "on_success",
allow_failure: false,
@@ -1617,6 +1719,7 @@ module Gitlab
describe "Hidden jobs" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) }
+
subject { config_processor.stage_builds_attributes("test") }
shared_examples 'hidden_job_handling' do
@@ -1626,6 +1729,7 @@ module Gitlab
stage: "test",
stage_idx: 2,
name: "normal_job",
+ only: { refs: %w[branches tags] },
options: {
script: ["test"]
},
@@ -1661,6 +1765,7 @@ module Gitlab
describe "YAML Alias/Anchor" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) }
+
subject { config_processor.stage_builds_attributes("build") }
shared_examples 'job_templates_handling' do
@@ -1670,6 +1775,7 @@ module Gitlab
stage: "build",
stage_idx: 1,
name: "job1",
+ only: { refs: %w[branches tags] },
options: {
script: ["execute-script-for-job"]
},
@@ -1681,6 +1787,7 @@ module Gitlab
stage: "build",
stage_idx: 1,
name: "job2",
+ only: { refs: %w[branches tags] },
options: {
script: ["execute-script-for-job"]
},
@@ -1758,7 +1865,7 @@ module Gitlab
config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
expect do
Gitlab::Ci::YamlProcessor.new(config)
- end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec tags should be an array of strings")
+ end.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, "jobs:rspec:tags config should be an array of strings")
end
it "returns errors if before_script parameter is invalid" do
@@ -2106,7 +2213,7 @@ module Gitlab
context "when the tags parameter is invalid" do
let(:content) { YAML.dump({ rspec: { script: "test", tags: "mysql" } }) }
- it { is_expected.to eq "jobs:rspec tags should be an array of strings" }
+ it { is_expected.to eq "jobs:rspec:tags config should be an array of strings" }
end
context "when YAML content is empty" do
@@ -2127,6 +2234,70 @@ module Gitlab
it { is_expected.to be_nil }
end
end
+
+ describe '.new_with_validation_errors' do
+ subject { Gitlab::Ci::YamlProcessor.new_with_validation_errors(content) }
+
+ context 'when the YAML could not be parsed' do
+ let(:content) { YAML.dump('invalid: yaml: test') }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(false)
+ expect(subject.errors).to eq(['Invalid configuration format'])
+ expect(subject.content).to be_blank
+ end
+ end
+
+ context 'when the tags parameter is invalid' do
+ let(:content) { YAML.dump({ rspec: { script: 'test', tags: 'mysql' } }) }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(false)
+ expect(subject.errors).to eq(['jobs:rspec:tags config should be an array of strings'])
+ expect(subject.content).to be_blank
+ end
+ end
+
+ context 'when the configuration contains multiple keyword-syntax errors' do
+ let(:content) { YAML.dump({ rspec: { script: 'test', bad_tags: 'mysql', rules: { wrong: 'format' } } }) }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(false)
+ expect(subject.errors).to eq(['jobs:rspec config contains unknown keys: bad_tags', 'jobs:rspec rules should be an array of hashes'])
+ expect(subject.content).to be_blank
+ end
+ end
+
+ context 'when YAML content is empty' do
+ let(:content) { '' }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(false)
+ expect(subject.errors).to eq(['Please provide content of .gitlab-ci.yml'])
+ expect(subject.content).to be_blank
+ end
+ end
+
+ context 'when the YAML contains an unknown alias' do
+ let(:content) { 'steps: *bad_alias' }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(false)
+ expect(subject.errors).to eq(['Unknown alias: bad_alias'])
+ expect(subject.content).to be_blank
+ end
+ end
+
+ context 'when the YAML is valid' do
+ let(:content) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
+
+ it 'returns errors and empty configuration' do
+ expect(subject.valid?).to eq(true)
+ expect(subject.errors).to be_empty
+ expect(subject.content).to be_present
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
index fc9792e16d7..75ef75fccc9 100644
--- a/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
+++ b/spec/lib/gitlab/cleanup/orphan_job_artifact_files_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
describe Gitlab::Cleanup::OrphanJobArtifactFiles do
let(:null_logger) { Logger.new('/dev/null') }
+
subject(:cleanup) { described_class.new(logger: null_logger) }
before do
diff --git a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
index c5b17aafdd2..9c16fb6f6dc 100644
--- a/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/shared_stage_spec.rb
@@ -9,7 +9,9 @@ shared_examples 'base stage' do
before do
allow(stage).to receive(:project_median).and_return(1.12)
- allow_any_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher).to receive(:event_result).and_return({})
+ allow_next_instance_of(Gitlab::CycleAnalytics::BaseEventFetcher) do |instance|
+ allow(instance).to receive(:event_result).and_return({})
+ end
end
it 'has the median data value' do
diff --git a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
index d4ab9bc225b..41ce9355708 100644
--- a/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/usage_data_spec.rb
@@ -17,7 +17,9 @@ describe Gitlab::CycleAnalytics::UsageData do
projects.each_with_index do |project, time|
issue = create(:issue, project: project, created_at: (time + 1).hour.ago)
- allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue])
+ allow_next_instance_of(Gitlab::ReferenceExtractor) do |instance|
+ allow(instance).to receive(:issues).and_return([issue])
+ end
milestone = create(:milestone, project: project)
mr = create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}")
diff --git a/spec/lib/gitlab/danger/changelog_spec.rb b/spec/lib/gitlab/danger/changelog_spec.rb
new file mode 100644
index 00000000000..888094eaf6e
--- /dev/null
+++ b/spec/lib/gitlab/danger/changelog_spec.rb
@@ -0,0 +1,163 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+require_relative 'danger_spec_helper'
+
+require 'gitlab/danger/changelog'
+
+describe Gitlab::Danger::Changelog do
+ using RSpec::Parameterized::TableSyntax
+ include DangerSpecHelper
+
+ let(:added_files) { nil }
+ let(:fake_git) { double('fake-git', added_files: added_files) }
+
+ let(:mr_labels) { nil }
+ let(:mr_json) { nil }
+ let(:fake_gitlab) { double('fake-gitlab', mr_labels: mr_labels, mr_json: mr_json) }
+
+ let(:changes_by_category) { nil }
+ let(:ee?) { false }
+ let(:fake_helper) { double('fake-helper', changes_by_category: changes_by_category, ee?: ee?) }
+
+ let(:fake_danger) { new_fake_danger.include(described_class) }
+
+ subject(:changelog) { fake_danger.new(git: fake_git, gitlab: fake_gitlab, helper: fake_helper) }
+
+ describe '#needed?' do
+ subject { changelog.needed? }
+
+ [
+ { docs: nil },
+ { none: nil },
+ { docs: nil, none: nil }
+ ].each do |categories|
+ let(:changes_by_category) { categories }
+ it "is falsy when categories don't require a changelog" do
+ is_expected.to be_falsy
+ end
+ end
+
+ where(:categories, :labels) do
+ { backend: nil } | %w[backend backstage]
+ { frontend: nil, docs: nil } | ['ci-build']
+ { engineering_productivity: nil, none: nil } | ['meta']
+ end
+
+ with_them do
+ let(:changes_by_category) { categories }
+ let(:mr_labels) { labels }
+
+ it "is falsy when labels require no changelog" do
+ is_expected.to be_falsy
+ end
+ end
+
+ where(:categories, :labels) do
+ { frontend: nil, docs: nil } | ['database::review pending', 'feature']
+ { backend: nil } | ['backend', 'technical debt']
+ { engineering_productivity: nil, none: nil } | ['frontend']
+ end
+
+ with_them do
+ let(:changes_by_category) { categories }
+ let(:mr_labels) { labels }
+
+ it "is truthy when categories and labels require a changelog" do
+ is_expected.to be_truthy
+ end
+ end
+ end
+
+ describe '#found' do
+ subject { changelog.found }
+
+ context 'added files contain a changelog' do
+ [
+ 'changelogs/unreleased/entry.md',
+ 'ee/changelogs/unreleased/entry.md',
+ 'changelogs/unreleased-ee/entry.md',
+ 'ee/changelogs/unreleased-ee/entry.md'
+ ].each do |file_path|
+ let(:added_files) { [file_path] }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'added files do not contain a changelog' do
+ [
+ 'app/models/model.rb',
+ 'app/assets/javascripts/file.js'
+ ].each do |file_path|
+ let(:added_files) { [file_path] }
+ it { is_expected.to eq(nil) }
+ end
+ end
+ end
+
+ describe '#presented_no_changelog_labels' do
+ subject { changelog.presented_no_changelog_labels }
+
+ it 'returns the labels formatted' do
+ is_expected.to eq('~backstage, ~ci-build, ~meta')
+ end
+ end
+
+ describe '#sanitized_mr_title' do
+ subject { changelog.sanitized_mr_title }
+
+ [
+ 'WIP: My MR title',
+ 'My MR title'
+ ].each do |mr_title|
+ let(:mr_json) { { "title" => mr_title } }
+ it { is_expected.to eq("My MR title") }
+ end
+ end
+
+ describe '#ee_changelog?' do
+ context 'is ee changelog' do
+ [
+ 'changelogs/unreleased-ee/entry.md',
+ 'ee/changelogs/unreleased-ee/entry.md'
+ ].each do |file_path|
+ subject { changelog.ee_changelog?(file_path) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'is not ee changelog' do
+ [
+ 'changelogs/unreleased/entry.md',
+ 'ee/changelogs/unreleased/entry.md'
+ ].each do |file_path|
+ subject { changelog.ee_changelog?(file_path) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+ end
+
+ describe '#ce_port_changelog?' do
+ where(:helper_ee?, :file_path, :expected) do
+ true | 'changelogs/unreleased-ee/entry.md' | false
+ true | 'ee/changelogs/unreleased-ee/entry.md' | false
+ false | 'changelogs/unreleased-ee/entry.md' | false
+ false | 'ee/changelogs/unreleased-ee/entry.md' | false
+ true | 'changelogs/unreleased/entry.md' | true
+ true | 'ee/changelogs/unreleased/entry.md' | true
+ false | 'changelogs/unreleased/entry.md' | false
+ false | 'ee/changelogs/unreleased/entry.md' | false
+ end
+
+ with_them do
+ let(:ee?) { helper_ee? }
+ subject { changelog.ce_port_changelog?(file_path) }
+
+ it { is_expected.to eq(expected) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/danger/danger_spec_helper.rb b/spec/lib/gitlab/danger/danger_spec_helper.rb
new file mode 100644
index 00000000000..b1e84b3c13d
--- /dev/null
+++ b/spec/lib/gitlab/danger/danger_spec_helper.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module DangerSpecHelper
+ def new_fake_danger
+ Class.new do
+ attr_reader :git, :gitlab, :helper
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def initialize(git: nil, gitlab: nil, helper: nil)
+ @git = git
+ @gitlab = gitlab
+ @helper = helper
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 8056418e697..d7e67444fca 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -2,29 +2,22 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
+require_relative 'danger_spec_helper'
require 'gitlab/danger/helper'
describe Gitlab::Danger::Helper do
using RSpec::Parameterized::TableSyntax
-
- class FakeDanger
- include Gitlab::Danger::Helper
-
- attr_reader :git, :gitlab
-
- def initialize(git:, gitlab:)
- @git = git
- @gitlab = gitlab
- end
- end
+ include DangerSpecHelper
let(:fake_git) { double('fake-git') }
let(:mr_author) { nil }
let(:fake_gitlab) { double('fake-gitlab', mr_author: mr_author) }
- subject(:helper) { FakeDanger.new(git: fake_git, gitlab: fake_gitlab) }
+ let(:fake_danger) { new_fake_danger.include(described_class) }
+
+ subject(:helper) { fake_danger.new(git: fake_git, gitlab: fake_gitlab) }
describe '#gitlab_helper' do
context 'when gitlab helper is not available' do
diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb
index 35edfa08a63..bf6152ff3c2 100644
--- a/spec/lib/gitlab/danger/teammate_spec.rb
+++ b/spec/lib/gitlab/danger/teammate_spec.rb
@@ -33,8 +33,8 @@ describe Gitlab::Danger::Teammate do
context 'when labels contain devops::create and the category is test' do
let(:labels) { ['devops::create'] }
- context 'when role is Test Automation Engineer, Create' do
- let(:role) { 'Test Automation Engineer, Create' }
+ context 'when role is Software Engineer in Test, Create' do
+ let(:role) { 'Software Engineer in Test, Create' }
it '#reviewer? returns true' do
expect(subject.reviewer?(project, :test, labels)).to be_truthy
@@ -45,7 +45,7 @@ describe Gitlab::Danger::Teammate do
end
context 'when hyperlink is mangled in the role' do
- let(:role) { '<a href="#">Test Automation Engineer</a>, Create' }
+ let(:role) { '<a href="#">Software Engineer in Test</a>, Create' }
it '#reviewer? returns true' do
expect(subject.reviewer?(project, :test, labels)).to be_truthy
@@ -53,16 +53,16 @@ describe Gitlab::Danger::Teammate do
end
end
- context 'when role is Test Automation Engineer' do
- let(:role) { 'Test Automation Engineer' }
+ context 'when role is Software Engineer in Test' do
+ let(:role) { 'Software Engineer in Test' }
it '#reviewer? returns false' do
expect(subject.reviewer?(project, :test, labels)).to be_falsey
end
end
- context 'when role is Test Automation Engineer, Manage' do
- let(:role) { 'Test Automation Engineer, Manage' }
+ context 'when role is Software Engineer in Test, Manage' do
+ let(:role) { 'Software Engineer in Test, Manage' }
it '#reviewer? returns false' do
expect(subject.reviewer?(project, :test, labels)).to be_falsey
diff --git a/spec/lib/gitlab/data_builder/build_spec.rb b/spec/lib/gitlab/data_builder/build_spec.rb
index b170ef788d9..fdb855de786 100644
--- a/spec/lib/gitlab/data_builder/build_spec.rb
+++ b/spec/lib/gitlab/data_builder/build_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
describe Gitlab::DataBuilder::Build do
- let(:build) { create(:ci_build) }
+ let(:runner) { create(:ci_runner, :instance) }
+ let(:build) { create(:ci_build, :running, runner: runner) }
describe '.build' do
let(:data) do
@@ -20,6 +21,10 @@ describe Gitlab::DataBuilder::Build do
it { expect(data[:build_failure_reason]).to eq(build.failure_reason) }
it { expect(data[:project_id]).to eq(build.project.id) }
it { expect(data[:project_name]).to eq(build.project.full_name) }
+ it { expect(data[:pipeline_id]).to eq(build.pipeline.id) }
+ it { expect(data[:commit][:id]).to eq(build.pipeline.id) }
+ it { expect(data[:runner][:id]).to eq(build.runner.id) }
+ it { expect(data[:runner][:description]).to eq(build.runner.description) }
context 'commit author_url' do
context 'when no commit present' do
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 931477d19c2..635bf56b72e 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -34,6 +34,7 @@ describe Gitlab::DataBuilder::Pipeline do
expect(build_data).to be_a(Hash)
expect(build_data[:id]).to eq(build.id)
expect(build_data[:status]).to eq(build.status)
+ expect(build_data[:allow_failure]).to eq(build.allow_failure)
expect(project_data).to eq(project.hook_attrs(backward: false))
expect(data[:merge_request]).to be_nil
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 449eee7a371..cac6908f4b4 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -212,44 +212,118 @@ describe Gitlab::Database::MigrationHelpers do
allow(model).to receive(:transaction_open?).and_return(false)
end
- it 'creates a concurrent foreign key and validates it' do
- expect(model).to receive(:disable_statement_timeout).and_call_original
- expect(model).to receive(:execute).with(/statement_timeout/)
- expect(model).to receive(:execute).ordered.with(/NOT VALID/)
- expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
- expect(model).to receive(:execute).with(/RESET ALL/)
+ context 'ON DELETE statements' do
+ context 'on_delete: :nullify' do
+ it 'appends ON DELETE SET NULL statement' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
+
+ expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
+
+ model.add_concurrent_foreign_key(:projects, :users,
+ column: :user_id,
+ on_delete: :nullify)
+ end
+ end
- model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
- end
+ context 'on_delete: :cascade' do
+ it 'appends ON DELETE CASCADE statement' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
+
+ expect(model).to receive(:execute).with(/ON DELETE CASCADE/)
+
+ model.add_concurrent_foreign_key(:projects, :users,
+ column: :user_id,
+ on_delete: :cascade)
+ end
+ end
- it 'appends a valid ON DELETE statement' do
- expect(model).to receive(:disable_statement_timeout).and_call_original
- expect(model).to receive(:execute).with(/statement_timeout/)
- expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
- expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
- expect(model).to receive(:execute).with(/RESET ALL/)
+ context 'on_delete: nil' do
+ it 'appends no ON DELETE statement' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
- model.add_concurrent_foreign_key(:projects, :users,
- column: :user_id,
- on_delete: :nullify)
+ expect(model).not_to receive(:execute).with(/ON DELETE/)
+
+ model.add_concurrent_foreign_key(:projects, :users,
+ column: :user_id,
+ on_delete: nil)
+ end
+ end
end
- it 'does not create a foreign key if it exists already' do
- expect(model).to receive(:foreign_key_exists?).with(:projects, :users, column: :user_id).and_return(true)
- expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/)
- expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/)
+ context 'when no custom key name is supplied' do
+ it 'creates a concurrent foreign key and validates it' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/NOT VALID/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
+
+ model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
+ end
+
+ it 'does not create a foreign key if it exists already' do
+ name = model.concurrent_foreign_key_name(:projects, :user_id)
+ expect(model).to receive(:foreign_key_exists?).with(:projects, :users,
+ column: :user_id,
+ on_delete: :cascade,
+ name: name).and_return(true)
+
+ expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/)
+ expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/)
- model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
+ model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
+ end
end
- it 'allows the use of a custom key name' do
- expect(model).to receive(:disable_statement_timeout).and_call_original
- expect(model).to receive(:execute).with(/statement_timeout/)
- expect(model).to receive(:execute).ordered.with(/NOT VALID/)
- expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+foo/)
- expect(model).to receive(:execute).with(/RESET ALL/)
+ context 'when a custom key name is supplied' do
+ context 'for creating a new foreign key for a column that does not presently exist' do
+ it 'creates a new foreign key' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/NOT VALID/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+foo/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
+
+ model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo)
+ end
+ end
+
+ context 'for creating a duplicate foreign key for a column that presently exists' do
+ context 'when the supplied key name is the same as the existing foreign key name' do
+ it 'does not create a new foreign key' do
+ expect(model).to receive(:foreign_key_exists?).with(:projects, :users,
+ name: :foo,
+ on_delete: :cascade,
+ column: :user_id).and_return(true)
+
+ expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/)
+ expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/)
+
+ model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo)
+ end
+ end
+
+ context 'when the supplied key name is different from the existing foreign key name' do
+ it 'creates a new foreign key' do
+ expect(model).to receive(:disable_statement_timeout).and_call_original
+ expect(model).to receive(:execute).with(/statement_timeout/)
+ expect(model).to receive(:execute).ordered.with(/NOT VALID/)
+ expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+bar/)
+ expect(model).to receive(:execute).with(/RESET ALL/)
- model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo)
+ model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :bar)
+ end
+ end
+ end
end
end
end
@@ -266,23 +340,61 @@ describe Gitlab::Database::MigrationHelpers do
describe '#foreign_key_exists?' do
before do
- key = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(:projects, :users, { column: :non_standard_id })
+ key = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(:projects, :users, { column: :non_standard_id, name: :fk_projects_users_non_standard_id, on_delete: :cascade })
allow(model).to receive(:foreign_keys).with(:projects).and_return([key])
end
- it 'finds existing foreign keys by column' do
- expect(model.foreign_key_exists?(:projects, :users, column: :non_standard_id)).to be_truthy
+ shared_examples_for 'foreign key checks' do
+ it 'finds existing foreign keys by column' do
+ expect(model.foreign_key_exists?(:projects, target_table, column: :non_standard_id)).to be_truthy
+ end
+
+ it 'finds existing foreign keys by name' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :fk_projects_users_non_standard_id)).to be_truthy
+ end
+
+ it 'finds existing foreign_keys by name and column' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :fk_projects_users_non_standard_id, column: :non_standard_id)).to be_truthy
+ end
+
+ it 'finds existing foreign_keys by name, column and on_delete' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :fk_projects_users_non_standard_id, column: :non_standard_id, on_delete: :cascade)).to be_truthy
+ end
+
+ it 'finds existing foreign keys by target table only' do
+ expect(model.foreign_key_exists?(:projects, target_table)).to be_truthy
+ end
+
+ it 'compares by column name if given' do
+ expect(model.foreign_key_exists?(:projects, target_table, column: :user_id)).to be_falsey
+ end
+
+ it 'compares by foreign key name if given' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :non_existent_foreign_key_name)).to be_falsey
+ end
+
+ it 'compares by foreign key name and column if given' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :non_existent_foreign_key_name, column: :non_standard_id)).to be_falsey
+ end
+
+ it 'compares by foreign key name, column and on_delete if given' do
+ expect(model.foreign_key_exists?(:projects, target_table, name: :fk_projects_users_non_standard_id, column: :non_standard_id, on_delete: :nullify)).to be_falsey
+ end
end
- it 'finds existing foreign keys by target table only' do
- expect(model.foreign_key_exists?(:projects, :users)).to be_truthy
+ context 'without specifying a target table' do
+ let(:target_table) { nil }
+
+ it_behaves_like 'foreign key checks'
end
- it 'compares by column name if given' do
- expect(model.foreign_key_exists?(:projects, :users, column: :user_id)).to be_falsey
+ context 'specifying a target table' do
+ let(:target_table) { :users }
+
+ it_behaves_like 'foreign key checks'
end
- it 'compares by target if no column given' do
+ it 'compares by target table if no column given' do
expect(model.foreign_key_exists?(:projects, :other_table)).to be_falsey
end
end
@@ -1328,4 +1440,17 @@ describe Gitlab::Database::MigrationHelpers do
end
end
end
+
+ describe '#create_or_update_plan_limit' do
+ it 'creates or updates plan limits' do
+ expect(model).to receive(:execute).with <<~SQL
+ INSERT INTO plan_limits (plan_id, "project_hooks")
+ VALUES
+ ((SELECT id FROM plans WHERE name = 'free' LIMIT 1), '10')
+ ON CONFLICT (plan_id) DO UPDATE SET "project_hooks" = EXCLUDED."project_hooks";
+ SQL
+
+ model.create_or_update_plan_limit('project_hooks', 'free', 10)
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb b/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb
index 6d38f7f1b95..0f68201a153 100644
--- a/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb
+++ b/spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb
@@ -4,25 +4,34 @@ require 'spec_helper'
describe Gitlab::Database::ObsoleteIgnoredColumns do
module Testing
+ # Used a fixed date to prevent tests failing across date boundaries
+ REMOVE_DATE = Date.new(2019, 12, 16)
+
class MyBase < ApplicationRecord
end
class SomeAbstract < MyBase
+ include IgnorableColumns
+
self.abstract_class = true
self.table_name = 'projects'
- self.ignored_columns += %i[unused]
+ ignore_column :unused, remove_after: '2019-01-01', remove_with: '12.0'
end
class B < MyBase
+ include IgnorableColumns
+
self.table_name = 'issues'
- self.ignored_columns += %i[id other]
+ ignore_column :id, :other, remove_after: '2019-01-01', remove_with: '12.0'
+ ignore_column :not_used_but_still_ignored, remove_after: REMOVE_DATE.to_s, remove_with: '12.1'
end
class A < SomeAbstract
- self.ignored_columns += %i[id also_unused]
+ ignore_column :also_unused, remove_after: '2019-02-01', remove_with: '12.1'
+ ignore_column :not_used_but_still_ignored, remove_after: REMOVE_DATE.to_s, remove_with: '12.1'
end
class C < MyBase
@@ -34,10 +43,17 @@ describe Gitlab::Database::ObsoleteIgnoredColumns do
describe '#execute' do
it 'returns a list of class names and columns pairs' do
- expect(subject.execute).to eq([
- ['Testing::A', %w(unused also_unused)],
- ['Testing::B', %w(other)]
- ])
+ Timecop.freeze(Testing::REMOVE_DATE) do
+ expect(subject.execute).to eq([
+ ['Testing::A', {
+ 'unused' => IgnorableColumns::ColumnIgnore.new(Date.parse('2019-01-01'), '12.0'),
+ 'also_unused' => IgnorableColumns::ColumnIgnore.new(Date.parse('2019-02-01'), '12.1')
+ }],
+ ['Testing::B', {
+ 'other' => IgnorableColumns::ColumnIgnore.new(Date.parse('2019-01-01'), '12.0')
+ }]
+ ])
+ end
end
end
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index 612c418e8bb..7b8437e4874 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -83,6 +83,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete
describe '#rename_path_for_routable' do
context 'for namespaces' do
let(:namespace) { create(:namespace, path: 'the-path') }
+
it "renames namespaces called the-path" do
subject.rename_path_for_routable(migration_namespace(namespace))
@@ -159,6 +160,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete
describe '#perform_rename' do
describe 'for namespaces' do
let(:namespace) { create(:namespace, path: 'the-path') }
+
it 'renames the path' do
subject.perform_rename(migration_namespace(namespace), 'the-path', 'renamed')
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index 8c4d7e323fa..46fc48ab3fc 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -95,6 +95,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
describe '#move_repositories' do
let(:namespace) { create(:group, name: 'hello-group') }
+
it 'moves a project for a namespace' do
create(:project, :repository, :legacy_storage, namespace: namespace, path: 'hello-project')
expected_path = File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git')
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 5b1a17e734d..ee3c99afdf1 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
@@ -279,5 +279,11 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do
end
end
end
+
+ it "tracks successful install" do
+ expect(Gitlab::Tracking).to receive(:event).with("self_monitoring", "project_created")
+
+ result
+ end
end
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 15fb1503529..3db8900ed8e 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -228,6 +228,7 @@ describe Gitlab::Database do
describe '.bulk_insert' do
before do
allow(described_class).to receive(:connection).and_return(connection)
+ allow(described_class).to receive(:version).and_return(version)
allow(connection).to receive(:quote_column_name, &:itself)
allow(connection).to receive(:quote, &:itself)
allow(connection).to receive(:execute)
@@ -242,6 +243,8 @@ describe Gitlab::Database do
]
end
+ let_it_be(:version) { 9.6 }
+
it 'does nothing with empty rows' do
expect(connection).not_to receive(:execute)
@@ -307,6 +310,30 @@ describe Gitlab::Database do
expect(ids).to eq([10])
end
+
+ context 'with version >= 9.5' do
+ it 'allows setting the upsert to do nothing' do
+ expect(connection)
+ .to receive(:execute)
+ .with(/ON CONFLICT DO NOTHING/)
+
+ described_class
+ .bulk_insert('test', [{ number: 10 }], on_conflict: :do_nothing)
+ end
+ end
+
+ context 'with version < 9.5' do
+ let(:version) { 9.4 }
+
+ it 'refuses setting the upsert' do
+ expect(connection)
+ .not_to receive(:execute)
+ .with(/ON CONFLICT/)
+
+ described_class
+ .bulk_insert('test', [{ number: 10 }], on_conflict: :do_nothing)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/diff/deprecated_highlight_cache_spec.rb b/spec/lib/gitlab/diff/deprecated_highlight_cache_spec.rb
new file mode 100644
index 00000000000..7e46632ea77
--- /dev/null
+++ b/spec/lib/gitlab/diff/deprecated_highlight_cache_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::DeprecatedHighlightCache do
+ let(:merge_request) { create(:merge_request_with_diffs) }
+
+ subject(:cache) { described_class.new(merge_request.diffs, backend: backend) }
+
+ describe '#decorate' do
+ let(:backend) { double('backend').as_null_object }
+
+ # Manually creates a Diff::File object to avoid triggering the cache on
+ # the FileCollection::MergeRequestDiff
+ let(:diff_file) do
+ diffs = merge_request.diffs
+ raw_diff = diffs.diffable.raw_diffs(diffs.diff_options.merge(paths: ['CHANGELOG'])).first
+ Gitlab::Diff::File.new(raw_diff,
+ repository: diffs.project.repository,
+ diff_refs: diffs.diff_refs,
+ fallback_diff_refs: diffs.fallback_diff_refs)
+ end
+
+ it 'does not calculate highlighting when reading from cache' do
+ cache.write_if_empty
+ cache.decorate(diff_file)
+
+ expect_any_instance_of(Gitlab::Diff::Highlight).not_to receive(:highlight)
+
+ diff_file.highlighted_diff_lines
+ end
+
+ it 'assigns highlighted diff lines to the DiffFile' do
+ cache.write_if_empty
+ cache.decorate(diff_file)
+
+ expect(diff_file.highlighted_diff_lines.size).to be > 5
+ end
+
+ it 'submits a single reading from the cache' do
+ cache.decorate(diff_file)
+ cache.decorate(diff_file)
+
+ expect(backend).to have_received(:read).with(cache.key).once
+ end
+ end
+
+ describe '#write_if_empty' do
+ let(:backend) { double('backend', read: {}).as_null_object }
+
+ it 'submits a single writing to the cache' do
+ cache.write_if_empty
+ cache.write_if_empty
+
+ expect(backend).to have_received(:write).with(cache.key,
+ hash_including('CHANGELOG-false-false-false'),
+ expires_in: 1.week).once
+ end
+ end
+
+ describe '#clear' do
+ let(:backend) { double('backend').as_null_object }
+
+ it 'clears cache' do
+ cache.clear
+
+ expect(backend).to have_received(:delete).with(cache.key)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/diff_refs_spec.rb b/spec/lib/gitlab/diff/diff_refs_spec.rb
index e12b46c15ad..33a7cf5ae12 100644
--- a/spec/lib/gitlab/diff/diff_refs_spec.rb
+++ b/spec/lib/gitlab/diff/diff_refs_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::Diff::DiffRefs do
describe '#==' do
let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
+
subject { commit.diff_refs }
context 'when shas are missing' do
@@ -63,6 +64,7 @@ describe Gitlab::Diff::DiffRefs do
describe '#compare_in' do
context 'with diff refs for the initial commit' do
let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
+
subject { commit.diff_refs }
it 'returns an appropriate comparison' do
@@ -74,6 +76,7 @@ describe Gitlab::Diff::DiffRefs do
context 'with diff refs for a commit' do
let(:commit) { project.commit('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
+
subject { commit.diff_refs }
it 'returns an appropriate comparison' do
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
index 265c6260ca9..7e945d1d140 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb
@@ -123,4 +123,8 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiffBatch do
collection_default_args)
end
end
+
+ it_behaves_like 'cacheable diff collection' do
+ let(:cacheable_files_count) { batch_size }
+ end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index d89be6fef4e..a4f74ddc8c2 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -4,22 +4,31 @@ require 'spec_helper'
describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:merge_request) { create(:merge_request) }
- let(:subject) { described_class.new(merge_request.merge_request_diff, diff_options: nil) }
+ let(:diffable) { merge_request.merge_request_diff }
+ let(:subject) { described_class.new(diffable, diff_options: nil) }
let(:diff_files) { subject.diff_files }
describe '#diff_files' do
it 'does not highlight binary files' do
- allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(false)
+ allow_next_instance_of(Gitlab::Diff::File) do |instance|
+ allow(instance).to receive(:text?).and_return(false)
+ end
- expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+ expect_next_instance_of(Gitlab::Diff::File) do |instance|
+ expect(instance).not_to receive(:highlighted_diff_lines)
+ end
diff_files
end
it 'does not highlight files marked as undiffable in .gitattributes' do
- allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(false)
+ allow_next_instance_of(Gitlab::Diff::File) do |instance|
+ allow(instance).to receive(:diffable?).and_return(false)
+ end
- expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+ expect_next_instance_of(Gitlab::Diff::File) do |instance|
+ expect(instance).not_to receive(:highlighted_diff_lines)
+ end
diff_files
end
@@ -29,13 +38,19 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:diffable) { merge_request.merge_request_diff }
end
- it 'uses a different cache key if diff line keys change' do
- mr_diff = described_class.new(merge_request.merge_request_diff, diff_options: nil)
- key = mr_diff.cache_key
+ context 'using Gitlab::Diff::DeprecatedHighlightCache' do
+ before do
+ stub_feature_flags(hset_redis_diff_caching: false)
+ end
+
+ it 'uses a different cache key if diff line keys change' do
+ mr_diff = described_class.new(merge_request.merge_request_diff, diff_options: nil)
+ key = mr_diff.cache_key
- stub_const('Gitlab::Diff::Line::SERIALIZE_KEYS', [:foo])
+ stub_const('Gitlab::Diff::Line::SERIALIZE_KEYS', [:foo])
- expect(mr_diff.cache_key).not_to eq(key)
+ expect(mr_diff.cache_key).not_to eq(key)
+ end
end
it_behaves_like 'diff statistics' do
@@ -46,6 +61,10 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:stub_path) { '.gitignore' }
end
+ it_behaves_like 'cacheable diff collection' do
+ let(:cacheable_files_count) { diffable.size.to_i }
+ end
+
it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
end
diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb
index bfcfed4231f..c73ec84e332 100644
--- a/spec/lib/gitlab/diff/highlight_cache_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb
@@ -2,14 +2,46 @@
require 'spec_helper'
-describe Gitlab::Diff::HighlightCache do
+describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
let(:merge_request) { create(:merge_request_with_diffs) }
+ let(:diff_hash) 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 },
+ { 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 },
+ { 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 },
+ { 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 }] }
+ end
- subject(:cache) { described_class.new(merge_request.diffs, backend: backend) }
+ let(:cache_key) { cache.key }
- describe '#decorate' do
- let(:backend) { double('backend').as_null_object }
+ subject(:cache) { described_class.new(merge_request.diffs) }
+ describe '#decorate' do
# Manually creates a Diff::File object to avoid triggering the cache on
# the FileCollection::MergeRequestDiff
let(:diff_file) do
@@ -37,34 +69,89 @@ describe Gitlab::Diff::HighlightCache do
expect(diff_file.highlighted_diff_lines.size).to be > 5
end
- it 'submits a single reading from the cache' do
- cache.decorate(diff_file)
+ it 'assigns highlighted diff lines which rich_text are HTML-safe' do
+ cache.write_if_empty
cache.decorate(diff_file)
- expect(backend).to have_received(:read).with(cache.key).once
+ rich_texts = diff_file.highlighted_diff_lines.map(&:rich_text)
+
+ expect(rich_texts).to all(be_html_safe)
end
end
- describe '#write_if_empty' do
- let(:backend) { double('backend', read: {}).as_null_object }
+ 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
+
+ 2.times { cache.write_if_empty }
+ end
+
+ it 'reads from cache once' do
+ expect(cache).to receive(:read_cache).once.and_call_original
- it 'submits a single writing to the cache' do
- cache.write_if_empty
cache.write_if_empty
+ end
+ end
- expect(backend).to have_received(:write).with(cache.key,
- hash_including('CHANGELOG-false-false-false'),
- expires_in: 1.week).once
+ describe '#write_if_empty' do
+ it_behaves_like 'caches missing entries' do
+ let(:paths) { merge_request.diffs.raw_diff_files.select(&:text?).map(&:file_path) }
+ end
+
+ context 'different diff_collections for the same diffable' do
+ before do
+ cache.write_if_empty
+ end
+
+ it 'writes an uncached files in the collection to the same redis hash' do
+ Gitlab::Redis::Cache.with { |r| r.hdel(cache_key, "files/whitespace") }
+
+ expect { cache.write_if_empty }
+ .to change { Gitlab::Redis::Cache.with { |r| r.hgetall(cache_key) } }
+ end
+ end
+
+ context 'when cache initialized with MergeRequestDiffBatch' do
+ let(:merge_request_diff_batch) do
+ Gitlab::Diff::FileCollection::MergeRequestDiffBatch.new(
+ merge_request.merge_request_diff,
+ 1,
+ 10,
+ diff_options: nil)
+ end
+
+ it_behaves_like 'caches missing entries' do
+ let(:cache) { described_class.new(merge_request_diff_batch) }
+ let(:paths) { merge_request_diff_batch.raw_diff_files.select(&:text?).map(&:file_path) }
+ end
end
end
- describe '#clear' do
- let(:backend) { double('backend').as_null_object }
+ describe '#write_to_redis_hash' do
+ it 'creates or updates a Redis hash' do
+ expect { cache.send(:write_to_redis_hash, diff_hash) }
+ .to change { Gitlab::Redis::Cache.with { |r| r.hgetall(cache_key) } }
+ end
+ # Note that this spec and the code it confirms can be removed when
+ # :hset_redis_diff_caching is fully launched.
+ #
+ it 'attempts to clear deprecated cache entries' do
+ expect_any_instance_of(Gitlab::Diff::DeprecatedHighlightCache)
+ .to receive(:clear).and_call_original
+
+ cache.send(:write_to_redis_hash, diff_hash)
+ end
+ end
+
+ describe '#clear' do
it 'clears cache' do
- cache.clear
+ expect_any_instance_of(Redis).to receive(:del).with(cache_key)
- expect(backend).to have_received(:delete).with(cache.key)
+ cache.clear
end
end
end
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index f5d3d14ccc5..ff4ec75358e 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -105,7 +105,7 @@ describe Gitlab::Diff::Highlight do
end
it 'keeps the original rich line' do
- allow(Gitlab::Sentry).to receive(:track_exception)
+ allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
code = %q{+ raise RuntimeError, "System commands must be given as an array of strings"}
@@ -114,7 +114,7 @@ describe Gitlab::Diff::Highlight do
end
it 'reports to Sentry if configured' do
- expect(Gitlab::Sentry).to receive(:track_exception).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).and_call_original
expect { subject }. to raise_exception(RangeError)
end
diff --git a/spec/lib/gitlab/diff/line_mapper_spec.rb b/spec/lib/gitlab/diff/line_mapper_spec.rb
index 1739bcd14a8..6a86f885c3b 100644
--- a/spec/lib/gitlab/diff/line_mapper_spec.rb
+++ b/spec/lib/gitlab/diff/line_mapper_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::Diff::LineMapper do
let(:diffs) { commit.raw_diffs }
let(:diff) { diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: repository) }
+
subject { described_class.new(diff_file) }
describe '#old_to_new' do
diff --git a/spec/lib/gitlab/diff/line_spec.rb b/spec/lib/gitlab/diff/line_spec.rb
index 29b9951ba4c..7961bec9d57 100644
--- a/spec/lib/gitlab/diff/line_spec.rb
+++ b/spec/lib/gitlab/diff/line_spec.rb
@@ -1,18 +1,48 @@
# frozen_string_literal: true
+require 'spec_helper'
+
describe Gitlab::Diff::Line do
- describe '.init_from_hash' do
+ shared_examples 'line object initialized by hash' do
it 'round-trips correctly with to_hash' do
- line = described_class.new('<input>', 'match', 0, 0, 1,
- parent_file: double(:file),
- line_code: double(:line_code),
- rich_text: '&lt;input&gt;')
-
- expect(described_class.init_from_hash(line.to_hash).to_hash)
+ expect(described_class.safe_init_from_hash(line.to_hash).to_hash)
.to eq(line.to_hash)
end
end
+ let(:line) do
+ described_class.new('<input>', 'match', 0, 0, 1,
+ parent_file: double(:file),
+ line_code: double(:line_code),
+ rich_text: rich_text)
+ end
+
+ describe '.init_from_hash' do
+ let(:rich_text) { '&lt;input&gt;' }
+
+ it_behaves_like 'line object initialized by hash'
+ end
+
+ describe '.safe_init_from_hash' do
+ let(:rich_text) { '<input>' }
+
+ it_behaves_like 'line object initialized by hash'
+
+ it 'ensures rich_text is HTML-safe' do
+ expect(line.rich_text).not_to be_html_safe
+
+ new_line = described_class.safe_init_from_hash(line.to_hash)
+
+ expect(new_line.rich_text).to be_html_safe
+ end
+
+ context 'when given hash has no rich_text' do
+ it_behaves_like 'line object initialized by hash' do
+ let(:rich_text) { nil }
+ end
+ end
+ end
+
context "when setting rich text" do
it 'escapes any HTML special characters in the diff chunk header' do
subject = described_class.new("<input>", "", 0, 0, 0)
diff --git a/spec/lib/gitlab/diff/parallel_diff_spec.rb b/spec/lib/gitlab/diff/parallel_diff_spec.rb
index 7540da71086..d275bf2c223 100644
--- a/spec/lib/gitlab/diff/parallel_diff_spec.rb
+++ b/spec/lib/gitlab/diff/parallel_diff_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::Diff::ParallelDiff do
let(:diffs) { commit.raw_diffs }
let(:diff) { diffs.first }
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: repository) }
+
subject { described_class.new(diff_file) }
describe '#parallelize' do
diff --git a/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb b/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb
index 15ee8c40b55..97d3a49ea90 100644
--- a/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb
+++ b/spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb
@@ -62,6 +62,15 @@ describe Gitlab::DiscussionsDiff::HighlightCache, :clean_gitlab_redis_cache do
expect(found.second.size).to eq(2)
expect(found.second).to all(be_a(Gitlab::Diff::Line))
end
+
+ it 'returns lines which rich_text are HTML-safe' do
+ described_class.write_multiple(mapping)
+
+ found = described_class.read_multiple(mapping.keys)
+ rich_texts = found.flatten.map(&:rich_text)
+
+ expect(rich_texts).to all(be_html_safe)
+ end
end
describe '#clear_multiple' do
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 2fa86b2b46f..9f5413f9607 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -95,7 +95,9 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
context "something is wrong" do
context "when the merge request could not be saved" do
before do
- allow_any_instance_of(MergeRequest).to receive(:save).and_return(false)
+ allow_next_instance_of(MergeRequest) do |instance|
+ allow(instance).to receive(:save).and_return(false)
+ end
end
it "raises an InvalidMergeRequestError" do
@@ -189,6 +191,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
describe '#patch_attachments' do
let(:email_raw) { email_fixture('emails/merge_request_multiple_patches.eml') }
let(:mail) { Mail::Message.new(email_raw) }
+
subject(:handler) { described_class.new(mail, mail_key) }
it 'orders attachments ending in `.patch` by name' do
diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb
index d2920b08956..5229b778ccf 100644
--- a/spec/lib/gitlab/email/handler_spec.rb
+++ b/spec/lib/gitlab/email/handler_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe Gitlab::Email::Handler do
describe '.for' do
- it 'picks issue handler if there is not merge request prefix' do
+ it 'picks issue handler if there is no merge request prefix' do
expect(described_class.for('email', 'project+key')).to be_an_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end
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
new file mode 100644
index 00000000000..04ef5ba516e
--- /dev/null
+++ b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do
+ let(:error_event) { build(:error_tracking_error_event) }
+
+ describe '.decorate' do
+ subject(:decorate) { described_class.decorate(error_event) }
+
+ it 'does not change issue_id' do
+ expect(decorate.issue_id).to eq(error_event.issue_id)
+ end
+
+ it 'does not change date_received' do
+ expect(decorate.date_received).to eq(error_event.date_received)
+ end
+
+ it 'decorates the stack trace context' do
+ expect(decorate.stack_trace_entries).to eq(
+ [
+ {
+ 'function' => 'puts',
+ 'lineNo' => 14,
+ 'filename' => 'hello_world.rb',
+ 'context' => [
+ [10, '<span id="LC1" class="line" lang="ruby"><span class="c1"># Ruby example</span></span>'],
+ [11, '<span id="LC1" class="line" lang="ruby"><span class="k">class</span> <span class="nc">HelloWorld</span></span>'],
+ [12, '<span id="LC1" class="line" lang="ruby"> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">message</span></span>'],
+ [13, '<span id="LC1" class="line" lang="ruby"> <span class="vi">@name</span> <span class="o">=</span> <span class="s1">\'World\'</span></span>'],
+ [14, %Q[<span id="LC1" class="line" lang="ruby"> <span class="nb">puts</span> <span class="s2">"Hello </span><span class="si">\#{</span><span class="vi">@name</span><span class="si">}</span><span class="s2">"</span></span>]],
+ [15, '<span id="LC1" class="line" lang="ruby"> <span class="k">end</span></span>'],
+ [16, '<span id="LC1" class="line" lang="ruby"><span class="k">end</span></span>']
+ ]
+ },
+ {
+ 'function' => 'print',
+ 'lineNo' => 6,
+ 'filename' => 'HelloWorld.swift',
+ 'context' => [
+ [1, '<span id="LC1" class="line" lang="swift"><span class="c1">// Swift example</span></span>'],
+ [2, '<span id="LC1" class="line" lang="swift"><span class="kd">struct</span> <span class="kt">HelloWorld</span> <span class="p">{</span></span>'],
+ [3, '<span id="LC1" class="line" lang="swift"> <span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="s">"World"</span></span>'],
+ [4, '<span id="LC1" class="line" lang="swift"></span>'],
+ [5, '<span id="LC1" class="line" lang="swift"> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">message</span><span class="p">()</span> <span class="p">{</span></span>'],
+ [6, '<span id="LC1" class="line" lang="swift"> <span class="nf">print</span><span class="p">(</span><span class="s">"Hello, </span><span class="se">\\(</span><span class="k">self</span><span class="o">.</span><span class="n">name</span><span class="se">)</span><span class="s">"</span><span class="p">)</span></span>'],
+ [7, '<span id="LC1" class="line" lang="swift"> <span class="p">}</span></span>'],
+ [8, '<span id="LC1" class="line" lang="swift"><span class="p">}</span></span>']
+ ]
+ },
+ {
+ 'filename' => 'blank.txt'
+ }
+ ]
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/error_tracking_spec.rb b/spec/lib/gitlab/error_tracking_spec.rb
new file mode 100644
index 00000000000..08718bc92a1
--- /dev/null
+++ b/spec/lib/gitlab/error_tracking_spec.rb
@@ -0,0 +1,172 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ErrorTracking do
+ let(:exception) { RuntimeError.new('boom') }
+ let(:issue_url) { 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
+
+ let(:expected_payload_includes) do
+ [
+ { 'exception.class' => 'RuntimeError' },
+ { 'exception.message' => 'boom' },
+ { 'tags.correlation_id' => 'cid' },
+ { 'extra.some_other_info' => 'info' },
+ { 'extra.issue_url' => 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
+ ]
+ end
+
+ before do
+ stub_sentry_settings
+
+ allow(described_class).to receive(:sentry_dsn).and_return(Gitlab.config.sentry.dsn)
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
+
+ described_class.configure
+ end
+
+ describe '.with_context' do
+ it 'adds the expected tags' do
+ described_class.with_context {}
+
+ expect(Raven.tags_context[:locale].to_s).to eq(I18n.locale.to_s)
+ expect(Raven.tags_context[Labkit::Correlation::CorrelationId::LOG_KEY.to_sym].to_s)
+ .to eq('cid')
+ end
+ end
+
+ describe '.track_and_raise_for_dev_exception' do
+ context 'when exceptions for dev should be raised' do
+ before do
+ expect(described_class).to receive(:should_raise_for_dev?).and_return(true)
+ end
+
+ it 'raises the exception' do
+ expect(Raven).to receive(:capture_exception)
+
+ expect { described_class.track_and_raise_for_dev_exception(exception) }
+ .to raise_error(RuntimeError)
+ end
+ end
+
+ context 'when exceptions for dev should not be raised' do
+ before do
+ expect(described_class).to receive(:should_raise_for_dev?).and_return(false)
+ end
+
+ it 'logs the exception with all attributes passed' do
+ expected_extras = {
+ some_other_info: 'info',
+ issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
+ }
+
+ expected_tags = {
+ correlation_id: 'cid'
+ }
+
+ expect(Raven).to receive(:capture_exception)
+ .with(exception,
+ tags: a_hash_including(expected_tags),
+ extra: a_hash_including(expected_extras))
+
+ described_class.track_and_raise_for_dev_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end
+
+ it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error)
+ .with(a_hash_including(*expected_payload_includes))
+
+ described_class.track_and_raise_for_dev_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end
+ end
+ end
+
+ describe '.track_and_raise_exception' do
+ it 'always raises the exception' do
+ expect(Raven).to receive(:capture_exception)
+
+ expect { described_class.track_and_raise_exception(exception) }
+ .to raise_error(RuntimeError)
+ end
+
+ it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error)
+ .with(a_hash_including(*expected_payload_includes))
+
+ expect do
+ described_class.track_and_raise_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end.to raise_error(RuntimeError)
+ end
+ end
+
+ describe '.track_exception' do
+ it 'calls Raven.capture_exception' do
+ expected_extras = {
+ some_other_info: 'info',
+ issue_url: issue_url
+ }
+
+ expected_tags = {
+ correlation_id: 'cid'
+ }
+
+ expect(Raven).to receive(:capture_exception)
+ .with(exception,
+ tags: a_hash_including(expected_tags),
+ extra: a_hash_including(expected_extras))
+
+ described_class.track_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end
+
+ it 'calls Gitlab::ErrorTracking::Logger.error with formatted payload' do
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error)
+ .with(a_hash_including(*expected_payload_includes))
+
+ described_class.track_exception(
+ exception,
+ issue_url: issue_url,
+ some_other_info: 'info'
+ )
+ end
+
+ context 'the exception implements :sentry_extra_data' do
+ let(:extra_info) { { event: 'explosion', size: :massive } }
+ let(:exception) { double(message: 'bang!', sentry_extra_data: extra_info, backtrace: caller) }
+
+ it 'includes the extra data from the exception in the tracking information' do
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+
+ described_class.track_exception(exception)
+ end
+ end
+
+ context 'the exception implements :sentry_extra_data, which returns nil' do
+ let(:exception) { double(message: 'bang!', sentry_extra_data: nil, backtrace: caller) }
+
+ it 'just includes the other extra info' do
+ extra_info = { issue_url: issue_url }
+ expect(Raven).to receive(:capture_exception)
+ .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
+
+ described_class.track_exception(exception, extra_info)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index e7734c6f9f6..24df67b3058 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -38,8 +38,9 @@ describe Gitlab::EtagCaching::Middleware do
end
it 'generates ETag' do
- expect_any_instance_of(Gitlab::EtagCaching::Store)
- .to receive(:touch).and_return('123')
+ expect_next_instance_of(Gitlab::EtagCaching::Store) do |instance|
+ expect(instance).to receive(:touch).and_return('123')
+ end
middleware.call(build_request(path, if_none_match))
end
@@ -177,9 +178,9 @@ describe Gitlab::EtagCaching::Middleware do
'SCRIPT_NAME' => '/relative-gitlab'
}
- expect_any_instance_of(Gitlab::EtagCaching::Store)
- .to receive(:get).with("/relative-gitlab#{enabled_path}")
- .and_return(nil)
+ expect_next_instance_of(Gitlab::EtagCaching::Store) do |instance|
+ expect(instance).to receive(:get).with("/relative-gitlab#{enabled_path}").and_return(nil)
+ end
middleware.call(env)
end
@@ -190,8 +191,9 @@ describe Gitlab::EtagCaching::Middleware do
end
def mock_value_in_store(value)
- allow_any_instance_of(Gitlab::EtagCaching::Store)
- .to receive(:get).and_return(value)
+ allow_next_instance_of(Gitlab::EtagCaching::Store) do |instance|
+ allow(instance).to receive(:get).and_return(value)
+ end
end
def build_request(path, if_none_match)
diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb
index 8fcd4eb3c21..e25ce4df4aa 100644
--- a/spec/lib/gitlab/etag_caching/router_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router_spec.rb
@@ -12,6 +12,15 @@ describe Gitlab::EtagCaching::Router do
expect(result.name).to eq 'issue_notes'
end
+ it 'matches MR notes endpoint' do
+ result = described_class.match(
+ '/my-group/and-subgroup/here-comes-the-project/noteable/merge_request/1/notes'
+ )
+
+ expect(result).to be_present
+ expect(result.name).to eq 'merge_request_notes'
+ end
+
it 'matches issue title endpoint' do
result = described_class.match(
'/my-group/my-project/issues/123/realtime_changes'
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index 9be6ace3be5..b8be72cf8d7 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -158,7 +158,9 @@ describe Gitlab::Experimentation do
context 'the user is part of the control group' do
before do
- allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
+ end
end
it 'pushes the right parameters to gon' do
diff --git a/spec/lib/gitlab/external_authorization/client_spec.rb b/spec/lib/gitlab/external_authorization/client_spec.rb
index a17d933e3bb..e28a155a47f 100644
--- a/spec/lib/gitlab/external_authorization/client_spec.rb
+++ b/spec/lib/gitlab/external_authorization/client_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Gitlab::ExternalAuthorization::Client do
let(:user) { build(:user, email: 'dummy_user@example.com') }
let(:dummy_url) { 'https://dummy.net/' }
+
subject(:client) { described_class.new(user, 'dummy_label') }
before do
diff --git a/spec/lib/gitlab/external_authorization/response_spec.rb b/spec/lib/gitlab/external_authorization/response_spec.rb
index e1f6e9ac1fa..5ce3325ef77 100644
--- a/spec/lib/gitlab/external_authorization/response_spec.rb
+++ b/spec/lib/gitlab/external_authorization/response_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
describe Gitlab::ExternalAuthorization::Response do
let(:excon_response) { double }
+
subject(:response) { described_class.new(excon_response) }
describe '#valid?' do
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index f3a9f706e86..23f7deba7f7 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -82,5 +82,21 @@ describe Gitlab::FileDetector do
it 'returns nil for an unknown file' do
expect(described_class.type_of('foo.txt')).to be_nil
end
+
+ it 'returns the type of an OpenAPI spec if file name is correct' do
+ openapi_types = [
+ 'openapi.yml', 'openapi.yaml', 'openapi.json',
+ 'swagger.yml', 'swagger.yaml', 'swagger.json',
+ 'gitlab_swagger.yml', 'openapi_gitlab.yml',
+ 'OpenAPI.YML', 'openapi.Yaml', 'openapi.JSON',
+ 'openapi.gitlab.yml', 'gitlab.openapi.yml'
+ ]
+
+ openapi_types.each do |type_name|
+ expect(described_class.type_of(type_name)).to eq(:openapi)
+ end
+
+ expect(described_class.type_of('openapiyml')).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb
index 7ea9d43c9f7..6cc5141a6fe 100644
--- a/spec/lib/gitlab/file_finder_spec.rb
+++ b/spec/lib/gitlab/file_finder_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Gitlab::FileFinder do
describe '#find' do
let(:project) { create(:project, :public, :repository) }
+
subject { described_class.new(project, project.default_branch) }
it_behaves_like 'file finder' do
diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb
index 676511211c8..ca6f374476c 100644
--- a/spec/lib/gitlab/fogbugz_import/client_spec.rb
+++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb
@@ -20,6 +20,8 @@ describe Gitlab::FogbugzImport::Client do
end
def stub_api(users)
- allow_any_instance_of(::Fogbugz::Interface).to receive(:command).with(:listPeople).and_return(users)
+ allow_next_instance_of(::Fogbugz::Interface) do |instance|
+ allow(instance).to receive(:command).with(:listPeople).and_return(users)
+ end
end
end
diff --git a/spec/lib/gitlab/fogbugz_import/importer_spec.rb b/spec/lib/gitlab/fogbugz_import/importer_spec.rb
new file mode 100644
index 00000000000..9e67047eeda
--- /dev/null
+++ b/spec/lib/gitlab/fogbugz_import/importer_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::FogbugzImport::Importer do
+ let(:project) { create(:project_empty_repo) }
+ let(:importer) { described_class.new(project) }
+ let(:repo) do
+ instance_double(Gitlab::FogbugzImport::Repository,
+ safe_name: 'vim',
+ path: 'vim',
+ raw_data: '')
+ end
+ let(:import_data) { { 'repo' => repo } }
+ let(:credentials) do
+ {
+ 'fb_session' => {
+ 'uri' => 'https://testing.fogbugz.com',
+ 'token' => 'token'
+ }
+ }
+ end
+
+ let(:closed_bug) do
+ {
+ fOpen: 'false',
+ sTitle: 'Closed bug',
+ sLatestTextSummary: "",
+ dtOpened: Time.now.to_s,
+ dtLastUpdated: Time.now.to_s,
+ events: { event: [] }
+ }.with_indifferent_access
+ end
+
+ let(:opened_bug) do
+ {
+ fOpen: 'true',
+ sTitle: 'Opened bug',
+ sLatestTextSummary: "",
+ dtOpened: Time.now.to_s,
+ dtLastUpdated: Time.now.to_s,
+ events: { event: [] }
+ }.with_indifferent_access
+ end
+
+ let(:fogbugz_bugs) { [opened_bug, closed_bug] }
+
+ before do
+ project.create_import_data(data: import_data, credentials: credentials)
+ allow_any_instance_of(::Fogbugz::Interface).to receive(:command).with(:listCategories).and_return([])
+ allow_any_instance_of(Gitlab::FogbugzImport::Client).to receive(:cases).and_return(fogbugz_bugs)
+ end
+
+ it 'imports bugs' do
+ expect { importer.execute }.to change { Issue.count }.by(2)
+ end
+
+ it 'imports opened bugs' do
+ importer.execute
+
+ issue = Issue.where(project_id: project.id).find_by_title(opened_bug[:sTitle])
+
+ expect(issue.state_id).to eq(Issue.available_states[:opened])
+ end
+
+ it 'imports closed bugs' do
+ importer.execute
+
+ issue = Issue.where(project_id: project.id).find_by_title(closed_bug[:sTitle])
+
+ expect(issue.state_id).to eq(Issue.available_states[:closed])
+ end
+end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index 026fd1fedde..d16f34af325 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -54,11 +54,13 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'code' do
let(:text) { "#1, but not `[#1]`" }
+
it { is_expected.to eq "#{issue_first.to_reference(new_project)}, but not `[#1]`" }
end
context 'code reverse' do
let(:text) { "not `#1`, but #1" }
+
it { is_expected.to eq "not `#1`, but #{issue_first.to_reference(new_project)}" }
end
@@ -74,11 +76,13 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'label referenced by id' do
let(:text) { '#1 and ~123' }
+
it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~123} }
end
context 'label referenced by text' do
let(:text) { '#1 and ~"test"' }
+
it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~123} }
end
end
@@ -93,11 +97,13 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'label referenced by id' do
let(:text) { '#1 and ~321' }
+
it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~321} }
end
context 'label referenced by text' do
let(:text) { '#1 and ~"group label"' }
+
it { is_expected.to eq %Q{#{old_project_ref}#1 and #{old_project_ref}~321} }
end
end
diff --git a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
index 134bd5657e7..6c4f650fa83 100644
--- a/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::AttributesAtRefParser, :seed_helper do
diff --git a/spec/lib/gitlab/git/attributes_parser_spec.rb b/spec/lib/gitlab/git/attributes_parser_spec.rb
index f431d4e2a53..94b7a086e59 100644
--- a/spec/lib/gitlab/git/attributes_parser_spec.rb
+++ b/spec/lib/gitlab/git/attributes_parser_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::AttributesParser, :seed_helper do
diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb
index ac085e2c266..9b2d6fa3bcb 100644
--- a/spec/lib/gitlab/git/blame_spec.rb
+++ b/spec/lib/gitlab/git/blame_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Blame, :seed_helper do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 7f680071969..a659af3d22e 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Blob, :seed_helper do
@@ -132,7 +134,9 @@ describe Gitlab::Git::Blob, :seed_helper do
describe '.find with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
- allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ allow_next_instance_of(Rugged) do |instance|
+ allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ end
described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
end
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index 02ef7b92538..cc26b7e7fcd 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Branch, :seed_helper do
@@ -77,7 +79,7 @@ describe Gitlab::Git::Branch, :seed_helper do
tree = parents.first.tree
{
- message: 'commit message',
+ message: +'commit message',
author: committer,
committer: committer,
tree: tree,
@@ -126,7 +128,7 @@ describe Gitlab::Git::Branch, :seed_helper do
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
- params[:message].delete!("\r")
+ params[:message].delete!(+"\r")
Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
end
end
diff --git a/spec/lib/gitlab/git/bundle_file_spec.rb b/spec/lib/gitlab/git/bundle_file_spec.rb
index ff7c981dadd..e88e163a03f 100644
--- a/spec/lib/gitlab/git/bundle_file_spec.rb
+++ b/spec/lib/gitlab/git/bundle_file_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::BundleFile do
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index cdab7127748..7ec655eb113 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Commit, :seed_helper do
@@ -15,13 +17,13 @@ describe Gitlab::Git::Commit, :seed_helper do
@committer = {
email: 'mike@smith.com',
name: "Mike Smith",
- time: Time.now
+ time: Time.new(2000, 1, 1, 0, 0, 0, "+08:00")
}
@author = {
email: 'john@smith.com',
name: "John Smith",
- time: Time.now
+ time: Time.new(2000, 1, 1, 0, 0, 0, "-08:00")
}
@parents = [rugged_repo.head.target]
@@ -46,7 +48,7 @@ describe Gitlab::Git::Commit, :seed_helper do
it { expect(@commit.id).to eq(@raw_commit.oid) }
it { expect(@commit.sha).to eq(@raw_commit.oid) }
it { expect(@commit.safe_message).to eq(@raw_commit.message) }
- it { expect(@commit.created_at).to eq(@raw_commit.author[:time]) }
+ it { expect(@commit.created_at).to eq(@raw_commit.committer[:time]) }
it { expect(@commit.date).to eq(@raw_commit.committer[:time]) }
it { expect(@commit.author_email).to eq(@author[:email]) }
it { expect(@commit.author_name).to eq(@author[:name]) }
@@ -64,8 +66,8 @@ describe Gitlab::Git::Commit, :seed_helper do
end
describe "Commit info from gitaly commit" do
- let(:subject) { "My commit".force_encoding('ASCII-8BIT') }
- let(:body) { subject + "My body".force_encoding('ASCII-8BIT') }
+ let(:subject) { (+"My commit").force_encoding('ASCII-8BIT') }
+ let(:body) { subject + (+"My body").force_encoding('ASCII-8BIT') }
let(:body_size) { body.length }
let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body, body_size: body_size) }
let(:id) { gitaly_commit.id }
@@ -77,15 +79,29 @@ describe Gitlab::Git::Commit, :seed_helper do
it { expect(commit.id).to eq(id) }
it { expect(commit.sha).to eq(id) }
it { expect(commit.safe_message).to eq(body) }
- it { expect(commit.created_at).to eq(Time.at(committer.date.seconds)) }
+ it { expect(commit.created_at).to eq(Time.at(committer.date.seconds).utc) }
it { expect(commit.author_email).to eq(author.email) }
it { expect(commit.author_name).to eq(author.name) }
it { expect(commit.committer_name).to eq(committer.name) }
it { expect(commit.committer_email).to eq(committer.email) }
it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) }
+ context 'non-UTC dates' do
+ let(:seconds) { Time.now.to_i }
+
+ it 'sets timezones correctly' do
+ gitaly_commit.author.date.seconds = seconds
+ gitaly_commit.author.timezone = '-0800'
+ gitaly_commit.committer.date.seconds = seconds
+ gitaly_commit.committer.timezone = '+0800'
+
+ expect(commit.authored_date).to eq(Time.at(seconds, in: '-08:00'))
+ expect(commit.committed_date).to eq(Time.at(seconds, in: '+08:00'))
+ end
+ end
+
context 'body_size != body.size' do
- let(:body) { "".force_encoding('ASCII-8BIT') }
+ let(:body) { (+"").force_encoding('ASCII-8BIT') }
context 'zero body_size' do
it { expect(commit.safe_message).to eq(subject) }
@@ -160,7 +176,9 @@ describe Gitlab::Git::Commit, :seed_helper do
describe '.find with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
- allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ allow_next_instance_of(Rugged) do |instance|
+ allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ end
described_class.find(repository, SeedRepo::Commit::ID)
end
@@ -422,7 +440,9 @@ describe Gitlab::Git::Commit, :seed_helper do
it_should_behave_like '.batch_by_oid'
it 'calls out to the Rugged implementation' do
- allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ allow_next_instance_of(Rugged) do |instance|
+ allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ end
described_class.batch_by_oid(repository, [SeedRepo::Commit::ID])
end
@@ -522,6 +542,7 @@ describe Gitlab::Git::Commit, :seed_helper do
skip 'move this test to gitaly-ruby' do
describe '#init_from_rugged' do
let(:gitlab_commit) { described_class.new(repository, rugged_commit) }
+
subject { gitlab_commit }
describe '#id' do
@@ -533,6 +554,7 @@ describe Gitlab::Git::Commit, :seed_helper do
describe '#init_from_hash' do
let(:commit) { described_class.new(repository, sample_commit_hash) }
+
subject { commit }
describe '#id' do
@@ -588,6 +610,7 @@ describe Gitlab::Git::Commit, :seed_helper do
describe '#to_hash' do
let(:hash) { commit.to_hash }
+
subject { hash }
it { is_expected.to be_kind_of Hash }
@@ -609,6 +632,7 @@ describe Gitlab::Git::Commit, :seed_helper do
describe '#ref_names' do
let(:commit) { described_class.find(repository, 'master') }
+
subject { commit.ref_names(repository) }
it 'has 2 element' do
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index 65dfb93d0db..6136df57acb 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Compare, :seed_helper do
diff --git a/spec/lib/gitlab/git/conflict/file_spec.rb b/spec/lib/gitlab/git/conflict/file_spec.rb
index a6cabd4966a..0ee9ff93e87 100644
--- a/spec/lib/gitlab/git/conflict/file_spec.rb
+++ b/spec/lib/gitlab/git/conflict/file_spec.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::Conflict::File do
let(:conflict) { { theirs: { path: 'foo', mode: 33188 }, ours: { path: 'foo', mode: 33188 } } }
- let(:invalid_content) { described_class.new(nil, nil, conflict, "a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }
- let(:valid_content) { described_class.new(nil, nil, conflict, "Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
+ let(:invalid_content) { described_class.new(nil, nil, conflict, (+"a\xC4\xFC").force_encoding(Encoding::ASCII_8BIT)) }
+ let(:valid_content) { described_class.new(nil, nil, conflict, (+"Espa\xC3\xB1a").force_encoding(Encoding::ASCII_8BIT)) }
describe '#lines' do
context 'when the content contains non-UTF-8 characters' do
diff --git a/spec/lib/gitlab/git/conflict/parser_spec.rb b/spec/lib/gitlab/git/conflict/parser_spec.rb
index 29a1702a1c6..600c870acd4 100644
--- a/spec/lib/gitlab/git/conflict/parser_spec.rb
+++ b/spec/lib/gitlab/git/conflict/parser_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::Conflict::Parser do
@@ -208,7 +210,7 @@ CONFLICT
# these strings.
context 'when the file contains UTF-8 characters' do
it 'does not raise' do
- expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
+ expect { parse_text((+"Espa\xC3\xB1a").force_encoding(Encoding::ASCII_8BIT)) }
.not_to raise_error
end
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index ce45d6e24ba..0d19d35bc52 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::DiffCollection, :seed_helper do
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 9ab669ad488..ac606da5cc1 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Diff, :seed_helper do
@@ -147,6 +149,7 @@ EOT
describe '.between' do
let(:diffs) { described_class.between(repository, 'feature', 'master') }
+
subject { diffs }
it { is_expected.to be_kind_of Gitlab::Git::DiffCollection }
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
index de81dcd227d..58d1d2c71da 100644
--- a/spec/lib/gitlab/git/gitmodules_parser_spec.rb
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::GitmodulesParser do
diff --git a/spec/lib/gitlab/git/hook_env_spec.rb b/spec/lib/gitlab/git/hook_env_spec.rb
index 5e49ea6da7a..22b016cee3e 100644
--- a/spec/lib/gitlab/git/hook_env_spec.rb
+++ b/spec/lib/gitlab/git/hook_env_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::HookEnv do
@@ -55,6 +57,7 @@ describe Gitlab::Git::HookEnv do
using RSpec::Parameterized::TableSyntax
let(:key) { 'GIT_OBJECT_DIRECTORY_RELATIVE' }
+
subject { described_class.to_env_hash(gl_repository) }
where(:input, :output) do
diff --git a/spec/lib/gitlab/git/lfs_changes_spec.rb b/spec/lib/gitlab/git/lfs_changes_spec.rb
index d035df7e0c2..a99e8c4f60c 100644
--- a/spec/lib/gitlab/git/lfs_changes_spec.rb
+++ b/spec/lib/gitlab/git/lfs_changes_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::LfsChanges do
diff --git a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
index d7f76737f3f..8bb26ed4854 100644
--- a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
+++ b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::LfsPointerFile do
diff --git a/spec/lib/gitlab/git/merge_base_spec.rb b/spec/lib/gitlab/git/merge_base_spec.rb
index dbb4e3d0b3e..fa95a1664ea 100644
--- a/spec/lib/gitlab/git/merge_base_spec.rb
+++ b/spec/lib/gitlab/git/merge_base_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Gitlab::Git::MergeBase do
set(:project) { create(:project, :repository) }
let(:repository) { project.repository }
+
subject(:merge_base) { described_class.new(repository, refs) }
shared_context 'existing refs with a merge base', :existing_refs do
diff --git a/spec/lib/gitlab/git/pre_receive_error_spec.rb b/spec/lib/gitlab/git/pre_receive_error_spec.rb
index cb030e38032..cb539261671 100644
--- a/spec/lib/gitlab/git/pre_receive_error_spec.rb
+++ b/spec/lib/gitlab/git/pre_receive_error_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::PreReceiveError do
diff --git a/spec/lib/gitlab/git/push_spec.rb b/spec/lib/gitlab/git/push_spec.rb
index 566c8209504..32c4c1c82d4 100644
--- a/spec/lib/gitlab/git/push_spec.rb
+++ b/spec/lib/gitlab/git/push_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::Push do
diff --git a/spec/lib/gitlab/git/raw_diff_change_spec.rb b/spec/lib/gitlab/git/raw_diff_change_spec.rb
index a0bb37fd84a..79b2fc21011 100644
--- a/spec/lib/gitlab/git/raw_diff_change_spec.rb
+++ b/spec/lib/gitlab/git/raw_diff_change_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::RawDiffChange do
diff --git a/spec/lib/gitlab/git/remote_mirror_spec.rb b/spec/lib/gitlab/git/remote_mirror_spec.rb
index dc63eef7814..9744562b51b 100644
--- a/spec/lib/gitlab/git/remote_mirror_spec.rb
+++ b/spec/lib/gitlab/git/remote_mirror_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::RemoteMirror do
diff --git a/spec/lib/gitlab/git/remote_repository_spec.rb b/spec/lib/gitlab/git/remote_repository_spec.rb
index e166628d4ca..b53eee293f0 100644
--- a/spec/lib/gitlab/git/remote_repository_spec.rb
+++ b/spec/lib/gitlab/git/remote_repository_spec.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::RemoteRepository, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+
subject { described_class.new(repository) }
describe '#empty?' do
diff --git a/spec/lib/gitlab/git/repository_cleaner_spec.rb b/spec/lib/gitlab/git/repository_cleaner_spec.rb
index 7bba0107e58..b387d1033d3 100644
--- a/spec/lib/gitlab/git/repository_cleaner_spec.rb
+++ b/spec/lib/gitlab/git/repository_cleaner_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::RepositoryCleaner do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 44c41da7560..6854d514dcc 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Repository, :seed_helper do
@@ -210,6 +212,7 @@ describe Gitlab::Git::Repository, :seed_helper do
describe '#ref_names' do
let(:ref_names) { repository.ref_names }
+
subject { ref_names }
it { is_expected.to be_kind_of Array }
diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb
index 4c0291f64f0..87db3f588ad 100644
--- a/spec/lib/gitlab/git/tag_spec.rb
+++ b/spec/lib/gitlab/git/tag_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Tag, :seed_helper do
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 7e169cfe270..d82acad866c 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::Git::Tree, :seed_helper do
@@ -143,7 +145,9 @@ describe Gitlab::Git::Tree, :seed_helper do
describe '.where with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
- allow_any_instance_of(Rugged).to receive(:lookup).with(SeedRepo::Commit::ID)
+ allow_next_instance_of(Rugged) do |instance|
+ allow(instance).to receive(:lookup).with(SeedRepo::Commit::ID)
+ end
described_class.where(repository, SeedRepo::Commit::ID, 'files', false)
end
diff --git a/spec/lib/gitlab/git/user_spec.rb b/spec/lib/gitlab/git/user_spec.rb
index d9d338206f8..6761413320a 100644
--- a/spec/lib/gitlab/git/user_spec.rb
+++ b/spec/lib/gitlab/git/user_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::User do
@@ -24,6 +26,7 @@ describe Gitlab::Git::User do
describe '.from_gitlab' do
context 'when no commit_email has been set' do
let(:user) { build(:user, email: 'alice@example.com', commit_email: nil) }
+
subject { described_class.from_gitlab(user) }
it { expect(subject).to eq(described_class.new(user.username, user.name, user.email, 'user-')) }
@@ -31,6 +34,7 @@ describe Gitlab::Git::User do
context 'when commit_email has been set' do
let(:user) { build(:user, email: 'alice@example.com', commit_email: 'bob@example.com') }
+
subject { described_class.from_gitlab(user) }
it { expect(subject).to eq(described_class.new(user.username, user.name, user.commit_email, 'user-')) }
diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb
index 88c871855df..81918f036f9 100644
--- a/spec/lib/gitlab/git/util_spec.rb
+++ b/spec/lib/gitlab/git/util_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::Util do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index 1e577392949..8bae2e8125e 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::Wiki do
diff --git a/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb
index bcf4814edb6..a4489cca443 100644
--- a/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb
+++ b/spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Git::WrapsGitalyErrors do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f74cc5623c9..36bde9de12d 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -730,7 +730,9 @@ describe Gitlab::GitAccess do
it 'checks LFS integrity only for first change' do
allow(project).to receive(:lfs_enabled?).and_return(true)
- expect_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).exactly(1).times
+ expect_next_instance_of(Gitlab::Checks::LfsIntegrity) do |instance|
+ expect(instance).to receive(:objects_missing?).exactly(1).times
+ end
push_access_check
end
diff --git a/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb b/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
index 3430fbb71f1..07f53797b2a 100644
--- a/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb
@@ -14,10 +14,11 @@ describe Gitlab::GitalyClient::CleanupService do
end
it 'sends an apply_bfg_object_map_stream message' do
- expect_any_instance_of(Gitaly::CleanupService::Stub)
- .to receive(:apply_bfg_object_map_stream)
- .with(kind_of(Enumerator), kind_of(Hash))
- .and_return([])
+ expect_next_instance_of(Gitaly::CleanupService::Stub) do |instance|
+ expect(instance).to receive(:apply_bfg_object_map_stream)
+ .with(kind_of(Enumerator), kind_of(Hash))
+ .and_return([])
+ end
client.apply_bfg_object_map_stream(StringIO.new)
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 1abdabe17bb..820578dfc6e 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -188,6 +188,7 @@ describe Gitlab::GitalyClient::CommitService do
describe '#find_commit' do
let(:revision) { Gitlab::Git::EMPTY_TREE_ID }
+
it 'sends an RPC request' do
request = Gitaly::FindCommitRequest.new(
repository: repository_message, revision: revision
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 2b4fe2ea5c0..d4a7f6e6df9 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -22,6 +22,7 @@ describe Gitlab::GitalyClient::RefService do
describe '#remote_branches' do
let(:remote_name) { 'my_remote' }
+
subject { client.remote_branches(remote_name) }
it 'sends a find_all_remote_branches message' do
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index b6c0c0ad523..4b69b4734f1 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -55,7 +55,9 @@ describe Gitlab::GitalyClient do
it 'returns an empty string when the storage is not found in the response' do
response = double("response")
allow(response).to receive(:storage_statuses).and_return([])
- allow_any_instance_of(Gitlab::GitalyClient::ServerService).to receive(:info).and_return(response)
+ allow_next_instance_of(Gitlab::GitalyClient::ServerService) do |instance|
+ allow(instance).to receive(:info).and_return(response)
+ end
expect(described_class.filesystem_id('default')).to eq(nil)
end
@@ -84,12 +86,11 @@ describe Gitlab::GitalyClient do
describe '.stub_certs' do
it 'skips certificates if OpenSSLError is raised and report it' do
- expect(Rails.logger).to receive(:error).at_least(:once)
- expect(Gitlab::Sentry)
- .to receive(:track_exception)
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_and_raise_for_dev_exception)
.with(
a_kind_of(OpenSSL::X509::CertificateError),
- extra: { cert_file: a_kind_of(String) }).at_least(:once)
+ cert_file: a_kind_of(String)).at_least(:once)
expect(OpenSSL::X509::Certificate)
.to receive(:new)
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 484458289af..66909976b43 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -144,9 +144,9 @@ describe Gitlab::GithubImport::Importer::DiffNoteImporter do
describe '#find_merge_request_id' do
it 'returns a merge request ID' do
- expect_any_instance_of(Gitlab::GithubImport::IssuableFinder)
- .to receive(:database_id)
- .and_return(10)
+ expect_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |instance|
+ expect(instance).to receive(:database_id).and_return(10)
+ end
expect(importer.find_merge_request_id).to eq(10)
end
diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
index a003ad7e091..0f2ba99f816 100644
--- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
@@ -99,7 +99,6 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
project_id: project.id,
description: 'This is my issue',
milestone_id: milestone.id,
- state: :opened,
state_id: 1,
created_at: created_at,
updated_at: updated_at
@@ -129,7 +128,6 @@ describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redis_cach
project_id: project.id,
description: "*Created by: alice*\n\nThis is my issue",
milestone_id: milestone.id,
- state: :opened,
state_id: 1,
created_at: created_at,
updated_at: updated_at
diff --git a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
index 19d40b2f380..9c02b0e280f 100644
--- a/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/label_links_importer_spec.rb
@@ -74,9 +74,9 @@ describe Gitlab::GithubImport::Importer::LabelLinksImporter do
describe '#find_target_id' do
it 'returns the ID of the issuable to create the label link for' do
- expect_any_instance_of(Gitlab::GithubImport::IssuableFinder)
- .to receive(:database_id)
- .and_return(10)
+ expect_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |instance|
+ expect(instance).to receive(:database_id).and_return(10)
+ end
expect(importer.find_target_id).to eq(10)
end
diff --git a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
index 2dcf1433154..16326da9ca4 100644
--- a/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/labels_importer_spec.rb
@@ -50,8 +50,9 @@ describe Gitlab::GithubImport::Importer::LabelsImporter, :clean_gitlab_redis_cac
describe '#build_labels_cache' do
it 'builds the labels cache' do
- expect_any_instance_of(Gitlab::GithubImport::LabelFinder)
- .to receive(:build_cache)
+ expect_next_instance_of(Gitlab::GithubImport::LabelFinder) do |instance|
+ expect(instance).to receive(:build_cache)
+ end
importer.build_labels_cache
end
diff --git a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
index eaf63e0e11b..294599c02f4 100644
--- a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
@@ -80,8 +80,9 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
describe '#build_milestones_cache' do
it 'builds the milestones cache' do
- expect_any_instance_of(Gitlab::GithubImport::MilestoneFinder)
- .to receive(:build_cache)
+ expect_next_instance_of(Gitlab::GithubImport::MilestoneFinder) do |instance|
+ expect(instance).to receive(:build_cache)
+ end
importer.build_milestones_cache
end
diff --git a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
index d2b8ba186c8..816041b771b 100644
--- a/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/note_importer_spec.rb
@@ -143,9 +143,9 @@ describe Gitlab::GithubImport::Importer::NoteImporter do
describe '#find_noteable_id' do
it 'returns the ID of the noteable' do
- expect_any_instance_of(Gitlab::GithubImport::IssuableFinder)
- .to receive(:database_id)
- .and_return(10)
+ expect_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |instance|
+ expect(instance).to receive(:database_id).and_return(10)
+ end
expect(importer.find_noteable_id).to eq(10)
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 50c27e7f4b7..877b4d4bbaf 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -94,7 +94,6 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
target_project_id: project.id,
source_branch: 'github/fork/alice/feature',
target_branch: 'master',
- state: :merged,
state_id: 3,
milestone_id: milestone.id,
author_id: user.id,
@@ -140,7 +139,6 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
target_project_id: project.id,
source_branch: 'github/fork/alice/feature',
target_branch: 'master',
- state: :merged,
state_id: 3,
milestone_id: milestone.id,
author_id: project.creator_id,
@@ -187,7 +185,6 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
target_project_id: project.id,
source_branch: 'master-42',
target_branch: 'master',
- state: :merged,
state_id: 3,
milestone_id: milestone.id,
author_id: user.id,
@@ -304,7 +301,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
it 'ignores Git command errors when creating a branch' do
expect(project.repository).to receive(:add_branch).and_raise(Gitlab::Git::CommandError)
- expect(Gitlab::Sentry).to receive(:track_acceptable_exception).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
mr = insert_git_data
diff --git a/spec/lib/gitlab/github_import/sequential_importer_spec.rb b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
index 8b1e8fbf3b7..256155dea03 100644
--- a/spec/lib/gitlab/github_import/sequential_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
@@ -9,8 +9,9 @@ describe Gitlab::GithubImport::SequentialImporter do
project = double(:project, id: 1, repository: repository)
importer = described_class.new(project, token: 'foo')
- expect_any_instance_of(Gitlab::GithubImport::Importer::RepositoryImporter)
- .to receive(:execute)
+ expect_next_instance_of(Gitlab::GithubImport::Importer::RepositoryImporter) do |instance|
+ expect(instance).to receive(:execute)
+ end
described_class::SEQUENTIAL_IMPORTERS.each do |klass|
instance = double(:instance)
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index 0f1745fcc02..246ef6c02f2 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitlabImport::Client do
@@ -19,18 +21,24 @@ describe Gitlab::GitlabImport::Client do
it 'uses membership and simple flags' do
stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true')
- expect_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+ expect_next_instance_of(OAuth2::Response) do |instance|
+ expect(instance).to receive(:parsed).and_return([])
+ end
expect(client.projects.to_a).to eq []
end
shared_examples 'pagination params' do
before do
- allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+ allow_next_instance_of(OAuth2::Response) do |instance|
+ allow(instance).to receive(:parsed).and_return([])
+ end
end
it 'allows page_limit param' do
- allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return(element_list)
+ allow_next_instance_of(OAuth2::Response) do |instance|
+ allow(instance).to receive(:parsed).and_return(element_list)
+ end
expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original
diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb
index 200edceca8c..2db1ddcfd0a 100644
--- a/spec/lib/gitlab/gitlab_import/importer_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitlabImport::Importer do
diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
index b814f5fc76c..c7ef978df37 100644
--- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GitlabImport::ProjectCreator do
diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb
index 37985c062b4..2e929a62ebc 100644
--- a/spec/lib/gitlab/google_code_import/client_spec.rb
+++ b/spec/lib/gitlab/google_code_import/client_spec.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::GoogleCodeImport::Client do
let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) }
+
subject { described_class.new(raw_data) }
describe "#valid?" do
diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb
index 031f57dbc65..7055df89c09 100644
--- a/spec/lib/gitlab/google_code_import/importer_spec.rb
+++ b/spec/lib/gitlab/google_code_import/importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe Gitlab::GoogleCodeImport::Importer do
@@ -37,7 +39,7 @@ describe Gitlab::GoogleCodeImport::Importer do
Performance Usability Maintainability Component-Panel Component-Taskbar Component-Battery
Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
).each do |label|
- label.sub!("-", ": ")
+ label = label.sub("-", ": ")
expect(project.labels.find_by(name: label)).not_to be_nil
end
end
diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
index b959e006292..2353c24f77b 100644
--- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GoogleCodeImport::ProjectCreator do
diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb
index 8401b683fd5..ea0a6e1b967 100644
--- a/spec/lib/gitlab/gpg/commit_spec.rb
+++ b/spec/lib/gitlab/gpg/commit_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Gpg::Commit do
diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
index da307754243..c1516a48b80 100644
--- a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
+++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index 52d6a86f7d0..8600ef223c6 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -177,6 +177,25 @@ describe Gitlab::Gpg do
end.not_to raise_error
end
+ it 'tracks an exception when cleaning up the tmp dir fails' do
+ expected_exception = described_class::CleanupError.new('cleanup failed')
+ expected_tmp_dir = nil
+
+ expect(described_class).to receive(:cleanup_tmp_dir).and_raise(expected_exception)
+ allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+
+ described_class.using_tmp_keychain do
+ expected_tmp_dir = described_class.current_home_dir
+ FileUtils.touch(File.join(expected_tmp_dir, 'dummy.file'))
+ end
+
+ expect(Gitlab::ErrorTracking).to have_received(:track_and_raise_for_dev_exception).with(
+ expected_exception,
+ issue_url: 'https://gitlab.com/gitlab-org/gitlab/issues/20918',
+ tmp_dir: expected_tmp_dir, contents: ['dummy.file']
+ )
+ end
+
shared_examples 'multiple deletion attempts of the tmp-dir' do |seconds|
let(:tmp_dir) do
tmp_dir = Dir.mktmpdir
@@ -211,15 +230,6 @@ describe Gitlab::Gpg do
expect(File.exist?(tmp_dir)).to be false
end
-
- it 'does not retry when the feature flag is disabled' do
- stub_feature_flags(gpg_cleanup_retries: false)
-
- expect(FileUtils).to receive(:remove_entry).with(tmp_dir, true).and_call_original
- expect(Retriable).not_to receive(:retriable)
-
- described_class.using_tmp_keychain {}
- end
end
it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::FG_CLEANUP_RUNTIME_S
diff --git a/spec/lib/gitlab/grafana_embed_usage_data_spec.rb b/spec/lib/gitlab/grafana_embed_usage_data_spec.rb
new file mode 100644
index 00000000000..162db46719b
--- /dev/null
+++ b/spec/lib/gitlab/grafana_embed_usage_data_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::GrafanaEmbedUsageData do
+ describe '#issue_count' do
+ subject { described_class.issue_count }
+
+ let(:project) { create(:project) }
+ let(:description_with_embed) { "Some comment\n\nhttps://grafana.example.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" }
+ let(:description_with_unintegrated_embed) { "Some comment\n\nhttps://grafana.exp.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" }
+ let(:description_with_non_grafana_inline_metric) { "Some comment\n\n#{Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(*['foo', 'bar', 12])}" }
+
+ shared_examples "zero count" do
+ it "does not count the issue" do
+ expect(subject).to eq(0)
+ end
+ end
+
+ context 'with project grafana integration enabled' do
+ before do
+ create(:grafana_integration, project: project, enabled: true)
+ end
+
+ context 'with valid and invalid embeds' do
+ before do
+ # Valid
+ create(:issue, project: project, description: description_with_embed)
+ create(:issue, project: project, description: description_with_embed)
+ # In-Valid
+ create(:issue, project: project, description: description_with_unintegrated_embed)
+ create(:issue, project: project, description: description_with_non_grafana_inline_metric)
+ create(:issue, project: project, description: nil)
+ create(:issue, project: project, description: '')
+ create(:issue, project: project)
+ end
+
+ it 'counts only the issues with embeds' do
+ expect(subject).to eq(2)
+ end
+ end
+ end
+
+ context 'with project grafana integration disabled' do
+ before do
+ create(:grafana_integration, project: project, enabled: false)
+ end
+
+ context 'with one issue having a grafana link in the description and one without' do
+ before do
+ create(:issue, project: project, description: description_with_embed)
+ create(:issue, project: project)
+ end
+
+ it_behaves_like('zero count')
+ end
+ end
+
+ context 'with an un-integrated project' do
+ context 'with one issue having a grafana link in the description and one without' do
+ before do
+ create(:issue, project: project, description: description_with_embed)
+ create(:issue, project: project)
+ end
+
+ it_behaves_like('zero count')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
index e21af023bb8..0cfda80b854 100644
--- a/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
+++ b/spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GrapeLogging::Loggers::ExceptionLogger do
diff --git a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
index f47b9dd3498..c0762e9892b 100644
--- a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
+++ b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::GrapeLogging::Loggers::QueueDurationLogger do
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
index aada9285b31..98659dbed57 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
@@ -25,6 +25,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
end
let(:current_user) { double(:current_user) }
+
subject(:service) { described_class.new(field) }
describe '#authorized_resolve' do
@@ -34,6 +35,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
let(:query_context) { OpenStruct.new(schema: schema) }
let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema, context: query_context), values: { current_user: current_user }, object: nil) }
+
subject(:resolved) { service.authorized_resolve.call(presented_type, {}, context) }
context 'scalar types' do
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
index 23762666ba8..67cb064b966 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Graphql::Authorize::AuthorizeResource do
@@ -25,6 +27,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
let(:user) { build(:user) }
let(:project) { build(:project) }
+
subject(:loading_resource) { fake_class.new(user, project) }
context 'when the user is allowed to perform the action' do
diff --git a/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb b/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb
index 1fda84f777e..20e87daa0d6 100644
--- a/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb
+++ b/spec/lib/gitlab/graphql/connections/filterable_array_connection_spec.rb
@@ -6,6 +6,7 @@ describe Gitlab::Graphql::Connections::FilterableArrayConnection do
let(:callback) { proc { |nodes| nodes } }
let(:all_nodes) { Gitlab::Graphql::FilterableArray.new(callback, 1, 2, 3, 4, 5) }
let(:arguments) { {} }
+
subject(:connection) do
described_class.new(all_nodes, arguments, max_page_size: 3)
end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
index 9dda2a41ec6..bd0fcbbdeb2 100644
--- a/spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Gitlab::Graphql::Connections::Keyset::Connection do
let(:nodes) { Project.all.order(id: :asc) }
let(:arguments) { {} }
+
subject(:connection) do
described_class.new(nodes, arguments, max_page_size: 3)
end
@@ -218,23 +219,11 @@ describe Gitlab::Graphql::Connections::Keyset::Connection do
end
end
- # TODO Enable this as part of below issue
- # https://gitlab.com/gitlab-org/gitlab/issues/32933
- # context 'when an invalid cursor is provided' do
- # let(:arguments) { { before: 'invalidcursor' } }
- #
- # it 'raises an error' do
- # expect { expect(subject.sliced_nodes) }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
- # end
- # end
-
- # TODO Remove this as part of below issue
- # https://gitlab.com/gitlab-org/gitlab/issues/32933
- context 'when an old style cursor is provided' do
- let(:arguments) { { before: Base64Bp.urlsafe_encode64(projects[1].id.to_s, padding: false) } }
+ context 'when an invalid cursor is provided' do
+ let(:arguments) { { before: Base64Bp.urlsafe_encode64('invalidcursor', padding: false) } }
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
+ it 'raises an error' do
+ expect { subject.sliced_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
end
end
end
diff --git a/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb b/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
deleted file mode 100644
index aaf28fed684..00000000000
--- a/spec/lib/gitlab/graphql/connections/keyset/legacy_keyset_connection_spec.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-# frozen_string_literal: true
-
-# TODO https://gitlab.com/gitlab-org/gitlab/issues/35104
-require 'spec_helper'
-
-describe Gitlab::Graphql::Connections::Keyset::LegacyKeysetConnection do
- describe 'old keyset_connection' do
- let(:described_class) { Gitlab::Graphql::Connections::Keyset::Connection }
- let(:nodes) { Project.all.order(id: :asc) }
- let(:arguments) { {} }
- subject(:connection) do
- described_class.new(nodes, arguments, max_page_size: 3)
- end
-
- before do
- stub_feature_flags(graphql_keyset_pagination: false)
- end
-
- def encoded_property(value)
- Base64Bp.urlsafe_encode64(value.to_s, padding: false)
- end
-
- describe '#cursor_from_nodes' do
- let(:project) { create(:project) }
-
- it 'returns an encoded ID' do
- expect(connection.cursor_from_node(project))
- .to eq(encoded_property(project.id))
- end
-
- context 'when an order was specified' do
- let(:nodes) { Project.order(:updated_at) }
-
- it 'returns the encoded value of the order' do
- expect(connection.cursor_from_node(project))
- .to eq(encoded_property(project.updated_at))
- end
- end
- end
-
- describe '#sliced_nodes' do
- let(:projects) { create_list(:project, 4) }
-
- context 'when before is passed' do
- let(:arguments) { { before: encoded_property(projects[1].id) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(id: :desc) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
- end
- end
- end
-
- context 'when after is passed' do
- let(:arguments) { { after: encoded_property(projects[1].id) } }
-
- it 'only returns the project before the selected one' do
- expect(subject.sliced_nodes).to contain_exactly(*projects[2..-1])
- end
-
- context 'when the sort order is descending' do
- let(:nodes) { Project.all.order(id: :desc) }
-
- it 'returns the correct nodes' do
- expect(subject.sliced_nodes).to contain_exactly(projects.first)
- end
- end
- end
-
- context 'when both before and after are passed' do
- let(:arguments) do
- {
- after: encoded_property(projects[1].id),
- before: encoded_property(projects[3].id)
- }
- end
-
- it 'returns the expected set' do
- expect(subject.sliced_nodes).to contain_exactly(projects[2])
- end
- end
- end
-
- describe '#paged_nodes' do
- let!(:projects) { create_list(:project, 5) }
-
- it 'returns the collection limited to max page size' do
- expect(subject.paged_nodes.size).to eq(3)
- end
-
- it 'is a loaded memoized array' do
- expect(subject.paged_nodes).to be_an(Array)
- expect(subject.paged_nodes.object_id).to eq(subject.paged_nodes.object_id)
- end
-
- context 'when `first` is passed' do
- let(:arguments) { { first: 2 } }
-
- it 'returns only the first elements' do
- expect(subject.paged_nodes).to contain_exactly(projects.first, projects.second)
- end
- end
-
- context 'when `last` is passed' do
- let(:arguments) { { last: 2 } }
-
- it 'returns only the last elements' do
- expect(subject.paged_nodes).to contain_exactly(projects[3], projects[4])
- end
- end
-
- context 'when both are passed' do
- let(:arguments) { { first: 2, last: 2 } }
-
- it 'raises an error' do
- expect { subject.paged_nodes }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb
index 22d8aa4274a..1e8de144b8d 100644
--- a/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb
+++ b/spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Graphql::Loaders::BatchLfsOidLoader do
diff --git a/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb
index a4bbd868558..79f9ecb39cf 100644
--- a/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb
+++ b/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Graphql::Loaders::BatchModelLoader do
diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb
index 09654e0439e..f92c7fb11a1 100644
--- a/spec/lib/gitlab/graphs/commits_spec.rb
+++ b/spec/lib/gitlab/graphs/commits_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Graphs::Commits do
diff --git a/spec/lib/gitlab/health_checks/db_check_spec.rb b/spec/lib/gitlab/health_checks/db_check_spec.rb
index 33c6c24449c..3c1c1e3818d 100644
--- a/spec/lib/gitlab/health_checks/db_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/db_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative './simple_check_shared'
diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
index 36e2fd04aeb..d4ce16ce6fc 100644
--- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HealthChecks::GitalyCheck do
diff --git a/spec/lib/gitlab/health_checks/master_check_spec.rb b/spec/lib/gitlab/health_checks/master_check_spec.rb
index 91441a7ddc3..cb20c1188af 100644
--- a/spec/lib/gitlab/health_checks/master_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/master_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative './simple_check_shared'
diff --git a/spec/lib/gitlab/health_checks/probes/collection_spec.rb b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
index 33efc640257..d8c411fa27b 100644
--- a/spec/lib/gitlab/health_checks/probes/collection_spec.rb
+++ b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::HealthChecks::Probes::Collection do
let(:readiness) { described_class.new(*checks) }
- describe '#call' do
+ describe '#execute' do
subject { readiness.execute }
context 'with all checks' do
diff --git a/spec/lib/gitlab/health_checks/puma_check_spec.rb b/spec/lib/gitlab/health_checks/puma_check_spec.rb
index 71b6386b174..dd052a4dd2c 100644
--- a/spec/lib/gitlab/health_checks/puma_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/puma_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HealthChecks::PumaCheck do
diff --git a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
index 3693f52b51b..aaf474d7eeb 100644
--- a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative '../simple_check_shared'
diff --git a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
index c69443d205d..f4b5e18da2a 100644
--- a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative '../simple_check_shared'
diff --git a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
index 03afc1cd761..ae7ee0d0859 100644
--- a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative '../simple_check_shared'
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
index b72e152bbe2..3e92b072254 100644
--- a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require_relative '../simple_check_shared'
diff --git a/spec/lib/gitlab/health_checks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb
index 03a7cf249cf..3d0f9b3cf7a 100644
--- a/spec/lib/gitlab/health_checks/simple_check_shared.rb
+++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
describe '#metrics' do
subject { described_class.metrics }
diff --git a/spec/lib/gitlab/health_checks/unicorn_check_spec.rb b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
index c02d0c37738..931b61cb168 100644
--- a/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HealthChecks::UnicornCheck do
diff --git a/spec/lib/gitlab/hook_data/base_builder_spec.rb b/spec/lib/gitlab/hook_data/base_builder_spec.rb
index e3c5ee3b905..4c3fd854c09 100644
--- a/spec/lib/gitlab/hook_data/base_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/base_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HookData::BaseBuilder do
@@ -13,6 +15,7 @@ describe Gitlab::HookData::BaseBuilder do
context 'with an upload prefix specified' do
let(:project_with_path) { double(full_path: 'baz/bar') }
let(:object_with_project) { double(project: project_with_path) }
+
subject { subclass.new(object_with_project) }
where do
diff --git a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
index 97a89b319ea..5135c84df19 100644
--- a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HookData::IssuableBuilder do
diff --git a/spec/lib/gitlab/hook_data/issue_builder_spec.rb b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
index ebd7feb0055..8008f3d72b2 100644
--- a/spec/lib/gitlab/hook_data/issue_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issue_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HookData::IssueBuilder do
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index 39f80f92fa6..506354e370c 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::HookData::MergeRequestBuilder do
diff --git a/spec/lib/gitlab/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
index f30528916dc..4bb5fb7c198 100644
--- a/spec/lib/gitlab/http_io_spec.rb
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -109,7 +109,9 @@ describe Gitlab::HttpIO do
end
it 'calls get_chunk only once' do
- expect_any_instance_of(Net::HTTP).to receive(:request).once.and_call_original
+ expect_next_instance_of(Net::HTTP) do |instance|
+ expect(instance).to receive(:request).once.and_call_original
+ end
http_io.each_line { |line| }
end
diff --git a/spec/lib/gitlab/i18n/metadata_entry_spec.rb b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
index a399517cc04..2d8bb538681 100644
--- a/spec/lib/gitlab/i18n/metadata_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::I18n::MetadataEntry do
diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb
index 3dbc23d2aaf..2ab363ee45c 100644
--- a/spec/lib/gitlab/i18n/po_linter_spec.rb
+++ b/spec/lib/gitlab/i18n/po_linter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require 'simple_po_parser'
diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb
index b301e6ea443..880da38052e 100644
--- a/spec/lib/gitlab/i18n/translation_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::I18n::TranslationEntry do
@@ -134,6 +136,7 @@ describe Gitlab::I18n::TranslationEntry do
describe '#contains_unescaped_chars' do
let(:data) { { msgid: '' } }
let(:entry) { described_class.new(data, 2) }
+
it 'is true when the msgid is an array' do
string = '「100%確定」'
diff --git a/spec/lib/gitlab/import/merge_request_helpers_spec.rb b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
index cc0f2baf905..42515888d4f 100644
--- a/spec/lib/gitlab/import/merge_request_helpers_spec.rb
+++ b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Import::MergeRequestHelpers, type: :helper do
target_project_id: project.id,
source_branch: 'master-42',
target_branch: 'master',
- state: :merged,
+ state_id: 3,
author_id: user.id,
assignee_id: user.id
}
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
index a3d2880182d..86ceb97b250 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AfterExportStrategies::BaseAfterExportStrategy do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index 21a227335cd..95c47d15f8f 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
diff --git a/spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb b/spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb
index bf727285a9f..9fe9e2eb73d 100644
--- a/spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AfterExportStrategyBuilder do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8f627fcc24d..2ea563c50b6 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -8,6 +8,7 @@ issues:
- milestone
- notes
- resource_label_events
+- sentry_issue
- label_links
- labels
- last_edited_by
@@ -15,6 +16,7 @@ issues:
- user_agent_detail
- moved_to
- duplicated_to
+- promoted_to_epic
- events
- merge_requests_closing_issues
- metrics
@@ -32,6 +34,7 @@ issues:
- zoom_meetings
- vulnerability_links
- related_vulnerabilities
+- user_mentions
events:
- author
- project
@@ -80,6 +83,7 @@ snippets:
- notes
- award_emoji
- user_agent_detail
+- user_mentions
releases:
- author
- project
@@ -138,6 +142,9 @@ merge_requests:
- blocking_merge_requests
- blocked_merge_requests
- description_versions
+- deployment_merge_requests
+- deployments
+- user_mentions
external_pull_requests:
- project
merge_request_diff:
@@ -287,6 +294,7 @@ project:
- microsoft_teams_service
- mattermost_service
- hangouts_chat_service
+- unify_circuit_service
- buildkite_service
- bamboo_service
- teamcity_service
@@ -364,6 +372,7 @@ project:
- root_of_fork_network
- fork_network_member
- fork_network
+- fork_network_projects
- custom_attributes
- lfs_file_locks
- project_badges
@@ -432,6 +441,9 @@ project:
- downstream_projects
- upstream_project_subscriptions
- downstream_project_subscriptions
+- service_desk_setting
+- import_failures
+- container_expiration_policy
award_emoji:
- awardable
- user
@@ -531,14 +543,18 @@ design: &design
- actions
- versions
- notes
+- user_mentions
designs: *design
actions:
- design
- version
versions: &version
+- author
- issue
- designs
- actions
zoom_meetings:
- issue
+sentry_issue:
+- issue
design_versions: *version
diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
index 1b28e26a7e8..44192c4639d 100644
--- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AttributeCleaner do
diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
index cc8ca1d87e3..58da25bbedb 100644
--- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
# Part of the test security suite for the Import/Export feature
@@ -10,21 +12,11 @@ require 'spec_helper'
describe 'Import/Export attribute configuration' do
include ConfigurationHelper
- let(:config_hash) { Gitlab::ImportExport::Config.new.to_h.deep_stringify_keys }
- let(:relation_names) do
- names = names_from_tree(config_hash.dig('tree', 'project'))
-
- # Remove duplicated or add missing models
- # - project is not part of the tree, so it has to be added manually.
- # - milestone, labels have both singular and plural versions in the tree, so remove the duplicates.
- names.flatten.uniq - %w(milestones labels) + ['project']
- end
-
let(:safe_attributes_file) { 'spec/lib/gitlab/import_export/safe_model_attributes.yml' }
let(:safe_model_attributes) { YAML.load_file(safe_attributes_file) }
it 'has no new columns' do
- relation_names.each do |relation_name|
+ relation_names_for(:project).each do |relation_name|
relation_class = relation_class_for_name(relation_name)
relation_attributes = relation_class.new.attributes.keys - relation_class.encrypted_attributes.keys.map(&:to_s)
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
index e44ff6bbcbd..662e1a5eaab 100644
--- a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AvatarRestorer do
@@ -12,8 +14,9 @@ describe Gitlab::ImportExport::AvatarRestorer do
context 'with avatar' do
before do
- allow_any_instance_of(described_class).to receive(:avatar_export_file)
- .and_return(uploaded_image_temp_path)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:avatar_export_file).and_return(uploaded_image_temp_path)
+ end
end
it 'restores a project avatar' do
@@ -31,8 +34,9 @@ describe Gitlab::ImportExport::AvatarRestorer do
Dir.mktmpdir do |tmpdir|
FileUtils.mkdir_p("#{tmpdir}/a/b")
- allow_any_instance_of(described_class).to receive(:avatar_export_path)
- .and_return("#{tmpdir}/a")
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:avatar_export_path).and_return("#{tmpdir}/a")
+ end
expect(described_class.new(project: project, shared: shared).restore).to be true
end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index 2bd1b9924c6..d2349e47c0a 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::AvatarSaver do
@@ -8,7 +10,9 @@ describe Gitlab::ImportExport::AvatarSaver do
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
- allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:export_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport::Shared) do |instance|
+ allow(instance).to receive(:export_path).and_return(export_path)
+ end
end
after do
diff --git a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
index b190a1007a0..6f90798f815 100644
--- a/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::FastHashSerializer do
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index fbc9bcd2df5..7c54c5f2da1 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::FileImporter do
@@ -16,9 +18,15 @@ describe Gitlab::ImportExport::FileImporter do
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
stub_uploads_object_storage(FileUploader)
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(storage_path)
- allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
- allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('test')
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(storage_path)
+ end
+ allow_next_instance_of(Gitlab::ImportExport::CommandLineUtil) do |instance|
+ allow(instance).to receive(:untar_zxf).and_return(true)
+ end
+ allow_next_instance_of(Gitlab::ImportExport::Shared) do |instance|
+ allow(instance).to receive(:relative_archive_path).and_return('test')
+ end
allow(SecureRandom).to receive(:hex).and_return('abcd')
setup_files
end
@@ -29,7 +37,7 @@ describe Gitlab::ImportExport::FileImporter do
context 'normal run' do
before do
- described_class.import(project: build(:project), archive_file: '', shared: shared)
+ described_class.import(importable: build(:project), archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
@@ -67,8 +75,10 @@ describe Gitlab::ImportExport::FileImporter do
context 'error' do
before do
- allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
- described_class.import(project: build(:project), archive_file: '', shared: shared)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:wait_for_archived_file).and_raise(StandardError)
+ end
+ described_class.import(importable: build(:project), archive_file: '', shared: shared)
end
it 'removes symlinks in root folder' do
diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb
index 5752fd8fa0d..09e4f62c686 100644
--- a/spec/lib/gitlab/import_export/fork_spec.rb
+++ b/spec/lib/gitlab/import_export/fork_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'forked project import' do
@@ -30,7 +32,9 @@ describe 'forked project import' do
end
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
saver.save
repo_saver.save
diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
index 1a5cb7806a3..0d0a2df4423 100644
--- a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::GroupProjectObjectBuilder do
diff --git a/spec/lib/gitlab/import_export/hash_util_spec.rb b/spec/lib/gitlab/import_export/hash_util_spec.rb
index 366582dece3..ddd874ddecf 100644
--- a/spec/lib/gitlab/import_export/hash_util_spec.rb
+++ b/spec/lib/gitlab/import_export/hash_util_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::HashUtil do
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
index a6b0dc758cd..2ece0dd4b56 100644
--- a/spec/lib/gitlab/import_export/import_export_spec.rb
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport do
diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb
index 898e4d07760..942af4084e5 100644
--- a/spec/lib/gitlab/import_export/importer_spec.rb
+++ b/spec/lib/gitlab/import_export/importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::Importer do
diff --git a/spec/lib/gitlab/import_export/lfs_restorer_spec.rb b/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
index 2b0bdb909ae..a932dc3ee4e 100644
--- a/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/lfs_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::LfsRestorer do
@@ -7,6 +9,7 @@ describe Gitlab::ImportExport::LfsRestorer do
let(:project) { create(:project) }
let(:shared) { project.import_export_shared }
let(:saver) { Gitlab::ImportExport::LfsSaver.new(project: project, shared: shared) }
+
subject(:restorer) { described_class.new(project: project, shared: shared) }
before do
diff --git a/spec/lib/gitlab/import_export/lfs_saver_spec.rb b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
index c3c88486e16..a8ff7867410 100644
--- a/spec/lib/gitlab/import_export/lfs_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/lfs_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::LfsSaver do
@@ -8,7 +10,9 @@ describe Gitlab::ImportExport::LfsSaver do
subject(:saver) { described_class.new(project: project, shared: shared) }
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
FileUtils.mkdir_p(shared.export_path)
end
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index a9e8431acba..01a7901062a 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper do
@@ -25,7 +27,7 @@ describe Gitlab::ImportExport::MembersMapper do
"email" => user2.email,
"username" => 'test'
},
- "user_id" => 19
+ "user_id" => 19
},
{
"id" => 3,
@@ -45,7 +47,7 @@ describe Gitlab::ImportExport::MembersMapper do
let(:members_mapper) do
described_class.new(
- exported_members: exported_members, user: user, project: project)
+ exported_members: exported_members, user: user, importable: project)
end
it 'includes the exported user ID in the map' do
@@ -81,7 +83,8 @@ describe Gitlab::ImportExport::MembersMapper do
end
it 'removes old user_id from member_hash to avoid conflict with user key' do
- expect(ProjectMember).to receive(:create)
+ expect(ProjectMember)
+ .to receive(:create)
.twice
.with(hash_excluding('user_id'))
.and_call_original
@@ -115,7 +118,7 @@ describe Gitlab::ImportExport::MembersMapper do
let(:project) { create(:project, :public, name: 'searchable_project', namespace: group) }
let(:members_mapper) do
described_class.new(
- exported_members: exported_members, user: user2, project: project)
+ exported_members: exported_members, user: user2, importable: project)
end
before do
@@ -138,7 +141,7 @@ describe Gitlab::ImportExport::MembersMapper do
let(:project) { create(:project, namespace: group) }
let(:members_mapper) do
described_class.new(
- exported_members: exported_members, user: user, project: project)
+ exported_members: exported_members, user: user, importable: project)
end
before do
@@ -161,7 +164,7 @@ describe Gitlab::ImportExport::MembersMapper do
it 'includes importer specific error message' do
expect(ProjectMember).to receive(:create!).and_raise(StandardError.new(exception_message))
- expect { members_mapper.map }.to raise_error(StandardError, "Error adding importer user to project members. #{exception_message}")
+ expect { members_mapper.map }.to raise_error(StandardError, "Error adding importer user to Project members. #{exception_message}")
end
end
end
diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
index 4b234411a44..c437efede4c 100644
--- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
+++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::MergeRequestParser do
@@ -33,9 +35,11 @@ describe Gitlab::ImportExport::MergeRequestParser do
end
it 'parses a MR that has no source branch' do
- allow_any_instance_of(described_class).to receive(:branch_exists?).and_call_original
- allow_any_instance_of(described_class).to receive(:branch_exists?).with(merge_request.source_branch).and_return(false)
- allow_any_instance_of(described_class).to receive(:fork_merge_request?).and_return(true)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:branch_exists?).and_call_original
+ allow(instance).to receive(:branch_exists?).with(merge_request.source_branch).and_return(false)
+ allow(instance).to receive(:fork_merge_request?).and_return(true)
+ end
allow(Gitlab::GitalyClient).to receive(:migrate).and_call_original
allow(Gitlab::GitalyClient).to receive(:migrate).with(:fetch_ref).and_return([nil, 0])
diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb
index 4426e68b474..cfbfe244988 100644
--- a/spec/lib/gitlab/import_export/model_configuration_spec.rb
+++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
# Part of the test security suite for the Import/Export feature
@@ -6,19 +8,10 @@ require 'spec_helper'
describe 'Import/Export model configuration' do
include ConfigurationHelper
- let(:config_hash) { Gitlab::ImportExport::Config.new.to_h.deep_stringify_keys }
- let(:model_names) do
- names = names_from_tree(config_hash.dig('tree', 'project'))
-
- # Remove duplicated or add missing models
- # - project is not part of the tree, so it has to be added manually.
- # - milestone, labels, merge_request have both singular and plural versions in the tree, so remove the duplicates.
- # - User, Author... Models we do not care about for checking models
- names.flatten.uniq - %w(milestones labels user author merge_request) + ['project']
- end
let(:all_models_yml) { 'spec/lib/gitlab/import_export/all_models.yml' }
let(:all_models_hash) { YAML.load_file(all_models_yml) }
let(:current_models) { setup_models }
+ let(:model_names) { relation_names_for(:project) }
it 'has no new models' do
model_names.each do |model_name|
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 64a648ca1f8..ec1b935ad63 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
include ImportExport::CommonUtil
@@ -29,9 +31,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
project_tree_restorer = described_class.new(user: @user, shared: @shared, project: @project)
- expect(Gitlab::ImportExport::RelationFactory).to receive(:create).with(hash_including(excluded_keys: ['whatever'])).and_call_original.at_least(:once)
- allow(project_tree_restorer).to receive(:excluded_keys_for_relation).and_return(['whatever'])
-
@restored_project_json = project_tree_restorer.restore
end
end
@@ -235,6 +234,22 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(meetings.first.url).to eq('https://zoom.us/j/123456789')
end
+ it 'restores sentry issues' do
+ sentry_issue = @project.issues.first.sentry_issue
+
+ expect(sentry_issue.sentry_issue_identifier).to eq(1234567891)
+ end
+
+ it 'restores container_expiration_policy' do
+ policy = Project.find_by_path('project').container_expiration_policy
+
+ aggregate_failures do
+ expect(policy).to be_an_instance_of(ContainerExpirationPolicy)
+ expect(policy).to be_persisted
+ expect(policy.cadence).to eq('3month')
+ end
+ end
+
context 'Merge requests' do
it 'always has the new project as a target' do
expect(MergeRequest.find_by_title('MR1').target_project).to eq(@project)
@@ -360,7 +375,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(restored_project_json).to eq(true)
end
- it_behaves_like 'restores project correctly',
+ it_behaves_like 'restores project successfully',
issues: 1,
labels: 2,
label_with_priorities: 'A project label',
@@ -373,7 +388,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
create(:ci_build, token: 'abcd')
end
- it_behaves_like 'restores project correctly',
+ it_behaves_like 'restores project successfully',
issues: 1,
labels: 2,
label_with_priorities: 'A project label',
@@ -450,7 +465,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(restored_project_json).to eq(true)
end
- it_behaves_like 'restores project correctly',
+ it_behaves_like 'restores project successfully',
issues: 2,
labels: 2,
label_with_priorities: 'A project label',
@@ -555,8 +570,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
context 'Minimal JSON' do
let(:project) { create(:project) }
+ let(:user) { create(:user) }
let(:tree_hash) { { 'visibility_level' => visibility } }
- let(:restorer) { described_class.new(user: nil, shared: shared, project: project) }
+ let(:restorer) { described_class.new(user: user, shared: shared, project: project) }
before do
expect(restorer).to receive(:read_tree_hash) { tree_hash }
@@ -631,4 +647,44 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
end
end
end
+
+ context 'JSON with invalid records' do
+ subject(:restored_project_json) { project_tree_restorer.restore }
+
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
+ let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
+ let(:correlation_id) { 'my-correlation-id' }
+
+ before do
+ setup_import_export_config('with_invalid_records')
+
+ # Import is running from the rake task, `correlation_id` is not assigned
+ expect(Labkit::Correlation::CorrelationId).to receive(:new_id).and_return(correlation_id)
+ subject
+ end
+
+ context 'when failures occur because a relation fails to be processed' do
+ it_behaves_like 'restores project successfully',
+ issues: 0,
+ labels: 0,
+ label_with_priorities: nil,
+ milestones: 1,
+ first_issue_labels: 0,
+ services: 0,
+ import_failures: 1
+
+ it 'records the failures in the database' do
+ import_failure = ImportFailure.last
+
+ expect(import_failure.project_id).to eq(project.id)
+ expect(import_failure.relation_key).to eq('milestones')
+ expect(import_failure.relation_index).to be_present
+ expect(import_failure.exception_class).to eq('ActiveRecord::RecordInvalid')
+ expect(import_failure.exception_message).to be_present
+ expect(import_failure.correlation_id_value).to eq('my-correlation-id')
+ expect(import_failure.created_at).to be_present
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 97d8b155826..29d0099d5c1 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver do
diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb
index 87f665bd995..e37ad281eb5 100644
--- a/spec/lib/gitlab/import_export/reader_spec.rb
+++ b/spec/lib/gitlab/import_export/reader_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::Reader do
@@ -7,19 +9,18 @@ describe Gitlab::ImportExport::Reader do
subject { described_class.new(shared: shared).project_tree }
it 'delegates to AttributesFinder#find_root' do
- expect_any_instance_of(Gitlab::ImportExport::AttributesFinder)
- .to receive(:find_root)
- .with(:project)
+ expect_next_instance_of(Gitlab::ImportExport::AttributesFinder) do |instance|
+ expect(instance).to receive(:find_root).with(:project)
+ end
subject
end
context 'when exception raised' do
before do
- expect_any_instance_of(Gitlab::ImportExport::AttributesFinder)
- .to receive(:find_root)
- .with(:project)
- .and_raise(StandardError)
+ expect_next_instance_of(Gitlab::ImportExport::AttributesFinder) do |instance|
+ expect(instance).to receive(:find_root).with(:project).and_raise(StandardError)
+ end
end
it { is_expected.to be false }
@@ -36,9 +37,9 @@ describe Gitlab::ImportExport::Reader do
subject { described_class.new(shared: shared).group_members_tree }
it 'delegates to AttributesFinder#find_root' do
- expect_any_instance_of(Gitlab::ImportExport::AttributesFinder)
- .to receive(:find_root)
- .with(:group_members)
+ expect_next_instance_of(Gitlab::ImportExport::AttributesFinder) do |instance|
+ expect(instance).to receive(:find_root).with(:group_members)
+ end
subject
end
diff --git a/spec/lib/gitlab/import_export/references_configuration_spec.rb b/spec/lib/gitlab/import_export/references_configuration_spec.rb
new file mode 100644
index 00000000000..91cf9f964c0
--- /dev/null
+++ b/spec/lib/gitlab/import_export/references_configuration_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# Part of the test security suite for the Import/Export feature
+# Checks whether there are new reference attributes ending with _id in models that are currently being exported as part of the
+# project Import/Export feature.
+# If there are new references (foreign keys), these will have to either be replaced with actual relation
+# or to be blacklisted by using the import_export.yml configuration file.
+# Likewise, new models added to import_export.yml, will need to be added with their correspondent relations
+# to this spec.
+describe 'Import/Export Project configuration' do
+ include ConfigurationHelper
+
+ where(:relation_path, :relation_name) do
+ relation_paths_for(:project).map do |relation_names|
+ next if relation_names.last == :author
+
+ [relation_names.join("."), relation_names.last]
+ end.compact
+ end
+
+ with_them do
+ context "where relation #{params[:relation_path]}" do
+ it 'does not have prohibited keys' do
+ relation_class = relation_class_for_name(relation_name)
+ relation_attributes = relation_class.new.attributes.keys - relation_class.encrypted_attributes.keys.map(&:to_s)
+ current_attributes = parsed_attributes(relation_name, relation_attributes)
+ prohibited_keys = current_attributes.select do |attribute|
+ prohibited_key?(attribute) || !relation_class.attribute_method?(attribute)
+ end
+ expect(prohibited_keys).to be_empty, failure_message(relation_class.to_s, prohibited_keys)
+ end
+ end
+ end
+
+ def failure_message(relation_class, prohibited_keys)
+ <<-MSG
+ It looks like #{relation_class}, which is exported using the project Import/Export, has references: #{prohibited_keys.join(',')}
+
+ Please replace it with actual relation in IMPORT_EXPORT_CONFIG if you consider this can be exported.
+ Please blacklist the attribute(s) in IMPORT_EXPORT_CONFIG by adding it to its correspondent
+ model in the +excluded_attributes+ section.
+
+ IMPORT_EXPORT_CONFIG: #{Gitlab::ImportExport.config_file}
+ MSG
+ end
+end
diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb
index a23e68a8f00..41d6e6f24fc 100644
--- a/spec/lib/gitlab/import_export/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::RelationFactory do
@@ -201,7 +203,7 @@ describe Gitlab::ImportExport::RelationFactory do
Gitlab::ImportExport::MembersMapper.new(
exported_members: [exported_member],
user: user,
- project: project)
+ importable: project)
end
it 'maps the right author to the imported note' do
diff --git a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb
new file mode 100644
index 00000000000..c761f9652ab
--- /dev/null
+++ b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+# This spec is a lightweight version of:
+# * project_tree_restorer_spec.rb
+#
+# In depth testing is being done in the above specs.
+# This spec tests that restore project works
+# but does not have 100% relation coverage.
+
+require 'spec_helper'
+
+describe Gitlab::ImportExport::RelationTreeRestorer do
+ include ImportExport::CommonUtil
+
+ let(:user) { create(:user) }
+ let(:shared) { Gitlab::ImportExport::Shared.new(importable) }
+ let(:members_mapper) { Gitlab::ImportExport::MembersMapper.new(exported_members: {}, user: user, importable: importable) }
+
+ let(:importable_hash) do
+ json = IO.read(path)
+ ActiveSupport::JSON.decode(json)
+ end
+
+ let(:relation_tree_restorer) do
+ described_class.new(
+ user: user,
+ shared: shared,
+ tree_hash: tree_hash,
+ importable: importable,
+ members_mapper: members_mapper,
+ relation_factory: relation_factory,
+ reader: reader
+ )
+ end
+
+ subject { relation_tree_restorer.restore }
+
+ context 'when restoring a project' do
+ let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' }
+ let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') }
+ let(:relation_factory) { Gitlab::ImportExport::RelationFactory }
+ let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) }
+ let(:tree_hash) { importable_hash }
+
+ it 'restores project tree' do
+ expect(subject).to eq(true)
+ end
+
+ describe 'imported project' do
+ let(:project) { Project.find_by_path('project') }
+
+ before do
+ subject
+ end
+
+ it 'has the project attributes and relations' do
+ expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.')
+ expect(project.labels.count).to eq(3)
+ expect(project.boards.count).to eq(1)
+ expect(project.project_feature).not_to be_nil
+ expect(project.custom_attributes.count).to eq(2)
+ expect(project.project_badges.count).to eq(2)
+ expect(project.snippets.count).to eq(1)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index e2ffb2adb9b..a61d966bdfa 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::RepoRestorer do
@@ -18,7 +20,9 @@ describe Gitlab::ImportExport::RepoRestorer do
end
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
bundler.save
end
diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb
index c3df371af43..fc1f782bfdd 100644
--- a/spec/lib/gitlab/import_export/repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::RepoSaver do
@@ -10,7 +12,9 @@ describe Gitlab::ImportExport::RepoSaver do
before do
project.add_maintainer(user)
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
end
after do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 04fe985cdb5..79442c35797 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -20,6 +20,7 @@ Issue:
- due_date
- moved_to_id
- duplicated_to_id
+- promoted_to_epic_id
- lock_version
- milestone_id
- weight
@@ -32,7 +33,6 @@ Issue:
Event:
- id
- target_type
-- target_id
- project_id
- group_id
- created_at
@@ -59,7 +59,6 @@ Note:
- attachment
- line_code
- commit_id
-- noteable_id
- system
- st_diff
- updated_by_id
@@ -72,11 +71,8 @@ Note:
- resolved_by_push
- discussion_id
- original_discussion_id
-- review_id
LabelLink:
- id
-- label_id
-- target_id
- target_type
- created_at
- updated_at
@@ -129,13 +125,11 @@ Release:
- released_at
Evidence:
- id
-- release_id
- summary
- created_at
- updated_at
Releases::Link:
- id
-- release_id
- url
- name
- created_at
@@ -143,7 +137,6 @@ Releases::Link:
ProjectMember:
- id
- access_level
-- source_id
- source_type
- user_id
- notification_level
@@ -442,6 +435,7 @@ Service:
- note_events
- pipeline_events
- job_events
+- comment_on_event_enabled
- category
- default
- wiki_page_events
@@ -598,7 +592,6 @@ AwardEmoji:
LabelPriority:
- id
- project_id
-- label_id
- priority
- created_at
- updated_at
@@ -606,7 +599,6 @@ Timelog:
- id
- time_spent
- merge_request_id
-- issue_id
- user_id
- spent_at
- created_at
@@ -621,7 +613,6 @@ ProjectAutoDevops:
- updated_at
IssueAssignee:
- user_id
-- issue_id
ProjectCustomAttribute:
- id
- created_at
@@ -650,6 +641,7 @@ PrometheusAlert:
- prometheus_metric_id
Badge:
- id
+- name
- link_url
- image_url
- project_id
@@ -676,7 +668,6 @@ ProtectedEnvironment::DeployAccessLevel:
ResourceLabelEvent:
- id
- action
-- issue_id
- merge_request_id
- label_id
- user_id
@@ -686,9 +677,11 @@ ErrorTracking::ProjectErrorTrackingSetting:
- project_id
- project_name
- organization_name
+SentryIssue:
+- id
+- sentry_issue_identifier
Suggestion:
- id
-- note_id
- relative_order
- applied
- commit_id
@@ -726,6 +719,7 @@ List:
- milestone_id
- user_id
- max_issue_count
+- max_issue_weight
ExternalPullRequest:
- id
- created_at
@@ -742,23 +736,31 @@ ExternalPullRequest:
DesignManagement::Design:
- id
- project_id
-- issue_id
- filename
DesignManagement::Action:
-- design_id
- event
-- version_id
DesignManagement::Version:
- id
- created_at
- sha
-- issue_id
- author_id
ZoomMeeting:
- id
-- issue_id
- project_id
- issue_status
- url
- created_at
- updated_at
+ServiceDeskSetting:
+- project_id
+- issue_template_key
+ContainerExpirationPolicy:
+- created_at
+- updated_at
+- next_run_at
+- project_id
+- name_regex
+- cadence
+- older_than
+- keep_n
+- enabled
diff --git a/spec/lib/gitlab/import_export/saver_spec.rb b/spec/lib/gitlab/import_export/saver_spec.rb
index aca63953677..a59cf7a1260 100644
--- a/spec/lib/gitlab/import_export/saver_spec.rb
+++ b/spec/lib/gitlab/import_export/saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require 'fileutils'
@@ -5,10 +7,13 @@ describe Gitlab::ImportExport::Saver do
let!(:project) { create(:project, :public, name: 'project') }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
let(:shared) { project.import_export_shared }
+
subject { described_class.new(exportable: project, shared: shared) }
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
FileUtils.mkdir_p(shared.export_path)
FileUtils.touch("#{shared.export_path}/tmp.bundle")
diff --git a/spec/lib/gitlab/import_export/shared_spec.rb b/spec/lib/gitlab/import_export/shared_spec.rb
index fc011f7e1be..8c16243576d 100644
--- a/spec/lib/gitlab/import_export/shared_spec.rb
+++ b/spec/lib/gitlab/import_export/shared_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require 'fileutils'
describe Gitlab::ImportExport::Shared do
let(:project) { build(:project) }
+
subject { project.import_export_shared }
context 'with a repository on disk' do
@@ -46,24 +49,9 @@ describe Gitlab::ImportExport::Shared do
it 'updates the import JID' do
import_state = create(:import_state, project: project, jid: 'jid-test')
- expect_next_instance_of(Gitlab::Import::Logger) do |logger|
- expect(logger).to receive(:error).with(hash_including(import_jid: import_state.jid))
- end
-
- subject.error(error)
- end
-
- it 'calls the error logger without a backtrace' do
- expect(subject).to receive(:log_error).with(message: error.message)
-
- subject.error(error)
- end
-
- it 'calls the error logger with the full message' do
- backtrace = caller
- allow(error).to receive(:backtrace).and_return(caller)
-
- expect(subject).to receive(:log_error).with(message: error.message, error_backtrace: Gitlab::Profiler.clean_backtrace(backtrace))
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(error, hash_including(import_jid: import_state.jid))
subject.error(error)
end
diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
index f13f639d6b7..e6d6ba840be 100644
--- a/spec/lib/gitlab/import_export/uploads_manager_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::UploadsManager do
@@ -10,7 +12,9 @@ describe Gitlab::ImportExport::UploadsManager do
subject(:manager) { described_class.new(project: project, shared: shared) }
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
FileUtils.mkdir_p(shared.export_path)
end
diff --git a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
index e2e8204b2fa..077ece87b31 100644
--- a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::UploadsRestorer do
@@ -6,7 +8,9 @@ describe Gitlab::ImportExport::UploadsRestorer do
let(:shared) { project.import_export_shared }
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/random'))
FileUtils.touch(File.join(shared.export_path, 'uploads/random', 'dummy.txt'))
end
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 24993460e51..8a36caef316 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::UploadsSaver do
@@ -7,7 +9,9 @@ describe Gitlab::ImportExport::UploadsSaver do
let(:shared) { project.import_export_shared }
before do
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
end
after do
diff --git a/spec/lib/gitlab/import_export/version_checker_spec.rb b/spec/lib/gitlab/import_export/version_checker_spec.rb
index 76f8253ec9b..befbd1b4c19 100644
--- a/spec/lib/gitlab/import_export/version_checker_spec.rb
+++ b/spec/lib/gitlab/import_export/version_checker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
include ImportExport::CommonUtil
@@ -6,10 +8,20 @@ describe Gitlab::ImportExport::VersionChecker do
describe 'bundle a project Git repo' do
let(:version) { Gitlab::ImportExport.version }
+ let(:version_file) { Tempfile.new('VERSION') }
before do
allow_any_instance_of(Gitlab::ImportExport::Shared).to receive(:relative_archive_path).and_return('')
- allow(File).to receive(:open).and_return(version)
+
+ version_file.write(version)
+ version_file.rewind
+
+ allow_any_instance_of(described_class).to receive(:version_file).and_return(version_file.path)
+ end
+
+ after do
+ version_file.close
+ version_file.unlink
end
it 'returns true if Import/Export have the same version' do
diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
index 249afbd23d1..59a59223d8d 100644
--- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::WikiRepoSaver do
@@ -11,7 +13,9 @@ describe Gitlab::ImportExport::WikiRepoSaver do
before do
project.add_maintainer(user)
- allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ allow_next_instance_of(Gitlab::ImportExport) do |instance|
+ allow(instance).to receive(:storage_path).and_return(export_path)
+ end
project_wiki.wiki
project_wiki.create_page("index", "test content")
end
diff --git a/spec/lib/gitlab/import_export/wiki_restorer_spec.rb b/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
index f99f198da33..33cd3e55393 100644
--- a/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ImportExport::WikiRestorer do
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
index 7f20ae98b06..8d0422bae9f 100644
--- a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -11,10 +11,17 @@ describe Gitlab::InsecureKeyFingerprint do
end
let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" }
+ let(:fingerprint_sha256) { "MQHWhS9nhzUezUdD42ytxubZoBKrZLbyBZzxCkmnxXc" }
describe "#fingerprint" do
it "generates the key's fingerprint" do
- expect(described_class.new(key.split[1]).fingerprint).to eq(fingerprint)
+ expect(described_class.new(key.split[1]).fingerprint_md5).to eq(fingerprint)
+ end
+ end
+
+ describe "#fingerprint" do
+ it "generates the key's fingerprint" do
+ expect(described_class.new(key.split[1]).fingerprint_sha256).to eq(fingerprint_sha256)
end
end
end
diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb
index 39cdd42088e..9d986abb8dd 100644
--- a/spec/lib/gitlab/json_cache_spec.rb
+++ b/spec/lib/gitlab/json_cache_spec.rb
@@ -7,6 +7,7 @@ describe Gitlab::JsonCache do
let(:namespace) { 'geo' }
let(:key) { 'foo' }
let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}" }
+
set(:broadcast_message) { create(:broadcast_message) }
subject(:cache) { described_class.new(namespace: namespace, backend: backend) }
@@ -378,6 +379,12 @@ describe Gitlab::JsonCache do
expect(result).to eq(broadcast_message)
end
+ it 'decodes enums correctly' do
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result.broadcast_type).to eq(broadcast_message.broadcast_type)
+ end
+
context 'when the cached value is an instance of ActiveRecord::Base' do
it 'returns a persisted record when id is set' do
backend.write(expanded_key, broadcast_message.to_json)
diff --git a/spec/lib/gitlab/kubernetes/config_map_spec.rb b/spec/lib/gitlab/kubernetes/config_map_spec.rb
index 911d6024804..0203772e069 100644
--- a/spec/lib/gitlab/kubernetes/config_map_spec.rb
+++ b/spec/lib/gitlab/kubernetes/config_map_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::ConfigMap do
@@ -16,6 +18,7 @@ describe Gitlab::Kubernetes::ConfigMap do
describe '#generate' do
let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: application.files) }
+
subject { config_map.generate }
it 'builds a Kubeclient Resource' do
diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
index 0de809833e6..5d9beec093a 100644
--- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::Api do
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 78a4eb44e38..c59078449b8 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::BaseCommand do
diff --git a/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
index 7e9853cf9ea..82e15864687 100644
--- a/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/delete_command_spec.rb
@@ -13,40 +13,57 @@ describe Gitlab::Kubernetes::Helm::DeleteCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm delete --purge app-name
EOS
end
end
- let(:tls_flags) do
- <<~EOS.squish
- --tls
- --tls-ca-cert /data/helm/app-name/config/ca.pem
- --tls-cert /data/helm/app-name/config/cert.pem
- --tls-key /data/helm/app-name/config/key.pem
- EOS
- end
-
- context 'when there is a ca.pem file' do
- let(:files) { { 'ca.pem': 'some file content' } }
+ context 'tillerless feature disabled' do
+ before do
+ stub_feature_flags(managed_apps_local_tiller: false)
+ end
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
- #{helm_delete_command}
+ for i in $(seq 1 30); do helm version && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ helm delete --purge app-name
EOS
end
+ end
- let(:helm_delete_command) do
+ context 'when there is a ca.pem file' do
+ let(:files) { { 'ca.pem': 'some file content' } }
+
+ let(:tls_flags) do
<<~EOS.squish
- helm delete --purge app-name
- #{tls_flags}
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
EOS
end
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ #{helm_delete_command}
+ EOS
+ end
+
+ let(:helm_delete_command) do
+ <<~EOS.squish
+ helm delete --purge app-name
+ #{tls_flags}
+ EOS
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index 4a3b9d4bf6a..f87ceb45766 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index e5a361bdab3..9c04e101e78 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InstallCommand do
@@ -21,22 +23,14 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
)
end
- let(:tls_flags) do
- <<~EOS.squish
- --tls
- --tls-ca-cert /data/helm/app-name/config/ca.pem
- --tls-cert /data/helm/app-name/config/cert.pem
- --tls-key /data/helm/app-name/config/key.pem
- EOS
- end
-
subject { install_command }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm repo add app-name https://repository.example.com
helm repo update
#{helm_install_comand}
@@ -48,7 +42,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm upgrade app-name chart-name
--install
--reset-values
- #{tls_flags}
--version 1.2.3
--set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
@@ -57,8 +50,19 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
end
end
- context 'when rbac is true' do
- let(:rbac) { true }
+ context 'tillerless feature disabled' do
+ before do
+ stub_feature_flags(managed_apps_local_tiller: false)
+ end
+
+ let(:tls_flags) do
+ <<~EOS.squish
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ EOS
+ end
it_behaves_like 'helm commands' do
let(:commands) do
@@ -67,6 +71,36 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
helm repo add app-name https://repository.example.com
helm repo update
+ #{helm_install_comand}
+ EOS
+ end
+
+ let(:helm_install_comand) do
+ <<~EOS.squish
+ helm upgrade app-name chart-name
+ --install
+ --reset-values
+ #{tls_flags}
+ --version 1.2.3
+ --set rbac.create\\=false,rbac.enabled\\=false
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'when rbac is true' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
+ helm repo add app-name https://repository.example.com
+ helm repo update
#{helm_install_command}
EOS
end
@@ -76,7 +110,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm upgrade app-name chart-name
--install
--reset-values
- #{tls_flags}
--version 1.2.3
--set rbac.create\\=true,rbac.enabled\\=true
--namespace gitlab-managed-apps
@@ -92,8 +125,9 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm repo add app-name https://repository.example.com
helm repo update
/bin/date
@@ -107,7 +141,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm upgrade app-name chart-name
--install
--reset-values
- #{tls_flags}
--version 1.2.3
--set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
@@ -123,8 +156,9 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm repo add app-name https://repository.example.com
helm repo update
#{helm_install_command}
@@ -138,7 +172,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm upgrade app-name chart-name
--install
--reset-values
- #{tls_flags}
--version 1.2.3
--set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
@@ -154,8 +187,9 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm repo add app-name https://repository.example.com
helm repo update
#{helm_install_command}
@@ -182,8 +216,9 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
- helm init --upgrade
- for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
helm repo add app-name https://repository.example.com
helm repo update
#{helm_install_command}
@@ -195,7 +230,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
helm upgrade app-name chart-name
--install
--reset-values
- #{tls_flags}
--set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
diff --git a/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb
new file mode 100644
index 00000000000..064efebdb96
--- /dev/null
+++ b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb
@@ -0,0 +1,218 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Kubernetes::Helm::PatchCommand do
+ let(:files) { { 'ca.pem': 'some file content' } }
+ let(:repository) { 'https://repository.example.com' }
+ let(:rbac) { false }
+ let(:version) { '1.2.3' }
+
+ subject(:patch_command) do
+ described_class.new(
+ name: 'app-name',
+ chart: 'chart-name',
+ rbac: rbac,
+ files: files,
+ version: version,
+ repository: repository
+ )
+ end
+
+ context 'when local tiller feature is disabled' do
+ before do
+ stub_feature_flags(managed_apps_local_tiller: false)
+ end
+
+ let(:tls_flags) do
+ <<~EOS.squish
+ --tls
+ --tls-ca-cert /data/helm/app-name/config/ca.pem
+ --tls-cert /data/helm/app-name/config/cert.pem
+ --tls-key /data/helm/app-name/config/key.pem
+ EOS
+ end
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --upgrade
+ for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_upgrade_comand}
+ EOS
+ end
+
+ let(:helm_upgrade_comand) do
+ <<~EOS.squish
+ helm upgrade app-name chart-name
+ --reuse-values
+ #{tls_flags}
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_upgrade_comand}
+ EOS
+ end
+
+ let(:helm_upgrade_comand) do
+ <<~EOS.squish
+ helm upgrade app-name chart-name
+ --reuse-values
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+
+ context 'when rbac is true' do
+ let(:rbac) { true }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_upgrade_command}
+ EOS
+ end
+
+ let(:helm_upgrade_command) do
+ <<~EOS.squish
+ helm upgrade app-name chart-name
+ --reuse-values
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ context 'when there is no ca.pem file' do
+ let(:files) { { 'file.txt': 'some content' } }
+
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ export HELM_HOST="localhost:44134"
+ tiller -listen ${HELM_HOST} -alsologtostderr &
+ helm init --client-only
+ helm repo add app-name https://repository.example.com
+ helm repo update
+ #{helm_upgrade_command}
+ EOS
+ end
+
+ let(:helm_upgrade_command) do
+ <<~EOS.squish
+ helm upgrade app-name chart-name
+ --reuse-values
+ --version 1.2.3
+ --namespace gitlab-managed-apps
+ -f /data/helm/app-name/config/values.yaml
+ EOS
+ end
+ end
+ end
+
+ describe '#pod_name' do
+ subject { patch_command.pod_name }
+
+ it { is_expected.to eq 'install-app-name' }
+ end
+
+ context 'when there is no version' do
+ let(:version) { nil }
+
+ it { expect { patch_command }.to raise_error(ArgumentError, 'version is required') }
+ end
+
+ describe '#rbac?' do
+ subject { patch_command.rbac? }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#pod_resource' do
+ subject { patch_command.pod_resource }
+
+ context 'rbac is enabled' do
+ let(:rbac) { true }
+
+ it 'generates a pod that uses the tiller serviceAccountName' do
+ expect(subject.spec.serviceAccountName).to eq('tiller')
+ end
+ end
+
+ context 'rbac is not enabled' do
+ let(:rbac) { false }
+
+ it 'generates a pod that uses the default serviceAccountName' do
+ expect(subject.spec.serviceAcccountName).to be_nil
+ end
+ end
+ end
+
+ describe '#config_map_resource' do
+ let(:metadata) do
+ {
+ name: "values-content-configuration-app-name",
+ namespace: 'gitlab-managed-apps',
+ labels: { name: "values-content-configuration-app-name" }
+ }
+ end
+
+ let(:resource) { ::Kubeclient::Resource.new(metadata: metadata, data: files) }
+
+ subject { patch_command.config_map_resource }
+
+ it 'returns a KubeClient resource with config map content for the application' do
+ is_expected.to eq(resource)
+ end
+ end
+
+ describe '#service_account_resource' do
+ subject { patch_command.service_account_resource }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+
+ describe '#cluster_role_binding_resource' do
+ subject { patch_command.cluster_role_binding_resource }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index e1b4bd0b664..24a734a2915 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Helm::Pod do
diff --git a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
index f24ab5579df..b65d7b9fdc6 100644
--- a/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb
@@ -45,4 +45,20 @@ describe Gitlab::Kubernetes::KubectlCmd do
end
end
end
+
+ describe '.api_resources' do
+ it 'constructs string properly' do
+ expected_command = 'kubectl api-resources -o name --api-group foo'
+
+ expect(described_class.api_resources("-o", "name", "--api-group", "foo")).to eq expected_command
+ end
+ end
+
+ describe '.delete_crds_from_group' do
+ it 'constructs string properly' do
+ expected_command = 'kubectl api-resources -o name --api-group foo | xargs kubectl delete --ignore-not-found crd'
+
+ expect(described_class.delete_crds_from_group("foo")).to eq expected_command
+ end
+ end
end
diff --git a/spec/lib/gitlab/kubernetes/namespace_spec.rb b/spec/lib/gitlab/kubernetes/namespace_spec.rb
index e91a755aa03..16634cc48e6 100644
--- a/spec/lib/gitlab/kubernetes/namespace_spec.rb
+++ b/spec/lib/gitlab/kubernetes/namespace_spec.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Kubernetes::Namespace do
let(:name) { 'a_namespace' }
let(:client) { double('kubernetes client') }
+
subject { described_class.new(name, client) }
it { expect(subject.name).to eq(name) }
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index 31bfd20449d..40c3e7d0b3c 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -27,6 +27,7 @@ describe Gitlab::Kubernetes do
context 'with a path prefix in the API URL' do
let(:api_url) { 'https://example.com/prefix/' }
+
it { expect(result.path).to eq('/prefix/api/v1/namespaces/default/pods/pod1/exec') }
end
diff --git a/spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb
index 48655851140..e96745f5fbe 100644
--- a/spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::BranchFormatter do
diff --git a/spec/lib/gitlab/legacy_github_import/client_spec.rb b/spec/lib/gitlab/legacy_github_import/client_spec.rb
index 80b767abce0..194518a1f36 100644
--- a/spec/lib/gitlab/legacy_github_import/client_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/client_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::Client do
diff --git a/spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb
index 413654e108c..0f03db312ce 100644
--- a/spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::CommentFormatter do
diff --git a/spec/lib/gitlab/legacy_github_import/importer_spec.rb b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
index 9163019514b..c6ee0a3c094 100644
--- a/spec/lib/gitlab/legacy_github_import/importer_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::Importer do
@@ -263,6 +265,7 @@ describe Gitlab::LegacyGithubImport::Importer do
context 'when importing a GitHub project' do
let(:api_root) { 'https://api.github.com' }
let(:repo_root) { 'https://github.com' }
+
subject { described_class.new(project) }
it_behaves_like 'Gitlab::LegacyGithubImport::Importer#execute'
@@ -285,6 +288,7 @@ describe Gitlab::LegacyGithubImport::Importer do
context 'when importing a Gitea project' do
let(:api_root) { 'https://try.gitea.io/api/v1' }
let(:repo_root) { 'https://try.gitea.io' }
+
subject { described_class.new(project) }
before do
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 3b5d8945344..f5bfc379e89 100644
--- a/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::IssuableFormatter do
diff --git a/spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb
index 1a4d5dbfb70..9a7a34afbe7 100644
--- a/spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::IssueFormatter do
diff --git a/spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb
index 0d1d04f1bf6..e56e2772f6a 100644
--- a/spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::LabelFormatter do
diff --git a/spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb
index 1db4bbb568c..f5d71888ac9 100644
--- a/spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::MilestoneFormatter do
@@ -87,6 +89,7 @@ describe Gitlab::LegacyGithubImport::MilestoneFormatter do
context 'when importing a Gitea project' do
let(:iid_attr) { :id }
+
before do
project.update(import_type: 'gitea')
end
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 8675d8691c8..b0687474c80 100644
--- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::ProjectCreator do
diff --git a/spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb
index 267a41e3f32..622210508b9 100644
--- a/spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::LegacyGithubImport::PullRequestFormatter do
diff --git a/spec/lib/gitlab/lets_encrypt/client_spec.rb b/spec/lib/gitlab/lets_encrypt/client_spec.rb
index cbb862cb0c9..e86de04b5cf 100644
--- a/spec/lib/gitlab/lets_encrypt/client_spec.rb
+++ b/spec/lib/gitlab/lets_encrypt/client_spec.rb
@@ -86,6 +86,7 @@ describe ::Gitlab::LetsEncrypt::Client do
describe '#load_order' do
let(:url) { 'https://example.com/order' }
+
subject { client.load_order(url) }
before do
@@ -102,6 +103,7 @@ describe ::Gitlab::LetsEncrypt::Client do
describe '#load_challenge' do
let(:url) { 'https://example.com/challenge' }
+
subject { client.load_challenge(url) }
before do
diff --git a/spec/lib/gitlab/mail_room/mail_room_spec.rb b/spec/lib/gitlab/mail_room/mail_room_spec.rb
new file mode 100644
index 00000000000..cb3e214d38b
--- /dev/null
+++ b/spec/lib/gitlab/mail_room/mail_room_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::MailRoom do
+ let(:default_port) { 143 }
+ let(:default_config) do
+ {
+ enabled: false,
+ port: default_port,
+ ssl: false,
+ start_tls: false,
+ mailbox: 'inbox',
+ idle_timeout: 60,
+ log_path: Rails.root.join('log', 'mail_room_json.log').to_s
+ }
+ end
+
+ before do
+ described_class.reset_config!
+ allow(File).to receive(:exist?).and_return true
+ end
+
+ describe '#config' do
+ context 'if the yml file cannot be found' do
+ before do
+ allow(File).to receive(:exist?).and_return false
+ end
+
+ it 'returns an empty hash' do
+ expect(described_class.config).to be_empty
+ end
+ end
+
+ before do
+ allow(described_class).to receive(:load_from_yaml).and_return(default_config)
+ end
+
+ it 'sets up config properly' do
+ expected_result = default_config
+
+ expect(described_class.config).to match expected_result
+ end
+
+ context 'when a config value is missing from the yml file' do
+ it 'overwrites missing values with the default' do
+ stub_config(port: nil)
+
+ expect(described_class.config[:port]).to eq default_port
+ end
+ end
+
+ describe 'setting up redis settings' do
+ let(:fake_redis_queues) { double(url: "localhost", sentinels: "yes, them", sentinels?: true) }
+
+ before do
+ allow(Gitlab::Redis::Queues).to receive(:new).and_return(fake_redis_queues)
+ end
+
+ target_proc = proc { described_class.config[:redis_url] }
+
+ it_behaves_like 'only truthy if both enabled and address are truthy', target_proc
+ end
+
+ describe 'setting up the log path' do
+ context 'if the log path is a relative path' do
+ it 'expands the log path to an absolute value' do
+ stub_config(log_path: 'tiny_log.log')
+
+ new_path = Pathname.new(described_class.config[:log_path])
+ expect(new_path.absolute?).to be_truthy
+ end
+ end
+
+ context 'if the log path is absolute path' do
+ it 'leaves the path as-is' do
+ new_path = '/dev/null'
+ stub_config(log_path: new_path)
+
+ expect(described_class.config[:log_path]).to eq new_path
+ end
+ end
+ end
+ end
+
+ describe '#enabled?' do
+ target_proc = proc { described_class.enabled? }
+
+ it_behaves_like 'only truthy if both enabled and address are truthy', target_proc
+ end
+
+ describe '#reset_config?' do
+ it 'resets config' do
+ described_class.instance_variable_set(:@config, { some_stuff: 'hooray' })
+
+ described_class.reset_config!
+
+ expect(described_class.instance_variable_get(:@config)).to be_nil
+ end
+ end
+
+ def stub_config(override_values)
+ modified_config = default_config.merge(override_values)
+ allow(described_class).to receive(:load_from_yaml).and_return(modified_config)
+ 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 c5fc74afea5..5b6c769d6eb 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -73,11 +73,11 @@ describe Gitlab::MarkdownCache::ActiveRecord::Extension do
let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
before do
- thing.state = 'closed'
+ thing.state_id = 2
thing.save
end
- it { expect(thing.state).to eq('closed') }
+ it { expect(thing.state_id).to eq(2) }
it { expect(thing.title).to eq(markdown) }
it { expect(thing.title_html).to eq(html) }
it { expect(thing.cached_markdown_version).to eq(cache_version) }
@@ -126,6 +126,7 @@ describe Gitlab::MarkdownCache::ActiveRecord::Extension do
describe '#cached_html_up_to_date?' do
let(:thing) { klass.create(title: updated_markdown, title_html: html, cached_markdown_version: nil) }
+
subject { thing.cached_html_up_to_date?(:title) }
it 'returns false if markdown has been changed but html has not' do
diff --git a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
index 0d4562f78f1..e0c8133994b 100644
--- a/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb
@@ -22,6 +22,12 @@ describe Gitlab::Metrics::Dashboard::ServiceSelector do
it { is_expected.to be Metrics::Dashboard::SystemDashboardService }
end
+
+ context 'when the path is for the pod dashboard' do
+ let(:arguments) { { dashboard_path: pod_dashboard_path } }
+
+ it { is_expected.to be Metrics::Dashboard::PodDashboardService }
+ end
end
context 'when the embedded flag is provided' do
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index 0e2f274f157..bf84a476df9 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -87,7 +87,9 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction)
.and_return(transaction)
- expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
+ expect_next_instance_of(Gitlab::Metrics::MethodCall) do |instance|
+ expect(instance).to receive(:measure)
+ end
@dummy.foo
end
@@ -165,7 +167,9 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction)
.and_return(transaction)
- expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
+ expect_next_instance_of(Gitlab::Metrics::MethodCall) do |instance|
+ expect(instance).to receive(:measure)
+ end
@dummy.new.bar
end
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index 335670278c4..1fc6fdcf622 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
describe Gitlab::Metrics::RequestsRackMiddleware do
let(:app) { double('app') }
+
subject { described_class.new(app) }
describe '#call' do
diff --git a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
index cdfd95e3885..a64aae73d43 100644
--- a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
@@ -52,6 +52,7 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
context 'unicorn listens on tcp sockets' do
let(:tcp_socket_address) { '0.0.0.0:8080' }
let(:tcp_sockets) { [tcp_socket_address] }
+
before do
allow(unicorn).to receive(:listener_names).and_return(tcp_sockets)
end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 16595102375..2b90035d148 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::Middleware::Go do
@@ -25,18 +27,18 @@ describe Gitlab::Middleware::Go do
describe 'when go-get=1' do
before do
env['QUERY_STRING'] = 'go-get=1'
- env['PATH_INFO'] = "/#{path}"
+ env['PATH_INFO'] = +"/#{path}"
end
shared_examples 'go-get=1' do |enabled_protocol:|
context 'with simple 2-segment project path' do
- let!(:project) { create(:project, :private) }
+ let!(:project) { create(:project, :private, :repository) }
context 'with subpackages' do
let(:path) { "#{project.full_path}/subpackage" }
it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end
end
@@ -44,19 +46,19 @@ describe Gitlab::Middleware::Go do
let(:path) { project.full_path }
it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end
end
end
context 'with a nested project path' do
let(:group) { create(:group, :nested) }
- let!(:project) { create(:project, :public, namespace: group) }
+ let!(:project) { create(:project, :public, :repository, namespace: group) }
shared_examples 'a nested project' do
context 'when the project is public' do
it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end
end
@@ -67,7 +69,7 @@ describe Gitlab::Middleware::Go do
shared_examples 'unauthorized' do
it 'returns the 2-segment group path' do
- expect_response_with_path(go, enabled_protocol, group.full_path)
+ expect_response_with_path(go, enabled_protocol, group.full_path, project.default_branch)
end
end
@@ -85,7 +87,7 @@ describe Gitlab::Middleware::Go do
shared_examples 'authenticated' do
context 'with access to the project' do
it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ expect_response_with_path(go, enabled_protocol, project.full_path, project.default_branch)
end
end
@@ -160,6 +162,36 @@ describe Gitlab::Middleware::Go do
go
end
end
+
+ context 'with a public project without a repository' do
+ let!(:project) { create(:project, :public) }
+ let(:path) { project.full_path }
+
+ it 'returns 404' do
+ response = go
+ expect(response[0]).to eq(404)
+ expect(response[1]['Content-Type']).to eq('text/html')
+ expected_body = %{<html><body>go get #{Gitlab.config.gitlab.url}/#{project.full_path}</body></html>}
+ expect(response[2].body).to eq([expected_body])
+ end
+ end
+
+ context 'with a non-standard head' do
+ let(:user) { create(:user) }
+ let!(:project) { create(:project, :public, :repository) }
+ let(:path) { project.full_path }
+ let(:default_branch) { 'default_branch' }
+
+ before do
+ project.add_maintainer(user)
+ project.repository.add_branch(user, default_branch, 'master')
+ project.change_head(default_branch)
+ end
+
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path, default_branch)
+ end
+ end
end
context 'with SSH disabled' do
@@ -199,16 +231,17 @@ describe Gitlab::Middleware::Go do
middleware.call(env)
end
- def expect_response_with_path(response, protocol, path)
+ def expect_response_with_path(response, protocol, path, branch)
repository_url = case protocol
when :ssh
"ssh://#{Gitlab.config.gitlab.user}@#{Gitlab.config.gitlab.host}/#{path}.git"
when :http, nil
"http://#{Gitlab.config.gitlab.host}/#{path}.git"
end
+ project_url = "http://#{Gitlab.config.gitlab.host}/#{path}"
expect(response[0]).to eq(200)
expect(response[1]['Content-Type']).to eq('text/html')
- expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /></head></html>}
+ expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /><meta name="go-source" content="#{Gitlab.config.gitlab.host}/#{path} #{project_url} #{project_url}/tree/#{branch}{/dir} #{project_url}/blob/#{branch}{/dir}/{file}#L{line}" /></head><body>go get #{Gitlab.config.gitlab.url}/#{path}</body></html>}
expect(response[2].body).to eq([expected_body])
end
end
diff --git a/spec/lib/gitlab/pages_spec.rb b/spec/lib/gitlab/pages_spec.rb
index affa2ebab2a..aecbc74385e 100644
--- a/spec/lib/gitlab/pages_spec.rb
+++ b/spec/lib/gitlab/pages_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
describe Gitlab::Pages do
- let(:pages_shared_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
+ let(:pages_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do
- allow(described_class).to receive(:secret).and_return(pages_shared_secret)
+ allow(described_class).to receive(:secret).and_return(pages_secret)
end
describe '.verify_api_request' do
diff --git a/spec/lib/gitlab/pagination/keyset/page_spec.rb b/spec/lib/gitlab/pagination/keyset/page_spec.rb
new file mode 100644
index 00000000000..5c03224c05a
--- /dev/null
+++ b/spec/lib/gitlab/pagination/keyset/page_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Pagination::Keyset::Page do
+ describe '#per_page' do
+ it 'limits to a maximum of 100 records per page' do
+ per_page = described_class.new(per_page: 101).per_page
+
+ expect(per_page).to eq(described_class::MAXIMUM_PAGE_SIZE)
+ end
+
+ it 'uses default value when given 0' do
+ per_page = described_class.new(per_page: 0).per_page
+
+ expect(per_page).to eq(described_class::DEFAULT_PAGE_SIZE)
+ end
+
+ it 'uses default value when given negative values' do
+ per_page = described_class.new(per_page: -1).per_page
+
+ expect(per_page).to eq(described_class::DEFAULT_PAGE_SIZE)
+ end
+
+ it 'uses the given value if it is within range' do
+ per_page = described_class.new(per_page: 10).per_page
+
+ expect(per_page).to eq(10)
+ end
+ end
+
+ describe '#next' do
+ let(:page) { described_class.new(order_by: order_by, lower_bounds: lower_bounds, per_page: per_page, end_reached: end_reached) }
+ subject { page.next(new_lower_bounds, new_end_reached) }
+
+ let(:order_by) { { id: :desc } }
+ let(:lower_bounds) { { id: 42 } }
+ let(:per_page) { 10 }
+ let(:end_reached) { false }
+
+ let(:new_lower_bounds) { { id: 21 } }
+ let(:new_end_reached) { true }
+
+ it 'copies over order_by' do
+ expect(subject.order_by).to eq(page.order_by)
+ end
+
+ it 'copies over per_page' do
+ expect(subject.per_page).to eq(page.per_page)
+ end
+
+ it 'dups the instance' do
+ expect(subject).not_to eq(page)
+ end
+
+ it 'sets lower_bounds only on new instance' do
+ expect(subject.lower_bounds).to eq(new_lower_bounds)
+ expect(page.lower_bounds).to eq(lower_bounds)
+ end
+
+ it 'sets end_reached only on new instance' do
+ expect(subject.end_reached?).to eq(new_end_reached)
+ expect(page.end_reached?).to eq(end_reached)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/pagination/keyset/pager_spec.rb b/spec/lib/gitlab/pagination/keyset/pager_spec.rb
new file mode 100644
index 00000000000..6d23fe2adcc
--- /dev/null
+++ b/spec/lib/gitlab/pagination/keyset/pager_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Pagination::Keyset::Pager do
+ let(:relation) { Project.all.order(id: :asc) }
+ let(:request) { double('request', page: page, apply_headers: nil) }
+ let(:page) { Gitlab::Pagination::Keyset::Page.new(order_by: { id: :asc }, per_page: 3) }
+ let(:next_page) { double('next page') }
+
+ before_all do
+ create_list(:project, 7)
+ end
+
+ describe '#paginate' do
+ subject { described_class.new(request).paginate(relation) }
+
+ it 'loads the result relation only once' do
+ expect do
+ subject
+ end.not_to exceed_query_limit(1)
+ end
+
+ it 'passes information about next page to request' do
+ lower_bounds = relation.limit(page.per_page).last.slice(:id)
+ expect(page).to receive(:next).with(lower_bounds, false).and_return(next_page)
+ expect(request).to receive(:apply_headers).with(next_page)
+
+ subject
+ end
+
+ context 'when retrieving the last page' do
+ let(:relation) { Project.where('id > ?', Project.maximum(:id) - page.per_page).order(id: :asc) }
+
+ it 'indicates this is the last page' do
+ expect(request).to receive(:apply_headers) do |next_page|
+ expect(next_page.end_reached?).to be_truthy
+ end
+
+ subject
+ end
+ end
+
+ context 'when retrieving an empty page' do
+ let(:relation) { Project.where('id > ?', Project.maximum(:id) + 1).order(id: :asc) }
+
+ it 'indicates this is the last page' do
+ expect(request).to receive(:apply_headers) do |next_page|
+ expect(next_page.end_reached?).to be_truthy
+ end
+
+ subject
+ end
+ end
+
+ it 'returns an array with the loaded records' do
+ expect(subject).to eq(relation.limit(page.per_page).to_a)
+ end
+
+ context 'validating the order clause' do
+ let(:page) { Gitlab::Pagination::Keyset::Page.new(order_by: { created_at: :asc }, per_page: 3) }
+
+ it 'raises an error if has a different order clause than the page' do
+ expect { subject }.to raise_error(ArgumentError, /order_by does not match/)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/pagination/keyset/request_context_spec.rb b/spec/lib/gitlab/pagination/keyset/request_context_spec.rb
new file mode 100644
index 00000000000..344ef90efa3
--- /dev/null
+++ b/spec/lib/gitlab/pagination/keyset/request_context_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Pagination::Keyset::RequestContext do
+ let(:request) { double('request', params: params) }
+
+ describe '#page' do
+ subject { described_class.new(request).page }
+
+ context 'with only order_by given' do
+ let(:params) { { order_by: :id } }
+
+ it 'extracts order_by/sorting information' do
+ page = subject
+
+ expect(page.order_by).to eq(id: :desc)
+ end
+ end
+
+ context 'with order_by and sort given' do
+ let(:params) { { order_by: :created_at, sort: :desc } }
+
+ it 'extracts order_by/sorting information and adds tie breaker' do
+ page = subject
+
+ expect(page.order_by).to eq(created_at: :desc, id: :desc)
+ end
+ end
+
+ context 'with no order_by information given' do
+ let(:params) { {} }
+
+ it 'defaults to tie breaker' do
+ page = subject
+
+ expect(page.order_by).to eq({ id: :desc })
+ end
+ end
+
+ context 'with per_page params given' do
+ let(:params) { { per_page: 10 } }
+
+ it 'extracts per_page information' do
+ page = subject
+
+ expect(page.per_page).to eq(params[:per_page])
+ end
+ end
+ end
+
+ describe '#apply_headers' do
+ let(:request) { double('request', url: "http://#{Gitlab.config.gitlab.host}/api/v4/projects?foo=bar") }
+ let(:params) { { foo: 'bar' } }
+ let(:request_context) { double('request context', params: params, request: request) }
+ let(:next_page) { double('next page', order_by: { id: :asc }, lower_bounds: { id: 42 }, end_reached?: false) }
+
+ subject { described_class.new(request_context).apply_headers(next_page) }
+
+ it 'sets Links header with same host/path as the original request' do
+ orig_uri = URI.parse(request_context.request.url)
+
+ expect(request_context).to receive(:header) do |name, header|
+ expect(name).to eq('Links')
+
+ first_link, _ = /<([^>]+)>; rel="next"/.match(header).captures
+
+ uri = URI.parse(first_link)
+
+ expect(uri.host).to eq(orig_uri.host)
+ expect(uri.path).to eq(orig_uri.path)
+ end
+
+ subject
+ end
+
+ it 'sets Links header with a link to the next page' do
+ orig_uri = URI.parse(request_context.request.url)
+
+ expect(request_context).to receive(:header) do |name, header|
+ expect(name).to eq('Links')
+
+ first_link, _ = /<([^>]+)>; rel="next"/.match(header).captures
+
+ query = CGI.parse(URI.parse(first_link).query)
+
+ expect(query.except('id_after')).to eq(CGI.parse(orig_uri.query).except('id_after'))
+ expect(query['id_after']).to eq(['42'])
+ end
+
+ subject
+ end
+
+ context 'with descending order' do
+ let(:next_page) { double('next page', order_by: { id: :desc }, lower_bounds: { id: 42 }, end_reached?: false) }
+
+ it 'sets Links header with a link to the next page' do
+ orig_uri = URI.parse(request_context.request.url)
+
+ expect(request_context).to receive(:header) do |name, header|
+ expect(name).to eq('Links')
+
+ first_link, _ = /<([^>]+)>; rel="next"/.match(header).captures
+
+ query = CGI.parse(URI.parse(first_link).query)
+
+ expect(query.except('id_before')).to eq(CGI.parse(orig_uri.query).except('id_before'))
+ expect(query['id_before']).to eq(['42'])
+ end
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/pagination/keyset_spec.rb b/spec/lib/gitlab/pagination/keyset_spec.rb
new file mode 100644
index 00000000000..5c2576d7b45
--- /dev/null
+++ b/spec/lib/gitlab/pagination/keyset_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Pagination::Keyset do
+ describe '.paginate' do
+ subject { described_class.paginate(request_context, relation) }
+
+ let(:request_context) { double }
+ let(:relation) { double }
+ let(:pager) { double }
+ let(:result) { double }
+
+ it 'uses Pager to paginate the relation' do
+ expect(Gitlab::Pagination::Keyset::Pager).to receive(:new).with(request_context).and_return(pager)
+ expect(pager).to receive(:paginate).with(relation).and_return(result)
+
+ expect(subject).to eq(result)
+ end
+ end
+
+ describe '.available?' do
+ subject { described_class }
+
+ let(:request_context) { double("request context", page: page)}
+ let(:page) { double("page", order_by: order_by) }
+
+ shared_examples_for 'keyset pagination is available' do
+ it 'returns true for Project' do
+ expect(subject.available?(request_context, Project.all)).to be_truthy
+ end
+
+ it 'return false for other types of relations' do
+ expect(subject.available?(request_context, User.all)).to be_falsey
+ end
+ end
+
+ context 'with order-by id asc' do
+ let(:order_by) { { id: :asc } }
+
+ it_behaves_like 'keyset pagination is available'
+ end
+
+ context 'with order-by id desc' do
+ let(:order_by) { { id: :desc } }
+
+ it_behaves_like 'keyset pagination is available'
+ end
+
+ context 'with other order-by columns' do
+ let(:order_by) { { created_at: :desc, id: :desc } }
+
+ it 'returns false for Project' do
+ expect(subject.available?(request_context, Project.all)).to be_falsey
+ end
+
+ it 'return false for other types of relations' do
+ expect(subject.available?(request_context, User.all)).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb
index b6629fad453..14a5d40d445 100644
--- a/spec/lib/gitlab/phabricator_import/cache/map_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/cache/map_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
describe Gitlab::PhabricatorImport::Cache::Map, :clean_gitlab_redis_cache do
set(:project) { create(:project) }
let(:redis) { Gitlab::Redis::Cache }
+
subject(:map) { described_class.new(project) }
describe '#get_gitlab_model' do
diff --git a/spec/lib/gitlab/phabricator_import/importer_spec.rb b/spec/lib/gitlab/phabricator_import/importer_spec.rb
index 99a6e4dad6b..2715b785379 100644
--- a/spec/lib/gitlab/phabricator_import/importer_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/importer_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::PhabricatorImport::Importer do
describe '#execute' do
let(:project) { create(:project, :import_scheduled) }
+
subject(:importer) { described_class.new(project) }
it 'sets a custom jid that will be kept up to date' do
diff --git a/spec/lib/gitlab/phabricator_import/project_creator_spec.rb b/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
index fd17284eea2..0b6a71290ed 100644
--- a/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/project_creator_spec.rb
@@ -8,6 +8,7 @@ describe Gitlab::PhabricatorImport::ProjectCreator do
phabricator_server_url: 'http://phab.example.com',
api_token: 'the-token' }
end
+
subject(:creator) { described_class.new(user, params) }
describe '#execute' do
diff --git a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
index 918ff28c8f5..14a00deeb16 100644
--- a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
describe Gitlab::PhabricatorImport::UserFinder, :clean_gitlab_redis_cache do
let(:project) { create(:project, namespace: create(:group)) }
+
subject(:finder) { described_class.new(project, ['first-phid', 'second-phid']) }
before do
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 99078f19361..6f4844d4543 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -53,6 +53,7 @@ describe Gitlab::ProjectSearchResults do
context "when #{entity_type} is disabled" do
let(:project) { disabled_project }
+
it "hides #{blob_kind} from members" do
project.add_reporter(user)
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 5559b1e4291..a2e3e2146f3 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::ProjectTemplate do
described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo'),
+ described_class.new('salesforcedx', 'SalesforceDX', _('A project boilerplate for Salesforce App development with Salesforce Developer tools.'), 'https://gitlab.com/gitlab-org/project-templates/salesforcedx'),
described_class.new('serverless_framework', 'Serverless Framework/JS', _('A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages'), 'https://gitlab.com/gitlab-org/project-templates/serverless-framework', 'illustrations/logos/serverless_framework.svg')
]
diff --git a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
index 0ad2de218fe..d82b7665f85 100644
--- a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
describe Gitlab::Prometheus::Queries::DeploymentQuery do
let(:environment) { create(:environment, slug: 'environment-slug') }
let(:deployment) { create(:deployment, environment: environment) }
-
let(:client) { double('prometheus_client') }
+
subject { described_class.new(client) }
around do |example|
diff --git a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
index 6361893c53c..fa2dccc7c92 100644
--- a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
@@ -7,8 +7,8 @@ describe Gitlab::Prometheus::Queries::KnativeInvocationQuery do
let(:project) { create(:project) }
let(:serverless_func) { Serverless::Function.new(project, 'test-name', 'test-ns') }
-
let(:client) { double('prometheus_client') }
+
subject { described_class.new(client) }
context 'verify queries' do
@@ -16,12 +16,12 @@ describe Gitlab::Prometheus::Queries::KnativeInvocationQuery do
create(:prometheus_metric,
:common,
identifier: :system_metrics_knative_function_invocation_count,
- query: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_app=~"%{function_name}.*"}[1m])*60))')
+ query: 'sum(ceil(rate(istio_requests_total{destination_service_namespace="%{kube_namespace}", destination_service=~"%{function_name}.*"}[1m])*60))')
end
it 'has the query, but no data' do
expect(client).to receive(:query_range).with(
- 'sum(ceil(rate(istio_requests_total{destination_service_namespace="test-ns", destination_app=~"test-name.*"}[1m])*60))',
+ 'sum(ceil(rate(istio_requests_total{destination_service_namespace="test-ns", destination_service=~"test-name.*"}[1m])*60))',
hash_including(:start, :stop)
)
diff --git a/spec/lib/gitlab/prometheus/query_variables_spec.rb b/spec/lib/gitlab/prometheus/query_variables_spec.rb
index 3f9b245a3fb..849265de513 100644
--- a/spec/lib/gitlab/prometheus/query_variables_spec.rb
+++ b/spec/lib/gitlab/prometheus/query_variables_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Prometheus::QueryVariables do
it do
is_expected.to include(environment_filter:
- %{container_name!="POD",environment="#{slug}"})
+ %Q[container_name!="POD",environment="#{slug}"])
end
context 'without deployment platform' do
diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb
index 86a1c14ed3f..4f9315e28e9 100644
--- a/spec/lib/gitlab/prometheus_client_spec.rb
+++ b/spec/lib/gitlab/prometheus_client_spec.rb
@@ -327,6 +327,7 @@ describe Gitlab::PrometheusClient do
context "without response code" do
let(:response_error) { Gitlab::HTTP::ResponseError }
+
it 'raises PrometheusClient::Error' do
expect { subject.proxy('query', { query: prometheus_query }) }.to(
raise_error(Gitlab::PrometheusClient::Error, 'Network connection error')
diff --git a/spec/lib/gitlab/puma_logging/json_formatter_spec.rb b/spec/lib/gitlab/puma_logging/json_formatter_spec.rb
new file mode 100644
index 00000000000..f7f5b99d5e5
--- /dev/null
+++ b/spec/lib/gitlab/puma_logging/json_formatter_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::PumaLogging::JSONFormatter do
+ it "generate json format with timestamp and pid" do
+ Timecop.freeze( Time.utc(2019, 12, 04, 9, 10, 11, 123456)) do
+ expect(subject.call('log message')).to eq "{\"timestamp\":\"2019-12-04T09:10:11.123Z\",\"pid\":#{Process.pid},\"message\":\"log message\"}"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/query_limiting/middleware_spec.rb b/spec/lib/gitlab/query_limiting/middleware_spec.rb
index fb1c30118c2..f996ea38bb9 100644
--- a/spec/lib/gitlab/query_limiting/middleware_spec.rb
+++ b/spec/lib/gitlab/query_limiting/middleware_spec.rb
@@ -7,8 +7,9 @@ describe Gitlab::QueryLimiting::Middleware do
it 'runs the application with query limiting in place' do
middleware = described_class.new(-> (env) { env })
- expect_any_instance_of(Gitlab::QueryLimiting::Transaction)
- .to receive(:act_upon_results)
+ expect_next_instance_of(Gitlab::QueryLimiting::Transaction) do |instance|
+ expect(instance).to receive(:act_upon_results)
+ end
expect(middleware.call({ number: 10 }))
.to eq({ number: 10 })
diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
index e4f25bc35a9..d0bb032f776 100644
--- a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
@@ -9,6 +9,7 @@ Hello! Let's do this!
/sub_name I like this stuff
EOF
end
+
subject do
described_class.new(:sub_name, action_block: proc { |text| "#{text} foo" })
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 1397add9f5a..c580b46cf8d 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -61,6 +61,12 @@ describe Gitlab::Regex do
it { is_expected.to match('my/image') }
it { is_expected.to match('my/awesome/image-1') }
it { is_expected.to match('my/awesome/image.test') }
+ it { is_expected.to match('my/awesome/image--test') }
+ # docker distribution allows for infinite `-`
+ # https://github.com/docker/distribution/blob/master/reference/regexp.go#L13
+ # but we have a range of 0,10 to add a reasonable limit.
+ it { is_expected.not_to match('my/image-----------test') }
+ it { is_expected.not_to match('my/image-.test') }
it { is_expected.not_to match('.my/image') }
it { is_expected.not_to match('my/image.') }
end
diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb
index cde12d4b310..87b8029de2e 100644
--- a/spec/lib/gitlab/request_context_spec.rb
+++ b/spec/lib/gitlab/request_context_spec.rb
@@ -43,7 +43,9 @@ describe Gitlab::RequestContext do
let(:ip) { '192.168.1.11' }
before do
- allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
+ allow_next_instance_of(Rack::Request) do |instance|
+ allow(instance).to receive(:ip).and_return(ip)
+ end
described_class.new(app).call(env)
end
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
index a8c7495376d..18fa96a2914 100644
--- a/spec/lib/gitlab/sanitizers/svg_spec.rb
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -14,7 +14,9 @@ describe Gitlab::Sanitizers::SVG do
let(:sanitized) { File.read(sanitized_svg_path) }
it 'delegates sanitization to scrubber' do
- expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
+ expect_next_instance_of(Gitlab::Sanitizers::SVG::Scrubber) do |instance|
+ expect(instance).to receive(:scrub).at_least(:once)
+ end
described_class.clean(data)
end
diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb
deleted file mode 100644
index 024ac733a07..00000000000
--- a/spec/lib/gitlab/sentry_spec.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Sentry do
- describe '.context' do
- it 'adds the expected tags' do
- expect(described_class).to receive(:enabled?).and_return(true)
- allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
-
- described_class.context(nil)
-
- expect(Raven.tags_context[:locale].to_s).to eq(I18n.locale.to_s)
- expect(Raven.tags_context[Labkit::Correlation::CorrelationId::LOG_KEY.to_sym].to_s)
- .to eq('cid')
- end
- end
-
- describe '.track_exception' do
- let(:exception) { RuntimeError.new('boom') }
-
- before do
- allow(described_class).to receive(:enabled?).and_return(true)
- end
-
- it 'raises the exception if it should' do
- expect(described_class).to receive(:should_raise_for_dev?).and_return(true)
- expect { described_class.track_exception(exception) }
- .to raise_error(RuntimeError)
- end
-
- context 'when exceptions should not be raised' do
- before do
- allow(described_class).to receive(:should_raise_for_dev?).and_return(false)
- allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
- end
-
- it 'logs the exception with all attributes passed' do
- expected_extras = {
- some_other_info: 'info',
- issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1'
- }
-
- expected_tags = {
- correlation_id: 'cid'
- }
-
- expect(Raven).to receive(:capture_exception)
- .with(exception,
- tags: a_hash_including(expected_tags),
- extra: a_hash_including(expected_extras))
-
- described_class.track_exception(
- exception,
- issue_url: 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1',
- extra: { some_other_info: 'info' }
- )
- end
-
- it 'sets the context' do
- expect(described_class).to receive(:context)
-
- described_class.track_exception(exception)
- end
- end
- end
-
- context '.track_acceptable_exception' do
- let(:exception) { RuntimeError.new('boom') }
- let(:issue_url) { 'http://gitlab.com/gitlab-org/gitlab-foss/issues/1' }
-
- before do
- allow(described_class).to receive(:enabled?).and_return(true)
- allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
- end
-
- it 'calls Raven.capture_exception' do
- expected_extras = {
- some_other_info: 'info',
- issue_url: issue_url
- }
-
- expected_tags = {
- correlation_id: 'cid'
- }
-
- expect(Raven).to receive(:capture_exception)
- .with(exception,
- tags: a_hash_including(expected_tags),
- extra: a_hash_including(expected_extras))
-
- described_class.track_acceptable_exception(
- exception,
- issue_url: issue_url,
- extra: { some_other_info: 'info' }
- )
- end
-
- context 'the exception implements :sentry_extra_data' do
- let(:extra_info) { { event: 'explosion', size: :massive } }
- let(:exception) { double(message: 'bang!', sentry_extra_data: extra_info) }
-
- it 'includes the extra data from the exception in the tracking information' do
- expect(Raven).to receive(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
-
- described_class.track_acceptable_exception(exception)
- end
- end
-
- context 'the exception implements :sentry_extra_data, which returns nil' do
- let(:exception) { double(message: 'bang!', sentry_extra_data: nil) }
-
- it 'just includes the other extra info' do
- extra_info = { issue_url: issue_url }
- expect(Raven).to receive(:capture_exception)
- .with(exception, a_hash_including(extra: a_hash_including(extra_info)))
-
- described_class.track_acceptable_exception(exception, extra_info)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index 2245c3ee8e2..728c44df4f3 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -167,8 +167,9 @@ describe Gitlab::Sherlock::Transaction do
allow(Gitlab::Sherlock).to receive(:enable_line_profiler?)
.and_return(true)
- allow_any_instance_of(Gitlab::Sherlock::LineProfiler)
- .to receive(:profile).and_return('cats are amazing', [])
+ allow_next_instance_of(Gitlab::Sherlock::LineProfiler) do |instance|
+ allow(instance).to receive(:profile).and_return('cats are amazing', [])
+ end
retval = transaction.profile_lines { 'cats are amazing' }
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
index 0ff694d409b..d5ed939485a 100644
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
@@ -28,7 +28,9 @@ describe Gitlab::SidekiqMiddleware::CorrelationInjector do
end
it 'injects into payload the correlation id' do
- expect_any_instance_of(described_class).to receive(:call).and_call_original
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:call).and_call_original
+ end
Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
TestWorker.perform_async(1234)
diff --git a/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
index 0d8cff3a295..36c6f377bde 100644
--- a/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/metrics_spec.rb
@@ -3,106 +3,201 @@
require 'fast_spec_helper'
describe Gitlab::SidekiqMiddleware::Metrics do
- let(:middleware) { described_class.new }
- let(:concurrency_metric) { double('concurrency metric') }
-
- let(:queue_duration_seconds) { double('queue duration seconds metric') }
- let(:completion_seconds_metric) { double('completion seconds metric') }
- let(:user_execution_seconds_metric) { double('user execution seconds metric') }
- let(:failed_total_metric) { double('failed total metric') }
- let(:retried_total_metric) { double('retried total metric') }
- let(:running_jobs_metric) { double('running jobs metric') }
-
- before do
- allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_queue_duration_seconds, anything, anything, anything).and_return(queue_duration_seconds)
- allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric)
- allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric)
- allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
- allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_retried_total, anything).and_return(retried_total_metric)
- allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
- allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
-
- allow(concurrency_metric).to receive(:set)
- end
+ context "with worker attribution" do
+ subject { described_class.new }
- describe '#initialize' do
- it 'sets general metrics' do
- expect(concurrency_metric).to receive(:set).with({}, Sidekiq.options[:concurrency].to_i)
+ let(:queue) { :test }
+ let(:worker_class) { worker.class }
+ let(:job) { {} }
+ let(:job_status) { :done }
+ let(:labels_with_job_status) { labels.merge(job_status: job_status.to_s) }
+ let(:default_labels) { { queue: queue.to_s, boundary: "", external_dependencies: "no", feature_category: "", latency_sensitive: "no" } }
+
+ shared_examples "a metrics middleware" do
+ context "with mocked prometheus" do
+ let(:concurrency_metric) { double('concurrency metric') }
+
+ let(:queue_duration_seconds) { double('queue duration seconds metric') }
+ let(:completion_seconds_metric) { double('completion seconds metric') }
+ let(:user_execution_seconds_metric) { double('user execution seconds metric') }
+ let(:failed_total_metric) { double('failed total metric') }
+ let(:retried_total_metric) { double('retried total metric') }
+ let(:running_jobs_metric) { double('running jobs metric') }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_queue_duration_seconds, anything, anything, anything).and_return(queue_duration_seconds)
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_completion_seconds, anything, anything, anything).and_return(completion_seconds_metric)
+ allow(Gitlab::Metrics).to receive(:histogram).with(:sidekiq_jobs_cpu_seconds, anything, anything, anything).and_return(user_execution_seconds_metric)
+ allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_failed_total, anything).and_return(failed_total_metric)
+ allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_jobs_retried_total, anything).and_return(retried_total_metric)
+ allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_running_jobs, anything, {}, :all).and_return(running_jobs_metric)
+ allow(Gitlab::Metrics).to receive(:gauge).with(:sidekiq_concurrency, anything, {}, :all).and_return(concurrency_metric)
+
+ allow(concurrency_metric).to receive(:set)
+ end
+
+ describe '#initialize' do
+ it 'sets concurrency metrics' do
+ expect(concurrency_metric).to receive(:set).with({}, Sidekiq.options[:concurrency].to_i)
+
+ subject
+ end
+ end
+
+ describe '#call' do
+ let(:thread_cputime_before) { 1 }
+ let(:thread_cputime_after) { 2 }
+ let(:thread_cputime_duration) { thread_cputime_after - thread_cputime_before }
+
+ let(:monotonic_time_before) { 11 }
+ let(:monotonic_time_after) { 20 }
+ let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
+
+ let(:queue_duration_for_job) { 0.01 }
+
+ before do
+ allow(subject).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+ allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
+
+ expect(running_jobs_metric).to receive(:increment).with(labels, 1)
+ expect(running_jobs_metric).to receive(:increment).with(labels, -1)
+
+ expect(queue_duration_seconds).to receive(:observe).with(labels, queue_duration_for_job) if queue_duration_for_job
+ expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, thread_cputime_duration)
+ expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, monotonic_time_duration)
+ end
+
+ it 'yields block' do
+ expect { |b| subject.call(worker, job, :test, &b) }.to yield_control.once
+ end
+
+ it 'sets queue specific metrics' do
+ subject.call(worker, job, :test) { nil }
+ end
+
+ context 'when job_duration is not available' do
+ let(:queue_duration_for_job) { nil }
+
+ it 'does not set the queue_duration_seconds histogram' do
+ expect(queue_duration_seconds).not_to receive(:observe)
+
+ subject.call(worker, job, :test) { nil }
+ end
+ end
+
+ context 'when error is raised' do
+ let(:job_status) { :fail }
+
+ it 'sets sidekiq_jobs_failed_total and reraises' do
+ expect(failed_total_metric).to receive(:increment).with(labels, 1)
+
+ expect { subject.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed")
+ end
+ end
+
+ context 'when job is retried' do
+ let(:job) { { 'retry_count' => 1 } }
+
+ it 'sets sidekiq_jobs_retried_total metric' do
+ expect(retried_total_metric).to receive(:increment)
+
+ subject.call(worker, job, :test) { nil }
+ end
+ end
+ end
+ end
- middleware
- end
- end
+ context "with prometheus integrated" do
+ describe '#call' do
+ it 'yields block' do
+ expect { |b| subject.call(worker, job, :test, &b) }.to yield_control.once
+ end
- it 'ignore user execution when measured 0' do
- allow(completion_seconds_metric).to receive(:observe)
+ context 'when error is raised' do
+ let(:job_status) { :fail }
- expect(user_execution_seconds_metric).not_to receive(:observe)
- end
+ it 'sets sidekiq_jobs_failed_total and reraises' do
+ expect { subject.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed")
+ end
+ end
+ end
+ end
+ end
- describe '#call' do
- let(:worker) { double(:worker) }
+ context "when workers are not attributed" do
+ class TestNonAttributedWorker
+ include Sidekiq::Worker
+ end
+ let(:worker) { TestNonAttributedWorker.new }
+ let(:labels) { default_labels }
- let(:job) { {} }
- let(:job_status) { :done }
- let(:labels) { { queue: :test } }
- let(:labels_with_job_status) { { queue: :test, job_status: job_status } }
+ it_behaves_like "a metrics middleware"
+ end
- let(:thread_cputime_before) { 1 }
- let(:thread_cputime_after) { 2 }
- let(:thread_cputime_duration) { thread_cputime_after - thread_cputime_before }
+ context "when workers are attributed" do
+ def create_attributed_worker_class(latency_sensitive, external_dependencies, resource_boundary, category)
+ Class.new do
+ include Sidekiq::Worker
+ include WorkerAttributes
+
+ latency_sensitive_worker! if latency_sensitive
+ worker_has_external_dependencies! if external_dependencies
+ worker_resource_boundary resource_boundary unless resource_boundary == :unknown
+ feature_category category unless category.nil?
+ end
+ end
- let(:monotonic_time_before) { 11 }
- let(:monotonic_time_after) { 20 }
- let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
+ let(:latency_sensitive) { false }
+ let(:external_dependencies) { false }
+ let(:resource_boundary) { :unknown }
+ let(:feature_category) { nil }
+ let(:worker_class) { create_attributed_worker_class(latency_sensitive, external_dependencies, resource_boundary, feature_category) }
+ let(:worker) { worker_class.new }
- let(:queue_duration_for_job) { 0.01 }
+ context "latency sensitive" do
+ let(:latency_sensitive) { true }
+ let(:labels) { default_labels.merge(latency_sensitive: "yes") }
- before do
- allow(middleware).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
- allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
- allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
+ it_behaves_like "a metrics middleware"
+ end
- expect(running_jobs_metric).to receive(:increment).with(labels, 1)
- expect(running_jobs_metric).to receive(:increment).with(labels, -1)
+ context "external dependencies" do
+ let(:external_dependencies) { true }
+ let(:labels) { default_labels.merge(external_dependencies: "yes") }
- expect(queue_duration_seconds).to receive(:observe).with(labels, queue_duration_for_job) if queue_duration_for_job
- expect(user_execution_seconds_metric).to receive(:observe).with(labels_with_job_status, thread_cputime_duration)
- expect(completion_seconds_metric).to receive(:observe).with(labels_with_job_status, monotonic_time_duration)
- end
+ it_behaves_like "a metrics middleware"
+ end
- it 'yields block' do
- expect { |b| middleware.call(worker, job, :test, &b) }.to yield_control.once
- end
+ context "cpu boundary" do
+ let(:resource_boundary) { :cpu }
+ let(:labels) { default_labels.merge(boundary: "cpu") }
- it 'sets queue specific metrics' do
- middleware.call(worker, job, :test) { nil }
- end
+ it_behaves_like "a metrics middleware"
+ end
- context 'when job_duration is not available' do
- let(:queue_duration_for_job) { nil }
+ context "memory boundary" do
+ let(:resource_boundary) { :memory }
+ let(:labels) { default_labels.merge(boundary: "memory") }
- it 'does not set the queue_duration_seconds histogram' do
- middleware.call(worker, job, :test) { nil }
+ it_behaves_like "a metrics middleware"
end
- end
- context 'when job is retried' do
- let(:job) { { 'retry_count' => 1 } }
+ context "feature category" do
+ let(:feature_category) { :authentication }
+ let(:labels) { default_labels.merge(feature_category: "authentication") }
- it 'sets sidekiq_jobs_retried_total metric' do
- expect(retried_total_metric).to receive(:increment)
-
- middleware.call(worker, job, :test) { nil }
+ it_behaves_like "a metrics middleware"
end
- end
-
- context 'when error is raised' do
- let(:job_status) { :fail }
- it 'sets sidekiq_jobs_failed_total and reraises' do
- expect(failed_total_metric).to receive(:increment).with(labels, 1)
+ context "combined" do
+ let(:latency_sensitive) { true }
+ let(:external_dependencies) { true }
+ let(:resource_boundary) { :cpu }
+ let(:feature_category) { :authentication }
+ let(:labels) { default_labels.merge(latency_sensitive: "yes", external_dependencies: "yes", boundary: "cpu", feature_category: "authentication") }
- expect { middleware.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed")
+ it_behaves_like "a metrics middleware"
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb
new file mode 100644
index 00000000000..aef472e0648
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'sidekiq/testing'
+
+describe Gitlab::SidekiqMiddleware do
+ class TestWorker
+ include Sidekiq::Worker
+
+ def perform(_arg)
+ end
+ end
+
+ around do |example|
+ Sidekiq::Testing.inline! { example.run }
+ end
+
+ let(:worker_class) { TestWorker }
+ let(:job_args) { [0.01] }
+
+ # The test sets up a new server middleware stack, ensuring that the
+ # appropriate middlewares, as passed into server_configurator,
+ # are invoked.
+ # Additionally the test ensure that each middleware is
+ # 1) not failing
+ # 2) yielding exactly once
+ describe '.server_configurator' do
+ around do |example|
+ original = Sidekiq::Testing.server_middleware.dup
+
+ example.run
+
+ Sidekiq::Testing.instance_variable_set :@server_chain, original
+ end
+
+ let(:middleware_expected_args) { [a_kind_of(worker_class), hash_including({ 'args' => job_args }), anything] }
+ let(:all_sidekiq_middlewares) do
+ [
+ Gitlab::SidekiqMiddleware::Monitor,
+ Gitlab::SidekiqMiddleware::BatchLoader,
+ Gitlab::SidekiqMiddleware::CorrelationLogger,
+ Gitlab::SidekiqMiddleware::InstrumentationLogger,
+ Gitlab::SidekiqStatus::ServerMiddleware,
+ Gitlab::SidekiqMiddleware::Metrics,
+ Gitlab::SidekiqMiddleware::ArgumentsLogger,
+ Gitlab::SidekiqMiddleware::MemoryKiller,
+ Gitlab::SidekiqMiddleware::RequestStoreMiddleware
+ ]
+ end
+ let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares }
+
+ before do
+ Sidekiq::Testing.server_middleware.clear
+ Sidekiq::Testing.server_middleware(&described_class.server_configurator(
+ metrics: metrics,
+ arguments_logger: arguments_logger,
+ memory_killer: memory_killer,
+ request_store: request_store
+ ))
+
+ enabled_sidekiq_middlewares.each do |middleware|
+ expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.and_call_original
+ end
+
+ disabled_sidekiq_middlewares.each do |middleware|
+ expect_any_instance_of(Gitlab::SidekiqMiddleware::ArgumentsLogger).not_to receive(:call)
+ end
+ end
+
+ context "all optional middlewares off" do
+ let(:metrics) { false }
+ let(:arguments_logger) { false }
+ let(:memory_killer) { false }
+ let(:request_store) { false }
+ let(:disabled_sidekiq_middlewares) do
+ [
+ Gitlab::SidekiqMiddleware::Metrics,
+ Gitlab::SidekiqMiddleware::ArgumentsLogger,
+ Gitlab::SidekiqMiddleware::MemoryKiller,
+ Gitlab::SidekiqMiddleware::RequestStoreMiddleware
+ ]
+ end
+
+ it "passes through server middlewares" do
+ worker_class.perform_async(*job_args)
+ end
+ end
+
+ context "all optional middlewares on" do
+ let(:metrics) { true }
+ let(:arguments_logger) { true }
+ let(:memory_killer) { true }
+ let(:request_store) { true }
+ let(:disabled_sidekiq_middlewares) { [] }
+
+ it "passes through server middlewares" do
+ worker_class.perform_async(*job_args)
+ end
+ end
+ end
+
+ # The test sets up a new client middleware stack. The test ensures
+ # that each middleware is:
+ # 1) not failing
+ # 2) yielding exactly once
+ describe '.client_configurator' do
+ let(:chain) { Sidekiq::Middleware::Chain.new }
+ let(:job) { { 'args' => job_args } }
+ let(:queue) { 'default' }
+ let(:redis_pool) { Sidekiq.redis_pool }
+ let(:middleware_expected_args) { [worker_class_arg, job, queue, redis_pool] }
+
+ before do
+ described_class.client_configurator.call(chain)
+ end
+
+ shared_examples "a client middleware chain" do
+ # Its possible that a middleware could accidentally omit a yield call
+ # this will prevent the full middleware chain from being executed.
+ # This test ensures that this does not happen
+ it "invokes the chain" do
+ expect_any_instance_of(Gitlab::SidekiqStatus::ClientMiddleware).to receive(:call).with(*middleware_expected_args).once.and_call_original
+ expect_any_instance_of(Gitlab::SidekiqMiddleware::CorrelationInjector).to receive(:call).with(*middleware_expected_args).once.and_call_original
+
+ expect { |b| chain.invoke(worker_class_arg, job, queue, redis_pool, &b) }.to yield_control.once
+ end
+ end
+
+ # Sidekiq documentation states that the worker class could be a string
+ # or a class reference. We should test for both
+ context "handles string worker_class values" do
+ let(:worker_class_arg) { worker_class.to_s }
+
+ it_behaves_like "a client middleware chain"
+ end
+
+ context "handles string worker_class values" do
+ let(:worker_class_arg) { worker_class }
+
+ it_behaves_like "a client middleware chain"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index 5a8c721a634..73b93589fac 100644
--- a/spec/lib/gitlab/slash_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -113,11 +113,13 @@ describe Gitlab::SlashCommands::Command do
context 'IssueMove is triggered' do
let(:params) { { text: 'issue move #78291 to gitlab/gitlab-ci' } }
+
it { is_expected.to eq(Gitlab::SlashCommands::IssueMove) }
end
context 'IssueComment is triggered' do
let(:params) { { text: "issue comment #503\ncomment body" } }
+
it { is_expected.to eq(Gitlab::SlashCommands::IssueComment) }
end
end
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index b766a9a1361..a142c8e4c92 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -42,6 +42,7 @@ describe Gitlab::SlashCommands::IssueSearch do
describe 'self.match' do
let(:query) { "my search keywords" }
+
it 'matches the query' do
match = described_class.match("issue search #{query}")
diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb
index b5ef417cb93..3741563a744 100644
--- a/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb
@@ -10,6 +10,7 @@ describe Gitlab::SlashCommands::Presenters::IssueComment do
describe '#present' do
let(:attachment) { subject[:attachments].first }
+
subject { described_class.new(note).present }
it { is_expected.to be_a(Hash) }
diff --git a/spec/lib/gitlab/slash_commands/run_spec.rb b/spec/lib/gitlab/slash_commands/run_spec.rb
index 900fae05719..32a23129e3c 100644
--- a/spec/lib/gitlab/slash_commands/run_spec.rb
+++ b/spec/lib/gitlab/slash_commands/run_spec.rb
@@ -56,13 +56,13 @@ describe Gitlab::SlashCommands::Run do
context 'when a pipeline could not be scheduled' do
it 'returns an error' do
- expect_any_instance_of(Gitlab::Chat::Command)
- .to receive(:try_create_pipeline)
- .and_return(nil)
+ expect_next_instance_of(Gitlab::Chat::Command) do |instance|
+ expect(instance).to receive(:try_create_pipeline).and_return(nil)
+ end
- expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
- .to receive(:failed_to_schedule)
- .with('foo')
+ expect_next_instance_of(Gitlab::SlashCommands::Presenters::Run) do |instance|
+ expect(instance).to receive(:failed_to_schedule).with('foo')
+ end
command.execute(command: 'foo', arguments: '')
end
@@ -77,17 +77,18 @@ describe Gitlab::SlashCommands::Run do
persisted?: true
)
- expect_any_instance_of(Gitlab::Chat::Command)
- .to receive(:try_create_pipeline)
- .and_return(pipeline)
+ expect_next_instance_of(Gitlab::Chat::Command) do |instance|
+ expect(instance).to receive(:try_create_pipeline).and_return(pipeline)
+ end
expect(Gitlab::Chat::Responder)
.to receive(:responder_for)
.with(build)
.and_return(nil)
- expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
- .to receive(:unsupported_chat_service)
+ expect_next_instance_of(Gitlab::SlashCommands::Presenters::Run) do |instance|
+ expect(instance).to receive(:unsupported_chat_service)
+ end
command.execute(command: 'foo', arguments: '')
end
@@ -103,18 +104,18 @@ describe Gitlab::SlashCommands::Run do
persisted?: true
)
- expect_any_instance_of(Gitlab::Chat::Command)
- .to receive(:try_create_pipeline)
- .and_return(pipeline)
+ expect_next_instance_of(Gitlab::Chat::Command) do |instance|
+ expect(instance).to receive(:try_create_pipeline).and_return(pipeline)
+ end
expect(Gitlab::Chat::Responder)
.to receive(:responder_for)
.with(build)
.and_return(responder)
- expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
- .to receive(:in_channel_response)
- .with(responder.scheduled_output)
+ expect_next_instance_of(Gitlab::SlashCommands::Presenters::Run) do |instance|
+ expect(instance).to receive(:in_channel_response).with(responder.scheduled_output)
+ end
command.execute(command: 'foo', arguments: '')
end
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index 31944d51b3c..38b93913f6d 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -207,5 +207,15 @@ describe Gitlab::SQL::Pattern do
expect(fuzzy_arel_match.to_sql).to match(/title.+I?LIKE '\%foo\%' AND .*title.*I?LIKE '\%baz\%' AND .*title.*I?LIKE '\%really bar\%'/)
end
end
+
+ context 'when passing an Arel column' do
+ let(:query) { 'foo' }
+
+ subject(:fuzzy_arel_match) { Project.fuzzy_arel_match(Route.arel_table[:path], query) }
+
+ it 'returns a condition with the table and column name' do
+ expect(fuzzy_arel_match.to_sql).to match(/"routes"."path".*ILIKE '\%foo\%'/)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index f8becb0c796..08e008c82d9 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -183,6 +183,34 @@ describe Gitlab::SSHPublicKey, lib: true do
end
end
+ describe '#fingerprint in SHA256 format' do
+ subject { public_key.fingerprint("SHA256").gsub("SHA256:", "") if public_key.fingerprint("SHA256") }
+
+ where(:factory, :fingerprint_sha256) do
+ [
+ [:rsa_key_2048, 'GdtgO0eHbwLB+mK47zblkoXujkqKRZjgMQrHH6Kks3E'],
+ [:rsa_key_4096, 'ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g'],
+ [:rsa_key_5120, 'PCCupLbFHScm4AbEufbGDvhBU27IM0MVAor715qKQK8'],
+ [:rsa_key_8192, 'CtHFQAS+9Hb8z4vrv4gVQPsHjNN0WIZhWODaB1mQLs4'],
+ [:dsa_key_2048, '+a3DQ7cU5GM+gaYOfmc0VWNnykHQSuth3VRcCpWuYNI'],
+ [:ecdsa_key_256, 'C+I5k3D+IGeM6k5iBR1ZsphqTKV+7uvL/XZ5hcrTr7g'],
+ [:ed25519_key_256, 'DCKAjzxWrdOTjaGKBBjtCW8qY5++GaiAJflrHPmp6W0']
+ ]
+ end
+
+ with_them do
+ let(:key) { attributes_for(factory)[:key] }
+
+ it { is_expected.to eq(fingerprint_sha256) }
+ end
+
+ context 'with an invalid SSH key' do
+ let(:key) { 'this is not a key' }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
describe '#key_text' do
let(:key) { 'this is not a key' }
diff --git a/spec/lib/gitlab/string_range_marker_spec.rb b/spec/lib/gitlab/string_range_marker_spec.rb
index 7ed43db3d10..ef9be7cd992 100644
--- a/spec/lib/gitlab/string_range_marker_spec.rb
+++ b/spec/lib/gitlab/string_range_marker_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::StringRangeMarker do
inline_diffs = [2..5]
described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:|
- "LEFT#{text}RIGHT"
+ "LEFT#{text}RIGHT".html_safe
end
end
diff --git a/spec/lib/gitlab/string_regex_marker_spec.rb b/spec/lib/gitlab/string_regex_marker_spec.rb
index 2b19edbe7f9..2ab1ccc447b 100644
--- a/spec/lib/gitlab/string_regex_marker_spec.rb
+++ b/spec/lib/gitlab/string_regex_marker_spec.rb
@@ -10,7 +10,7 @@ describe Gitlab::StringRegexMarker do
subject do
described_class.new(raw, rich).mark(/"[^"]+":\s*"(?<name>[^"]+)"/, group: :name) do |text, left:, right:|
- %{<a href="#">#{text}</a>}
+ %{<a href="#">#{text}</a>}.html_safe
end
end
@@ -26,7 +26,7 @@ describe Gitlab::StringRegexMarker do
subject do
described_class.new(raw, rich).mark(/<[a-z]>/) do |text, left:, right:|
- %{<strong>#{text}</strong>}
+ %{<strong>#{text}</strong>}.html_safe
end
end
diff --git a/spec/lib/gitlab/throttle_spec.rb b/spec/lib/gitlab/throttle_spec.rb
new file mode 100644
index 00000000000..674646a5f06
--- /dev/null
+++ b/spec/lib/gitlab/throttle_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Throttle do
+ describe '.protected_paths_enabled?' do
+ subject { described_class.protected_paths_enabled? }
+
+ context 'when omnibus protected paths throttle should be used' do
+ before do
+ expect(described_class).to receive(:should_use_omnibus_protected_paths?).and_return(true)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when omnibus protected paths throttle should not be used' do
+ before do
+ expect(described_class).to receive(:should_use_omnibus_protected_paths?).and_return(false)
+ end
+
+ it 'returns Application Settings throttle_protected_paths_enabled?' do
+ expect(Gitlab::CurrentSettings.current_application_settings).to receive(:throttle_protected_paths_enabled?)
+
+ subject
+ end
+ end
+ end
+
+ describe '.should_use_omnibus_protected_paths?' do
+ subject { described_class.should_use_omnibus_protected_paths? }
+
+ context 'when rack_attack.admin_area_protected_paths_enabled config is unspecified' do
+ context 'when the omnibus protected paths throttle has been recently used (it has data)' do
+ before do
+ expect(described_class).to receive(:omnibus_protected_paths_present?).and_return(true)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when the omnibus protected paths throttle has not been recently used' do
+ before do
+ expect(described_class).to receive(:omnibus_protected_paths_present?).and_return(false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'when rack_attack.admin_area_protected_paths_enabled config is false' do
+ before do
+ stub_config(rack_attack: {
+ admin_area_protected_paths_enabled: false
+ })
+ end
+
+ context 'when the omnibus protected paths throttle has been recently used (it has data)' do
+ before do
+ expect(described_class).to receive(:omnibus_protected_paths_present?).and_return(true)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when the omnibus protected paths throttle has not been recently used' do
+ before do
+ expect(described_class).to receive(:omnibus_protected_paths_present?).and_return(false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'when rack_attack.admin_area_protected_paths_enabled config is true' do
+ before do
+ stub_config(rack_attack: {
+ admin_area_protected_paths_enabled: true
+ })
+
+ expect(described_class).not_to receive(:omnibus_protected_paths_present?)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index dc877f20cae..efb07d9dc95 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -97,7 +97,7 @@ describe Gitlab::Tracking do
'_property_',
'_value_',
nil,
- timestamp.to_i
+ (timestamp.to_f * 1000).to_i
)
track_event
@@ -130,7 +130,7 @@ describe Gitlab::Tracking do
expect(tracker).to receive(:track_self_describing_event).with(
'_event_json_',
nil,
- timestamp.to_i
+ (timestamp.to_f * 1000).to_i
)
track_event
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 08d3c638f9e..0aab02b6c4c 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -59,6 +59,26 @@ describe Gitlab::UrlBuilder do
end
end
+ context 'when passing a ProjectSnippet' do
+ it 'returns a proper URL' do
+ project_snippet = create(:project_snippet)
+
+ url = described_class.build(project_snippet)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.full_path}/snippets/#{project_snippet.id}"
+ end
+ end
+
+ context 'when passing a PersonalSnippet' do
+ it 'returns a proper URL' do
+ personal_snippet = create(:personal_snippet)
+
+ url = described_class.build(personal_snippet)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/snippets/#{personal_snippet.id}"
+ end
+ end
+
context 'when passing a Note' do
context 'on a Commit' do
it 'returns a proper URL' do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 484684eeb65..6ab23b00d5c 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -27,6 +27,7 @@ describe Gitlab::UsageData do
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed)
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed)
+ create(:sentry_issue, issue: projects[0].issues[0])
# Enabled clusters
gcp_cluster = create(:cluster_provider_gcp, :created).cluster
@@ -53,6 +54,8 @@ describe Gitlab::UsageData do
create(:grafana_integration, project: projects[1], enabled: true)
create(:grafana_integration, project: projects[2], enabled: false)
+ allow(Gitlab::GrafanaEmbedUsageData).to receive(:issue_count).and_return(2)
+
ProjectFeature.first.update_attribute('repository_access_level', 0)
end
@@ -150,8 +153,10 @@ describe Gitlab::UsageData do
grafana_integrated_projects
groups
issues
+ issues_created_from_gitlab_error_tracking_ui
issues_with_associated_zoom_link
issues_using_zoom_quick_actions
+ issues_with_embedded_grafana_charts_approx
keys
label_lists
labels
@@ -209,8 +214,10 @@ describe Gitlab::UsageData do
expect(count_data[:projects_mattermost_active]).to eq(1)
expect(count_data[:projects_with_repositories_enabled]).to eq(3)
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
+ expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1)
expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
+ expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2)
expect(count_data[:clusters_enabled]).to eq(4)
expect(count_data[:project_clusters_enabled]).to eq(3)
@@ -293,6 +300,24 @@ describe Gitlab::UsageData do
end
end
+ describe '#ingress_modsecurity_usage' do
+ subject { described_class.ingress_modsecurity_usage }
+
+ it 'gathers variable data' do
+ allow_any_instance_of(
+ ::Clusters::Applications::IngressModsecurityUsageService
+ ).to receive(:execute).and_return(
+ {
+ ingress_modsecurity_blocking: 1,
+ ingress_modsecurity_disabled: 2
+ }
+ )
+
+ expect(subject[:ingress_modsecurity_blocking]).to eq(1)
+ expect(subject[:ingress_modsecurity_disabled]).to eq(2)
+ end
+ end
+
describe '#license_usage_data' do
subject { described_class.license_usage_data }
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index 75dc7d8e6d1..16a05af2216 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -95,4 +95,28 @@ describe Gitlab::VisibilityLevel do
expect(described_class.valid_level?(described_class::PUBLIC)).to be_truthy
end
end
+
+ describe '#visibility_level_decreased?' do
+ let(:project) { create(:project, :internal) }
+
+ context 'when visibility level decreases' do
+ before do
+ project.update!(visibility_level: described_class::PRIVATE)
+ end
+
+ it 'returns true' do
+ expect(project.visibility_level_decreased?).to be(true)
+ end
+ end
+
+ context 'when visibility level does not decrease' do
+ before do
+ project.update!(visibility_level: described_class::PUBLIC)
+ end
+
+ it 'returns false' do
+ expect(project.visibility_level_decreased?).to be(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 88bc5034da5..89381057f6b 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -116,6 +116,7 @@ describe Gitlab::Workhorse do
describe '.send_git_patch' do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
+
subject { described_class.send_git_patch(repository, diff_refs) }
it 'sets the header correctly' do
@@ -178,6 +179,7 @@ describe Gitlab::Workhorse do
describe '.send_git_diff' do
let(:diff_refs) { double(base_sha: "base", head_sha: "head") }
+
subject { described_class.send_git_diff(repository, diff_refs) }
it 'sets the header correctly' do
diff --git a/spec/lib/google_api/auth_spec.rb b/spec/lib/google_api/auth_spec.rb
index a25004ac385..719e98c5fdf 100644
--- a/spec/lib/google_api/auth_spec.rb
+++ b/spec/lib/google_api/auth_spec.rb
@@ -30,8 +30,9 @@ describe GoogleApi::Auth do
end
before do
- allow_any_instance_of(OAuth2::Strategy::AuthCode)
- .to receive(:get_token).and_return(token)
+ allow_next_instance_of(OAuth2::Strategy::AuthCode) do |instance|
+ allow(instance).to receive(:get_token).and_return(token)
+ end
end
it 'returns token and expires_at' do
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index 473ad639ead..bd063648ca1 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -92,7 +92,12 @@ describe GoogleApi::CloudPlatform::Client do
name: cluster_name,
initial_node_count: cluster_size,
node_config: {
- machine_type: machine_type
+ machine_type: machine_type,
+ oauth_scopes: [
+ "https://www.googleapis.com/auth/devstorage.read_only",
+ "https://www.googleapis.com/auth/logging.write",
+ "https://www.googleapis.com/auth/monitoring"
+ ]
},
master_auth: {
username: 'admin',
diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb
index a127c787e28..62fddbb97c7 100644
--- a/spec/lib/json_web_token/rsa_token_spec.rb
+++ b/spec/lib/json_web_token/rsa_token_spec.rb
@@ -41,6 +41,7 @@ describe JSONWebToken::RSAToken do
context 'for invalid key to raise an exception' do
let(:new_key) { OpenSSL::PKey::RSA.generate(512) }
+
subject { JWT.decode(rsa_encoded, new_key, true, { algorithm: 'RS256' }) }
it { expect {subject}.to raise_error(JWT::DecodeError) }
diff --git a/spec/lib/marginalia_spec.rb b/spec/lib/marginalia_spec.rb
new file mode 100644
index 00000000000..5dc54af99ce
--- /dev/null
+++ b/spec/lib/marginalia_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Marginalia spec' do
+ class MarginaliaTestController < ActionController::Base
+ def first_user
+ User.first
+ render body: nil
+ end
+ end
+
+ class MarginaliaTestJob
+ include Sidekiq::Worker
+
+ def perform
+ User.first
+ end
+ end
+
+ class MarginaliaTestMailer < BaseMailer
+ def first_user
+ User.first
+ end
+ end
+
+ def add_sidekiq_middleware
+ # Reference: https://github.com/mperham/sidekiq/wiki/Testing#testing-server-middlewaresidekiq
+ # Sidekiq test harness fakes worker without its server middlewares, so include instrumentation to 'Sidekiq::Testing' server middleware.
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.add Marginalia::SidekiqInstrumentation::Middleware
+ end
+ end
+
+ def remove_sidekiq_middleware
+ Sidekiq::Testing.server_middleware do |chain|
+ chain.remove Marginalia::SidekiqInstrumentation::Middleware
+ end
+ end
+
+ def stub_feature(value)
+ allow(Gitlab::Marginalia).to receive(:cached_feature_enabled?).and_return(value)
+ end
+
+ def make_request(correlation_id)
+ request_env = Rack::MockRequest.env_for('/')
+
+ ::Labkit::Correlation::CorrelationId.use_id(correlation_id) do
+ MarginaliaTestController.action(:first_user).call(request_env)
+ end
+ end
+
+ describe 'For rails web requests' do
+ let(:correlation_id) { SecureRandom.uuid }
+ let(:recorded) { ActiveRecord::QueryRecorder.new { make_request(correlation_id) } }
+
+ let(:component_map) do
+ {
+ "application" => "test",
+ "controller" => "marginalia_test",
+ "action" => "first_user",
+ "line" => "/spec/support/helpers/query_recorder.rb",
+ "correlation_id" => correlation_id
+ }
+ end
+
+ context 'when the feature is enabled' do
+ before do
+ stub_feature(true)
+ end
+
+ it 'generates a query that includes the component and value' do
+ component_map.each do |component, value|
+ expect(recorded.log.last).to include("#{component}:#{value}")
+ end
+ end
+ end
+
+ context 'when the feature is disabled' do
+ before do
+ stub_feature(false)
+ end
+
+ it 'excludes annotations in generated queries' do
+ expect(recorded.log.last).not_to include("/*")
+ expect(recorded.log.last).not_to include("*/")
+ end
+ end
+ end
+
+ describe 'for Sidekiq worker jobs' do
+ before(:all) do
+ add_sidekiq_middleware
+
+ # Because of faking, 'Sidekiq.server?' does not work so implicitly set application name which is done in config/initializers/0_marginalia.rb
+ Marginalia.application_name = "sidekiq"
+ end
+
+ after(:all) do
+ MarginaliaTestJob.clear
+ remove_sidekiq_middleware
+ end
+
+ around do |example|
+ Sidekiq::Testing.fake! { example.run }
+ end
+
+ before do
+ MarginaliaTestJob.perform_async
+ end
+
+ let(:sidekiq_job) { MarginaliaTestJob.jobs.first }
+ let(:recorded) { ActiveRecord::QueryRecorder.new { MarginaliaTestJob.drain } }
+
+ let(:component_map) do
+ {
+ "application" => "sidekiq",
+ "job_class" => "MarginaliaTestJob",
+ "line" => "/spec/support/sidekiq_middleware.rb",
+ "correlation_id" => sidekiq_job['correlation_id'],
+ "jid" => sidekiq_job['jid']
+ }
+ end
+
+ context 'when the feature is enabled' do
+ before do
+ stub_feature(true)
+ end
+
+ it 'generates a query that includes the component and value' do
+ component_map.each do |component, value|
+ expect(recorded.log.last).to include("#{component}:#{value}")
+ end
+ end
+
+ describe 'for ActionMailer delivery jobs' do
+ let(:delivery_job) { MarginaliaTestMailer.first_user.deliver_later }
+
+ let(:recorded) do
+ ActiveRecord::QueryRecorder.new do
+ delivery_job.perform_now
+ end
+ end
+
+ let(:component_map) do
+ {
+ "application" => "sidekiq",
+ "line" => "/lib/gitlab/i18n.rb",
+ "jid" => delivery_job.job_id,
+ "job_class" => delivery_job.arguments.first
+ }
+ end
+
+ it 'generates a query that includes the component and value' do
+ component_map.each do |component, value|
+ expect(recorded.log.last).to include("#{component}:#{value}")
+ end
+ end
+ end
+ end
+
+ context 'when the feature is disabled' do
+ before do
+ stub_feature(false)
+ end
+
+ it 'excludes annotations in generated queries' do
+ expect(recorded.log.last).not_to include("/*")
+ expect(recorded.log.last).not_to include("*/")
+ end
+ end
+ end
+end
diff --git a/spec/lib/omni_auth/strategies/saml_spec.rb b/spec/lib/omni_auth/strategies/saml_spec.rb
index 73e86872308..447800bd93c 100644
--- a/spec/lib/omni_auth/strategies/saml_spec.rb
+++ b/spec/lib/omni_auth/strategies/saml_spec.rb
@@ -15,7 +15,9 @@ describe OmniAuth::Strategies::SAML, type: :strategy do
it 'stores request ID during request phase' do
request_id = double
- allow_any_instance_of(OneLogin::RubySaml::Authrequest).to receive(:uuid).and_return(request_id)
+ allow_next_instance_of(OneLogin::RubySaml::Authrequest) do |instance|
+ allow(instance).to receive(:uuid).and_return(request_id)
+ end
post '/users/auth/saml'
expect(session['last_authn_request_id']).to eq(request_id)
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
index da5ba4c4d99..795aa43b849 100644
--- a/spec/lib/quality/helm_client_spec.rb
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -3,7 +3,8 @@
require 'fast_spec_helper'
RSpec.describe Quality::HelmClient do
- let(:namespace) { 'review-apps-ee' }
+ let(:tiller_namespace) { 'review-apps-ee' }
+ let(:namespace) { tiller_namespace }
let(:release_name) { 'my-release' }
let(:raw_helm_list_page1) do
<<~OUTPUT
@@ -30,12 +31,12 @@ RSpec.describe Quality::HelmClient do
OUTPUT
end
- subject { described_class.new(namespace: namespace) }
+ subject { described_class.new(tiller_namespace: tiller_namespace, namespace: namespace) }
describe '#releases' do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.releases.to_a }.to raise_error(described_class::CommandFailedError)
@@ -43,7 +44,7 @@ RSpec.describe Quality::HelmClient do
it 'calls helm list with default arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
subject.releases.to_a
@@ -51,7 +52,7 @@ RSpec.describe Quality::HelmClient do
it 'calls helm list with extra arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --deployed)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json --deployed)])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
subject.releases(args: ['--deployed']).to_a
@@ -59,7 +60,7 @@ RSpec.describe Quality::HelmClient do
it 'returns a list of Release objects' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --deployed)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json --deployed)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
releases = subject.releases(args: ['--deployed']).to_a
@@ -78,10 +79,10 @@ RSpec.describe Quality::HelmClient do
it 'automatically paginates releases' do
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page1, '', double(success?: true)))
expect(Gitlab::Popen).to receive(:popen_with_detail).ordered
- .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{namespace}" --output json --offset review-6709-group-t40qbv)])
+ .with([%(helm list --namespace "#{namespace}" --tiller-namespace "#{tiller_namespace}" --output json --offset review-6709-group-t40qbv)])
.and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true)))
releases = subject.releases.to_a
@@ -94,7 +95,7 @@ RSpec.describe Quality::HelmClient do
describe '#delete' do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name})])
+ .with([%(helm delete --tiller-namespace "#{tiller_namespace}" --purge #{release_name})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
@@ -102,7 +103,7 @@ RSpec.describe Quality::HelmClient do
it 'calls helm delete with default arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name})])
+ .with([%(helm delete --tiller-namespace "#{tiller_namespace}" --purge #{release_name})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(subject.delete(release_name: release_name)).to eq('')
@@ -113,7 +114,7 @@ RSpec.describe Quality::HelmClient do
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .with([%(helm delete --tiller-namespace "#{tiller_namespace}" --purge #{release_name.join(' ')})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
@@ -121,7 +122,7 @@ RSpec.describe Quality::HelmClient do
it 'calls helm delete with multiple release names' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .with([%(helm delete --tiller-namespace "#{tiller_namespace}" --purge #{release_name.join(' ')})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
expect(subject.delete(release_name: release_name)).to eq('')
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 5bac102ac41..59d4a977d5e 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -5,15 +5,27 @@ require 'fast_spec_helper'
RSpec.describe Quality::KubernetesClient do
let(:namespace) { 'review-apps-ee' }
let(:release_name) { 'my-release' }
+ let(:pod_for_release) { "pod-my-release-abcd" }
+ let(:raw_resource_names_str) { "NAME\nfoo\n#{pod_for_release}\nbar" }
+ let(:raw_resource_names) { raw_resource_names_str.lines.map(&:strip) }
subject { described_class.new(namespace: namespace) }
+ describe 'RESOURCE_LIST' do
+ it 'returns the correct list of resources separated by commas' do
+ expect(described_class::RESOURCE_LIST).to eq('ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd')
+ end
+ end
+
describe '#cleanup' do
+ before do
+ allow(subject).to receive(:raw_resource_names).and_return(raw_resource_names)
+ end
+
it 'raises an error if the Kubernetes command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=true -l release=\"#{release_name}\""])
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
@@ -21,9 +33,12 @@ RSpec.describe Quality::KubernetesClient do
it 'calls kubectl with the correct arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=true -l release=\"#{release_name}\""])
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
.and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
@@ -35,20 +50,22 @@ RSpec.describe Quality::KubernetesClient do
it 'raises an error if the Kubernetes command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})'"])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
end
it 'calls kubectl with the correct arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})'"])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
@@ -58,24 +75,37 @@ RSpec.describe Quality::KubernetesClient do
context 'with `wait: false`' do
it 'raises an error if the Kubernetes command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=false -l release=\"#{release_name}\""])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
expect { subject.cleanup(release_name: release_name, wait: false) }.to raise_error(described_class::CommandFailedError)
end
it 'calls kubectl with the correct arguments' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
- .with([%(kubectl --namespace "#{namespace}" delete ) \
- 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
- "--now --ignore-not-found --include-uninitialized --wait=false -l release=\"#{release_name}\""])
- .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+ .with(["kubectl delete #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl delete --namespace "#{namespace}" #{pod_for_release})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
# We're not verifying the output here, just silencing it
expect { subject.cleanup(release_name: release_name, wait: false) }.to output.to_stdout
end
end
end
+
+ describe '#raw_resource_names' do
+ it 'calls kubectl to retrieve the resource names' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(["kubectl get #{described_class::RESOURCE_LIST} " +
+ %(--namespace "#{namespace}" -o custom-columns=NAME:.metadata.name)])
+ .and_return(Gitlab::Popen::Result.new([], raw_resource_names_str, '', double(success?: true)))
+
+ expect(subject.__send__(:raw_resource_names)).to eq(raw_resource_names)
+ end
+ end
end
diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb
index c85994402dd..13817bdcc72 100644
--- a/spec/lib/quality/test_level_spec.rb
+++ b/spec/lib/quality/test_level_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
- .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb")
+ .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,haml_lint,helpers,initializers,javascripts,lib,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb")
end
end
@@ -82,7 +82,7 @@ RSpec.describe Quality::TestLevel do
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
- .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)})
+ .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|haml_lint|helpers|initializers|javascripts|lib|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)})
end
end
diff --git a/spec/lib/sentry/client/projects_spec.rb b/spec/lib/sentry/client/projects_spec.rb
new file mode 100644
index 00000000000..462f74eaac9
--- /dev/null
+++ b/spec/lib/sentry/client/projects_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Sentry::Client::Projects do
+ include SentryClientHelpers
+
+ let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
+ let(:token) { 'test-token' }
+ let(:client) { Sentry::Client.new(sentry_url, token) }
+ let(:projects_sample_response) do
+ Gitlab::Utils.deep_indifferent_access(
+ JSON.parse(fixture_file('sentry/list_projects_sample_response.json'))
+ )
+ end
+
+ shared_examples 'has correct return type' do |klass|
+ it "returns objects of type #{klass}" do
+ expect(subject).to all( be_a(klass) )
+ end
+ end
+
+ shared_examples 'has correct length' do |length|
+ it { expect(subject.length).to eq(length) }
+ end
+
+ describe '#projects' do
+ let(:sentry_list_projects_url) { 'https://sentrytest.gitlab.com/api/0/projects/' }
+ let(:sentry_api_response) { projects_sample_response }
+ let!(:sentry_api_request) { stub_sentry_request(sentry_list_projects_url, body: sentry_api_response) }
+
+ subject { client.projects }
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Project
+ it_behaves_like 'has correct length', 2
+
+ context 'essential keys missing in API response' do
+ let(:sentry_api_response) do
+ projects_sample_response[0...1].map do |project|
+ project.except(:slug)
+ end
+ end
+
+ it 'raises exception' do
+ expect { subject }.to raise_error(Sentry::Client::MissingKeysError, 'Sentry API response is missing keys. key not found: "slug"')
+ end
+ end
+
+ context 'optional keys missing in sentry response' do
+ let(:sentry_api_response) do
+ projects_sample_response[0...1].map do |project|
+ project[:organization].delete(:id)
+ project.delete(:id)
+ project.except(:status)
+ end
+ end
+
+ it_behaves_like 'calls sentry api'
+
+ it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Project
+ it_behaves_like 'has correct length', 1
+ end
+
+ context 'error object created from sentry response' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:sentry_project_object, :sentry_response) do
+ :id | :id
+ :name | :name
+ :status | :status
+ :slug | :slug
+ :organization_name | [:organization, :name]
+ :organization_id | [:organization, :id]
+ :organization_slug | [:organization, :slug]
+ end
+
+ with_them do
+ it do
+ expect(subject[0].public_send(sentry_project_object)).to(
+ eq(sentry_api_response[0].dig(*sentry_response))
+ )
+ end
+ end
+ end
+
+ context 'redirects' do
+ let(:sentry_api_url) { sentry_list_projects_url }
+
+ it_behaves_like 'no Sentry redirects'
+ end
+
+ # Sentry API returns 404 if there are extra slashes in the URL!
+ context 'extra slashes in URL' do
+ let(:sentry_url) { 'https://sentrytest.gitlab.com/api//0/projects//' }
+ let!(:valid_req_stub) do
+ stub_sentry_request(sentry_list_projects_url)
+ end
+
+ it 'removes extra slashes in api url' do
+ expect(Gitlab::HTTP).to receive(:get).with(
+ URI(sentry_list_projects_url),
+ anything
+ ).and_call_original
+
+ subject
+
+ expect(valid_req_stub).to have_been_requested
+ end
+ end
+
+ context 'when exception is raised' do
+ let(:sentry_request_url) { sentry_list_projects_url }
+
+ it_behaves_like 'maps Sentry exceptions'
+ end
+ end
+end
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index 8101664d34f..cff06bf4a5f 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -3,8 +3,16 @@
require 'spec_helper'
describe Sentry::Client do
+ include SentryClientHelpers
+
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
+ let(:default_httparty_options) do
+ {
+ follow_redirects: false,
+ headers: { "Authorization" => "Bearer test-token" }
+ }
+ end
let(:issues_sample_response) do
Gitlab::Utils.deep_indifferent_access(
@@ -12,102 +20,60 @@ describe Sentry::Client do
)
end
- let(:projects_sample_response) do
- Gitlab::Utils.deep_indifferent_access(
- JSON.parse(fixture_file('sentry/list_projects_sample_response.json'))
- )
- end
-
subject(:client) { described_class.new(sentry_url, token) }
- # Requires sentry_api_url and subject to be defined
- shared_examples 'no redirects' do
- let(:redirect_to) { 'https://redirected.example.com' }
- let(:other_url) { 'https://other.example.org' }
-
- let!(:redirected_req_stub) { stub_sentry_request(other_url) }
-
- let!(:redirect_req_stub) do
- stub_sentry_request(
- sentry_api_url,
- status: 302,
- headers: { location: redirect_to }
- )
- end
-
- it 'does not follow redirects' do
- expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response status code: 302')
- expect(redirect_req_stub).to have_been_requested
- expect(redirected_req_stub).not_to have_been_requested
- end
- end
-
- shared_examples 'has correct return type' do |klass|
+ shared_examples 'issues has correct return type' do |klass|
it "returns objects of type #{klass}" do
- expect(subject).to all( be_a(klass) )
+ expect(subject[:issues]).to all( be_a(klass) )
end
end
- shared_examples 'has correct length' do |length|
- it { expect(subject.length).to eq(length) }
- end
-
- # Requires sentry_api_request and subject to be defined
- shared_examples 'calls sentry api' do
- it 'calls sentry api' do
- subject
-
- expect(sentry_api_request).to have_been_requested
- end
- end
-
- shared_examples 'maps exceptions' do
- exceptions = {
- Gitlab::HTTP::Error => 'Error when connecting to Sentry',
- Net::OpenTimeout => 'Connection to Sentry timed out',
- SocketError => 'Received SocketError when trying to connect to Sentry',
- OpenSSL::SSL::SSLError => 'Sentry returned invalid SSL data',
- Errno::ECONNREFUSED => 'Connection refused',
- StandardError => 'Sentry request failed due to StandardError'
- }
-
- exceptions.each do |exception, message|
- context "#{exception}" do
- before do
- stub_request(:get, sentry_request_url).to_raise(exception)
- end
-
- it do
- expect { subject }
- .to raise_exception(Sentry::Client::Error, message)
- end
- end
- end
+ shared_examples 'issues has correct length' do |length|
+ it { expect(subject[:issues].length).to eq(length) }
end
describe '#list_issues' do
let(:issue_status) { 'unresolved' }
let(:limit) { 20 }
+ let(:search_term) { '' }
+ let(:cursor) { nil }
+ let(:sort) { 'last_seen' }
let(:sentry_api_response) { issues_sample_response }
let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
- subject { client.list_issues(issue_status: issue_status, limit: limit) }
+ subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term, sort: sort, cursor: cursor) }
it_behaves_like 'calls sentry api'
- it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'has correct length', 1
+ it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues has correct length', 1
shared_examples 'has correct external_url' do
context 'external_url' do
it 'is constructed correctly' do
- expect(subject[0].external_url).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/11')
+ expect(subject[:issues][0].external_url).to eq('https://sentrytest.gitlab.com/sentry-org/sentry-project/issues/11')
end
end
end
+ context 'when response has a pagination info' do
+ let(:headers) do
+ {
+ link: '<https://sentrytest.gitlab.com>; rel="previous"; results="true"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="true"; cursor="1572959139000:0:0"'
+ }
+ end
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response, headers: headers) }
+
+ it 'parses the pagination' do
+ expect(subject[:pagination]).to eq(
+ 'previous' => { 'cursor' => '1573556671000:0:1' },
+ 'next' => { 'cursor' => '1572959139000:0:0' }
+ )
+ end
+ end
+
context 'error object created from sentry response' do
using RSpec::Parameterized::TableSyntax
@@ -130,7 +96,7 @@ describe Sentry::Client do
end
with_them do
- it { expect(subject[0].public_send(error_object)).to eq(sentry_api_response[0].dig(*sentry_response)) }
+ it { expect(subject[:issues][0].public_send(error_object)).to eq(sentry_api_response[0].dig(*sentry_response)) }
end
it_behaves_like 'has correct external_url'
@@ -139,7 +105,7 @@ describe Sentry::Client do
context 'redirects' do
let(:sentry_api_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
- it_behaves_like 'no redirects'
+ it_behaves_like 'no Sentry redirects'
end
# Sentry API returns 404 if there are extra slashes in the URL!
@@ -164,6 +130,35 @@ describe Sentry::Client do
end
end
+ context 'requests with sort parameter in sentry api' do
+ let(:sentry_request_url) do
+ 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
+ 'issues/?limit=20&query=is:unresolved&sort=freq'
+ end
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
+
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'frequency') }
+
+ it 'calls the sentry api with sort params' do
+ expect(Gitlab::HTTP).to receive(:get).with(
+ URI("#{sentry_url}/issues/"),
+ default_httparty_options.merge(query: { limit: 20, query: "is:unresolved", sort: "freq" })
+ ).and_call_original
+
+ subject
+
+ expect(sentry_api_request).to have_been_requested
+ end
+ end
+
+ context 'with invalid sort params' do
+ subject { client.list_issues(issue_status: issue_status, limit: limit, sort: 'fish') }
+
+ it 'throws an error' do
+ expect { subject }.to raise_error(Sentry::Client::BadRequestError, 'Invalid value for sort param')
+ end
+ end
+
context 'Older sentry versions where keys are not present' do
let(:sentry_api_response) do
issues_sample_response[0...1].map do |issue|
@@ -174,8 +169,8 @@ describe Sentry::Client do
it_behaves_like 'calls sentry api'
- it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Error
- it_behaves_like 'has correct length', 1
+ it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues has correct length', 1
it_behaves_like 'has correct external_url'
end
@@ -201,114 +196,26 @@ describe Sentry::Client do
end
end
- it_behaves_like 'maps exceptions'
- end
-
- describe '#list_projects' do
- let(:sentry_list_projects_url) { 'https://sentrytest.gitlab.com/api/0/projects/' }
-
- let(:sentry_api_response) { projects_sample_response }
-
- let!(:sentry_api_request) { stub_sentry_request(sentry_list_projects_url, body: sentry_api_response) }
-
- subject { client.list_projects }
-
- it_behaves_like 'calls sentry api'
-
- it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Project
- it_behaves_like 'has correct length', 2
-
- context 'essential keys missing in API response' do
- let(:sentry_api_response) do
- projects_sample_response[0...1].map do |project|
- project.except(:slug)
- end
- end
-
- it 'raises exception' do
- expect { subject }.to raise_error(Sentry::Client::MissingKeysError, 'Sentry API response is missing keys. key not found: "slug"')
- end
- end
+ it_behaves_like 'maps Sentry exceptions'
- context 'optional keys missing in sentry response' do
- let(:sentry_api_response) do
- projects_sample_response[0...1].map do |project|
- project[:organization].delete(:id)
- project.delete(:id)
- project.except(:status)
- end
- end
+ context 'when search term is present' do
+ let(:search_term) { 'NoMethodError' }
+ let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
it_behaves_like 'calls sentry api'
- it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Project
- it_behaves_like 'has correct length', 1
+ it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues has correct length', 1
end
- context 'error object created from sentry response' do
- using RSpec::Parameterized::TableSyntax
-
- where(:sentry_project_object, :sentry_response) do
- :id | :id
- :name | :name
- :status | :status
- :slug | :slug
- :organization_name | [:organization, :name]
- :organization_id | [:organization, :id]
- :organization_slug | [:organization, :slug]
- end
+ context 'when cursor is present' do
+ let(:cursor) { '1572959139000:0:0' }
+ let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&cursor=#{cursor}&query=is:unresolved" }
- with_them do
- it do
- expect(subject[0].public_send(sentry_project_object)).to(
- eq(sentry_api_response[0].dig(*sentry_response))
- )
- end
- end
- end
-
- context 'redirects' do
- let(:sentry_api_url) { sentry_list_projects_url }
-
- it_behaves_like 'no redirects'
- end
-
- # Sentry API returns 404 if there are extra slashes in the URL!
- context 'extra slashes in URL' do
- let(:sentry_url) { 'https://sentrytest.gitlab.com/api//0/projects//' }
- let(:client) { described_class.new(sentry_url, token) }
-
- let!(:valid_req_stub) do
- stub_sentry_request(sentry_list_projects_url)
- end
-
- it 'removes extra slashes in api url' do
- expect(Gitlab::HTTP).to receive(:get).with(
- URI(sentry_list_projects_url),
- anything
- ).and_call_original
-
- subject
-
- expect(valid_req_stub).to have_been_requested
- end
- end
-
- context 'when exception is raised' do
- let(:sentry_request_url) { sentry_list_projects_url }
+ it_behaves_like 'calls sentry api'
- it_behaves_like 'maps exceptions'
+ it_behaves_like 'issues has correct return type', Gitlab::ErrorTracking::Error
+ it_behaves_like 'issues has correct length', 1
end
end
-
- private
-
- def stub_sentry_request(url, body: {}, status: 200, headers: {})
- stub_request(:get, url)
- .to_return(
- status: status,
- headers: { 'Content-Type' => 'application/json' }.merge(headers),
- body: body.to_json
- )
- end
end
diff --git a/spec/lib/sentry/pagination_parser_spec.rb b/spec/lib/sentry/pagination_parser_spec.rb
new file mode 100644
index 00000000000..1be6f9f4163
--- /dev/null
+++ b/spec/lib/sentry/pagination_parser_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'support/helpers/fixture_helpers'
+
+describe Sentry::PaginationParser do
+ include FixtureHelpers
+
+ describe '.parse' do
+ subject { described_class.parse(headers) }
+
+ context 'when headers do not have "link" param' do
+ let(:headers) { {} }
+
+ it 'returns empty hash' do
+ is_expected.to eq({})
+ end
+ end
+
+ context 'when headers.link has previous and next pages' do
+ let(:headers) do
+ {
+ 'link' => '<https://sentrytest.gitlab.com>; rel="previous"; results="true"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="true"; cursor="1572959139000:0:0"'
+ }
+ end
+
+ it 'returns info about both pages' do
+ is_expected.to eq(
+ 'previous' => { 'cursor' => '1573556671000:0:1' },
+ 'next' => { 'cursor' => '1572959139000:0:0' }
+ )
+ end
+ end
+
+ context 'when headers.link has only next page' do
+ let(:headers) do
+ {
+ 'link' => '<https://sentrytest.gitlab.com>; rel="previous"; results="false"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="true"; cursor="1572959139000:0:0"'
+ }
+ end
+
+ it 'returns only info about the next page' do
+ is_expected.to eq(
+ 'next' => { 'cursor' => '1572959139000:0:0' }
+ )
+ end
+ end
+
+ context 'when headers.link has only previous page' do
+ let(:headers) do
+ {
+ 'link' => '<https://sentrytest.gitlab.com>; rel="previous"; results="true"; cursor="1573556671000:0:1", <https://sentrytest.gitlab.com>; rel="next"; results="false"; cursor="1572959139000:0:0"'
+ }
+ end
+
+ it 'returns only info about the previous page' do
+ is_expected.to eq(
+ 'previous' => { 'cursor' => '1573556671000:0:1' }
+ )
+ end
+ end
+ end
+end