Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
commit43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch)
treedceebdc68925362117480a5d672bcff122fb625b /spec/services
parent20c84b99005abd1c82101dfeff264ac50d2df211 (diff)
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/access_token_validation_service_spec.rb2
-rw-r--r--spec/services/achievements/award_service_spec.rb80
-rw-r--r--spec/services/achievements/destroy_service_spec.rb39
-rw-r--r--spec/services/achievements/revoke_service_spec.rb66
-rw-r--r--spec/services/achievements/update_service_spec.rb48
-rw-r--r--spec/services/admin/abuse_report_update_service_spec.rb199
-rw-r--r--spec/services/admin/set_feature_flag_service_spec.rb2
-rw-r--r--spec/services/alert_management/alerts/todo/create_service_spec.rb2
-rw-r--r--spec/services/alert_management/alerts/update_service_spec.rb2
-rw-r--r--spec/services/alert_management/create_alert_issue_service_spec.rb2
-rw-r--r--spec/services/alert_management/http_integrations/create_service_spec.rb2
-rw-r--r--spec/services/alert_management/http_integrations/destroy_service_spec.rb2
-rw-r--r--spec/services/alert_management/http_integrations/update_service_spec.rb2
-rw-r--r--spec/services/alert_management/metric_images/upload_service_spec.rb2
-rw-r--r--spec/services/alert_management/process_prometheus_alert_service_spec.rb2
-rw-r--r--spec/services/analytics/cycle_analytics/stages/list_service_spec.rb2
-rw-r--r--spec/services/application_settings/update_service_spec.rb6
-rw-r--r--spec/services/audit_event_service_spec.rb2
-rw-r--r--spec/services/audit_events/build_service_spec.rb2
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb2
-rw-r--r--spec/services/auth/dependency_proxy_authentication_service_spec.rb2
-rw-r--r--spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb2
-rw-r--r--spec/services/authorized_project_update/periodic_recalculate_service_spec.rb2
-rw-r--r--spec/services/authorized_project_update/project_access_changed_service_spec.rb2
-rw-r--r--spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb2
-rw-r--r--spec/services/authorized_project_update/project_recalculate_service_spec.rb2
-rw-r--r--spec/services/auto_merge/base_service_spec.rb2
-rw-r--r--spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb2
-rw-r--r--spec/services/auto_merge_service_spec.rb2
-rw-r--r--spec/services/award_emojis/add_service_spec.rb2
-rw-r--r--spec/services/award_emojis/base_service_spec.rb2
-rw-r--r--spec/services/award_emojis/collect_user_emoji_service_spec.rb2
-rw-r--r--spec/services/award_emojis/copy_service_spec.rb2
-rw-r--r--spec/services/award_emojis/destroy_service_spec.rb2
-rw-r--r--spec/services/award_emojis/toggle_service_spec.rb2
-rw-r--r--spec/services/base_container_service_spec.rb2
-rw-r--r--spec/services/base_count_service_spec.rb2
-rw-r--r--spec/services/boards/create_service_spec.rb2
-rw-r--r--spec/services/boards/destroy_service_spec.rb2
-rw-r--r--spec/services/boards/issues/create_service_spec.rb2
-rw-r--r--spec/services/boards/issues/list_service_spec.rb14
-rw-r--r--spec/services/boards/issues/move_service_spec.rb2
-rw-r--r--spec/services/boards/lists/create_service_spec.rb2
-rw-r--r--spec/services/boards/lists/destroy_service_spec.rb2
-rw-r--r--spec/services/boards/lists/list_service_spec.rb2
-rw-r--r--spec/services/boards/lists/move_service_spec.rb2
-rw-r--r--spec/services/boards/lists/update_service_spec.rb2
-rw-r--r--spec/services/boards/visits/create_service_spec.rb2
-rw-r--r--spec/services/branches/create_service_spec.rb4
-rw-r--r--spec/services/branches/delete_merged_service_spec.rb2
-rw-r--r--spec/services/branches/delete_service_spec.rb2
-rw-r--r--spec/services/branches/diverging_commit_counts_service_spec.rb2
-rw-r--r--spec/services/branches/validate_new_service_spec.rb2
-rw-r--r--spec/services/bulk_create_integration_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/archive_extraction_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/batched_relation_export_service_spec.rb104
-rw-r--r--spec/services/bulk_imports/create_service_spec.rb321
-rw-r--r--spec/services/bulk_imports/export_service_spec.rb53
-rw-r--r--spec/services/bulk_imports/file_decompression_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/file_download_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/file_export_service_spec.rb64
-rw-r--r--spec/services/bulk_imports/lfs_objects_export_service_spec.rb25
-rw-r--r--spec/services/bulk_imports/relation_batch_export_service_spec.rb67
-rw-r--r--spec/services/bulk_imports/relation_export_service_spec.rb24
-rw-r--r--spec/services/bulk_imports/repository_bundle_export_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/tree_export_service_spec.rb12
-rw-r--r--spec/services/bulk_imports/uploads_export_service_spec.rb35
-rw-r--r--spec/services/bulk_push_event_payload_service_spec.rb2
-rw-r--r--spec/services/bulk_update_integration_service_spec.rb10
-rw-r--r--spec/services/captcha/captcha_verification_service_spec.rb2
-rw-r--r--spec/services/chat_names/find_user_service_spec.rb2
-rw-r--r--spec/services/ci/abort_pipelines_service_spec.rb2
-rw-r--r--spec/services/ci/append_build_trace_service_spec.rb2
-rw-r--r--spec/services/ci/archive_trace_service_spec.rb34
-rw-r--r--spec/services/ci/build_cancel_service_spec.rb2
-rw-r--r--spec/services/ci/build_erase_service_spec.rb2
-rw-r--r--spec/services/ci/build_report_result_service_spec.rb2
-rw-r--r--spec/services/ci/build_unschedule_service_spec.rb2
-rw-r--r--spec/services/ci/catalog/validate_resource_service_spec.rb57
-rw-r--r--spec/services/ci/change_variable_service_spec.rb2
-rw-r--r--spec/services/ci/change_variables_service_spec.rb2
-rw-r--r--spec/services/ci/compare_accessibility_reports_service_spec.rb2
-rw-r--r--spec/services/ci/compare_codequality_reports_service_spec.rb2
-rw-r--r--spec/services/ci/compare_reports_base_service_spec.rb2
-rw-r--r--spec/services/ci/compare_test_reports_service_spec.rb2
-rw-r--r--spec/services/ci/components/fetch_service_spec.rb2
-rw-r--r--spec/services/ci/copy_cross_database_associations_service_spec.rb2
-rw-r--r--spec/services/ci/create_downstream_pipeline_service_spec.rb14
-rw-r--r--spec/services/ci/create_pipeline_service/artifacts_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/cache_spec.rb18
-rw-r--r--spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb11
-rw-r--r--spec/services/ci/create_pipeline_service/custom_config_content_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/dry_run_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/environment_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/include_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/logger_spec.rb6
-rw-r--r--spec/services/ci/create_pipeline_service/merge_requests_spec.rb15
-rw-r--r--spec/services/ci/create_pipeline_service/needs_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/parallel_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/parameter_content_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/partitioning_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service/rate_limit_spec.rb5
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb139
-rw-r--r--spec/services/ci/create_pipeline_service/scripts_spec.rb28
-rw-r--r--spec/services/ci/create_pipeline_service/tags_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/variables_spec.rb3
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb333
-rw-r--r--spec/services/ci/create_web_ide_terminal_service_spec.rb2
-rw-r--r--spec/services/ci/daily_build_group_report_result_service_spec.rb2
-rw-r--r--spec/services/ci/delete_objects_service_spec.rb2
-rw-r--r--spec/services/ci/delete_unit_tests_service_spec.rb2
-rw-r--r--spec/services/ci/deployments/destroy_service_spec.rb2
-rw-r--r--spec/services/ci/destroy_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/destroy_secure_file_service_spec.rb2
-rw-r--r--spec/services/ci/disable_user_pipeline_schedules_service_spec.rb2
-rw-r--r--spec/services/ci/drop_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/ensure_stage_service_spec.rb2
-rw-r--r--spec/services/ci/expire_pipeline_cache_service_spec.rb2
-rw-r--r--spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/find_exposed_artifacts_service_spec.rb2
-rw-r--r--spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb2
-rw-r--r--spec/services/ci/generate_coverage_reports_service_spec.rb2
-rw-r--r--spec/services/ci/generate_kubeconfig_service_spec.rb14
-rw-r--r--spec/services/ci/generate_terraform_reports_service_spec.rb2
-rw-r--r--spec/services/ci/job_artifacts/bulk_delete_by_project_service_spec.rb121
-rw-r--r--spec/services/ci/job_artifacts/create_service_spec.rb525
-rw-r--r--spec/services/ci/job_artifacts/delete_project_artifacts_service_spec.rb2
-rw-r--r--spec/services/ci/job_artifacts/delete_service_spec.rb2
-rw-r--r--spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb40
-rw-r--r--spec/services/ci/job_artifacts/destroy_associations_service_spec.rb31
-rw-r--r--spec/services/ci/job_artifacts/destroy_batch_service_spec.rb15
-rw-r--r--spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb2
-rw-r--r--spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb62
-rw-r--r--spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb3
-rw-r--r--spec/services/ci/job_token_scope/add_project_service_spec.rb4
-rw-r--r--spec/services/ci/list_config_variables_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb3
-rw-r--r--spec/services/ci/pipeline_bridge_status_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb58
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb21
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_test_on_failure_no_needs.yml31
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_test1_and_test2_have_when.yml46
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_with_allow_failure_test1_and_test2_have_when.yml47
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/stage_test_on_failure_no_prev_stage.yml29
-rw-r--r--spec/services/ci/pipeline_schedules/take_ownership_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_trigger_service_spec.rb2
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb12
-rw-r--r--spec/services/ci/pipelines/hook_service_spec.rb2
-rw-r--r--spec/services/ci/play_bridge_service_spec.rb2
-rw-r--r--spec/services/ci/play_build_service_spec.rb5
-rw-r--r--spec/services/ci/play_manual_stage_service_spec.rb7
-rw-r--r--spec/services/ci/prepare_build_service_spec.rb2
-rw-r--r--spec/services/ci/process_build_service_spec.rb2
-rw-r--r--spec/services/ci/process_pipeline_service_spec.rb2
-rw-r--r--spec/services/ci/process_sync_events_service_spec.rb5
-rw-r--r--spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb2
-rw-r--r--spec/services/ci/queue/pending_builds_strategy_spec.rb2
-rw-r--r--spec/services/ci/register_job_service_spec.rb1166
-rw-r--r--spec/services/ci/reset_skipped_jobs_service_spec.rb487
-rw-r--r--spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb2
-rw-r--r--spec/services/ci/retry_job_service_spec.rb15
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb22
-rw-r--r--spec/services/ci/run_scheduled_build_service_spec.rb5
-rw-r--r--spec/services/ci/runners/create_runner_service_spec.rb203
-rw-r--r--spec/services/ci/runners/process_runner_version_update_service_spec.rb13
-rw-r--r--spec/services/ci/runners/register_runner_service_spec.rb64
-rw-r--r--spec/services/ci/runners/stale_managers_cleanup_service_spec.rb (renamed from spec/services/ci/runners/stale_machines_cleanup_service_spec.rb)22
-rw-r--r--spec/services/ci/runners/unregister_runner_manager_service_spec.rb63
-rw-r--r--spec/services/ci/stuck_builds/drop_pending_service_spec.rb2
-rw-r--r--spec/services/ci/stuck_builds/drop_running_service_spec.rb2
-rw-r--r--spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb2
-rw-r--r--spec/services/ci/test_failure_history_service_spec.rb2
-rw-r--r--spec/services/ci/track_failed_build_service_spec.rb2
-rw-r--r--spec/services/ci/unlock_artifacts_service_spec.rb20
-rw-r--r--spec/services/ci/update_build_queue_service_spec.rb2
-rw-r--r--spec/services/ci/update_instance_variables_service_spec.rb2
-rw-r--r--spec/services/ci/update_pending_build_service_spec.rb2
-rw-r--r--spec/services/clusters/agent_tokens/create_service_spec.rb8
-rw-r--r--spec/services/clusters/agent_tokens/revoke_service_spec.rb77
-rw-r--r--spec/services/clusters/agent_tokens/track_usage_service_spec.rb2
-rw-r--r--spec/services/clusters/agents/authorizations/ci_access/filter_service_spec.rb (renamed from spec/services/clusters/agents/filter_authorizations_service_spec.rb)16
-rw-r--r--spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb (renamed from spec/services/clusters/agents/refresh_authorization_service_spec.rb)34
-rw-r--r--spec/services/clusters/agents/authorizations/user_access/refresh_service_spec.rb181
-rw-r--r--spec/services/clusters/agents/authorize_proxy_user_service_spec.rb65
-rw-r--r--spec/services/clusters/agents/create_activity_event_service_spec.rb13
-rw-r--r--spec/services/clusters/agents/create_service_spec.rb2
-rw-r--r--spec/services/clusters/agents/delete_expired_events_service_spec.rb2
-rw-r--r--spec/services/clusters/agents/delete_service_spec.rb2
-rw-r--r--spec/services/clusters/build_kubernetes_namespace_service_spec.rb2
-rw-r--r--spec/services/clusters/build_service_spec.rb2
-rw-r--r--spec/services/clusters/cleanup/project_namespace_service_spec.rb2
-rw-r--r--spec/services/clusters/cleanup/service_account_service_spec.rb18
-rw-r--r--spec/services/clusters/create_service_spec.rb4
-rw-r--r--spec/services/clusters/destroy_service_spec.rb2
-rw-r--r--spec/services/clusters/integrations/create_service_spec.rb2
-rw-r--r--spec/services/clusters/integrations/prometheus_health_check_service_spec.rb2
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb4
-rw-r--r--spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb2
-rw-r--r--spec/services/clusters/kubernetes/fetch_kubernetes_token_service_spec.rb2
-rw-r--r--spec/services/clusters/kubernetes_spec.rb2
-rw-r--r--spec/services/clusters/management/validate_management_project_permissions_service_spec.rb2
-rw-r--r--spec/services/clusters/update_service_spec.rb2
-rw-r--r--spec/services/cohorts_service_spec.rb2
-rw-r--r--spec/services/commits/cherry_pick_service_spec.rb2
-rw-r--r--spec/services/commits/commit_patch_service_spec.rb2
-rw-r--r--spec/services/commits/tag_service_spec.rb2
-rw-r--r--spec/services/compare_service_spec.rb2
-rw-r--r--spec/services/concerns/audit_event_save_type_spec.rb2
-rw-r--r--spec/services/concerns/exclusive_lease_guard_spec.rb48
-rw-r--r--spec/services/concerns/merge_requests/assigns_merge_params_spec.rb2
-rw-r--r--spec/services/concerns/rate_limited_service_spec.rb2
-rw-r--r--spec/services/container_expiration_policies/cleanup_service_spec.rb3
-rw-r--r--spec/services/container_expiration_policies/update_service_spec.rb2
-rw-r--r--spec/services/customer_relations/contacts/create_service_spec.rb6
-rw-r--r--spec/services/customer_relations/contacts/update_service_spec.rb2
-rw-r--r--spec/services/customer_relations/organizations/create_service_spec.rb8
-rw-r--r--spec/services/customer_relations/organizations/update_service_spec.rb14
-rw-r--r--spec/services/database/consistency_check_service_spec.rb2
-rw-r--r--spec/services/database/consistency_fix_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/auth_token_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/find_cached_manifest_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/group_settings/update_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/head_manifest_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb2
-rw-r--r--spec/services/dependency_proxy/request_token_service_spec.rb2
-rw-r--r--spec/services/deploy_keys/create_service_spec.rb2
-rw-r--r--spec/services/deployments/archive_in_project_service_spec.rb2
-rw-r--r--spec/services/deployments/create_for_build_service_spec.rb2
-rw-r--r--spec/services/deployments/create_service_spec.rb2
-rw-r--r--spec/services/deployments/link_merge_requests_service_spec.rb2
-rw-r--r--spec/services/deployments/older_deployments_drop_service_spec.rb2
-rw-r--r--spec/services/deployments/update_environment_service_spec.rb2
-rw-r--r--spec/services/deployments/update_service_spec.rb2
-rw-r--r--spec/services/design_management/copy_design_collection/copy_service_spec.rb3
-rw-r--r--spec/services/design_management/copy_design_collection/queue_service_spec.rb3
-rw-r--r--spec/services/design_management/delete_designs_service_spec.rb4
-rw-r--r--spec/services/design_management/design_user_notes_count_service_spec.rb2
-rw-r--r--spec/services/design_management/generate_image_versions_service_spec.rb2
-rw-r--r--spec/services/design_management/move_designs_service_spec.rb2
-rw-r--r--spec/services/design_management/save_designs_service_spec.rb5
-rw-r--r--spec/services/discussions/capture_diff_note_position_service_spec.rb2
-rw-r--r--spec/services/discussions/capture_diff_note_positions_service_spec.rb2
-rw-r--r--spec/services/discussions/update_diff_position_service_spec.rb2
-rw-r--r--spec/services/draft_notes/create_service_spec.rb2
-rw-r--r--spec/services/draft_notes/destroy_service_spec.rb2
-rw-r--r--spec/services/draft_notes/publish_service_spec.rb2
-rw-r--r--spec/services/emails/confirm_service_spec.rb2
-rw-r--r--spec/services/emails/create_service_spec.rb2
-rw-r--r--spec/services/emails/destroy_service_spec.rb2
-rw-r--r--spec/services/environments/auto_stop_service_spec.rb3
-rw-r--r--spec/services/environments/canary_ingress/update_service_spec.rb3
-rw-r--r--spec/services/environments/create_for_build_service_spec.rb2
-rw-r--r--spec/services/environments/reset_auto_stop_service_spec.rb2
-rw-r--r--spec/services/environments/schedule_to_delete_review_apps_service_spec.rb2
-rw-r--r--spec/services/environments/stop_service_spec.rb2
-rw-r--r--spec/services/error_tracking/base_service_spec.rb2
-rw-r--r--spec/services/error_tracking/collect_error_service_spec.rb2
-rw-r--r--spec/services/error_tracking/issue_details_service_spec.rb2
-rw-r--r--spec/services/error_tracking/issue_latest_event_service_spec.rb2
-rw-r--r--spec/services/error_tracking/issue_update_service_spec.rb2
-rw-r--r--spec/services/error_tracking/list_issues_service_spec.rb2
-rw-r--r--spec/services/event_create_service_spec.rb60
-rw-r--r--spec/services/events/destroy_service_spec.rb2
-rw-r--r--spec/services/events/render_service_spec.rb2
-rw-r--r--spec/services/feature_flags/create_service_spec.rb10
-rw-r--r--spec/services/feature_flags/destroy_service_spec.rb5
-rw-r--r--spec/services/feature_flags/hook_service_spec.rb2
-rw-r--r--spec/services/feature_flags/update_service_spec.rb7
-rw-r--r--spec/services/files/create_service_spec.rb2
-rw-r--r--spec/services/files/delete_service_spec.rb6
-rw-r--r--spec/services/files/multi_service_spec.rb16
-rw-r--r--spec/services/files/update_service_spec.rb6
-rw-r--r--spec/services/git/base_hooks_service_spec.rb38
-rw-r--r--spec/services/git/branch_hooks_service_spec.rb2
-rw-r--r--spec/services/git/branch_push_service_spec.rb2
-rw-r--r--spec/services/git/process_ref_changes_service_spec.rb2
-rw-r--r--spec/services/git/tag_hooks_service_spec.rb2
-rw-r--r--spec/services/git/tag_push_service_spec.rb2
-rw-r--r--spec/services/git/wiki_push_service/change_spec.rb12
-rw-r--r--spec/services/google_cloud/create_cloudsql_instance_service_spec.rb2
-rw-r--r--spec/services/google_cloud/create_service_accounts_service_spec.rb2
-rw-r--r--spec/services/google_cloud/enable_cloud_run_service_spec.rb2
-rw-r--r--spec/services/google_cloud/enable_cloudsql_service_spec.rb2
-rw-r--r--spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb2
-rw-r--r--spec/services/google_cloud/generate_pipeline_service_spec.rb2
-rw-r--r--spec/services/google_cloud/get_cloudsql_instances_service_spec.rb2
-rw-r--r--spec/services/google_cloud/service_accounts_service_spec.rb2
-rw-r--r--spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb2
-rw-r--r--spec/services/gpg_keys/create_service_spec.rb2
-rw-r--r--spec/services/grafana/proxy_service_spec.rb2
-rw-r--r--spec/services/gravatar_service_spec.rb2
-rw-r--r--spec/services/groups/auto_devops_service_spec.rb2
-rw-r--r--spec/services/groups/autocomplete_service_spec.rb2
-rw-r--r--spec/services/groups/deploy_tokens/create_service_spec.rb2
-rw-r--r--spec/services/groups/deploy_tokens/destroy_service_spec.rb2
-rw-r--r--spec/services/groups/deploy_tokens/revoke_service_spec.rb2
-rw-r--r--spec/services/groups/group_links/create_service_spec.rb2
-rw-r--r--spec/services/groups/group_links/destroy_service_spec.rb2
-rw-r--r--spec/services/groups/group_links/update_service_spec.rb4
-rw-r--r--spec/services/groups/import_export/export_service_spec.rb2
-rw-r--r--spec/services/groups/import_export/import_service_spec.rb2
-rw-r--r--spec/services/groups/merge_requests_count_service_spec.rb2
-rw-r--r--spec/services/groups/nested_create_service_spec.rb2
-rw-r--r--spec/services/groups/open_issues_count_service_spec.rb2
-rw-r--r--spec/services/groups/participants_service_spec.rb2
-rw-r--r--spec/services/groups/transfer_service_spec.rb61
-rw-r--r--spec/services/groups/update_service_spec.rb2
-rw-r--r--spec/services/groups/update_shared_runners_service_spec.rb2
-rw-r--r--spec/services/groups/update_statistics_service_spec.rb2
-rw-r--r--spec/services/ide/base_config_service_spec.rb2
-rw-r--r--spec/services/ide/schemas_config_service_spec.rb2
-rw-r--r--spec/services/ide/terminal_config_service_spec.rb2
-rw-r--r--spec/services/import/bitbucket_server_service_spec.rb4
-rw-r--r--spec/services/import/fogbugz_service_spec.rb5
-rw-r--r--spec/services/import/github/cancel_project_import_service_spec.rb14
-rw-r--r--spec/services/import/github/notes/create_service_spec.rb2
-rw-r--r--spec/services/import/github_service_spec.rb7
-rw-r--r--spec/services/import/gitlab_projects/create_project_service_spec.rb12
-rw-r--r--spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb2
-rw-r--r--spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb2
-rw-r--r--spec/services/import/prepare_service_spec.rb2
-rw-r--r--spec/services/import/validate_remote_git_endpoint_service_spec.rb24
-rw-r--r--spec/services/import_csv/base_service_spec.rb69
-rw-r--r--spec/services/import_export_clean_up_service_spec.rb2
-rw-r--r--spec/services/incident_management/incidents/create_service_spec.rb2
-rw-r--r--spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb3
-rw-r--r--spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb2
-rw-r--r--spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb2
-rw-r--r--spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb3
-rw-r--r--spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb2
-rw-r--r--spec/services/incident_management/pager_duty/process_webhook_service_spec.rb2
-rw-r--r--spec/services/incident_management/timeline_event_tags/create_service_spec.rb2
-rw-r--r--spec/services/incident_management/timeline_events/create_service_spec.rb4
-rw-r--r--spec/services/incident_management/timeline_events/destroy_service_spec.rb3
-rw-r--r--spec/services/incident_management/timeline_events/update_service_spec.rb1
-rw-r--r--spec/services/integrations/propagate_service_spec.rb2
-rw-r--r--spec/services/integrations/slack_event_service_spec.rb56
-rw-r--r--spec/services/integrations/slack_events/app_home_opened_service_spec.rb113
-rw-r--r--spec/services/integrations/slack_events/url_verification_service_spec.rb11
-rw-r--r--spec/services/integrations/slack_interaction_service_spec.rb70
-rw-r--r--spec/services/integrations/slack_interactions/block_action_service_spec.rb48
-rw-r--r--spec/services/integrations/slack_interactions/incident_management/incident_modal_closed_service_spec.rb78
-rw-r--r--spec/services/integrations/slack_interactions/incident_management/incident_modal_opened_service_spec.rb141
-rw-r--r--spec/services/integrations/slack_interactions/incident_management/incident_modal_submit_service_spec.rb296
-rw-r--r--spec/services/integrations/slack_interactions/slack_block_actions/incident_management/project_update_handler_spec.rb158
-rw-r--r--spec/services/integrations/slack_option_service_spec.rb76
-rw-r--r--spec/services/integrations/slack_options/label_search_handler_spec.rb47
-rw-r--r--spec/services/integrations/slack_options/user_search_handler_spec.rb52
-rw-r--r--spec/services/integrations/test/project_service_spec.rb2
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb2
-rw-r--r--spec/services/issuable/callbacks/milestone_spec.rb101
-rw-r--r--spec/services/issuable/common_system_notes_service_spec.rb2
-rw-r--r--spec/services/issuable/destroy_label_links_service_spec.rb2
-rw-r--r--spec/services/issuable/destroy_service_spec.rb2
-rw-r--r--spec/services/issuable/discussions_list_service_spec.rb2
-rw-r--r--spec/services/issuable/process_assignees_spec.rb2
-rw-r--r--spec/services/issue_links/create_service_spec.rb3
-rw-r--r--spec/services/issue_links/destroy_service_spec.rb3
-rw-r--r--spec/services/issue_links/list_service_spec.rb2
-rw-r--r--spec/services/issues/after_create_service_spec.rb9
-rw-r--r--spec/services/issues/base_service_spec.rb9
-rw-r--r--spec/services/issues/build_service_spec.rb36
-rw-r--r--spec/services/issues/clone_service_spec.rb2
-rw-r--r--spec/services/issues/close_service_spec.rb75
-rw-r--r--spec/services/issues/create_service_spec.rb77
-rw-r--r--spec/services/issues/duplicate_service_spec.rb2
-rw-r--r--spec/services/issues/import_csv_service_spec.rb3
-rw-r--r--spec/services/issues/issuable_base_service_spec.rb9
-rw-r--r--spec/services/issues/prepare_import_csv_service_spec.rb2
-rw-r--r--spec/services/issues/referenced_merge_requests_service_spec.rb2
-rw-r--r--spec/services/issues/related_branches_service_spec.rb2
-rw-r--r--spec/services/issues/relative_position_rebalancing_service_spec.rb2
-rw-r--r--spec/services/issues/reopen_service_spec.rb12
-rw-r--r--spec/services/issues/reorder_service_spec.rb2
-rw-r--r--spec/services/issues/resolve_discussions_spec.rb14
-rw-r--r--spec/services/issues/set_crm_contacts_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb101
-rw-r--r--spec/services/issues/zoom_link_service_spec.rb3
-rw-r--r--spec/services/jira/requests/projects/list_service_spec.rb2
-rw-r--r--spec/services/jira_connect/sync_service_spec.rb12
-rw-r--r--spec/services/jira_connect_installations/destroy_service_spec.rb2
-rw-r--r--spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb6
-rw-r--r--spec/services/jira_connect_subscriptions/create_service_spec.rb2
-rw-r--r--spec/services/jira_import/cloud_users_mapper_service_spec.rb2
-rw-r--r--spec/services/jira_import/server_users_mapper_service_spec.rb2
-rw-r--r--spec/services/jira_import/start_import_service_spec.rb2
-rw-r--r--spec/services/jira_import/users_importer_spec.rb2
-rw-r--r--spec/services/keys/create_service_spec.rb2
-rw-r--r--spec/services/keys/destroy_service_spec.rb2
-rw-r--r--spec/services/keys/expiry_notification_service_spec.rb2
-rw-r--r--spec/services/keys/last_used_service_spec.rb60
-rw-r--r--spec/services/keys/revoke_service_spec.rb13
-rw-r--r--spec/services/labels/available_labels_service_spec.rb2
-rw-r--r--spec/services/labels/create_service_spec.rb2
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb2
-rw-r--r--spec/services/labels/promote_service_spec.rb2
-rw-r--r--spec/services/labels/transfer_service_spec.rb2
-rw-r--r--spec/services/labels/update_service_spec.rb2
-rw-r--r--spec/services/lfs/lock_file_service_spec.rb2
-rw-r--r--spec/services/lfs/locks_finder_service_spec.rb2
-rw-r--r--spec/services/lfs/push_service_spec.rb2
-rw-r--r--spec/services/lfs/unlock_file_service_spec.rb2
-rw-r--r--spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb2
-rw-r--r--spec/services/loose_foreign_keys/cleaner_service_spec.rb2
-rw-r--r--spec/services/loose_foreign_keys/process_deleted_records_service_spec.rb2
-rw-r--r--spec/services/markdown_content_rewriter_service_spec.rb2
-rw-r--r--spec/services/markup/rendering_service_spec.rb19
-rw-r--r--spec/services/mattermost/create_team_service_spec.rb28
-rw-r--r--spec/services/members/approve_access_request_service_spec.rb20
-rw-r--r--spec/services/members/base_service_spec.rb5
-rw-r--r--spec/services/members/create_service_spec.rb3
-rw-r--r--spec/services/members/creator_service_spec.rb2
-rw-r--r--spec/services/members/destroy_service_spec.rb143
-rw-r--r--spec/services/members/groups/creator_service_spec.rb4
-rw-r--r--spec/services/members/import_project_team_service_spec.rb2
-rw-r--r--spec/services/members/invitation_reminder_email_service_spec.rb2
-rw-r--r--spec/services/members/invite_member_builder_spec.rb2
-rw-r--r--spec/services/members/invite_service_spec.rb3
-rw-r--r--spec/services/members/projects/creator_service_spec.rb4
-rw-r--r--spec/services/members/request_access_service_spec.rb2
-rw-r--r--spec/services/members/standard_member_builder_spec.rb2
-rw-r--r--spec/services/members/unassign_issuables_service_spec.rb2
-rw-r--r--spec/services/members/update_service_spec.rb2
-rw-r--r--spec/services/merge_requests/add_context_service_spec.rb2
-rw-r--r--spec/services/merge_requests/add_spent_time_service_spec.rb2
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb24
-rw-r--r--spec/services/merge_requests/after_create_service_spec.rb23
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb2
-rw-r--r--spec/services/merge_requests/assign_issues_service_spec.rb16
-rw-r--r--spec/services/merge_requests/base_service_spec.rb38
-rw-r--r--spec/services/merge_requests/cleanup_refs_service_spec.rb2
-rw-r--r--spec/services/merge_requests/close_service_spec.rb6
-rw-r--r--spec/services/merge_requests/conflicts/list_service_spec.rb2
-rw-r--r--spec/services/merge_requests/conflicts/resolve_service_spec.rb21
-rw-r--r--spec/services/merge_requests/create_approval_event_service_spec.rb2
-rw-r--r--spec/services/merge_requests/create_pipeline_service_spec.rb2
-rw-r--r--spec/services/merge_requests/create_service_spec.rb55
-rw-r--r--spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb3
-rw-r--r--spec/services/merge_requests/execute_approval_hooks_service_spec.rb2
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb14
-rw-r--r--spec/services/merge_requests/get_urls_service_spec.rb2
-rw-r--r--spec/services/merge_requests/handle_assignees_change_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_orchestration_service_spec.rb9
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb122
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb28
-rw-r--r--spec/services/merge_requests/mergeability/check_base_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/check_open_status_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb27
-rw-r--r--spec/services/merge_requests/mergeability/logger_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/run_checks_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability_check_service_spec.rb22
-rw-r--r--spec/services/merge_requests/migrate_external_diffs_service_spec.rb2
-rw-r--r--spec/services/merge_requests/post_merge_service_spec.rb8
-rw-r--r--spec/services/merge_requests/push_options_handler_service_spec.rb2
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb28
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb238
-rw-r--r--spec/services/merge_requests/reload_diffs_service_spec.rb10
-rw-r--r--spec/services/merge_requests/reload_merge_head_diff_service_spec.rb2
-rw-r--r--spec/services/merge_requests/reopen_service_spec.rb8
-rw-r--r--spec/services/merge_requests/request_review_service_spec.rb2
-rw-r--r--spec/services/merge_requests/resolve_todos_service_spec.rb2
-rw-r--r--spec/services/merge_requests/resolved_discussion_notification_service_spec.rb2
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb26
-rw-r--r--spec/services/merge_requests/update_assignees_service_spec.rb36
-rw-r--r--spec/services/merge_requests/update_reviewers_service_spec.rb20
-rw-r--r--spec/services/merge_requests/update_service_spec.rb65
-rw-r--r--spec/services/metrics/dashboard/annotations/create_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/annotations/delete_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/clone_dashboard_service_spec.rb6
-rw-r--r--spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/custom_dashboard_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/default_embed_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/dynamic_embed_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb2
-rw-r--r--spec/services/metrics/dashboard/panel_preview_service_spec.rb22
-rw-r--r--spec/services/metrics/dashboard/pod_dashboard_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb88
-rw-r--r--spec/services/metrics/dashboard/system_dashboard_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/transient_embed_service_spec.rb3
-rw-r--r--spec/services/metrics/dashboard/update_dashboard_service_spec.rb2
-rw-r--r--spec/services/metrics/global_metrics_update_service_spec.rb14
-rw-r--r--spec/services/metrics/sample_metrics_service_spec.rb2
-rw-r--r--spec/services/metrics/users_starred_dashboards/create_service_spec.rb2
-rw-r--r--spec/services/metrics/users_starred_dashboards/delete_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/milestones/closed_issues_count_service_spec.rb3
-rw-r--r--spec/services/milestones/create_service_spec.rb2
-rw-r--r--spec/services/milestones/destroy_service_spec.rb2
-rw-r--r--spec/services/milestones/find_or_create_service_spec.rb2
-rw-r--r--spec/services/milestones/issues_count_service_spec.rb3
-rw-r--r--spec/services/milestones/merge_requests_count_service_spec.rb3
-rw-r--r--spec/services/milestones/promote_service_spec.rb2
-rw-r--r--spec/services/milestones/transfer_service_spec.rb2
-rw-r--r--spec/services/milestones/update_service_spec.rb2
-rw-r--r--spec/services/ml/experiment_tracking/candidate_repository_spec.rb32
-rw-r--r--spec/services/ml/experiment_tracking/experiment_repository_spec.rb2
-rw-r--r--spec/services/ml/experiment_tracking/handle_candidate_gitlab_metadata_service_spec.rb40
-rw-r--r--spec/services/namespace_settings/update_service_spec.rb2
-rw-r--r--spec/services/namespaces/in_product_marketing_emails_service_spec.rb2
-rw-r--r--spec/services/namespaces/package_settings/update_service_spec.rb2
-rw-r--r--spec/services/namespaces/statistics_refresher_service_spec.rb2
-rw-r--r--spec/services/note_summary_spec.rb2
-rw-r--r--spec/services/notes/build_service_spec.rb12
-rw-r--r--spec/services/notes/copy_service_spec.rb8
-rw-r--r--spec/services/notes/create_service_spec.rb141
-rw-r--r--spec/services/notes/destroy_service_spec.rb14
-rw-r--r--spec/services/notes/post_process_service_spec.rb2
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb106
-rw-r--r--spec/services/notes/render_service_spec.rb16
-rw-r--r--spec/services/notes/resolve_service_spec.rb2
-rw-r--r--spec/services/notes/update_service_spec.rb4
-rw-r--r--spec/services/notification_recipients/build_service_spec.rb2
-rw-r--r--spec/services/notification_recipients/builder/default_spec.rb2
-rw-r--r--spec/services/notification_recipients/builder/new_note_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb34
-rw-r--r--spec/services/onboarding/progress_service_spec.rb2
-rw-r--r--spec/services/packages/cleanup/execute_policy_service_spec.rb2
-rw-r--r--spec/services/packages/cleanup/update_policy_service_spec.rb2
-rw-r--r--spec/services/packages/composer/composer_json_service_spec.rb2
-rw-r--r--spec/services/packages/composer/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/composer/version_parser_service_spec.rb2
-rw-r--r--spec/services/packages/conan/create_package_file_service_spec.rb2
-rw-r--r--spec/services/packages/conan/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/conan/search_service_spec.rb28
-rw-r--r--spec/services/packages/conan/single_package_search_service_spec.rb45
-rw-r--r--spec/services/packages/create_dependency_service_spec.rb2
-rw-r--r--spec/services/packages/create_event_service_spec.rb48
-rw-r--r--spec/services/packages/create_package_file_service_spec.rb2
-rw-r--r--spec/services/packages/create_temporary_package_service_spec.rb2
-rw-r--r--spec/services/packages/debian/create_package_file_service_spec.rb2
-rw-r--r--spec/services/packages/debian/extract_changes_metadata_service_spec.rb2
-rw-r--r--spec/services/packages/debian/extract_metadata_service_spec.rb86
-rw-r--r--spec/services/packages/debian/find_or_create_package_service_spec.rb21
-rw-r--r--spec/services/packages/debian/generate_distribution_service_spec.rb28
-rw-r--r--spec/services/packages/debian/parse_debian822_service_spec.rb20
-rw-r--r--spec/services/packages/debian/process_changes_service_spec.rb36
-rw-r--r--spec/services/packages/debian/process_package_file_service_spec.rb41
-rw-r--r--spec/services/packages/generic/create_package_file_service_spec.rb16
-rw-r--r--spec/services/packages/generic/find_or_create_package_service_spec.rb2
-rw-r--r--spec/services/packages/go/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/go/sync_packages_service_spec.rb2
-rw-r--r--spec/services/packages/helm/extract_file_metadata_service_spec.rb2
-rw-r--r--spec/services/packages/helm/process_file_service_spec.rb2
-rw-r--r--spec/services/packages/mark_package_files_for_destruction_service_spec.rb3
-rw-r--r--spec/services/packages/mark_package_for_destruction_service_spec.rb8
-rw-r--r--spec/services/packages/mark_packages_for_destruction_service_spec.rb7
-rw-r--r--spec/services/packages/maven/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/maven/find_or_create_package_service_spec.rb51
-rw-r--r--spec/services/packages/maven/metadata/append_package_file_service_spec.rb2
-rw-r--r--spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb2
-rw-r--r--spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb2
-rw-r--r--spec/services/packages/maven/metadata/sync_service_spec.rb2
-rw-r--r--spec/services/packages/npm/create_metadata_cache_service_spec.rb83
-rw-r--r--spec/services/packages/npm/create_package_service_spec.rb178
-rw-r--r--spec/services/packages/npm/create_tag_service_spec.rb2
-rw-r--r--spec/services/packages/npm/deprecate_package_service_spec.rb115
-rw-r--r--spec/services/packages/npm/generate_metadata_service_spec.rb173
-rw-r--r--spec/services/packages/nuget/create_dependency_service_spec.rb2
-rw-r--r--spec/services/packages/nuget/metadata_extraction_service_spec.rb2
-rw-r--r--spec/services/packages/nuget/search_service_spec.rb2
-rw-r--r--spec/services/packages/nuget/sync_metadatum_service_spec.rb2
-rw-r--r--spec/services/packages/nuget/update_package_from_metadata_service_spec.rb23
-rw-r--r--spec/services/packages/pypi/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/remove_tag_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/parse_package_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_filelist_xml_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_other_xml_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_primary_xml_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_repomd_xml_service_spec.rb2
-rw-r--r--spec/services/packages/rpm/repository_metadata/update_xml_service_spec.rb2
-rw-r--r--spec/services/packages/rubygems/create_dependencies_service_spec.rb2
-rw-r--r--spec/services/packages/rubygems/create_gemspec_service_spec.rb2
-rw-r--r--spec/services/packages/rubygems/dependency_resolver_service_spec.rb2
-rw-r--r--spec/services/packages/rubygems/metadata_extraction_service_spec.rb2
-rw-r--r--spec/services/packages/rubygems/process_gem_service_spec.rb2
-rw-r--r--spec/services/packages/terraform_module/create_package_service_spec.rb2
-rw-r--r--spec/services/packages/update_package_file_service_spec.rb2
-rw-r--r--spec/services/packages/update_tags_service_spec.rb2
-rw-r--r--spec/services/pages/delete_service_spec.rb2
-rw-r--r--spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb2
-rw-r--r--spec/services/pages/zip_directory_service_spec.rb2
-rw-r--r--spec/services/pages_domains/create_acme_order_service_spec.rb2
-rw-r--r--spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb2
-rw-r--r--spec/services/personal_access_tokens/create_service_spec.rb20
-rw-r--r--spec/services/personal_access_tokens/last_used_service_spec.rb2
-rw-r--r--spec/services/personal_access_tokens/revoke_service_spec.rb2
-rw-r--r--spec/services/personal_access_tokens/rotate_service_spec.rb67
-rw-r--r--spec/services/post_receive_service_spec.rb2
-rw-r--r--spec/services/preview_markdown_service_spec.rb12
-rw-r--r--spec/services/product_analytics/build_activity_graph_service_spec.rb2
-rw-r--r--spec/services/product_analytics/build_graph_service_spec.rb2
-rw-r--r--spec/services/projects/after_rename_service_spec.rb2
-rw-r--r--spec/services/projects/alerting/notify_service_spec.rb2
-rw-r--r--spec/services/projects/all_issues_count_service_spec.rb2
-rw-r--r--spec/services/projects/all_merge_requests_count_service_spec.rb17
-rw-r--r--spec/services/projects/android_target_platform_detector_service_spec.rb30
-rw-r--r--spec/services/projects/apple_target_platform_detector_service_spec.rb2
-rw-r--r--spec/services/projects/auto_devops/disable_service_spec.rb2
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb2
-rw-r--r--spec/services/projects/batch_open_issues_count_service_spec.rb2
-rw-r--r--spec/services/projects/batch_open_merge_requests_count_service_spec.rb32
-rw-r--r--spec/services/projects/blame_service_spec.rb131
-rw-r--r--spec/services/projects/branches_by_mode_service_spec.rb2
-rw-r--r--spec/services/projects/cleanup_service_spec.rb2
-rw-r--r--spec/services/projects/container_repository/cleanup_tags_service_spec.rb7
-rw-r--r--spec/services/projects/container_repository/delete_tags_service_spec.rb2
-rw-r--r--spec/services/projects/container_repository/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb54
-rw-r--r--spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb2
-rw-r--r--spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb174
-rw-r--r--spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb2
-rw-r--r--spec/services/projects/count_service_spec.rb2
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb2
-rw-r--r--spec/services/projects/create_service_spec.rb68
-rw-r--r--spec/services/projects/deploy_tokens/create_service_spec.rb2
-rw-r--r--spec/services/projects/deploy_tokens/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb17
-rw-r--r--spec/services/projects/detect_repository_languages_service_spec.rb2
-rw-r--r--spec/services/projects/download_service_spec.rb2
-rw-r--r--spec/services/projects/enable_deploy_key_service_spec.rb2
-rw-r--r--spec/services/projects/fetch_statistics_increment_service_spec.rb2
-rw-r--r--spec/services/projects/fork_service_spec.rb40
-rw-r--r--spec/services/projects/forks/sync_service_spec.rb185
-rw-r--r--spec/services/projects/forks_count_service_spec.rb2
-rw-r--r--spec/services/projects/git_deduplication_service_spec.rb4
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb2
-rw-r--r--spec/services/projects/group_links/create_service_spec.rb15
-rw-r--r--spec/services/projects/group_links/destroy_service_spec.rb11
-rw-r--r--spec/services/projects/group_links/update_service_spec.rb11
-rw-r--r--spec/services/projects/hashed_storage/base_attachment_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migration_service_spec.rb18
-rw-r--r--spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/rollback_repository_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/rollback_service_spec.rb2
-rw-r--r--spec/services/projects/import_error_filter_spec.rb2
-rw-r--r--spec/services/projects/import_export/relation_export_service_spec.rb4
-rw-r--r--spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb2
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb2
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_service_spec.rb2
-rw-r--r--spec/services/projects/lfs_pointers/lfs_import_service_spec.rb2
-rw-r--r--spec/services/projects/lfs_pointers/lfs_link_service_spec.rb35
-rw-r--r--spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb2
-rw-r--r--spec/services/projects/move_access_service_spec.rb2
-rw-r--r--spec/services/projects/move_deploy_keys_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_forks_service_spec.rb2
-rw-r--r--spec/services/projects/move_lfs_objects_projects_service_spec.rb2
-rw-r--r--spec/services/projects/move_notification_settings_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb2
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb2
-rw-r--r--spec/services/projects/move_users_star_projects_service_spec.rb2
-rw-r--r--spec/services/projects/open_issues_count_service_spec.rb2
-rw-r--r--spec/services/projects/open_merge_requests_count_service_spec.rb7
-rw-r--r--spec/services/projects/operations/update_service_spec.rb2
-rw-r--r--spec/services/projects/overwrite_project_service_spec.rb2
-rw-r--r--spec/services/projects/participants_service_spec.rb2
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb26
-rw-r--r--spec/services/projects/prometheus/metrics/destroy_service_spec.rb2
-rw-r--r--spec/services/projects/protect_default_branch_service_spec.rb4
-rw-r--r--spec/services/projects/readme_renderer_service_spec.rb4
-rw-r--r--spec/services/projects/record_target_platforms_service_spec.rb70
-rw-r--r--spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb2
-rw-r--r--spec/services/projects/repository_languages_service_spec.rb2
-rw-r--r--spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb52
-rw-r--r--spec/services/projects/unlink_fork_service_spec.rb8
-rw-r--r--spec/services/projects/update_pages_service_spec.rb68
-rw-r--r--spec/services/projects/update_remote_mirror_service_spec.rb2
-rw-r--r--spec/services/projects/update_repository_storage_service_spec.rb2
-rw-r--r--spec/services/projects/update_service_spec.rb247
-rw-r--r--spec/services/projects/update_statistics_service_spec.rb4
-rw-r--r--spec/services/prometheus/proxy_service_spec.rb2
-rw-r--r--spec/services/prometheus/proxy_variable_substitution_service_spec.rb2
-rw-r--r--spec/services/protected_branches/api_service_spec.rb2
-rw-r--r--spec/services/protected_branches/cache_service_spec.rb3
-rw-r--r--spec/services/protected_branches/destroy_service_spec.rb2
-rw-r--r--spec/services/protected_branches/update_service_spec.rb2
-rw-r--r--spec/services/protected_tags/create_service_spec.rb2
-rw-r--r--spec/services/protected_tags/destroy_service_spec.rb2
-rw-r--r--spec/services/protected_tags/update_service_spec.rb2
-rw-r--r--spec/services/push_event_payload_service_spec.rb2
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb267
-rw-r--r--spec/services/quick_actions/target_service_spec.rb2
-rw-r--r--spec/services/releases/create_evidence_service_spec.rb2
-rw-r--r--spec/services/releases/create_service_spec.rb20
-rw-r--r--spec/services/releases/destroy_service_spec.rb2
-rw-r--r--spec/services/releases/links/create_service_spec.rb84
-rw-r--r--spec/services/releases/links/destroy_service_spec.rb72
-rw-r--r--spec/services/releases/links/update_service_spec.rb89
-rw-r--r--spec/services/repositories/changelog_service_spec.rb2
-rw-r--r--spec/services/repositories/destroy_service_spec.rb2
-rw-r--r--spec/services/repository_archive_clean_up_service_spec.rb2
-rw-r--r--spec/services/reset_project_cache_service_spec.rb2
-rw-r--r--spec/services/resource_access_tokens/create_service_spec.rb108
-rw-r--r--spec/services/resource_access_tokens/revoke_service_spec.rb2
-rw-r--r--spec/services/resource_events/change_labels_service_spec.rb2
-rw-r--r--spec/services/resource_events/change_milestone_service_spec.rb2
-rw-r--r--spec/services/resource_events/change_state_service_spec.rb2
-rw-r--r--spec/services/resource_events/merge_into_notes_service_spec.rb2
-rw-r--r--spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb2
-rw-r--r--spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb8
-rw-r--r--spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb2
-rw-r--r--spec/services/search/global_service_spec.rb2
-rw-r--r--spec/services/search/group_service_spec.rb2
-rw-r--r--spec/services/search/snippet_service_spec.rb2
-rw-r--r--spec/services/security/ci_configuration/container_scanning_create_service_spec.rb2
-rw-r--r--spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb2
-rw-r--r--spec/services/security/ci_configuration/sast_iac_create_service_spec.rb2
-rw-r--r--spec/services/security/ci_configuration/sast_parser_service_spec.rb2
-rw-r--r--spec/services/security/ci_configuration/secret_detection_create_service_spec.rb2
-rw-r--r--spec/services/security/merge_reports_service_spec.rb2
-rw-r--r--spec/services/serverless/associate_domain_service_spec.rb91
-rw-r--r--spec/services/service_desk_settings/update_service_spec.rb2
-rw-r--r--spec/services/service_ping/submit_service_ping_service_spec.rb2
-rw-r--r--spec/services/service_response_spec.rb2
-rw-r--r--spec/services/snippets/bulk_destroy_service_spec.rb2
-rw-r--r--spec/services/snippets/count_service_spec.rb2
-rw-r--r--spec/services/snippets/create_service_spec.rb2
-rw-r--r--spec/services/snippets/destroy_service_spec.rb4
-rw-r--r--spec/services/snippets/repository_validation_service_spec.rb2
-rw-r--r--spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb2
-rw-r--r--spec/services/snippets/update_repository_storage_service_spec.rb2
-rw-r--r--spec/services/snippets/update_service_spec.rb2
-rw-r--r--spec/services/snippets/update_statistics_service_spec.rb2
-rw-r--r--spec/services/spam/akismet_mark_as_spam_service_spec.rb2
-rw-r--r--spec/services/spam/akismet_service_spec.rb2
-rw-r--r--spec/services/spam/ham_service_spec.rb2
-rw-r--r--spec/services/spam/spam_action_service_spec.rb32
-rw-r--r--spec/services/spam/spam_params_spec.rb2
-rw-r--r--spec/services/spam/spam_verdict_service_spec.rb255
-rw-r--r--spec/services/submodules/update_service_spec.rb2
-rw-r--r--spec/services/suggestions/apply_service_spec.rb2
-rw-r--r--spec/services/suggestions/create_service_spec.rb2
-rw-r--r--spec/services/suggestions/outdate_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb2
-rw-r--r--spec/services/system_note_service_spec.rb4
-rw-r--r--spec/services/system_notes/alert_management_service_spec.rb2
-rw-r--r--spec/services/system_notes/base_service_spec.rb2
-rw-r--r--spec/services/system_notes/commit_service_spec.rb82
-rw-r--r--spec/services/system_notes/design_management_service_spec.rb2
-rw-r--r--spec/services/system_notes/incident_service_spec.rb2
-rw-r--r--spec/services/system_notes/incidents_service_spec.rb2
-rw-r--r--spec/services/system_notes/issuables_service_spec.rb26
-rw-r--r--spec/services/system_notes/merge_requests_service_spec.rb2
-rw-r--r--spec/services/system_notes/time_tracking_service_spec.rb8
-rw-r--r--spec/services/system_notes/zoom_service_spec.rb2
-rw-r--r--spec/services/tags/create_service_spec.rb2
-rw-r--r--spec/services/tags/destroy_service_spec.rb2
-rw-r--r--spec/services/task_list_toggle_service_spec.rb21
-rw-r--r--spec/services/tasks_to_be_done/base_service_spec.rb6
-rw-r--r--spec/services/terraform/remote_state_handler_spec.rb3
-rw-r--r--spec/services/terraform/states/destroy_service_spec.rb2
-rw-r--r--spec/services/terraform/states/trigger_destroy_service_spec.rb2
-rw-r--r--spec/services/test_hooks/project_service_spec.rb2
-rw-r--r--spec/services/test_hooks/system_service_spec.rb2
-rw-r--r--spec/services/timelogs/delete_service_spec.rb2
-rw-r--r--spec/services/todo_service_spec.rb102
-rw-r--r--spec/services/todos/allowed_target_filter_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/confidential_issue_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/design_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/destroyed_issuable_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/project_private_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/unauthorized_features_service_spec.rb2
-rw-r--r--spec/services/topics/merge_service_spec.rb4
-rw-r--r--spec/services/two_factor/destroy_service_spec.rb2
-rw-r--r--spec/services/update_container_registry_info_service_spec.rb2
-rw-r--r--spec/services/update_merge_request_metrics_service_spec.rb2
-rw-r--r--spec/services/upload_service_spec.rb2
-rw-r--r--spec/services/uploads/destroy_service_spec.rb2
-rw-r--r--spec/services/user_preferences/update_service_spec.rb6
-rw-r--r--spec/services/user_project_access_changed_service_spec.rb2
-rw-r--r--spec/services/users/activity_service_spec.rb3
-rw-r--r--spec/services/users/approve_service_spec.rb20
-rw-r--r--spec/services/users/authorized_build_service_spec.rb2
-rw-r--r--spec/services/users/ban_service_spec.rb2
-rw-r--r--spec/services/users/banned_user_base_service_spec.rb2
-rw-r--r--spec/services/users/batch_status_cleaner_service_spec.rb2
-rw-r--r--spec/services/users/block_service_spec.rb2
-rw-r--r--spec/services/users/build_service_spec.rb2
-rw-r--r--spec/services/users/create_service_spec.rb2
-rw-r--r--spec/services/users/deactivate_service_spec.rb86
-rw-r--r--spec/services/users/destroy_service_spec.rb2
-rw-r--r--spec/services/users/dismiss_callout_service_spec.rb2
-rw-r--r--spec/services/users/dismiss_group_callout_service_spec.rb2
-rw-r--r--spec/services/users/dismiss_project_callout_service_spec.rb2
-rw-r--r--spec/services/users/email_verification/generate_token_service_spec.rb23
-rw-r--r--spec/services/users/email_verification/validate_token_service_spec.rb7
-rw-r--r--spec/services/users/in_product_marketing_email_records_spec.rb2
-rw-r--r--spec/services/users/keys_count_service_spec.rb2
-rw-r--r--spec/services/users/last_push_event_service_spec.rb2
-rw-r--r--spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb2
-rw-r--r--spec/services/users/migrate_records_to_ghost_user_service_spec.rb2
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb2
-rw-r--r--spec/services/users/registrations_build_service_spec.rb2
-rw-r--r--spec/services/users/reject_service_spec.rb2
-rw-r--r--spec/services/users/repair_ldap_blocked_service_spec.rb2
-rw-r--r--spec/services/users/respond_to_terms_service_spec.rb2
-rw-r--r--spec/services/users/saved_replies/create_service_spec.rb2
-rw-r--r--spec/services/users/saved_replies/destroy_service_spec.rb2
-rw-r--r--spec/services/users/saved_replies/update_service_spec.rb2
-rw-r--r--spec/services/users/set_status_service_spec.rb2
-rw-r--r--spec/services/users/signup_service_spec.rb8
-rw-r--r--spec/services/users/unban_service_spec.rb2
-rw-r--r--spec/services/users/unblock_service_spec.rb2
-rw-r--r--spec/services/users/update_canonical_email_service_spec.rb28
-rw-r--r--spec/services/users/update_highest_member_role_service_spec.rb2
-rw-r--r--spec/services/users/update_service_spec.rb48
-rw-r--r--spec/services/users/update_todo_count_cache_service_spec.rb2
-rw-r--r--spec/services/users/upsert_credit_card_validation_service_spec.rb10
-rw-r--r--spec/services/users/validate_manual_otp_service_spec.rb33
-rw-r--r--spec/services/users/validate_push_otp_service_spec.rb2
-rw-r--r--spec/services/verify_pages_domain_service_spec.rb2
-rw-r--r--spec/services/web_hooks/destroy_service_spec.rb2
-rw-r--r--spec/services/web_hooks/log_destroy_service_spec.rb2
-rw-r--r--spec/services/web_hooks/log_execution_service_spec.rb2
-rw-r--r--spec/services/webauthn/authenticate_service_spec.rb2
-rw-r--r--spec/services/webauthn/register_service_spec.rb2
-rw-r--r--spec/services/wiki_pages/base_service_spec.rb2
-rw-r--r--spec/services/wiki_pages/create_service_spec.rb2
-rw-r--r--spec/services/wiki_pages/destroy_service_spec.rb2
-rw-r--r--spec/services/wiki_pages/event_create_service_spec.rb2
-rw-r--r--spec/services/wiki_pages/update_service_spec.rb2
-rw-r--r--spec/services/wikis/create_attachment_service_spec.rb2
-rw-r--r--spec/services/work_items/build_service_spec.rb2
-rw-r--r--spec/services/work_items/create_from_task_service_spec.rb2
-rw-r--r--spec/services/work_items/create_service_spec.rb335
-rw-r--r--spec/services/work_items/delete_service_spec.rb2
-rw-r--r--spec/services/work_items/delete_task_service_spec.rb2
-rw-r--r--spec/services/work_items/export_csv_service_spec.rb28
-rw-r--r--spec/services/work_items/import_csv_service_spec.rb122
-rw-r--r--spec/services/work_items/parent_links/base_service_spec.rb31
-rw-r--r--spec/services/work_items/parent_links/create_service_spec.rb94
-rw-r--r--spec/services/work_items/parent_links/destroy_service_spec.rb38
-rw-r--r--spec/services/work_items/parent_links/reorder_service_spec.rb176
-rw-r--r--spec/services/work_items/prepare_import_csv_service_spec.rb52
-rw-r--r--spec/services/work_items/task_list_reference_removal_service_spec.rb2
-rw-r--r--spec/services/work_items/task_list_reference_replacement_service_spec.rb2
-rw-r--r--spec/services/work_items/update_service_spec.rb29
-rw-r--r--spec/services/work_items/widgets/assignees_service/update_service_spec.rb24
-rw-r--r--spec/services/work_items/widgets/award_emoji_service/update_service_spec.rb96
-rw-r--r--spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb106
-rw-r--r--spec/services/work_items/widgets/description_service/update_service_spec.rb23
-rw-r--r--spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb31
-rw-r--r--spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb98
-rw-r--r--spec/services/work_items/widgets/labels_service/update_service_spec.rb48
-rw-r--r--spec/services/work_items/widgets/milestone_service/create_service_spec.rb28
-rw-r--r--spec/services/work_items/widgets/milestone_service/update_service_spec.rb58
-rw-r--r--spec/services/work_items/widgets/notifications_service/update_service_spec.rb117
-rw-r--r--spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb24
-rw-r--r--spec/services/x509_certificate_revoke_service_spec.rb2
869 files changed, 11783 insertions, 4173 deletions
diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb
index 2bf74d64dc9..4cdce094358 100644
--- a/spec/services/access_token_validation_service_spec.rb
+++ b/spec/services/access_token_validation_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AccessTokenValidationService do
+RSpec.describe AccessTokenValidationService, feature_category: :system_access do
describe ".include_any_scope?" do
let(:request) { double("request") }
diff --git a/spec/services/achievements/award_service_spec.rb b/spec/services/achievements/award_service_spec.rb
new file mode 100644
index 00000000000..c70c1d5c22d
--- /dev/null
+++ b/spec/services/achievements/award_service_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Achievements::AwardService, feature_category: :user_profile do
+ describe '#execute' do
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:recipient) { create(:user) }
+
+ let(:achievement_id) { achievement.id }
+ let(:recipient_id) { recipient.id }
+
+ subject(:response) { described_class.new(current_user, achievement_id, recipient_id).execute }
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when user does not have permission' do
+ let(:current_user) { developer }
+
+ it 'returns an error' do
+ expect(response).to be_error
+ expect(response.message).to match_array(
+ ['You have insufficient permissions to award this achievement'])
+ end
+ end
+
+ context 'when user has permission' do
+ let(:current_user) { maintainer }
+ let(:notification_service) { instance_double(NotificationService) }
+ let(:mail_message) { instance_double(ActionMailer::MessageDelivery) }
+
+ it 'creates an achievement and sends an e-mail' do
+ allow(NotificationService).to receive(:new).and_return(notification_service)
+ expect(notification_service).to receive(:new_achievement_email).with(recipient, achievement)
+ .and_return(mail_message)
+ expect(mail_message).to receive(:deliver_later)
+
+ expect(response).to be_success
+ end
+
+ context 'when the achievement is not persisted' do
+ let(:user_achievement) { instance_double('Achievements::UserAchievement') }
+
+ it 'returns the correct error' do
+ allow(user_achievement).to receive(:persisted?).and_return(false)
+ allow(user_achievement).to receive(:errors).and_return(nil)
+ allow(Achievements::UserAchievement).to receive(:create).and_return(user_achievement)
+
+ expect(response).to be_error
+ expect(response.message).to match_array(["Failed to award achievement"])
+ end
+ end
+
+ context 'when the achievement does not exist' do
+ let(:achievement_id) { non_existing_record_id }
+
+ it 'returns the correct error' do
+ expect(response).to be_error
+ expect(response.message)
+ .to contain_exactly("Couldn't find Achievements::Achievement with 'id'=#{non_existing_record_id}")
+ end
+ end
+
+ context 'when the recipient does not exist' do
+ let(:recipient_id) { non_existing_record_id }
+
+ it 'returns the correct error' do
+ expect(response).to be_error
+ expect(response.message).to contain_exactly("Couldn't find User with 'id'=#{non_existing_record_id}")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/achievements/destroy_service_spec.rb b/spec/services/achievements/destroy_service_spec.rb
new file mode 100644
index 00000000000..7af10ceec6a
--- /dev/null
+++ b/spec/services/achievements/destroy_service_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Achievements::DestroyService, feature_category: :user_profile do
+ describe '#execute' do
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+
+ let(:achievement) { create(:achievement, namespace: group) }
+
+ subject(:response) { described_class.new(current_user, achievement).execute }
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when user does not have permission' do
+ let(:current_user) { developer }
+
+ it 'returns an error' do
+ expect(response).to be_error
+ expect(response.message).to match_array(
+ ['You have insufficient permissions to delete this achievement'])
+ end
+ end
+
+ context 'when user has permission' do
+ let(:current_user) { maintainer }
+
+ it 'deletes the achievement' do
+ expect(response).to be_success
+ expect(Achievements::Achievement.find_by(id: achievement.id)).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/services/achievements/revoke_service_spec.rb b/spec/services/achievements/revoke_service_spec.rb
new file mode 100644
index 00000000000..c9925f1e3df
--- /dev/null
+++ b/spec/services/achievements/revoke_service_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Achievements::RevokeService, feature_category: :user_profile do
+ describe '#execute' do
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+ let_it_be(:user_achievement) { create(:user_achievement, achievement: achievement) }
+
+ let(:user_achievement_param) { user_achievement }
+
+ subject(:response) { described_class.new(current_user, user_achievement_param).execute }
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when user does not have permission' do
+ let(:current_user) { developer }
+
+ it 'returns an error' do
+ expect(response).to be_error
+ expect(response.message).to match_array(
+ ['You have insufficient permissions to revoke this achievement'])
+ end
+ end
+
+ context 'when user has permission' do
+ let(:current_user) { maintainer }
+
+ it 'revokes an achievement' do
+ expect(response).to be_success
+ end
+
+ context 'when the achievement has already been revoked' do
+ let_it_be(:revoked_achievement) { create(:user_achievement, :revoked, achievement: achievement) }
+ let(:user_achievement_param) { revoked_achievement }
+
+ it 'returns the correct error' do
+ expect(response).to be_error
+ expect(response.message)
+ .to contain_exactly('This achievement has already been revoked')
+ end
+ end
+
+ context 'when the user achievement fails to save' do
+ let(:user_achievement_param) { instance_double('Achievements::UserAchievement') }
+
+ it 'returns the correct error' do
+ allow(user_achievement_param).to receive(:save).and_return(false)
+ allow(user_achievement_param).to receive(:achievement).and_return(achievement)
+ allow(user_achievement_param).to receive(:revoked?).and_return(false)
+ allow(user_achievement_param).to receive(:errors).and_return(nil)
+ expect(user_achievement_param).to receive(:assign_attributes)
+
+ expect(response).to be_error
+ expect(response.message).to match_array(["Failed to revoke achievement"])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/achievements/update_service_spec.rb b/spec/services/achievements/update_service_spec.rb
new file mode 100644
index 00000000000..6168d60450b
--- /dev/null
+++ b/spec/services/achievements/update_service_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Achievements::UpdateService, feature_category: :user_profile do
+ describe '#execute' do
+ let_it_be(:user) { create(:user) }
+
+ let(:params) { attributes_for(:achievement, namespace: group) }
+
+ subject(:response) { described_class.new(user, group, params).execute }
+
+ context 'when user does not have permission' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+
+ before_all do
+ group.add_developer(user)
+ end
+
+ it 'returns an error' do
+ expect(response).to be_error
+ expect(response.message).to match_array(
+ ['You have insufficient permission to update this achievement'])
+ end
+ end
+
+ context 'when user has permission' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:achievement) { create(:achievement, namespace: group) }
+
+ before_all do
+ group.add_maintainer(user)
+ end
+
+ it 'updates an achievement' do
+ expect(response).to be_success
+ end
+
+ it 'returns an error when the achievement cannot be updated' do
+ params[:name] = nil
+
+ expect(response).to be_error
+ expect(response.message).to include("Name can't be blank")
+ end
+ end
+ end
+end
diff --git a/spec/services/admin/abuse_report_update_service_spec.rb b/spec/services/admin/abuse_report_update_service_spec.rb
new file mode 100644
index 00000000000..e85b516b87f
--- /dev/null
+++ b/spec/services/admin/abuse_report_update_service_spec.rb
@@ -0,0 +1,199 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Admin::AbuseReportUpdateService, feature_category: :instance_resiliency do
+ let_it_be_with_reload(:abuse_report) { create(:abuse_report) }
+ let(:action) { 'ban_user' }
+ let(:close) { true }
+ let(:reason) { 'spam' }
+ let(:params) { { user_action: action, close: close, reason: reason, comment: 'obvious spam' } }
+ let_it_be(:admin) { create(:admin) }
+
+ let(:service) { described_class.new(abuse_report, admin, params) }
+
+ describe '#execute', :enable_admin_mode do
+ subject { service.execute }
+
+ shared_examples 'returns an error response' do |error|
+ it 'returns an error response' do
+ expect(subject.status).to eq :error
+ expect(subject.message).to eq error
+ end
+ end
+
+ shared_examples 'closes the report' do
+ it 'closes the report' do
+ expect { subject }.to change { abuse_report.closed? }.from(false).to(true)
+ end
+ end
+
+ shared_examples 'does not close the report' do
+ it 'does not close the report' do
+ subject
+ expect(abuse_report.closed?).to be(false)
+ end
+ end
+
+ shared_examples 'does not record an event' do
+ it 'does not record an event' do
+ expect { subject }.not_to change { abuse_report.events.count }
+ end
+ end
+
+ shared_examples 'records an event' do |action:|
+ it 'records the event', :aggregate_failures do
+ expect { subject }.to change { abuse_report.events.count }.by(1)
+
+ expect(abuse_report.events.last).to have_attributes(
+ action: action,
+ user: admin,
+ reason: reason,
+ comment: params[:comment]
+ )
+ end
+ end
+
+ context 'when invalid parameters are given' do
+ describe 'invalid user' do
+ describe 'when no user is given' do
+ let_it_be(:admin) { nil }
+
+ it_behaves_like 'returns an error response', 'Admin is required'
+ end
+
+ describe 'when given user is no admin' do
+ let_it_be(:admin) { create(:user) }
+
+ it_behaves_like 'returns an error response', 'Admin is required'
+ end
+ end
+
+ describe 'invalid action' do
+ describe 'when no action is given' do
+ let(:action) { '' }
+ let(:close) { 'false' }
+
+ it_behaves_like 'returns an error response', 'Action is required'
+ end
+
+ describe 'when unknown action is given' do
+ let(:action) { 'unknown' }
+ let(:close) { 'false' }
+
+ it_behaves_like 'returns an error response', 'Action is required'
+ end
+ end
+
+ describe 'invalid reason' do
+ let(:reason) { '' }
+
+ it 'sets the reason to `other`' do
+ subject
+
+ expect(abuse_report.events.last).to have_attributes(reason: 'other')
+ end
+ end
+ end
+
+ describe 'when banning the user' do
+ it 'calls the Users::BanService' do
+ expect_next_instance_of(Users::BanService, admin) do |service|
+ expect(service).to receive(:execute).with(abuse_report.user).and_return(status: :success)
+ end
+
+ subject
+ end
+
+ context 'when closing the report' do
+ it_behaves_like 'closes the report'
+ it_behaves_like 'records an event', action: 'ban_user_and_close_report'
+ end
+
+ context 'when not closing the report' do
+ let(:close) { 'false' }
+
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'records an event', action: 'ban_user'
+ end
+
+ context 'when banning the user fails' do
+ before do
+ allow_next_instance_of(Users::BanService, admin) do |service|
+ allow(service).to receive(:execute).with(abuse_report.user)
+ .and_return(status: :error, message: 'Banning the user failed')
+ end
+ end
+
+ it_behaves_like 'returns an error response', 'Banning the user failed'
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'does not record an event'
+ end
+ end
+
+ describe 'when blocking the user' do
+ let(:action) { 'block_user' }
+
+ it 'calls the Users::BlockService' do
+ expect_next_instance_of(Users::BlockService, admin) do |service|
+ expect(service).to receive(:execute).with(abuse_report.user).and_return(status: :success)
+ end
+
+ subject
+ end
+
+ context 'when closing the report' do
+ it_behaves_like 'closes the report'
+ it_behaves_like 'records an event', action: 'block_user_and_close_report'
+ end
+
+ context 'when not closing the report' do
+ let(:close) { 'false' }
+
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'records an event', action: 'block_user'
+ end
+
+ context 'when blocking the user fails' do
+ before do
+ allow_next_instance_of(Users::BlockService, admin) do |service|
+ allow(service).to receive(:execute).with(abuse_report.user)
+ .and_return(status: :error, message: 'Blocking the user failed')
+ end
+ end
+
+ it_behaves_like 'returns an error response', 'Blocking the user failed'
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'does not record an event'
+ end
+ end
+
+ describe 'when deleting the user' do
+ let(:action) { 'delete_user' }
+
+ it 'calls the delete_async method' do
+ expect(abuse_report.user).to receive(:delete_async).with(deleted_by: admin)
+ subject
+ end
+
+ context 'when closing the report' do
+ it_behaves_like 'closes the report'
+ it_behaves_like 'records an event', action: 'delete_user_and_close_report'
+ end
+
+ context 'when not closing the report' do
+ let(:close) { 'false' }
+
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'records an event', action: 'delete_user'
+ end
+ end
+
+ describe 'when only closing the report' do
+ let(:action) { '' }
+
+ it_behaves_like 'closes the report'
+ it_behaves_like 'records an event', action: 'close_report'
+ end
+ end
+end
diff --git a/spec/services/admin/set_feature_flag_service_spec.rb b/spec/services/admin/set_feature_flag_service_spec.rb
index 45ee914558a..e66802f6332 100644
--- a/spec/services/admin/set_feature_flag_service_spec.rb
+++ b/spec/services/admin/set_feature_flag_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::SetFeatureFlagService do
+RSpec.describe Admin::SetFeatureFlagService, feature_category: :feature_flags do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/alert_management/alerts/todo/create_service_spec.rb b/spec/services/alert_management/alerts/todo/create_service_spec.rb
index fa4fd8ed0b2..fd81c0893ed 100644
--- a/spec/services/alert_management/alerts/todo/create_service_spec.rb
+++ b/spec/services/alert_management/alerts/todo/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::Alerts::Todo::CreateService do
+RSpec.describe AlertManagement::Alerts::Todo::CreateService, feature_category: :incident_management do
let_it_be(:user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert) }
diff --git a/spec/services/alert_management/alerts/update_service_spec.rb b/spec/services/alert_management/alerts/update_service_spec.rb
index 8375c8cdf7d..69e2f2de291 100644
--- a/spec/services/alert_management/alerts/update_service_spec.rb
+++ b/spec/services/alert_management/alerts/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::Alerts::UpdateService do
+RSpec.describe AlertManagement::Alerts::UpdateService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:other_user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
diff --git a/spec/services/alert_management/create_alert_issue_service_spec.rb b/spec/services/alert_management/create_alert_issue_service_spec.rb
index 7255a722d26..b8d93f99ae4 100644
--- a/spec/services/alert_management/create_alert_issue_service_spec.rb
+++ b/spec/services/alert_management/create_alert_issue_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::CreateAlertIssueService do
+RSpec.describe AlertManagement::CreateAlertIssueService, feature_category: :incident_management do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/services/alert_management/http_integrations/create_service_spec.rb b/spec/services/alert_management/http_integrations/create_service_spec.rb
index ac5c62caf84..5200ec27dd1 100644
--- a/spec/services/alert_management/http_integrations/create_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::HttpIntegrations::CreateService do
+RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be_with_reload(:project) { create(:project) }
diff --git a/spec/services/alert_management/http_integrations/destroy_service_spec.rb b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
index cd949d728de..a8e9746cb85 100644
--- a/spec/services/alert_management/http_integrations/destroy_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::HttpIntegrations::DestroyService do
+RSpec.describe AlertManagement::HttpIntegrations::DestroyService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/alert_management/http_integrations/update_service_spec.rb b/spec/services/alert_management/http_integrations/update_service_spec.rb
index 94c34d9a29c..3f1a0967aa9 100644
--- a/spec/services/alert_management/http_integrations/update_service_spec.rb
+++ b/spec/services/alert_management/http_integrations/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::HttpIntegrations::UpdateService do
+RSpec.describe AlertManagement::HttpIntegrations::UpdateService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/alert_management/metric_images/upload_service_spec.rb b/spec/services/alert_management/metric_images/upload_service_spec.rb
index 527d9db0fd9..2cafd2c9029 100644
--- a/spec/services/alert_management/metric_images/upload_service_spec.rb
+++ b/spec/services/alert_management/metric_images/upload_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::MetricImages::UploadService do
+RSpec.describe AlertManagement::MetricImages::UploadService, feature_category: :metrics do
subject(:service) { described_class.new(alert, current_user, params) }
let_it_be_with_refind(:project) { create(:project) }
diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
index ae52a09be48..eb5f3808021 100644
--- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb
+++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AlertManagement::ProcessPrometheusAlertService do
+RSpec.describe AlertManagement::ProcessPrometheusAlertService, feature_category: :incident_management do
let_it_be(:project, reload: true) { create(:project, :repository) }
let(:service) { described_class.new(project, payload) }
diff --git a/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb b/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb
index 7bfae0cd9fc..c39965a2799 100644
--- a/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb
+++ b/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Analytics::CycleAnalytics::Stages::ListService do
+RSpec.describe Analytics::CycleAnalytics::Stages::ListService, feature_category: :value_stream_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:project_namespace) { project.project_namespace.reload }
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index e20d59fb0ef..79d4fc67538 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe ApplicationSettings::UpdateService do
end
end
- describe 'markdown cache invalidators' do
+ describe 'markdown cache invalidators', feature_category: :team_planning do
shared_examples 'invalidates markdown cache' do |attribute|
let(:params) { attribute }
@@ -144,7 +144,7 @@ RSpec.describe ApplicationSettings::UpdateService do
end
end
- describe 'performance bar settings' do
+ describe 'performance bar settings', feature_category: :application_performance do
using RSpec::Parameterized::TableSyntax
where(:params_performance_bar_enabled,
@@ -247,7 +247,7 @@ RSpec.describe ApplicationSettings::UpdateService do
end
end
- context 'when external authorization is enabled' do
+ context 'when external authorization is enabled', feature_category: :system_access do
before do
enable_external_authorization_service_check
end
diff --git a/spec/services/audit_event_service_spec.rb b/spec/services/audit_event_service_spec.rb
index 4f8b90fcb4a..8a20f7775eb 100644
--- a/spec/services/audit_event_service_spec.rb
+++ b/spec/services/audit_event_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuditEventService, :with_license do
+RSpec.describe AuditEventService, :with_license, feature_category: :audit_events do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, :with_sign_ins) }
let_it_be(:project_member) { create(:project_member, user: user) }
diff --git a/spec/services/audit_events/build_service_spec.rb b/spec/services/audit_events/build_service_spec.rb
index caf405a53aa..575ec9e58b8 100644
--- a/spec/services/audit_events/build_service_spec.rb
+++ b/spec/services/audit_events/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuditEvents::BuildService do
+RSpec.describe AuditEvents::BuildService, feature_category: :audit_events do
let(:author) { build_stubbed(:author, current_sign_in_ip: '127.0.0.1') }
let(:deploy_token) { build_stubbed(:deploy_token, user: author) }
let(:scope) { build_stubbed(:group) }
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index ba7acd3d3df..90aba1ae54c 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Auth::ContainerRegistryAuthenticationService do
+RSpec.describe Auth::ContainerRegistryAuthenticationService, feature_category: :container_registry do
include AdminModeHelper
it_behaves_like 'a container registry auth service'
diff --git a/spec/services/auth/dependency_proxy_authentication_service_spec.rb b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
index 667f361dc34..8f92fbe272c 100644
--- a/spec/services/auth/dependency_proxy_authentication_service_spec.rb
+++ b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Auth::DependencyProxyAuthenticationService do
+RSpec.describe Auth::DependencyProxyAuthenticationService, feature_category: :dependency_proxy do
let_it_be(:user) { create(:user) }
let(:service) { Auth::DependencyProxyAuthenticationService.new(nil, user) }
diff --git a/spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb b/spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb
index 691fb3f60f4..e8f86b4d7c5 100644
--- a/spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb
+++ b/spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService do
+RSpec.describe AuthorizedProjectUpdate::FindRecordsDueForRefreshService, feature_category: :projects do
# We're using let! here so that any expectations for the service class are not
# triggered twice.
let!(:project) { create(:project) }
diff --git a/spec/services/authorized_project_update/periodic_recalculate_service_spec.rb b/spec/services/authorized_project_update/periodic_recalculate_service_spec.rb
index 782f6858870..51cab6d188b 100644
--- a/spec/services/authorized_project_update/periodic_recalculate_service_spec.rb
+++ b/spec/services/authorized_project_update/periodic_recalculate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuthorizedProjectUpdate::PeriodicRecalculateService do
+RSpec.describe AuthorizedProjectUpdate::PeriodicRecalculateService, feature_category: :projects do
subject(:service) { described_class.new }
describe '#execute' do
diff --git a/spec/services/authorized_project_update/project_access_changed_service_spec.rb b/spec/services/authorized_project_update/project_access_changed_service_spec.rb
index da428bece20..7c09d7755ca 100644
--- a/spec/services/authorized_project_update/project_access_changed_service_spec.rb
+++ b/spec/services/authorized_project_update/project_access_changed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuthorizedProjectUpdate::ProjectAccessChangedService do
+RSpec.describe AuthorizedProjectUpdate::ProjectAccessChangedService, feature_category: :projects do
describe '#execute' do
it 'executes projects_authorizations refresh' do
expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:bulk_perform_async)
diff --git a/spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb b/spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb
index 62862d0e558..7b2dd52810f 100644
--- a/spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb
+++ b/spec/services/authorized_project_update/project_recalculate_per_user_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuthorizedProjectUpdate::ProjectRecalculatePerUserService, '#execute' do
+RSpec.describe AuthorizedProjectUpdate::ProjectRecalculatePerUserService, '#execute', feature_category: :projects do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:another_user) { create(:user) }
diff --git a/spec/services/authorized_project_update/project_recalculate_service_spec.rb b/spec/services/authorized_project_update/project_recalculate_service_spec.rb
index c339faaeabf..8360f3c67ab 100644
--- a/spec/services/authorized_project_update/project_recalculate_service_spec.rb
+++ b/spec/services/authorized_project_update/project_recalculate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuthorizedProjectUpdate::ProjectRecalculateService, '#execute' do
+RSpec.describe AuthorizedProjectUpdate::ProjectRecalculateService, '#execute', feature_category: :projects do
let_it_be(:project) { create(:project) }
subject(:execute) { described_class.new(project).execute }
diff --git a/spec/services/auto_merge/base_service_spec.rb b/spec/services/auto_merge/base_service_spec.rb
index 6c804a14620..7afe5d406ba 100644
--- a/spec/services/auto_merge/base_service_spec.rb
+++ b/spec/services/auto_merge/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AutoMerge::BaseService do
+RSpec.describe AutoMerge::BaseService, feature_category: :code_review_workflow do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:service) { described_class.new(project, user, params) }
diff --git a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
index 676f55be28a..a0b22267960 100644
--- a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AutoMerge::MergeWhenPipelineSucceedsService do
+RSpec.describe AutoMerge::MergeWhenPipelineSucceedsService, feature_category: :code_review_workflow do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/services/auto_merge_service_spec.rb b/spec/services/auto_merge_service_spec.rb
index 7584e44152e..94f4b414dca 100644
--- a/spec/services/auto_merge_service_spec.rb
+++ b/spec/services/auto_merge_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AutoMergeService do
+RSpec.describe AutoMergeService, feature_category: :code_review_workflow do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/award_emojis/add_service_spec.rb b/spec/services/award_emojis/add_service_spec.rb
index 0fbb785e2d6..99dbe6dc606 100644
--- a/spec/services/award_emojis/add_service_spec.rb
+++ b/spec/services/award_emojis/add_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::AddService do
+RSpec.describe AwardEmojis::AddService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:awardable) { create(:note, project: project) }
diff --git a/spec/services/award_emojis/base_service_spec.rb b/spec/services/award_emojis/base_service_spec.rb
index e0c8fd39ad9..f1ee4d1cfb8 100644
--- a/spec/services/award_emojis/base_service_spec.rb
+++ b/spec/services/award_emojis/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::BaseService do
+RSpec.describe AwardEmojis::BaseService, feature_category: :team_planning do
let(:awardable) { build(:note) }
let(:current_user) { build(:user) }
diff --git a/spec/services/award_emojis/collect_user_emoji_service_spec.rb b/spec/services/award_emojis/collect_user_emoji_service_spec.rb
index bf5aa0eb9ef..d75d5804f93 100644
--- a/spec/services/award_emojis/collect_user_emoji_service_spec.rb
+++ b/spec/services/award_emojis/collect_user_emoji_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::CollectUserEmojiService do
+RSpec.describe AwardEmojis::CollectUserEmojiService, feature_category: :team_planning do
describe '#execute' do
it 'returns an Array containing the awarded emoji names' do
user = create(:user)
diff --git a/spec/services/award_emojis/copy_service_spec.rb b/spec/services/award_emojis/copy_service_spec.rb
index abb9c65e25d..6c1d7fb21e2 100644
--- a/spec/services/award_emojis/copy_service_spec.rb
+++ b/spec/services/award_emojis/copy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::CopyService do
+RSpec.describe AwardEmojis::CopyService, feature_category: :team_planning do
let_it_be(:from_awardable) do
create(
:issue,
diff --git a/spec/services/award_emojis/destroy_service_spec.rb b/spec/services/award_emojis/destroy_service_spec.rb
index f743de7c59e..109bdbfa986 100644
--- a/spec/services/award_emojis/destroy_service_spec.rb
+++ b/spec/services/award_emojis/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::DestroyService do
+RSpec.describe AwardEmojis::DestroyService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:awardable) { create(:note) }
let_it_be(:project) { awardable.project }
diff --git a/spec/services/award_emojis/toggle_service_spec.rb b/spec/services/award_emojis/toggle_service_spec.rb
index 74e97c66193..61dcc22561f 100644
--- a/spec/services/award_emojis/toggle_service_spec.rb
+++ b/spec/services/award_emojis/toggle_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AwardEmojis::ToggleService do
+RSpec.describe AwardEmojis::ToggleService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:awardable) { create(:note, project: project) }
diff --git a/spec/services/base_container_service_spec.rb b/spec/services/base_container_service_spec.rb
index 1de79eec702..7406f0aea93 100644
--- a/spec/services/base_container_service_spec.rb
+++ b/spec/services/base_container_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BaseContainerService do
+RSpec.describe BaseContainerService, feature_category: :container_registry do
let(:project) { Project.new }
let(:user) { User.new }
diff --git a/spec/services/base_count_service_spec.rb b/spec/services/base_count_service_spec.rb
index 18cab2e8e9a..9a731f52b09 100644
--- a/spec/services/base_count_service_spec.rb
+++ b/spec/services/base_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BaseCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe BaseCountService, :use_clean_rails_memory_store_caching, feature_category: :shared do
let(:service) { described_class.new }
describe '#relation_for_count' do
diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb
index f6a9f0903ce..5aaef9d529c 100644
--- a/spec/services/boards/create_service_spec.rb
+++ b/spec/services/boards/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::CreateService do
+RSpec.describe Boards::CreateService, feature_category: :team_planning do
describe '#execute' do
context 'when board parent is a project' do
let(:parent) { create(:project) }
diff --git a/spec/services/boards/destroy_service_spec.rb b/spec/services/boards/destroy_service_spec.rb
index cd6df832547..feaca3cfcce 100644
--- a/spec/services/boards/destroy_service_spec.rb
+++ b/spec/services/boards/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::DestroyService do
+RSpec.describe Boards::DestroyService, feature_category: :team_planning do
context 'with project board' do
let_it_be(:parent) { create(:project) }
diff --git a/spec/services/boards/issues/create_service_spec.rb b/spec/services/boards/issues/create_service_spec.rb
index c4f1eb093dc..f9a9c338f58 100644
--- a/spec/services/boards/issues/create_service_spec.rb
+++ b/spec/services/boards/issues/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Issues::CreateService do
+RSpec.describe Boards::Issues::CreateService, feature_category: :team_planning do
describe '#execute' do
let(:project) { create(:project) }
let(:board) { create(:board, project: project) }
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 1959710bb0c..4089e9e6da0 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Issues::ListService do
+RSpec.describe Boards::Issues::ListService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
@@ -57,7 +57,15 @@ RSpec.describe Boards::Issues::ListService do
end
context 'when filtering' do
- let_it_be(:incident) { create(:labeled_issue, project: project, milestone: m1, labels: [development, p1], issue_type: 'incident') }
+ let_it_be(:incident) do
+ create(
+ :labeled_issue,
+ :incident,
+ project: project,
+ milestone: m1,
+ labels: [development, p1]
+ )
+ end
context 'when filtering by type' do
it 'only returns the specified type' do
@@ -77,7 +85,6 @@ RSpec.describe Boards::Issues::ListService do
end
end
- # rubocop: disable RSpec/MultipleMemoizedHelpers
context 'when parent is a group' do
let(:project) { create(:project, :empty_repo, namespace: group) }
let(:project1) { create(:project, :empty_repo, namespace: group) }
@@ -148,7 +155,6 @@ RSpec.describe Boards::Issues::ListService do
it_behaves_like 'issues list service'
end
end
- # rubocop: enable RSpec/MultipleMemoizedHelpers
end
describe '.initialize_relative_positions' do
diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb
index 3a25f13762c..9c173f3f86e 100644
--- a/spec/services/boards/issues/move_service_spec.rb
+++ b/spec/services/boards/issues/move_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Issues::MoveService do
+RSpec.describe Boards::Issues::MoveService, feature_category: :team_planning do
describe '#execute' do
context 'when parent is a project' do
let(:user) { create(:user) }
diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb
index cac26b3c88d..da317fdd474 100644
--- a/spec/services/boards/lists/create_service_spec.rb
+++ b/spec/services/boards/lists/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Lists::CreateService do
+RSpec.describe Boards::Lists::CreateService, feature_category: :team_planning do
context 'when board parent is a project' do
let_it_be(:parent) { create(:project) }
let_it_be(:board) { create(:board, project: parent) }
diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb
index d5358bcc1e1..837635d49e8 100644
--- a/spec/services/boards/lists/destroy_service_spec.rb
+++ b/spec/services/boards/lists/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Lists::DestroyService do
+RSpec.describe Boards::Lists::DestroyService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let(:list_type) { :list }
diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb
index 2d41de42581..90b705e05c3 100644
--- a/spec/services/boards/lists/list_service_spec.rb
+++ b/spec/services/boards/lists/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Lists::ListService do
+RSpec.describe Boards::Lists::ListService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb
index 2861fc48b4d..abf7d48e114 100644
--- a/spec/services/boards/lists/move_service_spec.rb
+++ b/spec/services/boards/lists/move_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Lists::MoveService do
+RSpec.describe Boards::Lists::MoveService, feature_category: :team_planning do
describe '#execute' do
context 'when board parent is a project' do
let(:project) { create(:project) }
diff --git a/spec/services/boards/lists/update_service_spec.rb b/spec/services/boards/lists/update_service_spec.rb
index 21216e1b945..341eaa52292 100644
--- a/spec/services/boards/lists/update_service_spec.rb
+++ b/spec/services/boards/lists/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Lists::UpdateService do
+RSpec.describe Boards::Lists::UpdateService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let!(:list) { create(:list, board: board, position: 0) }
diff --git a/spec/services/boards/visits/create_service_spec.rb b/spec/services/boards/visits/create_service_spec.rb
index 8910345d170..4af4e914da5 100644
--- a/spec/services/boards/visits/create_service_spec.rb
+++ b/spec/services/boards/visits/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Boards::Visits::CreateService do
+RSpec.describe Boards::Visits::CreateService, feature_category: :team_planning do
describe '#execute' do
let(:user) { create(:user) }
diff --git a/spec/services/branches/create_service_spec.rb b/spec/services/branches/create_service_spec.rb
index 19a32aafa38..7fb7d9d440d 100644
--- a/spec/services/branches/create_service_spec.rb
+++ b/spec/services/branches/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Branches::CreateService, :use_clean_rails_redis_caching do
+RSpec.describe Branches::CreateService, :use_clean_rails_redis_caching, feature_category: :source_code_management do
subject(:service) { described_class.new(project, user) }
let_it_be(:project) { create(:project_empty_repo) }
@@ -108,7 +108,7 @@ RSpec.describe Branches::CreateService, :use_clean_rails_redis_caching do
control = RedisCommands::Recorder.new(pattern: ':branch_names:') { subject }
- expect(control.by_command(:sadd).count).to eq(1)
+ expect(control).not_to exceed_redis_command_calls_limit(:sadd, 1)
end
end
diff --git a/spec/services/branches/delete_merged_service_spec.rb b/spec/services/branches/delete_merged_service_spec.rb
index 46611670fe1..23a892a56ef 100644
--- a/spec/services/branches/delete_merged_service_spec.rb
+++ b/spec/services/branches/delete_merged_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Branches::DeleteMergedService do
+RSpec.describe Branches::DeleteMergedService, feature_category: :source_code_management do
include ProjectForksHelper
subject(:service) { described_class.new(project, project.first_owner) }
diff --git a/spec/services/branches/delete_service_spec.rb b/spec/services/branches/delete_service_spec.rb
index 727cadc5a50..003645b1b8c 100644
--- a/spec/services/branches/delete_service_spec.rb
+++ b/spec/services/branches/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Branches::DeleteService do
+RSpec.describe Branches::DeleteService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user) }
diff --git a/spec/services/branches/diverging_commit_counts_service_spec.rb b/spec/services/branches/diverging_commit_counts_service_spec.rb
index 34a2b81c831..3cccc74735c 100644
--- a/spec/services/branches/diverging_commit_counts_service_spec.rb
+++ b/spec/services/branches/diverging_commit_counts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Branches::DivergingCommitCountsService do
+RSpec.describe Branches::DivergingCommitCountsService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
diff --git a/spec/services/branches/validate_new_service_spec.rb b/spec/services/branches/validate_new_service_spec.rb
index 02127c8c10d..a5b75a09353 100644
--- a/spec/services/branches/validate_new_service_spec.rb
+++ b/spec/services/branches/validate_new_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Branches::ValidateNewService do
+RSpec.describe Branches::ValidateNewService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
subject(:service) { described_class.new(project) }
diff --git a/spec/services/bulk_create_integration_service_spec.rb b/spec/services/bulk_create_integration_service_spec.rb
index 22bb1736f9f..57bdfdbd4cb 100644
--- a/spec/services/bulk_create_integration_service_spec.rb
+++ b/spec/services/bulk_create_integration_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkCreateIntegrationService do
+RSpec.describe BulkCreateIntegrationService, feature_category: :integrations do
include JiraIntegrationHelpers
before_all do
diff --git a/spec/services/bulk_imports/archive_extraction_service_spec.rb b/spec/services/bulk_imports/archive_extraction_service_spec.rb
index da9df31cde9..40f8d8718ae 100644
--- a/spec/services/bulk_imports/archive_extraction_service_spec.rb
+++ b/spec/services/bulk_imports/archive_extraction_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::ArchiveExtractionService do
+RSpec.describe BulkImports::ArchiveExtractionService, feature_category: :importers do
let_it_be(:tmpdir) { Dir.mktmpdir }
let_it_be(:filename) { 'symlink_export.tar' }
let_it_be(:filepath) { File.join(tmpdir, filename) }
diff --git a/spec/services/bulk_imports/batched_relation_export_service_spec.rb b/spec/services/bulk_imports/batched_relation_export_service_spec.rb
new file mode 100644
index 00000000000..c361dfe5052
--- /dev/null
+++ b/spec/services/bulk_imports/batched_relation_export_service_spec.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::BatchedRelationExportService, feature_category: :importers do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:portable) { create(:group) }
+
+ let(:relation) { 'labels' }
+ let(:jid) { '123' }
+
+ subject(:service) { described_class.new(user, portable, relation, jid) }
+
+ describe '#execute' do
+ context 'when there are batches to export' do
+ let_it_be(:label) { create(:group_label, group: portable) }
+
+ it 'marks export as started' do
+ service.execute
+
+ export = portable.bulk_import_exports.first
+
+ expect(export.reload.started?).to eq(true)
+ end
+
+ it 'removes existing batches' do
+ expect_next_instance_of(BulkImports::Export) do |export|
+ expect(export.batches).to receive(:destroy_all)
+ end
+
+ service.execute
+ end
+
+ it 'enqueues export jobs for each batch & caches batch record ids' do
+ expect(BulkImports::RelationBatchExportWorker).to receive(:perform_async)
+ expect(Gitlab::Cache::Import::Caching).to receive(:set_add)
+
+ service.execute
+ end
+
+ it 'enqueues FinishBatchedRelationExportWorker' do
+ expect(BulkImports::FinishBatchedRelationExportWorker).to receive(:perform_async)
+
+ service.execute
+ end
+
+ context 'when there are multiple batches' do
+ it 'creates a batch record for each batch of records' do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+
+ create_list(:group_label, 10, group: portable)
+
+ service.execute
+
+ export = portable.bulk_import_exports.first
+
+ expect(export.batches.count).to eq(11)
+ end
+ end
+ end
+
+ context 'when there are no batches to export' do
+ let(:relation) { 'milestones' }
+
+ it 'marks export as finished' do
+ service.execute
+
+ export = portable.bulk_import_exports.first
+
+ expect(export.finished?).to eq(true)
+ expect(export.batches.count).to eq(0)
+ end
+ end
+
+ context 'when exception occurs' do
+ it 'tracks exception and marks export as failed' do
+ allow_next_instance_of(BulkImports::Export) do |export|
+ allow(export).to receive(:update!).and_call_original
+
+ allow(export)
+ .to receive(:update!)
+ .with(status_event: 'finish', total_objects_count: 0, batched: true, batches_count: 0, jid: jid, error: nil)
+ .and_raise(StandardError, 'Error!')
+ end
+
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(StandardError, portable_id: portable.id, portable_type: portable.class.name)
+
+ service.execute
+
+ export = portable.bulk_import_exports.first
+
+ expect(export.reload.failed?).to eq(true)
+ end
+ end
+ end
+
+ describe '.cache_key' do
+ it 'returns cache key given export and batch ids' do
+ expect(described_class.cache_key(1, 1)).to eq('bulk_imports/batched_relation_export/1/1')
+ end
+ end
+end
diff --git a/spec/services/bulk_imports/create_service_spec.rb b/spec/services/bulk_imports/create_service_spec.rb
index 7f892cfe722..ff4afd6abd0 100644
--- a/spec/services/bulk_imports/create_service_spec.rb
+++ b/spec/services/bulk_imports/create_service_spec.rb
@@ -35,6 +35,9 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
]
end
+ let(:source_entity_identifier) { ERB::Util.url_encode(params[0][:source_full_path]) }
+ let(:source_entity_type) { BulkImports::CreateService::ENTITY_TYPES_MAPPING.fetch(params[0][:source_type]) }
+
subject { described_class.new(user, params, credentials) }
describe '#execute' do
@@ -59,6 +62,34 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
end
+ context 'when direct transfer setting query returns a 404' do
+ it 'raises a ServiceResponse::Error' do
+ stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
+ stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
+ .to_return(
+ status: 200,
+ body: source_version.to_json,
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(status: 404)
+
+ expect_next_instance_of(BulkImports::Clients::HTTP) do |client|
+ expect(client).to receive(:get).and_raise(BulkImports::Error.setting_not_enabled)
+ end
+
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result).to be_error
+ expect(result.message)
+ .to eq(
+ "Group import disabled on source or destination instance. " \
+ "Ask an administrator to enable it on both instances and try again."
+ )
+ end
+ end
+
context 'when required scopes are not present' do
it 'returns ServiceResponse with error if token does not have api scope' do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
@@ -68,9 +99,13 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
body: source_version.to_json,
headers: { 'Content-Type' => 'application/json' }
)
+ stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(
+ status: 200
+ )
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
- allow(client).to receive(:validate_instance_version!).and_raise(BulkImports::Error.scope_validation_failure)
+ allow(client).to receive(:validate_import_scopes!).and_raise(BulkImports::Error.scope_validation_failure)
end
result = subject.execute
@@ -79,8 +114,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect(result).to be_error
expect(result.message)
.to eq(
- "Import aborted as the provided personal access token does not have the required 'api' scope or is " \
- "no longer valid."
+ "Personal access token does not " \
+ "have the required 'api' scope or is no longer valid."
)
end
end
@@ -90,16 +125,21 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404)
stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token')
.to_return(status: 200, body: source_version.to_json, headers: { 'Content-Type' => 'application/json' })
+ stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(
+ status: 200
+ )
stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token')
.to_return(
status: 200,
body: { 'scopes' => ['api'] }.to_json,
headers: { 'Content-Type' => 'application/json' }
)
+
+ parent_group.add_owner(user)
end
it 'creates bulk import' do
- parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
@@ -111,7 +151,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
- label: 'bulk_import_group'
+ label: 'bulk_import_group',
+ extra: { source_equals_destination: false }
)
expect_snowplow_event(
@@ -123,6 +164,23 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
)
end
+ context 'on the same instance' do
+ before do
+ allow(Settings.gitlab).to receive(:base_url).and_return('http://gitlab.example')
+ end
+
+ it 'tracks the same instance migration' do
+ expect { subject.execute }.to change { BulkImport.count }.by(1)
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'bulk_import_group',
+ extra: { source_equals_destination: true }
+ )
+ end
+ end
+
describe 'projects migration flag' do
let(:import) { BulkImport.last }
@@ -169,11 +227,16 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
allow(instance).to receive(:instance_enterprise).and_return(false)
+ stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(
+ status: 200
+ )
end
+
+ parent_group.add_owner(user)
end
it 'creates bulk import' do
- parent_group.add_owner(user)
expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
@@ -186,7 +249,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect_snowplow_event(
category: 'BulkImports::CreateService',
action: 'create',
- label: 'bulk_import_group'
+ label: 'bulk_import_group',
+ extra: { source_equals_destination: false }
)
expect_snowplow_event(
@@ -198,6 +262,23 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
)
end
+ context 'on the same instance' do
+ before do
+ allow(Settings.gitlab).to receive(:base_url).and_return('http://gitlab.example')
+ end
+
+ it 'tracks the same instance migration' do
+ expect { subject.execute }.to change { BulkImport.count }.by(1)
+
+ expect_snowplow_event(
+ category: 'BulkImports::CreateService',
+ action: 'create',
+ label: 'bulk_import_group',
+ extra: { source_equals_destination: true }
+ )
+ end
+ end
+
it 'creates bulk import entities' do
expect { subject.execute }.to change { BulkImports::Entity.count }.by(3)
end
@@ -227,11 +308,10 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message).to eq("Validation failed: Source full path can't be blank, " \
- "Source full path cannot start with a non-alphanumeric character except " \
- "for periods or underscores, can contain only alphanumeric characters, " \
- "forward slashes, periods, and underscores, cannot end with " \
- "a period or forward slash, and has a relative path structure " \
- "with no http protocol chars or leading or trailing forward slashes")
+ "Source full path must have a relative path structure with " \
+ "no HTTP protocol characters, or leading or trailing forward slashes. " \
+ "Path segments must not start or end with a special character, and " \
+ "must not contain consecutive special characters.")
end
describe '#user-role' do
@@ -263,6 +343,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
it 'defines access_level as not a member' do
+ parent_group.members.delete_all
+
subject.execute
expect_snowplow_event(
category: 'BulkImports::CreateService',
@@ -325,7 +407,210 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
end
- describe '.validate_destination_full_path' do
+ describe '#validate_setting_enabled!' do
+ let(:entity_source_id) { 'gid://gitlab/Model/12345' }
+ let(:graphql_client) { instance_double(BulkImports::Clients::Graphql) }
+ let(:http_client) { instance_double(BulkImports::Clients::HTTP) }
+ let(:http_response) { double(code: 200, success?: true) } # rubocop:disable RSpec/VerifiedDoubles
+
+ before do
+ allow(BulkImports::Clients::HTTP).to receive(:new).and_return(http_client)
+ allow(BulkImports::Clients::Graphql).to receive(:new).and_return(graphql_client)
+
+ allow(http_client).to receive(:instance_version).and_return(status: 200)
+ allow(http_client).to receive(:instance_enterprise).and_return(false)
+ allow(http_client).to receive(:validate_instance_version!).and_return(source_version)
+ allow(http_client).to receive(:validate_import_scopes!).and_return(true)
+ end
+
+ context 'when the source_type is a group' do
+ context 'when the source_full_path contains only integer characters' do
+ let(:query_string) { BulkImports::Groups::Graphql::GetGroupQuery.new(context: nil).to_s }
+ let(:graphql_response) do
+ double(original_hash: { 'data' => { 'group' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles
+ end
+
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: '67890',
+ destination_slug: 'destination-group-1',
+ destination_namespace: 'destination1'
+ }
+ ]
+ end
+
+ before do
+ allow(graphql_client).to receive(:parse).with(query_string)
+ allow(graphql_client).to receive(:execute).and_return(graphql_response)
+
+ allow(http_client).to receive(:get)
+ .with("/groups/12345/export_relations/status")
+ .and_return(http_response)
+
+ stub_request(:get, "http://gitlab.example/api/v4/groups/12345/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(status: 200, body: "", headers: {})
+ end
+
+ it 'makes a graphql request using the group full path and an http request with the correct id' do
+ expect(graphql_client).to receive(:parse).with(query_string)
+ expect(graphql_client).to receive(:execute).and_return(graphql_response)
+
+ expect(http_client).to receive(:get).with("/groups/12345/export_relations/status")
+
+ subject.execute
+ end
+ end
+ end
+
+ context 'when the source_type is a project' do
+ context 'when the source_full_path contains only integer characters' do
+ let(:query_string) { BulkImports::Projects::Graphql::GetProjectQuery.new(context: nil).to_s }
+ let(:graphql_response) do
+ double(original_hash: { 'data' => { 'project' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles
+ end
+
+ let(:params) do
+ [
+ {
+ source_type: 'project_entity',
+ source_full_path: '67890',
+ destination_slug: 'destination-group-1',
+ destination_namespace: 'destination1'
+ }
+ ]
+ end
+
+ before do
+ allow(graphql_client).to receive(:parse).with(query_string)
+ allow(graphql_client).to receive(:execute).and_return(graphql_response)
+
+ allow(http_client).to receive(:get)
+ .with("/projects/12345/export_relations/status")
+ .and_return(http_response)
+
+ stub_request(:get, "http://gitlab.example/api/v4/projects/12345/export_relations/status?page=1&per_page=30&private_token=token")
+ .to_return(status: 200, body: "", headers: {})
+ end
+
+ it 'makes a graphql request using the group full path and an http request with the correct id' do
+ expect(graphql_client).to receive(:parse).with(query_string)
+ expect(graphql_client).to receive(:execute).and_return(graphql_response)
+
+ expect(http_client).to receive(:get).with("/projects/12345/export_relations/status")
+
+ subject.execute
+ end
+ end
+ end
+ end
+
+ describe '#validate_destination_namespace' do
+ context 'when the destination_namespace does not exist' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/source',
+ destination_slug: 'destination-slug',
+ destination_namespace: 'destination-namespace',
+ migrate_projects: migrate_projects
+ }
+ ]
+ end
+
+ it 'returns ServiceResponse with an error message' do
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result).to be_error
+ expect(result.message)
+ .to eq("Import failed. Destination 'destination-namespace' is invalid, or you don't have permission.")
+ end
+ end
+
+ context 'when the user does not have permission to create subgroups' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/source',
+ destination_slug: 'destination-slug',
+ destination_namespace: parent_group.path,
+ migrate_projects: migrate_projects
+ }
+ ]
+ end
+
+ it 'returns ServiceResponse with an error message' do
+ parent_group.members.delete_all
+
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result).to be_error
+ expect(result.message)
+ .to eq("Import failed. Destination '#{parent_group.path}' is invalid, or you don't have permission.")
+ end
+ end
+
+ context 'when the user does not have permission to create projects' do
+ let(:params) do
+ [
+ {
+ source_type: 'project_entity',
+ source_full_path: 'full/path/to/source',
+ destination_slug: 'destination-slug',
+ destination_namespace: parent_group.path,
+ migrate_projects: migrate_projects
+ }
+ ]
+ end
+
+ it 'returns ServiceResponse with an error message' do
+ parent_group.members.delete_all
+
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result).to be_error
+ expect(result.message)
+ .to eq("Import failed. Destination '#{parent_group.path}' is invalid, or you don't have permission.")
+ end
+ end
+ end
+
+ describe '#validate_destination_slug' do
+ context 'when the destination_slug is invalid' do
+ let(:params) do
+ [
+ {
+ source_type: 'group_entity',
+ source_full_path: 'full/path/to/source',
+ destination_slug: 'destin-*-ation-slug',
+ destination_namespace: parent_group.path,
+ migrate_projects: migrate_projects
+ }
+ ]
+ end
+
+ it 'returns ServiceResponse with an error message' do
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result).to be_error
+ expect(result.message)
+ .to eq(
+ "Import failed. Destination URL " \
+ "must not start or end with a special character and must " \
+ "not contain consecutive special characters."
+ )
+ end
+ end
+ end
+
+ describe '#validate_destination_full_path' do
context 'when the source_type is a group' do
context 'when the provided destination_slug already exists in the destination_namespace' do
let_it_be(:existing_subgroup) { create(:group, path: 'existing-subgroup', parent_id: parent_group.id ) }
@@ -349,7 +634,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect(result).to be_error
expect(result.message)
.to eq(
- "Import aborted as 'parent-group/existing-subgroup' already exists. " \
+ "Import failed. 'parent-group/existing-subgroup' already exists. " \
"Change the destination and try again."
)
end
@@ -376,7 +661,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
expect(result).to be_error
expect(result.message)
.to eq(
- "Import aborted as 'top-level-group' already exists. " \
+ "Import failed. 'top-level-group' already exists. " \
"Change the destination and try again."
)
end
@@ -421,13 +706,15 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
it 'returns ServiceResponse with an error message' do
+ existing_group.add_owner(user)
+
result = subject.execute
expect(result).to be_a(ServiceResponse)
expect(result).to be_error
expect(result.message)
.to eq(
- "Import aborted as 'existing-group/existing-project' already exists. " \
+ "Import failed. 'existing-group/existing-project' already exists. " \
"Change the destination and try again."
)
end
@@ -448,6 +735,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do
end
it 'returns success ServiceResponse' do
+ existing_group.add_owner(user)
+
result = subject.execute
expect(result).to be_a(ServiceResponse)
diff --git a/spec/services/bulk_imports/export_service_spec.rb b/spec/services/bulk_imports/export_service_spec.rb
index 2414f7c5ca7..25a4547477c 100644
--- a/spec/services/bulk_imports/export_service_spec.rb
+++ b/spec/services/bulk_imports/export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::ExportService do
+RSpec.describe BulkImports::ExportService, feature_category: :importers do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
@@ -13,17 +13,36 @@ RSpec.describe BulkImports::ExportService do
subject { described_class.new(portable: group, user: user) }
describe '#execute' do
- it 'schedules RelationExportWorker for each top level relation' do
- expect(subject).to receive(:execute).and_return(ServiceResponse.success).and_call_original
- top_level_relations = BulkImports::FileTransfer.config_for(group).portable_relations
-
- top_level_relations.each do |relation|
- expect(BulkImports::RelationExportWorker)
- .to receive(:perform_async)
- .with(user.id, group.id, group.class.name, relation)
+ let_it_be(:top_level_relations) { BulkImports::FileTransfer.config_for(group).portable_relations }
+
+ before do
+ allow(subject).to receive(:execute).and_return(ServiceResponse.success).and_call_original
+ end
+
+ context 'when export is not batched' do
+ it 'schedules RelationExportWorker for each top level relation' do
+ top_level_relations.each do |relation|
+ expect(BulkImports::RelationExportWorker)
+ .to receive(:perform_async)
+ .with(user.id, group.id, group.class.name, relation, false)
+ end
+
+ subject.execute
end
+ end
+
+ context 'when export is batched' do
+ subject { described_class.new(portable: group, user: user, batched: true) }
- subject.execute
+ it 'schedules RelationExportWorker with a `batched: true` flag' do
+ top_level_relations.each do |relation|
+ expect(BulkImports::RelationExportWorker)
+ .to receive(:perform_async)
+ .with(user.id, group.id, group.class.name, relation, true)
+ end
+
+ subject.execute
+ end
end
context 'when exception occurs' do
@@ -38,6 +57,20 @@ RSpec.describe BulkImports::ExportService do
service.execute
end
+
+ context 'when user is not allowed to perform export' do
+ let(:another_user) { create(:user) }
+
+ it 'does not schedule RelationExportWorker' do
+ another_user = create(:user)
+ service = described_class.new(portable: group, user: another_user)
+ response = service.execute
+
+ expect(response.status).to eq(:error)
+ expect(response.message).to eq(Gitlab::ImportExport::Error)
+ expect(response.http_status).to eq(:unprocessable_entity)
+ end
+ end
end
end
end
diff --git a/spec/services/bulk_imports/file_decompression_service_spec.rb b/spec/services/bulk_imports/file_decompression_service_spec.rb
index 77348428d60..9b8320aeac5 100644
--- a/spec/services/bulk_imports/file_decompression_service_spec.rb
+++ b/spec/services/bulk_imports/file_decompression_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::FileDecompressionService do
+RSpec.describe BulkImports::FileDecompressionService, feature_category: :importers do
let_it_be(:tmpdir) { Dir.mktmpdir }
let_it_be(:ndjson_filename) { 'labels.ndjson' }
let_it_be(:ndjson_filepath) { File.join(tmpdir, ndjson_filename) }
diff --git a/spec/services/bulk_imports/file_download_service_spec.rb b/spec/services/bulk_imports/file_download_service_spec.rb
index 27f77b678e3..7c64d6efc65 100644
--- a/spec/services/bulk_imports/file_download_service_spec.rb
+++ b/spec/services/bulk_imports/file_download_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::FileDownloadService do
+RSpec.describe BulkImports::FileDownloadService, feature_category: :importers do
describe '#execute' do
let_it_be(:allowed_content_types) { %w(application/gzip application/octet-stream) }
let_it_be(:file_size_limit) { 5.gigabytes }
diff --git a/spec/services/bulk_imports/file_export_service_spec.rb b/spec/services/bulk_imports/file_export_service_spec.rb
index 453fc1d0c0d..001fccb2054 100644
--- a/spec/services/bulk_imports/file_export_service_spec.rb
+++ b/spec/services/bulk_imports/file_export_service_spec.rb
@@ -2,21 +2,23 @@
require 'spec_helper'
-RSpec.describe BulkImports::FileExportService do
+RSpec.describe BulkImports::FileExportService, feature_category: :importers do
let_it_be(:project) { create(:project) }
+ let(:relations) do
+ {
+ 'uploads' => BulkImports::UploadsExportService,
+ 'lfs_objects' => BulkImports::LfsObjectsExportService,
+ 'repository' => BulkImports::RepositoryBundleExportService,
+ 'design' => BulkImports::RepositoryBundleExportService
+ }
+ end
+
describe '#execute' do
it 'executes export service and archives exported data for each file relation' do
- relations = {
- 'uploads' => BulkImports::UploadsExportService,
- 'lfs_objects' => BulkImports::LfsObjectsExportService,
- 'repository' => BulkImports::RepositoryBundleExportService,
- 'design' => BulkImports::RepositoryBundleExportService
- }
-
relations.each do |relation, klass|
Dir.mktmpdir do |export_path|
- service = described_class.new(project, export_path, relation)
+ service = described_class.new(project, export_path, relation, nil)
expect_next_instance_of(klass) do |service|
expect(service).to receive(:execute)
@@ -31,18 +33,58 @@ RSpec.describe BulkImports::FileExportService do
context 'when unsupported relation is passed' do
it 'raises an error' do
- service = described_class.new(project, nil, 'unsupported')
+ service = described_class.new(project, nil, 'unsupported', nil)
expect { service.execute }.to raise_error(BulkImports::Error, 'Unsupported relation export type')
end
end
end
+ describe '#execute_batch' do
+ it 'calls execute with provided array of record ids' do
+ relations.each do |relation, klass|
+ Dir.mktmpdir do |export_path|
+ service = described_class.new(project, export_path, relation, nil)
+
+ expect_next_instance_of(klass) do |service|
+ expect(service).to receive(:execute).with({ batch_ids: [1, 2, 3] })
+ end
+
+ service.export_batch([1, 2, 3])
+ end
+ end
+ end
+ end
+
describe '#exported_filename' do
it 'returns filename of the exported file' do
- service = described_class.new(project, nil, 'uploads')
+ service = described_class.new(project, nil, 'uploads', nil)
expect(service.exported_filename).to eq('uploads.tar')
end
end
+
+ describe '#exported_objects_count' do
+ context 'when relation is a collection' do
+ it 'returns a number of exported relations' do
+ %w[uploads lfs_objects].each do |relation|
+ service = described_class.new(project, nil, relation, nil)
+
+ allow(service).to receive_message_chain(:export_service, :exported_objects_count).and_return(10)
+
+ expect(service.exported_objects_count).to eq(10)
+ end
+ end
+ end
+
+ context 'when relation is a repository' do
+ it 'returns 1' do
+ %w[repository design].each do |relation|
+ service = described_class.new(project, nil, relation, nil)
+
+ expect(service.exported_objects_count).to eq(1)
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/bulk_imports/lfs_objects_export_service_spec.rb b/spec/services/bulk_imports/lfs_objects_export_service_spec.rb
index 894789c7941..587c99d9897 100644
--- a/spec/services/bulk_imports/lfs_objects_export_service_spec.rb
+++ b/spec/services/bulk_imports/lfs_objects_export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::LfsObjectsExportService do
+RSpec.describe BulkImports::LfsObjectsExportService, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:lfs_json_filename) { "#{BulkImports::FileTransfer::ProjectConfig::LFS_OBJECTS_RELATION}.json" }
let_it_be(:remote_url) { 'http://my-object-storage.local' }
@@ -53,6 +53,19 @@ RSpec.describe BulkImports::LfsObjectsExportService do
)
end
+ context 'when export is batched' do
+ it 'exports only specified lfs objects' do
+ new_lfs_object = create(:lfs_object, :with_file)
+
+ project.lfs_objects << new_lfs_object
+
+ service.execute(batch_ids: [new_lfs_object.id])
+
+ expect(File).to exist(File.join(export_path, new_lfs_object.oid))
+ expect(File).not_to exist(File.join(export_path, lfs_object.oid))
+ end
+ end
+
context 'when lfs object has file on disk missing' do
it 'does not attempt to copy non-existent file' do
FileUtils.rm(lfs_object.file.path)
@@ -79,4 +92,14 @@ RSpec.describe BulkImports::LfsObjectsExportService do
end
end
end
+
+ describe '#exported_objects_count' do
+ it 'return the number of exported lfs objects' do
+ project.lfs_objects << create(:lfs_object, :with_file)
+
+ service.execute
+
+ expect(service.exported_objects_count).to eq(2)
+ end
+ end
end
diff --git a/spec/services/bulk_imports/relation_batch_export_service_spec.rb b/spec/services/bulk_imports/relation_batch_export_service_spec.rb
new file mode 100644
index 00000000000..c3abd02aff8
--- /dev/null
+++ b/spec/services/bulk_imports/relation_batch_export_service_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::RelationBatchExportService, feature_category: :importers do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:label) { create(:label, project: project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:export) { create(:bulk_import_export, :batched, project: project) }
+ let_it_be(:batch) { create(:bulk_import_export_batch, export: export) }
+ let_it_be(:cache_key) { BulkImports::BatchedRelationExportService.cache_key(export.id, batch.id) }
+
+ subject(:service) { described_class.new(user.id, batch.id) }
+
+ before(:all) do
+ Gitlab::Cache::Import::Caching.set_add(cache_key, label.id)
+ end
+
+ after(:all) do
+ Gitlab::Cache::Import::Caching.expire(cache_key, 0)
+ end
+
+ describe '#execute' do
+ it 'exports relation batch' do
+ expect(Gitlab::Cache::Import::Caching).to receive(:values_from_set).with(cache_key).and_call_original
+
+ service.execute
+ batch.reload
+
+ expect(batch.finished?).to eq(true)
+ expect(batch.objects_count).to eq(1)
+ expect(batch.error).to be_nil
+ expect(export.upload.export_file).to be_present
+ end
+
+ it 'removes exported contents after export' do
+ double = instance_double(BulkImports::FileTransfer::ProjectConfig, export_path: 'foo')
+
+ allow(BulkImports::FileTransfer).to receive(:config_for).and_return(double)
+ allow(double).to receive(:export_service_for).and_raise(StandardError, 'Error!')
+ allow(FileUtils).to receive(:remove_entry)
+
+ expect(FileUtils).to receive(:remove_entry).with('foo')
+
+ service.execute
+ end
+
+ context 'when exception occurs' do
+ before do
+ allow(service).to receive(:gzip).and_raise(StandardError, 'Error!')
+ end
+
+ it 'marks batch as failed' do
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .with(StandardError, portable_id: project.id, portable_type: 'Project')
+
+ service.execute
+ batch.reload
+
+ expect(batch.failed?).to eq(true)
+ expect(batch.objects_count).to eq(0)
+ expect(batch.error).to eq('Error!')
+ end
+ end
+ end
+end
diff --git a/spec/services/bulk_imports/relation_export_service_spec.rb b/spec/services/bulk_imports/relation_export_service_spec.rb
index f0f85217d2e..1c050fe4143 100644
--- a/spec/services/bulk_imports/relation_export_service_spec.rb
+++ b/spec/services/bulk_imports/relation_export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::RelationExportService do
+RSpec.describe BulkImports::RelationExportService, feature_category: :importers do
let_it_be(:jid) { 'jid' }
let_it_be(:relation) { 'labels' }
let_it_be(:user) { create(:user) }
@@ -35,6 +35,10 @@ RSpec.describe BulkImports::RelationExportService do
expect(export.reload.upload.export_file).to be_present
expect(export.finished?).to eq(true)
+ expect(export.batched?).to eq(false)
+ expect(export.batches_count).to eq(0)
+ expect(export.batches.count).to eq(0)
+ expect(export.total_objects_count).to eq(0)
end
it 'removes temp export files' do
@@ -133,13 +137,23 @@ RSpec.describe BulkImports::RelationExportService do
include_examples 'tracks exception', ActiveRecord::RecordInvalid
end
+ end
+
+ context 'when export was batched' do
+ let(:relation) { 'milestones' }
+ let(:export) { create(:bulk_import_export, group: group, relation: relation, batched: true, batches_count: 2) }
- context 'when user is not allowed to perform export' do
- let(:another_user) { create(:user) }
+ it 'removes existing batches and marks export as not batched' do
+ create(:bulk_import_export_batch, batch_number: 1, export: export)
+ create(:bulk_import_export_batch, batch_number: 2, export: export)
- subject { described_class.new(another_user, group, relation, jid) }
+ expect { described_class.new(user, group, relation, jid).execute }
+ .to change { export.reload.batches.count }
+ .from(2)
+ .to(0)
- include_examples 'tracks exception', Gitlab::ImportExport::Error
+ expect(export.batched?).to eq(false)
+ expect(export.batches_count).to eq(0)
end
end
end
diff --git a/spec/services/bulk_imports/repository_bundle_export_service_spec.rb b/spec/services/bulk_imports/repository_bundle_export_service_spec.rb
index f0d63de1ab9..92d5d21c33f 100644
--- a/spec/services/bulk_imports/repository_bundle_export_service_spec.rb
+++ b/spec/services/bulk_imports/repository_bundle_export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::RepositoryBundleExportService do
+RSpec.describe BulkImports::RepositoryBundleExportService, feature_category: :importers do
let(:project) { create(:project) }
let(:export_path) { Dir.mktmpdir }
diff --git a/spec/services/bulk_imports/tree_export_service_spec.rb b/spec/services/bulk_imports/tree_export_service_spec.rb
index 6e26cb6dc2b..ae78858976f 100644
--- a/spec/services/bulk_imports/tree_export_service_spec.rb
+++ b/spec/services/bulk_imports/tree_export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkImports::TreeExportService do
+RSpec.describe BulkImports::TreeExportService, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:export_path) { Dir.mktmpdir }
@@ -53,4 +53,14 @@ RSpec.describe BulkImports::TreeExportService do
end
end
end
+
+ describe '#export_batch' do
+ it 'serializes relation with specified ids' do
+ expect_next_instance_of(Gitlab::ImportExport::Json::StreamingSerializer) do |serializer|
+ expect(serializer).to receive(:serialize_relation).with(anything, batch_ids: [1, 2, 3])
+ end
+
+ subject.export_batch([1, 2, 3])
+ end
+ end
end
diff --git a/spec/services/bulk_imports/uploads_export_service_spec.rb b/spec/services/bulk_imports/uploads_export_service_spec.rb
index ad6e005485c..709ade4a504 100644
--- a/spec/services/bulk_imports/uploads_export_service_spec.rb
+++ b/spec/services/bulk_imports/uploads_export_service_spec.rb
@@ -2,10 +2,9 @@
require 'spec_helper'
-RSpec.describe BulkImports::UploadsExportService do
- let_it_be(:export_path) { Dir.mktmpdir }
- let_it_be(:project) { create(:project, avatar: fixture_file_upload('spec/fixtures/rails_sample.png', 'image/png')) }
-
+RSpec.describe BulkImports::UploadsExportService, feature_category: :importers do
+ let(:export_path) { Dir.mktmpdir }
+ let(:project) { create(:project, avatar: fixture_file_upload('spec/fixtures/rails_sample.png', 'image/png')) }
let!(:upload) { create(:upload, :with_file, :issuable_upload, uploader: FileUploader, model: project) }
let(:exported_filepath) { File.join(export_path, upload.secret, upload.retrieve_uploader.filename) }
@@ -23,6 +22,16 @@ RSpec.describe BulkImports::UploadsExportService do
expect(File).to exist(exported_filepath)
end
+ context 'when export is batched' do
+ it 'exports only specified uploads' do
+ service.execute(batch_ids: [upload.id])
+
+ expect(service.exported_objects_count).to eq(1)
+ expect(File).not_to exist(File.join(export_path, 'avatar', 'rails_sample.png'))
+ expect(File).to exist(exported_filepath)
+ end
+ end
+
context 'when upload has underlying file missing' do
context 'with an upload missing its file' do
it 'does not cause errors' do
@@ -53,6 +62,16 @@ RSpec.describe BulkImports::UploadsExportService do
}
)
+ expect(Gitlab::ErrorTracking)
+ .to receive(:log_exception)
+ .with(
+ instance_of(exception), {
+ portable_id: project.id,
+ portable_class: 'Project',
+ upload_id: project.avatar.upload.id
+ }
+ )
+
service.execute
expect(File).not_to exist(exported_filepath)
@@ -73,4 +92,12 @@ RSpec.describe BulkImports::UploadsExportService do
end
end
end
+
+ describe '#exported_objects_count' do
+ it 'return the number of exported uploads' do
+ service.execute
+
+ expect(service.exported_objects_count).to eq(2)
+ end
+ end
end
diff --git a/spec/services/bulk_push_event_payload_service_spec.rb b/spec/services/bulk_push_event_payload_service_spec.rb
index 381c735c003..95e1c831498 100644
--- a/spec/services/bulk_push_event_payload_service_spec.rb
+++ b/spec/services/bulk_push_event_payload_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkPushEventPayloadService do
+RSpec.describe BulkPushEventPayloadService, feature_category: :source_code_management do
let(:event) { create(:push_event) }
let(:push_data) do
diff --git a/spec/services/bulk_update_integration_service_spec.rb b/spec/services/bulk_update_integration_service_spec.rb
index 24a868b524d..9095fa9a0fa 100644
--- a/spec/services/bulk_update_integration_service_spec.rb
+++ b/spec/services/bulk_update_integration_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe BulkUpdateIntegrationService do
+RSpec.describe BulkUpdateIntegrationService, feature_category: :integrations do
include JiraIntegrationHelpers
before_all do
@@ -56,14 +56,14 @@ RSpec.describe BulkUpdateIntegrationService do
end
it 'does not change the created_at timestamp' do
- subgroup_integration.update_column(:created_at, Time.utc('2022-01-01'))
+ subgroup_integration.update_column(:created_at, Time.utc(2022, 1, 1))
expect do
described_class.new(subgroup_integration, batch).execute
end.not_to change { integration.reload.created_at }
end
- it 'sets the updated_at timestamp to the current time', time_travel_to: Time.utc('2022-01-01') do
+ it 'sets the updated_at timestamp to the current time', time_travel_to: Time.utc(2022, 1, 1) do
expect do
described_class.new(subgroup_integration, batch).execute
end.to change { integration.reload.updated_at }.to(Time.current)
@@ -85,14 +85,14 @@ RSpec.describe BulkUpdateIntegrationService do
end
it 'does not change the created_at timestamp' do
- subgroup_integration.data_fields.update_column(:created_at, Time.utc('2022-01-02'))
+ subgroup_integration.data_fields.update_column(:created_at, Time.utc(2022, 1, 2))
expect do
described_class.new(subgroup_integration, batch).execute
end.not_to change { integration.data_fields.reload.created_at }
end
- it 'sets the updated_at timestamp to the current time', time_travel_to: Time.utc('2022-01-01') do
+ it 'sets the updated_at timestamp to the current time', time_travel_to: Time.utc(2022, 1, 1) do
expect do
described_class.new(subgroup_integration, batch).execute
end.to change { integration.data_fields.reload.updated_at }.to(Time.current)
diff --git a/spec/services/captcha/captcha_verification_service_spec.rb b/spec/services/captcha/captcha_verification_service_spec.rb
index fe2199fb53e..b67e725bf91 100644
--- a/spec/services/captcha/captcha_verification_service_spec.rb
+++ b/spec/services/captcha/captcha_verification_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Captcha::CaptchaVerificationService do
+RSpec.describe Captcha::CaptchaVerificationService, feature_category: :team_planning do
describe '#execute' do
let(:captcha_response) { 'abc123' }
let(:fake_ip) { '1.2.3.4' }
diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb
index 10cb0a2f065..14bece4efb4 100644
--- a/spec/services/chat_names/find_user_service_spec.rb
+++ b/spec/services/chat_names/find_user_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
+RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state, feature_category: :user_profile do
describe '#execute' do
subject { described_class.new(team_id, user_id).execute }
diff --git a/spec/services/ci/abort_pipelines_service_spec.rb b/spec/services/ci/abort_pipelines_service_spec.rb
index e43faf0af51..60f3ee11442 100644
--- a/spec/services/ci/abort_pipelines_service_spec.rb
+++ b/spec/services/ci/abort_pipelines_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::AbortPipelinesService do
+RSpec.describe Ci::AbortPipelinesService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb
index 20f7967d1f1..113c88dc5f3 100644
--- a/spec/services/ci/append_build_trace_service_spec.rb
+++ b/spec/services/ci/append_build_trace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::AppendBuildTraceService do
+RSpec.describe Ci::AppendBuildTraceService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be_with_reload(:build) { create(:ci_build, :running, pipeline: pipeline) }
diff --git a/spec/services/ci/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb
index 3fb9d092ae7..e7f489e841a 100644
--- a/spec/services/ci/archive_trace_service_spec.rb
+++ b/spec/services/ci/archive_trace_service_spec.rb
@@ -63,19 +63,6 @@ RSpec.describe Ci::ArchiveTraceService, '#execute', feature_category: :continuou
end
end
- context 'when job does not have trace' do
- let(:job) { create(:ci_build, :success) }
-
- it 'leaves a warning message in sidekiq log' do
- expect(Sidekiq.logger).to receive(:warn).with(
- class: Ci::ArchiveTraceWorker.name,
- message: 'The job does not have live trace but going to be archived.',
- job_id: job.id)
-
- subject
- end
- end
-
context 'when the job is out of archival attempts' do
before do
create(:ci_build_trace_metadata,
@@ -149,23 +136,6 @@ RSpec.describe Ci::ArchiveTraceService, '#execute', feature_category: :continuou
subject
end
end
-
- context 'when job failed to archive trace but did not raise an exception' do
- before do
- allow_next_instance_of(Gitlab::Ci::Trace) do |instance|
- allow(instance).to receive(:archive!) {}
- end
- end
-
- it 'leaves a warning message in sidekiq log' do
- expect(Sidekiq.logger).to receive(:warn).with(
- class: Ci::ArchiveTraceWorker.name,
- message: 'The job does not have archived trace after archiving.',
- job_id: job.id)
-
- subject
- end
- end
end
context 'when job is running' do
@@ -175,8 +145,8 @@ RSpec.describe Ci::ArchiveTraceService, '#execute', feature_category: :continuou
expect(Gitlab::ErrorTracking)
.to receive(:track_and_raise_for_dev_exception)
.with(::Gitlab::Ci::Trace::ArchiveError,
- issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/51502',
- job_id: job.id).once
+ issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/51502',
+ job_id: job.id).once
expect(Sidekiq.logger).to receive(:warn).with(
class: Ci::ArchiveTraceWorker.name,
diff --git a/spec/services/ci/build_cancel_service_spec.rb b/spec/services/ci/build_cancel_service_spec.rb
index fe036dc1368..314d2e86bd3 100644
--- a/spec/services/ci/build_cancel_service_spec.rb
+++ b/spec/services/ci/build_cancel_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildCancelService do
+RSpec.describe Ci::BuildCancelService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/ci/build_erase_service_spec.rb b/spec/services/ci/build_erase_service_spec.rb
index e750a163621..35e74f76a26 100644
--- a/spec/services/ci/build_erase_service_spec.rb
+++ b/spec/services/ci/build_erase_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildEraseService do
+RSpec.describe Ci::BuildEraseService, feature_category: :continuous_integration do
let_it_be(:user) { user }
let(:build) { create(:ci_build, :artifacts, :trace_artifact, artifacts_expire_at: 100.days.from_now) }
diff --git a/spec/services/ci/build_report_result_service_spec.rb b/spec/services/ci/build_report_result_service_spec.rb
index c5238b7f5e0..c3ce6714241 100644
--- a/spec/services/ci/build_report_result_service_spec.rb
+++ b/spec/services/ci/build_report_result_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildReportResultService do
+RSpec.describe Ci::BuildReportResultService, feature_category: :continuous_integration do
describe '#execute', :clean_gitlab_redis_shared_state do
subject(:build_report_result) { described_class.new.execute(build) }
diff --git a/spec/services/ci/build_unschedule_service_spec.rb b/spec/services/ci/build_unschedule_service_spec.rb
index d784d9a2754..539c66047e4 100644
--- a/spec/services/ci/build_unschedule_service_spec.rb
+++ b/spec/services/ci/build_unschedule_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::BuildUnscheduleService do
+RSpec.describe Ci::BuildUnscheduleService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/ci/catalog/validate_resource_service_spec.rb b/spec/services/ci/catalog/validate_resource_service_spec.rb
new file mode 100644
index 00000000000..3bee37b7e55
--- /dev/null
+++ b/spec/services/ci/catalog/validate_resource_service_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Catalog::ValidateResourceService, feature_category: :pipeline_composition do
+ describe '#execute' do
+ context 'with a project that has a README and a description' do
+ it 'is valid' do
+ project = create(:project, :repository, description: 'Component project')
+ response = described_class.new(project, project.default_branch).execute
+
+ expect(response).to be_success
+ end
+ end
+
+ context 'with a project that has neither a description nor a README' do
+ it 'is not valid' do
+ project = create(:project, :empty_repo)
+ project.repository.create_file(
+ project.creator,
+ 'ruby.rb',
+ 'I like this',
+ message: 'Ruby like this',
+ branch_name: 'master'
+ )
+ response = described_class.new(project, project.default_branch).execute
+
+ expect(response.message).to eq('Project must have a README , Project must have a description')
+ end
+ end
+
+ context 'with a project that has a description but not a README' do
+ it 'is not valid' do
+ project = create(:project, :empty_repo, description: 'project with no README')
+ project.repository.create_file(
+ project.creator,
+ 'text.txt',
+ 'I do not like this',
+ message: 'only text like text',
+ branch_name: 'master'
+ )
+ response = described_class.new(project, project.default_branch).execute
+
+ expect(response.message).to eq('Project must have a README')
+ end
+ end
+
+ context 'with a project that has a README and not a description' do
+ it 'is not valid' do
+ project = create(:project, :repository)
+ response = described_class.new(project, project.default_branch).execute
+
+ expect(response.message).to eq('Project must have a description')
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/change_variable_service_spec.rb b/spec/services/ci/change_variable_service_spec.rb
index f86a87132b1..fd2ddded375 100644
--- a/spec/services/ci/change_variable_service_spec.rb
+++ b/spec/services/ci/change_variable_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ChangeVariableService do
+RSpec.describe Ci::ChangeVariableService, feature_category: :secrets_management do
let(:service) { described_class.new(container: group, current_user: user, params: params) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/change_variables_service_spec.rb b/spec/services/ci/change_variables_service_spec.rb
index b710ca78554..e22aebb8f5d 100644
--- a/spec/services/ci/change_variables_service_spec.rb
+++ b/spec/services/ci/change_variables_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ChangeVariablesService do
+RSpec.describe Ci::ChangeVariablesService, feature_category: :secrets_management do
let(:service) { described_class.new(container: group, current_user: user, params: params) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/compare_accessibility_reports_service_spec.rb b/spec/services/ci/compare_accessibility_reports_service_spec.rb
index e0b84219834..57514c18042 100644
--- a/spec/services/ci/compare_accessibility_reports_service_spec.rb
+++ b/spec/services/ci/compare_accessibility_reports_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CompareAccessibilityReportsService do
+RSpec.describe Ci::CompareAccessibilityReportsService, feature_category: :continuous_integration do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/compare_codequality_reports_service_spec.rb b/spec/services/ci/compare_codequality_reports_service_spec.rb
index ef762a2e9ad..de2300c354b 100644
--- a/spec/services/ci/compare_codequality_reports_service_spec.rb
+++ b/spec/services/ci/compare_codequality_reports_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CompareCodequalityReportsService do
+RSpec.describe Ci::CompareCodequalityReportsService, feature_category: :continuous_integration do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/compare_reports_base_service_spec.rb b/spec/services/ci/compare_reports_base_service_spec.rb
index 20d8cd37553..2906d61066d 100644
--- a/spec/services/ci/compare_reports_base_service_spec.rb
+++ b/spec/services/ci/compare_reports_base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CompareReportsBaseService do
+RSpec.describe Ci::CompareReportsBaseService, feature_category: :continuous_integration do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/compare_test_reports_service_spec.rb b/spec/services/ci/compare_test_reports_service_spec.rb
index f259072fe87..d29cef0c583 100644
--- a/spec/services/ci/compare_test_reports_service_spec.rb
+++ b/spec/services/ci/compare_test_reports_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CompareTestReportsService do
+RSpec.describe Ci::CompareTestReportsService, feature_category: :continuous_integration do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/components/fetch_service_spec.rb b/spec/services/ci/components/fetch_service_spec.rb
index f2eaa8d31b4..532098b3b20 100644
--- a/spec/services/ci/components/fetch_service_spec.rb
+++ b/spec/services/ci/components/fetch_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Components::FetchService, feature_category: :pipeline_authoring do
+RSpec.describe Ci::Components::FetchService, feature_category: :pipeline_composition do
let_it_be(:project) { create(:project, :repository, create_tag: 'v1.0') }
let_it_be(:user) { create(:user) }
let_it_be(:current_user) { user }
diff --git a/spec/services/ci/copy_cross_database_associations_service_spec.rb b/spec/services/ci/copy_cross_database_associations_service_spec.rb
index 5938ac258d0..2be0cbd9582 100644
--- a/spec/services/ci/copy_cross_database_associations_service_spec.rb
+++ b/spec/services/ci/copy_cross_database_associations_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CopyCrossDatabaseAssociationsService do
+RSpec.describe Ci::CopyCrossDatabaseAssociationsService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:old_build) { create(:ci_build, pipeline: pipeline) }
diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb
index 7b576339c61..6da6ec3379a 100644
--- a/spec/services/ci/create_downstream_pipeline_service_spec.rb
+++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb
@@ -26,10 +26,13 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category
end
let(:bridge) do
- create(:ci_bridge, status: :pending,
- user: user,
- options: trigger,
- pipeline: upstream_pipeline)
+ create(
+ :ci_bridge,
+ status: :pending,
+ user: user,
+ options: trigger,
+ pipeline: upstream_pipeline
+ )
end
let(:service) { described_class.new(upstream_project, user) }
@@ -704,8 +707,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute', feature_category
context 'when user does not have access to push protected branch of downstream project' do
before do
- create(:protected_branch, :maintainers_can_push,
- project: downstream_project, name: 'feature')
+ create(:protected_branch, :maintainers_can_push, project: downstream_project, name: 'feature')
end
it 'changes status of the bridge build' do
diff --git a/spec/services/ci/create_pipeline_service/artifacts_spec.rb b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
index e5e405492a0..c193900b2d3 100644
--- a/spec/services/ci/create_pipeline_service/artifacts_spec.rb
+++ b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :build_artifacts do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb
index f9640f99031..2a65f92bfd6 100644
--- a/spec/services/ci/create_pipeline_service/cache_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cache_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
context 'cache' do
let(:project) { create(:project, :custom_repo, files: files) }
let(:user) { project.first_owner }
@@ -38,7 +39,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
policy: 'pull-push',
untracked: true,
unprotect: false,
- when: 'on_success'
+ when: 'on_success',
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -71,7 +73,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -88,7 +91,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -122,7 +126,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
@@ -139,7 +144,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success',
- unprotect: false
+ unprotect: false,
+ fallback_keys: []
}
expect(pipeline).to be_persisted
diff --git a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
index 0ebcecdd6e6..036116dc037 100644
--- a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
+++ b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :continuous_integration do
describe 'creation errors and warnings' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
index 0d5017a763f..e6bdb2a3fc6 100644
--- a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb
@@ -2,11 +2,12 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness do
- let_it_be(:group) { create(:group, name: 'my-organization') }
+RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
+ let_it_be(:group) { create(:group) }
- let(:upstream_project) { create(:project, :repository, name: 'upstream', group: group) }
- let(:downstream_project) { create(:project, :repository, name: 'downstream', group: group) }
+ let(:upstream_project) { create(:project, :repository, group: group) }
+ let(:downstream_project) { create(:project, :repository, group: group) }
let(:user) { create(:user) }
let(:service) do
@@ -27,7 +28,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_fl
stage: test
resource_group: iOS
trigger:
- project: my-organization/downstream
+ project: #{downstream_project.full_path}
strategy: depend
YAML
end
diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
index dafa227c4c8..819946bfd27 100644
--- a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
+++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
index 3b042f05fc0..b78ad68194a 100644
--- a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
describe '!reference tags' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/dry_run_spec.rb b/spec/services/ci/create_pipeline_service/dry_run_spec.rb
index de1ed251c82..7136fa5dc13 100644
--- a/spec/services/ci/create_pipeline_service/dry_run_spec.rb
+++ b/spec/services/ci/create_pipeline_service/dry_run_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb
index b713cad2cad..96e54af43cd 100644
--- a/spec/services/ci/create_pipeline_service/environment_spec.rb
+++ b/spec/services/ci/create_pipeline_service/environment_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :pipeline_composition do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user) }
diff --git a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
index e84726d31f6..b40e504f99b 100644
--- a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :pipeline_composition do
let_it_be(:group) { create(:group, :private) }
let_it_be(:group_variable) { create(:ci_group_variable, group: group, key: 'RUNNER_TAG', value: 'group') }
let_it_be(:project) { create(:project, :repository, group: group) }
diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb
index f18b4883aaf..1280ab4b7bd 100644
--- a/spec/services/ci/create_pipeline_service/include_spec.rb
+++ b/spec/services/ci/create_pipeline_service/include_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService,
-:yaml_processor_feature_flag_corectness, feature_category: :pipeline_authoring do
+ :yaml_processor_feature_flag_corectness, feature_category: :pipeline_composition do
include RepoHelpers
context 'include:' do
diff --git a/spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb b/spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb
index 003d109a27c..b0730eaf215 100644
--- a/spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb
+++ b/spec/services/ci/create_pipeline_service/limit_active_jobs_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
let_it_be(:existing_pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb
index ecb24a61075..6a1987fcc7c 100644
--- a/spec/services/ci/create_pipeline_service/logger_spec.rb
+++ b/spec/services/ci/create_pipeline_service/logger_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService, # rubocop: disable RSpec/FilePath
- :yaml_processor_feature_flag_corectness,
- feature_category: :continuous_integration do
+ :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
describe 'pipeline logger' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -142,7 +142,7 @@ RSpec.describe Ci::CreatePipelineService, # rubocop: disable RSpec/FilePath
describe 'pipeline includes count' do
before do
- stub_const('Gitlab::Ci::Config::External::Context::MAX_INCLUDES', 2)
+ stub_const('Gitlab::Ci::Config::External::Context::TEMP_MAX_INCLUDES', 2)
end
context 'when the includes count exceeds the maximum' do
diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
index 80f48451e5c..25f6e43d600 100644
--- a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
+++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
context 'merge requests handling' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -30,11 +31,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when pushing a change' do
context 'when a merge request already exists' do
let!(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
+ create(
+ :merge_request,
+ source_project: project,
+ source_branch: 'feature',
+ target_project: project,
+ target_branch: 'master'
+ )
end
it 'does not create a pipeline' do
diff --git a/spec/services/ci/create_pipeline_service/needs_spec.rb b/spec/services/ci/create_pipeline_service/needs_spec.rb
index 38e330316ea..068cad68e64 100644
--- a/spec/services/ci/create_pipeline_service/needs_spec.rb
+++ b/spec/services/ci/create_pipeline_service/needs_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :pipeline_composition do
context 'needs' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/parallel_spec.rb b/spec/services/ci/create_pipeline_service/parallel_spec.rb
index 5ee378a9719..71434fe0b0c 100644
--- a/spec/services/ci/create_pipeline_service/parallel_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parallel_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
index cae88bb67cf..16555dd68d6 100644
--- a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
index eb17935967c..e644273df9a 100644
--- a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
index a87135cefdd..70c4eb49698 100644
--- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb
+++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :aggregate_failures,
-:ci_partitionable, feature_category: :continuous_integration do
+ :ci_partitionable, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
index db110bdc608..d935824e6cc 100644
--- a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
+++ b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
describe '.pre/.post stages' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
index dfa74870341..26a9484dbc4 100644
--- a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
RSpec.describe Ci::CreatePipelineService, :freeze_time,
- :clean_gitlab_redis_rate_limiting,
- :yaml_processor_feature_flag_corectness do
+ :clean_gitlab_redis_rate_limiting,
+ :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
describe 'rate limiting' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index 26bb8b7d006..87112137675 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -1,25 +1,18 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :pipeline_authoring do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :pipeline_composition do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
let(:ref) { 'refs/heads/master' }
let(:source) { :push }
- let(:service) { described_class.new(project, user, { ref: ref }) }
- let(:response) { execute_service }
+ let(:service) { described_class.new(project, user, initialization_params) }
+ let(:response) { service.execute(source) }
let(:pipeline) { response.payload }
let(:build_names) { pipeline.builds.pluck(:name) }
- def execute_service(before: '00000000', variables_attributes: nil)
- params = { ref: ref, before: before, after: project.commit(ref).sha, variables_attributes: variables_attributes }
-
- described_class
- .new(project, user, params)
- .execute(source) do |pipeline|
- yield(pipeline) if block_given?
- end
- end
+ let(:base_initialization_params) { { ref: ref, before: '00000000', after: project.commit(ref).sha, variables_attributes: nil } }
+ let(:initialization_params) { base_initialization_params }
context 'job:rules' do
let(:regular_job) { find_job('regular-job') }
@@ -393,6 +386,109 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
expect(regular_job.allow_failure).to eq(true)
end
end
+
+ context 'with needs:' do
+ let(:config) do
+ <<-EOY
+ job1:
+ script: ls
+
+ job2:
+ script: ls
+ rules:
+ - if: $var == null
+ needs: [job1]
+ - when: on_success
+
+ job3:
+ script: ls
+ rules:
+ - if: $var == null
+ needs: [job1]
+ - needs: [job2]
+
+ job4:
+ script: ls
+ needs: [job1]
+ rules:
+ - if: $var == null
+ needs: [job2]
+ - when: on_success
+ needs: [job3]
+ EOY
+ end
+
+ let(:job1) { pipeline.builds.find_by(name: 'job1') }
+ let(:job2) { pipeline.builds.find_by(name: 'job2') }
+ let(:job3) { pipeline.builds.find_by(name: 'job3') }
+ let(:job4) { pipeline.builds.find_by(name: 'job4') }
+
+ context 'when the `$var` rule matches' do
+ it 'creates a pipeline with overridden needs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job1', 'job2', 'job3', 'job4')
+
+ expect(job1.needs).to be_empty
+ expect(job2.needs).to contain_exactly(an_object_having_attributes(name: 'job1'))
+ expect(job3.needs).to contain_exactly(an_object_having_attributes(name: 'job1'))
+ expect(job4.needs).to contain_exactly(an_object_having_attributes(name: 'job2'))
+ end
+ end
+
+ context 'when the `$var` rule does not match' do
+ let(:initialization_params) { base_initialization_params.merge(variables_attributes: variables_attributes) }
+
+ let(:variables_attributes) do
+ [{ key: 'var', secret_value: 'SOME_VAR' }]
+ end
+
+ it 'creates a pipeline with overridden needs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job1', 'job2', 'job3', 'job4')
+
+ expect(job1.needs).to be_empty
+ expect(job2.needs).to be_empty
+ expect(job3.needs).to contain_exactly(an_object_having_attributes(name: 'job2'))
+ expect(job4.needs).to contain_exactly(an_object_having_attributes(name: 'job3'))
+ end
+ end
+
+ context 'when the FF introduce_rules_with_needs is disabled' do
+ before do
+ stub_feature_flags(introduce_rules_with_needs: false)
+ end
+
+ context 'when the `$var` rule matches' do
+ it 'creates a pipeline without overridden needs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job1', 'job2', 'job3', 'job4')
+
+ expect(job1.needs).to be_empty
+ expect(job2.needs).to be_empty
+ expect(job3.needs).to be_empty
+ expect(job4.needs).to contain_exactly(an_object_having_attributes(name: 'job1'))
+ end
+ end
+
+ context 'when the `$var` rule does not match' do
+ let(:initialization_params) { base_initialization_params.merge(variables_attributes: variables_attributes) }
+
+ let(:variables_attributes) do
+ [{ key: 'var', secret_value: 'SOME_VAR' }]
+ end
+
+ it 'creates a pipeline without overridden needs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job1', 'job2', 'job3', 'job4')
+
+ expect(job1.needs).to be_empty
+ expect(job2.needs).to be_empty
+ expect(job3.needs).to be_empty
+ expect(job4.needs).to contain_exactly(an_object_having_attributes(name: 'job1'))
+ end
+ end
+ end
+ end
end
context 'changes:' do
@@ -516,11 +612,10 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
)
end
+ let(:initialization_params) { base_initialization_params.merge(before: nil) }
let(:changed_file) { 'file2.txt' }
let(:ref) { 'feature_2' }
- let(:response) { execute_service(before: nil) }
-
context 'for jobs rules' do
let(:config) do
<<-EOY
@@ -1230,9 +1325,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
context 'with pipeline variables' do
- let(:pipeline) do
- execute_service(variables_attributes: variables_attributes).payload
- end
+ let(:initialization_params) { base_initialization_params.merge(variables_attributes: variables_attributes) }
let(:config) do
<<-EOY
@@ -1267,10 +1360,10 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
context 'with trigger variables' do
- let(:pipeline) do
- execute_service do |pipeline|
+ let(:response) do
+ service.execute(source) do |pipeline|
pipeline.variables.build(variables)
- end.payload
+ end
end
let(:config) do
@@ -1434,10 +1527,10 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
[{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAL' }]
end
- let(:pipeline) do
- execute_service do |pipeline|
+ let(:response) do
+ service.execute(source) do |pipeline|
pipeline.variables.build(variables)
- end.payload
+ end
end
let(:config) do
diff --git a/spec/services/ci/create_pipeline_service/scripts_spec.rb b/spec/services/ci/create_pipeline_service/scripts_spec.rb
index 50b558e505a..d541257a086 100644
--- a/spec/services/ci/create_pipeline_service/scripts_spec.rb
+++ b/spec/services/ci/create_pipeline_service/scripts_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -83,30 +84,5 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
options: { script: ["echo 'hello job3 script'"] }
)
end
-
- context 'when the FF ci_hooks_pre_get_sources_script is disabled' do
- before do
- stub_feature_flags(ci_hooks_pre_get_sources_script: false)
- end
-
- it 'creates jobs without hook data' do
- expect(pipeline).to be_created_successfully
- expect(pipeline.builds.find_by(name: 'job1')).to have_attributes(
- name: 'job1',
- stage: 'test',
- options: { script: ["echo 'hello job1 script'"] }
- )
- expect(pipeline.builds.find_by(name: 'job2')).to have_attributes(
- name: 'job2',
- stage: 'test',
- options: { script: ["echo 'hello job2 script'"] }
- )
- expect(pipeline.builds.find_by(name: 'job3')).to have_attributes(
- name: 'job3',
- stage: 'test',
- options: { script: ["echo 'hello job3 script'"] }
- )
- end
- end
end
end
diff --git a/spec/services/ci/create_pipeline_service/tags_spec.rb b/spec/services/ci/create_pipeline_service/tags_spec.rb
index 7450df11eac..cb2dbf1c3a4 100644
--- a/spec/services/ci/create_pipeline_service/tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/tags_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, feature_category: :continuous_integration do
describe 'tags:' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/variables_spec.rb b/spec/services/ci/create_pipeline_service/variables_spec.rb
index fd138bde656..aac9a0c9c2d 100644
--- a/spec/services/ci/create_pipeline_service/variables_spec.rb
+++ b/spec/services/ci/create_pipeline_service/variables_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness,
+ feature_category: :secrets_management do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index b0ba07ea295..b08dda72a69 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -794,7 +794,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
before do
config = YAML.dump(
deploy: {
- environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_BUILD_ID" },
+ environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_JOB_ID" },
script: 'ls'
}
)
@@ -802,7 +802,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
stub_ci_pipeline_yaml_file(config)
end
- it 'skipps persisted variables in environment name' do
+ it 'skips persisted variables in environment name' do
result = execute_service.payload
expect(result).to be_persisted
@@ -810,6 +810,32 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
end
+ context 'when FF `ci_remove_legacy_predefined_variables` is disabled' do
+ before do
+ stub_feature_flags(ci_remove_legacy_predefined_variables: false)
+ end
+
+ context 'with environment name including persisted variables' do
+ before do
+ config = YAML.dump(
+ deploy: {
+ environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_BUILD_ID" },
+ script: 'ls'
+ }
+ )
+
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'skips persisted variables in environment name' do
+ result = execute_service.payload
+
+ expect(result).to be_persisted
+ expect(Environment.find_by(name: "review/id1/id2")).to be_present
+ end
+ end
+ end
+
context 'environment with Kubernetes configuration' do
let(:kubernetes_namespace) { 'custom-namespace' }
@@ -1167,8 +1193,10 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
context 'when pipeline is running for a tag' do
before do
- config = YAML.dump(test: { script: 'test', only: ['branches'] },
- deploy: { script: 'deploy', only: ['tags'] })
+ config = YAML.dump(
+ test: { script: 'test', only: ['branches'] },
+ deploy: { script: 'deploy', only: ['tags'] }
+ )
stub_ci_pipeline_yaml_file(config)
end
@@ -1343,11 +1371,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
describe 'Pipeline for external pull requests' do
let(:response) do
- execute_service(source: source,
- external_pull_request: pull_request,
- ref: ref_name,
- source_sha: source_sha,
- target_sha: target_sha)
+ execute_service(
+ source: source,
+ external_pull_request: pull_request,
+ ref: ref_name,
+ source_sha: source_sha,
+ target_sha: target_sha
+ )
end
let(:pipeline) { response.payload }
@@ -1499,11 +1529,13 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
describe 'Pipelines for merge requests' do
let(:response) do
- execute_service(source: source,
- merge_request: merge_request,
- ref: ref_name,
- source_sha: source_sha,
- target_sha: target_sha)
+ execute_service(
+ source: source,
+ merge_request: merge_request,
+ ref: ref_name,
+ source_sha: source_sha,
+ target_sha: target_sha
+ )
end
let(:pipeline) { response.payload }
@@ -1898,5 +1930,278 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
end
end
+
+ describe 'pipeline components' do
+ let(:components_project) do
+ create(:project, :repository, creator: user, namespace: user.namespace)
+ end
+
+ let(:component_path) do
+ "#{Gitlab.config.gitlab.host}/#{components_project.full_path}/my-component@v0.1"
+ end
+
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ suffix:
+ default: my-job
+ ---
+ test-$[[ inputs.suffix ]]:
+ stage: $[[ inputs.stage ]]
+ script: run tests
+ YAML
+ end
+
+ let(:sha) do
+ components_project.repository.create_file(
+ user,
+ 'my-component/template.yml',
+ template,
+ message: 'Add my first CI component',
+ branch_name: 'master'
+ )
+ end
+
+ let(:config) do
+ <<~YAML
+ include:
+ - component: #{component_path}
+ inputs:
+ stage: my-stage
+
+ stages:
+ - my-stage
+
+ test-1:
+ stage: my-stage
+ script: run test-1
+ YAML
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ context 'when there is no version with specified tag' do
+ before do
+ components_project.repository.add_tag(user, 'v0.01', sha)
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include "my-component@v0.1' - content not found"
+ end
+ end
+
+ context 'when there is a proper revision available' do
+ before do
+ components_project.repository.add_tag(user, 'v0.1', sha)
+ end
+
+ context 'when component is valid' do
+ it 'creates a pipeline using a pipeline component' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors).to be_blank
+ expect(pipeline.statuses.count).to eq 2
+ expect(pipeline.statuses.map(&:name)).to match_array %w[test-1 test-my-job]
+ end
+ end
+
+ context 'when interpolation is invalid' do
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ ---
+ test:
+ stage: $[[ inputs.stage ]]
+ script: rspec --suite $[[ inputs.suite ]]
+ YAML
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include 'interpolation interrupted by errors, unknown interpolation key: `suite`'
+ end
+ end
+
+ context 'when there is a syntax error in the template' do
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ ---
+ :test
+ stage: $[[ inputs.stage ]]
+ YAML
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include 'content does not have a valid YAML syntax'
+ end
+ end
+ end
+ end
+
+ # TODO: Remove this test section when include:with is removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/408369
+ describe 'pipeline components using include:with instead of include:inputs' do
+ let(:components_project) do
+ create(:project, :repository, creator: user, namespace: user.namespace)
+ end
+
+ let(:component_path) do
+ "#{Gitlab.config.gitlab.host}/#{components_project.full_path}/my-component@v0.1"
+ end
+
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ suffix:
+ default: my-job
+ ---
+ test-$[[ inputs.suffix ]]:
+ stage: $[[ inputs.stage ]]
+ script: run tests
+ YAML
+ end
+
+ let(:sha) do
+ components_project.repository.create_file(
+ user,
+ 'my-component/template.yml',
+ template,
+ message: 'Add my first CI component',
+ branch_name: 'master'
+ )
+ end
+
+ let(:config) do
+ <<~YAML
+ include:
+ - component: #{component_path}
+ with:
+ stage: my-stage
+
+ stages:
+ - my-stage
+
+ test-1:
+ stage: my-stage
+ script: run test-1
+ YAML
+ end
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ context 'when there is no version with specified tag' do
+ before do
+ components_project.repository.add_tag(user, 'v0.01', sha)
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include "my-component@v0.1' - content not found"
+ end
+ end
+
+ context 'when there is a proper revision available' do
+ before do
+ components_project.repository.add_tag(user, 'v0.1', sha)
+ end
+
+ context 'when component is valid' do
+ it 'creates a pipeline using a pipeline component' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors).to be_blank
+ expect(pipeline.statuses.count).to eq 2
+ expect(pipeline.statuses.map(&:name)).to match_array %w[test-1 test-my-job]
+ end
+ end
+
+ context 'when interpolation is invalid' do
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ ---
+ test:
+ stage: $[[ inputs.stage ]]
+ script: rspec --suite $[[ inputs.suite ]]
+ YAML
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include 'interpolation interrupted by errors, unknown interpolation key: `suite`'
+ end
+ end
+
+ context 'when there is a syntax error in the template' do
+ let(:template) do
+ <<~YAML
+ spec:
+ inputs:
+ stage:
+ ---
+ :test
+ stage: $[[ inputs.stage ]]
+ YAML
+ end
+
+ it 'does not create a pipeline' do
+ response = execute_service(save_on_errors: true)
+
+ pipeline = response.payload
+
+ expect(pipeline).to be_persisted
+ expect(pipeline.yaml_errors)
+ .to include 'content does not have a valid YAML syntax'
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/services/ci/create_web_ide_terminal_service_spec.rb b/spec/services/ci/create_web_ide_terminal_service_spec.rb
index 3462b48cfe7..b22ca1472b7 100644
--- a/spec/services/ci/create_web_ide_terminal_service_spec.rb
+++ b/spec/services/ci/create_web_ide_terminal_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreateWebIdeTerminalService do
+RSpec.describe Ci::CreateWebIdeTerminalService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/daily_build_group_report_result_service_spec.rb b/spec/services/ci/daily_build_group_report_result_service_spec.rb
index 32651247adb..bb6ce559fbd 100644
--- a/spec/services/ci/daily_build_group_report_result_service_spec.rb
+++ b/spec/services/ci/daily_build_group_report_result_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
+RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute', feature_category: :continuous_integration do
let_it_be(:group) { create(:group, :private) }
let_it_be(:pipeline) { create(:ci_pipeline, project: create(:project, group: group), created_at: '2020-02-06 00:01:10') }
let_it_be(:rspec_job) { create(:ci_build, pipeline: pipeline, name: 'rspec 3/3', coverage: 80) }
diff --git a/spec/services/ci/delete_objects_service_spec.rb b/spec/services/ci/delete_objects_service_spec.rb
index 448f8979681..939b72cef3b 100644
--- a/spec/services/ci/delete_objects_service_spec.rb
+++ b/spec/services/ci/delete_objects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DeleteObjectsService, :aggregate_failure do
+RSpec.describe Ci::DeleteObjectsService, :aggregate_failures, feature_category: :continuous_integration do
let(:service) { described_class.new }
let(:artifact) { create(:ci_job_artifact, :archive) }
let(:data) { [artifact] }
diff --git a/spec/services/ci/delete_unit_tests_service_spec.rb b/spec/services/ci/delete_unit_tests_service_spec.rb
index 4c63c513d48..2f07e709107 100644
--- a/spec/services/ci/delete_unit_tests_service_spec.rb
+++ b/spec/services/ci/delete_unit_tests_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DeleteUnitTestsService do
+RSpec.describe Ci::DeleteUnitTestsService, feature_category: :continuous_integration do
describe '#execute' do
let!(:unit_test_1) { create(:ci_unit_test) }
let!(:unit_test_2) { create(:ci_unit_test) }
diff --git a/spec/services/ci/deployments/destroy_service_spec.rb b/spec/services/ci/deployments/destroy_service_spec.rb
index 60a57c05728..d0e7f5acb2b 100644
--- a/spec/services/ci/deployments/destroy_service_spec.rb
+++ b/spec/services/ci/deployments/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::Deployments::DestroyService do
+RSpec.describe ::Ci::Deployments::DestroyService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb
index 6bd7fe7559c..a1883d90b0a 100644
--- a/spec/services/ci/destroy_pipeline_service_spec.rb
+++ b/spec/services/ci/destroy_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::DestroyPipelineService do
+RSpec.describe ::Ci::DestroyPipelineService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let!(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.id) }
diff --git a/spec/services/ci/destroy_secure_file_service_spec.rb b/spec/services/ci/destroy_secure_file_service_spec.rb
index 6a30d33f4ca..321efc2ed71 100644
--- a/spec/services/ci/destroy_secure_file_service_spec.rb
+++ b/spec/services/ci/destroy_secure_file_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::DestroySecureFileService do
+RSpec.describe ::Ci::DestroySecureFileService, feature_category: :continuous_integration do
let_it_be(:maintainer_user) { create(:user) }
let_it_be(:developer_user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/ci/disable_user_pipeline_schedules_service_spec.rb b/spec/services/ci/disable_user_pipeline_schedules_service_spec.rb
index 4ff8dcf075b..d422cf0dab9 100644
--- a/spec/services/ci/disable_user_pipeline_schedules_service_spec.rb
+++ b/spec/services/ci/disable_user_pipeline_schedules_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DisableUserPipelineSchedulesService do
+RSpec.describe Ci::DisableUserPipelineSchedulesService, feature_category: :continuous_integration do
describe '#execute' do
let(:user) { create(:user) }
diff --git a/spec/services/ci/drop_pipeline_service_spec.rb b/spec/services/ci/drop_pipeline_service_spec.rb
index ddb53712d9c..ed45b3460c1 100644
--- a/spec/services/ci/drop_pipeline_service_spec.rb
+++ b/spec/services/ci/drop_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::DropPipelineService do
+RSpec.describe Ci::DropPipelineService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let(:failure_reason) { :user_blocked }
diff --git a/spec/services/ci/ensure_stage_service_spec.rb b/spec/services/ci/ensure_stage_service_spec.rb
index 026814edda6..5d6025095a1 100644
--- a/spec/services/ci/ensure_stage_service_spec.rb
+++ b/spec/services/ci/ensure_stage_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::EnsureStageService, '#execute' do
+RSpec.describe Ci::EnsureStageService, '#execute', feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/expire_pipeline_cache_service_spec.rb b/spec/services/ci/expire_pipeline_cache_service_spec.rb
index 8cfe756faf3..3d0ce456aa5 100644
--- a/spec/services/ci/expire_pipeline_cache_service_spec.rb
+++ b/spec/services/ci/expire_pipeline_cache_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ExpirePipelineCacheService do
+RSpec.describe Ci::ExpirePipelineCacheService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb
index d5881d3b204..1b548aaf614 100644
--- a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb
+++ b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do
+RSpec.describe Ci::ExternalPullRequests::CreatePipelineService, feature_category: :continuous_integration do
describe '#execute' do
let_it_be(:project) { create(:project, :auto_devops, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ci/find_exposed_artifacts_service_spec.rb b/spec/services/ci/find_exposed_artifacts_service_spec.rb
index 6e11c153a75..69360e73b86 100644
--- a/spec/services/ci/find_exposed_artifacts_service_spec.rb
+++ b/spec/services/ci/find_exposed_artifacts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::FindExposedArtifactsService do
+RSpec.describe Ci::FindExposedArtifactsService, feature_category: :build_artifacts do
include Gitlab::Routing
let(:metadata) do
diff --git a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb
index 63bc7a1caf8..c33b182e9a9 100644
--- a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb
+++ b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::GenerateCodequalityMrDiffReportService do
+RSpec.describe Ci::GenerateCodequalityMrDiffReportService, feature_category: :code_review_workflow do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/generate_coverage_reports_service_spec.rb b/spec/services/ci/generate_coverage_reports_service_spec.rb
index 212e6be9d07..811431bf9d6 100644
--- a/spec/services/ci/generate_coverage_reports_service_spec.rb
+++ b/spec/services/ci/generate_coverage_reports_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::GenerateCoverageReportsService do
+RSpec.describe Ci::GenerateCoverageReportsService, feature_category: :code_testing do
let_it_be(:project) { create(:project, :repository) }
let(:service) { described_class.new(project) }
diff --git a/spec/services/ci/generate_kubeconfig_service_spec.rb b/spec/services/ci/generate_kubeconfig_service_spec.rb
index c0858b0f0c9..a03c6ef0c9d 100644
--- a/spec/services/ci/generate_kubeconfig_service_spec.rb
+++ b/spec/services/ci/generate_kubeconfig_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::GenerateKubeconfigService do
+RSpec.describe Ci::GenerateKubeconfigService, feature_category: :deployment_management do
describe '#execute' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
@@ -13,12 +13,12 @@ RSpec.describe Ci::GenerateKubeconfigService do
let_it_be(:project_agent_authorization) do
agent = create(:cluster_agent, project: agent_project)
- create(:agent_project_authorization, agent: agent, project: project)
+ create(:agent_ci_access_project_authorization, agent: agent, project: project)
end
let_it_be(:group_agent_authorization) do
agent = create(:cluster_agent, project: agent_project)
- create(:agent_group_authorization, agent: agent, group: group)
+ create(:agent_ci_access_group_authorization, agent: agent, group: group)
end
let(:template) do
@@ -33,7 +33,7 @@ RSpec.describe Ci::GenerateKubeconfigService do
let(:agent_authorizations) { [project_agent_authorization, group_agent_authorization] }
let(:filter_service) do
instance_double(
- ::Clusters::Agents::FilterAuthorizationsService,
+ ::Clusters::Agents::Authorizations::CiAccess::FilterService,
execute: agent_authorizations
)
end
@@ -42,7 +42,7 @@ RSpec.describe Ci::GenerateKubeconfigService do
before do
allow(Gitlab::Kubernetes::Kubeconfig::Template).to receive(:new).and_return(template)
- allow(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).and_return(filter_service)
+ allow(::Clusters::Agents::Authorizations::CiAccess::FilterService).to receive(:new).and_return(filter_service)
end
it 'returns a Kubeconfig Template' do
@@ -59,7 +59,7 @@ RSpec.describe Ci::GenerateKubeconfigService do
end
it "filters the pipeline's agents by `nil` environment" do
- expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with(
+ expect(::Clusters::Agents::Authorizations::CiAccess::FilterService).to receive(:new).with(
pipeline.cluster_agent_authorizations,
environment: nil
)
@@ -89,7 +89,7 @@ RSpec.describe Ci::GenerateKubeconfigService do
subject(:execute) { described_class.new(pipeline, token: build.token, environment: 'production').execute }
it "filters the pipeline's agents by the specified environment" do
- expect(::Clusters::Agents::FilterAuthorizationsService).to receive(:new).with(
+ expect(::Clusters::Agents::Authorizations::CiAccess::FilterService).to receive(:new).with(
pipeline.cluster_agent_authorizations,
environment: 'production'
)
diff --git a/spec/services/ci/generate_terraform_reports_service_spec.rb b/spec/services/ci/generate_terraform_reports_service_spec.rb
index c32e8bcaeb8..b2142d391b8 100644
--- a/spec/services/ci/generate_terraform_reports_service_spec.rb
+++ b/spec/services/ci/generate_terraform_reports_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::GenerateTerraformReportsService do
+RSpec.describe Ci::GenerateTerraformReportsService, feature_category: :infrastructure_as_code do
let_it_be(:project) { create(:project, :repository) }
describe '#execute' do
diff --git a/spec/services/ci/job_artifacts/bulk_delete_by_project_service_spec.rb b/spec/services/ci/job_artifacts/bulk_delete_by_project_service_spec.rb
new file mode 100644
index 00000000000..a180837f9a9
--- /dev/null
+++ b/spec/services/ci/job_artifacts/bulk_delete_by_project_service_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Ci::JobArtifacts::BulkDeleteByProjectService, "#execute", feature_category: :build_artifacts do
+ subject(:execute) do
+ described_class.new(
+ job_artifact_ids: job_artifact_ids,
+ current_user: current_user,
+ project: project).execute
+ end
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:build, reload: true) do
+ create(:ci_build, :artifacts, :trace_artifact, user: current_user)
+ end
+
+ let_it_be(:project) { build.project }
+ let_it_be(:job_artifact_ids) { build.job_artifacts.map(&:id) }
+
+ describe '#execute' do
+ context 'when number of artifacts exceeds limits to delete' do
+ let_it_be(:second_build, reload: true) do
+ create(:ci_build, :artifacts, :trace_artifact, user: current_user, project: project)
+ end
+
+ let_it_be(:job_artifact_ids) { ::Ci::JobArtifact.all.map(&:id) }
+
+ before do
+ project.add_maintainer(current_user)
+ stub_const("#{described_class}::JOB_ARTIFACTS_COUNT_LIMIT", 1)
+ end
+
+ it 'fails to destroy' do
+ result = execute
+
+ expect(result).to be_error
+ expect(result[:message]).to eq('Can only delete up to 1 job artifacts per call')
+ end
+ end
+
+ context 'when requested not existing artifacts do delete' do
+ let_it_be(:deleted_build, reload: true) do
+ create(:ci_build, :artifacts, :trace_artifact, user: current_user, project: project)
+ end
+
+ let_it_be(:deleted_job_artifacts) { deleted_build.job_artifacts }
+ let_it_be(:job_artifact_ids) { ::Ci::JobArtifact.all.map(&:id) }
+
+ before do
+ project.add_maintainer(current_user)
+ deleted_job_artifacts.each(&:destroy!)
+ end
+
+ it 'fails to destroy' do
+ result = execute
+
+ expect(result).to be_error
+ expect(result[:message]).to eq("Artifacts (#{deleted_job_artifacts.map(&:id).join(',')}) not found")
+ end
+ end
+
+ context 'when maintainer has access to the project' do
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ it 'is successful' do
+ result = execute
+
+ expect(result).to be_success
+ expect(result.payload).to eq(
+ {
+ destroyed_count: job_artifact_ids.count,
+ destroyed_ids: job_artifact_ids,
+ errors: []
+ }
+ )
+ expect(::Ci::JobArtifact.where(id: job_artifact_ids).count).to eq(0)
+ end
+
+ context 'and partially owns artifacts' do
+ let_it_be(:orphan_artifact) { create(:ci_job_artifact, :archive) }
+ let_it_be(:orphan_artifact_id) { orphan_artifact.id }
+ let_it_be(:owned_artifacts_ids) { build.job_artifacts.erasable.map(&:id) }
+ let_it_be(:job_artifact_ids) { [orphan_artifact_id] + owned_artifacts_ids }
+
+ it 'fails to destroy' do
+ result = execute
+
+ expect(result).to be_error
+ expect(result[:message]).to be('Not all artifacts belong to requested project')
+ expect(::Ci::JobArtifact.where(id: job_artifact_ids).count).to eq(3)
+ end
+ end
+
+ context 'and request all artifacts from a different project' do
+ let_it_be(:different_project_artifact) { create(:ci_job_artifact, :archive) }
+ let_it_be(:job_artifact_ids) { [different_project_artifact] }
+
+ let_it_be(:different_build, reload: true) do
+ create(:ci_build, :artifacts, :trace_artifact, user: current_user)
+ end
+
+ let_it_be(:different_project) { different_build.project }
+
+ before do
+ different_project.add_maintainer(current_user)
+ end
+
+ it 'returns a error' do
+ result = execute
+
+ expect(result).to be_error
+ expect(result[:message]).to be('Not all artifacts belong to requested project')
+ expect(::Ci::JobArtifact.where(id: job_artifact_ids).count).to eq(job_artifact_ids.count)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index 47e9e5994ef..f71d7feb04a 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -2,108 +2,215 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::CreateService do
+RSpec.describe Ci::JobArtifacts::CreateService, :clean_gitlab_redis_shared_state, feature_category: :build_artifacts do
+ include WorkhorseHelpers
+ include Gitlab::Utils::Gzip
+
let_it_be(:project) { create(:project) }
let(:service) { described_class.new(job) }
let(:job) { create(:ci_build, project: project) }
- let(:artifacts_sha256) { '0' * 64 }
- let(:metadata_file) { nil }
- let(:artifacts_file) do
- file_to_upload('spec/fixtures/ci_build_artifacts.zip', sha256: artifacts_sha256)
- end
+ describe '#authorize', :aggregate_failures do
+ let(:artifact_type) { 'archive' }
+ let(:filesize) { nil }
- let(:params) do
- {
- 'artifact_type' => 'archive',
- 'artifact_format' => 'zip'
- }.with_indifferent_access
- end
+ subject(:authorize) { service.authorize(artifact_type: artifact_type, filesize: filesize) }
- def file_to_upload(path, params = {})
- upload = Tempfile.new('upload')
- FileUtils.copy(path, upload.path)
- # This is a workaround for https://github.com/docker/for-linux/issues/1015
- FileUtils.touch(upload.path)
+ shared_examples_for 'handling lsif artifact' do
+ context 'when artifact is lsif' do
+ let(:artifact_type) { 'lsif' }
- UploadedFile.new(upload.path, **params)
- end
+ it 'includes ProcessLsif in the headers' do
+ expect(authorize[:headers][:ProcessLsif]).to eq(true)
+ end
+ end
+ end
- describe '#execute' do
- subject { service.execute(artifacts_file, params, metadata_file: metadata_file) }
+ shared_examples_for 'validating requirements' do
+ context 'when filesize is specified' do
+ let(:max_artifact_size) { 10 }
- context 'when artifacts file is uploaded' do
- it 'logs the created artifact' do
- expect(Gitlab::Ci::Artifacts::Logger)
- .to receive(:log_created)
- .with(an_instance_of(Ci::JobArtifact))
+ before do
+ allow(Ci::JobArtifact)
+ .to receive(:max_artifact_size)
+ .with(type: artifact_type, project: project)
+ .and_return(max_artifact_size)
+ end
- subject
- end
+ context 'and filesize exceeds the limit' do
+ let(:filesize) { max_artifact_size + 1 }
- it 'returns artifact in the response' do
- response = subject
- new_artifact = job.job_artifacts.last
+ it 'returns error' do
+ expect(authorize[:status]).to eq(:error)
+ end
+ end
- expect(response[:artifact]).to eq(new_artifact)
- end
+ context 'and filesize does not exceed the limit' do
+ let(:filesize) { max_artifact_size - 1 }
- it 'saves artifact for the given type' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(1)
+ it 'returns success' do
+ expect(authorize[:status]).to eq(:success)
+ end
+ end
+ end
+ end
- new_artifact = job.job_artifacts.last
- expect(new_artifact.project).to eq(job.project)
- expect(new_artifact.file).to be_present
- expect(new_artifact.file_type).to eq(params['artifact_type'])
- expect(new_artifact.file_format).to eq(params['artifact_format'])
- expect(new_artifact.file_sha256).to eq(artifacts_sha256)
- expect(new_artifact.locked).to eq(job.pipeline.locked)
+ shared_examples_for 'uploading to temp location' do |store_type|
+ # We are not testing the entire headers here because this is fully tested
+ # in workhorse_authorize's spec. We just want to confirm that it indeed used the temp path
+ # by checking some indicators in the headers returned.
+ if store_type == :object_storage
+ it 'includes the authorize headers' do
+ expect(authorize[:status]).to eq(:success)
+ expect(authorize[:headers][:RemoteObject][:StoreURL]).to include(ObjectStorage::TMP_UPLOAD_PATH)
+ end
+ else
+ it 'includes the authorize headers' do
+ expect(authorize[:status]).to eq(:success)
+ expect(authorize[:headers][:TempPath]).to include(ObjectStorage::TMP_UPLOAD_PATH)
+ end
end
- it 'sets accessibility level by default to public' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(1)
+ it_behaves_like 'handling lsif artifact'
+ it_behaves_like 'validating requirements'
+ end
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_public_accessibility
- end
+ context 'when object storage is enabled' do
+ context 'and direct upload is enabled' do
+ let(:final_store_path) { '12/34/abc-123' }
- context 'when accessibility level passed as private' do
before do
- params.merge!('accessibility' => 'private')
+ stub_artifacts_object_storage(JobArtifactUploader, direct_upload: true)
+ allow(JobArtifactUploader).to receive(:generate_final_store_path).and_return(final_store_path)
end
- it 'sets accessibility level to private' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(1)
+ it 'includes the authorize headers' do
+ expect(authorize[:status]).to eq(:success)
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_private_accessibility
+ expect(authorize[:headers][:RemoteObject][:ID]).to eq(final_store_path)
+
+ # We are not testing the entire headers here because this is fully tested
+ # in workhorse_authorize's spec. We just want to confirm that it indeed used the final path
+ # by checking some indicators in the headers returned.
+ expect(authorize[:headers][:RemoteObject][:StoreURL])
+ .to include(final_store_path)
+
+ # We have to ensure to tell Workhorse to skip deleting the file after upload
+ # because we are uploading the file to its final location
+ expect(authorize[:headers][:RemoteObject][:SkipDelete]).to eq(true)
+ end
+
+ it_behaves_like 'handling lsif artifact'
+ it_behaves_like 'validating requirements'
+
+ context 'with ci_artifacts_upload_to_final_location feature flag disabled' do
+ before do
+ stub_feature_flags(ci_artifacts_upload_to_final_location: false)
+ end
+
+ it_behaves_like 'uploading to temp location', :object_storage
end
end
- context 'when accessibility passed as public' do
+ context 'and direct upload is disabled' do
before do
- params.merge!('accessibility' => 'public')
+ stub_artifacts_object_storage(JobArtifactUploader, direct_upload: false)
end
+ it_behaves_like 'uploading to temp location', :local_storage
+ end
+ end
+
+ context 'when object storage is disabled' do
+ it_behaves_like 'uploading to temp location', :local_storage
+ end
+ end
+
+ describe '#execute' do
+ let(:artifacts_sha256) { '0' * 64 }
+ let(:metadata_file) { nil }
+
+ let(:params) do
+ {
+ 'artifact_type' => 'archive',
+ 'artifact_format' => 'zip'
+ }.with_indifferent_access
+ end
+
+ subject(:execute) { service.execute(artifacts_file, params, metadata_file: metadata_file) }
+
+ shared_examples_for 'handling accessibility' do
+ shared_examples 'public accessibility' do
it 'sets accessibility to public level' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(1)
+ expect(job.job_artifacts).to all be_public_accessibility
+ end
+ end
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_public_accessibility
+ shared_examples 'private accessibility' do
+ it 'sets accessibility to private level' do
+ expect(job.job_artifacts).to all be_private_accessibility
+ end
+ end
+
+ context 'when non_public_artifacts flag is disabled' do
+ before do
+ stub_feature_flags(non_public_artifacts: false)
+ end
+
+ it_behaves_like 'public accessibility'
+ end
+
+ context 'when non_public_artifacts flag is enabled' do
+ context 'and accessibility is defined in the params' do
+ context 'and is passed as private' do
+ before do
+ params.merge!('accessibility' => 'private')
+ end
+
+ it_behaves_like 'private accessibility'
+ end
+
+ context 'and is passed as public' do
+ before do
+ params.merge!('accessibility' => 'public')
+ end
+
+ it_behaves_like 'public accessibility'
+ end
+ end
+
+ context 'and accessibility is not defined in the params' do
+ context 'and job has no public artifacts defined in its CI config' do
+ it_behaves_like 'public accessibility'
+ end
+
+ context 'and job artifacts defined as private in the CI config' do
+ let(:job) { create(:ci_build, :with_private_artifacts_config, project: project) }
+
+ it_behaves_like 'private accessibility'
+ end
+
+ context 'and job artifacts defined as public in the CI config' do
+ let(:job) { create(:ci_build, :with_public_artifacts_config, project: project) }
+
+ it_behaves_like 'public accessibility'
+ end
end
end
context 'when accessibility passed as invalid value' do
before do
- params.merge!('accessibility' => 'invalid_value')
+ params.merge!('accessibility' => 'foo')
end
it 'fails with argument error' do
- expect { subject }.to raise_error(ArgumentError)
+ expect { execute }.to raise_error(ArgumentError, "'foo' is not a valid accessibility")
end
end
+ end
+ shared_examples_for 'handling metadata file' do
context 'when metadata file is also uploaded' do
let(:metadata_file) do
file_to_upload('spec/fixtures/ci_build_artifacts_metadata.gz', sha256: artifacts_sha256)
@@ -113,8 +220,8 @@ RSpec.describe Ci::JobArtifacts::CreateService do
stub_application_setting(default_artifacts_expire_in: '1 day')
end
- it 'saves metadata artifact' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(2)
+ it 'creates a new metadata job artifact' do
+ expect { execute }.to change { Ci::JobArtifact.where(file_type: :metadata).count }.by(1)
new_artifact = job.job_artifacts.last
expect(new_artifact.project).to eq(job.project)
@@ -125,13 +232,6 @@ RSpec.describe Ci::JobArtifacts::CreateService do
expect(new_artifact.locked).to eq(job.pipeline.locked)
end
- it 'sets accessibility by default to public' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(2)
-
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_public_accessibility
- end
-
it 'logs the created artifact and metadata' do
expect(Gitlab::Ci::Artifacts::Logger)
.to receive(:log_created)
@@ -140,36 +240,12 @@ RSpec.describe Ci::JobArtifacts::CreateService do
subject
end
- context 'when accessibility level passed as private' do
- before do
- params.merge!('accessibility' => 'private')
- end
-
- it 'sets accessibility to private level' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(2)
-
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_private_accessibility
- end
- end
-
- context 'when accessibility passed as public' do
- before do
- params.merge!('accessibility' => 'public')
- end
-
- it 'sets accessibility level to public' do
- expect { subject }.to change { Ci::JobArtifact.count }.by(2)
-
- new_artifact = job.job_artifacts.last
- expect(new_artifact).to be_public_accessibility
- end
- end
+ it_behaves_like 'handling accessibility'
it 'sets expiration date according to application settings' do
expected_expire_at = 1.day.from_now
- expect(subject).to match(a_hash_including(status: :success, artifact: anything))
+ expect(execute).to match(a_hash_including(status: :success, artifact: anything))
archive_artifact, metadata_artifact = job.job_artifacts.last(2)
expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at)
@@ -185,7 +261,7 @@ RSpec.describe Ci::JobArtifacts::CreateService do
it 'sets expiration date according to the parameter' do
expected_expire_at = 2.hours.from_now
- expect(subject).to match(a_hash_including(status: :success, artifact: anything))
+ expect(execute).to match(a_hash_including(status: :success, artifact: anything))
archive_artifact, metadata_artifact = job.job_artifacts.last(2)
expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at)
@@ -202,7 +278,7 @@ RSpec.describe Ci::JobArtifacts::CreateService do
it 'sets expiration date according to the parameter' do
expected_expire_at = nil
- expect(subject).to be_truthy
+ expect(execute).to be_truthy
archive_artifact, metadata_artifact = job.job_artifacts.last(2)
expect(job.artifacts_expire_at).to eq(expected_expire_at)
@@ -213,96 +289,237 @@ RSpec.describe Ci::JobArtifacts::CreateService do
end
end
- context 'when artifacts file already exists' do
- let!(:existing_artifact) do
- create(:ci_job_artifact, :archive, file_sha256: existing_sha256, job: job)
- end
+ shared_examples_for 'handling dotenv' do |storage_type|
+ context 'when artifact type is dotenv' do
+ let(:params) do
+ {
+ 'artifact_type' => 'dotenv',
+ 'artifact_format' => 'gzip'
+ }.with_indifferent_access
+ end
- context 'when sha256 of uploading artifact is the same of the existing one' do
- let(:existing_sha256) { artifacts_sha256 }
+ if storage_type == :object_storage
+ let(:object_body) { File.read('spec/fixtures/build.env.gz') }
+ let(:upload_filename) { 'build.env.gz' }
- it 'ignores the changes' do
- expect { subject }.not_to change { Ci::JobArtifact.count }
- expect(subject).to match(a_hash_including(status: :success))
+ before do
+ stub_request(:get, %r{s3.amazonaws.com/#{remote_path}})
+ .to_return(status: 200, body: File.read('spec/fixtures/build.env.gz'))
+ end
+ else
+ let(:artifacts_file) do
+ file_to_upload('spec/fixtures/build.env.gz', sha256: artifacts_sha256)
+ end
+ end
+
+ it 'calls parse service' do
+ expect_any_instance_of(Ci::ParseDotenvArtifactService) do |service|
+ expect(service).to receive(:execute).once.and_call_original
+ end
+
+ expect(execute[:status]).to eq(:success)
+ expect(job.job_variables.as_json(only: [:key, :value, :source])).to contain_exactly(
+ hash_including('key' => 'KEY1', 'value' => 'VAR1', 'source' => 'dotenv'),
+ hash_including('key' => 'KEY2', 'value' => 'VAR2', 'source' => 'dotenv'))
end
end
+ end
- context 'when sha256 of uploading artifact is different than the existing one' do
- let(:existing_sha256) { '1' * 64 }
+ shared_examples_for 'handling object storage errors' do
+ shared_examples 'rescues object storage error' do |klass, message, expected_message|
+ it "handles #{klass}" do
+ allow_next_instance_of(JobArtifactUploader) do |uploader|
+ allow(uploader).to receive(:store!).and_raise(klass, message)
+ end
- it 'returns error status' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_exception)
+ .and_call_original
- expect { subject }.not_to change { Ci::JobArtifact.count }
- expect(subject).to match(
+ expect(execute).to match(
a_hash_including(
- http_status: :bad_request, message: 'another artifact of the same type already exists', status: :error))
+ http_status: :service_unavailable,
+ message: expected_message || message,
+ status: :error))
end
end
+
+ it_behaves_like 'rescues object storage error',
+ Errno::EIO, 'some/path', 'Input/output error - some/path'
+
+ it_behaves_like 'rescues object storage error',
+ Google::Apis::ServerError, 'Server error'
+
+ it_behaves_like 'rescues object storage error',
+ Signet::RemoteServerError, 'The service is currently unavailable'
end
- context 'when artifact type is dotenv' do
- let(:artifacts_file) do
- file_to_upload('spec/fixtures/build.env.gz', sha256: artifacts_sha256)
- end
+ shared_examples_for 'validating requirements' do
+ context 'when filesize is specified' do
+ let(:max_artifact_size) { 10 }
+
+ before do
+ allow(Ci::JobArtifact)
+ .to receive(:max_artifact_size)
+ .with(type: 'archive', project: project)
+ .and_return(max_artifact_size)
- let(:params) do
- {
- 'artifact_type' => 'dotenv',
- 'artifact_format' => 'gzip'
- }.with_indifferent_access
+ allow(artifacts_file).to receive(:size).and_return(filesize)
+ end
+
+ context 'and filesize exceeds the limit' do
+ let(:filesize) { max_artifact_size + 1 }
+
+ it 'returns error' do
+ expect(execute[:status]).to eq(:error)
+ end
+ end
+
+ context 'and filesize does not exceed the limit' do
+ let(:filesize) { max_artifact_size - 1 }
+
+ it 'returns success' do
+ expect(execute[:status]).to eq(:success)
+ end
+ end
end
+ end
- it 'calls parse service' do
- expect_any_instance_of(Ci::ParseDotenvArtifactService) do |service|
- expect(service).to receive(:execute).once.and_call_original
+ shared_examples_for 'handling existing artifact' do
+ context 'when job already has an artifact of the same file type' do
+ let!(:existing_artifact) do
+ create(:ci_job_artifact, params[:artifact_type], file_sha256: existing_sha256, job: job)
end
- expect(subject[:status]).to eq(:success)
- expect(job.job_variables.as_json(only: [:key, :value, :source])).to contain_exactly(
- hash_including('key' => 'KEY1', 'value' => 'VAR1', 'source' => 'dotenv'),
- hash_including('key' => 'KEY2', 'value' => 'VAR2', 'source' => 'dotenv'))
+ context 'when sha256 of uploading artifact is the same of the existing one' do
+ let(:existing_sha256) { artifacts_sha256 }
+
+ it 'ignores the changes' do
+ expect { execute }.not_to change { Ci::JobArtifact.count }
+ expect(execute).to match(a_hash_including(status: :success))
+ end
+ end
+
+ context 'when sha256 of uploading artifact is different than the existing one' do
+ let(:existing_sha256) { '1' * 64 }
+
+ it 'returns error status' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
+
+ expect { execute }.not_to change { Ci::JobArtifact.count }
+ expect(execute).to match(
+ a_hash_including(
+ http_status: :bad_request,
+ message: 'another artifact of the same type already exists',
+ status: :error
+ )
+ )
+ end
+ end
+ end
+ end
+
+ shared_examples_for 'logging artifact' do
+ it 'logs the created artifact' do
+ expect(Gitlab::Ci::Artifacts::Logger)
+ .to receive(:log_created)
+ .with(an_instance_of(Ci::JobArtifact))
+
+ execute
end
end
- context 'with job partitioning', :ci_partitionable do
- let(:pipeline) { create(:ci_pipeline, project: project, partition_id: ci_testing_partition_id) }
- let(:job) { create(:ci_build, pipeline: pipeline) }
+ shared_examples_for 'handling uploads' do
+ context 'when artifacts file is uploaded' do
+ it 'creates a new job artifact' do
+ expect { execute }.to change { Ci::JobArtifact.count }.by(1)
- it 'sets partition_id on artifacts' do
- expect { subject }.to change { Ci::JobArtifact.count }
+ new_artifact = execute[:artifact]
+ expect(new_artifact).to eq(job.job_artifacts.last)
+ expect(new_artifact.project).to eq(job.project)
+ expect(new_artifact.file.filename).to eq(artifacts_file.original_filename)
+ expect(new_artifact.file_identifier).to eq(artifacts_file.original_filename)
+ expect(new_artifact.file_type).to eq(params['artifact_type'])
+ expect(new_artifact.file_format).to eq(params['artifact_format'])
+ expect(new_artifact.file_sha256).to eq(artifacts_sha256)
+ expect(new_artifact.locked).to eq(job.pipeline.locked)
+ expect(new_artifact.size).to eq(artifacts_file.size)
- artifacts_partitions = job.job_artifacts.map(&:partition_id).uniq
+ expect(execute[:status]).to eq(:success)
+ end
- expect(artifacts_partitions).to eq([ci_testing_partition_id])
+ it_behaves_like 'handling accessibility'
+ it_behaves_like 'handling metadata file'
+ it_behaves_like 'handling partitioning'
+ it_behaves_like 'logging artifact'
end
end
- shared_examples 'rescues object storage error' do |klass, message, expected_message|
- it "handles #{klass}" do
- allow_next_instance_of(JobArtifactUploader) do |uploader|
- allow(uploader).to receive(:store!).and_raise(klass, message)
+ shared_examples_for 'handling partitioning' do
+ context 'with job partitioned', :ci_partitionable do
+ let(:pipeline) { create(:ci_pipeline, project: project, partition_id: ci_testing_partition_id) }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'sets partition_id on artifacts' do
+ expect { execute }.to change { Ci::JobArtifact.count }
+
+ artifacts_partitions = job.job_artifacts.map(&:partition_id).uniq
+
+ expect(artifacts_partitions).to eq([ci_testing_partition_id])
end
+ end
+ end
- expect(Gitlab::ErrorTracking)
- .to receive(:track_exception)
- .and_call_original
+ context 'when object storage and direct upload is enabled' do
+ let(:fog_connection) { stub_artifacts_object_storage(JobArtifactUploader, direct_upload: true) }
+ let(:remote_path) { File.join(remote_store_path, remote_id) }
+ let(:object_body) { File.open('spec/fixtures/ci_build_artifacts.zip') }
+ let(:upload_filename) { 'artifacts.zip' }
+ let(:object) do
+ fog_connection.directories
+ .new(key: 'artifacts')
+ .files
+ .create( # rubocop:disable Rails/SaveBang
+ key: remote_path,
+ body: object_body
+ )
+ end
- expect(subject).to match(
- a_hash_including(
- http_status: :service_unavailable,
- message: expected_message || message,
- status: :error))
+ let(:artifacts_file) do
+ fog_to_uploaded_file(
+ object,
+ filename: upload_filename,
+ sha256: artifacts_sha256,
+ remote_id: remote_id
+ )
end
+
+ let(:remote_id) { 'generated-remote-id-12345' }
+ let(:remote_store_path) { ObjectStorage::TMP_UPLOAD_PATH }
+
+ it_behaves_like 'handling uploads'
+ it_behaves_like 'handling dotenv', :object_storage
+ it_behaves_like 'handling object storage errors'
+ it_behaves_like 'validating requirements'
end
- it_behaves_like 'rescues object storage error',
- Errno::EIO, 'some/path', 'Input/output error - some/path'
+ context 'when using local storage' do
+ let(:artifacts_file) do
+ file_to_upload('spec/fixtures/ci_build_artifacts.zip', sha256: artifacts_sha256)
+ end
+
+ it_behaves_like 'handling uploads'
+ it_behaves_like 'handling dotenv', :local_storage
+ it_behaves_like 'validating requirements'
+ end
+ end
- it_behaves_like 'rescues object storage error',
- Google::Apis::ServerError, 'Server error'
+ def file_to_upload(path, params = {})
+ upload = Tempfile.new('upload')
+ FileUtils.copy(path, upload.path)
+ # This is a workaround for https://github.com/docker/for-linux/issues/1015
+ FileUtils.touch(upload.path)
- it_behaves_like 'rescues object storage error',
- Signet::RemoteServerError, 'The service is currently unavailable'
+ UploadedFile.new(upload.path, **params)
end
end
diff --git a/spec/services/ci/job_artifacts/delete_project_artifacts_service_spec.rb b/spec/services/ci/job_artifacts/delete_project_artifacts_service_spec.rb
index 74fa42962f3..9c711e54b00 100644
--- a/spec/services/ci/job_artifacts/delete_project_artifacts_service_spec.rb
+++ b/spec/services/ci/job_artifacts/delete_project_artifacts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::DeleteProjectArtifactsService do
+RSpec.describe Ci::JobArtifacts::DeleteProjectArtifactsService, feature_category: :build_artifacts do
let_it_be(:project) { create(:project) }
subject { described_class.new(project: project) }
diff --git a/spec/services/ci/job_artifacts/delete_service_spec.rb b/spec/services/ci/job_artifacts/delete_service_spec.rb
index 78e8be48255..1560d0fc6f4 100644
--- a/spec/services/ci/job_artifacts/delete_service_spec.rb
+++ b/spec/services/ci/job_artifacts/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::DeleteService do
+RSpec.describe Ci::JobArtifacts::DeleteService, feature_category: :build_artifacts do
let_it_be(:build, reload: true) do
create(:ci_build, :artifacts, :trace_artifact, artifacts_expire_at: 100.days.from_now)
end
diff --git a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
index 457be67c1ea..cdbb0c0f8ce 100644
--- a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_shared_state,
-feature_category: :build_artifacts do
+ feature_category: :build_artifacts do
include ExclusiveLeaseHelpers
let(:service) { described_class.new }
@@ -39,32 +39,12 @@ feature_category: :build_artifacts do
second_artifact
end
- context 'with ci_destroy_unlocked_job_artifacts feature flag disabled' do
- before do
- stub_feature_flags(ci_destroy_unlocked_job_artifacts: false)
- end
-
- it 'performs a consistent number of queries' do
- control = ActiveRecord::QueryRecorder.new { service.execute }
-
- more_artifacts
-
- expect { subject }.not_to exceed_query_limit(control.count)
- end
- end
-
- context 'with ci_destroy_unlocked_job_artifacts feature flag enabled' do
- before do
- stub_feature_flags(ci_destroy_unlocked_job_artifacts: true)
- end
-
- it 'performs a consistent number of queries' do
- control = ActiveRecord::QueryRecorder.new { service.execute }
+ it 'performs a consistent number of queries' do
+ control = ActiveRecord::QueryRecorder.new { service.execute }
- more_artifacts
+ more_artifacts
- expect { subject }.not_to exceed_query_limit(control.count)
- end
+ expect { subject }.not_to exceed_query_limit(control.count)
end
end
@@ -251,6 +231,16 @@ feature_category: :build_artifacts do
end
end
+ context 'when some artifacts are trace' do
+ let!(:artifact) { create(:ci_job_artifact, :expired, job: job, locked: job.pipeline.locked) }
+ let!(:trace_artifact) { create(:ci_job_artifact, :trace, :expired, job: job, locked: job.pipeline.locked) }
+
+ it 'destroys only non trace artifacts' do
+ expect { subject }.to change { Ci::JobArtifact.count }.by(-1)
+ expect(trace_artifact).to be_persisted
+ end
+ end
+
context 'when all artifacts are locked' do
let!(:artifact) { create(:ci_job_artifact, :expired, job: locked_job, locked: locked_job.pipeline.locked) }
diff --git a/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb
index ca36c923dcf..f4839ccb04b 100644
--- a/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb
@@ -2,24 +2,37 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::DestroyAssociationsService do
+RSpec.describe Ci::JobArtifacts::DestroyAssociationsService, feature_category: :build_artifacts do
let_it_be(:project_1) { create(:project) }
let_it_be(:project_2) { create(:project) }
let_it_be(:artifact_1, refind: true) { create(:ci_job_artifact, :zip, project: project_1) }
- let_it_be(:artifact_2, refind: true) { create(:ci_job_artifact, :zip, project: project_2) }
- let_it_be(:artifact_3, refind: true) { create(:ci_job_artifact, :zip, project: project_1) }
+ let_it_be(:artifact_2, refind: true) { create(:ci_job_artifact, :junit, project: project_2) }
+ let_it_be(:artifact_3, refind: true) { create(:ci_job_artifact, :terraform, project: project_1) }
+ let_it_be(:artifact_4, refind: true) { create(:ci_job_artifact, :trace, project: project_2) }
+ let_it_be(:artifact_5, refind: true) { create(:ci_job_artifact, :metadata, project: project_2) }
- let(:artifacts) { Ci::JobArtifact.where(id: [artifact_1.id, artifact_2.id, artifact_3.id]) }
+ let_it_be(:locked_artifact, refind: true) { create(:ci_job_artifact, :zip, :locked, project: project_1) }
+
+ let(:artifact_ids_to_be_removed) { [artifact_1.id, artifact_2.id, artifact_3.id, artifact_4.id, artifact_5.id] }
+ let(:artifacts) { Ci::JobArtifact.where(id: artifact_ids_to_be_removed) }
let(:service) { described_class.new(artifacts) }
describe '#destroy_records' do
- it 'removes artifacts without updating statistics' do
+ it 'removes all types of artifacts without updating statistics' do
expect_next_instance_of(Ci::JobArtifacts::DestroyBatchService) do |service|
expect(service).to receive(:execute).with(update_stats: false).and_call_original
end
- expect { service.destroy_records }.to change { Ci::JobArtifact.count }.by(-3)
+ expect { service.destroy_records }.to change { Ci::JobArtifact.count }.by(-artifact_ids_to_be_removed.count)
+ end
+
+ context 'with a locked artifact' do
+ let(:artifact_ids_to_be_removed) { [artifact_1.id, locked_artifact.id] }
+
+ it 'removes all artifacts' do
+ expect { service.destroy_records }.to change { Ci::JobArtifact.count }.by(-artifact_ids_to_be_removed.count)
+ end
end
context 'when there are no artifacts' do
@@ -42,7 +55,11 @@ RSpec.describe Ci::JobArtifacts::DestroyAssociationsService do
have_attributes(amount: -artifact_1.size, ref: artifact_1.id),
have_attributes(amount: -artifact_3.size, ref: artifact_3.id)
]
- project2_increments = [have_attributes(amount: -artifact_2.size, ref: artifact_2.id)]
+ project2_increments = [
+ have_attributes(amount: -artifact_2.size, ref: artifact_2.id),
+ have_attributes(amount: -artifact_4.size, ref: artifact_4.id),
+ have_attributes(amount: -artifact_5.size, ref: artifact_5.id)
+ ]
expect(ProjectStatistics).to receive(:bulk_increment_statistic).once
.with(project_1, :build_artifacts_size, match_array(project1_increments))
diff --git a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
index cde42783d8c..6f9dcf47535 100644
--- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::DestroyBatchService do
- let(:artifacts) { Ci::JobArtifact.where(id: [artifact_with_file.id, artifact_without_file.id, trace_artifact.id]) }
+RSpec.describe Ci::JobArtifacts::DestroyBatchService, feature_category: :build_artifacts do
+ let(:artifacts) { Ci::JobArtifact.where(id: [artifact_with_file.id, artifact_without_file.id]) }
let(:skip_projects_on_refresh) { false }
let(:service) do
described_class.new(
@@ -25,10 +25,6 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
create(:ci_job_artifact)
end
- let_it_be(:trace_artifact, refind: true) do
- create(:ci_job_artifact, :trace, :expired)
- end
-
describe '#execute' do
subject(:execute) { service.execute }
@@ -60,11 +56,6 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
execute
end
- it 'preserves trace artifacts' do
- expect { subject }
- .to not_change { Ci::JobArtifact.exists?(trace_artifact.id) }
- end
-
context 'when artifact belongs to a project that is undergoing stats refresh' do
let!(:artifact_under_refresh_1) do
create(:ci_job_artifact, :zip)
@@ -287,7 +278,7 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
end
it 'reports the number of destroyed artifacts' do
- is_expected.to eq(destroyed_artifacts_count: 0, statistics_updates: {}, status: :success)
+ is_expected.to eq(destroyed_artifacts_count: 0, destroyed_ids: [], statistics_updates: {}, status: :success)
end
end
end
diff --git a/spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb b/spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb
index fb9dd6b876b..69cdf39107a 100644
--- a/spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb
+++ b/spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::ExpireProjectBuildArtifactsService do
+RSpec.describe Ci::JobArtifacts::ExpireProjectBuildArtifactsService, feature_category: :build_artifacts do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline, reload: true) { create(:ci_pipeline, :unlocked, project: project) }
diff --git a/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb
index d4d56825e1f..af0c9b0833d 100644
--- a/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb
+++ b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
+RSpec.describe Ci::JobArtifacts::TrackArtifactReportService, feature_category: :build_artifacts do
describe '#execute', :clean_gitlab_redis_shared_state do
let_it_be(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, group: group) }
@@ -33,13 +33,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
.with(test_event_name_1, values: user1.id)
.and_call_original
- expect { track_artifact_report }
- .to change {
- counter.unique_events(event_names: test_event_name_1,
- start_date: start_time,
- end_date: end_time)
- }
- .by 1
+ expect { track_artifact_report }.to change {
+ counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time)
+ }.by 1
end
end
@@ -81,13 +77,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
expect do
described_class.new.execute(pipeline1)
described_class.new.execute(pipeline2)
- end
- .to change {
- counter.unique_events(event_names: test_event_name_1,
- start_date: start_time,
- end_date: end_time)
- }
- .by 1
+ end.to change {
+ counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time)
+ }.by 1
end
end
@@ -109,13 +101,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
expect do
described_class.new.execute(pipeline1)
described_class.new.execute(pipeline2)
- end
- .to change {
- counter.unique_events(event_names: test_event_name_1,
- start_date: start_time,
- end_date: end_time)
- }
- .by 2
+ end.to change {
+ counter.unique_events(event_names: test_event_name_1, start_date: start_time, end_date: end_time)
+ }.by 2
end
end
@@ -134,13 +122,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
.with(test_event_name_2, values: user1.id)
.and_call_original
- expect { track_artifact_report }
- .to change {
- counter.unique_events(event_names: test_event_name_2,
- start_date: start_time,
- end_date: end_time)
- }
- .by 1
+ expect { track_artifact_report }.to change {
+ counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time)
+ }.by 1
end
end
@@ -158,13 +142,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
expect do
described_class.new.execute(pipeline1)
described_class.new.execute(pipeline2)
- end
- .to change {
- counter.unique_events(event_names: test_event_name_2,
- start_date: start_time,
- end_date: end_time)
- }
- .by 1
+ end.to change {
+ counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time)
+ }.by 1
end
end
@@ -186,13 +166,9 @@ RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
expect do
described_class.new.execute(pipeline1)
described_class.new.execute(pipeline2)
- end
- .to change {
- counter.unique_events(event_names: test_event_name_2,
- start_date: start_time,
- end_date: end_time)
- }
- .by 2
+ end.to change {
+ counter.unique_events(event_names: test_event_name_2, start_date: start_time, end_date: end_time)
+ }.by 2
end
end
end
diff --git a/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb b/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb
index 67412e41fb8..5f6a89b89e1 100644
--- a/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb
+++ b/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::JobArtifacts::UpdateUnknownLockedStatusService, :clean_gitlab_redis_shared_state do
+RSpec.describe Ci::JobArtifacts::UpdateUnknownLockedStatusService, :clean_gitlab_redis_shared_state,
+ feature_category: :build_artifacts do
include ExclusiveLeaseHelpers
let(:service) { described_class.new }
diff --git a/spec/services/ci/job_token_scope/add_project_service_spec.rb b/spec/services/ci/job_token_scope/add_project_service_spec.rb
index e6674ee384f..dc7ad81afef 100644
--- a/spec/services/ci/job_token_scope/add_project_service_spec.rb
+++ b/spec/services/ci/job_token_scope/add_project_service_spec.rb
@@ -37,8 +37,8 @@ RSpec.describe Ci::JobTokenScope::AddProjectService, feature_category: :continuo
it_behaves_like 'adds project'
- it 'creates an outbound link by default' do
- expect(resulting_direction).to eq('outbound')
+ it 'creates an inbound link by default' do
+ expect(resulting_direction).to eq('inbound')
end
context 'when direction is specified' do
diff --git a/spec/services/ci/list_config_variables_service_spec.rb b/spec/services/ci/list_config_variables_service_spec.rb
index e2bbdefef7f..07c9085b83a 100644
--- a/spec/services/ci/list_config_variables_service_spec.rb
+++ b/spec/services/ci/list_config_variables_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::ListConfigVariablesService,
-:use_clean_rails_memory_store_caching, feature_category: :pipeline_authoring do
+ :use_clean_rails_memory_store_caching, feature_category: :secrets_management do
include ReactiveCachingHelpers
let(:ci_config) { {} }
diff --git a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
index c4558bddc85..b7b32d2a0af 100644
--- a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineArtifacts::CoverageReportService do
+RSpec.describe Ci::PipelineArtifacts::CoverageReportService, feature_category: :build_artifacts do
describe '#execute' do
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
index 5d854b61f14..20265a0ca48 100644
--- a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService do
+RSpec.describe ::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService, feature_category: :build_artifacts do
describe '#execute' do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
diff --git a/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb b/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb
index 47e8766c215..b46648760e1 100644
--- a/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_shared_state do
+RSpec.describe Ci::PipelineArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_shared_state,
+ feature_category: :build_artifacts do
let(:service) { described_class.new }
describe '.execute' do
diff --git a/spec/services/ci/pipeline_bridge_status_service_spec.rb b/spec/services/ci/pipeline_bridge_status_service_spec.rb
index 1346f68c952..3d8219251d6 100644
--- a/spec/services/ci/pipeline_bridge_status_service_spec.rb
+++ b/spec/services/ci/pipeline_bridge_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineBridgeStatusService do
+RSpec.describe Ci::PipelineBridgeStatusService, feature_category: :continuous_integration do
let(:user) { build(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb b/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
index ab4ba20e716..06139c091b9 100644
--- a/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
+++ b/spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineCreation::StartPipelineService do
+RSpec.describe Ci::PipelineCreation::StartPipelineService, feature_category: :continuous_integration do
let(:pipeline) { build(:ci_pipeline) }
subject(:service) { described_class.new(pipeline) }
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
index d0aa1ba4c6c..89b3c45485b 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection do
+RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection,
+ feature_category: :continuous_integration do
using RSpec::Parameterized::TableSyntax
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -31,15 +32,15 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection
let(:collection) { described_class.new(pipeline) }
- describe '#set_processable_status' do
- it 'does update existing status of processable' do
- collection.set_processable_status(test_a.id, 'success', 100)
+ describe '#set_job_status' do
+ it 'does update existing status of job' do
+ collection.set_job_status(test_a.id, 'success', 100)
- expect(collection.status_for_names(['test-a'], dag: false)).to eq('success')
+ expect(collection.status_of_jobs(['test-a'])).to eq('success')
end
- it 'ignores a missing processable' do
- collection.set_processable_status(-1, 'failed', 100)
+ it 'ignores a missing job' do
+ collection.set_job_status(-1, 'failed', 100)
end
end
@@ -49,24 +50,21 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection
end
end
- describe '#status_for_names' do
- where(:names, :status, :dag) do
- %w[build-a] | 'success' | false
- %w[build-a build-b] | 'failed' | false
- %w[build-a test-a] | 'running' | false
- %w[build-a] | 'success' | true
- %w[build-a build-b] | 'failed' | true
- %w[build-a test-a] | 'pending' | true
+ describe '#status_of_jobs' do
+ where(:names, :status) do
+ %w[build-a] | 'success'
+ %w[build-a build-b] | 'failed'
+ %w[build-a test-a] | 'running'
end
with_them do
it 'returns composite status of given names' do
- expect(collection.status_for_names(names, dag: dag)).to eq(status)
+ expect(collection.status_of_jobs(names)).to eq(status)
end
end
end
- describe '#status_for_prior_stage_position' do
+ describe '#status_of_jobs_prior_to_stage' do
where(:stage, :status) do
0 | 'success'
1 | 'failed'
@@ -74,13 +72,13 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection
end
with_them do
- it 'returns composite status for processables in prior stages' do
- expect(collection.status_for_prior_stage_position(stage)).to eq(status)
+ it 'returns composite status for jobs in prior stages' do
+ expect(collection.status_of_jobs_prior_to_stage(stage)).to eq(status)
end
end
end
- describe '#status_for_stage_position' do
+ describe '#status_of_stage' do
where(:stage, :status) do
0 | 'failed'
1 | 'running'
@@ -88,23 +86,23 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection
end
with_them do
- it 'returns composite status for processables at a given stages' do
- expect(collection.status_for_stage_position(stage)).to eq(status)
+ it 'returns composite status for jobs at a given stages' do
+ expect(collection.status_of_stage(stage)).to eq(status)
end
end
end
- describe '#created_processable_ids_for_stage_position' do
- it 'returns IDs of processables at a given stage position' do
- expect(collection.created_processable_ids_for_stage_position(0)).to be_empty
- expect(collection.created_processable_ids_for_stage_position(1)).to be_empty
- expect(collection.created_processable_ids_for_stage_position(2)).to contain_exactly(deploy.id)
+ describe '#created_job_ids_in_stage' do
+ it 'returns IDs of jobs at a given stage position' do
+ expect(collection.created_job_ids_in_stage(0)).to be_empty
+ expect(collection.created_job_ids_in_stage(1)).to be_empty
+ expect(collection.created_job_ids_in_stage(2)).to contain_exactly(deploy.id)
end
end
- describe '#processing_processables' do
- it 'returns processables marked as processing' do
- expect(collection.processing_processables.map { |processable| processable[:id] })
+ describe '#processing_jobs' do
+ it 'returns jobs marked as processing' do
+ expect(collection.processing_jobs.map { |job| job[:id] })
.to contain_exactly(build_a.id, build_b.id, test_a.id, test_b.id, deploy.id)
end
end
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
index c1669e0424a..8c52603e769 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -59,17 +59,17 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category
end
def event_on_jobs(event, job_names)
- statuses = pipeline.latest_statuses.by_name(job_names).to_a
- expect(statuses.count).to eq(job_names.count) # ensure that we have the same counts
+ jobs = pipeline.latest_statuses.by_name(job_names).to_a
+ expect(jobs.count).to eq(job_names.count) # ensure that we have the same counts
- statuses.each do |status|
+ jobs.each do |job|
case event
when 'play'
- status.play(user)
+ job.play(user)
when 'retry'
- ::Ci::RetryJobService.new(project, user).execute(status)
+ ::Ci::RetryJobService.new(project, user).execute(job)
else
- status.public_send("#{event}!")
+ job.public_send("#{event}!")
end
end
end
@@ -646,8 +646,7 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category
# Users need ability to merge into a branch in order to trigger
# protected manual actions.
#
- create(:protected_branch, :developers_can_merge,
- name: 'master', project: project)
+ create(:protected_branch, :developers_can_merge, name: 'master', project: project)
end
it 'properly processes entire pipeline' do
@@ -983,8 +982,8 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category
bridge1 = all_builds.find_by(name: 'deploy: [ovh, monitoring]')
bridge2 = all_builds.find_by(name: 'deploy: [ovh, app]')
- downstream_job1 = bridge1.downstream_pipeline.processables.first
- downstream_job2 = bridge2.downstream_pipeline.processables.first
+ downstream_job1 = bridge1.downstream_pipeline.all_jobs.first
+ downstream_job2 = bridge2.downstream_pipeline.all_jobs.first
expect(downstream_job1.scoped_variables.to_hash).to include('PROVIDER' => 'ovh', 'STACK' => 'monitoring')
expect(downstream_job2.scoped_variables.to_hash).to include('PROVIDER' => 'ovh', 'STACK' => 'app')
@@ -1068,7 +1067,7 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category
private
def all_builds
- pipeline.processables.order(:stage_idx, :id)
+ pipeline.all_jobs.order(:stage_idx, :id)
end
def builds
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_test_on_failure_no_needs.yml b/spec/services/ci/pipeline_processing/test_cases/dag_test_on_failure_no_needs.yml
new file mode 100644
index 00000000000..12c51828628
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_test_on_failure_no_needs.yml
@@ -0,0 +1,31 @@
+config:
+ test1:
+ stage: test
+ script: exit 0
+ needs: []
+
+ test2:
+ stage: test
+ when: on_failure
+ script: exit 0
+ needs: []
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ test1: pending
+ test2: skipped
+
+transitions:
+ - event: success
+ jobs: [test1]
+ expect:
+ pipeline: success
+ stages:
+ test: success
+ jobs:
+ test1: success
+ test2: skipped
diff --git a/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_test1_and_test2_have_when.yml b/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_test1_and_test2_have_when.yml
new file mode 100644
index 00000000000..cc92aaba679
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_test1_and_test2_have_when.yml
@@ -0,0 +1,46 @@
+config:
+ build:
+ stage: build
+ script: sleep 10
+
+ test1:
+ stage: test
+ script: exit 0
+ when: on_success
+
+ test2:
+ stage: test
+ script: exit 0
+ when: on_failure
+
+ deploy:
+ stage: deploy
+ script: exit 0
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ build: pending
+ test: created
+ deploy: created
+ jobs:
+ build: pending
+ test1: created
+ test2: created
+ deploy: created
+
+transitions:
+ - event: cancel
+ jobs: [build]
+ expect:
+ pipeline: canceled
+ stages:
+ build: canceled
+ test: skipped
+ deploy: skipped
+ jobs:
+ build: canceled
+ test1: skipped
+ test2: skipped
+ deploy: skipped
diff --git a/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_with_allow_failure_test1_and_test2_have_when.yml b/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_with_allow_failure_test1_and_test2_have_when.yml
new file mode 100644
index 00000000000..34f01afe1de
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/stage_build_cancels_with_allow_failure_test1_and_test2_have_when.yml
@@ -0,0 +1,47 @@
+config:
+ build:
+ stage: build
+ script: sleep 10
+ allow_failure: true
+
+ test1:
+ stage: test
+ script: exit 0
+ when: on_success
+
+ test2:
+ stage: test
+ script: exit 0
+ when: on_failure
+
+ deploy:
+ stage: deploy
+ script: exit 0
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ build: pending
+ test: created
+ deploy: created
+ jobs:
+ build: pending
+ test1: created
+ test2: created
+ deploy: created
+
+transitions:
+ - event: cancel
+ jobs: [build]
+ expect:
+ pipeline: pending
+ stages:
+ build: success
+ test: pending
+ deploy: created
+ jobs:
+ build: canceled
+ test1: pending
+ test2: skipped
+ deploy: created
diff --git a/spec/services/ci/pipeline_processing/test_cases/stage_test_on_failure_no_prev_stage.yml b/spec/services/ci/pipeline_processing/test_cases/stage_test_on_failure_no_prev_stage.yml
new file mode 100644
index 00000000000..57b3aa9ae80
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/stage_test_on_failure_no_prev_stage.yml
@@ -0,0 +1,29 @@
+config:
+ test1:
+ stage: test
+ script: exit 0
+
+ test2:
+ stage: test
+ when: on_failure
+ script: exit 0
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ test1: pending
+ test2: skipped
+
+transitions:
+ - event: success
+ jobs: [test1]
+ expect:
+ pipeline: success
+ stages:
+ test: success
+ jobs:
+ test1: success
+ test2: skipped
diff --git a/spec/services/ci/pipeline_schedules/take_ownership_service_spec.rb b/spec/services/ci/pipeline_schedules/take_ownership_service_spec.rb
index 9a3aad20d89..1d45a06f9ea 100644
--- a/spec/services/ci/pipeline_schedules/take_ownership_service_spec.rb
+++ b/spec/services/ci/pipeline_schedules/take_ownership_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineSchedules::TakeOwnershipService do
+RSpec.describe Ci::PipelineSchedules::TakeOwnershipService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:owner) { create(:user) }
let_it_be(:reporter) { create(:user) }
diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb
index 4946367380e..b6e07e82bb5 100644
--- a/spec/services/ci/pipeline_trigger_service_spec.rb
+++ b/spec/services/ci/pipeline_trigger_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineTriggerService do
+RSpec.describe Ci::PipelineTriggerService, feature_category: :continuous_integration do
include AfterNextHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb
index c62aa9506bd..6380a6a5ec3 100644
--- a/spec/services/ci/pipelines/add_job_service_spec.rb
+++ b/spec/services/ci/pipelines/add_job_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Pipelines::AddJobService do
+RSpec.describe Ci::Pipelines::AddJobService, feature_category: :continuous_integration do
include ExclusiveLeaseHelpers
let_it_be_with_reload(:pipeline) { create(:ci_pipeline) }
@@ -86,5 +86,15 @@ RSpec.describe Ci::Pipelines::AddJobService do
expect(execute.payload[:job]).to eq(job)
end
end
+
+ it 'locks pipelines and stages before persisting builds', :aggregate_failures do
+ expect(job).not_to be_persisted
+
+ recorder = ActiveRecord::QueryRecorder.new(skip_cached: false) { execute }
+ entries = recorder.log.select { |query| query.match(/LOCK|INSERT INTO ".{0,2}ci_builds"/) }
+
+ expect(entries.size).to eq(2)
+ expect(entries.first).to match(/LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;/)
+ end
end
end
diff --git a/spec/services/ci/pipelines/hook_service_spec.rb b/spec/services/ci/pipelines/hook_service_spec.rb
index 8d138a3d957..e773ae2d2c3 100644
--- a/spec/services/ci/pipelines/hook_service_spec.rb
+++ b/spec/services/ci/pipelines/hook_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Pipelines::HookService do
+RSpec.describe Ci::Pipelines::HookService, feature_category: :continuous_integration do
describe '#execute_hooks' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
diff --git a/spec/services/ci/play_bridge_service_spec.rb b/spec/services/ci/play_bridge_service_spec.rb
index 56b1615a56d..5727ed64f8b 100644
--- a/spec/services/ci/play_bridge_service_spec.rb
+++ b/spec/services/ci/play_bridge_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PlayBridgeService, '#execute' do
+RSpec.describe Ci::PlayBridgeService, '#execute', feature_category: :continuous_integration do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb
index fc07801b672..46b6622d6ec 100644
--- a/spec/services/ci/play_build_service_spec.rb
+++ b/spec/services/ci/play_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PlayBuildService, '#execute' do
+RSpec.describe Ci::PlayBuildService, '#execute', feature_category: :continuous_integration do
let(:user) { create(:user, developer_projects: [project]) }
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
@@ -16,8 +16,7 @@ RSpec.describe Ci::PlayBuildService, '#execute' do
let(:project) { create(:project) }
it 'allows user to play build if protected branch rules are met' do
- create(:protected_branch, :developers_can_merge,
- name: build.ref, project: project)
+ create(:protected_branch, :developers_can_merge, name: build.ref, project: project)
service.execute(build)
diff --git a/spec/services/ci/play_manual_stage_service_spec.rb b/spec/services/ci/play_manual_stage_service_spec.rb
index 24f0a21f3dd..dd8e037b129 100644
--- a/spec/services/ci/play_manual_stage_service_spec.rb
+++ b/spec/services/ci/play_manual_stage_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PlayManualStageService, '#execute' do
+RSpec.describe Ci::PlayManualStageService, '#execute', feature_category: :continuous_integration do
let(:current_user) { create(:user) }
let(:pipeline) { create(:ci_pipeline, user: current_user) }
let(:project) { pipeline.project }
@@ -11,10 +11,7 @@ RSpec.describe Ci::PlayManualStageService, '#execute' do
let(:stage_status) { 'manual' }
let(:stage) do
- create(:ci_stage,
- pipeline: pipeline,
- project: project,
- name: 'test')
+ create(:ci_stage, pipeline: pipeline, project: project, name: 'test')
end
before do
diff --git a/spec/services/ci/prepare_build_service_spec.rb b/spec/services/ci/prepare_build_service_spec.rb
index f75cb322fe9..8583b8e667c 100644
--- a/spec/services/ci/prepare_build_service_spec.rb
+++ b/spec/services/ci/prepare_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PrepareBuildService do
+RSpec.describe Ci::PrepareBuildService, feature_category: :continuous_integration do
describe '#execute' do
let(:build) { create(:ci_build, :preparing) }
diff --git a/spec/services/ci/process_build_service_spec.rb b/spec/services/ci/process_build_service_spec.rb
index de308bb1a87..d1442b75731 100644
--- a/spec/services/ci/process_build_service_spec.rb
+++ b/spec/services/ci/process_build_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::ProcessBuildService, '#execute' do
+RSpec.describe Ci::ProcessBuildService, '#execute', feature_category: :continuous_integration do
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb
index 404e1bf7c87..d1586ad4c8b 100644
--- a/spec/services/ci/process_pipeline_service_spec.rb
+++ b/spec/services/ci/process_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ProcessPipelineService do
+RSpec.describe Ci::ProcessPipelineService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let(:pipeline) do
diff --git a/spec/services/ci/process_sync_events_service_spec.rb b/spec/services/ci/process_sync_events_service_spec.rb
index 7ab7911e578..84b6d7d96f6 100644
--- a/spec/services/ci/process_sync_events_service_spec.rb
+++ b/spec/services/ci/process_sync_events_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ProcessSyncEventsService do
+RSpec.describe Ci::ProcessSyncEventsService, feature_category: :continuous_integration do
let!(:group) { create(:group) }
let!(:project1) { create(:project, group: group) }
let!(:project2) { create(:project, group: group) }
@@ -147,8 +147,7 @@ RSpec.describe Ci::ProcessSyncEventsService do
context 'when the FFs use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
before do
- stub_feature_flags(use_traversal_ids: false,
- use_traversal_ids_for_ancestors: false)
+ stub_feature_flags(use_traversal_ids: false, use_traversal_ids_for_ancestors: false)
end
it_behaves_like 'event consuming'
diff --git a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
index 0b100af5902..a9ee5216d81 100644
--- a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
+++ b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do
+RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService, feature_category: :continuous_integration do
let_it_be(:project) { create(:project) }
let(:params) { {} }
diff --git a/spec/services/ci/queue/pending_builds_strategy_spec.rb b/spec/services/ci/queue/pending_builds_strategy_spec.rb
index 6f22c256c17..ea9207ddb8f 100644
--- a/spec/services/ci/queue/pending_builds_strategy_spec.rb
+++ b/spec/services/ci/queue/pending_builds_strategy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::Queue::PendingBuildsStrategy do
+RSpec.describe Ci::Queue::PendingBuildsStrategy, feature_category: :continuous_integration do
let_it_be(:group) { create(:group) }
let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let_it_be(:project) { create(:project, group: group) }
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 9183df359b4..6fb61bb3ec5 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -14,128 +14,157 @@ module Ci
let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
describe '#execute' do
- subject(:execute) { described_class.new(runner, runner_machine).execute }
+ subject(:execute) { described_class.new(runner, runner_manager).execute }
- context 'with runner_machine specified' do
- let(:runner) { project_runner }
- let!(:runner_machine) { create(:ci_runner_machine, runner: project_runner) }
+ let(:runner_manager) { nil }
+
+ context 'checks database loadbalancing stickiness' do
+ let(:runner) { shared_runner }
before do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
- pending_job.create_queuing_entry!
- project_runner.update!(tag_list: ["linux"])
+ project.update!(shared_runners_enabled: false)
end
- it 'sets runner_machine on job' do
- expect { execute }.to change { pending_job.reload.runner_machine }.from(nil).to(runner_machine)
+ it 'result is valid if replica did caught-up', :aggregate_failures do
+ expect(ApplicationRecord.sticking).to receive(:all_caught_up?).with(:runner, runner.id) { true }
- expect(execute.build).to eq(pending_job)
+ expect { execute }.not_to change { Ci::RunnerManagerBuild.count }.from(0)
+ expect(execute).to be_valid
+ expect(execute.build).to be_nil
+ expect(execute.build_json).to be_nil
end
- end
-
- context 'with no runner machine' do
- let(:runner_machine) { nil }
-
- context 'checks database loadbalancing stickiness' do
- let(:runner) { shared_runner }
-
- before do
- project.update!(shared_runners_enabled: false)
- end
- it 'result is valid if replica did caught-up', :aggregate_failures do
- expect(ApplicationRecord.sticking).to receive(:all_caught_up?).with(:runner, runner.id) { true }
+ it 'result is invalid if replica did not caught-up', :aggregate_failures do
+ expect(ApplicationRecord.sticking).to receive(:all_caught_up?)
+ .with(:runner, shared_runner.id) { false }
- expect(execute).to be_valid
- expect(execute.build).to be_nil
- expect(execute.build_json).to be_nil
- end
+ expect(subject).not_to be_valid
+ expect(subject.build).to be_nil
+ expect(subject.build_json).to be_nil
+ end
+ end
- it 'result is invalid if replica did not caught-up', :aggregate_failures do
- expect(ApplicationRecord.sticking).to receive(:all_caught_up?)
- .with(:runner, shared_runner.id) { false }
+ shared_examples 'handles runner assignment' do
+ context 'runner follows tag list' do
+ subject(:build) { build_on(project_runner, runner_manager: project_runner_manager) }
- expect(subject).not_to be_valid
- expect(subject.build).to be_nil
- expect(subject.build_json).to be_nil
- end
- end
+ let(:project_runner_manager) { nil }
- shared_examples 'handles runner assignment' do
- context 'runner follow tag list' do
- it "picks build with the same tag" do
+ context 'when job has tag' do
+ before do
pending_job.update!(tag_list: ["linux"])
pending_job.reload
pending_job.create_queuing_entry!
- project_runner.update!(tag_list: ["linux"])
- expect(build_on(project_runner)).to eq(pending_job)
end
- it "does not pick build with different tag" do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
- pending_job.create_queuing_entry!
- project_runner.update!(tag_list: ["win32"])
- expect(build_on(project_runner)).to be_falsey
+ context 'and runner has matching tag' do
+ before do
+ project_runner.update!(tag_list: ["linux"])
+ end
+
+ context 'with no runner manager specified' do
+ it 'picks build' do
+ expect(build).to eq(pending_job)
+ expect(pending_job.runner_manager).to be_nil
+ end
+ end
+
+ context 'with runner manager specified' do
+ let(:project_runner_manager) { create(:ci_runner_machine, runner: project_runner) }
+
+ it 'picks build and assigns runner manager' do
+ expect(build).to eq(pending_job)
+ expect(pending_job.runner_manager).to eq(project_runner_manager)
+ end
+ end
end
- it "picks build without tag" do
- expect(build_on(project_runner)).to eq(pending_job)
+ it 'does not pick build with different tag' do
+ project_runner.update!(tag_list: ["win32"])
+ expect(build).to be_falsey
end
- it "does not pick build with tag" do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
+ it 'does not pick build with tag' do
pending_job.create_queuing_entry!
- expect(build_on(project_runner)).to be_falsey
+ expect(build).to be_falsey
end
+ end
- it "pick build without tag" do
- project_runner.update!(tag_list: ["win32"])
- expect(build_on(project_runner)).to eq(pending_job)
+ context 'when job has no tag' do
+ it 'picks build' do
+ expect(build).to eq(pending_job)
+ end
+
+ context 'when runner has tag' do
+ before do
+ project_runner.update!(tag_list: ["win32"])
+ end
+
+ it 'picks build' do
+ expect(build).to eq(pending_job)
+ end
end
end
+ end
- context 'deleted projects' do
+ context 'deleted projects' do
+ before do
+ project.update!(pending_delete: true)
+ end
+
+ context 'for shared runners' do
before do
- project.update!(pending_delete: true)
+ project.update!(shared_runners_enabled: true)
end
- context 'for shared runners' do
- before do
- project.update!(shared_runners_enabled: true)
- end
+ it 'does not pick a build' do
+ expect(build_on(shared_runner)).to be_nil
+ end
+ end
+
+ context 'for project runner' do
+ subject(:build) { build_on(project_runner, runner_manager: project_runner_manager) }
+ let(:project_runner_manager) { nil }
+
+ context 'with no runner manager specified' do
it 'does not pick a build' do
- expect(build_on(shared_runner)).to be_nil
+ expect(build).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
+ expect(Ci::RunnerManagerBuild.all).to be_empty
end
end
- context 'for project runner' do
+ context 'with runner manager specified' do
+ let(:project_runner_manager) { create(:ci_runner_machine, runner: project_runner) }
+
it 'does not pick a build' do
- expect(build_on(project_runner)).to be_nil
+ expect(build).to be_nil
expect(pending_job.reload).to be_failed
expect(pending_job.queuing_entry).to be_nil
+ expect(Ci::RunnerManagerBuild.all).to be_empty
end
end
end
+ end
- context 'allow shared runners' do
- before do
- project.update!(shared_runners_enabled: true)
- pipeline.reload
- pending_job.reload
- pending_job.create_queuing_entry!
- end
+ context 'allow shared runners' do
+ before do
+ project.update!(shared_runners_enabled: true)
+ pipeline.reload
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ end
- context 'when build owner has been blocked' do
- let(:user) { create(:user, :blocked) }
+ context 'when build owner has been blocked' do
+ let(:user) { create(:user, :blocked) }
- before do
- pending_job.update!(user: user)
- end
+ before do
+ pending_job.update!(user: user)
+ end
+ context 'with no runner manager specified' do
it 'does not pick the build and drops the build' do
expect(build_on(shared_runner)).to be_falsey
@@ -143,690 +172,701 @@ module Ci
end
end
- context 'for multiple builds' do
- let!(:project2) { create :project, shared_runners_enabled: true }
- let!(:pipeline2) { create :ci_pipeline, project: project2 }
- let!(:project3) { create :project, shared_runners_enabled: true }
- let!(:pipeline3) { create :ci_pipeline, project: project3 }
- let!(:build1_project1) { pending_job }
- let!(:build2_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- let!(:build3_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- let!(:build1_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
- let!(:build2_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
- let!(:build1_project3) { create(:ci_build, :pending, :queued, pipeline: pipeline3) }
+ context 'with runner manager specified' do
+ let(:runner_manager) { create(:ci_runner_machine, runner: runner) }
- it 'picks builds one-by-one' do
- expect(Ci::Build).to receive(:find).with(pending_job.id).and_call_original
+ it 'does not pick the build and does not create join record' do
+ expect(build_on(shared_runner, runner_manager: runner_manager)).to be_falsey
- expect(build_on(shared_runner)).to eq(build1_project1)
+ expect(Ci::RunnerManagerBuild.all).to be_empty
end
+ end
+ end
- context 'when using fair scheduling' do
- context 'when all builds are pending' do
- it 'prefers projects without builds first' do
- # it gets for one build from each of the projects
- expect(build_on(shared_runner)).to eq(build1_project1)
- expect(build_on(shared_runner)).to eq(build1_project2)
- expect(build_on(shared_runner)).to eq(build1_project3)
-
- # then it gets a second build from each of the projects
- expect(build_on(shared_runner)).to eq(build2_project1)
- expect(build_on(shared_runner)).to eq(build2_project2)
-
- # in the end the third build
- expect(build_on(shared_runner)).to eq(build3_project1)
- end
- end
+ context 'for multiple builds' do
+ let!(:project2) { create :project, shared_runners_enabled: true }
+ let!(:pipeline2) { create :ci_pipeline, project: project2 }
+ let!(:project3) { create :project, shared_runners_enabled: true }
+ let!(:pipeline3) { create :ci_pipeline, project: project3 }
+ let!(:build1_project1) { pending_job }
+ let!(:build2_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ let!(:build3_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ let!(:build1_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
+ let!(:build2_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
+ let!(:build1_project3) { create(:ci_build, :pending, :queued, pipeline: pipeline3) }
+
+ it 'picks builds one-by-one' do
+ expect(Ci::Build).to receive(:find).with(pending_job.id).and_call_original
+
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ end
- context 'when some builds transition to success' do
- it 'equalises number of running builds' do
- # after finishing the first build for project 1, get a second build from the same project
- expect(build_on(shared_runner)).to eq(build1_project1)
- build1_project1.reload.success
- expect(build_on(shared_runner)).to eq(build2_project1)
-
- expect(build_on(shared_runner)).to eq(build1_project2)
- build1_project2.reload.success
- expect(build_on(shared_runner)).to eq(build2_project2)
- expect(build_on(shared_runner)).to eq(build1_project3)
- expect(build_on(shared_runner)).to eq(build3_project1)
- end
+ context 'when using fair scheduling' do
+ context 'when all builds are pending' do
+ it 'prefers projects without builds first' do
+ # it gets for one build from each of the projects
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+
+ # then it gets a second build from each of the projects
+ expect(build_on(shared_runner)).to eq(build2_project1)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+
+ # in the end the third build
+ expect(build_on(shared_runner)).to eq(build3_project1)
end
end
- context 'when using DEFCON mode that disables fair scheduling' do
- before do
- stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true)
+ context 'when some builds transition to success' do
+ it 'equalises number of running builds' do
+ # after finishing the first build for project 1, get a second build from the same project
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ build1_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project1)
+
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ build1_project2.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+ expect(build_on(shared_runner)).to eq(build3_project1)
end
+ end
+ end
- context 'when all builds are pending' do
- it 'returns builds in order of creation (FIFO)' do
- # it gets for one build from each of the projects
- expect(build_on(shared_runner)).to eq(build1_project1)
- expect(build_on(shared_runner)).to eq(build2_project1)
- expect(build_on(shared_runner)).to eq(build3_project1)
- expect(build_on(shared_runner)).to eq(build1_project2)
- expect(build_on(shared_runner)).to eq(build2_project2)
- expect(build_on(shared_runner)).to eq(build1_project3)
- end
+ context 'when using DEFCON mode that disables fair scheduling' do
+ before do
+ stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true)
+ end
+
+ context 'when all builds are pending' do
+ it 'returns builds in order of creation (FIFO)' do
+ # it gets for one build from each of the projects
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ expect(build_on(shared_runner)).to eq(build2_project1)
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
end
+ end
- context 'when some builds transition to success' do
- it 'returns builds in order of creation (FIFO)' do
- expect(build_on(shared_runner)).to eq(build1_project1)
- build1_project1.reload.success
- expect(build_on(shared_runner)).to eq(build2_project1)
-
- expect(build_on(shared_runner)).to eq(build3_project1)
- build2_project1.reload.success
- expect(build_on(shared_runner)).to eq(build1_project2)
- expect(build_on(shared_runner)).to eq(build2_project2)
- expect(build_on(shared_runner)).to eq(build1_project3)
- end
+ context 'when some builds transition to success' do
+ it 'returns builds in order of creation (FIFO)' do
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ build1_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project1)
+
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ build2_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
end
end
end
+ end
- context 'shared runner' do
- let(:response) { described_class.new(shared_runner, nil).execute }
- let(:build) { response.build }
+ context 'shared runner' do
+ let(:response) { described_class.new(shared_runner, nil).execute }
+ let(:build) { response.build }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(shared_runner) }
- it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) }
- end
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(shared_runner) }
+ it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) }
+ end
- context 'project runner' do
- let(:build) { build_on(project_runner) }
+ context 'project runner' do
+ let(:build) { build_on(project_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(project_runner) }
- end
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(project_runner) }
end
+ end
- context 'disallow shared runners' do
- before do
- project.update!(shared_runners_enabled: false)
- end
+ context 'disallow shared runners' do
+ before do
+ project.update!(shared_runners_enabled: false)
+ end
- context 'shared runner' do
- let(:build) { build_on(shared_runner) }
+ context 'shared runner' do
+ let(:build) { build_on(shared_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'project runner' do
- let(:build) { build_on(project_runner) }
+ context 'project runner' do
+ let(:build) { build_on(project_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(project_runner) }
- end
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(project_runner) }
end
+ end
- context 'disallow when builds are disabled' do
- before do
- project.update!(shared_runners_enabled: true, group_runners_enabled: true)
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ context 'disallow when builds are disabled' do
+ before do
+ project.update!(shared_runners_enabled: true, group_runners_enabled: true)
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
- pending_job.reload.create_queuing_entry!
- end
+ pending_job.reload.create_queuing_entry!
+ end
- context 'and uses shared runner' do
- let(:build) { build_on(shared_runner) }
+ context 'and uses shared runner' do
+ let(:build) { build_on(shared_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'and uses group runner' do
- let(:build) { build_on(group_runner) }
+ context 'and uses group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'and uses project runner' do
- let(:build) { build_on(project_runner) }
+ context 'and uses project runner' do
+ let(:build) { build_on(project_runner) }
- it 'does not pick a build' do
- expect(build).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job.queuing_entry).to be_nil
- end
+ it 'does not pick a build' do
+ expect(build).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
end
end
+ end
- context 'allow group runners' do
- before do
- project.update!(group_runners_enabled: true)
- end
+ context 'allow group runners' do
+ before do
+ project.update!(group_runners_enabled: true)
+ end
- context 'for multiple builds' do
- let!(:project2) { create(:project, group_runners_enabled: true, group: group) }
- let!(:pipeline2) { create(:ci_pipeline, project: project2) }
- let!(:project3) { create(:project, group_runners_enabled: true, group: group) }
- let!(:pipeline3) { create(:ci_pipeline, project: project3) }
+ context 'for multiple builds' do
+ let!(:project2) { create(:project, group_runners_enabled: true, group: group) }
+ let!(:pipeline2) { create(:ci_pipeline, project: project2) }
+ let!(:project3) { create(:project, group_runners_enabled: true, group: group) }
+ let!(:pipeline3) { create(:ci_pipeline, project: project3) }
- let!(:build1_project1) { pending_job }
- let!(:build2_project1) { create(:ci_build, :queued, pipeline: pipeline) }
- let!(:build3_project1) { create(:ci_build, :queued, pipeline: pipeline) }
- let!(:build1_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
- let!(:build2_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
- let!(:build1_project3) { create(:ci_build, :queued, pipeline: pipeline3) }
+ let!(:build1_project1) { pending_job }
+ let!(:build2_project1) { create(:ci_build, :queued, pipeline: pipeline) }
+ let!(:build3_project1) { create(:ci_build, :queued, pipeline: pipeline) }
+ let!(:build1_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
+ let!(:build2_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
+ let!(:build1_project3) { create(:ci_build, :queued, pipeline: pipeline3) }
- # these shouldn't influence the scheduling
- let!(:unrelated_group) { create(:group) }
- let!(:unrelated_project) { create(:project, group_runners_enabled: true, group: unrelated_group) }
- let!(:unrelated_pipeline) { create(:ci_pipeline, project: unrelated_project) }
- let!(:build1_unrelated_project) { create(:ci_build, :pending, :queued, pipeline: unrelated_pipeline) }
- let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
+ # these shouldn't influence the scheduling
+ let!(:unrelated_group) { create(:group) }
+ let!(:unrelated_project) { create(:project, group_runners_enabled: true, group: unrelated_group) }
+ let!(:unrelated_pipeline) { create(:ci_pipeline, project: unrelated_project) }
+ let!(:build1_unrelated_project) { create(:ci_build, :pending, :queued, pipeline: unrelated_pipeline) }
+ let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
- it 'does not consider builds from other group runners' do
- queue = ::Ci::Queue::BuildQueueService.new(group_runner)
+ it 'does not consider builds from other group runners' do
+ queue = ::Ci::Queue::BuildQueueService.new(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 6
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 6
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 5
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 5
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 4
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 4
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 3
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 3
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 2
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 2
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 1
- build_on(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 1
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 0
- expect(build_on(group_runner)).to be_nil
- end
+ expect(queue.builds_for_group_runner.size).to eq 0
+ expect(build_on(group_runner)).to be_nil
end
+ end
- context 'group runner' do
- let(:build) { build_on(group_runner) }
+ context 'group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(group_runner) }
- end
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(group_runner) }
end
+ end
- context 'disallow group runners' do
- before do
- project.update!(group_runners_enabled: false)
+ context 'disallow group runners' do
+ before do
+ project.update!(group_runners_enabled: false)
- pending_job.reload.create_queuing_entry!
- end
+ pending_job.reload.create_queuing_entry!
+ end
- context 'group runner' do
- let(:build) { build_on(group_runner) }
+ context 'group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
end
+ end
- context 'when first build is stalled' do
- before do
- allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!).and_call_original
- allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!)
- .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
- end
+ context 'when first build is stalled' do
+ before do
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!).and_call_original
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!)
+ .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
+ end
- subject { described_class.new(project_runner, nil).execute }
+ subject { described_class.new(project_runner, nil).execute }
- context 'with multiple builds are in queue' do
- let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'with multiple builds are in queue' do
+ let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id))
- end
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id))
+ end
- it "receives second build from the queue" do
- expect(subject).to be_valid
- expect(subject.build).to eq(other_build)
- end
+ it "receives second build from the queue" do
+ expect(subject).to be_valid
+ expect(subject.build).to eq(other_build)
end
+ end
- context 'when single build is in queue' do
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return(Ci::Build.where(id: pending_job).pluck(:id))
- end
+ context 'when single build is in queue' do
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return(Ci::Build.where(id: pending_job).pluck(:id))
+ end
- it "does not receive any valid result" do
- expect(subject).not_to be_valid
- end
+ it "does not receive any valid result" do
+ expect(subject).not_to be_valid
end
+ end
- context 'when there is no build in queue' do
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return([])
- end
+ context 'when there is no build in queue' do
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return([])
+ end
- it "does not receive builds but result is valid" do
- expect(subject).to be_valid
- expect(subject.build).to be_nil
- end
+ it "does not receive builds but result is valid" do
+ expect(subject).to be_valid
+ expect(subject.build).to be_nil
end
end
+ end
- context 'when access_level of runner is not_protected' do
- let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
+ context 'when access_level of runner is not_protected' do
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
- context 'when a job is protected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
+ context 'when a job is protected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
- it 'picks the job' do
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
end
+ end
- context 'when a job is unprotected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when a job is unprotected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- it 'picks the job' do
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
end
+ end
- context 'when protected attribute of a job is nil' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when protected attribute of a job is nil' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- pending_job.update_attribute(:protected, nil)
- end
+ before do
+ pending_job.update_attribute(:protected, nil)
+ end
- it 'picks the job' do
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
end
end
+ end
- context 'when access_level of runner is ref_protected' do
- let!(:project_runner) { create(:ci_runner, :project, :ref_protected, projects: [project]) }
+ context 'when access_level of runner is ref_protected' do
+ let!(:project_runner) { create(:ci_runner, :project, :ref_protected, projects: [project]) }
- context 'when a job is protected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
+ context 'when a job is protected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
- it 'picks the job' do
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
end
+ end
- context 'when a job is unprotected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when a job is unprotected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- it 'does not pick the job' do
- expect(build_on(project_runner)).to be_nil
- end
+ it 'does not pick the job' do
+ expect(build_on(project_runner)).to be_nil
end
+ end
- context 'when protected attribute of a job is nil' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when protected attribute of a job is nil' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- pending_job.update_attribute(:protected, nil)
- end
+ before do
+ pending_job.update_attribute(:protected, nil)
+ end
- it 'does not pick the job' do
- expect(build_on(project_runner)).to be_nil
- end
+ it 'does not pick the job' do
+ expect(build_on(project_runner)).to be_nil
end
end
+ end
- context 'runner feature set is verified' do
- let(:options) { { artifacts: { reports: { junit: "junit.xml" } } } }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, options: options) }
+ context 'runner feature set is verified' do
+ let(:options) { { artifacts: { reports: { junit: "junit.xml" } } } }
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, options: options) }
- subject { build_on(project_runner, params: params) }
+ subject { build_on(project_runner, params: params) }
- context 'when feature is missing by runner' do
- let(:params) { {} }
+ context 'when feature is missing by runner' do
+ let(:params) { {} }
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job).to be_runner_unsupported
- end
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job).to be_runner_unsupported
end
+ end
- context 'when feature is supported by runner' do
- let(:params) do
- { info: { features: { upload_multiple_artifacts: true } } }
- end
+ context 'when feature is supported by runner' do
+ let(:params) do
+ { info: { features: { upload_multiple_artifacts: true } } }
+ end
- it 'does pick job' do
- expect(subject).not_to be_nil
- end
+ it 'does pick job' do
+ expect(subject).not_to be_nil
end
end
+ end
- context 'when "dependencies" keyword is specified' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
+ context 'when "dependencies" keyword is specified' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'test', stage_idx: 0)
+ end
- let!(:pending_job) do
- create(:ci_build, :pending, :queued,
- pipeline: pipeline, stage_idx: 1,
- options: { script: ["bash"], dependencies: dependencies })
- end
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued,
+ pipeline: pipeline, stage_idx: 1,
+ options: { script: ["bash"], dependencies: dependencies })
+ end
- let(:dependencies) { %w[test] }
+ let(:dependencies) { %w[test] }
- subject { build_on(project_runner) }
+ subject { build_on(project_runner) }
- it 'picks a build with a dependency' do
- picked_build = build_on(project_runner)
+ it 'picks a build with a dependency' do
+ picked_build = build_on(project_runner)
- expect(picked_build).to be_present
+ expect(picked_build).to be_present
+ end
+
+ context 'when there are multiple dependencies with artifacts' do
+ let!(:pre_stage_job_second) do
+ create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'deploy', stage_idx: 0)
end
- context 'when there are multiple dependencies with artifacts' do
- let!(:pre_stage_job_second) do
- create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'deploy', stage_idx: 0)
- end
+ let(:dependencies) { %w[test deploy] }
- let(:dependencies) { %w[test deploy] }
+ it 'logs build artifacts size' do
+ build_on(project_runner)
- it 'logs build artifacts size' do
- build_on(project_runner)
+ artifacts_size = [pre_stage_job, pre_stage_job_second].sum do |job|
+ job.job_artifacts_archive.size
+ end
- artifacts_size = [pre_stage_job, pre_stage_job_second].sum do |job|
- job.job_artifacts_archive.size
- end
+ expect(artifacts_size).to eq 107464 * 2
+ expect(Gitlab::ApplicationContext.current).to include({
+ 'meta.artifacts_dependencies_size' => artifacts_size,
+ 'meta.artifacts_dependencies_count' => 2
+ })
+ end
+ end
- expect(artifacts_size).to eq 107464 * 2
- expect(Gitlab::ApplicationContext.current).to include({
- 'meta.artifacts_dependencies_size' => artifacts_size,
- 'meta.artifacts_dependencies_count' => 2
- })
- end
+ shared_examples 'not pick' do
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job).to be_missing_dependency_failure
end
+ end
- shared_examples 'not pick' do
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job).to be_missing_dependency_failure
+ shared_examples 'validation is active' do
+ context 'when depended job has not been completed yet' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- end
- shared_examples 'validation is active' do
- context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) do
- create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
+ it { is_expected.to eq(pending_job) }
+ end
- it { is_expected.to eq(pending_job) }
+ context 'when artifacts of depended job has been expired' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- context 'when artifacts of depended job has been expired' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
+ context 'when the pipeline is locked' do
+ before do
+ pipeline.artifacts_locked!
end
- context 'when the pipeline is locked' do
- before do
- pipeline.artifacts_locked!
- end
+ it { is_expected.to eq(pending_job) }
+ end
- it { is_expected.to eq(pending_job) }
+ context 'when the pipeline is unlocked' do
+ before do
+ pipeline.unlocked!
end
- context 'when the pipeline is unlocked' do
- before do
- pipeline.unlocked!
- end
+ it_behaves_like 'not pick'
+ end
+ end
- it_behaves_like 'not pick'
- end
+ context 'when artifacts of depended job has been erased' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
end
- context 'when artifacts of depended job has been erased' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
- end
+ it_behaves_like 'not pick'
+ end
- it_behaves_like 'not pick'
+ context 'when job object is staled' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- context 'when job object is staled' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
-
- before do
- pipeline.unlocked!
+ before do
+ pipeline.unlocked!
- allow_next_instance_of(Ci::Build) do |build|
- expect(build).to receive(:drop!)
- .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
- end
+ allow_next_instance_of(Ci::Build) do |build|
+ expect(build).to receive(:drop!)
+ .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
end
+ end
- it 'does not drop nor pick' do
- expect(subject).to be_nil
- end
+ it 'does not drop nor pick' do
+ expect(subject).to be_nil
end
end
+ end
- shared_examples 'validation is not active' do
- context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) do
- create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
-
- it { expect(subject).to eq(pending_job) }
+ shared_examples 'validation is not active' do
+ context 'when depended job has not been completed yet' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- context 'when artifacts of depended job has been expired' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
+ it { expect(subject).to eq(pending_job) }
+ end
- it { expect(subject).to eq(pending_job) }
+ context 'when artifacts of depended job has been expired' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- context 'when artifacts of depended job has been erased' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
- end
+ it { expect(subject).to eq(pending_job) }
+ end
- it { expect(subject).to eq(pending_job) }
+ context 'when artifacts of depended job has been erased' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
end
- end
- it_behaves_like 'validation is active'
+ it { expect(subject).to eq(pending_job) }
+ end
end
- context 'when build is degenerated' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :degenerated, pipeline: pipeline) }
+ it_behaves_like 'validation is active'
+ end
- subject { build_on(project_runner) }
+ context 'when build is degenerated' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :degenerated, pipeline: pipeline) }
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
+ subject { build_on(project_runner) }
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_archived_failure
- end
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_archived_failure
end
+ end
- context 'when build has data integrity problem' do
- let!(:pending_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline)
- end
+ context 'when build has data integrity problem' do
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued, pipeline: pipeline)
+ end
- before do
- pending_job.update_columns(options: "string")
- end
+ before do
+ pending_job.update_columns(options: "string")
+ end
- subject { build_on(project_runner) }
+ subject { build_on(project_runner) }
- it 'does drop the build and logs both failures' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- .with(anything, a_hash_including(build_id: pending_job.id))
- .twice
- .and_call_original
+ it 'does drop the build and logs both failures' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(anything, a_hash_including(build_id: pending_job.id))
+ .twice
+ .and_call_original
- expect(subject).to be_nil
+ expect(subject).to be_nil
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_data_integrity_failure
- end
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_data_integrity_failure
end
+ end
- context 'when build fails to be run!' do
- let!(:pending_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline)
- end
+ context 'when build fails to be run!' do
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued, pipeline: pipeline)
+ end
- before do
- expect_any_instance_of(Ci::Build).to receive(:run!)
- .and_raise(RuntimeError, 'scheduler error')
- end
+ before do
+ expect_any_instance_of(Ci::Build).to receive(:run!)
+ .and_raise(RuntimeError, 'scheduler error')
+ end
- subject { build_on(project_runner) }
+ subject { build_on(project_runner) }
- it 'does drop the build and logs failure' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- .with(anything, a_hash_including(build_id: pending_job.id))
- .once
- .and_call_original
+ it 'does drop the build and logs failure' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(anything, a_hash_including(build_id: pending_job.id))
+ .once
+ .and_call_original
- expect(subject).to be_nil
+ expect(subject).to be_nil
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_scheduler_failure
- end
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_scheduler_failure
end
+ end
- context 'when an exception is raised during a persistent ref creation' do
- before do
- allow_any_instance_of(Ci::PersistentRef).to receive(:exist?) { false }
- allow_any_instance_of(Ci::PersistentRef).to receive(:create_ref) { raise ArgumentError }
- end
+ context 'when an exception is raised during a persistent ref creation' do
+ before do
+ allow_any_instance_of(Ci::PersistentRef).to receive(:exist?) { false }
+ allow_any_instance_of(Ci::PersistentRef).to receive(:create_ref) { raise ArgumentError }
+ end
- subject { build_on(project_runner) }
+ subject { build_on(project_runner) }
- it 'picks the build' do
- expect(subject).to eq(pending_job)
+ it 'picks the build' do
+ expect(subject).to eq(pending_job)
- pending_job.reload
- expect(pending_job).to be_running
- end
+ pending_job.reload
+ expect(pending_job).to be_running
end
+ end
- context 'when only some builds can be matched by runner' do
- let!(:project_runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[matching]) }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[matching]) }
+ context 'when only some builds can be matched by runner' do
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[matching]) }
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[matching]) }
- before do
- # create additional matching and non-matching jobs
- create_list(:ci_build, 2, :pending, :queued, pipeline: pipeline, tag_list: %w[matching])
- create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[non-matching])
- end
+ before do
+ # create additional matching and non-matching jobs
+ create_list(:ci_build, 2, :pending, :queued, pipeline: pipeline, tag_list: %w[matching])
+ create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[non-matching])
+ end
- it 'observes queue size of only matching jobs' do
- # pending_job + 2 x matching ones
- expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
- .with({ runner_type: project_runner.runner_type }, 3)
+ it 'observes queue size of only matching jobs' do
+ # pending_job + 2 x matching ones
+ expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, 3)
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
- it 'observes queue processing time by the runner type' do
- expect(Gitlab::Ci::Queue::Metrics.queue_iteration_duration_seconds)
- .to receive(:observe)
- .with({ runner_type: project_runner.runner_type }, anything)
+ it 'observes queue processing time by the runner type' do
+ expect(Gitlab::Ci::Queue::Metrics.queue_iteration_duration_seconds)
+ .to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, anything)
- expect(Gitlab::Ci::Queue::Metrics.queue_retrieval_duration_seconds)
- .to receive(:observe)
- .with({ runner_type: project_runner.runner_type }, anything)
+ expect(Gitlab::Ci::Queue::Metrics.queue_retrieval_duration_seconds)
+ .to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, anything)
- expect(build_on(project_runner)).to eq(pending_job)
- end
+ expect(build_on(project_runner)).to eq(pending_job)
end
+ end
- context 'when ci_register_job_temporary_lock is enabled' do
- before do
- stub_feature_flags(ci_register_job_temporary_lock: true)
+ context 'when ci_register_job_temporary_lock is enabled' do
+ before do
+ stub_feature_flags(ci_register_job_temporary_lock: true)
+
+ allow(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ end
- allow(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ context 'when a build is temporarily locked' do
+ let(:service) { described_class.new(project_runner, nil) }
+
+ before do
+ service.send(:acquire_temporary_lock, pending_job.id)
end
- context 'when a build is temporarily locked' do
- let(:service) { described_class.new(project_runner, nil) }
+ it 'skips this build and marks queue as invalid' do
+ expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ .with(operation: :queue_iteration)
+ expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ .with(operation: :build_temporary_locked)
- before do
- service.send(:acquire_temporary_lock, pending_job.id)
- end
+ expect(service.execute).not_to be_valid
+ end
- it 'skips this build and marks queue as invalid' do
+ context 'when there is another build in queue' do
+ let!(:next_pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+
+ it 'skips this build and picks another build' do
expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :queue_iteration)
+ .with(operation: :queue_iteration).twice
expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
.with(operation: :build_temporary_locked)
- expect(service.execute).not_to be_valid
- end
-
- context 'when there is another build in queue' do
- let!(:next_pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
-
- it 'skips this build and picks another build' do
- expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :queue_iteration).twice
- expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :build_temporary_locked)
+ result = service.execute
- result = service.execute
-
- expect(result.build).to eq(next_pending_job)
- expect(result).to be_valid
- end
+ expect(result.build).to eq(next_pending_job)
+ expect(result).to be_valid
end
end
end
end
+ end
- context 'when using pending builds table' do
- include_examples 'handles runner assignment'
+ context 'when using pending builds table' do
+ let!(:runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[conflict]) }
- context 'when a conflicting data is stored in denormalized table' do
- let!(:runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[conflict]) }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[conflict]) }
+ include_examples 'handles runner assignment'
- before do
- pending_job.update_column(:status, :running)
- end
+ context 'when a conflicting data is stored in denormalized table' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[conflict]) }
- it 'removes queuing entry upon build assignment attempt' do
- expect(pending_job.reload).to be_running
- expect(pending_job.queuing_entry).to be_present
+ before do
+ pending_job.update_column(:status, :running)
+ end
- expect(execute).not_to be_valid
- expect(pending_job.reload.queuing_entry).not_to be_present
- end
+ it 'removes queuing entry upon build assignment attempt' do
+ expect(pending_job.reload).to be_running
+ expect(pending_job.queuing_entry).to be_present
+
+ expect(execute).not_to be_valid
+ expect(pending_job.reload.queuing_entry).not_to be_present
end
end
end
@@ -997,8 +1037,8 @@ module Ci
end
end
- def build_on(runner, runner_machine: nil, params: {})
- described_class.new(runner, runner_machine).execute(params).build
+ def build_on(runner, runner_manager: nil, params: {})
+ described_class.new(runner, runner_manager).execute(params).build
end
end
end
diff --git a/spec/services/ci/reset_skipped_jobs_service_spec.rb b/spec/services/ci/reset_skipped_jobs_service_spec.rb
index 712a21e665b..ba6a4a4e822 100644
--- a/spec/services/ci/reset_skipped_jobs_service_spec.rb
+++ b/spec/services/ci/reset_skipped_jobs_service_spec.rb
@@ -6,13 +6,22 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
let_it_be(:project) { create(:project, :empty_repo) }
let_it_be(:user) { project.first_owner }
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a1) { find_job('a1') }
+ let(:a2) { find_job('a2') }
+ let(:b1) { find_job('b1') }
+ let(:input_processables) { a1 } # This is the input used when running service.execute()
+
before_all do
project.repository.create_file(user, 'init', 'init', message: 'init', branch_name: 'master')
end
subject(:service) { described_class.new(project, user) }
- context 'with a stage-dag mixed pipeline' do
+ shared_examples 'with a stage-dag mixed pipeline' do
let(:config) do
<<-YAML
stages: [a, b, c]
@@ -52,13 +61,6 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
YAML
end
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
- end
-
- let(:a1) { find_job('a1') }
- let(:b1) { find_job('b1') }
-
before do
stub_ci_pipeline_yaml_file(config)
check_jobs_statuses(
@@ -107,7 +109,7 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
end
it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a1)
+ service.execute(input_processables)
check_jobs_statuses(
a1: 'pending',
@@ -135,7 +137,7 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
{ 'name' => 'c2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => [] }
)
- execute_after_requeue_service(a1)
+ service.execute(input_processables)
expect(jobs_name_status_owner_needs).to contain_exactly(
{ 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
@@ -150,7 +152,7 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
end
end
- context 'with stage-dag mixed pipeline with some same-stage needs' do
+ shared_examples 'with stage-dag mixed pipeline with some same-stage needs' do
let(:config) do
<<-YAML
stages: [a, b, c]
@@ -184,12 +186,6 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
YAML
end
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
- end
-
- let(:a1) { find_job('a1') }
-
before do
stub_ci_pipeline_yaml_file(config)
check_jobs_statuses(
@@ -224,7 +220,7 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
end
it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a1)
+ service.execute(input_processables)
check_jobs_statuses(
a1: 'pending',
@@ -237,61 +233,465 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
end
end
- context 'with same-stage needs' do
+ shared_examples 'with same-stage needs' do
let(:config) do
<<-YAML
- a:
+ a1:
script: exit $(($RANDOM % 2))
- b:
+ b1:
script: exit 0
- needs: [a]
+ needs: [a1]
- c:
+ c1:
script: exit 0
- needs: [b]
+ needs: [b1]
YAML
end
- let(:pipeline) do
- Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a1: 'pending',
+ b1: 'created',
+ c1: 'created'
+ )
+
+ a1.drop!
+ check_jobs_statuses(
+ a1: 'failed',
+ b1: 'skipped',
+ c1: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+ check_jobs_statuses(
+ a1: 'pending',
+ b1: 'skipped',
+ c1: 'skipped'
+ )
end
- let(:a) { find_job('a') }
+ it 'marks subsequent skipped jobs as processable' do
+ service.execute(input_processables)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ b1: 'created',
+ c1: 'created'
+ )
+ end
+ end
+
+ context 'with same-stage needs where the parent jobs do not share the same descendants' do
+ let(:config) do
+ <<-YAML
+ a1:
+ script: exit $(($RANDOM % 2))
+
+ a2:
+ script: exit $(($RANDOM % 2))
+
+ b1:
+ script: exit 0
+ needs: [a1]
+
+ b2:
+ script: exit 0
+ needs: [a2]
+
+ c1:
+ script: exit 0
+ needs: [b1]
+
+ c2:
+ script: exit 0
+ needs: [b2]
+ YAML
+ end
before do
stub_ci_pipeline_yaml_file(config)
check_jobs_statuses(
- a: 'pending',
- b: 'created',
- c: 'created'
+ a1: 'pending',
+ a2: 'pending',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
)
- a.drop!
+ a1.drop!
+ a2.drop!
+
check_jobs_statuses(
- a: 'failed',
- b: 'skipped',
- c: 'skipped'
+ a1: 'failed',
+ a2: 'failed',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'failed',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
)
- new_a = Ci::RetryJobService.new(project, user).clone!(a)
- new_a.enqueue!
+ new_a2 = Ci::RetryJobService.new(project, user).clone!(a2)
+ new_a2.enqueue!
+
check_jobs_statuses(
- a: 'pending',
- b: 'skipped',
- c: 'skipped'
+ a1: 'pending',
+ a2: 'pending',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
)
end
+ # This demonstrates that when only a1 is inputted, only the *1 subsequent jobs are reset.
+ # This is in contrast to the following example when both a1 and a2 are inputted.
it 'marks subsequent skipped jobs as processable' do
- execute_after_requeue_service(a)
+ service.execute(input_processables)
check_jobs_statuses(
- a: 'pending',
- b: 'created',
- c: 'created'
+ a1: 'pending',
+ a2: 'pending',
+ b1: 'created',
+ b2: 'skipped',
+ c1: 'created',
+ c2: 'skipped'
)
end
+
+ context 'when multiple processables are inputted' do
+ # When both a1 and a2 are inputted, all subsequent jobs are reset.
+ it 'marks subsequent skipped jobs as processable' do
+ input_processables = [a1, a2]
+ service.execute(input_processables)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'pending',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+ end
+ end
+
+ context 'when a single processable is inputted' do
+ it_behaves_like 'with a stage-dag mixed pipeline'
+ it_behaves_like 'with stage-dag mixed pipeline with some same-stage needs'
+ it_behaves_like 'with same-stage needs'
+ end
+
+ context 'when multiple processables are inputted' do
+ let(:input_processables) { [a1, b1] }
+
+ it_behaves_like 'with a stage-dag mixed pipeline'
+ it_behaves_like 'with stage-dag mixed pipeline with some same-stage needs'
+ it_behaves_like 'with same-stage needs'
+ end
+
+ context 'when FF is `ci_support_reset_skipped_jobs_for_multiple_jobs` disabled' do
+ before do
+ stub_feature_flags(ci_support_reset_skipped_jobs_for_multiple_jobs: false)
+ end
+
+ context 'with a stage-dag mixed pipeline' do
+ let(:config) do
+ <<-YAML
+ stages: [a, b, c]
+
+ a1:
+ stage: a
+ script: exit $(($RANDOM % 2))
+
+ a2:
+ stage: a
+ script: exit 0
+ needs: [a1]
+
+ a3:
+ stage: a
+ script: exit 0
+ needs: [a2]
+
+ b1:
+ stage: b
+ script: exit 0
+ needs: []
+
+ b2:
+ stage: b
+ script: exit 0
+ needs: [a2]
+
+ c1:
+ stage: c
+ script: exit 0
+ needs: [b2]
+
+ c2:
+ stage: c
+ script: exit 0
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a1) { find_job('a1') }
+ let(:b1) { find_job('b1') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'pending',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ b1.success!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ a1.drop!
+ check_jobs_statuses(
+ a1: 'failed',
+ a2: 'skipped',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'skipped',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'created',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+
+ context 'when executed by a different user than the original owner' do
+ let(:retryer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:service) { described_class.new(project, retryer) }
+
+ it 'reassigns jobs with updated statuses to the retryer' do
+ expect(jobs_name_status_owner_needs).to contain_exactly(
+ { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'a2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a1'] },
+ { 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'b2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { 'name' => 'c1', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['b2'] },
+ { 'name' => 'c2', 'status' => 'skipped', 'user_id' => user.id, 'needs' => [] }
+ )
+
+ execute_after_requeue_service(a1)
+
+ expect(jobs_name_status_owner_needs).to contain_exactly(
+ { 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'a2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a1'] },
+ { 'name' => 'a3', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
+ { 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
+ { 'name' => 'b2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
+ { 'name' => 'c1', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['b2'] },
+ { 'name' => 'c2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => [] }
+ )
+ end
+ end
+ end
+
+ context 'with stage-dag mixed pipeline with some same-stage needs' do
+ let(:config) do
+ <<-YAML
+ stages: [a, b, c]
+
+ a1:
+ stage: a
+ script: exit $(($RANDOM % 2))
+
+ a2:
+ stage: a
+ script: exit 0
+ needs: [a1]
+
+ b1:
+ stage: b
+ script: exit 0
+ needs: [b2]
+
+ b2:
+ stage: b
+ script: exit 0
+
+ c1:
+ stage: c
+ script: exit 0
+ needs: [b2]
+
+ c2:
+ stage: c
+ script: exit 0
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a1) { find_job('a1') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+
+ a1.drop!
+ check_jobs_statuses(
+ a1: 'failed',
+ a2: 'skipped',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
+ new_a1.enqueue!
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'skipped',
+ b1: 'skipped',
+ b2: 'skipped',
+ c1: 'skipped',
+ c2: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ b1: 'created',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+ end
+
+ context 'with same-stage needs' do
+ let(:config) do
+ <<-YAML
+ a:
+ script: exit $(($RANDOM % 2))
+
+ b:
+ script: exit 0
+ needs: [a]
+
+ c:
+ script: exit 0
+ needs: [b]
+ YAML
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a) { find_job('a') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+
+ a.drop!
+ check_jobs_statuses(
+ a: 'failed',
+ b: 'skipped',
+ c: 'skipped'
+ )
+
+ new_a = Ci::RetryJobService.new(project, user).clone!(a)
+ new_a.enqueue!
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'skipped',
+ c: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a)
+
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+ end
+ end
end
private
@@ -314,6 +714,7 @@ RSpec.describe Ci::ResetSkippedJobsService, :sidekiq_inline, feature_category: :
end
end
+ # Remove this method when FF is `ci_support_reset_skipped_jobs_for_multiple_jobs` is removed
def execute_after_requeue_service(processable)
service.execute(processable)
end
diff --git a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
index 3d1abe290bc..ea15e3ea2c0 100644
--- a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
+++ b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do
+RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService, feature_category: :continuous_integration do
include ConcurrentHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index fed66bc535d..f15f4a16d4f 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -51,11 +51,13 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do
let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let_it_be(:job_to_clone) do
- create(:ci_build, :failed, :picked, :expired, :erased, :queued, :coverage, :tags,
- :allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group,
- description: 'my-job', ci_stage: stage,
- pipeline: pipeline, auto_canceled_by: another_pipeline,
- scheduled_at: 10.seconds.since)
+ create(
+ :ci_build, :failed, :picked, :expired, :erased, :queued, :coverage, :tags,
+ :allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group,
+ description: 'my-job', ci_stage: stage,
+ pipeline: pipeline, auto_canceled_by: another_pipeline,
+ scheduled_at: 10.seconds.since
+ )
end
before do
@@ -236,8 +238,7 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do
context 'when a build with a deployment is retried' do
let!(:job) do
- create(:ci_build, :with_deployment, :deploy_to_production,
- pipeline: pipeline, ci_stage: stage)
+ create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline, ci_stage: stage)
end
it 'creates a new deployment' do
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 07518c35fab..fc2c66e7f73 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RetryPipelineService, '#execute' do
+RSpec.describe Ci::RetryPipelineService, '#execute', feature_category: :continuous_integration do
include ProjectForksHelper
let_it_be_with_refind(:user) { create(:user) }
@@ -19,8 +19,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
before do
project.add_developer(user)
- create(:protected_branch, :developers_can_merge,
- name: pipeline.ref, project: project)
+ create(:protected_branch, :developers_can_merge, name: pipeline.ref, project: project)
end
context 'when there are already retried jobs present' do
@@ -408,8 +407,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when user is not allowed to trigger manual action' do
before do
project.add_developer(user)
- create(:protected_branch, :maintainers_can_push,
- name: pipeline.ref, project: project)
+ create(:protected_branch, :maintainers_can_push, name: pipeline.ref, project: project)
end
context 'when there is a failed manual action present' do
@@ -490,11 +488,15 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
end
def create_processable(type, name, status, stage, **opts)
- create(type, name: name,
- status: status,
- ci_stage: stage,
- stage_idx: stage.position,
- pipeline: pipeline, **opts) do |_job|
+ create(
+ type,
+ name: name,
+ status: status,
+ ci_stage: stage,
+ stage_idx: stage.position,
+ pipeline: pipeline,
+ **opts
+ ) do |_job|
::Ci::ProcessPipelineService.new(pipeline).execute
end
end
diff --git a/spec/services/ci/run_scheduled_build_service_spec.rb b/spec/services/ci/run_scheduled_build_service_spec.rb
index 27d25e88944..33f9efcb89f 100644
--- a/spec/services/ci/run_scheduled_build_service_spec.rb
+++ b/spec/services/ci/run_scheduled_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RunScheduledBuildService do
+RSpec.describe Ci::RunScheduledBuildService, feature_category: :continuous_integration do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
@@ -13,8 +13,7 @@ RSpec.describe Ci::RunScheduledBuildService do
before do
project.add_developer(user)
- create(:protected_branch, :developers_can_merge,
- name: pipeline.ref, project: project)
+ create(:protected_branch, :developers_can_merge, name: pipeline.ref, project: project)
end
context 'when build is scheduled' do
diff --git a/spec/services/ci/runners/create_runner_service_spec.rb b/spec/services/ci/runners/create_runner_service_spec.rb
index 673bf3ef90e..db337b0b005 100644
--- a/spec/services/ci/runners/create_runner_service_spec.rb
+++ b/spec/services/ci/runners/create_runner_service_spec.rb
@@ -3,24 +3,20 @@
require 'spec_helper'
RSpec.describe ::Ci::Runners::CreateRunnerService, "#execute", feature_category: :runner_fleet do
- subject(:execute) { described_class.new(user: current_user, type: type, params: params).execute }
+ subject(:execute) { described_class.new(user: current_user, params: params).execute }
let(:runner) { execute.payload[:runner] }
let_it_be(:admin) { create(:admin) }
let_it_be(:non_admin_user) { create(:user) }
let_it_be(:anonymous) { nil }
+ let_it_be(:group_owner) { create(:user) }
- shared_context 'when admin user' do
- let(:current_user) { admin }
-
- before do
- allow(current_user).to receive(:can?).with(:create_instance_runners).and_return true
- end
- end
+ let_it_be(:group) { create(:group) }
shared_examples 'it can create a runner' do
- it 'creates a runner of the specified type' do
+ it 'creates a runner of the specified type', :aggregate_failures do
+ is_expected.to be_success
expect(runner.runner_type).to eq expected_type
end
@@ -42,7 +38,7 @@ RSpec.describe ::Ci::Runners::CreateRunnerService, "#execute", feature_category:
expect(runner.active).to be true
expect(runner.creator).to be current_user
expect(runner.authenticated_user_registration_type?).to be_truthy
- expect(runner.runner_type).to eq 'instance_type'
+ expect(runner.runner_type).to eq expected_type
end
end
@@ -81,7 +77,50 @@ RSpec.describe ::Ci::Runners::CreateRunnerService, "#execute", feature_category:
expect(runner.maximum_timeout).to eq args[:maximum_timeout]
expect(runner.authenticated_user_registration_type?).to be_truthy
- expect(runner.runner_type).to eq 'instance_type'
+ expect(runner.runner_type).to eq expected_type
+ end
+
+ context 'with a nil paused value' do
+ let(:args) do
+ {
+ paused: nil,
+ description: 'some description',
+ maintenance_note: 'a note',
+ tag_list: %w[tag1 tag2],
+ access_level: 'ref_protected',
+ locked: true,
+ maximum_timeout: 600,
+ run_untagged: false
+ }
+ end
+
+ it { is_expected.to be_success }
+
+ it 'creates runner with active set to true' do
+ expect(runner).to be_an_instance_of(::Ci::Runner)
+ expect(runner.active).to eq true
+ end
+ end
+
+ context 'with no paused value given' do
+ let(:args) do
+ {
+ description: 'some description',
+ maintenance_note: 'a note',
+ tag_list: %w[tag1 tag2],
+ access_level: 'ref_protected',
+ locked: true,
+ maximum_timeout: 600,
+ run_untagged: false
+ }
+ end
+
+ it { is_expected.to be_success }
+
+ it 'creates runner with active set to true' do
+ expect(runner).to be_an_instance_of(::Ci::Runner)
+ expect(runner.active).to eq true
+ end
end
end
end
@@ -95,7 +134,6 @@ RSpec.describe ::Ci::Runners::CreateRunnerService, "#execute", feature_category:
end
shared_examples 'it can return an error' do
- let(:group) { create(:group) }
let(:runner_double) { Ci::Runner.new }
context 'when the runner fails to save' do
@@ -111,25 +149,148 @@ RSpec.describe ::Ci::Runners::CreateRunnerService, "#execute", feature_category:
end
end
- context 'with type param set to nil' do
+ context 'with :runner_type param set to instance_type' do
let(:expected_type) { 'instance_type' }
- let(:type) { nil }
- let(:params) { {} }
+ let(:params) { { runner_type: 'instance_type' } }
- it_behaves_like 'it cannot create a runner' do
+ context 'when anonymous user' do
let(:current_user) { anonymous }
+
+ it_behaves_like 'it cannot create a runner'
end
- it_behaves_like 'it cannot create a runner' do
+ context 'when non-admin user' do
let(:current_user) { non_admin_user }
+
+ it_behaves_like 'it cannot create a runner'
end
- it_behaves_like 'it can create a runner' do
- include_context 'when admin user'
+ context 'when admin user' do
+ let(:current_user) { admin }
+
+ it_behaves_like 'it cannot create a runner'
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it_behaves_like 'it can create a runner'
+ it_behaves_like 'it can return an error'
+
+ context 'with unexpected scope param specified' do
+ let(:params) { { runner_type: 'instance_type', scope: group } }
+
+ it_behaves_like 'it cannot create a runner'
+ end
+
+ context 'when model validation fails' do
+ let(:params) { { runner_type: 'instance_type', run_untagged: false, tag_list: [] } }
+
+ it_behaves_like 'it cannot create a runner'
+
+ it 'returns error message and reason', :aggregate_failures do
+ expect(execute.reason).to eq(:save_error)
+ expect(execute.message).to contain_exactly(a_string_including('Tags list can not be empty'))
+ end
+ end
+ end
+ end
+ end
+
+ context 'with :runner_type param set to group_type' do
+ let(:expected_type) { 'group_type' }
+ let(:params) { { runner_type: 'group_type', scope: group } }
+
+ before do
+ group.add_developer(non_admin_user)
+ group.add_owner(group_owner)
+ end
+
+ context 'when anonymous user' do
+ let(:current_user) { anonymous }
+
+ it_behaves_like 'it cannot create a runner'
+ end
+
+ context 'when non-admin user' do
+ let(:current_user) { non_admin_user }
+
+ it_behaves_like 'it cannot create a runner'
end
- it_behaves_like 'it can return an error' do
- include_context 'when admin user'
+ context 'when group owner' do
+ let(:current_user) { group_owner }
+
+ it_behaves_like 'it can create a runner'
+
+ context 'with missing scope param' do
+ let(:params) { { runner_type: 'group_type' } }
+
+ it_behaves_like 'it cannot create a runner'
+ end
+ end
+
+ context 'when admin user' do
+ let(:current_user) { admin }
+
+ it_behaves_like 'it cannot create a runner'
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it_behaves_like 'it can create a runner'
+ it_behaves_like 'it can return an error'
+ end
+ end
+ end
+
+ context 'with :runner_type param set to project_type' do
+ let_it_be(:project) { create(:project, namespace: group) }
+
+ let(:expected_type) { 'project_type' }
+ let(:params) { { runner_type: 'project_type', scope: project } }
+
+ before do
+ group.add_developer(non_admin_user)
+ group.add_owner(group_owner)
+ end
+
+ context 'when anonymous user' do
+ let(:current_user) { anonymous }
+
+ it_behaves_like 'it cannot create a runner'
+ end
+
+ context 'when group owner' do
+ let(:current_user) { group_owner }
+
+ it_behaves_like 'it can create a runner'
+
+ context 'with missing scope param' do
+ let(:params) { { runner_type: 'project_type' } }
+
+ it_behaves_like 'it cannot create a runner'
+ end
+ end
+
+ context 'when non-admin user' do
+ let(:current_user) { non_admin_user }
+
+ it_behaves_like 'it cannot create a runner'
+
+ context 'with project permissions to create runner' do
+ before do
+ project.add_maintainer(current_user)
+ end
+
+ it_behaves_like 'it can create a runner'
+ end
+ end
+
+ context 'when admin user' do
+ let(:current_user) { admin }
+
+ it_behaves_like 'it cannot create a runner'
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it_behaves_like 'it can create a runner'
+ it_behaves_like 'it can return an error'
+ end
end
end
end
diff --git a/spec/services/ci/runners/process_runner_version_update_service_spec.rb b/spec/services/ci/runners/process_runner_version_update_service_spec.rb
index e62cb1ec3e3..f8b7aa281af 100644
--- a/spec/services/ci/runners/process_runner_version_update_service_spec.rb
+++ b/spec/services/ci/runners/process_runner_version_update_service_spec.rb
@@ -29,6 +29,19 @@ RSpec.describe Ci::Runners::ProcessRunnerVersionUpdateService, feature_category:
end
end
+ context 'when fetching runner releases is disabled' do
+ before do
+ stub_application_setting(update_runner_versions_enabled: false)
+ end
+
+ it 'does not update ci_runner_versions records', :aggregate_failures do
+ expect do
+ expect(execute).to be_error
+ expect(execute.message).to eq 'version update disabled'
+ end.not_to change(Ci::RunnerVersion, :count).from(0)
+ end
+ end
+
context 'with successful result from upgrade check' do
before do
url = ::Gitlab::CurrentSettings.current_application_settings.public_runner_releases_url
diff --git a/spec/services/ci/runners/register_runner_service_spec.rb b/spec/services/ci/runners/register_runner_service_spec.rb
index c67040e45eb..b5921773364 100644
--- a/spec/services/ci/runners/register_runner_service_spec.rb
+++ b/spec/services/ci/runners/register_runner_service_spec.rb
@@ -7,13 +7,23 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
let(:token) {}
let(:args) { {} }
let(:runner) { execute.payload[:runner] }
+ let(:allow_runner_registration_token) { true }
before do
stub_application_setting(runners_registration_token: registration_token)
stub_application_setting(valid_runner_registrars: ApplicationSetting::VALID_RUNNER_REGISTRAR_TYPES)
+ stub_application_setting(allow_runner_registration_token: allow_runner_registration_token)
end
- subject(:execute) { described_class.new.execute(token, args) }
+ subject(:execute) { described_class.new(token, args).execute }
+
+ shared_examples 'runner registration is disallowed' do
+ it 'returns error response with runner_registration_disallowed reason' do
+ expect(execute).to be_error
+ expect(execute.message).to eq 'runner registration disallowed'
+ expect(execute.reason).to eq :runner_registration_disallowed
+ end
+ end
context 'when no token is provided' do
let(:token) { '' }
@@ -36,7 +46,7 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
end
context 'when valid token is provided' do
- context 'with a registration token' do
+ context 'when instance registration token is used' do
let(:token) { registration_token }
it 'creates runner with default values' do
@@ -51,6 +61,12 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
expect(runner).to be_instance_type
end
+ context 'when registering instance runners is disallowed' do
+ let(:allow_runner_registration_token) { false }
+
+ it_behaves_like 'runner registration is disallowed'
+ end
+
context 'with non-default arguments' do
let(:args) do
{
@@ -112,9 +128,15 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
end
end
- context 'when project token is used' do
- let(:project) { create(:project) }
+ context 'when project registration token is used' do
+ let_it_be(:project) { create(:project, :with_namespace_settings) }
+
let(:token) { project.runners_token }
+ let(:allow_group_runner_registration_token) { true }
+
+ before do
+ project.namespace.update!(allow_runner_registration_token: allow_group_runner_registration_token)
+ end
it 'creates project runner' do
expect(execute).to be_success
@@ -127,6 +149,18 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
expect(runner).to be_project_type
end
+ context 'with runner registration disabled at instance level' do
+ let(:allow_runner_registration_token) { false }
+
+ it_behaves_like 'runner registration is disallowed'
+ end
+
+ context 'with runner registration disabled at group level' do
+ let(:allow_group_runner_registration_token) { false }
+
+ it_behaves_like 'runner registration is disallowed'
+ end
+
context 'when it exceeds the application limits' do
before do
create(:ci_runner, runner_type: :project_type, projects: [project], contacted_at: 1.second.ago)
@@ -173,9 +207,15 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
end
end
- context 'when group token is used' do
- let(:group) { create(:group) }
+ context 'when group registration token is used' do
+ let_it_be_with_refind(:group) { create(:group) }
+
let(:token) { group.runners_token }
+ let(:allow_group_runner_registration_token) { true }
+
+ before do
+ group.update!(allow_runner_registration_token: allow_group_runner_registration_token)
+ end
it 'creates a group runner' do
expect(execute).to be_success
@@ -188,6 +228,18 @@ RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute', feature_categor
expect(runner).to be_group_type
end
+ context 'with runner registration disabled at instance level' do
+ let(:allow_runner_registration_token) { false }
+
+ it_behaves_like 'runner registration is disallowed'
+ end
+
+ context 'with runner registration disabled at group level' do
+ let(:allow_group_runner_registration_token) { false }
+
+ it_behaves_like 'runner registration is disallowed'
+ end
+
context 'when it exceeds the application limits' do
before do
create(:ci_runner, runner_type: :group_type, groups: [group], contacted_at: nil, created_at: 1.month.ago)
diff --git a/spec/services/ci/runners/stale_machines_cleanup_service_spec.rb b/spec/services/ci/runners/stale_managers_cleanup_service_spec.rb
index 456dbcebb84..a78506ca5f7 100644
--- a/spec/services/ci/runners/stale_machines_cleanup_service_spec.rb
+++ b/spec/services/ci/runners/stale_managers_cleanup_service_spec.rb
@@ -2,22 +2,22 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::StaleMachinesCleanupService, feature_category: :runner_fleet do
+RSpec.describe Ci::Runners::StaleManagersCleanupService, feature_category: :runner_fleet do
let(:service) { described_class.new }
- let!(:runner_machine3) { create(:ci_runner_machine, created_at: 6.months.ago, contacted_at: Time.current) }
+ let!(:runner_manager3) { create(:ci_runner_machine, created_at: 6.months.ago, contacted_at: Time.current) }
subject(:response) { service.execute }
- context 'with no stale runner machines' do
- it 'does not clean any runner machines and returns :success status' do
+ context 'with no stale runner managers' do
+ it 'does not clean any runner managers and returns :success status' do
expect do
expect(response).to be_success
- expect(response.payload).to match({ deleted_machines: false })
- end.not_to change { Ci::RunnerMachine.count }.from(1)
+ expect(response.payload).to match({ deleted_managers: false })
+ end.not_to change { Ci::RunnerManager.count }.from(1)
end
end
- context 'with some stale runner machines' do
+ context 'with some stale runner managers' do
before do
create(:ci_runner_machine, :stale)
create(:ci_runner_machine, :stale, contacted_at: nil)
@@ -25,8 +25,8 @@ RSpec.describe Ci::Runners::StaleMachinesCleanupService, feature_category: :runn
it 'only leaves non-stale runners' do
expect(response).to be_success
- expect(response.payload).to match({ deleted_machines: true })
- expect(Ci::RunnerMachine.all).to contain_exactly(runner_machine3)
+ expect(response.payload).to match({ deleted_managers: true })
+ expect(Ci::RunnerManager.all).to contain_exactly(runner_manager3)
end
context 'with more stale runners than MAX_DELETIONS' do
@@ -37,8 +37,8 @@ RSpec.describe Ci::Runners::StaleMachinesCleanupService, feature_category: :runn
it 'only leaves non-stale runners' do
expect do
expect(response).to be_success
- expect(response.payload).to match({ deleted_machines: true })
- end.to change { Ci::RunnerMachine.count }.by(-Ci::Runners::StaleMachinesCleanupService::MAX_DELETIONS)
+ expect(response.payload).to match({ deleted_managers: true })
+ end.to change { Ci::RunnerManager.count }.by(-Ci::Runners::StaleManagersCleanupService::MAX_DELETIONS)
end
end
end
diff --git a/spec/services/ci/runners/unregister_runner_manager_service_spec.rb b/spec/services/ci/runners/unregister_runner_manager_service_spec.rb
new file mode 100644
index 00000000000..8bfda8e2083
--- /dev/null
+++ b/spec/services/ci/runners/unregister_runner_manager_service_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Ci::Runners::UnregisterRunnerManagerService, '#execute', feature_category: :runner_fleet do
+ subject(:execute) { described_class.new(runner, 'some_token', system_id: system_id).execute }
+
+ context 'with runner registered with registration token' do
+ let!(:runner) { create(:ci_runner, registration_type: :registration_token) }
+ let(:system_id) { nil }
+
+ it 'does not destroy runner or runner managers' do
+ expect do
+ expect(execute).to be_error
+ end.to not_change { Ci::Runner.count }
+ .and not_change { Ci::RunnerManager.count }
+ expect(runner[:errors]).to be_nil
+ end
+ end
+
+ context 'with runner created in UI' do
+ let!(:runner_manager1) { create(:ci_runner_machine, runner: runner, system_xid: 'system_id_1') }
+ let!(:runner_manager2) { create(:ci_runner_machine, runner: runner, system_xid: 'system_id_2') }
+ let!(:runner) { create(:ci_runner, registration_type: :authenticated_user) }
+
+ context 'with system_id specified' do
+ let(:system_id) { runner_manager1.system_xid }
+
+ it 'destroys runner_manager1 and leaves runner', :aggregate_failures do
+ expect do
+ expect(execute).to be_success
+ end.to change { Ci::RunnerManager.count }.by(-1)
+ .and not_change { Ci::Runner.count }
+ expect(runner[:errors]).to be_nil
+ expect(runner.runner_managers).to contain_exactly(runner_manager2)
+ end
+ end
+
+ context 'with unknown system_id' do
+ let(:system_id) { 'unknown_system_id' }
+
+ it 'raises RecordNotFound error', :aggregate_failures do
+ expect do
+ execute
+ end.to raise_error(ActiveRecord::RecordNotFound)
+ .and not_change { Ci::Runner.count }
+ .and not_change { Ci::RunnerManager.count }
+ end
+ end
+
+ context 'with system_id missing' do
+ let(:system_id) { nil }
+
+ it 'returns error and leaves runner_manager1', :aggregate_failures do
+ expect do
+ expect(execute).to be_error
+ expect(execute.message).to eq('`system_id` needs to be specified for runners created in the UI.')
+ end.to not_change { Ci::Runner.count }
+ .and not_change { Ci::RunnerManager.count }
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/stuck_builds/drop_pending_service_spec.rb b/spec/services/ci/stuck_builds/drop_pending_service_spec.rb
index a452a65829a..6d91f5098eb 100644
--- a/spec/services/ci/stuck_builds/drop_pending_service_spec.rb
+++ b/spec/services/ci/stuck_builds/drop_pending_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::StuckBuilds::DropPendingService do
+RSpec.describe Ci::StuckBuilds::DropPendingService, feature_category: :runner_fleet do
let_it_be(:runner) { create(:ci_runner) }
let_it_be(:pipeline) { create(:ci_empty_pipeline) }
let_it_be_with_reload(:job) do
diff --git a/spec/services/ci/stuck_builds/drop_running_service_spec.rb b/spec/services/ci/stuck_builds/drop_running_service_spec.rb
index c1c92c2b8e2..deb807753c2 100644
--- a/spec/services/ci/stuck_builds/drop_running_service_spec.rb
+++ b/spec/services/ci/stuck_builds/drop_running_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::StuckBuilds::DropRunningService do
+RSpec.describe Ci::StuckBuilds::DropRunningService, feature_category: :runner_fleet do
let!(:runner) { create :ci_runner }
let!(:job) { create(:ci_build, runner: runner, created_at: created_at, updated_at: updated_at, status: status) }
diff --git a/spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb b/spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb
index a4f9f97fffc..f2e658c3ae3 100644
--- a/spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb
+++ b/spec/services/ci/stuck_builds/drop_scheduled_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::StuckBuilds::DropScheduledService do
+RSpec.describe Ci::StuckBuilds::DropScheduledService, feature_category: :runner_fleet do
let_it_be(:runner) { create :ci_runner }
let!(:job) { create :ci_build, :scheduled, scheduled_at: scheduled_at, runner: runner }
diff --git a/spec/services/ci/test_failure_history_service_spec.rb b/spec/services/ci/test_failure_history_service_spec.rb
index 10f6c6f5007..e77c6533483 100644
--- a/spec/services/ci/test_failure_history_service_spec.rb
+++ b/spec/services/ci/test_failure_history_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures do
+RSpec.describe Ci::TestFailureHistoryService, :aggregate_failures, feature_category: :continuous_integration do
let_it_be(:project) { create(:project, :repository) }
let_it_be_with_reload(:pipeline) do
diff --git a/spec/services/ci/track_failed_build_service_spec.rb b/spec/services/ci/track_failed_build_service_spec.rb
index 676769d2fc7..23e7cee731d 100644
--- a/spec/services/ci/track_failed_build_service_spec.rb
+++ b/spec/services/ci/track_failed_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::TrackFailedBuildService do
+RSpec.describe Ci::TrackFailedBuildService, feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project, user: user) }
diff --git a/spec/services/ci/unlock_artifacts_service_spec.rb b/spec/services/ci/unlock_artifacts_service_spec.rb
index c15e1cb2b5d..0d6ac333587 100644
--- a/spec/services/ci/unlock_artifacts_service_spec.rb
+++ b/spec/services/ci/unlock_artifacts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::UnlockArtifactsService do
+RSpec.describe Ci::UnlockArtifactsService, feature_category: :continuous_integration do
using RSpec::Parameterized::TableSyntax
where(:tag) do
@@ -24,7 +24,7 @@ RSpec.describe Ci::UnlockArtifactsService do
let!(:older_ambiguous_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: !tag, project: project, locked: :artifacts_locked) }
let!(:code_coverage_pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
- let!(:child_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:child_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, child_of: pipeline, project: project, locked: :artifacts_locked) }
let!(:newer_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:other_ref_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: 'other_ref', tag: tag, project: project, locked: :artifacts_locked) }
let!(:sources_pipeline) { create(:ci_sources_pipeline, source_job: source_job, source_project: project, pipeline: child_pipeline, project: project) }
@@ -120,6 +120,12 @@ RSpec.describe Ci::UnlockArtifactsService do
let(:before_pipeline) { pipeline }
it 'produces the expected SQL string' do
+ # To be removed when the ignored column id_convert_to_bigint for ci_pipelines is removed
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/397000
+ selected_columns =
+ Ci::Pipeline.column_names.map do |field|
+ Ci::Pipeline.connection.quote_table_name("#{Ci::Pipeline.table_name}.#{field}")
+ end.join(', ')
expect(subject.squish).to eq <<~SQL.squish
UPDATE
"ci_pipelines"
@@ -140,14 +146,14 @@ RSpec.describe Ci::UnlockArtifactsService do
"base_and_descendants"
AS
((SELECT
- "ci_pipelines".*
+ #{selected_columns}
FROM
"ci_pipelines"
WHERE
"ci_pipelines"."id" = #{before_pipeline.id})
UNION
(SELECT
- "ci_pipelines".*
+ #{selected_columns}
FROM
"ci_pipelines",
"base_and_descendants",
@@ -201,8 +207,7 @@ RSpec.describe Ci::UnlockArtifactsService do
describe '#unlock_job_artifacts_query' do
subject { described_class.new(pipeline.project, pipeline.user).unlock_job_artifacts_query(pipeline_ids) }
- context 'when running on a ref before a pipeline' do
- let(:before_pipeline) { pipeline }
+ context 'when given a single pipeline ID' do
let(:pipeline_ids) { [older_pipeline.id] }
it 'produces the expected SQL string' do
@@ -226,8 +231,7 @@ RSpec.describe Ci::UnlockArtifactsService do
end
end
- context 'when running on just the ref' do
- let(:before_pipeline) { nil }
+ context 'when given multiple pipeline IDs' do
let(:pipeline_ids) { [older_pipeline.id, newer_pipeline.id, pipeline.id] }
it 'produces the expected SQL string' do
diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb
index dd26339831c..4fd4492278d 100644
--- a/spec/services/ci/update_build_queue_service_spec.rb
+++ b/spec/services/ci/update_build_queue_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::UpdateBuildQueueService do
+RSpec.describe Ci::UpdateBuildQueueService, feature_category: :continuous_integration do
let(:project) { create(:project, :repository) }
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
diff --git a/spec/services/ci/update_instance_variables_service_spec.rb b/spec/services/ci/update_instance_variables_service_spec.rb
index f235d006e34..889f49eca5a 100644
--- a/spec/services/ci/update_instance_variables_service_spec.rb
+++ b/spec/services/ci/update_instance_variables_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::UpdateInstanceVariablesService do
+RSpec.describe Ci::UpdateInstanceVariablesService, feature_category: :secrets_management do
let(:params) { { variables_attributes: variables_attributes } }
subject { described_class.new(params) }
diff --git a/spec/services/ci/update_pending_build_service_spec.rb b/spec/services/ci/update_pending_build_service_spec.rb
index e49b22299f0..abf31dd5184 100644
--- a/spec/services/ci/update_pending_build_service_spec.rb
+++ b/spec/services/ci/update_pending_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::UpdatePendingBuildService do
+RSpec.describe Ci::UpdatePendingBuildService, feature_category: :continuous_integration do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be_with_reload(:pending_build_1) { create(:ci_pending_build, project: project, instance_runners_enabled: false) }
diff --git a/spec/services/clusters/agent_tokens/create_service_spec.rb b/spec/services/clusters/agent_tokens/create_service_spec.rb
index dc7abd1504b..803bd947629 100644
--- a/spec/services/clusters/agent_tokens/create_service_spec.rb
+++ b/spec/services/clusters/agent_tokens/create_service_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-RSpec.describe Clusters::AgentTokens::CreateService do
- subject(:service) { described_class.new(container: project, current_user: user, params: params) }
+RSpec.describe Clusters::AgentTokens::CreateService, feature_category: :deployment_management do
+ subject(:service) { described_class.new(agent: cluster_agent, current_user: user, params: params) }
let_it_be(:user) { create(:user) }
let(:cluster_agent) { create(:cluster_agent) }
let(:project) { cluster_agent.project }
- let(:params) { { agent_id: cluster_agent.id, description: 'token description', name: 'token name' } }
+ let(:params) { { description: 'token description', name: 'token name' } }
describe '#execute' do
subject { service.execute }
@@ -75,7 +75,7 @@ RSpec.describe Clusters::AgentTokens::CreateService do
it 'returns validation errors', :aggregate_failures do
expect(subject.status).to eq(:error)
- expect(subject.message).to eq(["Agent must exist", "Name can't be blank"])
+ expect(subject.message).to eq(["Name can't be blank"])
end
end
end
diff --git a/spec/services/clusters/agent_tokens/revoke_service_spec.rb b/spec/services/clusters/agent_tokens/revoke_service_spec.rb
new file mode 100644
index 00000000000..a1537658723
--- /dev/null
+++ b/spec/services/clusters/agent_tokens/revoke_service_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::AgentTokens::RevokeService, feature_category: :deployment_management do
+ describe '#execute' do
+ subject { described_class.new(token: agent_token, current_user: user).execute }
+
+ let(:agent) { create(:cluster_agent) }
+ let(:agent_token) { create(:cluster_agent_token, agent: agent) }
+ let(:project) { agent.project }
+ let(:user) { agent.created_by_user }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when user is authorized' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'when user revokes agent token' do
+ it 'succeeds' do
+ subject
+
+ expect(agent_token.revoked?).to be true
+ end
+
+ it 'creates an activity event' do
+ expect { subject }.to change { ::Clusters::Agents::ActivityEvent.count }.by(1)
+
+ event = agent.activity_events.last
+
+ expect(event).to have_attributes(
+ kind: 'token_revoked',
+ level: 'info',
+ recorded_at: agent_token.reload.updated_at,
+ user: user,
+ agent_token: agent_token
+ )
+ end
+ end
+
+ context 'when there is a validation failure' do
+ before do
+ agent_token.name = '' # make the record invalid, as we require a name to be present
+ end
+
+ it 'fails without raising an error', :aggregate_failures do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq(["Name can't be blank"])
+ end
+
+ it 'does not create an activity event' do
+ expect { subject }.not_to change { ::Clusters::Agents::ActivityEvent.count }
+ end
+ end
+ end
+
+ context 'when user is not authorized' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_guest(user)
+ end
+
+ context 'when user attempts to revoke agent token' do
+ it 'fails' do
+ subject
+
+ expect(agent_token.revoked?).to be false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/agent_tokens/track_usage_service_spec.rb b/spec/services/clusters/agent_tokens/track_usage_service_spec.rb
index 3350b15a5ce..6bea8afcc80 100644
--- a/spec/services/clusters/agent_tokens/track_usage_service_spec.rb
+++ b/spec/services/clusters/agent_tokens/track_usage_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::AgentTokens::TrackUsageService do
+RSpec.describe Clusters::AgentTokens::TrackUsageService, feature_category: :deployment_management do
let_it_be(:agent) { create(:cluster_agent) }
describe '#execute', :clean_gitlab_redis_cache do
diff --git a/spec/services/clusters/agents/filter_authorizations_service_spec.rb b/spec/services/clusters/agents/authorizations/ci_access/filter_service_spec.rb
index 62cff405d0c..45443cfd887 100644
--- a/spec/services/clusters/agents/filter_authorizations_service_spec.rb
+++ b/spec/services/clusters/agents/authorizations/ci_access/filter_service_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::FilterAuthorizationsService, feature_category: :continuous_integration do
+RSpec.describe Clusters::Agents::Authorizations::CiAccess::FilterService, feature_category: :continuous_integration do
describe '#execute' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let(:agent_authorizations_without_env) do
[
- build(:agent_project_authorization, project: project, agent: build(:cluster_agent, project: project)),
- build(:agent_group_authorization, group: group, agent: build(:cluster_agent, project: project)),
- ::Clusters::Agents::ImplicitAuthorization.new(agent: build(:cluster_agent, project: project))
+ build(:agent_ci_access_project_authorization, project: project, agent: build(:cluster_agent, project: project)),
+ build(:agent_ci_access_group_authorization, group: group, agent: build(:cluster_agent, project: project)),
+ ::Clusters::Agents::Authorizations::CiAccess::ImplicitAuthorization.new(agent: build(:cluster_agent, project: project))
]
end
@@ -31,13 +31,13 @@ RSpec.describe Clusters::Agents::FilterAuthorizationsService, feature_category:
let(:agent_authorizations_with_env) do
[
build(
- :agent_project_authorization,
+ :agent_ci_access_project_authorization,
project: project,
agent: build(:cluster_agent, project: project),
environments: ['staging', 'review/*', 'production']
),
build(
- :agent_group_authorization,
+ :agent_ci_access_group_authorization,
group: group,
agent: build(:cluster_agent, project: project),
environments: ['staging', 'review/*', 'production']
@@ -48,13 +48,13 @@ RSpec.describe Clusters::Agents::FilterAuthorizationsService, feature_category:
let(:agent_authorizations_with_different_env) do
[
build(
- :agent_project_authorization,
+ :agent_ci_access_project_authorization,
project: project,
agent: build(:cluster_agent, project: project),
environments: ['staging']
),
build(
- :agent_group_authorization,
+ :agent_ci_access_group_authorization,
group: group,
agent: build(:cluster_agent, project: project),
environments: ['staging']
diff --git a/spec/services/clusters/agents/refresh_authorization_service_spec.rb b/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb
index 51c054ddc98..c12592cc071 100644
--- a/spec/services/clusters/agents/refresh_authorization_service_spec.rb
+++ b/spec/services/clusters/agents/authorizations/ci_access/refresh_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category: :kubernetes_management do
+RSpec.describe Clusters::Agents::Authorizations::CiAccess::RefreshService, feature_category: :deployment_management do
describe '#execute' do
let_it_be(:root_ancestor) { create(:group) }
@@ -39,11 +39,11 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
before do
default_config = { default_namespace: 'default' }
- agent.group_authorizations.create!(group: removed_group, config: default_config)
- agent.group_authorizations.create!(group: modified_group, config: default_config)
+ agent.ci_access_group_authorizations.create!(group: removed_group, config: default_config)
+ agent.ci_access_group_authorizations.create!(group: modified_group, config: default_config)
- agent.project_authorizations.create!(project: removed_project, config: default_config)
- agent.project_authorizations.create!(project: modified_project, config: default_config)
+ agent.ci_access_project_authorizations.create!(project: removed_project, config: default_config)
+ agent.ci_access_project_authorizations.create!(project: modified_project, config: default_config)
end
shared_examples 'removing authorization' do
@@ -78,12 +78,12 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
describe 'group authorization' do
it 'refreshes authorizations for the agent' do
expect(subject).to be_truthy
- expect(agent.authorized_groups).to contain_exactly(added_group, modified_group)
+ expect(agent.ci_access_authorized_groups).to contain_exactly(added_group, modified_group)
- added_authorization = agent.group_authorizations.find_by(group: added_group)
+ added_authorization = agent.ci_access_group_authorizations.find_by(group: added_group)
expect(added_authorization.config).to eq({ 'default_namespace' => 'default' })
- modified_authorization = agent.group_authorizations.find_by(group: modified_group)
+ modified_authorization = agent.ci_access_group_authorizations.find_by(group: modified_group)
expect(modified_authorization.config).to eq({ 'default_namespace' => 'new-namespace' })
end
@@ -94,24 +94,24 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
it 'authorizes groups up to the limit' do
expect(subject).to be_truthy
- expect(agent.authorized_groups).to contain_exactly(added_group)
+ expect(agent.ci_access_authorized_groups).to contain_exactly(added_group)
end
end
include_examples 'removing authorization' do
- let(:authorizations) { agent.authorized_groups }
+ let(:authorizations) { agent.ci_access_authorized_groups }
end
end
describe 'project authorization' do
it 'refreshes authorizations for the agent' do
expect(subject).to be_truthy
- expect(agent.authorized_projects).to contain_exactly(added_project, modified_project)
+ expect(agent.ci_access_authorized_projects).to contain_exactly(added_project, modified_project)
- added_authorization = agent.project_authorizations.find_by(project: added_project)
+ added_authorization = agent.ci_access_project_authorizations.find_by(project: added_project)
expect(added_authorization.config).to eq({ 'default_namespace' => 'default' })
- modified_authorization = agent.project_authorizations.find_by(project: modified_project)
+ modified_authorization = agent.ci_access_project_authorizations.find_by(project: modified_project)
expect(modified_authorization.config).to eq({ 'default_namespace' => 'new-namespace' })
end
@@ -121,7 +121,7 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
it 'creates an authorization record for the project' do
expect(subject).to be_truthy
- expect(agent.authorized_projects).to contain_exactly(added_project)
+ expect(agent.ci_access_authorized_projects).to contain_exactly(added_project)
end
end
@@ -131,7 +131,7 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
it 'creates an authorization record for the project' do
expect(subject).to be_truthy
- expect(agent.authorized_projects).to contain_exactly(added_project)
+ expect(agent.ci_access_authorized_projects).to contain_exactly(added_project)
end
end
@@ -142,12 +142,12 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category:
it 'authorizes projects up to the limit' do
expect(subject).to be_truthy
- expect(agent.authorized_projects).to contain_exactly(added_project)
+ expect(agent.ci_access_authorized_projects).to contain_exactly(added_project)
end
end
include_examples 'removing authorization' do
- let(:authorizations) { agent.authorized_projects }
+ let(:authorizations) { agent.ci_access_authorized_projects }
end
end
end
diff --git a/spec/services/clusters/agents/authorizations/user_access/refresh_service_spec.rb b/spec/services/clusters/agents/authorizations/user_access/refresh_service_spec.rb
new file mode 100644
index 00000000000..da546ca44a9
--- /dev/null
+++ b/spec/services/clusters/agents/authorizations/user_access/refresh_service_spec.rb
@@ -0,0 +1,181 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::Authorizations::UserAccess::RefreshService, feature_category: :deployment_management do
+ describe '#execute' do
+ let_it_be(:root_ancestor) { create(:group) }
+ let_it_be(:agent_management_project) { create(:project, namespace: root_ancestor) }
+ let_it_be(:group_1) { create(:group, path: 'group-path-with-UPPERCASE', parent: root_ancestor) }
+ let_it_be(:group_2) { create(:group, parent: root_ancestor) }
+ let_it_be(:project_1) { create(:project, path: 'project-path-with-UPPERCASE', namespace: root_ancestor) }
+ let_it_be(:project_2) { create(:project, namespace: root_ancestor) }
+
+ let(:agent) { create(:cluster_agent, project: agent_management_project) }
+
+ let(:config) do
+ {
+ user_access: {
+ groups: [
+ { id: group_2.full_path }
+ ],
+ projects: [
+ { id: project_2.full_path }
+ ]
+ }
+ }.deep_merge(extra_config).deep_stringify_keys
+ end
+
+ let(:extra_config) { {} }
+
+ subject { described_class.new(agent, config: config).execute }
+
+ before do
+ agent.user_access_group_authorizations.create!(group: group_1, config: {})
+ agent.user_access_project_authorizations.create!(project: project_1, config: {})
+ end
+
+ shared_examples 'removing authorization' do
+ context 'when config contains no groups or projects' do
+ let(:config) { {} }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(authorizations).to be_empty
+ end
+ end
+
+ context 'when config contains groups or projects outside of the configuration project hierarchy' do
+ let_it_be(:agent_management_project) { create(:project, namespace: create(:group)) }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(authorizations).to be_empty
+ end
+ end
+
+ context 'when configuration project does not belong to a group' do
+ let_it_be(:agent_management_project) { create(:project) }
+
+ it 'removes all authorizations' do
+ expect(subject).to be_truthy
+ expect(authorizations).to be_empty
+ end
+ end
+ end
+
+ describe 'group authorization' do
+ it 'refreshes authorizations for the agent' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_groups).to contain_exactly(group_2)
+
+ added_authorization = agent.user_access_group_authorizations.find_by(group: group_2)
+ expect(added_authorization.config).to eq({})
+ end
+
+ context 'when config contains "access_as" keyword' do
+ let(:extra_config) do
+ {
+ user_access: {
+ access_as: {
+ agent: {}
+ }
+ }
+ }
+ end
+
+ it 'refreshes authorizations for the agent' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_groups).to contain_exactly(group_2)
+
+ added_authorization = agent.user_access_group_authorizations.find_by(group: group_2)
+ expect(added_authorization.config).to eq({ 'access_as' => { 'agent' => {} } })
+ end
+ end
+
+ context 'when config contains too many groups' do
+ before do
+ stub_const("#{described_class}::AUTHORIZED_ENTITY_LIMIT", 0)
+ end
+
+ it 'authorizes groups up to the limit' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_groups).to be_empty
+ end
+ end
+
+ include_examples 'removing authorization' do
+ let(:authorizations) { agent.user_access_authorized_groups }
+ end
+ end
+
+ describe 'project authorization' do
+ it 'refreshes authorizations for the agent' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_projects).to contain_exactly(project_2)
+
+ added_authorization = agent.user_access_project_authorizations.find_by(project: project_2)
+ expect(added_authorization.config).to eq({})
+ end
+
+ context 'when config contains "access_as" keyword' do
+ let(:extra_config) do
+ {
+ user_access: {
+ access_as: {
+ agent: {}
+ }
+ }
+ }
+ end
+
+ it 'refreshes authorizations for the agent' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_projects).to contain_exactly(project_2)
+
+ added_authorization = agent.user_access_project_authorizations.find_by(project: project_2)
+ expect(added_authorization.config).to eq({ 'access_as' => { 'agent' => {} } })
+ end
+ end
+
+ context 'when project belongs to a user namespace, and is in the same namespace as the agent' do
+ let_it_be(:root_ancestor) { create(:namespace) }
+ let_it_be(:agent_management_project) { create(:project, namespace: root_ancestor) }
+ let_it_be(:project_1) { create(:project, path: 'project-path-with-UPPERCASE', namespace: root_ancestor) }
+ let_it_be(:project_2) { create(:project, namespace: root_ancestor) }
+
+ it 'creates an authorization record for the project' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_projects).to contain_exactly(project_2)
+ end
+ end
+
+ context 'when project belongs to a user namespace, and is authorizing itself' do
+ let_it_be(:root_ancestor) { create(:namespace) }
+ let_it_be(:agent_management_project) { create(:project, namespace: root_ancestor) }
+ let_it_be(:project_1) { create(:project, path: 'project-path-with-UPPERCASE', namespace: root_ancestor) }
+ let_it_be(:project_2) { agent_management_project }
+
+ it 'creates an authorization record for the project' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_projects).to contain_exactly(project_2)
+ end
+ end
+
+ context 'when config contains too many projects' do
+ before do
+ stub_const("#{described_class}::AUTHORIZED_ENTITY_LIMIT", 0)
+ end
+
+ it 'authorizes projects up to the limit' do
+ expect(subject).to be_truthy
+ expect(agent.user_access_authorized_projects).to be_empty
+ end
+ end
+
+ include_examples 'removing authorization' do
+ let(:authorizations) { agent.user_access_authorized_projects }
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/agents/authorize_proxy_user_service_spec.rb b/spec/services/clusters/agents/authorize_proxy_user_service_spec.rb
new file mode 100644
index 00000000000..2d6c79c5cb3
--- /dev/null
+++ b/spec/services/clusters/agents/authorize_proxy_user_service_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::AuthorizeProxyUserService, feature_category: :deployment_management do
+ subject(:service_response) { service.execute }
+
+ let(:service) { described_class.new(user, agent) }
+ let(:user) { create(:user) }
+
+ let_it_be(:organization) { create(:group) }
+ let_it_be(:configuration_project) { create(:project, group: organization) }
+ let_it_be(:agent) { create(:cluster_agent, name: 'the-agent', project: configuration_project) }
+ let_it_be(:deployment_project) { create(:project, group: organization) }
+ let_it_be(:deployment_group) { create(:group, parent: organization) }
+
+ let(:user_access_config) do
+ {
+ 'user_access' => {
+ 'access_as' => { 'agent' => {} },
+ 'projects' => [{ 'id' => deployment_project.full_path }],
+ 'groups' => [{ 'id' => deployment_group.full_path }]
+ }
+ }
+ end
+
+ before do
+ Clusters::Agents::Authorizations::UserAccess::RefreshService.new(agent, config: user_access_config).execute
+ end
+
+ it 'returns forbidden when user has no access to any project', :aggregate_failures do
+ expect(service_response).to be_error
+ expect(service_response.reason).to eq :forbidden
+ end
+
+ context 'when user is member of an authorized group' do
+ it 'authorizes developers', :aggregate_failures do
+ deployment_group.add_member(user, :developer)
+ expect(service_response).to be_success
+ expect(service_response.payload[:user]).to include(id: user.id, username: user.username)
+ expect(service_response.payload[:agent]).to include(id: agent.id, config_project: { id: agent.project.id })
+ end
+
+ it 'does not authorize reporters', :aggregate_failures do
+ deployment_group.add_member(user, :reporter)
+ expect(service_response).to be_error
+ expect(service_response.reason).to eq :forbidden
+ end
+ end
+
+ context 'when user is member of an authorized project' do
+ it 'authorizes developers', :aggregate_failures do
+ deployment_project.add_member(user, :developer)
+ expect(service_response).to be_success
+ expect(service_response.payload[:user]).to include(id: user.id, username: user.username)
+ expect(service_response.payload[:agent]).to include(id: agent.id, config_project: { id: agent.project.id })
+ end
+
+ it 'does not authorize reporters', :aggregate_failures do
+ deployment_project.add_member(user, :reporter)
+ expect(service_response).to be_error
+ expect(service_response.reason).to eq :forbidden
+ end
+ end
+end
diff --git a/spec/services/clusters/agents/create_activity_event_service_spec.rb b/spec/services/clusters/agents/create_activity_event_service_spec.rb
index 7a8f0e16d60..0d784bb69c7 100644
--- a/spec/services/clusters/agents/create_activity_event_service_spec.rb
+++ b/spec/services/clusters/agents/create_activity_event_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::CreateActivityEventService do
+RSpec.describe Clusters::Agents::CreateActivityEventService, feature_category: :deployment_management do
let_it_be(:agent) { create(:cluster_agent) }
let_it_be(:token) { create(:cluster_agent_token, agent: agent) }
let_it_be(:user) { create(:user) }
@@ -40,5 +40,16 @@ RSpec.describe Clusters::Agents::CreateActivityEventService do
subject
end
+
+ context 'when activity event creation fails' do
+ let(:params) { {} }
+
+ it 'tracks the exception without raising' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(instance_of(ActiveRecord::RecordInvalid), agent_id: agent.id)
+
+ subject
+ end
+ end
end
end
diff --git a/spec/services/clusters/agents/create_service_spec.rb b/spec/services/clusters/agents/create_service_spec.rb
index 2b3bbcae13c..85607fcdf3a 100644
--- a/spec/services/clusters/agents/create_service_spec.rb
+++ b/spec/services/clusters/agents/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::CreateService do
+RSpec.describe Clusters::Agents::CreateService, feature_category: :deployment_management do
subject(:service) { described_class.new(project, user) }
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/services/clusters/agents/delete_expired_events_service_spec.rb b/spec/services/clusters/agents/delete_expired_events_service_spec.rb
index 3dc166f54eb..7dc9c280ab4 100644
--- a/spec/services/clusters/agents/delete_expired_events_service_spec.rb
+++ b/spec/services/clusters/agents/delete_expired_events_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::DeleteExpiredEventsService do
+RSpec.describe Clusters::Agents::DeleteExpiredEventsService, feature_category: :deployment_management do
let_it_be(:agent) { create(:cluster_agent) }
describe '#execute' do
diff --git a/spec/services/clusters/agents/delete_service_spec.rb b/spec/services/clusters/agents/delete_service_spec.rb
index abe1bdaab27..febbb7ba5c8 100644
--- a/spec/services/clusters/agents/delete_service_spec.rb
+++ b/spec/services/clusters/agents/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Agents::DeleteService do
+RSpec.describe Clusters::Agents::DeleteService, feature_category: :deployment_management do
subject(:service) { described_class.new(container: project, current_user: user) }
let(:cluster_agent) { create(:cluster_agent) }
diff --git a/spec/services/clusters/build_kubernetes_namespace_service_spec.rb b/spec/services/clusters/build_kubernetes_namespace_service_spec.rb
index 4ee933374f6..fea17495914 100644
--- a/spec/services/clusters/build_kubernetes_namespace_service_spec.rb
+++ b/spec/services/clusters/build_kubernetes_namespace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::BuildKubernetesNamespaceService do
+RSpec.describe Clusters::BuildKubernetesNamespaceService, feature_category: :deployment_management do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:environment) { create(:environment) }
let(:project) { environment.project }
diff --git a/spec/services/clusters/build_service_spec.rb b/spec/services/clusters/build_service_spec.rb
index c7a64435d3b..909d3f58c48 100644
--- a/spec/services/clusters/build_service_spec.rb
+++ b/spec/services/clusters/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::BuildService do
+RSpec.describe Clusters::BuildService, feature_category: :deployment_management do
describe '#execute' do
subject { described_class.new(cluster_subject).execute }
diff --git a/spec/services/clusters/cleanup/project_namespace_service_spec.rb b/spec/services/clusters/cleanup/project_namespace_service_spec.rb
index 8d3ae217a9f..34311d6e830 100644
--- a/spec/services/clusters/cleanup/project_namespace_service_spec.rb
+++ b/spec/services/clusters/cleanup/project_namespace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Cleanup::ProjectNamespaceService do
+RSpec.describe Clusters::Cleanup::ProjectNamespaceService, feature_category: :deployment_management do
describe '#execute' do
subject { service.execute }
diff --git a/spec/services/clusters/cleanup/service_account_service_spec.rb b/spec/services/clusters/cleanup/service_account_service_spec.rb
index 769762237f9..f5a3c2e8eb1 100644
--- a/spec/services/clusters/cleanup/service_account_service_spec.rb
+++ b/spec/services/clusters/cleanup/service_account_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Cleanup::ServiceAccountService do
+RSpec.describe Clusters::Cleanup::ServiceAccountService, feature_category: :deployment_management do
describe '#execute' do
subject { service.execute }
@@ -55,14 +55,16 @@ RSpec.describe Clusters::Cleanup::ServiceAccountService do
context 'when there is a Kubeclient::HttpError' do
['Unauthorized', 'forbidden', 'Certificate verify Failed'].each do |message|
- before do
- allow(kubeclient_instance_double)
- .to receive(:delete_service_account)
- .and_raise(Kubeclient::HttpError.new(401, message, nil))
- end
+ context "with error:#{message}" do
+ before do
+ allow(kubeclient_instance_double)
+ .to receive(:delete_service_account)
+ .and_raise(Kubeclient::HttpError.new(401, message, nil))
+ end
- it 'destroys cluster' do
- expect { subject }.to change { Clusters::Cluster.where(id: cluster.id).exists? }.from(true).to(false)
+ it 'destroys cluster' do
+ expect { subject }.to change { Clusters::Cluster.where(id: cluster.id).exists? }.from(true).to(false)
+ end
end
end
end
diff --git a/spec/services/clusters/create_service_spec.rb b/spec/services/clusters/create_service_spec.rb
index 95f10cdbd80..e130f713cb2 100644
--- a/spec/services/clusters/create_service_spec.rb
+++ b/spec/services/clusters/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::CreateService do
+RSpec.describe Clusters::CreateService, feature_category: :deployment_management do
let(:access_token) { 'xxx' }
let(:project) { create(:project) }
let(:user) { create(:user) }
@@ -50,7 +50,7 @@ RSpec.describe Clusters::CreateService do
end
context 'when project has a cluster' do
- include_context 'valid cluster create params'
+ include_context 'with valid cluster create params'
let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it 'creates another cluster' do
diff --git a/spec/services/clusters/destroy_service_spec.rb b/spec/services/clusters/destroy_service_spec.rb
index dc600c9e830..dd3e24d0e12 100644
--- a/spec/services/clusters/destroy_service_spec.rb
+++ b/spec/services/clusters/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::DestroyService do
+RSpec.describe Clusters::DestroyService, feature_category: :deployment_management do
describe '#execute' do
subject { described_class.new(cluster.user, params).execute(cluster) }
diff --git a/spec/services/clusters/integrations/create_service_spec.rb b/spec/services/clusters/integrations/create_service_spec.rb
index 9104e07504d..b716e4f4651 100644
--- a/spec/services/clusters/integrations/create_service_spec.rb
+++ b/spec/services/clusters/integrations/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Integrations::CreateService, '#execute' do
+RSpec.describe Clusters::Integrations::CreateService, '#execute', feature_category: :deployment_management do
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
diff --git a/spec/services/clusters/integrations/prometheus_health_check_service_spec.rb b/spec/services/clusters/integrations/prometheus_health_check_service_spec.rb
index 526462931a6..9390d4b368b 100644
--- a/spec/services/clusters/integrations/prometheus_health_check_service_spec.rb
+++ b/spec/services/clusters/integrations/prometheus_health_check_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Integrations::PrometheusHealthCheckService, '#execute' do
+RSpec.describe Clusters::Integrations::PrometheusHealthCheckService, '#execute', feature_category: :deployment_management do
let(:service) { described_class.new(cluster) }
subject { service.execute }
diff --git a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
index 90956e7b4ea..48941792c4b 100644
--- a/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute' do
+RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute', feature_category: :deployment_management do
include KubernetesHelpers
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
@@ -11,7 +11,7 @@ RSpec.describe Clusters::Kubernetes::CreateOrUpdateNamespaceService, '#execute'
let(:project) { cluster.project }
let(:environment) { create(:environment, project: project) }
let(:cluster_project) { cluster.cluster_project }
- let(:namespace) { "#{project.name}-#{project.id}-#{environment.slug}" }
+ let(:namespace) { "#{project.path}-#{project.id}-#{environment.slug}" }
subject do
described_class.new(
diff --git a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
index 37478a0bcd9..ab0c5691b06 100644
--- a/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
+++ b/spec/services/clusters/kubernetes/create_or_update_service_account_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService do
+RSpec.describe Clusters::Kubernetes::CreateOrUpdateServiceAccountService, feature_category: :deployment_management do
include KubernetesHelpers
let(:api_url) { 'http://111.111.111.111' }
diff --git a/spec/services/clusters/kubernetes/fetch_kubernetes_token_service_spec.rb b/spec/services/clusters/kubernetes/fetch_kubernetes_token_service_spec.rb
index 03c402fb066..439dc37e684 100644
--- a/spec/services/clusters/kubernetes/fetch_kubernetes_token_service_spec.rb
+++ b/spec/services/clusters/kubernetes/fetch_kubernetes_token_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Kubernetes::FetchKubernetesTokenService do
+RSpec.describe Clusters::Kubernetes::FetchKubernetesTokenService, feature_category: :deployment_management do
include KubernetesHelpers
describe '#execute' do
diff --git a/spec/services/clusters/kubernetes_spec.rb b/spec/services/clusters/kubernetes_spec.rb
index 12af63890fc..cd430f81a65 100644
--- a/spec/services/clusters/kubernetes_spec.rb
+++ b/spec/services/clusters/kubernetes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Kubernetes do
+RSpec.describe Clusters::Kubernetes, feature_category: :deployment_management do
it { is_expected.to be_const_defined(:GITLAB_SERVICE_ACCOUNT_NAME) }
it { is_expected.to be_const_defined(:GITLAB_SERVICE_ACCOUNT_NAMESPACE) }
it { is_expected.to be_const_defined(:GITLAB_ADMIN_TOKEN_NAME) }
diff --git a/spec/services/clusters/management/validate_management_project_permissions_service_spec.rb b/spec/services/clusters/management/validate_management_project_permissions_service_spec.rb
index a21c378d3d1..46032de600d 100644
--- a/spec/services/clusters/management/validate_management_project_permissions_service_spec.rb
+++ b/spec/services/clusters/management/validate_management_project_permissions_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::Management::ValidateManagementProjectPermissionsService do
+RSpec.describe Clusters::Management::ValidateManagementProjectPermissionsService, feature_category: :deployment_management do
describe '#execute' do
subject { described_class.new(user).execute(cluster, management_project_id) }
diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb
index 9aead97f41c..cc759407376 100644
--- a/spec/services/clusters/update_service_spec.rb
+++ b/spec/services/clusters/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Clusters::UpdateService do
+RSpec.describe Clusters::UpdateService, feature_category: :deployment_management do
include KubernetesHelpers
describe '#execute' do
diff --git a/spec/services/cohorts_service_spec.rb b/spec/services/cohorts_service_spec.rb
index dce8d4f80f2..ab53bcf8657 100644
--- a/spec/services/cohorts_service_spec.rb
+++ b/spec/services/cohorts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe CohortsService do
+RSpec.describe CohortsService, feature_category: :shared do
describe '#execute' do
def month_start(months_ago)
months_ago.months.ago.beginning_of_month.to_date
diff --git a/spec/services/commits/cherry_pick_service_spec.rb b/spec/services/commits/cherry_pick_service_spec.rb
index 2565e17ac90..880ebea1c09 100644
--- a/spec/services/commits/cherry_pick_service_spec.rb
+++ b/spec/services/commits/cherry_pick_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Commits::CherryPickService do
+RSpec.describe Commits::CherryPickService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
# * ddd0f15ae83993f5cb66a927a28673882e99100b (HEAD -> master, origin/master, origin/HEAD) Merge branch 'po-fix-test-en
# |\
diff --git a/spec/services/commits/commit_patch_service_spec.rb b/spec/services/commits/commit_patch_service_spec.rb
index edd0918e488..a9d61be23be 100644
--- a/spec/services/commits/commit_patch_service_spec.rb
+++ b/spec/services/commits/commit_patch_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Commits::CommitPatchService do
+RSpec.describe Commits::CommitPatchService, feature_category: :source_code_management do
describe '#execute' do
let(:patches) do
patches_folder = Rails.root.join('spec/fixtures/patchfiles')
diff --git a/spec/services/commits/tag_service_spec.rb b/spec/services/commits/tag_service_spec.rb
index dd742ebe469..25aa84276c3 100644
--- a/spec/services/commits/tag_service_spec.rb
+++ b/spec/services/commits/tag_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Commits::TagService do
+RSpec.describe Commits::TagService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb
index e96a7f2f4f4..6757fbdf5d4 100644
--- a/spec/services/compare_service_spec.rb
+++ b/spec/services/compare_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe CompareService do
+RSpec.describe CompareService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:service) { described_class.new(project, 'feature') }
diff --git a/spec/services/concerns/audit_event_save_type_spec.rb b/spec/services/concerns/audit_event_save_type_spec.rb
index fbaebd9f85c..a89eb513d27 100644
--- a/spec/services/concerns/audit_event_save_type_spec.rb
+++ b/spec/services/concerns/audit_event_save_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AuditEventSaveType do
+RSpec.describe AuditEventSaveType, feature_category: :audit_events do
subject(:target) { Object.new.extend(described_class) }
describe '#should_save_database? and #should_save_stream?' do
diff --git a/spec/services/concerns/exclusive_lease_guard_spec.rb b/spec/services/concerns/exclusive_lease_guard_spec.rb
index 6a2aa0a377b..ca8bff4ecc4 100644
--- a/spec/services/concerns/exclusive_lease_guard_spec.rb
+++ b/spec/services/concerns/exclusive_lease_guard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state do
+RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state, feature_category: :shared do
subject :subject_class do
Class.new do
include ExclusiveLeaseGuard
@@ -49,11 +49,49 @@ RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state do
subject.exclusive_lease.cancel
end
- it 'does not call internal_method but logs error', :aggregate_failures do
- expect(subject).not_to receive(:internal_method)
- expect(Gitlab::AppLogger).to receive(:error).with("Cannot obtain an exclusive lease for #{subject.lease_key}. There must be another instance already in execution.")
+ context 'when the class does not override lease_taken_log_level' do
+ it 'does not call internal_method but logs error', :aggregate_failures do
+ expect(subject).not_to receive(:internal_method)
+ expect(Gitlab::AppJsonLogger).to receive(:error).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
- subject.call
+ subject.call
+ end
+ end
+
+ context 'when the class overrides lease_taken_log_level to return :info' do
+ subject :overwritten_subject_class do
+ Class.new(subject_class) do
+ def lease_taken_log_level
+ :info
+ end
+ end
+ end
+
+ let(:subject) { overwritten_subject_class.new }
+
+ it 'logs info', :aggregate_failures do
+ expect(Gitlab::AppJsonLogger).to receive(:info).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
+
+ subject.call
+ end
+ end
+
+ context 'when the class overrides lease_taken_log_level to return :debug' do
+ subject :overwritten_subject_class do
+ Class.new(subject_class) do
+ def lease_taken_log_level
+ :debug
+ end
+ end
+ end
+
+ let(:subject) { overwritten_subject_class.new }
+
+ it 'logs debug', :aggregate_failures do
+ expect(Gitlab::AppJsonLogger).to receive(:debug).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })
+
+ subject.call
+ end
end
end
diff --git a/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb b/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb
index 5b1e8fca31b..c6ee5b78c13 100644
--- a/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb
+++ b/spec/services/concerns/merge_requests/assigns_merge_params_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::AssignsMergeParams do
+RSpec.describe MergeRequests::AssignsMergeParams, feature_category: :code_review_workflow do
it 'raises an error when used from an instance that does not respond to #current_user' do
define_class = -> { Class.new { include MergeRequests::AssignsMergeParams }.new }
diff --git a/spec/services/concerns/rate_limited_service_spec.rb b/spec/services/concerns/rate_limited_service_spec.rb
index d913cd17067..2172c756ecf 100644
--- a/spec/services/concerns/rate_limited_service_spec.rb
+++ b/spec/services/concerns/rate_limited_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe RateLimitedService do
+RSpec.describe RateLimitedService, feature_category: :rate_limiting do
let(:key) { :issues_create }
let(:scope) { [:container, :current_user] }
let(:opts) { { scope: scope, users_allowlist: -> { [User.support_bot.username] } } }
diff --git a/spec/services/container_expiration_policies/cleanup_service_spec.rb b/spec/services/container_expiration_policies/cleanup_service_spec.rb
index 6e1be7271e1..4663944f0b9 100644
--- a/spec/services/container_expiration_policies/cleanup_service_spec.rb
+++ b/spec/services/container_expiration_policies/cleanup_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ContainerExpirationPolicies::CleanupService do
+RSpec.describe ContainerExpirationPolicies::CleanupService, feature_category: :container_registry do
let_it_be(:repository, reload: true) { create(:container_repository, expiration_policy_started_at: 30.minutes.ago) }
let_it_be(:project) { repository.project }
@@ -190,6 +190,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
context 'with only the current repository started_at before the policy next_run_at' do
before do
+ repository.update!(expiration_policy_started_at: policy.next_run_at + 9.minutes)
repository2.update!(expiration_policy_started_at: policy.next_run_at + 10.minutes)
repository3.update!(expiration_policy_started_at: policy.next_run_at + 12.minutes)
end
diff --git a/spec/services/container_expiration_policies/update_service_spec.rb b/spec/services/container_expiration_policies/update_service_spec.rb
index 7d949b77de7..992240201e0 100644
--- a/spec/services/container_expiration_policies/update_service_spec.rb
+++ b/spec/services/container_expiration_policies/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ContainerExpirationPolicies::UpdateService do
+RSpec.describe ContainerExpirationPolicies::UpdateService, feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
let_it_be(:project, reload: true) { create(:project) }
diff --git a/spec/services/customer_relations/contacts/create_service_spec.rb b/spec/services/customer_relations/contacts/create_service_spec.rb
index db6cce799fe..91aa51385e7 100644
--- a/spec/services/customer_relations/contacts/create_service_spec.rb
+++ b/spec/services/customer_relations/contacts/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe CustomerRelations::Contacts::CreateService do
+RSpec.describe CustomerRelations::Contacts::CreateService, feature_category: :service_desk do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:not_found_or_does_not_belong) { 'The specified organization was not found or does not belong to this group' }
@@ -50,8 +50,8 @@ RSpec.describe CustomerRelations::Contacts::CreateService do
end
it 'returns an error when the organization belongs to a different group' do
- organization = create(:organization)
- params[:organization_id] = organization.id
+ crm_organization = create(:crm_organization)
+ params[:organization_id] = crm_organization.id
expect(response).to be_error
expect(response.message).to match_array([not_found_or_does_not_belong])
diff --git a/spec/services/customer_relations/contacts/update_service_spec.rb b/spec/services/customer_relations/contacts/update_service_spec.rb
index 729fdc2058b..105b5bad5f7 100644
--- a/spec/services/customer_relations/contacts/update_service_spec.rb
+++ b/spec/services/customer_relations/contacts/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe CustomerRelations::Contacts::UpdateService do
+RSpec.describe CustomerRelations::Contacts::UpdateService, feature_category: :service_desk do
let_it_be(:user) { create(:user) }
let(:contact) { create(:contact, first_name: 'Mark', group: group, state: 'active') }
diff --git a/spec/services/customer_relations/organizations/create_service_spec.rb b/spec/services/customer_relations/organizations/create_service_spec.rb
index 18eefdd716e..8748fe44763 100644
--- a/spec/services/customer_relations/organizations/create_service_spec.rb
+++ b/spec/services/customer_relations/organizations/create_service_spec.rb
@@ -2,16 +2,16 @@
require 'spec_helper'
-RSpec.describe CustomerRelations::Organizations::CreateService do
+RSpec.describe CustomerRelations::Organizations::CreateService, feature_category: :service_desk do
describe '#execute' do
let_it_be(:user) { create(:user) }
let(:group) { create(:group, :crm_enabled) }
- let(:params) { attributes_for(:organization, group: group) }
+ let(:params) { attributes_for(:crm_organization, group: group) }
subject(:response) { described_class.new(group: group, current_user: user, params: params).execute }
- it 'creates an organization' do
+ it 'creates a crm_organization' do
group.add_developer(user)
expect(response).to be_success
@@ -24,7 +24,7 @@ RSpec.describe CustomerRelations::Organizations::CreateService do
expect(response.message).to match_array(['You have insufficient permissions to create an organization for this group'])
end
- it 'returns an error when the organization is not persisted' do
+ it 'returns an error when the crm_organization is not persisted' do
group.add_developer(user)
params[:name] = nil
diff --git a/spec/services/customer_relations/organizations/update_service_spec.rb b/spec/services/customer_relations/organizations/update_service_spec.rb
index 4764ba85551..f11b99b101e 100644
--- a/spec/services/customer_relations/organizations/update_service_spec.rb
+++ b/spec/services/customer_relations/organizations/update_service_spec.rb
@@ -2,12 +2,12 @@
require 'spec_helper'
-RSpec.describe CustomerRelations::Organizations::UpdateService do
+RSpec.describe CustomerRelations::Organizations::UpdateService, feature_category: :service_desk do
let_it_be(:user) { create(:user) }
- let(:organization) { create(:organization, name: 'Test', group: group, state: 'active') }
+ let(:crm_organization) { create(:crm_organization, name: 'Test', group: group, state: 'active') }
- subject(:update) { described_class.new(group: group, current_user: user, params: params).execute(organization) }
+ subject(:update) { described_class.new(group: group, current_user: user, params: params).execute(crm_organization) }
describe '#execute' do
context 'when the user has no permission' do
@@ -33,7 +33,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
context 'when name is changed' do
let(:params) { { name: 'GitLab' } }
- it 'updates the organization' do
+ it 'updates the crm_organization' do
response = update
expect(response).to be_success
@@ -42,7 +42,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
end
context 'when activating' do
- let(:organization) { create(:organization, state: 'inactive') }
+ let(:crm_organization) { create(:crm_organization, state: 'inactive') }
let(:params) { { active: true } }
it 'updates the contact' do
@@ -56,7 +56,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
context 'when deactivating' do
let(:params) { { active: false } }
- it 'updates the organization' do
+ it 'updates the crm_organization' do
response = update
expect(response).to be_success
@@ -64,7 +64,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
end
end
- context 'when the organization is invalid' do
+ context 'when the crm_organization is invalid' do
let(:params) { { name: nil } }
it 'returns an error' do
diff --git a/spec/services/database/consistency_check_service_spec.rb b/spec/services/database/consistency_check_service_spec.rb
index 6288fedfb59..8b7560f80ad 100644
--- a/spec/services/database/consistency_check_service_spec.rb
+++ b/spec/services/database/consistency_check_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Database::ConsistencyCheckService, feature_category: :pods do
+RSpec.describe Database::ConsistencyCheckService, feature_category: :cell do
let(:batch_size) { 5 }
let(:max_batches) { 2 }
diff --git a/spec/services/database/consistency_fix_service_spec.rb b/spec/services/database/consistency_fix_service_spec.rb
index 9a0fac2191c..ea0916e8d2b 100644
--- a/spec/services/database/consistency_fix_service_spec.rb
+++ b/spec/services/database/consistency_fix_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Database::ConsistencyFixService do
+RSpec.describe Database::ConsistencyFixService, feature_category: :cell do
describe '#execute' do
context 'fixing namespaces inconsistencies' do
subject(:consistency_fix_service) do
diff --git a/spec/services/dependency_proxy/auth_token_service_spec.rb b/spec/services/dependency_proxy/auth_token_service_spec.rb
index c686f57c5cb..2612c5765a4 100644
--- a/spec/services/dependency_proxy/auth_token_service_spec.rb
+++ b/spec/services/dependency_proxy/auth_token_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DependencyProxy::AuthTokenService do
+RSpec.describe DependencyProxy::AuthTokenService, feature_category: :dependency_proxy do
include DependencyProxyHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/dependency_proxy/find_cached_manifest_service_spec.rb b/spec/services/dependency_proxy/find_cached_manifest_service_spec.rb
index 470c6eb9e03..13620b3dfc1 100644
--- a/spec/services/dependency_proxy/find_cached_manifest_service_spec.rb
+++ b/spec/services/dependency_proxy/find_cached_manifest_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DependencyProxy::FindCachedManifestService do
+RSpec.describe DependencyProxy::FindCachedManifestService, feature_category: :dependency_proxy do
include DependencyProxyHelpers
let_it_be(:image) { 'alpine' }
diff --git a/spec/services/dependency_proxy/group_settings/update_service_spec.rb b/spec/services/dependency_proxy/group_settings/update_service_spec.rb
index 4954d9ec267..38f837a828a 100644
--- a/spec/services/dependency_proxy/group_settings/update_service_spec.rb
+++ b/spec/services/dependency_proxy/group_settings/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::DependencyProxy::GroupSettings::UpdateService do
+RSpec.describe ::DependencyProxy::GroupSettings::UpdateService, feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group) }
diff --git a/spec/services/dependency_proxy/head_manifest_service_spec.rb b/spec/services/dependency_proxy/head_manifest_service_spec.rb
index 949a8eb3bee..a9646a185bc 100644
--- a/spec/services/dependency_proxy/head_manifest_service_spec.rb
+++ b/spec/services/dependency_proxy/head_manifest_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DependencyProxy::HeadManifestService do
+RSpec.describe DependencyProxy::HeadManifestService, feature_category: :dependency_proxy do
include DependencyProxyHelpers
let(:image) { 'alpine' }
diff --git a/spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb b/spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb
index 3a6ba2cca71..f58434222a5 100644
--- a/spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb
+++ b/spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::DependencyProxy::ImageTtlGroupPolicies::UpdateService do
+RSpec.describe ::DependencyProxy::ImageTtlGroupPolicies::UpdateService, feature_category: :dependency_proxy do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group) }
diff --git a/spec/services/dependency_proxy/request_token_service_spec.rb b/spec/services/dependency_proxy/request_token_service_spec.rb
index 8b3ba783b8d..0cc3695f0b0 100644
--- a/spec/services/dependency_proxy/request_token_service_spec.rb
+++ b/spec/services/dependency_proxy/request_token_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DependencyProxy::RequestTokenService do
+RSpec.describe DependencyProxy::RequestTokenService, feature_category: :dependency_proxy do
include DependencyProxyHelpers
let(:image) { 'alpine:3.9' }
diff --git a/spec/services/deploy_keys/create_service_spec.rb b/spec/services/deploy_keys/create_service_spec.rb
index 2e3318236f5..8bff80b2d11 100644
--- a/spec/services/deploy_keys/create_service_spec.rb
+++ b/spec/services/deploy_keys/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe DeployKeys::CreateService do
+RSpec.describe DeployKeys::CreateService, feature_category: :continuous_delivery do
let(:user) { create(:user) }
let(:params) { attributes_for(:deploy_key) }
diff --git a/spec/services/deployments/archive_in_project_service_spec.rb b/spec/services/deployments/archive_in_project_service_spec.rb
index a316c210d64..ed03ce06255 100644
--- a/spec/services/deployments/archive_in_project_service_spec.rb
+++ b/spec/services/deployments/archive_in_project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::ArchiveInProjectService do
+RSpec.describe Deployments::ArchiveInProjectService, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :repository) }
let(:service) { described_class.new(project, nil) }
diff --git a/spec/services/deployments/create_for_build_service_spec.rb b/spec/services/deployments/create_for_build_service_spec.rb
index 3748df87d99..c07fc07cfbf 100644
--- a/spec/services/deployments/create_for_build_service_spec.rb
+++ b/spec/services/deployments/create_for_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::CreateForBuildService do
+RSpec.describe Deployments::CreateForBuildService, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/deployments/create_service_spec.rb b/spec/services/deployments/create_service_spec.rb
index 0f2a6ce32e1..2a70d450575 100644
--- a/spec/services/deployments/create_service_spec.rb
+++ b/spec/services/deployments/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::CreateService do
+RSpec.describe Deployments::CreateService, feature_category: :continuous_delivery do
let(:user) { create(:user) }
describe '#execute' do
diff --git a/spec/services/deployments/link_merge_requests_service_spec.rb b/spec/services/deployments/link_merge_requests_service_spec.rb
index a653cd2b48b..a468af90ffb 100644
--- a/spec/services/deployments/link_merge_requests_service_spec.rb
+++ b/spec/services/deployments/link_merge_requests_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::LinkMergeRequestsService do
+RSpec.describe Deployments::LinkMergeRequestsService, feature_category: :continuous_delivery do
let(:project) { create(:project, :repository) }
# * ddd0f15 Merge branch 'po-fix-test-env-path' into 'master'
diff --git a/spec/services/deployments/older_deployments_drop_service_spec.rb b/spec/services/deployments/older_deployments_drop_service_spec.rb
index d9a512a5dd2..7e3074a1688 100644
--- a/spec/services/deployments/older_deployments_drop_service_spec.rb
+++ b/spec/services/deployments/older_deployments_drop_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::OlderDeploymentsDropService do
+RSpec.describe Deployments::OlderDeploymentsDropService, feature_category: :continuous_delivery do
let(:environment) { create(:environment) }
let(:deployment) { create(:deployment, environment: environment) }
let(:service) { described_class.new(deployment) }
diff --git a/spec/services/deployments/update_environment_service_spec.rb b/spec/services/deployments/update_environment_service_spec.rb
index 31a3abda8c7..33c9c9ed592 100644
--- a/spec/services/deployments/update_environment_service_spec.rb
+++ b/spec/services/deployments/update_environment_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::UpdateEnvironmentService do
+RSpec.describe Deployments::UpdateEnvironmentService, feature_category: :continuous_delivery do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:options) { { name: environment_name } }
diff --git a/spec/services/deployments/update_service_spec.rb b/spec/services/deployments/update_service_spec.rb
index d3840189ba4..0814091765c 100644
--- a/spec/services/deployments/update_service_spec.rb
+++ b/spec/services/deployments/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Deployments::UpdateService do
+RSpec.describe Deployments::UpdateService, feature_category: :continuous_delivery do
let(:deploy) { create(:deployment) }
describe '#execute' do
diff --git a/spec/services/design_management/copy_design_collection/copy_service_spec.rb b/spec/services/design_management/copy_design_collection/copy_service_spec.rb
index 89a78c9bf5f..048327792e0 100644
--- a/spec/services/design_management/copy_design_collection/copy_service_spec.rb
+++ b/spec/services/design_management/copy_design_collection/copy_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DesignManagement::CopyDesignCollection::CopyService, :clean_gitlab_redis_shared_state do
+RSpec.describe DesignManagement::CopyDesignCollection::CopyService, :clean_gitlab_redis_shared_state, feature_category: :portfolio_management do
include DesignManagementTestHelpers
let_it_be(:user) { create(:user) }
@@ -117,6 +117,7 @@ RSpec.describe DesignManagement::CopyDesignCollection::CopyService, :clean_gitla
new_designs.zip(old_designs).each do |new_design, old_design|
expect(new_design).to have_attributes(
filename: old_design.filename,
+ description: old_design.description,
relative_position: old_design.relative_position,
issue: target_issue,
project: target_issue.project
diff --git a/spec/services/design_management/copy_design_collection/queue_service_spec.rb b/spec/services/design_management/copy_design_collection/queue_service_spec.rb
index 05a7b092ccf..e6809e65d8a 100644
--- a/spec/services/design_management/copy_design_collection/queue_service_spec.rb
+++ b/spec/services/design_management/copy_design_collection/queue_service_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DesignManagement::CopyDesignCollection::QueueService, :clean_gitlab_redis_shared_state do
+RSpec.describe DesignManagement::CopyDesignCollection::QueueService, :clean_gitlab_redis_shared_state,
+ feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/design_management/delete_designs_service_spec.rb b/spec/services/design_management/delete_designs_service_spec.rb
index 48e53a92758..22570a14443 100644
--- a/spec/services/design_management/delete_designs_service_spec.rb
+++ b/spec/services/design_management/delete_designs_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DesignManagement::DeleteDesignsService do
+RSpec.describe DesignManagement::DeleteDesignsService, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
@@ -99,7 +99,7 @@ RSpec.describe DesignManagement::DeleteDesignsService do
rescue StandardError
nil
end
- .not_to change { redis_hll.unique_events(event_names: event, start_date: 1.day.ago, end_date: 1.day.from_now) }
+ .not_to change { redis_hll.unique_events(event_names: event, start_date: Date.today, end_date: 1.week.from_now) }
begin
run_service
diff --git a/spec/services/design_management/design_user_notes_count_service_spec.rb b/spec/services/design_management/design_user_notes_count_service_spec.rb
index 37806d3461c..1dbd055038c 100644
--- a/spec/services/design_management/design_user_notes_count_service_spec.rb
+++ b/spec/services/design_management/design_user_notes_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe DesignManagement::DesignUserNotesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe DesignManagement::DesignUserNotesCountService, :use_clean_rails_memory_store_caching, feature_category: :design_management do
let_it_be(:design) { create(:design, :with_file) }
subject { described_class.new(design) }
diff --git a/spec/services/design_management/generate_image_versions_service_spec.rb b/spec/services/design_management/generate_image_versions_service_spec.rb
index 5409ec12016..08442f221fa 100644
--- a/spec/services/design_management/generate_image_versions_service_spec.rb
+++ b/spec/services/design_management/generate_image_versions_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe DesignManagement::GenerateImageVersionsService do
+RSpec.describe DesignManagement::GenerateImageVersionsService, feature_category: :design_management do
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:version) { create(:design, :with_lfs_file, issue: issue).versions.first }
diff --git a/spec/services/design_management/move_designs_service_spec.rb b/spec/services/design_management/move_designs_service_spec.rb
index 519378a8dd4..8276d8d186a 100644
--- a/spec/services/design_management/move_designs_service_spec.rb
+++ b/spec/services/design_management/move_designs_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DesignManagement::MoveDesignsService do
+RSpec.describe DesignManagement::MoveDesignsService, feature_category: :design_management do
include DesignManagementTestHelpers
let_it_be(:issue) { create(:issue) }
diff --git a/spec/services/design_management/save_designs_service_spec.rb b/spec/services/design_management/save_designs_service_spec.rb
index a87494d87f7..ea53fcc3b12 100644
--- a/spec/services/design_management/save_designs_service_spec.rb
+++ b/spec/services/design_management/save_designs_service_spec.rb
@@ -11,7 +11,10 @@ RSpec.describe DesignManagement::SaveDesignsService, feature_category: :design_m
let(:project) { issue.project }
let(:user) { developer }
let(:files) { [rails_sample] }
- let(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project) }
+ let(:design_repository) do
+ ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)
+ end
+
let(:rails_sample_name) { 'rails_sample.jpg' }
let(:rails_sample) { sample_image(rails_sample_name) }
let(:dk_png) { sample_image('dk.png') }
diff --git a/spec/services/discussions/capture_diff_note_position_service_spec.rb b/spec/services/discussions/capture_diff_note_position_service_spec.rb
index 11614ccfd55..313e828bf0a 100644
--- a/spec/services/discussions/capture_diff_note_position_service_spec.rb
+++ b/spec/services/discussions/capture_diff_note_position_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Discussions::CaptureDiffNotePositionService do
+RSpec.describe Discussions::CaptureDiffNotePositionService, feature_category: :code_review_workflow do
subject { described_class.new(note.noteable, paths) }
context 'image note on diff' do
diff --git a/spec/services/discussions/capture_diff_note_positions_service_spec.rb b/spec/services/discussions/capture_diff_note_positions_service_spec.rb
index 8ba54495d4c..96922535eb2 100644
--- a/spec/services/discussions/capture_diff_note_positions_service_spec.rb
+++ b/spec/services/discussions/capture_diff_note_positions_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Discussions::CaptureDiffNotePositionsService do
+RSpec.describe Discussions::CaptureDiffNotePositionsService, feature_category: :code_review_workflow do
context 'when merge request has a discussion' do
let(:source_branch) { 'compare-with-merge-head-source' }
let(:target_branch) { 'compare-with-merge-head-target' }
diff --git a/spec/services/discussions/update_diff_position_service_spec.rb b/spec/services/discussions/update_diff_position_service_spec.rb
index e7a3505bbd4..274430fdccb 100644
--- a/spec/services/discussions/update_diff_position_service_spec.rb
+++ b/spec/services/discussions/update_diff_position_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Discussions::UpdateDiffPositionService do
+RSpec.describe Discussions::UpdateDiffPositionService, feature_category: :code_review_workflow do
let(:project) { create(:project, :repository) }
let(:current_user) { project.first_owner }
let(:create_commit) { project.commit("913c66a37b4a45b9769037c55c2d238bd0942d2e") }
diff --git a/spec/services/draft_notes/create_service_spec.rb b/spec/services/draft_notes/create_service_spec.rb
index 528c8717525..93731a80dcc 100644
--- a/spec/services/draft_notes/create_service_spec.rb
+++ b/spec/services/draft_notes/create_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DraftNotes::CreateService do
+RSpec.describe DraftNotes::CreateService, feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.target_project }
let(:user) { merge_request.author }
diff --git a/spec/services/draft_notes/destroy_service_spec.rb b/spec/services/draft_notes/destroy_service_spec.rb
index 1f246a56eb3..f4cc9daa9e9 100644
--- a/spec/services/draft_notes/destroy_service_spec.rb
+++ b/spec/services/draft_notes/destroy_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DraftNotes::DestroyService do
+RSpec.describe DraftNotes::DestroyService, feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.target_project }
let(:user) { merge_request.author }
diff --git a/spec/services/draft_notes/publish_service_spec.rb b/spec/services/draft_notes/publish_service_spec.rb
index 9e811eaa25e..dab06637c1a 100644
--- a/spec/services/draft_notes/publish_service_spec.rb
+++ b/spec/services/draft_notes/publish_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe DraftNotes::PublishService do
+RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workflow do
include RepoHelpers
let(:merge_request) { create(:merge_request) }
diff --git a/spec/services/emails/confirm_service_spec.rb b/spec/services/emails/confirm_service_spec.rb
index e8d3c0d673b..43fca75a5ea 100644
--- a/spec/services/emails/confirm_service_spec.rb
+++ b/spec/services/emails/confirm_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Emails::ConfirmService do
+RSpec.describe Emails::ConfirmService, feature_category: :user_management do
let_it_be(:user) { create(:user) }
subject(:service) { described_class.new(user) }
diff --git a/spec/services/emails/create_service_spec.rb b/spec/services/emails/create_service_spec.rb
index b13197f21b8..3ef67036483 100644
--- a/spec/services/emails/create_service_spec.rb
+++ b/spec/services/emails/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Emails::CreateService do
+RSpec.describe Emails::CreateService, feature_category: :user_management do
let_it_be(:user) { create(:user) }
let(:opts) { { email: 'new@email.com', user: user } }
diff --git a/spec/services/emails/destroy_service_spec.rb b/spec/services/emails/destroy_service_spec.rb
index 7dcf367016e..9d5e2b45647 100644
--- a/spec/services/emails/destroy_service_spec.rb
+++ b/spec/services/emails/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Emails::DestroyService do
+RSpec.describe Emails::DestroyService, feature_category: :user_management do
let!(:user) { create(:user) }
let!(:email) { create(:email, user: user) }
diff --git a/spec/services/environments/auto_stop_service_spec.rb b/spec/services/environments/auto_stop_service_spec.rb
index d688690c376..57fb860a557 100644
--- a/spec/services/environments/auto_stop_service_spec.rb
+++ b/spec/services/environments/auto_stop_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Environments::AutoStopService, :clean_gitlab_redis_shared_state, :sidekiq_inline do
+RSpec.describe Environments::AutoStopService, :clean_gitlab_redis_shared_state, :sidekiq_inline,
+ feature_category: :continuous_delivery do
include CreateEnvironmentsHelpers
include ExclusiveLeaseHelpers
diff --git a/spec/services/environments/canary_ingress/update_service_spec.rb b/spec/services/environments/canary_ingress/update_service_spec.rb
index 531f7d68a9f..f7d446c13f9 100644
--- a/spec/services/environments/canary_ingress/update_service_spec.rb
+++ b/spec/services/environments/canary_ingress/update_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Environments::CanaryIngress::UpdateService, :clean_gitlab_redis_cache do
+RSpec.describe Environments::CanaryIngress::UpdateService, :clean_gitlab_redis_cache,
+ feature_category: :continuous_delivery do
include KubernetesHelpers
let_it_be(:project, refind: true) { create(:project) }
diff --git a/spec/services/environments/create_for_build_service_spec.rb b/spec/services/environments/create_for_build_service_spec.rb
index c7aadb20c01..223401a243d 100644
--- a/spec/services/environments/create_for_build_service_spec.rb
+++ b/spec/services/environments/create_for_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Environments::CreateForBuildService do
+RSpec.describe Environments::CreateForBuildService, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/services/environments/reset_auto_stop_service_spec.rb b/spec/services/environments/reset_auto_stop_service_spec.rb
index 4a0b091c12d..a3b8b2e0aa1 100644
--- a/spec/services/environments/reset_auto_stop_service_spec.rb
+++ b/spec/services/environments/reset_auto_stop_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Environments::ResetAutoStopService do
+RSpec.describe Environments::ResetAutoStopService, feature_category: :continuous_delivery do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
let_it_be(:reporter) { create(:user).tap { |user| project.add_reporter(user) } }
diff --git a/spec/services/environments/schedule_to_delete_review_apps_service_spec.rb b/spec/services/environments/schedule_to_delete_review_apps_service_spec.rb
index 401d6203b2c..3047f415815 100644
--- a/spec/services/environments/schedule_to_delete_review_apps_service_spec.rb
+++ b/spec/services/environments/schedule_to_delete_review_apps_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Environments::ScheduleToDeleteReviewAppsService do
+RSpec.describe Environments::ScheduleToDeleteReviewAppsService, feature_category: :continuous_delivery do
include ExclusiveLeaseHelpers
let_it_be(:maintainer) { create(:user) }
diff --git a/spec/services/environments/stop_service_spec.rb b/spec/services/environments/stop_service_spec.rb
index 5f983a2151a..6e3b36b5636 100644
--- a/spec/services/environments/stop_service_spec.rb
+++ b/spec/services/environments/stop_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Environments::StopService do
+RSpec.describe Environments::StopService, feature_category: :continuous_delivery do
include CreateEnvironmentsHelpers
let(:project) { create(:project, :private, :repository) }
diff --git a/spec/services/error_tracking/base_service_spec.rb b/spec/services/error_tracking/base_service_spec.rb
index de3523cb847..ed9efd9f95a 100644
--- a/spec/services/error_tracking/base_service_spec.rb
+++ b/spec/services/error_tracking/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::BaseService do
+RSpec.describe ErrorTracking::BaseService, feature_category: :error_tracking do
describe '#compose_response' do
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user, id: non_existing_record_id) }
diff --git a/spec/services/error_tracking/collect_error_service_spec.rb b/spec/services/error_tracking/collect_error_service_spec.rb
index 159c070c683..3ff753e8c65 100644
--- a/spec/services/error_tracking/collect_error_service_spec.rb
+++ b/spec/services/error_tracking/collect_error_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::CollectErrorService do
+RSpec.describe ErrorTracking::CollectErrorService, feature_category: :error_tracking do
let_it_be(:project) { create(:project) }
let(:parsed_event_file) { 'error_tracking/parsed_event.json' }
diff --git a/spec/services/error_tracking/issue_details_service_spec.rb b/spec/services/error_tracking/issue_details_service_spec.rb
index 29f8154a27c..7ac41ffead6 100644
--- a/spec/services/error_tracking/issue_details_service_spec.rb
+++ b/spec/services/error_tracking/issue_details_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::IssueDetailsService do
+RSpec.describe ErrorTracking::IssueDetailsService, feature_category: :error_tracking do
include_context 'sentry error tracking context'
subject { described_class.new(project, user, params) }
diff --git a/spec/services/error_tracking/issue_latest_event_service_spec.rb b/spec/services/error_tracking/issue_latest_event_service_spec.rb
index aa2430ddffb..bfde14c7ef1 100644
--- a/spec/services/error_tracking/issue_latest_event_service_spec.rb
+++ b/spec/services/error_tracking/issue_latest_event_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::IssueLatestEventService do
+RSpec.describe ErrorTracking::IssueLatestEventService, feature_category: :error_tracking do
include_context 'sentry error tracking context'
let(:params) { {} }
diff --git a/spec/services/error_tracking/issue_update_service_spec.rb b/spec/services/error_tracking/issue_update_service_spec.rb
index a06c3588264..4dae6cc2fa0 100644
--- a/spec/services/error_tracking/issue_update_service_spec.rb
+++ b/spec/services/error_tracking/issue_update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::IssueUpdateService do
+RSpec.describe ErrorTracking::IssueUpdateService, feature_category: :error_tracking do
include_context 'sentry error tracking context'
let(:arguments) { { issue_id: non_existing_record_id, status: 'resolved' } }
diff --git a/spec/services/error_tracking/list_issues_service_spec.rb b/spec/services/error_tracking/list_issues_service_spec.rb
index a7bd6c75df5..2c35c2b8acd 100644
--- a/spec/services/error_tracking/list_issues_service_spec.rb
+++ b/spec/services/error_tracking/list_issues_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ErrorTracking::ListIssuesService do
+RSpec.describe ErrorTracking::ListIssuesService, feature_category: :error_tracking do
include_context 'sentry error tracking context'
let(:params) { {} }
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index b969bd76053..6a4769d77d5 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -2,20 +2,31 @@
require 'spec_helper'
-RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state do
+RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state, feature_category: :service_ping do
include SnowplowHelpers
let(:service) { described_class.new }
+ let(:dates) { { start_date: Date.today.beginning_of_week, end_date: Date.today.next_week } }
let_it_be(:user, reload: true) { create :user }
let_it_be(:project) { create(:project) }
shared_examples 'it records the event in the event counter' do
specify do
- tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
+ tracking_params = { event_names: event_action, **dates }
expect { subject }
- .to change { Gitlab::UsageDataCounters::TrackUniqueEvents.count_unique_events(**tracking_params) }
+ .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**tracking_params) }
+ .by(1)
+ end
+ end
+
+ shared_examples 'it records a git write event' do
+ specify do
+ tracking_params = { event_names: 'git_write_action', **dates }
+
+ expect { subject }
+ .to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**tracking_params) }
.by(1)
end
end
@@ -65,11 +76,10 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
+ let(:event_action) { :merge_request_action }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'created' }
let(:label) { described_class::MR_EVENT_LABEL }
@@ -95,11 +105,10 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
+ let(:event_action) { :merge_request_action }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'closed' }
let(:label) { described_class::MR_EVENT_LABEL }
@@ -125,11 +134,10 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
+ let(:event_action) { :merge_request_action }
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.name }
let(:action) { 'merged' }
let(:label) { described_class::MR_EVENT_LABEL }
@@ -276,8 +284,10 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::WIKI_ACTION }
+ let(:event_action) { :wiki_action }
end
+
+ it_behaves_like "it records a git write event"
end
(Event.actions.keys - Event::WIKI_ACTIONS).each do |bad_action|
@@ -312,9 +322,11 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
it_behaves_like 'service for creating a push event', PushEventPayloadService
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::PUSH_ACTION }
+ let(:event_action) { :project_action }
end
+ it_behaves_like "it records a git write event"
+
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:category) { described_class.to_s }
let(:action) { :push }
@@ -338,9 +350,11 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
it_behaves_like 'service for creating a push event', BulkPushEventPayloadService
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::PUSH_ACTION }
+ let(:event_action) { :project_action }
end
+ it_behaves_like "it records a git write event"
+
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
let(:category) { described_class.to_s }
let(:action) { :push }
@@ -400,16 +414,17 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
+ let(:event_action) { :design_action }
end
+ it_behaves_like "it records a git write event"
+
describe 'Snowplow tracking' do
let(:project) { design.project }
let(:namespace) { project.namespace }
let(:category) { described_class.name }
- let(:property) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s }
+ let(:property) { :design_action.to_s }
let(:label) { ::EventCreateService::DEGIGN_EVENT_LABEL }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
context 'for create event' do
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
@@ -448,9 +463,11 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
end
it_behaves_like "it records the event in the event counter" do
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
+ let(:event_action) { :design_action }
end
+ it_behaves_like "it records a git write event"
+
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
subject(:design_service) { service.destroy_designs([design], author) }
@@ -459,9 +476,8 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:category) { described_class.name }
let(:action) { 'destroy' }
let(:user) { author }
- let(:property) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s }
+ let(:property) { :design_action.to_s }
let(:label) { ::EventCreateService::DEGIGN_EVENT_LABEL }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
end
end
end
@@ -471,7 +487,7 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
let(:note) { create(:note) }
let(:author) { create(:user) }
- let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION }
+ let(:event_action) { :merge_request_action }
it { expect(leave_note).to be_truthy }
@@ -485,7 +501,6 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
it_behaves_like "it records the event in the event counter"
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:note) { create(:diff_note_on_merge_request) }
let(:category) { described_class.name }
let(:action) { 'commented' }
@@ -502,10 +517,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
context 'when it is not a diff note' do
it 'does not change the unique action counter' do
- counter_class = Gitlab::UsageDataCounters::TrackUniqueEvents
- tracking_params = { event_action: event_action, date_from: Date.yesterday, date_to: Date.today }
+ tracking_params = { event_names: event_action, start_date: Date.yesterday, end_date: Date.today }
- expect { subject }.not_to change { counter_class.count_unique_events(**tracking_params) }
+ expect { subject }.not_to change { Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**tracking_params) }
end
end
end
diff --git a/spec/services/events/destroy_service_spec.rb b/spec/services/events/destroy_service_spec.rb
index 8b07852c040..e50fe247238 100644
--- a/spec/services/events/destroy_service_spec.rb
+++ b/spec/services/events/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Events::DestroyService do
+RSpec.describe Events::DestroyService, feature_category: :user_profile do
subject(:service) { described_class.new(project) }
let_it_be(:project, reload: true) { create(:project, :repository) }
diff --git a/spec/services/events/render_service_spec.rb b/spec/services/events/render_service_spec.rb
index 24a3b9abe14..2e8cd26781b 100644
--- a/spec/services/events/render_service_spec.rb
+++ b/spec/services/events/render_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Events::RenderService do
+RSpec.describe Events::RenderService, feature_category: :user_profile do
describe '#execute' do
let!(:note) { build(:note) }
let!(:event) { build(:event, target: note, project: note.project) }
diff --git a/spec/services/feature_flags/create_service_spec.rb b/spec/services/feature_flags/create_service_spec.rb
index 1a32faad948..18c48714ccd 100644
--- a/spec/services/feature_flags/create_service_spec.rb
+++ b/spec/services/feature_flags/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe FeatureFlags::CreateService do
+RSpec.describe FeatureFlags::CreateService, feature_category: :feature_flags do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:reporter) { create(:user) }
@@ -46,6 +46,8 @@ RSpec.describe FeatureFlags::CreateService do
end
context 'when feature flag is saved correctly' do
+ let(:audit_event_details) { AuditEvent.last.details }
+ let(:audit_event_message) { audit_event_details[:custom_message] }
let(:params) do
{
name: 'feature_flag',
@@ -88,9 +90,9 @@ RSpec.describe FeatureFlags::CreateService do
it 'creates audit event', :with_license do
expect { subject }.to change { AuditEvent.count }.by(1)
- expect(AuditEvent.last.details[:custom_message]).to start_with('Created feature flag feature_flag with description "description".')
- expect(AuditEvent.last.details[:custom_message]).to include('Created strategy "default" with scopes "*".')
- expect(AuditEvent.last.details[:custom_message]).to include('Created strategy "default" with scopes "production".')
+ expect(audit_event_message).to start_with('Created feature flag feature_flag with description "description".')
+ expect(audit_event_message).to include('Created strategy "default" with scopes "*".')
+ expect(audit_event_message).to include('Created strategy "default" with scopes "production".')
end
context 'when user is reporter' do
diff --git a/spec/services/feature_flags/destroy_service_spec.rb b/spec/services/feature_flags/destroy_service_spec.rb
index b2793dc0560..1ec0ee6e68c 100644
--- a/spec/services/feature_flags/destroy_service_spec.rb
+++ b/spec/services/feature_flags/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe FeatureFlags::DestroyService do
+RSpec.describe FeatureFlags::DestroyService, feature_category: :feature_flags do
include FeatureFlagHelpers
let_it_be(:project) { create(:project) }
@@ -20,7 +20,8 @@ RSpec.describe FeatureFlags::DestroyService do
describe '#execute' do
subject { described_class.new(project, user, params).execute(feature_flag) }
- let(:audit_event_message) { AuditEvent.last.details[:custom_message] }
+ let(:audit_event_details) { AuditEvent.last.details }
+ let(:audit_event_message) { audit_event_details[:custom_message] }
let(:params) { {} }
it 'returns status success' do
diff --git a/spec/services/feature_flags/hook_service_spec.rb b/spec/services/feature_flags/hook_service_spec.rb
index f3edaca52a9..2a3ce9085b8 100644
--- a/spec/services/feature_flags/hook_service_spec.rb
+++ b/spec/services/feature_flags/hook_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe FeatureFlags::HookService do
+RSpec.describe FeatureFlags::HookService, feature_category: :feature_flags do
describe '#execute_hooks' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
diff --git a/spec/services/feature_flags/update_service_spec.rb b/spec/services/feature_flags/update_service_spec.rb
index 1c5af71a50a..55c09f06f16 100644
--- a/spec/services/feature_flags/update_service_spec.rb
+++ b/spec/services/feature_flags/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe FeatureFlags::UpdateService, :with_license do
+RSpec.describe FeatureFlags::UpdateService, :with_license, feature_category: :feature_flags do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:reporter) { create(:user) }
@@ -19,9 +19,8 @@ RSpec.describe FeatureFlags::UpdateService, :with_license do
subject { described_class.new(project, user, params).execute(feature_flag) }
let(:params) { { name: 'new_name' } }
- let(:audit_event_message) do
- AuditEvent.last.details[:custom_message]
- end
+ let(:audit_event_details) { AuditEvent.last.details }
+ let(:audit_event_message) { audit_event_details[:custom_message] }
it 'returns success status' do
expect(subject[:status]).to eq(:success)
diff --git a/spec/services/files/create_service_spec.rb b/spec/services/files/create_service_spec.rb
index 3b3dbd1fcfe..26f57f43120 100644
--- a/spec/services/files/create_service_spec.rb
+++ b/spec/services/files/create_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Files::CreateService do
+RSpec.describe Files::CreateService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user, :commit_email) }
diff --git a/spec/services/files/delete_service_spec.rb b/spec/services/files/delete_service_spec.rb
index 3823d027812..dd99e5f9742 100644
--- a/spec/services/files/delete_service_spec.rb
+++ b/spec/services/files/delete_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Files::DeleteService do
+RSpec.describe Files::DeleteService, feature_category: :source_code_management do
subject { described_class.new(project, user, commit_params) }
let(:project) { create(:project, :repository) }
@@ -52,8 +52,8 @@ RSpec.describe Files::DeleteService do
end
describe "#execute" do
- context "when the file's last commit sha does not match the supplied last_commit_sha" do
- let(:last_commit_sha) { "foo" }
+ context "when the file's last commit is earlier than the latest commit for this branch" do
+ let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).parent_id }
it "returns a hash with the correct error message and a :error status" do
expect { subject.execute }
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
index 6a5c7d2749d..7149fa77d6a 100644
--- a/spec/services/files/multi_service_spec.rb
+++ b/spec/services/files/multi_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Files::MultiService do
+RSpec.describe Files::MultiService, feature_category: :source_code_management do
subject { described_class.new(project, user, commit_params) }
let(:project) { create(:project, :repository) }
@@ -19,6 +19,10 @@ RSpec.describe Files::MultiService do
Gitlab::Git::Commit.last_for_path(project.repository, branch_name, original_file_path).sha
end
+ let(:branch_commit_id) do
+ Gitlab::Git::Commit.find(project.repository, branch_name).sha
+ end
+
let(:default_action) do
{
action: action,
@@ -78,6 +82,16 @@ RSpec.describe Files::MultiService do
end
end
+ context 'when file not changed, but later commit id is used' do
+ let(:actions) { [default_action.merge(last_commit_id: branch_commit_id)] }
+
+ it 'accepts the commit' do
+ results = subject.execute
+
+ expect(results[:status]).to eq(:success)
+ end
+ end
+
context 'when the file have not been modified' do
it 'accepts the commit' do
results = subject.execute
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 6d7459e0b29..6a9f9d6b86f 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe Files::UpdateService do
+RSpec.describe Files::UpdateService, feature_category: :source_code_management do
subject { described_class.new(project, user, commit_params) }
let(:project) { create(:project, :repository) }
@@ -31,8 +31,8 @@ RSpec.describe Files::UpdateService do
end
describe "#execute" do
- context "when the file's last commit sha does not match the supplied last_commit_sha" do
- let(:last_commit_sha) { "foo" }
+ context "when the file's last commit sha is earlier than the latest change for that branch" do
+ let(:last_commit_sha) { Gitlab::Git::Commit.last_for_path(project.repository, project.default_branch, file_path).parent_id }
it "returns a hash with the correct error message and a :error status" do
expect { subject.execute }
diff --git a/spec/services/git/base_hooks_service_spec.rb b/spec/services/git/base_hooks_service_spec.rb
index 5afd7b30ab0..8a686a19c4c 100644
--- a/spec/services/git/base_hooks_service_spec.rb
+++ b/spec/services/git/base_hooks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::BaseHooksService do
+RSpec.describe Git::BaseHooksService, feature_category: :source_code_management do
include RepoHelpers
let_it_be(:user) { create(:user) }
@@ -325,4 +325,40 @@ RSpec.describe Git::BaseHooksService do
end
end
end
+
+ describe 'notifying KAS' do
+ let(:kas_enabled) { true }
+
+ before do
+ allow(Gitlab::Kas).to receive(:enabled?).and_return(kas_enabled)
+ end
+
+ it 'enqueues the notification worker' do
+ expect(Clusters::Agents::NotifyGitPushWorker).to receive(:perform_async).with(project.id).once
+
+ subject.execute
+ end
+
+ context 'when KAS is disabled' do
+ let(:kas_enabled) { false }
+
+ it do
+ expect(Clusters::Agents::NotifyGitPushWorker).not_to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+
+ context 'when :notify_kas_on_git_push feature flag is disabled' do
+ before do
+ stub_feature_flags(notify_kas_on_git_push: false)
+ end
+
+ it do
+ expect(Clusters::Agents::NotifyGitPushWorker).not_to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+ end
end
diff --git a/spec/services/git/branch_hooks_service_spec.rb b/spec/services/git/branch_hooks_service_spec.rb
index 973ead28462..e991b5bd842 100644
--- a/spec/services/git/branch_hooks_service_spec.rb
+++ b/spec/services/git/branch_hooks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::BranchHooksService, :clean_gitlab_redis_shared_state do
+RSpec.describe Git::BranchHooksService, :clean_gitlab_redis_shared_state, feature_category: :source_code_management do
include RepoHelpers
include ProjectForksHelper
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
index a9f5b07fef4..aa534777f3e 100644
--- a/spec/services/git/branch_push_service_spec.rb
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::BranchPushService, :use_clean_rails_redis_caching, services: true do
+RSpec.describe Git::BranchPushService, :use_clean_rails_redis_caching, services: true, feature_category: :source_code_management do
include RepoHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/git/process_ref_changes_service_spec.rb b/spec/services/git/process_ref_changes_service_spec.rb
index 8d2da4a899e..9ec13bc957b 100644
--- a/spec/services/git/process_ref_changes_service_spec.rb
+++ b/spec/services/git/process_ref_changes_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::ProcessRefChangesService do
+RSpec.describe Git::ProcessRefChangesService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
let(:params) { { changes: git_changes } }
diff --git a/spec/services/git/tag_hooks_service_spec.rb b/spec/services/git/tag_hooks_service_spec.rb
index 01a0d2e8600..73f6eff36ba 100644
--- a/spec/services/git/tag_hooks_service_spec.rb
+++ b/spec/services/git/tag_hooks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::TagHooksService, :service do
+RSpec.describe Git::TagHooksService, :service, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
diff --git a/spec/services/git/tag_push_service_spec.rb b/spec/services/git/tag_push_service_spec.rb
index 597254d46fa..0d40c331d11 100644
--- a/spec/services/git/tag_push_service_spec.rb
+++ b/spec/services/git/tag_push_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::TagPushService do
+RSpec.describe Git::TagPushService, feature_category: :source_code_management do
include RepoHelpers
let(:user) { create(:user) }
diff --git a/spec/services/git/wiki_push_service/change_spec.rb b/spec/services/git/wiki_push_service/change_spec.rb
index 3616bf62b20..719e67666ce 100644
--- a/spec/services/git/wiki_push_service/change_spec.rb
+++ b/spec/services/git/wiki_push_service/change_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::WikiPushService::Change do
+RSpec.describe Git::WikiPushService::Change, feature_category: :source_code_management do
subject { described_class.new(project_wiki, change, raw_change) }
let(:project_wiki) { double('ProjectWiki') }
@@ -60,11 +60,13 @@ RSpec.describe Git::WikiPushService::Change do
end
%i[added renamed modified].each do |op|
- let(:operation) { op }
- let(:slug) { new_path.chomp('.md') }
- let(:revision) { change[:newrev] }
+ context "the operation is #{op}" do
+ let(:operation) { op }
+ let(:slug) { new_path.chomp('.md') }
+ let(:revision) { change[:newrev] }
- it { is_expected.to have_attributes(page: wiki_page) }
+ it { is_expected.to have_attributes(page: wiki_page) }
+ end
end
end
end
diff --git a/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb b/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb
index cd0dd75e576..4f2e0bea623 100644
--- a/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb
+++ b/spec/services/google_cloud/create_cloudsql_instance_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::CreateCloudsqlInstanceService do
+RSpec.describe GoogleCloud::CreateCloudsqlInstanceService, feature_category: :deployment_management do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:gcp_project_id) { 'gcp_project_120' }
diff --git a/spec/services/google_cloud/create_service_accounts_service_spec.rb b/spec/services/google_cloud/create_service_accounts_service_spec.rb
index 3f500e7c235..3b57f2a9e5f 100644
--- a/spec/services/google_cloud/create_service_accounts_service_spec.rb
+++ b/spec/services/google_cloud/create_service_accounts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::CreateServiceAccountsService do
+RSpec.describe GoogleCloud::CreateServiceAccountsService, feature_category: :deployment_management do
describe '#execute' do
before do
mock_google_oauth2_creds = Struct.new(:app_id, :app_secret)
diff --git a/spec/services/google_cloud/enable_cloud_run_service_spec.rb b/spec/services/google_cloud/enable_cloud_run_service_spec.rb
index 6d2b1f5cfd5..3de9e7fcd5c 100644
--- a/spec/services/google_cloud/enable_cloud_run_service_spec.rb
+++ b/spec/services/google_cloud/enable_cloud_run_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::EnableCloudRunService do
+RSpec.describe GoogleCloud::EnableCloudRunService, feature_category: :deployment_management do
describe 'when a project does not have any gcp projects' do
let_it_be(:project) { create(:project) }
diff --git a/spec/services/google_cloud/enable_cloudsql_service_spec.rb b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
index aa6d2402d7c..b14b827e8b8 100644
--- a/spec/services/google_cloud/enable_cloudsql_service_spec.rb
+++ b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::EnableCloudsqlService do
+RSpec.describe GoogleCloud::EnableCloudsqlService, feature_category: :deployment_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:params) do
diff --git a/spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb b/spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb
index b2cd5632be0..a748fed7134 100644
--- a/spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb
+++ b/spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::GcpRegionAddOrReplaceService do
+RSpec.describe GoogleCloud::GcpRegionAddOrReplaceService, feature_category: :deployment_management do
it 'adds and replaces GCP region vars' do
project = create(:project, :public)
service = described_class.new(project)
diff --git a/spec/services/google_cloud/generate_pipeline_service_spec.rb b/spec/services/google_cloud/generate_pipeline_service_spec.rb
index a78d8ff6661..c18514884ca 100644
--- a/spec/services/google_cloud/generate_pipeline_service_spec.rb
+++ b/spec/services/google_cloud/generate_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::GeneratePipelineService do
+RSpec.describe GoogleCloud::GeneratePipelineService, feature_category: :deployment_management do
describe 'for cloud-run' do
describe 'when there is no existing pipeline' do
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb b/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb
index 4587a5077c0..ed41d0fd487 100644
--- a/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb
+++ b/spec/services/google_cloud/get_cloudsql_instances_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::GetCloudsqlInstancesService do
+RSpec.describe GoogleCloud::GetCloudsqlInstancesService, feature_category: :deployment_management do
let(:service) { described_class.new(project) }
let(:project) { create(:project) }
diff --git a/spec/services/google_cloud/service_accounts_service_spec.rb b/spec/services/google_cloud/service_accounts_service_spec.rb
index 10e387126a3..c900bf7d300 100644
--- a/spec/services/google_cloud/service_accounts_service_spec.rb
+++ b/spec/services/google_cloud/service_accounts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::ServiceAccountsService do
+RSpec.describe GoogleCloud::ServiceAccountsService, feature_category: :deployment_management do
let(:service) { described_class.new(project) }
describe 'find_for_project' do
diff --git a/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb b/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
index 0a0f05ab4be..5095277f61a 100644
--- a/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
+++ b/spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GoogleCloud::SetupCloudsqlInstanceService do
+RSpec.describe GoogleCloud::SetupCloudsqlInstanceService, feature_category: :deployment_management do
let(:random_user) { create(:user) }
let(:project) { create(:project) }
let(:list_databases_empty) { Google::Apis::SqladminV1beta4::ListDatabasesResponse.new(items: []) }
diff --git a/spec/services/gpg_keys/create_service_spec.rb b/spec/services/gpg_keys/create_service_spec.rb
index 9ac56355b4b..d603ce951ec 100644
--- a/spec/services/gpg_keys/create_service_spec.rb
+++ b/spec/services/gpg_keys/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GpgKeys::CreateService do
+RSpec.describe GpgKeys::CreateService, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:params) { attributes_for(:gpg_key) }
diff --git a/spec/services/grafana/proxy_service_spec.rb b/spec/services/grafana/proxy_service_spec.rb
index 99120de3593..7029bab379a 100644
--- a/spec/services/grafana/proxy_service_spec.rb
+++ b/spec/services/grafana/proxy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Grafana::ProxyService do
+RSpec.describe Grafana::ProxyService, feature_category: :metrics do
include ReactiveCachingHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/services/gravatar_service_spec.rb b/spec/services/gravatar_service_spec.rb
index a6418b02f78..6ccb362cc5c 100644
--- a/spec/services/gravatar_service_spec.rb
+++ b/spec/services/gravatar_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GravatarService do
+RSpec.describe GravatarService, feature_category: :user_profile do
describe '#execute' do
let(:url) { 'http://example.com/avatar?hash=%{hash}&size=%{size}&email=%{email}&username=%{username}' }
diff --git a/spec/services/groups/auto_devops_service_spec.rb b/spec/services/groups/auto_devops_service_spec.rb
index 486a99dd8df..0724e072dab 100644
--- a/spec/services/groups/auto_devops_service_spec.rb
+++ b/spec/services/groups/auto_devops_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Groups::AutoDevopsService, '#execute' do
+RSpec.describe Groups::AutoDevopsService, '#execute', feature_category: :auto_devops do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/groups/autocomplete_service_spec.rb b/spec/services/groups/autocomplete_service_spec.rb
index 00d0ad3b347..9f55322e72d 100644
--- a/spec/services/groups/autocomplete_service_spec.rb
+++ b/spec/services/groups/autocomplete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::AutocompleteService do
+RSpec.describe Groups::AutocompleteService, feature_category: :subgroups do
let_it_be(:group, refind: true) { create(:group, :nested, :private, avatar: fixture_file_upload('spec/fixtures/dk.png')) }
let_it_be(:sub_group) { create(:group, :private, parent: group) }
diff --git a/spec/services/groups/deploy_tokens/create_service_spec.rb b/spec/services/groups/deploy_tokens/create_service_spec.rb
index 0c28075f998..e408c2787d8 100644
--- a/spec/services/groups/deploy_tokens/create_service_spec.rb
+++ b/spec/services/groups/deploy_tokens/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::DeployTokens::CreateService do
+RSpec.describe Groups::DeployTokens::CreateService, feature_category: :deployment_management do
it_behaves_like 'a deploy token creation service' do
let(:entity) { create(:group) }
let(:deploy_token_class) { GroupDeployToken }
diff --git a/spec/services/groups/deploy_tokens/destroy_service_spec.rb b/spec/services/groups/deploy_tokens/destroy_service_spec.rb
index 28e60b12993..c4694758b2f 100644
--- a/spec/services/groups/deploy_tokens/destroy_service_spec.rb
+++ b/spec/services/groups/deploy_tokens/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::DeployTokens::DestroyService do
+RSpec.describe Groups::DeployTokens::DestroyService, feature_category: :deployment_management do
it_behaves_like 'a deploy token deletion service' do
let_it_be(:entity) { create(:group) }
let_it_be(:deploy_token_class) { GroupDeployToken }
diff --git a/spec/services/groups/deploy_tokens/revoke_service_spec.rb b/spec/services/groups/deploy_tokens/revoke_service_spec.rb
index fcf11bbb8e6..c302dd14e3b 100644
--- a/spec/services/groups/deploy_tokens/revoke_service_spec.rb
+++ b/spec/services/groups/deploy_tokens/revoke_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::DeployTokens::RevokeService do
+RSpec.describe Groups::DeployTokens::RevokeService, feature_category: :deployment_management do
let_it_be(:entity) { create(:group) }
let_it_be(:deploy_token) { create(:deploy_token, :group, groups: [entity]) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/groups/group_links/create_service_spec.rb b/spec/services/groups/group_links/create_service_spec.rb
index bfbaedbd06f..ced87421858 100644
--- a/spec/services/groups/group_links/create_service_spec.rb
+++ b/spec/services/groups/group_links/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::GroupLinks::CreateService, '#execute' do
+RSpec.describe Groups::GroupLinks::CreateService, '#execute', feature_category: :subgroups do
let_it_be(:shared_with_group_parent) { create(:group, :private) }
let_it_be(:shared_with_group) { create(:group, :private, parent: shared_with_group_parent) }
let_it_be(:shared_with_group_child) { create(:group, :private, parent: shared_with_group) }
diff --git a/spec/services/groups/group_links/destroy_service_spec.rb b/spec/services/groups/group_links/destroy_service_spec.rb
index a570c28cf8b..5821ec44192 100644
--- a/spec/services/groups/group_links/destroy_service_spec.rb
+++ b/spec/services/groups/group_links/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::GroupLinks::DestroyService, '#execute' do
+RSpec.describe Groups::GroupLinks::DestroyService, '#execute', feature_category: :subgroups do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:shared_group) { create(:group, :private) }
diff --git a/spec/services/groups/group_links/update_service_spec.rb b/spec/services/groups/group_links/update_service_spec.rb
index 31446c8e4bf..f17d2f50a02 100644
--- a/spec/services/groups/group_links/update_service_spec.rb
+++ b/spec/services/groups/group_links/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::GroupLinks::UpdateService, '#execute' do
+RSpec.describe Groups::GroupLinks::UpdateService, '#execute', feature_category: :subgroups do
let(:user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
@@ -18,7 +18,7 @@ RSpec.describe Groups::GroupLinks::UpdateService, '#execute' do
expires_at: expiry_date }
end
- subject { described_class.new(link).execute(group_link_params) }
+ subject { described_class.new(link, user).execute(group_link_params) }
before do
group.add_developer(group_member_user)
diff --git a/spec/services/groups/import_export/export_service_spec.rb b/spec/services/groups/import_export/export_service_spec.rb
index ec42a728409..c44c2e3b911 100644
--- a/spec/services/groups/import_export/export_service_spec.rb
+++ b/spec/services/groups/import_export/export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::ImportExport::ExportService do
+RSpec.describe Groups::ImportExport::ExportService, feature_category: :importers do
describe '#async_execute' do
let(:user) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/services/groups/import_export/import_service_spec.rb b/spec/services/groups/import_export/import_service_spec.rb
index 972b12d7ee5..75db6e26cbf 100644
--- a/spec/services/groups/import_export/import_service_spec.rb
+++ b/spec/services/groups/import_export/import_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::ImportExport::ImportService do
+RSpec.describe Groups::ImportExport::ImportService, feature_category: :importers do
describe '#async_execute' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/groups/merge_requests_count_service_spec.rb b/spec/services/groups/merge_requests_count_service_spec.rb
index 8bd350d6f0e..32c4c618eda 100644
--- a/spec/services/groups/merge_requests_count_service_spec.rb
+++ b/spec/services/groups/merge_requests_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::MergeRequestsCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Groups::MergeRequestsCountService, :use_clean_rails_memory_store_caching, feature_category: :subgroups do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
diff --git a/spec/services/groups/nested_create_service_spec.rb b/spec/services/groups/nested_create_service_spec.rb
index a43c1d8d9c3..476bc2aa23c 100644
--- a/spec/services/groups/nested_create_service_spec.rb
+++ b/spec/services/groups/nested_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::NestedCreateService do
+RSpec.describe Groups::NestedCreateService, feature_category: :subgroups do
let(:user) { create(:user) }
subject(:service) { described_class.new(user, params) }
diff --git a/spec/services/groups/open_issues_count_service_spec.rb b/spec/services/groups/open_issues_count_service_spec.rb
index 923caa6c150..725b913bf15 100644
--- a/spec/services/groups/open_issues_count_service_spec.rb
+++ b/spec/services/groups/open_issues_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::OpenIssuesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Groups::OpenIssuesCountService, :use_clean_rails_memory_store_caching, feature_category: :subgroups do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/groups/participants_service_spec.rb b/spec/services/groups/participants_service_spec.rb
index 750aead277f..37966a523c2 100644
--- a/spec/services/groups/participants_service_spec.rb
+++ b/spec/services/groups/participants_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::ParticipantsService do
+RSpec.describe Groups::ParticipantsService, feature_category: :subgroups do
describe '#group_members' do
let(:user) { create(:user) }
let(:parent_group) { create(:group) }
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index 10399bed655..d6eb060ea7e 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -35,10 +35,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
end
context 'handling packages' do
- let_it_be(:group) { create(:group, :public) }
- let_it_be(:new_group) { create(:group, :public) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:new_group) { create(:group) }
- let(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:project) { create(:project, namespace: group) }
before do
group.add_owner(user)
@@ -46,46 +46,63 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
end
context 'with an npm package' do
- before do
- create(:npm_package, project: project)
- end
+ let_it_be(:npm_package) { create(:npm_package, project: project, name: "@testscope/test") }
- shared_examples 'transfer not allowed' do
- it 'does not allow transfer when there is a root namespace change' do
+ shared_examples 'transfer allowed' do
+ it 'allows transfer' do
transfer_service.execute(new_group)
- expect(transfer_service.error).to eq('Transfer failed: Group contains projects with NPM packages.')
- expect(group.parent).not_to eq(new_group)
+ expect(transfer_service.error).to be nil
+ expect(group.parent).to eq(new_group)
end
end
- it_behaves_like 'transfer not allowed'
+ it_behaves_like 'transfer allowed'
context 'with a project within subgroup' do
let_it_be(:root_group) { create(:group) }
let_it_be(:group) { create(:group, parent: root_group) }
+ let_it_be(:project) { create(:project, namespace: group) }
before do
root_group.add_owner(user)
end
- it_behaves_like 'transfer not allowed'
+ it_behaves_like 'transfer allowed'
context 'without a root namespace change' do
- let(:new_group) { create(:group, parent: root_group) }
+ let_it_be(:new_group) { create(:group, parent: root_group) }
+
+ it_behaves_like 'transfer allowed'
+ end
+
+ context 'with namespaced packages present' do
+ let_it_be(:package) { create(:npm_package, project: project, name: "@#{project.root_namespace.path}/test") }
- it 'allows transfer' do
+ it 'does not allow transfer' do
transfer_service.execute(new_group)
- expect(transfer_service.error).to be nil
- expect(group.parent).to eq(new_group)
+ expect(transfer_service.error).to eq('Transfer failed: Group contains projects with NPM packages scoped to the current root level group.')
+ expect(group.parent).not_to eq(new_group)
+ end
+
+ context 'namespaced package is pending destruction' do
+ let!(:group) { create(:group) }
+
+ before do
+ package.pending_destruction!
+ end
+
+ it_behaves_like 'transfer allowed'
end
end
context 'when transferring a group into a root group' do
- let(:new_group) { nil }
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
+ let_it_be(:new_group) { nil }
- it_behaves_like 'transfer not allowed'
+ it_behaves_like 'transfer allowed'
end
end
end
@@ -458,7 +475,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
it 'updates projects path' do
new_parent_path = new_parent_group.path
group.projects.each do |project|
- expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.name}")
+ expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.path}")
end
end
@@ -525,7 +542,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
it 'updates projects path' do
new_parent_path = new_parent_group.path
group.projects.each do |project|
- expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.name}")
+ expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.path}")
end
end
@@ -576,7 +593,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
it 'updates projects path' do
new_parent_path = "#{new_parent_group.path}/#{group.path}"
subgroup1.projects.each do |project|
- project_full_path = "#{new_parent_path}/#{project.namespace.path}/#{project.name}"
+ project_full_path = "#{new_parent_path}/#{project.namespace.path}/#{project.path}"
expect(project.full_path).to eq(project_full_path)
end
end
@@ -890,7 +907,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :subg
let(:subsub_project) { create(:project, group: subsubgroup) }
let!(:contacts) { create_list(:contact, 4, group: root_group) }
- let!(:organizations) { create_list(:organization, 2, group: root_group) }
+ let!(:organizations) { create_list(:crm_organization, 2, group: root_group) }
before do
create(:issue_customer_relations_contact, contact: contacts[0], issue: create(:issue, project: root_project))
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index c758d3d5477..6baa8e5d6b6 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::UpdateService do
+RSpec.describe Groups::UpdateService, feature_category: :subgroups do
let!(:user) { create(:user) }
let!(:private_group) { create(:group, :private) }
let!(:internal_group) { create(:group, :internal) }
diff --git a/spec/services/groups/update_shared_runners_service_spec.rb b/spec/services/groups/update_shared_runners_service_spec.rb
index a29f73a71c2..48c81f109aa 100644
--- a/spec/services/groups/update_shared_runners_service_spec.rb
+++ b/spec/services/groups/update_shared_runners_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::UpdateSharedRunnersService do
+RSpec.describe Groups::UpdateSharedRunnersService, feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:params) { {} }
diff --git a/spec/services/groups/update_statistics_service_spec.rb b/spec/services/groups/update_statistics_service_spec.rb
index 84b18b670a7..13a88839de0 100644
--- a/spec/services/groups/update_statistics_service_spec.rb
+++ b/spec/services/groups/update_statistics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::UpdateStatisticsService do
+RSpec.describe Groups::UpdateStatisticsService, feature_category: :subgroups do
let_it_be(:group, reload: true) { create(:group) }
let(:statistics) { %w(wiki_size) }
diff --git a/spec/services/ide/base_config_service_spec.rb b/spec/services/ide/base_config_service_spec.rb
index ee57f2c18ec..ac57a13d7fc 100644
--- a/spec/services/ide/base_config_service_spec.rb
+++ b/spec/services/ide/base_config_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ide::BaseConfigService do
+RSpec.describe Ide::BaseConfigService, feature_category: :web_ide do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ide/schemas_config_service_spec.rb b/spec/services/ide/schemas_config_service_spec.rb
index f277b8e9954..b6f229edc78 100644
--- a/spec/services/ide/schemas_config_service_spec.rb
+++ b/spec/services/ide/schemas_config_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ide::SchemasConfigService do
+RSpec.describe Ide::SchemasConfigService, feature_category: :web_ide do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/ide/terminal_config_service_spec.rb b/spec/services/ide/terminal_config_service_spec.rb
index 73614f28b06..76c8d9f2e6f 100644
--- a/spec/services/ide/terminal_config_service_spec.rb
+++ b/spec/services/ide/terminal_config_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ide::TerminalConfigService do
+RSpec.describe Ide::TerminalConfigService, feature_category: :web_ide do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/import/bitbucket_server_service_spec.rb b/spec/services/import/bitbucket_server_service_spec.rb
index 555812ca9cf..ca554fb01c3 100644
--- a/spec/services/import/bitbucket_server_service_spec.rb
+++ b/spec/services/import/bitbucket_server_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::BitbucketServerService do
+RSpec.describe Import::BitbucketServerService, feature_category: :importers do
let_it_be(:user) { create(:user) }
let(:base_uri) { "https://test:7990" }
@@ -93,7 +93,7 @@ RSpec.describe Import::BitbucketServerService do
result = subject.execute(credentials)
expect(result).to include(
- message: "You don't have permissions to create this project",
+ message: "You don't have permissions to import this project",
status: :error,
http_status: :unauthorized
)
diff --git a/spec/services/import/fogbugz_service_spec.rb b/spec/services/import/fogbugz_service_spec.rb
index 027d0240a7a..e9c676dcd23 100644
--- a/spec/services/import/fogbugz_service_spec.rb
+++ b/spec/services/import/fogbugz_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::FogbugzService do
+RSpec.describe Import::FogbugzService, feature_category: :importers do
let_it_be(:user) { create(:user) }
let(:base_uri) { "https://test:7990" }
@@ -18,6 +18,7 @@ RSpec.describe Import::FogbugzService do
before do
allow(subject).to receive(:authorized?).and_return(true)
+ stub_application_setting(import_sources: ['fogbugz'])
end
context 'when no repo is found' do
@@ -61,7 +62,7 @@ RSpec.describe Import::FogbugzService do
result = subject.execute(credentials)
expect(result).to include(
- message: "You don't have permissions to create this project",
+ message: "You don't have permissions to import this project",
status: :error,
http_status: :unauthorized
)
diff --git a/spec/services/import/github/cancel_project_import_service_spec.rb b/spec/services/import/github/cancel_project_import_service_spec.rb
index 77b8771ee65..d8ea303fa50 100644
--- a/spec/services/import/github/cancel_project_import_service_spec.rb
+++ b/spec/services/import/github/cancel_project_import_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::Github::CancelProjectImportService do
+RSpec.describe Import::Github::CancelProjectImportService, feature_category: :importers do
subject(:import_cancel) { described_class.new(project, project.owner) }
let_it_be(:user) { create(:user) }
@@ -14,6 +14,18 @@ RSpec.describe Import::Github::CancelProjectImportService do
it 'update import state to be canceled' do
expect(import_cancel.execute).to eq({ status: :success, project: project })
end
+
+ it 'tracks canceled imports' do
+ metrics_double = instance_double('Gitlab::Import::Metrics')
+
+ expect(Gitlab::Import::Metrics)
+ .to receive(:new)
+ .with(:github_importer, project)
+ .and_return(metrics_double)
+ expect(metrics_double).to receive(:track_canceled_import)
+
+ import_cancel.execute
+ end
end
context 'when import is finished' do
diff --git a/spec/services/import/github/notes/create_service_spec.rb b/spec/services/import/github/notes/create_service_spec.rb
index 57699def848..37cb903b66e 100644
--- a/spec/services/import/github/notes/create_service_spec.rb
+++ b/spec/services/import/github/notes/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::Github::Notes::CreateService do
+RSpec.describe Import::Github::Notes::CreateService, feature_category: :importers do
it 'does not support quick actions' do
project = create(:project, :repository)
user = create(:user)
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index 293e247c140..fa8b2489599 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::GithubService do
+RSpec.describe Import::GithubService, feature_category: :importers do
let_it_be(:user) { create(:user) }
let_it_be(:token) { 'complex-token' }
let_it_be(:access_params) { { github_access_token: 'github-complex-token' } }
@@ -152,7 +152,8 @@ RSpec.describe Import::GithubService do
{
single_endpoint_issue_events_import: true,
single_endpoint_notes_import: 'false',
- attachments_import: false
+ attachments_import: false,
+ collaborators_import: true
}
end
@@ -291,7 +292,7 @@ RSpec.describe Import::GithubService do
{
status: :error,
http_status: :unprocessable_entity,
- message: 'This namespace has already been taken. Choose a different one.'
+ message: 'You are not allowed to import projects in this namespace.'
}
end
end
diff --git a/spec/services/import/gitlab_projects/create_project_service_spec.rb b/spec/services/import/gitlab_projects/create_project_service_spec.rb
index 59c384bad3c..a77e9bdfce1 100644
--- a/spec/services/import/gitlab_projects/create_project_service_spec.rb
+++ b/spec/services/import/gitlab_projects/create_project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failures do
+RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failures, feature_category: :importers do
let(:fake_file_acquisition_strategy) do
Class.new do
attr_reader :errors
@@ -35,6 +35,7 @@ RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failur
before do
stub_const('FakeStrategy', fake_file_acquisition_strategy)
+ stub_application_setting(import_sources: ['gitlab_project'])
end
describe 'validation' do
@@ -127,7 +128,7 @@ RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failur
expect(response.payload).to eq(other_errors: [])
end
- context 'when the project contains multilple errors' do
+ context 'when the project contains multiple errors' do
it 'fails to create a project' do
params.merge!(name: '_ an invalid name _', path: '_ an invalid path _')
@@ -137,10 +138,13 @@ RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failur
expect(response).to be_error
expect(response.http_status).to eq(:bad_request)
- expect(response.message)
- .to eq(%{Project namespace path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'})
+ expect(response.message).to eq(
+ 'Project namespace path must not start or end with a special character and must not contain consecutive ' \
+ 'special characters.'
+ )
expect(response.payload).to eq(
other_errors: [
+ %{Project namespace path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'},
%{Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'},
%{Path must not start or end with a special character and must not contain consecutive special characters.}
])
diff --git a/spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb b/spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb
index 3c788138157..3a94ed02dd5 100644
--- a/spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb
+++ b/spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Import::GitlabProjects::FileAcquisitionStrategies::FileUpload, :aggregate_failures do
+RSpec.describe ::Import::GitlabProjects::FileAcquisitionStrategies::FileUpload, :aggregate_failures, feature_category: :importers do
let(:file) { UploadedFile.new(File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz')) }
describe 'validation' do
diff --git a/spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb b/spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb
index d9042e95149..411e2ec5286 100644
--- a/spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb
+++ b/spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Import::GitlabProjects::FileAcquisitionStrategies::RemoteFileS3, :aggregate_failures do
+RSpec.describe ::Import::GitlabProjects::FileAcquisitionStrategies::RemoteFileS3, :aggregate_failures, feature_category: :importers do
let(:region_name) { 'region_name' }
let(:bucket_name) { 'bucket_name' }
let(:file_key) { 'file_key' }
diff --git a/spec/services/import/prepare_service_spec.rb b/spec/services/import/prepare_service_spec.rb
index 0097198f7a9..fcb90575d96 100644
--- a/spec/services/import/prepare_service_spec.rb
+++ b/spec/services/import/prepare_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::PrepareService do
+RSpec.describe Import::PrepareService, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/import/validate_remote_git_endpoint_service_spec.rb b/spec/services/import/validate_remote_git_endpoint_service_spec.rb
index 221ac2cd73a..1d2b3975832 100644
--- a/spec/services/import/validate_remote_git_endpoint_service_spec.rb
+++ b/spec/services/import/validate_remote_git_endpoint_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Import::ValidateRemoteGitEndpointService do
+RSpec.describe Import::ValidateRemoteGitEndpointService, feature_category: :importers do
include StubRequests
let_it_be(:base_url) { 'http://demo.host/path' }
@@ -35,6 +35,28 @@ RSpec.describe Import::ValidateRemoteGitEndpointService do
end
end
+ context 'when uri is using an invalid protocol' do
+ subject { described_class.new(url: 'ssh://demo.host/repo') }
+
+ it 'reports error when invalid URL is provided' do
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result.error?).to be(true)
+ end
+ end
+
+ context 'when uri is invalid' do
+ subject { described_class.new(url: 'http:example.com') }
+
+ it 'reports error when invalid URL is provided' do
+ result = subject.execute
+
+ expect(result).to be_a(ServiceResponse)
+ expect(result.error?).to be(true)
+ end
+ end
+
context 'when receiving HTTP response' do
subject { described_class.new(url: base_url) }
diff --git a/spec/services/import_csv/base_service_spec.rb b/spec/services/import_csv/base_service_spec.rb
index 0c0ed40ff4d..93fff0d546a 100644
--- a/spec/services/import_csv/base_service_spec.rb
+++ b/spec/services/import_csv/base_service_spec.rb
@@ -24,40 +24,65 @@ RSpec.describe ImportCsv::BaseService, feature_category: :importers do
it_behaves_like 'abstract method', :validate_headers_presence!, "any"
it_behaves_like 'abstract method', :create_object_class
- describe '#detect_col_sep' do
- context 'when header contains invalid separators' do
- it 'raises error' do
- header = 'Name&email'
+ context 'when given a class' do
+ let(:importer_klass) do
+ Class.new(described_class) do
+ def attributes_for(row)
+ { title: row[:title] }
+ end
- expect { subject.send(:detect_col_sep, header) }.to raise_error(CSV::MalformedCSVError)
- end
- end
+ def validate_headers_presence!(headers)
+ raise CSV::MalformedCSVError.new("Missing required headers", 1) unless headers.present?
+ end
- context 'when header is valid' do
- shared_examples 'header with valid separators' do
- let(:header) { "Name#{separator}email" }
+ def create_object_class
+ Class.new
+ end
- it 'returns separator value' do
- expect(subject.send(:detect_col_sep, header)).to eq(separator)
+ def email_results_to_user
+ # no-op
end
end
+ end
- context 'with ; as separator' do
- let(:separator) { ';' }
+ let(:service) do
+ uploader = FileUploader.new(project)
+ uploader.store!(file)
- it_behaves_like 'header with valid separators'
- end
+ importer_klass.new(user, project, uploader)
+ end
+
+ subject { service.execute }
+
+ it_behaves_like 'correctly handles invalid files'
+
+ describe '#detect_col_sep' do
+ using RSpec::Parameterized::TableSyntax
- context 'with \t as separator' do
- let(:separator) { "\t" }
+ let(:file) { double }
- it_behaves_like 'header with valid separators'
+ before do
+ allow(service).to receive_message_chain('csv_data.lines.first').and_return(header)
end
- context 'with , as separator' do
- let(:separator) { ',' }
+ where(:sep_character, :valid) do
+ '&' | false
+ '?' | false
+ ';' | true
+ ',' | true
+ "\t" | true
+ end
+
+ with_them do
+ let(:header) { "Name#{sep_character}email" }
- it_behaves_like 'header with valid separators'
+ it 'responds appropriately' do
+ if valid
+ expect(service.send(:detect_col_sep)).to eq sep_character
+ else
+ expect { service.send(:detect_col_sep) }.to raise_error(CSV::MalformedCSVError)
+ end
+ end
end
end
end
diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb
index 2bcdfa6dd8f..7b638b4948b 100644
--- a/spec/services/import_export_clean_up_service_spec.rb
+++ b/spec/services/import_export_clean_up_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ImportExportCleanUpService do
+RSpec.describe ImportExportCleanUpService, feature_category: :importers do
describe '#execute' do
let(:service) { described_class.new }
diff --git a/spec/services/incident_management/incidents/create_service_spec.rb b/spec/services/incident_management/incidents/create_service_spec.rb
index 7db762b9c5b..e6ded379434 100644
--- a/spec/services/incident_management/incidents/create_service_spec.rb
+++ b/spec/services/incident_management/incidents/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::Incidents::CreateService do
+RSpec.describe IncidentManagement::Incidents::CreateService, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { User.alert_bot }
diff --git a/spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb
index 4b0c8d9113c..9b1994af1bb 100644
--- a/spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb
+++ b/spec/services/incident_management/issuable_escalation_statuses/after_update_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::IssuableEscalationStatuses::AfterUpdateService do
+RSpec.describe IncidentManagement::IssuableEscalationStatuses::AfterUpdateService,
+ feature_category: :incident_management do
let_it_be(:current_user) { create(:user) }
let_it_be(:escalation_status, reload: true) { create(:incident_management_issuable_escalation_status, :triggered) }
let_it_be(:issue, reload: true) { escalation_status.issue }
diff --git a/spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb
index b5c5238d483..56a159f452c 100644
--- a/spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb
+++ b/spec/services/incident_management/issuable_escalation_statuses/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::IssuableEscalationStatuses::BuildService do
+RSpec.describe IncidentManagement::IssuableEscalationStatuses::BuildService, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:incident, reload: true) { create(:incident, project: project) }
diff --git a/spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb
index b6ae03a19fe..e6c63d63123 100644
--- a/spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb
+++ b/spec/services/incident_management/issuable_escalation_statuses/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::IssuableEscalationStatuses::CreateService do
+RSpec.describe IncidentManagement::IssuableEscalationStatuses::CreateService, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let(:incident) { create(:incident, project: project) }
diff --git a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
index e8208c410d5..3f3174d0112 100644
--- a/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
+++ b/spec/services/incident_management/issuable_escalation_statuses/prepare_update_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService, factory_default: :keep do
+RSpec.describe IncidentManagement::IssuableEscalationStatuses::PrepareUpdateService, factory_default: :keep,
+ feature_category: :incident_management do
let_it_be(:project) { create_default(:project) }
let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, :triggered) }
let_it_be(:user_with_permissions) { create(:user) }
diff --git a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
index 2fda789cf56..caa5ee495b7 100644
--- a/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/create_incident_issue_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService do
+RSpec.describe IncidentManagement::PagerDuty::CreateIncidentIssueService, feature_category: :incident_management do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:user) { User.alert_bot }
diff --git a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
index e2aba0b61af..06f423bc63c 100644
--- a/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
+++ b/spec/services/incident_management/pager_duty/process_webhook_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::PagerDuty::ProcessWebhookService do
+RSpec.describe IncidentManagement::PagerDuty::ProcessWebhookService, feature_category: :incident_management do
let_it_be(:project, reload: true) { create(:project) }
describe '#execute' do
diff --git a/spec/services/incident_management/timeline_event_tags/create_service_spec.rb b/spec/services/incident_management/timeline_event_tags/create_service_spec.rb
index c1b993ce3d9..b21a116d5f9 100644
--- a/spec/services/incident_management/timeline_event_tags/create_service_spec.rb
+++ b/spec/services/incident_management/timeline_event_tags/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::TimelineEventTags::CreateService do
+RSpec.describe IncidentManagement::TimelineEventTags::CreateService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be_with_reload(:project) { create(:project) }
diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb
index fa5f4c64a43..fff6241f083 100644
--- a/spec/services/incident_management/timeline_events/create_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::TimelineEvents::CreateService do
+RSpec.describe IncidentManagement::TimelineEvents::CreateService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -57,7 +57,6 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
@@ -286,7 +285,6 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_created
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
diff --git a/spec/services/incident_management/timeline_events/destroy_service_spec.rb b/spec/services/incident_management/timeline_events/destroy_service_spec.rb
index f90ff72a2bf..78f6659beec 100644
--- a/spec/services/incident_management/timeline_events/destroy_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IncidentManagement::TimelineEvents::DestroyService do
+RSpec.describe IncidentManagement::TimelineEvents::DestroyService, feature_category: :incident_management do
let_it_be(:user_with_permissions) { create(:user) }
let_it_be(:user_without_permissions) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -67,7 +67,6 @@ RSpec.describe IncidentManagement::TimelineEvents::DestroyService do
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_deleted
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:user) { current_user }
diff --git a/spec/services/incident_management/timeline_events/update_service_spec.rb b/spec/services/incident_management/timeline_events/update_service_spec.rb
index ebaa4dde7a2..c38126baa65 100644
--- a/spec/services/incident_management/timeline_events/update_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/update_service_spec.rb
@@ -50,7 +50,6 @@ RSpec.describe IncidentManagement::TimelineEvents::UpdateService, feature_catego
it_behaves_like 'an incident management tracked event', :incident_management_timeline_event_edited
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace.reload }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_timeline_event_edited' }
diff --git a/spec/services/integrations/propagate_service_spec.rb b/spec/services/integrations/propagate_service_spec.rb
index c971c4a0ad0..0267b1b0ed0 100644
--- a/spec/services/integrations/propagate_service_spec.rb
+++ b/spec/services/integrations/propagate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Integrations::PropagateService do
+RSpec.describe Integrations::PropagateService, feature_category: :integrations do
describe '.propagate' do
include JiraIntegrationHelpers
diff --git a/spec/services/integrations/slack_event_service_spec.rb b/spec/services/integrations/slack_event_service_spec.rb
new file mode 100644
index 00000000000..17433aee329
--- /dev/null
+++ b/spec/services/integrations/slack_event_service_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackEventService, feature_category: :integrations do
+ describe '#execute' do
+ subject(:execute) { described_class.new(params).execute }
+
+ let(:params) do
+ {
+ type: 'event_callback',
+ event: {
+ type: 'app_home_opened',
+ foo: 'bar'
+ }
+ }
+ end
+
+ it 'queues a worker and returns success response' do
+ expect(Integrations::SlackEventWorker).to receive(:perform_async)
+ .with(
+ {
+ slack_event: 'app_home_opened',
+ params: {
+ event: {
+ foo: 'bar'
+ }
+ }
+ }
+ )
+ expect(execute.payload).to eq({})
+ is_expected.to be_success
+ end
+
+ context 'when event a url verification request' do
+ let(:params) { { type: 'url_verification', foo: 'bar' } }
+
+ it 'executes the service instead of queueing a worker and returns success response' do
+ expect(Integrations::SlackEventWorker).not_to receive(:perform_async)
+ expect_next_instance_of(Integrations::SlackEvents::UrlVerificationService, { foo: 'bar' }) do |service|
+ expect(service).to receive(:execute).and_return({ baz: 'qux' })
+ end
+ expect(execute.payload).to eq({ baz: 'qux' })
+ is_expected.to be_success
+ end
+ end
+
+ context 'when event is unknown' do
+ let(:params) { super().merge(event: { type: 'foo' }) }
+
+ it 'raises an error' do
+ expect { execute }.to raise_error(described_class::UnknownEventError)
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_events/app_home_opened_service_spec.rb b/spec/services/integrations/slack_events/app_home_opened_service_spec.rb
new file mode 100644
index 00000000000..0eb4c019e0a
--- /dev/null
+++ b/spec/services/integrations/slack_events/app_home_opened_service_spec.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackEvents::AppHomeOpenedService, feature_category: :integrations do
+ describe '#execute' do
+ let_it_be(:slack_installation) { create(:slack_integration) }
+
+ let(:slack_workspace_id) { slack_installation.team_id }
+ let(:slack_user_id) { 'U0123ABCDEF' }
+ let(:api_url) { "#{Slack::API::BASE_URL}/views.publish" }
+ let(:api_response) { { ok: true } }
+ let(:params) do
+ {
+ team_id: slack_workspace_id,
+ event: { user: slack_user_id },
+ event_id: 'Ev03SA75UJKB'
+ }
+ end
+
+ subject(:execute) { described_class.new(params).execute }
+
+ before do
+ stub_request(:post, api_url)
+ .to_return(
+ status: 200,
+ body: api_response.to_json,
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ shared_examples 'there is no bot token' do
+ it 'does not call the Slack API, logs info, and returns a success response' do
+ expect(Gitlab::IntegrationsLogger).to receive(:info).with(
+ {
+ slack_user_id: slack_user_id,
+ slack_workspace_id: slack_workspace_id,
+ message: 'SlackInstallation record has no bot token'
+ }
+ )
+
+ is_expected.to be_success
+ end
+ end
+
+ it 'calls the Slack API correctly and returns a success response' do
+ mock_view = { type: 'home', blocks: [] }
+
+ expect_next_instance_of(Slack::BlockKit::AppHomeOpened) do |ui|
+ expect(ui).to receive(:build).and_return(mock_view)
+ end
+
+ is_expected.to be_success
+
+ expect(WebMock).to have_requested(:post, api_url).with(
+ body: {
+ user_id: slack_user_id,
+ view: mock_view
+ },
+ headers: {
+ 'Authorization' => "Bearer #{slack_installation.bot_access_token}",
+ 'Content-Type' => 'application/json; charset=utf-8'
+ })
+ end
+
+ context 'when the slack installation is a legacy record' do
+ let_it_be(:slack_installation) { create(:slack_integration, :legacy) }
+
+ it_behaves_like 'there is no bot token'
+ end
+
+ context 'when the slack installation cannot be found' do
+ let(:slack_workspace_id) { non_existing_record_id }
+
+ it_behaves_like 'there is no bot token'
+ end
+
+ context 'when the Slack API call raises an HTTP exception' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_raise(Errno::ECONNREFUSED, 'error message')
+ end
+
+ it 'tracks the exception and returns an error response' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ Errno::ECONNREFUSED.new('HTTP exception when calling Slack API'),
+ {
+ slack_user_id: slack_user_id,
+ slack_workspace_id: slack_workspace_id
+ }
+ )
+ is_expected.to be_error
+ end
+ end
+
+ context 'when the Slack API returns an error' do
+ let(:api_response) { { ok: false, foo: 'bar' } }
+
+ it 'tracks the exception and returns an error response' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ StandardError.new('Slack API returned an error'),
+ {
+ slack_user_id: slack_user_id,
+ slack_workspace_id: slack_workspace_id,
+ response: api_response.with_indifferent_access
+ }
+ )
+ is_expected.to be_error
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_events/url_verification_service_spec.rb b/spec/services/integrations/slack_events/url_verification_service_spec.rb
new file mode 100644
index 00000000000..0d668acafb9
--- /dev/null
+++ b/spec/services/integrations/slack_events/url_verification_service_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackEvents::UrlVerificationService, feature_category: :integrations do
+ describe '#execute' do
+ it 'returns the challenge' do
+ expect(described_class.new({ challenge: 'foo' }).execute).to eq({ challenge: 'foo' })
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interaction_service_spec.rb b/spec/services/integrations/slack_interaction_service_spec.rb
new file mode 100644
index 00000000000..599320c7986
--- /dev/null
+++ b/spec/services/integrations/slack_interaction_service_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractionService, feature_category: :integrations do
+ describe '#execute' do
+ subject(:execute) { described_class.new(params).execute }
+
+ let(:params) do
+ {
+ type: slack_interaction,
+ foo: 'bar'
+ }
+ end
+
+ context 'when view is closed' do
+ let(:slack_interaction) { 'view_closed' }
+
+ it 'executes the correct service' do
+ view_closed_service = described_class::INTERACTIONS['view_closed']
+
+ expect_next_instance_of(view_closed_service, { foo: 'bar' }) do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when view is submitted' do
+ let(:slack_interaction) { 'view_submission' }
+
+ it 'executes the submission service' do
+ view_submission_service = described_class::INTERACTIONS['view_submission']
+
+ expect_next_instance_of(view_submission_service, { foo: 'bar' }) do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when block action service is submitted' do
+ let(:slack_interaction) { 'block_actions' }
+
+ it 'executes the block actions service' do
+ block_action_service = described_class::INTERACTIONS['block_actions']
+
+ expect_next_instance_of(block_action_service, { foo: 'bar' }) do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when slack_interaction is not known' do
+ let(:slack_interaction) { 'foo' }
+
+ it 'raises an error and does not execute a service class' do
+ described_class::INTERACTIONS.each_value do |service_class|
+ expect(service_class).not_to receive(:new)
+ end
+
+ expect { execute }.to raise_error(described_class::UnknownInteractionError)
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interactions/block_action_service_spec.rb b/spec/services/integrations/slack_interactions/block_action_service_spec.rb
new file mode 100644
index 00000000000..9a188ddcfe4
--- /dev/null
+++ b/spec/services/integrations/slack_interactions/block_action_service_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractions::BlockActionService, feature_category: :integrations do
+ describe '#execute' do
+ let_it_be(:slack_installation) { create(:slack_integration) }
+
+ let(:params) do
+ {
+ view: {
+ team_id: slack_installation.team_id
+ },
+ actions: [{
+ action_id: action_id
+ }]
+ }
+ end
+
+ subject(:execute) { described_class.new(params).execute }
+
+ context 'when action_id is incident_management_project' do
+ let(:action_id) { 'incident_management_project' }
+
+ it 'executes the correct handler' do
+ project_handler = described_class::ALLOWED_UPDATES_HANDLERS['incident_management_project']
+
+ expect_next_instance_of(project_handler, params, params[:actions].first) do |handler|
+ expect(handler).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when action_id is not known' do
+ let(:action_id) { 'random' }
+
+ it 'does not execute the handlers' do
+ described_class::ALLOWED_UPDATES_HANDLERS.each_value do |handler_class|
+ expect(handler_class).not_to receive(:new)
+ end
+
+ execute
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interactions/incident_management/incident_modal_closed_service_spec.rb b/spec/services/integrations/slack_interactions/incident_management/incident_modal_closed_service_spec.rb
new file mode 100644
index 00000000000..64cddf9a66b
--- /dev/null
+++ b/spec/services/integrations/slack_interactions/incident_management/incident_modal_closed_service_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractions::IncidentManagement::IncidentModalClosedService,
+ feature_category: :integrations do
+ describe '#execute' do
+ let_it_be(:request_body) do
+ {
+ replace_original: 'true',
+ text: 'Incident creation cancelled.'
+ }
+ end
+
+ let(:params) do
+ {
+ view: {
+ private_metadata: 'https://api.slack.com/id/1234'
+ }
+ }
+ end
+
+ let(:service) { described_class.new(params) }
+
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_return({ ok: true })
+ end
+
+ context 'when executed' do
+ it 'makes the POST call and closes the modal' do
+ expect(Gitlab::HTTP).to receive(:post).with(
+ 'https://api.slack.com/id/1234',
+ body: Gitlab::Json.dump(request_body),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+
+ service.execute
+ end
+ end
+
+ context 'when the POST call raises an HTTP exception' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_raise(Errno::ECONNREFUSED, 'error message')
+ end
+
+ it 'tracks the exception and returns an error response' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ Errno::ECONNREFUSED.new('HTTP exception when calling Slack API'),
+ {
+ params: params
+ }
+ )
+
+ service.execute
+ end
+ end
+
+ context 'when response is not ok' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_return({ ok: false })
+ end
+
+ it 'returns error response and tracks the exception' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ StandardError.new('Something went wrong while closing the incident form.'),
+ {
+ response: { ok: false },
+ params: params
+ }
+ )
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interactions/incident_management/incident_modal_opened_service_spec.rb b/spec/services/integrations/slack_interactions/incident_management/incident_modal_opened_service_spec.rb
new file mode 100644
index 00000000000..2a1aa0aabec
--- /dev/null
+++ b/spec/services/integrations/slack_interactions/incident_management/incident_modal_opened_service_spec.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractions::IncidentManagement::IncidentModalOpenedService,
+ feature_category: :incident_management do
+ describe '#execute' do
+ let_it_be(:slack_installation) { create(:slack_integration) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user, developer_projects: [project]) }
+ let_it_be(:trigger_id) { '12345.98765.abcd2358fdea' }
+
+ let(:slack_workspace_id) { slack_installation.team_id }
+ let(:response_url) { 'https://api.slack.com/id/123' }
+ let(:api_url) { "#{Slack::API::BASE_URL}/views.open" }
+ let(:mock_modal) { { type: 'modal', blocks: [] } }
+ let(:params) do
+ {
+ team_id: slack_workspace_id,
+ response_url: response_url,
+ trigger_id: trigger_id
+ }
+ end
+
+ before do
+ response = {
+ id: '123',
+ state: {
+ values: {
+ project_and_severity_selector: {
+ incident_management_project: {
+ selected_option: {
+ value: project.id.to_s
+ }
+ }
+ }
+ }
+ }
+ }
+ stub_request(:post, api_url)
+ .to_return(
+ status: 200,
+ body: Gitlab::Json.dump({ ok: true, view: response }),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ subject { described_class.new(slack_installation, user, params) }
+
+ context 'when triggered' do
+ it 'opens the modal' do
+ expect_next_instance_of(Slack::BlockKit::IncidentManagement::IncidentModalOpened) do |ui|
+ expect(ui).to receive(:build).and_return(mock_modal)
+ end
+
+ expect(Rails.cache).to receive(:write).with(
+ 'slack:incident_modal_opened:123', project.id.to_s, { expires_in: 5.minutes })
+
+ response = subject.execute
+
+ expect(WebMock).to have_requested(:post, api_url).with(
+ body: {
+ trigger_id: trigger_id,
+ view: mock_modal
+ },
+ headers: {
+ 'Authorization' => "Bearer #{slack_installation.bot_access_token}",
+ 'Content-Type' => 'application/json; charset=utf-8'
+ })
+
+ expect(response.message).to eq('Please complete the incident creation form.')
+ end
+ end
+
+ context 'when there are no projects with slack integration' do
+ let(:params) do
+ {
+ team_id: 'some_random_id',
+ response_url: response_url,
+ trigger_id: trigger_id
+ }
+ end
+
+ let(:user) { create(:user) }
+
+ it 'does not open the modal' do
+ response = subject.execute
+
+ expect(Rails.cache).not_to receive(:write)
+ expect(response.message).to be('You do not have access to any projects for creating incidents.')
+ end
+ end
+
+ context 'when Slack API call raises an HTTP exception' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_raise(Errno::ECONNREFUSED, 'error message')
+ end
+
+ it 'tracks the exception and returns an error response' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ Errno::ECONNREFUSED.new('HTTP exception when calling Slack API'),
+ {
+ slack_workspace_id: slack_workspace_id
+ }
+ )
+
+ expect(Rails.cache).not_to receive(:write)
+ expect(subject.execute).to be_error
+ end
+ end
+
+ context 'when api returns an error' do
+ before do
+ stub_request(:post, api_url)
+ .to_return(
+ status: 404,
+ body: Gitlab::Json.dump({ ok: false }),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ it 'returns error when called' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ StandardError.new('Something went wrong while opening the incident form.'),
+ {
+ response: { "ok" => false },
+ slack_workspace_id: slack_workspace_id,
+ slack_user_id: slack_installation.user_id
+ }
+ )
+
+ expect(Rails.cache).not_to receive(:write)
+ response = subject.execute
+
+ expect(response.message).to eq('Something went wrong while opening the incident form.')
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interactions/incident_management/incident_modal_submit_service_spec.rb b/spec/services/integrations/slack_interactions/incident_management/incident_modal_submit_service_spec.rb
new file mode 100644
index 00000000000..adaeadaa997
--- /dev/null
+++ b/spec/services/integrations/slack_interactions/incident_management/incident_modal_submit_service_spec.rb
@@ -0,0 +1,296 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractions::IncidentManagement::IncidentModalSubmitService,
+ feature_category: :incident_management do
+ include Gitlab::Routing
+
+ describe '#execute' do
+ let_it_be(:slack_installation) { create(:slack_integration) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:api_url) { 'https://api.slack.com/id/1234' }
+
+ let_it_be(:chat_name) do
+ create(:chat_name,
+ user: user,
+ team_id: slack_installation.team_id,
+ chat_id: slack_installation.user_id
+ )
+ end
+
+ # Setting below params as they are optional, have added values wherever required in specs
+ let(:zoom_link) { '' }
+ let(:severity) { {} }
+ let(:status) { '' }
+ let(:assignee_id) { nil }
+ let(:selected_label_ids) { [] }
+ let(:label_ids) { { selected_options: selected_label_ids } }
+ let(:confidential_selected_options) { [] }
+ let(:confidential) { { selected_options: confidential_selected_options } }
+ let(:title) { 'Incident title' }
+
+ let(:zoom) do
+ {
+ link: {
+ value: zoom_link
+ }
+ }
+ end
+
+ let(:params) do
+ {
+ team: {
+ id: slack_installation.team_id
+ },
+ user: {
+ id: slack_installation.user_id
+ },
+ view: {
+ private_metadata: api_url,
+ state: {
+ values: {
+ title_input: {
+ title: {
+ value: title
+ }
+ },
+ incident_description: {
+ description: {
+ value: 'Incident description'
+ }
+ },
+ project_and_severity_selector: {
+ incident_management_project: {
+ selected_option: {
+ value: project.id.to_s
+ }
+ },
+ severity: severity
+ },
+ confidentiality: {
+ confidential: confidential
+ },
+ zoom: zoom,
+ status_and_assignee_selector: {
+ status: {
+ selected_option: {
+ value: status
+ }
+ },
+ assignee: {
+ selected_option: {
+ value: assignee_id
+ }
+ }
+ },
+ label_selector: {
+ labels: label_ids
+ }
+ }
+ }
+ }
+ }
+ end
+
+ subject(:execute_service) { described_class.new(params).execute }
+
+ shared_examples 'error in creation' do |error_message|
+ it 'returns error and raises exception' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ described_class::IssueCreateError.new(error_message),
+ {
+ slack_workspace_id: slack_installation.team_id,
+ slack_user_id: slack_installation.user_id
+ }
+ )
+
+ expect(Gitlab::HTTP).to receive(:post)
+ .with(
+ api_url,
+ body: Gitlab::Json.dump(
+ {
+ replace_original: 'true',
+ text: 'There was a problem creating the incident. Please try again.'
+ }
+ ),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+
+ response = execute_service
+
+ expect(response).to be_error
+ expect(response.message).to eq(error_message)
+ end
+ end
+
+ context 'when user has permissions to create incidents' do
+ let(:api_response) { '{"ok":true}' }
+
+ before do
+ project.add_developer(user)
+ stub_request(:post, api_url)
+ .to_return(body: api_response, headers: { 'Content-Type' => 'application/json' })
+ end
+
+ context 'with markup string in title' do
+ let(:title) { '<a href="url">incident title</a>' }
+ let(:incident) { create(:incident, title: title, project: project) }
+
+ before do
+ allow_next_instance_of(Issues::CreateService) do |service|
+ allow(service).to receive(:execute).and_return(
+ ServiceResponse.success(payload: { issue: incident, error: [] })
+ )
+ end
+ end
+
+ it 'strips the markup and saves sends the title' do
+ expect(Gitlab::HTTP).to receive(:post)
+ .with(
+ api_url,
+ body: Gitlab::Json.dump(
+ {
+ replace_original: 'true',
+ text: "New incident has been created: " \
+ "<#{issue_url(incident)}|#{incident.to_reference} - a href=\"url\"incident title/a>. "
+ }
+ ),
+ headers: { 'Content-Type' => 'application/json' }
+ ).and_return(api_response)
+
+ execute_service
+ end
+ end
+
+ context 'with non-optional params' do
+ it 'creates incident' do
+ response = execute_service
+ incident = response[:incident]
+
+ expect(response).to be_success
+ expect(incident).not_to be_nil
+ expect(incident.description).to eq('Incident description')
+ expect(incident.author).to eq(user)
+ expect(incident.severity).to eq('unknown')
+ expect(incident.confidential).to be_falsey
+ expect(incident.escalation_status).to be_triggered
+ end
+
+ it 'sends incident link to slack' do
+ execute_service
+
+ expect(WebMock).to have_requested(:post, api_url)
+ end
+ end
+
+ context 'with zoom_link' do
+ let(:zoom_link) { 'https://gitlab.zoom.us/j/1234' }
+
+ it 'sets zoom link as quick action' do
+ incident = execute_service[:incident]
+ zoom_meeting = ZoomMeeting.find_by_issue_id(incident.id)
+
+ expect(incident.description).to eq("Incident description")
+ expect(zoom_meeting.url).to eq(zoom_link)
+ end
+ end
+
+ context 'with confidential and severity' do
+ let(:confidential_selected_options) { ['confidential'] }
+ let(:severity) do
+ {
+ selected_option: {
+ value: 'high'
+ }
+ }
+ end
+
+ it 'sets confidential and severity' do
+ incident = execute_service[:incident]
+
+ expect(incident.confidential).to be_truthy
+ expect(incident.severity).to eq('high')
+ end
+ end
+
+ context 'with incident status' do
+ let(:status) { 'resolved' }
+
+ it 'sets the incident status' do
+ incident = execute_service[:incident]
+
+ expect(incident.escalation_status).to be_resolved
+ end
+ end
+
+ context 'with assignee id' do
+ let(:assignee_id) { user.id.to_s }
+
+ it 'assigns the incident to user' do
+ incident = execute_service[:incident]
+
+ expect(incident.assignees).to contain_exactly(user)
+ end
+
+ context 'when user is not a member of the project' do
+ let(:assignee_id) { create(:user).id.to_s }
+
+ it 'does not assign the user' do
+ incident = execute_service[:incident]
+
+ expect(incident.assignees).to be_empty
+ end
+ end
+ end
+
+ context 'with label ids' do
+ let_it_be(:project_label1) { create(:label, project: project, title: 'Label 1') }
+ let_it_be(:project_label2) { create(:label, project: project, title: 'Label 2') }
+
+ let(:selected_label_ids) do
+ [
+ { value: project_label1.id.to_s },
+ { value: project_label2.id.to_s }
+ ]
+ end
+
+ it 'assigns the label to the incident' do
+ incident = execute_service[:incident]
+
+ expect(incident.labels).to contain_exactly(project_label1, project_label2)
+ end
+ end
+
+ context 'when response is not ok' do
+ let(:api_response) { '{"ok":false}' }
+
+ it 'returns error response and tracks the exception' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ StandardError.new('Something went wrong when sending the incident link to Slack.'),
+ {
+ response: { 'ok' => false },
+ slack_workspace_id: slack_installation.team_id,
+ slack_user_id: slack_installation.user_id
+ }
+ )
+
+ execute_service
+ end
+ end
+
+ context 'when incident creation fails' do
+ let(:title) { '' }
+
+ it_behaves_like 'error in creation', "Title can't be blank"
+ end
+ end
+
+ context 'when user does not have permission to create incidents' do
+ it_behaves_like 'error in creation', 'Operation not allowed'
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_interactions/slack_block_actions/incident_management/project_update_handler_spec.rb b/spec/services/integrations/slack_interactions/slack_block_actions/incident_management/project_update_handler_spec.rb
new file mode 100644
index 00000000000..5edffc99977
--- /dev/null
+++ b/spec/services/integrations/slack_interactions/slack_block_actions/incident_management/project_update_handler_spec.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackInteractions::SlackBlockActions::IncidentManagement::ProjectUpdateHandler,
+ feature_category: :incident_management do
+ describe '#execute' do
+ let_it_be(:slack_installation) { create(:slack_integration) }
+ let_it_be(:old_project) { create(:project) }
+ let_it_be(:new_project) { create(:project) }
+ let_it_be(:user) { create(:user, developer_projects: [old_project, new_project]) }
+ let_it_be(:chat_name) { create(:chat_name, user: user) }
+ let_it_be(:api_url) { "#{Slack::API::BASE_URL}/views.update" }
+
+ let(:block) do
+ {
+ block_id: 'incident_description',
+ element: {
+ initial_value: ''
+ }
+ }
+ end
+
+ let(:view) do
+ {
+ id: 'V04EQH1SP27',
+ team_id: slack_installation.team_id,
+ blocks: [block]
+ }
+ end
+
+ let(:action) do
+ {
+ selected_option: {
+ value: new_project.id.to_s
+ }
+ }
+ end
+
+ let(:params) do
+ {
+ view: view,
+ user: {
+ id: slack_installation.user_id
+ }
+ }
+ end
+
+ before do
+ allow_next_instance_of(ChatNames::FindUserService) do |user_service|
+ allow(user_service).to receive(:execute).and_return(chat_name)
+ end
+
+ stub_request(:post, api_url)
+ .to_return(
+ status: 200,
+ body: Gitlab::Json.dump({ ok: true }),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ shared_examples 'does not make api call' do
+ it 'does not make the api call and returns nil' do
+ expect(Rails.cache).to receive(:read).and_return(project.id.to_s)
+ expect(Rails.cache).not_to receive(:write)
+
+ expect(execute).to be_nil
+ expect(WebMock).not_to have_requested(:post, api_url)
+ end
+ end
+
+ subject(:execute) { described_class.new(params, action).execute }
+
+ context 'when project is updated' do
+ it 'returns success response and updates cache' do
+ expect(Rails.cache).to receive(:read).and_return(old_project.id.to_s)
+ expect(Rails.cache).to receive(:write).with(
+ "slack:incident_modal_opened:#{view[:id]}",
+ new_project.id.to_s,
+ expires_in: 5.minutes
+ )
+
+ expect(execute.message).to eq('Modal updated')
+
+ updated_block = block.dup
+ updated_block[:block_id] = new_project.id.to_s
+ view[:blocks] = [updated_block]
+
+ expect(WebMock).to have_requested(:post, api_url).with(
+ body: {
+ view_id: view[:id],
+ view: view.except!(:team_id, :id)
+ },
+ headers: {
+ 'Authorization' => "Bearer #{slack_installation.bot_access_token}",
+ 'Content-Type' => 'application/json; charset=utf-8'
+ })
+ end
+ end
+
+ context 'when project is unchanged' do
+ it_behaves_like 'does not make api call' do
+ let(:project) { new_project }
+ end
+ end
+
+ context 'when user does not have permission to read a project' do
+ it_behaves_like 'does not make api call' do
+ let(:project) { create(:project) }
+ end
+ end
+
+ context 'when api response is not ok' do
+ before do
+ stub_request(:post, api_url)
+ .to_return(
+ status: 404,
+ body: Gitlab::Json.dump({ ok: false }),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+
+ it 'returns error response' do
+ expect(Rails.cache).to receive(:read).and_return(old_project.id.to_s)
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ StandardError.new('Something went wrong while updating the modal.'),
+ {
+ response: { "ok" => false },
+ slack_workspace_id: slack_installation.team_id,
+ slack_user_id: slack_installation.user_id
+ }
+ )
+
+ expect(execute.message).to eq('Something went wrong while updating the modal.')
+ end
+ end
+
+ context 'when Slack API call raises an HTTP exception' do
+ before do
+ allow(Gitlab::HTTP).to receive(:post).and_raise(Errno::ECONNREFUSED, 'error message')
+ end
+
+ it 'tracks the exception and returns an error message' do
+ expect(Rails.cache).to receive(:read).and_return(old_project.id.to_s)
+ expect(::Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(
+ Errno::ECONNREFUSED.new('HTTP exception when calling Slack API'),
+ {
+ slack_workspace_id: slack_installation.team_id
+ }
+ )
+
+ expect(execute).to be_error
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_option_service_spec.rb b/spec/services/integrations/slack_option_service_spec.rb
new file mode 100644
index 00000000000..2e114b932d2
--- /dev/null
+++ b/spec/services/integrations/slack_option_service_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackOptionService, feature_category: :integrations do
+ describe '#execute' do
+ subject(:execute) { described_class.new(params).execute }
+
+ let_it_be(:slack_installation) { create(:slack_integration) }
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:chat_name) do
+ create(:chat_name,
+ user: user,
+ team_id: slack_installation.team_id,
+ chat_id: slack_installation.user_id
+ )
+ end
+
+ let(:params) do
+ {
+ action_id: action_id,
+ view: {
+ id: 'VHDFR54DSA'
+ },
+ value: 'Search value',
+ team: {
+ id: slack_installation.team_id
+ },
+ user: {
+ id: slack_installation.user_id
+ }
+ }
+ end
+
+ context 'when action_id is assignee' do
+ let(:action_id) { 'assignee' }
+
+ it 'executes the user search handler' do
+ user_search_handler = described_class::OPTIONS['assignee']
+
+ expect_next_instance_of(user_search_handler, chat_name, 'Search value', 'VHDFR54DSA') do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when action_id is labels' do
+ let(:action_id) { 'labels' }
+
+ it 'executes the label search handler' do
+ label_search_handler = described_class::OPTIONS['labels']
+
+ expect_next_instance_of(label_search_handler, chat_name, 'Search value', 'VHDFR54DSA') do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.success)
+ end
+
+ execute
+ end
+ end
+
+ context 'when action_id is unknown' do
+ let(:action_id) { 'foo' }
+
+ it 'raises an error and does not execute a service class' do
+ described_class::OPTIONS.each_value do |service_class|
+ expect(service_class).not_to receive(:new)
+ end
+
+ expect { execute }.to raise_error(described_class::UnknownOptionError)
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_options/label_search_handler_spec.rb b/spec/services/integrations/slack_options/label_search_handler_spec.rb
new file mode 100644
index 00000000000..3b006061f1d
--- /dev/null
+++ b/spec/services/integrations/slack_options/label_search_handler_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackOptions::LabelSearchHandler, feature_category: :integrations do
+ describe '#execute' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :private, namespace: group) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:chat_name) { create(:chat_name, user: current_user) }
+ let_it_be(:project_label1) { create(:label, project: project, title: 'Label 1') }
+ let_it_be(:project_label2) { create(:label, project: project, title: 'Label 2') }
+ let_it_be(:group_label1) { create(:group_label, group: group, title: 'LabelG 1') }
+ let_it_be(:group_label2) { create(:group_label, group: group, title: 'glb 2') }
+ let_it_be(:view_id) { 'VXHD54DR' }
+
+ let(:search_value) { 'Lab' }
+
+ subject(:execute) { described_class.new(chat_name, search_value, view_id).execute }
+
+ context 'when user has permission to read project and group labels' do
+ before do
+ allow(Rails.cache).to receive(:read).and_return(project.id)
+ project.add_developer(current_user)
+ end
+
+ it 'returns the labels matching the search term' do
+ labels = execute.payload[:options]
+ label_names = labels.map { |label| label.dig(:text, :text) }
+
+ expect(label_names).to contain_exactly(
+ project_label1.name,
+ project_label2.name,
+ group_label1.name
+ )
+ end
+ end
+
+ context 'when user does not have permissions to read project/group labels' do
+ it 'returns empty array' do
+ expect(LabelsFinder).not_to receive(:execute)
+
+ expect(execute.payload).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/slack_options/user_search_handler_spec.rb b/spec/services/integrations/slack_options/user_search_handler_spec.rb
new file mode 100644
index 00000000000..e827bf643d2
--- /dev/null
+++ b/spec/services/integrations/slack_options/user_search_handler_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::SlackOptions::UserSearchHandler, feature_category: :integrations do
+ describe '#execute' do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:chat_name) { create(:chat_name, user: current_user) }
+ let_it_be(:user1) { create(:user, name: 'Rajendra Kadam') }
+ let_it_be(:user2) { create(:user, name: 'Rajesh K') }
+ let_it_be(:user3) { create(:user) }
+ let_it_be(:view_id) { 'VXHD54DR' }
+
+ let(:search_value) { 'Raj' }
+
+ subject(:execute) { described_class.new(chat_name, search_value, view_id).execute }
+
+ context 'when user has permissions to read project members' do
+ before do
+ project.add_developer(current_user)
+ project.add_guest(user1)
+ project.add_reporter(user2)
+ project.add_maintainer(user3)
+ end
+
+ it 'returns the user matching the search term' do
+ expect(Rails.cache).to receive(:read).and_return(project.id)
+
+ members = execute.payload[:options]
+ user_names = members.map { |member| member.dig(:text, :text) }
+
+ expect(members.count).to eq(2)
+ expect(user_names).to contain_exactly(
+ "#{user1.name} - #{user1.username}",
+ "#{user2.name} - #{user2.username}"
+ )
+ end
+ end
+
+ context 'when user does not have permissions to read project members' do
+ it 'returns empty array' do
+ expect(Rails.cache).to receive(:read).and_return(project.id)
+ expect(MembersFinder).not_to receive(:execute)
+
+ members = execute.payload
+
+ expect(members).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/services/integrations/test/project_service_spec.rb b/spec/services/integrations/test/project_service_spec.rb
index 74833686283..4f8f932fb45 100644
--- a/spec/services/integrations/test/project_service_spec.rb
+++ b/spec/services/integrations/test/project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Integrations::Test::ProjectService do
+RSpec.describe Integrations::Test::ProjectService, feature_category: :integrations do
include AfterNextHelpers
describe '#execute' do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 7ba349ceeae..a76d575a1e0 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::BulkUpdateService do
+RSpec.describe Issuable::BulkUpdateService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository, namespace: user.namespace) }
diff --git a/spec/services/issuable/callbacks/milestone_spec.rb b/spec/services/issuable/callbacks/milestone_spec.rb
new file mode 100644
index 00000000000..085ed029a6c
--- /dev/null
+++ b/spec/services/issuable/callbacks/milestone_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Issuable::Callbacks::Milestone, feature_category: :team_planning do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :private, group: group) }
+ let_it_be(:project_milestone) { create(:milestone, project: project) }
+ let_it_be(:group_milestone) { create(:milestone, group: group) }
+ let_it_be(:reporter) do
+ create(:user).tap { |u| project.add_reporter(u) }
+ end
+
+ let(:issuable) { build(:issue, project: project) }
+ let(:current_user) { reporter }
+ let(:params) { { milestone_id: project_milestone.id } }
+ let(:callback) { described_class.new(issuable: issuable, current_user: current_user, params: params) }
+
+ describe '#after_initialize' do
+ it "sets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(nil).to(project_milestone)
+ end
+
+ context 'when assigning a group milestone' do
+ let(:params) { { milestone_id: group_milestone.id } }
+
+ it "sets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(nil).to(group_milestone)
+ end
+ end
+
+ context 'when assigning a group milestone outside the project ancestors' do
+ let(:another_group_milestone) { create(:milestone, group: create(:group)) }
+ let(:params) { { milestone_id: another_group_milestone.id } }
+
+ it "does not change the issuable's milestone" do
+ expect { callback.after_initialize }.not_to change { issuable.milestone }
+ end
+ end
+
+ context 'when user is not allowed to set issuable metadata' do
+ let(:current_user) { create(:user) }
+
+ it "does not change the issuable's milestone" do
+ expect { callback.after_initialize }.not_to change { issuable.milestone }
+ end
+ end
+
+ context 'when unsetting a milestone' do
+ let(:issuable) { create(:issue, project: project, milestone: project_milestone) }
+
+ context 'when milestone_id is nil' do
+ let(:params) { { milestone_id: nil } }
+
+ it "unsets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
+ end
+ end
+
+ context 'when milestone_id is an empty string' do
+ let(:params) { { milestone_id: '' } }
+
+ it "unsets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
+ end
+ end
+
+ context 'when milestone_id is 0' do
+ let(:params) { { milestone_id: '0' } }
+
+ it "unsets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
+ end
+ end
+
+ context "when milestone_id is '0'" do
+ let(:params) { { milestone_id: 0 } }
+
+ it "unsets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
+ end
+ end
+
+ context 'when milestone_id is not given' do
+ let(:params) { {} }
+
+ it "does not unset the issuable's milestone" do
+ expect { callback.after_initialize }.not_to change { issuable.milestone }
+ end
+ end
+
+ context 'when new type does not support milestones' do
+ let(:params) { { excluded_in_new_type: true } }
+
+ it "unsets the issuable's milestone" do
+ expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb
index 0d2b8a4ac3c..9306aeaac44 100644
--- a/spec/services/issuable/common_system_notes_service_spec.rb
+++ b/spec/services/issuable/common_system_notes_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::CommonSystemNotesService do
+RSpec.describe Issuable::CommonSystemNotesService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/issuable/destroy_label_links_service_spec.rb b/spec/services/issuable/destroy_label_links_service_spec.rb
index bbc69e266c9..f0a92c201d2 100644
--- a/spec/services/issuable/destroy_label_links_service_spec.rb
+++ b/spec/services/issuable/destroy_label_links_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::DestroyLabelLinksService do
+RSpec.describe Issuable::DestroyLabelLinksService, feature_category: :team_planning do
describe '#execute' do
context 'when target is an Issue' do
let_it_be(:target) { create(:issue) }
diff --git a/spec/services/issuable/destroy_service_spec.rb b/spec/services/issuable/destroy_service_spec.rb
index 29f548e1c47..1acaf01dce0 100644
--- a/spec/services/issuable/destroy_service_spec.rb
+++ b/spec/services/issuable/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::DestroyService do
+RSpec.describe Issuable::DestroyService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, group: group) }
diff --git a/spec/services/issuable/discussions_list_service_spec.rb b/spec/services/issuable/discussions_list_service_spec.rb
index a6f57088ad1..03b6a1b4556 100644
--- a/spec/services/issuable/discussions_list_service_spec.rb
+++ b/spec/services/issuable/discussions_list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::DiscussionsListService do
+RSpec.describe Issuable::DiscussionsListService, feature_category: :team_planning do
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, :repository, :private, group: group) }
diff --git a/spec/services/issuable/process_assignees_spec.rb b/spec/services/issuable/process_assignees_spec.rb
index 9e909b68172..2c8d4c5e11d 100644
--- a/spec/services/issuable/process_assignees_spec.rb
+++ b/spec/services/issuable/process_assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issuable::ProcessAssignees do
+RSpec.describe Issuable::ProcessAssignees, feature_category: :team_planning do
describe '#execute' do
it 'returns assignee_ids when add_assignee_ids and remove_assignee_ids are not specified' do
process = Issuable::ProcessAssignees.new(assignee_ids: %w(5 7 9),
diff --git a/spec/services/issue_links/create_service_spec.rb b/spec/services/issue_links/create_service_spec.rb
index 0629b8b091b..71603da1c90 100644
--- a/spec/services/issue_links/create_service_spec.rb
+++ b/spec/services/issue_links/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IssueLinks::CreateService do
+RSpec.describe IssueLinks::CreateService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create :user }
let_it_be(:namespace) { create :namespace }
@@ -43,7 +43,6 @@ RSpec.describe IssueLinks::CreateService do
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_relate' }
diff --git a/spec/services/issue_links/destroy_service_spec.rb b/spec/services/issue_links/destroy_service_spec.rb
index ecb53b5cd31..5c4814f5ad1 100644
--- a/spec/services/issue_links/destroy_service_spec.rb
+++ b/spec/services/issue_links/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IssueLinks::DestroyService do
+RSpec.describe IssueLinks::DestroyService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:project) { create(:project_empty_repo, :private) }
let_it_be(:user) { create(:user) }
@@ -27,7 +27,6 @@ RSpec.describe IssueLinks::DestroyService do
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue_b.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_unrelate' }
diff --git a/spec/services/issue_links/list_service_spec.rb b/spec/services/issue_links/list_service_spec.rb
index 7a3ba845c7c..bfb6127ed56 100644
--- a/spec/services/issue_links/list_service_spec.rb
+++ b/spec/services/issue_links/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe IssueLinks::ListService do
+RSpec.describe IssueLinks::ListService, feature_category: :team_planning do
let(:user) { create :user }
let(:project) { create(:project_empty_repo, :private) }
let(:issue) { create :issue, project: project }
diff --git a/spec/services/issues/after_create_service_spec.rb b/spec/services/issues/after_create_service_spec.rb
index 39a6799dbad..b59578b14a0 100644
--- a/spec/services/issues/after_create_service_spec.rb
+++ b/spec/services/issues/after_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::AfterCreateService do
+RSpec.describe Issues::AfterCreateService, feature_category: :team_planning do
include AfterNextHelpers
let_it_be(:project) { create(:project) }
@@ -28,13 +28,6 @@ RSpec.describe Issues::AfterCreateService do
expect { after_create_service.execute(issue) }.to change { Todo.where(attributes).count }.by(1)
end
- it 'deletes milestone issues count cache' do
- expect_next(Milestones::IssuesCountService, milestone)
- .to receive(:delete_cache).and_call_original
-
- after_create_service.execute(issue)
- end
-
context 'with a regular issue' do
it_behaves_like 'does not track incident management event', :incident_management_incident_created do
subject { after_create_service.execute(issue) }
diff --git a/spec/services/issues/base_service_spec.rb b/spec/services/issues/base_service_spec.rb
new file mode 100644
index 00000000000..94165d557d8
--- /dev/null
+++ b/spec/services/issues/base_service_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Issues::BaseService, feature_category: :team_planning do
+ describe '#constructor_container_arg' do
+ it { expect(described_class.constructor_container_arg("some-value")).to eq({ container: "some-value" }) }
+ end
+end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index 2160c45d079..8368a34caf0 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::BuildService do
+RSpec.describe Issues::BuildService, feature_category: :team_planning do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project, :repository) }
@@ -161,8 +161,8 @@ RSpec.describe Issues::BuildService do
end
end
- context 'when guest' do
- let(:user) { guest }
+ context 'when user is not a project member' do
+ let(:user) { create(:user) }
it 'cannot set milestone' do
milestone = create(:milestone, project: project)
@@ -175,31 +175,37 @@ RSpec.describe Issues::BuildService do
describe 'setting issue type' do
context 'with a corresponding WorkItems::Type' do
+ let_it_be(:type_task) { WorkItems::Type.default_by_type(:task) }
+ let_it_be(:type_task_id) { type_task.id }
let_it_be(:type_issue_id) { WorkItems::Type.default_issue_type.id }
let_it_be(:type_incident_id) { WorkItems::Type.default_by_type(:incident).id }
-
- where(:issue_type, :current_user, :work_item_type_id, :resulting_issue_type) do
- nil | ref(:guest) | ref(:type_issue_id) | 'issue'
- 'issue' | ref(:guest) | ref(:type_issue_id) | 'issue'
- 'incident' | ref(:guest) | ref(:type_issue_id) | 'issue'
- 'incident' | ref(:reporter) | ref(:type_incident_id) | 'incident'
+ let(:combined_params) { { work_item_type: type_task, issue_type: 'issue' } }
+ let(:work_item_params) { { work_item_type_id: type_task_id } }
+
+ where(:issue_params, :current_user, :work_item_type_id, :resulting_issue_type) do
+ { issue_type: nil } | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'issue' } | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'incident' } | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'incident' } | ref(:reporter) | ref(:type_incident_id) | 'incident'
+ ref(:combined_params) | ref(:reporter) | ref(:type_task_id) | 'task'
+ ref(:work_item_params) | ref(:reporter) | ref(:type_task_id) | 'task'
# update once support for test_case is enabled
- 'test_case' | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'test_case' } | ref(:guest) | ref(:type_issue_id) | 'issue'
# update once support for requirement is enabled
- 'requirement' | ref(:guest) | ref(:type_issue_id) | 'issue'
- 'invalid' | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'requirement' } | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'invalid' } | ref(:guest) | ref(:type_issue_id) | 'issue'
# ensure that we don't set a value which has a permission check but is an invalid issue type
- 'project' | ref(:guest) | ref(:type_issue_id) | 'issue'
+ { issue_type: 'project' } | ref(:guest) | ref(:type_issue_id) | 'issue'
end
with_them do
let(:user) { current_user }
it 'builds an issue' do
- issue = build_issue(issue_type: issue_type)
+ issue = build_issue(**issue_params)
- expect(issue.issue_type).to eq(resulting_issue_type)
expect(issue.work_item_type_id).to eq(work_item_type_id)
+ expect(issue.attributes['issue_type']).to eq(resulting_issue_type)
end
end
end
diff --git a/spec/services/issues/clone_service_spec.rb b/spec/services/issues/clone_service_spec.rb
index eafaea93015..2fb14d8ce8e 100644
--- a/spec/services/issues/clone_service_spec.rb
+++ b/spec/services/issues/clone_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::CloneService do
+RSpec.describe Issues::CloneService, feature_category: :team_planning do
include DesignManagementTestHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 803808e667c..47925236a74 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe Issues::CloseService do
+RSpec.describe Issues::CloseService, feature_category: :team_planning do
let(:project) { create(:project, :repository) }
+ let(:delegated_project) { project.project_namespace.project }
let(:user) { create(:user, email: "user@example.com") }
let(:user2) { create(:user, email: "user2@example.com") }
let(:guest) { create(:user) }
@@ -100,7 +101,6 @@ RSpec.describe Issues::CloseService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_closed
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_closed' }
@@ -202,34 +202,17 @@ RSpec.describe Issues::CloseService do
end
it 'mentions closure via a merge request' do
- close_issue
-
- email = ActionMailer::Base.deliveries.last
+ expect_next_instance_of(NotificationService::Async) do |service|
+ expect(service).to receive(:close_issue).with(issue, user, { closed_via: closing_merge_request })
+ end
- expect(email.to.first).to eq(user2.email)
- expect(email.subject).to include(issue.title)
- expect(email.body.parts.map(&:body)).to all(include(closing_merge_request.to_reference))
+ close_issue
end
it_behaves_like 'records an onboarding progress action', :issue_auto_closed do
let(:namespace) { project.namespace }
end
- context 'when user cannot read merge request' do
- it 'does not mention merge request' do
- project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
-
- close_issue
-
- email = ActionMailer::Base.deliveries.last
- body_text = email.body.parts.map(&:body).join(" ")
-
- expect(email.to.first).to eq(user2.email)
- expect(email.subject).to include(issue.title)
- expect(body_text).not_to include(closing_merge_request.to_reference)
- end
- end
-
context 'updating `metrics.first_mentioned_in_commit_at`' do
context 'when `metrics.first_mentioned_in_commit_at` is not set' do
it 'uses the first commit authored timestamp' do
@@ -265,31 +248,11 @@ RSpec.describe Issues::CloseService do
context "closed by a commit", :sidekiq_might_not_need_inline do
it 'mentions closure via a commit' do
- perform_enqueued_jobs do
- described_class.new(container: project, current_user: user).close_issue(issue, closed_via: closing_commit)
+ expect_next_instance_of(NotificationService::Async) do |service|
+ expect(service).to receive(:close_issue).with(issue, user, { closed_via: "commit #{closing_commit.id}" })
end
- email = ActionMailer::Base.deliveries.last
-
- expect(email.to.first).to eq(user2.email)
- expect(email.subject).to include(issue.title)
- expect(email.body.parts.map(&:body)).to all(include(closing_commit.id))
- end
-
- context 'when user cannot read the commit' do
- it 'does not mention the commit id' do
- project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
- perform_enqueued_jobs do
- described_class.new(container: project, current_user: user).close_issue(issue, closed_via: closing_commit)
- end
-
- email = ActionMailer::Base.deliveries.last
- body_text = email.body.parts.map(&:body).join(" ")
-
- expect(email.to.first).to eq(user2.email)
- expect(email.subject).to include(issue.title)
- expect(body_text).not_to include(closing_commit.id)
- end
+ described_class.new(container: project, current_user: user).close_issue(issue, closed_via: closing_commit)
end
end
@@ -321,12 +284,12 @@ RSpec.describe Issues::CloseService do
expect(issue.reload.closed_by_id).to be(user.id)
end
- it 'sends email to user2 about assign of new issue', :sidekiq_might_not_need_inline do
- close_issue
+ it 'sends notification', :sidekiq_might_not_need_inline do
+ expect_next_instance_of(NotificationService::Async) do |service|
+ expect(service).to receive(:close_issue).with(issue, user, { closed_via: nil })
+ end
- email = ActionMailer::Base.deliveries.last
- expect(email.to.first).to eq(user2.email)
- expect(email.subject).to include(issue.title)
+ close_issue
end
it 'creates resource state event about the issue being closed' do
@@ -435,10 +398,10 @@ RSpec.describe Issues::CloseService do
end
it 'executes issue hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
- expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
+ expect(delegated_project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(delegated_project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
- described_class.new(container: project, current_user: user).close_issue(issue)
+ described_class.new(container: delegated_project, current_user: user).close_issue(issue)
end
end
@@ -446,8 +409,8 @@ RSpec.describe Issues::CloseService do
it 'executes confidential issue hooks' do
issue = create(:issue, :confidential, project: project)
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
+ expect(delegated_project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
+ expect(delegated_project).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
described_class.new(container: project, current_user: user).close_issue(issue)
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index ada5b300d7a..548d9455ebf 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::CreateService do
+RSpec.describe Issues::CreateService, feature_category: :team_planning do
include AfterNextHelpers
let_it_be(:group) { create(:group, :crm_enabled) }
@@ -124,6 +124,15 @@ RSpec.describe Issues::CreateService do
expect(issue.issue_customer_relations_contacts).to be_empty
end
+ context 'with milestone' do
+ it 'deletes milestone issues count cache' do
+ expect_next(Milestones::IssuesCountService, milestone)
+ .to receive(:delete_cache).and_call_original
+
+ expect(result).to be_success
+ end
+ end
+
context 'when the work item type is not allowed to create' do
before do
allow_next_instance_of(::Issues::BuildService) do |instance|
@@ -136,7 +145,6 @@ RSpec.describe Issues::CreateService do
expect(issue).to be_persisted
expect(issue).to be_a(::Issue)
expect(issue.work_item_type.base_type).to eq('issue')
- expect(issue.issue_type).to eq('issue')
end
end
@@ -149,7 +157,7 @@ RSpec.describe Issues::CreateService do
context 'when a build_service is provided' do
let(:result) { described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params, build_service: build_service).execute }
- let(:issue_from_builder) { WorkItem.new(project: project, title: 'Issue from builder') }
+ let(:issue_from_builder) { build(:work_item, project: project, title: 'Issue from builder') }
let(:build_service) { double(:build_service, execute: issue_from_builder) }
it 'uses the provided service to build the issue' do
@@ -372,6 +380,13 @@ RSpec.describe Issues::CreateService do
expect(assignee.assigned_open_issues_count).to eq 1
end
+
+ it 'records the assignee assignment event' do
+ result = described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
+
+ issue = result.payload[:issue]
+ expect(issue.assignment_events).to match([have_attributes(user_id: assignee.id, action: 'add')])
+ end
end
context 'when duplicate label titles are given' do
@@ -436,8 +451,8 @@ RSpec.describe Issues::CreateService do
end
it 'executes issue hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
- expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
end
@@ -459,8 +474,8 @@ RSpec.describe Issues::CreateService do
end
it 'executes confidential issue hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, :confidential_issue_hooks)
- expect(project).to receive(:execute_integrations).with(expected_payload, :confidential_issue_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(expected_payload, :confidential_issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(expected_payload, :confidential_issue_hooks)
described_class.new(container: project, current_user: user, params: opts, spam_params: spam_params).execute
end
@@ -493,7 +508,7 @@ RSpec.describe Issues::CreateService do
end
it 'schedules a namespace onboarding create action worker' do
- expect(Onboarding::IssueCreatedWorker).to receive(:perform_async).with(project.namespace.id)
+ expect(Onboarding::IssueCreatedWorker).to receive(:perform_async).with(project.project_namespace_id)
issue
end
@@ -565,36 +580,6 @@ RSpec.describe Issues::CreateService do
end
context 'Quick actions' do
- context 'as work item' do
- let(:opts) do
- {
- title: "My work item",
- work_item_type: work_item_type,
- description: "/shrug"
- }
- end
-
- context 'when work item type is not the default Issue' do
- let(:work_item_type) { create(:work_item_type, namespace: project.namespace) }
-
- it 'saves the work item without applying the quick action' do
- expect(result).to be_success
- expect(issue).to be_persisted
- expect(issue.description).to eq("/shrug")
- end
- end
-
- context 'when work item type is the default Issue' do
- let(:work_item_type) { WorkItems::Type.default_by_type(:issue) }
-
- it 'saves the work item and applies the quick action' do
- expect(result).to be_success
- expect(issue).to be_persisted
- expect(issue.description).to eq(" ¯\\_(ツ)_/¯")
- end
- end
- end
-
context 'with assignee, milestone, and contact in params and command' do
let_it_be(:contact) { create(:contact, group: group) }
@@ -687,6 +672,22 @@ RSpec.describe Issues::CreateService do
expect(issue.labels).to eq([label])
end
end
+
+ context 'when using promote_to_incident' do
+ let(:opts) { { title: 'Title', description: '/promote_to_incident' } }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'creates an issue with the correct issue type' do
+ expect { result }.to change(Issue, :count).by(1)
+
+ created_issue = Issue.last
+
+ expect(created_issue.work_item_type).to eq(WorkItems::Type.default_by_type('incident'))
+ end
+ end
end
context 'resolving discussions' do
diff --git a/spec/services/issues/duplicate_service_spec.rb b/spec/services/issues/duplicate_service_spec.rb
index f49bce70cd0..f9d8bf04ae9 100644
--- a/spec/services/issues/duplicate_service_spec.rb
+++ b/spec/services/issues/duplicate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::DuplicateService do
+RSpec.describe Issues::DuplicateService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:canonical_project) { create(:project) }
let(:duplicate_project) { create(:project) }
diff --git a/spec/services/issues/import_csv_service_spec.rb b/spec/services/issues/import_csv_service_spec.rb
index 90e360f9cf1..6a147782209 100644
--- a/spec/services/issues/import_csv_service_spec.rb
+++ b/spec/services/issues/import_csv_service_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Issues::ImportCsvService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:assignee) { create(:user, username: 'csv_assignee') }
+ let(:file) { fixture_file_upload('spec/fixtures/csv_complex.csv') }
let(:service) do
uploader = FileUploader.new(project)
uploader.store!(file)
@@ -19,8 +20,6 @@ RSpec.describe Issues::ImportCsvService, feature_category: :team_planning do
end
describe '#execute' do
- let(:file) { fixture_file_upload('spec/fixtures/csv_complex.csv') }
-
subject { service.execute }
it 'sets all issueable attributes and executes quick actions' do
diff --git a/spec/services/issues/issuable_base_service_spec.rb b/spec/services/issues/issuable_base_service_spec.rb
new file mode 100644
index 00000000000..e1680d5c908
--- /dev/null
+++ b/spec/services/issues/issuable_base_service_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IssuableBaseService, feature_category: :team_planning do
+ describe '#constructor_container_arg' do
+ it { expect(described_class.constructor_container_arg("some-value")).to eq({ container: "some-value" }) }
+ end
+end
diff --git a/spec/services/issues/prepare_import_csv_service_spec.rb b/spec/services/issues/prepare_import_csv_service_spec.rb
index ded23ee43b9..d318d4fd25e 100644
--- a/spec/services/issues/prepare_import_csv_service_spec.rb
+++ b/spec/services/issues/prepare_import_csv_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::PrepareImportCsvService do
+RSpec.describe Issues::PrepareImportCsvService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/issues/referenced_merge_requests_service_spec.rb b/spec/services/issues/referenced_merge_requests_service_spec.rb
index aee3583b834..4781daf7688 100644
--- a/spec/services/issues/referenced_merge_requests_service_spec.rb
+++ b/spec/services/issues/referenced_merge_requests_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::ReferencedMergeRequestsService do
+RSpec.describe Issues::ReferencedMergeRequestsService, feature_category: :team_planning do
def create_referencing_mr(attributes = {})
create(:merge_request, attributes).tap do |merge_request|
create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true))
diff --git a/spec/services/issues/related_branches_service_spec.rb b/spec/services/issues/related_branches_service_spec.rb
index 05c61d0abfc..940d988668e 100644
--- a/spec/services/issues/related_branches_service_spec.rb
+++ b/spec/services/issues/related_branches_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::RelatedBranchesService do
+RSpec.describe Issues::RelatedBranchesService, feature_category: :team_planning do
let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
let_it_be(:developer) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/services/issues/relative_position_rebalancing_service_spec.rb b/spec/services/issues/relative_position_rebalancing_service_spec.rb
index 27c0394ac8b..68f1af49b5f 100644
--- a/spec/services/issues/relative_position_rebalancing_service_spec.rb
+++ b/spec/services/issues/relative_position_rebalancing_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_shared_state do
+RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_shared_state, feature_category: :team_planning do
let_it_be(:project, reload: true) { create(:project, :repository_disabled, skip_disk_validation: true) }
let_it_be(:user) { project.creator }
let_it_be(:start) { RelativePositioning::START_POSITION }
diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb
index 68015a2327e..bb1151dfac7 100644
--- a/spec/services/issues/reopen_service_spec.rb
+++ b/spec/services/issues/reopen_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::ReopenService do
+RSpec.describe Issues::ReopenService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:issue) { create(:issue, :closed, project: project) }
@@ -75,7 +75,6 @@ RSpec.describe Issues::ReopenService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_reopened
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_reopened' }
@@ -110,8 +109,8 @@ RSpec.describe Issues::ReopenService do
end
it 'executes issue hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
- expect(project).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(expected_payload, :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(expected_payload, :issue_hooks)
execute
end
@@ -121,8 +120,9 @@ RSpec.describe Issues::ReopenService do
let(:issue) { create(:issue, :confidential, :closed, project: project) }
it 'executes confidential issue hooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
+ issue_hooks = :confidential_issue_hooks
+ expect(project.project_namespace).to receive(:execute_hooks).with(an_instance_of(Hash), issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), issue_hooks)
execute
end
diff --git a/spec/services/issues/reorder_service_spec.rb b/spec/services/issues/reorder_service_spec.rb
index 430a9e9f526..b98d23e0f7f 100644
--- a/spec/services/issues/reorder_service_spec.rb
+++ b/spec/services/issues/reorder_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::ReorderService do
+RSpec.describe Issues::ReorderService, feature_category: :team_planning do
let_it_be(:user) { create_default(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project, reload: true) { create(:project, namespace: group) }
diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb
index 1ac71b966bc..c2111bffdda 100644
--- a/spec/services/issues/resolve_discussions_spec.rb
+++ b/spec/services/issues/resolve_discussions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::ResolveDiscussions do
+RSpec.describe Issues::ResolveDiscussions, feature_category: :team_planning do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
@@ -11,7 +11,7 @@ RSpec.describe Issues::ResolveDiscussions do
DummyService.class_eval do
include ::Issues::ResolveDiscussions
- def initialize(project:, current_user: nil, params: {})
+ def initialize(container:, current_user: nil, params: {})
super
filter_resolve_discussion_params
end
@@ -26,7 +26,7 @@ RSpec.describe Issues::ResolveDiscussions do
let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: "fix") }
describe "#merge_request_for_resolving_discussion" do
- let(:service) { DummyService.new(project: project, current_user: user, params: { merge_request_to_resolve_discussions_of: merge_request.iid }) }
+ let(:service) { DummyService.new(container: project, current_user: user, params: { merge_request_to_resolve_discussions_of: merge_request.iid }) }
it "finds the merge request" do
expect(service.merge_request_to_resolve_discussions_of).to eq(merge_request)
@@ -45,7 +45,7 @@ RSpec.describe Issues::ResolveDiscussions do
describe "#discussions_to_resolve" do
it "contains a single discussion when matching merge request and discussion are passed" do
service = DummyService.new(
- project: project,
+ container: project,
current_user: user,
params: {
discussion_to_resolve: discussion.id,
@@ -65,7 +65,7 @@ RSpec.describe Issues::ResolveDiscussions do
project: merge_request.target_project,
line_number: 15)])
service = DummyService.new(
- project: project,
+ container: project,
current_user: user,
params: { merge_request_to_resolve_discussions_of: merge_request.iid }
)
@@ -83,7 +83,7 @@ RSpec.describe Issues::ResolveDiscussions do
line_number: 15
)])
service = DummyService.new(
- project: project,
+ container: project,
current_user: user,
params: { merge_request_to_resolve_discussions_of: merge_request.iid }
)
@@ -96,7 +96,7 @@ RSpec.describe Issues::ResolveDiscussions do
it "is empty when a discussion and another merge request are passed" do
service = DummyService.new(
- project: project,
+ container: project,
current_user: user,
params: {
discussion_to_resolve: discussion.id,
diff --git a/spec/services/issues/set_crm_contacts_service_spec.rb b/spec/services/issues/set_crm_contacts_service_spec.rb
index 5613cc49cc5..aa5dec20a13 100644
--- a/spec/services/issues/set_crm_contacts_service_spec.rb
+++ b/spec/services/issues/set_crm_contacts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::SetCrmContactsService do
+RSpec.describe Issues::SetCrmContactsService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :crm_enabled) }
let_it_be(:project) { create(:project, group: create(:group, :crm_enabled, parent: group)) }
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 973025bd2e3..f96fbf54f08 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::UpdateService, :mailer do
+RSpec.describe Issues::UpdateService, :mailer, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
let_it_be(:user3) { create(:user) }
@@ -191,7 +191,6 @@ RSpec.describe Issues::UpdateService, :mailer do
it_behaves_like 'an incident management tracked event', :incident_management_incident_change_confidential
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
@@ -260,7 +259,7 @@ RSpec.describe Issues::UpdateService, :mailer do
it 'creates system note about issue type' do
update_issue(issue_type: 'incident')
- note = find_note('changed issue type to incident')
+ note = find_note('changed type from issue to incident')
expect(note).not_to eq(nil)
end
@@ -593,8 +592,8 @@ RSpec.describe Issues::UpdateService, :mailer do
end
it 'executes confidential issue hooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(an_instance_of(Hash), :confidential_issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), :confidential_issue_hooks)
update_issue(confidential: true)
end
@@ -696,7 +695,6 @@ RSpec.describe Issues::UpdateService, :mailer do
it_behaves_like 'an incident management tracked event', :incident_management_incident_assigned
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:label) { 'redis_hll_counters.incident_management.incident_management_total_unique_counts_monthly' }
@@ -1109,19 +1107,37 @@ RSpec.describe Issues::UpdateService, :mailer do
end
context 'updating asssignee_id' do
+ it 'changes assignee' do
+ expect_next_instance_of(NotificationService::Async) do |service|
+ expect(service).to receive(:reassigned_issue).with(issue, user, [user3])
+ end
+
+ update_issue(assignee_ids: [user2.id])
+
+ expect(issue.reload.assignees).to eq([user2])
+ end
+
it 'does not update assignee when assignee_id is invalid' do
+ expect(NotificationService).not_to receive(:new)
+
update_issue(assignee_ids: [-1])
expect(issue.reload.assignees).to eq([user3])
end
it 'unassigns assignee when user id is 0' do
+ expect_next_instance_of(NotificationService::Async) do |service|
+ expect(service).to receive(:reassigned_issue).with(issue, user, [user3])
+ end
+
update_issue(assignee_ids: [0])
expect(issue.reload.assignees).to be_empty
end
it 'does not update assignee_id when user cannot read issue' do
+ expect(NotificationService).not_to receive(:new)
+
update_issue(assignee_ids: [create(:user).id])
expect(issue.reload.assignees).to eq([user3])
@@ -1132,6 +1148,8 @@ RSpec.describe Issues::UpdateService, :mailer do
levels.each do |level|
it "does not update with unauthorized assignee when project is #{Gitlab::VisibilityLevel.level_name(level)}" do
+ expect(NotificationService).not_to receive(:new)
+
assignee = create(:user)
project.update!(visibility_level: level)
feature_visibility_attr = :"#{issue.model_name.plural}_access_level"
@@ -1141,6 +1159,39 @@ RSpec.describe Issues::UpdateService, :mailer do
end
end
end
+
+ it 'tracks the assignment events' do
+ original_assignee = issue.assignees.first!
+
+ update_issue(assignee_ids: [user2.id])
+ update_issue(assignee_ids: [])
+ update_issue(assignee_ids: [user3.id])
+
+ expected_events = [
+ have_attributes({
+ issue_id: issue.id,
+ user_id: original_assignee.id,
+ action: 'remove'
+ }),
+ have_attributes({
+ issue_id: issue.id,
+ user_id: user2.id,
+ action: 'add'
+ }),
+ have_attributes({
+ issue_id: issue.id,
+ user_id: user2.id,
+ action: 'remove'
+ }),
+ have_attributes({
+ issue_id: issue.id,
+ user_id: user3.id,
+ action: 'add'
+ })
+ ]
+
+ expect(issue.assignment_events).to match_array(expected_events)
+ end
end
context 'updating mentions' do
@@ -1166,9 +1217,9 @@ RSpec.describe Issues::UpdateService, :mailer do
end
it 'triggers webhooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks)
update_issue(opts)
end
@@ -1280,9 +1331,9 @@ RSpec.describe Issues::UpdateService, :mailer do
end
it 'triggers webhooks' do
- expect(project).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
- expect(project).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks)
+ expect(project.project_namespace).to receive(:execute_hooks).with(an_instance_of(Hash), :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), :issue_hooks)
+ expect(project.project_namespace).to receive(:execute_integrations).with(an_instance_of(Hash), :incident_hooks)
update_issue(opts)
end
@@ -1475,31 +1526,5 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:existing_issue) { create(:issue, project: project) }
let(:issuable) { described_class.new(container: project, current_user: user, params: params).execute(existing_issue) }
end
-
- context 'with quick actions' do
- context 'as work item' do
- let(:opts) { { description: "/shrug" } }
-
- context 'when work item type is not the default Issue' do
- let(:issue) { create(:work_item, :task, description: "") }
-
- it 'does not apply the quick action' do
- expect do
- update_issue(opts)
- end.to change(issue, :description).to("/shrug")
- end
- end
-
- context 'when work item type is the default Issue' do
- let(:issue) { create(:work_item, :issue, description: "") }
-
- it 'does not apply the quick action' do
- expect do
- update_issue(opts)
- end.to change(issue, :description).to(" ¯\\_(ツ)_/¯")
- end
- end
- end
- end
end
end
diff --git a/spec/services/issues/zoom_link_service_spec.rb b/spec/services/issues/zoom_link_service_spec.rb
index 230e4c1b5e1..f2a81cbe33f 100644
--- a/spec/services/issues/zoom_link_service_spec.rb
+++ b/spec/services/issues/zoom_link_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Issues::ZoomLinkService do
+RSpec.describe Issues::ZoomLinkService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue) }
@@ -97,7 +97,6 @@ RSpec.describe Issues::ZoomLinkService do
it_behaves_like 'an incident management tracked event', :incident_management_incident_zoom_meeting
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_zoom_meeting' }
diff --git a/spec/services/jira/requests/projects/list_service_spec.rb b/spec/services/jira/requests/projects/list_service_spec.rb
index 78ee9cb9698..37e9f66d273 100644
--- a/spec/services/jira/requests/projects/list_service_spec.rb
+++ b/spec/services/jira/requests/projects/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Jira::Requests::Projects::ListService do
+RSpec.describe Jira::Requests::Projects::ListService, feature_category: :projects do
include AfterNextHelpers
let(:jira_integration) { create(:jira_integration) }
diff --git a/spec/services/jira_connect/sync_service_spec.rb b/spec/services/jira_connect/sync_service_spec.rb
index 32580a7735f..7457cdca13c 100644
--- a/spec/services/jira_connect/sync_service_spec.rb
+++ b/spec/services/jira_connect/sync_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::SyncService do
+RSpec.describe JiraConnect::SyncService, feature_category: :integrations do
include AfterNextHelpers
describe '#execute' do
@@ -44,16 +44,18 @@ RSpec.describe JiraConnect::SyncService do
subject
end
- context 'when a request returns an error' do
- it 'logs the response as an error' do
+ context 'when a request returns errors' do
+ it 'logs each response as an error' do
expect_next(client).to store_info(
[
{ 'errorMessages' => ['some error message'] },
- { 'errorMessages' => ['x'] }
+ { 'errorMessage' => 'a single error message' },
+ { 'errorMessages' => [] },
+ { 'errorMessage' => '' }
])
expect_log(:error, { 'errorMessages' => ['some error message'] })
- expect_log(:error, { 'errorMessages' => ['x'] })
+ expect_log(:error, { 'errorMessage' => 'a single error message' })
subject
end
diff --git a/spec/services/jira_connect_installations/destroy_service_spec.rb b/spec/services/jira_connect_installations/destroy_service_spec.rb
index bb5bab53ccb..b8b59d6cc57 100644
--- a/spec/services/jira_connect_installations/destroy_service_spec.rb
+++ b/spec/services/jira_connect_installations/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnectInstallations::DestroyService do
+RSpec.describe JiraConnectInstallations::DestroyService, feature_category: :integrations do
describe '.execute' do
it 'creates an instance and calls execute' do
expect_next_instance_of(described_class, 'param1', 'param2', 'param3') do |destroy_service|
diff --git a/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb b/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb
index c621388a734..3c144de2208 100644
--- a/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb
+++ b/spec/services/jira_connect_installations/proxy_lifecycle_event_service_spec.rb
@@ -94,9 +94,9 @@ RSpec.describe JiraConnectInstallations::ProxyLifecycleEventService, feature_cat
expect(Gitlab::IntegrationsLogger).to receive(:info).with(
integration: 'JiraConnect',
message: 'Proxy lifecycle event received error response',
- event_type: evnet_type,
- status_code: 422,
- body: 'Error message'
+ jira_event_type: evnet_type,
+ jira_status_code: 422,
+ jira_body: 'Error message'
)
execute_service
diff --git a/spec/services/jira_connect_subscriptions/create_service_spec.rb b/spec/services/jira_connect_subscriptions/create_service_spec.rb
index 85208a30c30..f9d3954b84c 100644
--- a/spec/services/jira_connect_subscriptions/create_service_spec.rb
+++ b/spec/services/jira_connect_subscriptions/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnectSubscriptions::CreateService do
+RSpec.describe JiraConnectSubscriptions::CreateService, feature_category: :integrations do
let_it_be(:installation) { create(:jira_connect_installation) }
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/jira_import/cloud_users_mapper_service_spec.rb b/spec/services/jira_import/cloud_users_mapper_service_spec.rb
index 6b06a982a80..e3f3d550467 100644
--- a/spec/services/jira_import/cloud_users_mapper_service_spec.rb
+++ b/spec/services/jira_import/cloud_users_mapper_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraImport::CloudUsersMapperService do
+RSpec.describe JiraImport::CloudUsersMapperService, feature_category: :integrations do
let(:start_at) { 7 }
let(:url) { "/rest/api/2/users?maxResults=50&startAt=#{start_at}" }
diff --git a/spec/services/jira_import/server_users_mapper_service_spec.rb b/spec/services/jira_import/server_users_mapper_service_spec.rb
index 71cb8aea0be..e2304953dd2 100644
--- a/spec/services/jira_import/server_users_mapper_service_spec.rb
+++ b/spec/services/jira_import/server_users_mapper_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraImport::ServerUsersMapperService do
+RSpec.describe JiraImport::ServerUsersMapperService, feature_category: :integrations do
let(:start_at) { 7 }
let(:url) { "/rest/api/2/user/search?username=''&maxResults=50&startAt=#{start_at}" }
diff --git a/spec/services/jira_import/start_import_service_spec.rb b/spec/services/jira_import/start_import_service_spec.rb
index c0db3012a30..9cb163e3d1a 100644
--- a/spec/services/jira_import/start_import_service_spec.rb
+++ b/spec/services/jira_import/start_import_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraImport::StartImportService do
+RSpec.describe JiraImport::StartImportService, feature_category: :integrations do
include JiraIntegrationHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/jira_import/users_importer_spec.rb b/spec/services/jira_import/users_importer_spec.rb
index ace9e0d5779..39f8475754a 100644
--- a/spec/services/jira_import/users_importer_spec.rb
+++ b/spec/services/jira_import/users_importer_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraImport::UsersImporter do
+RSpec.describe JiraImport::UsersImporter, feature_category: :integrations do
include JiraIntegrationHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/keys/create_service_spec.rb b/spec/services/keys/create_service_spec.rb
index 1dbe383ad8e..0a9fe2f5856 100644
--- a/spec/services/keys/create_service_spec.rb
+++ b/spec/services/keys/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Keys::CreateService do
+RSpec.describe Keys::CreateService, feature_category: :source_code_management do
let(:user) { create(:user) }
let(:params) { attributes_for(:key) }
diff --git a/spec/services/keys/destroy_service_spec.rb b/spec/services/keys/destroy_service_spec.rb
index dd40f9d73fd..9f064cb7236 100644
--- a/spec/services/keys/destroy_service_spec.rb
+++ b/spec/services/keys/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Keys::DestroyService do
+RSpec.describe Keys::DestroyService, feature_category: :source_code_management do
let(:user) { create(:user) }
subject { described_class.new(user) }
diff --git a/spec/services/keys/expiry_notification_service_spec.rb b/spec/services/keys/expiry_notification_service_spec.rb
index 7cb6cbce311..b8db4f28df7 100644
--- a/spec/services/keys/expiry_notification_service_spec.rb
+++ b/spec/services/keys/expiry_notification_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Keys::ExpiryNotificationService do
+RSpec.describe Keys::ExpiryNotificationService, feature_category: :source_code_management do
let_it_be_with_reload(:user) { create(:user) }
let(:params) { { keys: user.keys, expiring_soon: expiring_soon } }
diff --git a/spec/services/keys/last_used_service_spec.rb b/spec/services/keys/last_used_service_spec.rb
index a2cd5ffdd38..32100d793ff 100644
--- a/spec/services/keys/last_used_service_spec.rb
+++ b/spec/services/keys/last_used_service_spec.rb
@@ -2,33 +2,51 @@
require 'spec_helper'
-RSpec.describe Keys::LastUsedService do
+RSpec.describe Keys::LastUsedService, feature_category: :source_code_management do
describe '#execute', :clean_gitlab_redis_shared_state do
- it 'updates the key when it has not been used recently' do
- key = create(:key, last_used_at: 1.year.ago)
- time = Time.zone.now
+ context 'when it has not been used recently' do
+ let(:key) { create(:key, last_used_at: 1.year.ago) }
+ let(:time) { Time.zone.now }
- travel_to(time) { described_class.new(key).execute }
+ it 'updates the key' do
+ travel_to(time) { described_class.new(key).execute }
- expect(key.reload.last_used_at).to be_like_time(time)
+ expect(key.reload.last_used_at).to be_like_time(time)
+ end
end
- it 'does not update the key when it has been used recently' do
- time = 1.minute.ago
- key = create(:key, last_used_at: time)
+ context 'when it has been used recently' do
+ let(:time) { 1.minute.ago }
+ let(:key) { create(:key, last_used_at: time) }
- described_class.new(key).execute
+ it 'does not update the key' do
+ described_class.new(key).execute
- expect(key.last_used_at).to be_like_time(time)
+ expect(key.reload.last_used_at).to be_like_time(time)
+ end
end
+ end
+
+ describe '#execute_async', :clean_gitlab_redis_shared_state do
+ context 'when it has not been used recently' do
+ let(:key) { create(:key, last_used_at: 1.year.ago) }
+ let(:time) { Time.zone.now }
- it 'does not update the updated_at field' do
- # Since a lot of these updates could happen in parallel for different keys
- # we want these updates to be as lightweight as possible, hence we want to
- # make sure we _only_ update last_used_at and not always updated_at.
- key = create(:key, last_used_at: 1.year.ago)
+ it 'schedules a job to update last_used_at' do
+ expect(::SshKeys::UpdateLastUsedAtWorker).to receive(:perform_async)
- expect { described_class.new(key).execute }.not_to change { key.updated_at }
+ travel_to(time) { described_class.new(key).execute_async }
+ end
+ end
+
+ context 'when it has been used recently' do
+ let(:key) { create(:key, last_used_at: 1.minute.ago) }
+
+ it 'does not schedule a job to update last_used_at' do
+ expect(::SshKeys::UpdateLastUsedAtWorker).not_to receive(:perform_async)
+
+ described_class.new(key).execute_async
+ end
end
end
@@ -47,14 +65,6 @@ RSpec.describe Keys::LastUsedService do
expect(service.update?).to eq(true)
end
- it 'returns false when a lease has already been obtained' do
- key = build(:key, last_used_at: 1.year.ago)
- service = described_class.new(key)
-
- expect(service.update?).to eq(true)
- expect(service.update?).to eq(false)
- end
-
it 'returns false when the key does not yet need to be updated' do
key = build(:key, last_used_at: 1.minute.ago)
service = described_class.new(key)
diff --git a/spec/services/keys/revoke_service_spec.rb b/spec/services/keys/revoke_service_spec.rb
index ec07701b4b7..8294ec5bbd1 100644
--- a/spec/services/keys/revoke_service_spec.rb
+++ b/spec/services/keys/revoke_service_spec.rb
@@ -32,17 +32,4 @@ RSpec.describe Keys::RevokeService, feature_category: :source_code_management do
expect { service.execute(key) }.not_to change { signature.reload.verification_status }
expect(key).to be_persisted
end
-
- context 'when revoke_ssh_signatures disabled' do
- before do
- stub_feature_flags(revoke_ssh_signatures: false)
- end
-
- it 'does not unverifies signatures' do
- key = create(:key)
- signature = create(:ssh_signature, key: key)
-
- expect { service.execute(key) }.not_to change { signature.reload.verification_status }
- end
- end
end
diff --git a/spec/services/labels/available_labels_service_spec.rb b/spec/services/labels/available_labels_service_spec.rb
index 355dbd0c712..51314c2c226 100644
--- a/spec/services/labels/available_labels_service_spec.rb
+++ b/spec/services/labels/available_labels_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Labels::AvailableLabelsService do
+RSpec.describe Labels::AvailableLabelsService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :public, group: group) }
let(:group) { create(:group) }
diff --git a/spec/services/labels/create_service_spec.rb b/spec/services/labels/create_service_spec.rb
index 02dec8ae690..9be611490cf 100644
--- a/spec/services/labels/create_service_spec.rb
+++ b/spec/services/labels/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Labels::CreateService do
+RSpec.describe Labels::CreateService, feature_category: :team_planning do
describe '#execute' do
let(:project) { create(:project) }
let(:group) { create(:group) }
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
index 3ea2727dc60..0bc1326942d 100644
--- a/spec/services/labels/find_or_create_service_spec.rb
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Labels::FindOrCreateService do
+RSpec.describe Labels::FindOrCreateService, feature_category: :team_planning do
describe '#execute' do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb
index 3af6cf4c8f4..79cc88c65c8 100644
--- a/spec/services/labels/promote_service_spec.rb
+++ b/spec/services/labels/promote_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Labels::PromoteService do
+RSpec.describe Labels::PromoteService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb
index e67ab6025a5..bf895692e64 100644
--- a/spec/services/labels/transfer_service_spec.rb
+++ b/spec/services/labels/transfer_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Labels::TransferService do
+RSpec.describe Labels::TransferService, feature_category: :team_planning do
shared_examples 'transfer labels' do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/labels/update_service_spec.rb b/spec/services/labels/update_service_spec.rb
index abc456f75f9..b9ac5282d10 100644
--- a/spec/services/labels/update_service_spec.rb
+++ b/spec/services/labels/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Labels::UpdateService do
+RSpec.describe Labels::UpdateService, feature_category: :team_planning do
describe '#execute' do
let(:project) { create(:project) }
diff --git a/spec/services/lfs/lock_file_service_spec.rb b/spec/services/lfs/lock_file_service_spec.rb
index b3a121866c8..47bf0c5f4ce 100644
--- a/spec/services/lfs/lock_file_service_spec.rb
+++ b/spec/services/lfs/lock_file_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Lfs::LockFileService do
+RSpec.describe Lfs::LockFileService, feature_category: :source_code_management do
let(:project) { create(:project) }
let(:current_user) { create(:user) }
diff --git a/spec/services/lfs/locks_finder_service_spec.rb b/spec/services/lfs/locks_finder_service_spec.rb
index 1167212eb69..38f8dadd38d 100644
--- a/spec/services/lfs/locks_finder_service_spec.rb
+++ b/spec/services/lfs/locks_finder_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Lfs::LocksFinderService do
+RSpec.describe Lfs::LocksFinderService, feature_category: :source_code_management do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:params) { {} }
diff --git a/spec/services/lfs/push_service_spec.rb b/spec/services/lfs/push_service_spec.rb
index f52bba94eea..1ec143a7fc9 100644
--- a/spec/services/lfs/push_service_spec.rb
+++ b/spec/services/lfs/push_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Lfs::PushService do
+RSpec.describe Lfs::PushService, feature_category: :source_code_management do
let(:logger) { service.send(:logger) }
let(:lfs_client) { service.send(:lfs_client) }
diff --git a/spec/services/lfs/unlock_file_service_spec.rb b/spec/services/lfs/unlock_file_service_spec.rb
index 7ab269f897a..45fd1adcfb4 100644
--- a/spec/services/lfs/unlock_file_service_spec.rb
+++ b/spec/services/lfs/unlock_file_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Lfs::UnlockFileService do
+RSpec.describe Lfs::UnlockFileService, feature_category: :source_code_management do
let(:project) { create(:project) }
let(:current_user) { create(:user) }
let(:lock_author) { create(:user) }
diff --git a/spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb b/spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb
index 735f090d926..6eee83d5ee9 100644
--- a/spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb
+++ b/spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe LooseForeignKeys::BatchCleanerService do
+RSpec.describe LooseForeignKeys::BatchCleanerService, feature_category: :database do
include MigrationsHelpers
def create_table_structure
diff --git a/spec/services/loose_foreign_keys/cleaner_service_spec.rb b/spec/services/loose_foreign_keys/cleaner_service_spec.rb
index 2cfd8385953..04f6270c5f2 100644
--- a/spec/services/loose_foreign_keys/cleaner_service_spec.rb
+++ b/spec/services/loose_foreign_keys/cleaner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe LooseForeignKeys::CleanerService do
+RSpec.describe LooseForeignKeys::CleanerService, feature_category: :database do
let(:schema) { ApplicationRecord.connection.current_schema }
let(:deleted_records) do
[
diff --git a/spec/services/loose_foreign_keys/process_deleted_records_service_spec.rb b/spec/services/loose_foreign_keys/process_deleted_records_service_spec.rb
index 1824f822ba8..af010547cc9 100644
--- a/spec/services/loose_foreign_keys/process_deleted_records_service_spec.rb
+++ b/spec/services/loose_foreign_keys/process_deleted_records_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe LooseForeignKeys::ProcessDeletedRecordsService do
+RSpec.describe LooseForeignKeys::ProcessDeletedRecordsService, feature_category: :database do
include MigrationsHelpers
def create_table_structure
diff --git a/spec/services/markdown_content_rewriter_service_spec.rb b/spec/services/markdown_content_rewriter_service_spec.rb
index d94289856cf..bf15ef08647 100644
--- a/spec/services/markdown_content_rewriter_service_spec.rb
+++ b/spec/services/markdown_content_rewriter_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MarkdownContentRewriterService do
+RSpec.describe MarkdownContentRewriterService, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:source_parent) { create(:project, :public) }
let_it_be(:target_parent) { create(:project, :public) }
diff --git a/spec/services/markup/rendering_service_spec.rb b/spec/services/markup/rendering_service_spec.rb
index 99ab87f2072..952ee33da98 100644
--- a/spec/services/markup/rendering_service_spec.rb
+++ b/spec/services/markup/rendering_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Markup::RenderingService do
+RSpec.describe Markup::RenderingService, feature_category: :projects do
describe '#execute' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) do
@@ -111,5 +111,22 @@ RSpec.describe Markup::RenderingService do
is_expected.to eq(expected_html)
end
end
+
+ context 'with reStructuredText' do
+ let(:file_name) { 'foo.rst' }
+ let(:text) { "####\nPART\n####" }
+
+ it 'returns rendered html' do
+ is_expected.to eq("<h1>PART</h1>\n\n")
+ end
+
+ context 'when input has an invalid syntax' do
+ let(:text) { "####\nPART\n##" }
+
+ it 'uses a simple formatter for html' do
+ is_expected.to eq("<p>####\n<br>PART\n<br>##</p>")
+ end
+ end
+ end
end
end
diff --git a/spec/services/mattermost/create_team_service_spec.rb b/spec/services/mattermost/create_team_service_spec.rb
new file mode 100644
index 00000000000..b9e5162aab4
--- /dev/null
+++ b/spec/services/mattermost/create_team_service_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mattermost::CreateTeamService, feature_category: :integrations do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+
+ subject { described_class.new(group, user) }
+
+ it 'creates a team' do
+ expect_next_instance_of(::Mattermost::Team) do |instance|
+ expect(instance).to receive(:create).with(name: anything, display_name: anything, type: anything)
+ end
+
+ subject.execute
+ end
+
+ it 'adds an error if a team could not be created' do
+ expect_next_instance_of(::Mattermost::Team) do |instance|
+ expect(instance).to receive(:create).and_raise(::Mattermost::ClientError, 'client error')
+ end
+
+ subject.execute
+
+ expect(group.errors).to be_present
+ end
+end
diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb
index ca5c052d032..6c0d47e98ba 100644
--- a/spec/services/members/approve_access_request_service_spec.rb
+++ b/spec/services/members/approve_access_request_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::ApproveAccessRequestService do
+RSpec.describe Members::ApproveAccessRequestService, feature_category: :subgroups do
let(:project) { create(:project, :public) }
let(:group) { create(:group, :public) }
let(:current_user) { create(:user) }
@@ -14,13 +14,17 @@ RSpec.describe Members::ApproveAccessRequestService do
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
it 'raises Gitlab::Access::AccessDeniedError' do
- expect { described_class.new(current_user, params).execute(access_requester, **opts) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect do
+ described_class.new(current_user, params).execute(access_requester, **opts)
+ end.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
shared_examples 'a service approving an access request' do
it 'succeeds' do
- expect { described_class.new(current_user, params).execute(access_requester, **opts) }.to change { source.requesters.count }.by(-1)
+ expect do
+ described_class.new(current_user, params).execute(access_requester, **opts)
+ end.to change { source.requesters.count }.by(-1)
end
it 'returns a <Source>Member' do
@@ -32,7 +36,15 @@ RSpec.describe Members::ApproveAccessRequestService do
it 'calls the method to resolve access request for the approver' do
expect_next_instance_of(described_class) do |instance|
- expect(instance).to receive(:resolve_access_request_todos).with(current_user, access_requester)
+ expect(instance).to receive(:resolve_access_request_todos).with(access_requester)
+ end
+
+ described_class.new(current_user, params).execute(access_requester, **opts)
+ end
+
+ it 'resolves the todos for the access requests' do
+ expect_next_instance_of(TodoService) do |instance|
+ expect(instance).to receive(:resolve_access_request_todos).with(access_requester)
end
described_class.new(current_user, params).execute(access_requester, **opts)
diff --git a/spec/services/members/base_service_spec.rb b/spec/services/members/base_service_spec.rb
index b2db599db9c..514c25fbc03 100644
--- a/spec/services/members/base_service_spec.rb
+++ b/spec/services/members/base_service_spec.rb
@@ -3,17 +3,16 @@
require 'spec_helper'
RSpec.describe Members::BaseService, feature_category: :projects do
- let_it_be(:current_user) { create(:user) }
let_it_be(:access_requester) { create(:group_member) }
describe '#resolve_access_request_todos' do
it 'calls the resolve_access_request_todos of todo service' do
expect_next_instance_of(TodoService) do |todo_service|
expect(todo_service)
- .to receive(:resolve_access_request_todos).with(current_user, access_requester)
+ .to receive(:resolve_access_request_todos).with(access_requester)
end
- described_class.new.send(:resolve_access_request_todos, current_user, access_requester)
+ described_class.new.send(:resolve_access_request_todos, access_requester)
end
end
end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 756e1cf403c..13f233162cd 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state, :sidekiq_inline do
+RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_cache, :clean_gitlab_redis_shared_state, :sidekiq_inline,
+ feature_category: :subgroups do
let_it_be(:source, reload: true) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:member) { create(:user) }
diff --git a/spec/services/members/creator_service_spec.rb b/spec/services/members/creator_service_spec.rb
index ad4c649086b..8191eefbe95 100644
--- a/spec/services/members/creator_service_spec.rb
+++ b/spec/services/members/creator_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::CreatorService do
+RSpec.describe Members::CreatorService, feature_category: :subgroups do
let_it_be(:source, reload: true) { create(:group, :public) }
let_it_be(:member_type) { GroupMember }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 2b956bec469..498b9576875 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
it 'resolves the access request todos for the owner' do
expect_next_instance_of(described_class) do |instance|
- expect(instance).to receive(:resolve_access_request_todos).with(current_user, member)
+ expect(instance).to receive(:resolve_access_request_todos).with(member)
end
described_class.new(current_user).execute(member, **opts)
@@ -463,16 +463,26 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
end
context 'subresources' do
- let(:user) { create(:user) }
- let(:member_user) { create(:user) }
+ let_it_be_with_reload(:user) { create(:user) }
+ let_it_be_with_reload(:member_user) { create(:user) }
+
+ let_it_be_with_reload(:group) { create(:group, :public) }
+ let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
+ let_it_be(:private_subgroup) { create(:group, :private, parent: group, name: 'private_subgroup') }
+ let_it_be(:private_subgroup_with_direct_membership) { create(:group, :private, parent: group) }
+ let_it_be_with_reload(:subsubgroup) { create(:group, parent: subgroup) }
+
+ let_it_be_with_reload(:group_project) { create(:project, :public, group: group) }
+ let_it_be_with_reload(:control_project) { create(:project, :private, group: subsubgroup) }
+ let_it_be_with_reload(:subsubproject) { create(:project, :public, group: subsubgroup) }
- let(:group) { create(:group, :public) }
- let(:subgroup) { create(:group, parent: group) }
- let(:subsubgroup) { create(:group, parent: subgroup) }
- let(:subsubproject) { create(:project, group: subsubgroup) }
+ let_it_be(:private_subgroup_project) do
+ create(:project, :private, group: private_subgroup, name: 'private_subgroup_project')
+ end
- let(:group_project) { create(:project, :public, group: group) }
- let(:control_project) { create(:project, group: subsubgroup) }
+ let_it_be(:private_subgroup_with_direct_membership_project) do
+ create(:project, :private, group: private_subgroup_with_direct_membership, name: 'private_subgroup_project')
+ end
context 'with memberships' do
before do
@@ -481,14 +491,68 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
subsubproject.add_developer(member_user)
group_project.add_developer(member_user)
control_project.add_maintainer(user)
+ private_subgroup_with_direct_membership.add_developer(member_user)
group.add_owner(user)
@group_member = create(:group_member, :developer, group: group, user: member_user)
end
+ let_it_be(:todo_in_public_group_project) do
+ create(:todo, :pending,
+ project: group_project,
+ user: member_user,
+ target: create(:issue, project: group_project)
+ )
+ end
+
+ let_it_be(:mr_in_public_group_project) do
+ create(:merge_request, source_project: group_project, assignees: [member_user])
+ end
+
+ let_it_be(:todo_in_private_subgroup_project) do
+ create(:todo, :pending,
+ project: private_subgroup_project,
+ user: member_user,
+ target: create(:issue, project: private_subgroup_project)
+ )
+ end
+
+ let_it_be(:mr_in_private_subgroup_project) do
+ create(:merge_request, source_project: private_subgroup_project, assignees: [member_user])
+ end
+
+ let_it_be(:todo_in_public_subsubgroup_project) do
+ create(:todo, :pending,
+ project: subsubproject,
+ user: member_user,
+ target: create(:issue, project: subsubproject)
+ )
+ end
+
+ let_it_be(:mr_in_public_subsubgroup_project) do
+ create(:merge_request, source_project: subsubproject, assignees: [member_user])
+ end
+
+ let_it_be(:todo_in_private_subgroup_with_direct_membership_project) do
+ create(:todo, :pending,
+ project: private_subgroup_with_direct_membership_project,
+ user: member_user,
+ target: create(:issue, project: private_subgroup_with_direct_membership_project)
+ )
+ end
+
+ let_it_be(:mr_in_private_subgroup_with_direct_membership_project) do
+ create(:merge_request,
+ source_project: private_subgroup_with_direct_membership_project,
+ assignees: [member_user]
+ )
+ end
+
context 'with skipping of subresources' do
+ subject(:execute_service) { described_class.new(user).execute(@group_member, skip_subresources: true) }
+
before do
- described_class.new(user).execute(@group_member, skip_subresources: true)
+ execute_service
end
it 'removes the group membership' do
@@ -514,11 +578,35 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
it 'does not remove the user from the control project' do
expect(control_project.members.map(&:user)).to include(user)
end
+
+ context 'todos', :sidekiq_inline do
+ it 'removes todos for which the user no longer has access' do
+ expect(member_user.todos).to include(
+ todo_in_public_group_project,
+ todo_in_public_subsubgroup_project,
+ todo_in_private_subgroup_with_direct_membership_project
+ )
+
+ expect(member_user.todos).not_to include(todo_in_private_subgroup_project)
+ end
+ end
+
+ context 'issuables', :sidekiq_inline do
+ subject(:execute_service) do
+ described_class.new(user).execute(@group_member, skip_subresources: true, unassign_issuables: true)
+ end
+
+ it 'removes assigned issuables, even in subresources' do
+ expect(member_user.assigned_merge_requests).to be_empty
+ end
+ end
end
context 'without skipping of subresources' do
+ subject(:execute_service) { described_class.new(user).execute(@group_member, skip_subresources: false) }
+
before do
- described_class.new(user).execute(@group_member, skip_subresources: false)
+ execute_service
end
it 'removes the project membership' do
@@ -544,6 +632,30 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
it 'does not remove the user from the control project' do
expect(control_project.members.map(&:user)).to include(user)
end
+
+ context 'todos', :sidekiq_inline do
+ it 'removes todos for which the user no longer has access' do
+ expect(member_user.todos).to include(
+ todo_in_public_group_project,
+ todo_in_public_subsubgroup_project
+ )
+
+ expect(member_user.todos).not_to include(
+ todo_in_private_subgroup_project,
+ todo_in_private_subgroup_with_direct_membership_project
+ )
+ end
+ end
+
+ context 'issuables', :sidekiq_inline do
+ subject(:execute_service) do
+ described_class.new(user).execute(@group_member, skip_subresources: false, unassign_issuables: true)
+ end
+
+ it 'removes assigned issuables' do
+ expect(member_user.assigned_merge_requests).to be_empty
+ end
+ end
end
end
@@ -626,4 +738,13 @@ RSpec.describe Members::DestroyService, feature_category: :subgroups do
expect(project.members.not_accepted_invitations_by_user(member_user)).to be_empty
end
end
+
+ describe '#mark_as_recursive_call' do
+ it 'marks the instance as recursive' do
+ service = described_class.new(current_user)
+ service.mark_as_recursive_call
+
+ expect(service.send(:recursive_call?)).to eq(true)
+ end
+ end
end
diff --git a/spec/services/members/groups/creator_service_spec.rb b/spec/services/members/groups/creator_service_spec.rb
index fced7195046..4c13106145e 100644
--- a/spec/services/members/groups/creator_service_spec.rb
+++ b/spec/services/members/groups/creator_service_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe Members::Groups::CreatorService do
+RSpec.describe Members::Groups::CreatorService, feature_category: :subgroups do
let_it_be(:source, reload: true) { create(:group, :public) }
+ let_it_be(:source2, reload: true) { create(:group, :public) }
let_it_be(:user) { create(:user) }
describe '.access_levels' do
@@ -16,6 +17,7 @@ RSpec.describe Members::Groups::CreatorService do
describe '.add_members' do
it_behaves_like 'bulk member creation' do
+ let_it_be(:source_type) { Group }
let_it_be(:member_type) { GroupMember }
end
end
diff --git a/spec/services/members/import_project_team_service_spec.rb b/spec/services/members/import_project_team_service_spec.rb
index 96e8db1ba73..af9b30aa0b3 100644
--- a/spec/services/members/import_project_team_service_spec.rb
+++ b/spec/services/members/import_project_team_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::ImportProjectTeamService do
+RSpec.describe Members::ImportProjectTeamService, feature_category: :subgroups do
describe '#execute' do
let_it_be(:source_project) { create(:project) }
let_it_be(:target_project) { create(:project) }
diff --git a/spec/services/members/invitation_reminder_email_service_spec.rb b/spec/services/members/invitation_reminder_email_service_spec.rb
index 768a8719d54..da23965eabe 100644
--- a/spec/services/members/invitation_reminder_email_service_spec.rb
+++ b/spec/services/members/invitation_reminder_email_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::InvitationReminderEmailService do
+RSpec.describe Members::InvitationReminderEmailService, feature_category: :subgroups do
describe 'sending invitation reminders' do
subject { described_class.new(invitation).execute }
diff --git a/spec/services/members/invite_member_builder_spec.rb b/spec/services/members/invite_member_builder_spec.rb
index 52de65364c4..e7bbec4e0ef 100644
--- a/spec/services/members/invite_member_builder_spec.rb
+++ b/spec/services/members/invite_member_builder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::InviteMemberBuilder do
+RSpec.describe Members::InviteMemberBuilder, feature_category: :subgroups do
let_it_be(:source) { create(:group) }
let_it_be(:existing_member) { create(:group_member) }
diff --git a/spec/services/members/invite_service_spec.rb b/spec/services/members/invite_service_spec.rb
index 23d4d671afc..22294b3fda5 100644
--- a/spec/services/members/invite_service_spec.rb
+++ b/spec/services/members/invite_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_shared_state, :sidekiq_inline do
+RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_shared_state, :sidekiq_inline,
+ feature_category: :subgroups do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:user) { project.first_owner }
let_it_be(:project_user) { create(:user) }
diff --git a/spec/services/members/projects/creator_service_spec.rb b/spec/services/members/projects/creator_service_spec.rb
index 5dfba7adf0f..7ec7361a285 100644
--- a/spec/services/members/projects/creator_service_spec.rb
+++ b/spec/services/members/projects/creator_service_spec.rb
@@ -2,8 +2,9 @@
require 'spec_helper'
-RSpec.describe Members::Projects::CreatorService do
+RSpec.describe Members::Projects::CreatorService, feature_category: :projects do
let_it_be(:source, reload: true) { create(:project, :public) }
+ let_it_be(:source2, reload: true) { create(:project, :public) }
let_it_be(:user) { create(:user) }
describe '.access_levels' do
@@ -16,6 +17,7 @@ RSpec.describe Members::Projects::CreatorService do
describe '.add_members' do
it_behaves_like 'bulk member creation' do
+ let_it_be(:source_type) { Project }
let_it_be(:member_type) { ProjectMember }
end
end
diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb
index 69eea2aea4b..ef8ee6492ab 100644
--- a/spec/services/members/request_access_service_spec.rb
+++ b/spec/services/members/request_access_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::RequestAccessService do
+RSpec.describe Members::RequestAccessService, feature_category: :subgroups do
let(:user) { create(:user) }
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
diff --git a/spec/services/members/standard_member_builder_spec.rb b/spec/services/members/standard_member_builder_spec.rb
index 16daff53d31..69b764f3f16 100644
--- a/spec/services/members/standard_member_builder_spec.rb
+++ b/spec/services/members/standard_member_builder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::StandardMemberBuilder do
+RSpec.describe Members::StandardMemberBuilder, feature_category: :subgroups do
let_it_be(:source) { create(:group) }
let_it_be(:existing_member) { create(:group_member) }
diff --git a/spec/services/members/unassign_issuables_service_spec.rb b/spec/services/members/unassign_issuables_service_spec.rb
index 3f7ccb7bab3..37dfbd16c56 100644
--- a/spec/services/members/unassign_issuables_service_spec.rb
+++ b/spec/services/members/unassign_issuables_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::UnassignIssuablesService do
+RSpec.describe Members::UnassignIssuablesService, feature_category: :subgroups do
let_it_be(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user, reload: true) { create(:user) }
diff --git a/spec/services/members/update_service_spec.rb b/spec/services/members/update_service_spec.rb
index 8a7f9a84c77..b94b44c8485 100644
--- a/spec/services/members/update_service_spec.rb
+++ b/spec/services/members/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Members::UpdateService do
+RSpec.describe Members::UpdateService, feature_category: :subgroups do
let_it_be(:project) { create(:project, :public) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:current_user) { create(:user) }
diff --git a/spec/services/merge_requests/add_context_service_spec.rb b/spec/services/merge_requests/add_context_service_spec.rb
index 448be27efe8..5fca2c17a3c 100644
--- a/spec/services/merge_requests/add_context_service_spec.rb
+++ b/spec/services/merge_requests/add_context_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::AddContextService do
+RSpec.describe MergeRequests::AddContextService, feature_category: :code_review_workflow do
let(:project) { create(:project, :repository) }
let(:admin) { create(:admin) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: admin) }
diff --git a/spec/services/merge_requests/add_spent_time_service_spec.rb b/spec/services/merge_requests/add_spent_time_service_spec.rb
index 1e0b3e07f26..5d6d33c14d7 100644
--- a/spec/services/merge_requests/add_spent_time_service_spec.rb
+++ b/spec/services/merge_requests/add_spent_time_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::AddSpentTimeService do
+RSpec.describe MergeRequests::AddSpentTimeService, feature_category: :code_review_workflow do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be_with_reload(:merge_request) { create(:merge_request, :simple, :unique_branches, source_project: project) }
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
index 8d1abe5ea89..1307e2be3be 100644
--- a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService do
+RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
@@ -74,10 +74,13 @@ RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService do
context 'when build belongs to a merge request pipeline' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request_event,
- ref: merge_request.merge_ref_path,
- merge_request: merge_request,
- merge_requests_as_head_pipeline: [merge_request])
+ create(
+ :ci_pipeline,
+ source: :merge_request_event,
+ ref: merge_request.merge_ref_path,
+ merge_request: merge_request,
+ merge_requests_as_head_pipeline: [merge_request]
+ )
end
let(:commit_status) { create(:ci_build, ref: merge_request.merge_ref_path, pipeline: pipeline) }
@@ -119,10 +122,13 @@ RSpec.describe ::MergeRequests::AddTodoWhenBuildFailsService do
context 'when build belongs to a merge request pipeline' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request_event,
- ref: merge_request.merge_ref_path,
- merge_request: merge_request,
- merge_requests_as_head_pipeline: [merge_request])
+ create(
+ :ci_pipeline,
+ source: :merge_request_event,
+ ref: merge_request.merge_ref_path,
+ merge_request: merge_request,
+ merge_requests_as_head_pipeline: [merge_request]
+ )
end
let(:commit_status) { create(:ci_build, ref: merge_request.merge_ref_path, pipeline: pipeline) }
diff --git a/spec/services/merge_requests/after_create_service_spec.rb b/spec/services/merge_requests/after_create_service_spec.rb
index f2823b1f0c7..50a3d49d4a3 100644
--- a/spec/services/merge_requests/after_create_service_spec.rb
+++ b/spec/services/merge_requests/after_create_service_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review_workflow do
let_it_be(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
subject(:after_create_service) do
described_class.new(project: merge_request.target_project, current_user: merge_request.author)
@@ -68,6 +69,12 @@ RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review
execute_service
end
+ it 'executes hooks with default action' do
+ expect(project).to receive(:execute_hooks)
+
+ execute_service
+ end
+
it_behaves_like 'records an onboarding progress action', :merge_request_created do
let(:namespace) { merge_request.target_project.namespace }
end
@@ -143,22 +150,6 @@ RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review
expect { execute_service }.to change { counter.read(:create) }.by(1)
end
- context 'with a milestone' do
- let(:milestone) { create(:milestone, project: merge_request.target_project) }
-
- before do
- merge_request.update!(milestone_id: milestone.id)
- end
-
- it 'deletes the cache key for milestone merge request counter', :use_clean_rails_memory_store_caching do
- expect_next_instance_of(Milestones::MergeRequestsCountService, milestone) do |service|
- expect(service).to receive(:delete_cache).and_call_original
- end
-
- execute_service
- end
- end
-
context 'todos' do
it 'does not creates todos' do
attributes = {
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index 1d6427900b9..6140021c8d2 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ApprovalService do
+RSpec.describe MergeRequests::ApprovalService, feature_category: :code_review_workflow do
describe '#execute' do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, reviewers: [user]) }
diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb
index cf405c0102e..9f82207086b 100644
--- a/spec/services/merge_requests/assign_issues_service_spec.rb
+++ b/spec/services/merge_requests/assign_issues_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::AssignIssuesService do
+RSpec.describe MergeRequests::AssignIssuesService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:issue) { create(:issue, project: project) }
@@ -37,12 +37,14 @@ RSpec.describe MergeRequests::AssignIssuesService do
it 'accepts precomputed data for closes_issues' do
issue2 = create(:issue, project: project)
- service2 = described_class.new(project: project,
- current_user: user,
- params: {
- merge_request: merge_request,
- closes_issues: [issue, issue2]
- })
+ service2 = described_class.new(
+ project: project,
+ current_user: user,
+ params: {
+ merge_request: merge_request,
+ closes_issues: [issue, issue2]
+ }
+ )
expect(service2.assignable_issues.count).to eq 2
end
diff --git a/spec/services/merge_requests/base_service_spec.rb b/spec/services/merge_requests/base_service_spec.rb
index bd907ba6015..1ca4bfe622c 100644
--- a/spec/services/merge_requests/base_service_spec.rb
+++ b/spec/services/merge_requests/base_service_spec.rb
@@ -15,6 +15,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
let_it_be(:project) { create(:project, :repository) }
+ let(:user) { project.first_owner }
let(:title) { 'Awesome merge_request' }
let(:params) do
{
@@ -25,14 +26,14 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
}
end
- subject { MergeRequests::CreateService.new(project: project, current_user: project.first_owner, params: params) }
-
describe '#execute_hooks' do
+ subject { MergeRequests::CreateService.new(project: project, current_user: user, params: params).execute }
+
shared_examples 'enqueues Jira sync worker' do
specify :aggregate_failures do
expect(JiraConnect::SyncMergeRequestWorker).to receive(:perform_async).with(kind_of(Numeric), kind_of(Numeric)).and_call_original
Sidekiq::Testing.fake! do
- expect { subject.execute }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
+ expect { subject }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
end
end
end
@@ -40,7 +41,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
shared_examples 'does not enqueue Jira sync worker' do
it do
Sidekiq::Testing.fake! do
- expect { subject.execute }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
+ expect { subject }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
end
end
end
@@ -53,7 +54,20 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
context 'MR contains Jira issue key' do
let(:title) { 'Awesome merge_request with issue JIRA-123' }
- it_behaves_like 'enqueues Jira sync worker'
+ it_behaves_like 'does not enqueue Jira sync worker'
+
+ context 'for UpdateService' do
+ subject { MergeRequests::UpdateService.new(project: project, current_user: user, params: params).execute(merge_request) }
+
+ let(:merge_request) do
+ create(:merge_request, :simple, title: 'Old title',
+ assignee_ids: [user.id],
+ source_project: project,
+ author: user)
+ end
+
+ it_behaves_like 'enqueues Jira sync worker'
+ end
end
context 'MR does not contain Jira issue key' do
@@ -69,13 +83,13 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
describe `#create_pipeline_for` do
let_it_be(:merge_request) { create(:merge_request) }
- subject { MergeRequests::ExampleService.new(project: project, current_user: project.first_owner, params: params) }
+ subject { MergeRequests::ExampleService.new(project: project, current_user: user, params: params) }
context 'async: false' do
it 'creates a pipeline directly' do
expect(MergeRequests::CreatePipelineService)
.to receive(:new)
- .with(hash_including(project: project, current_user: project.first_owner, params: { allow_duplicate: false }))
+ .with(hash_including(project: project, current_user: user, params: { allow_duplicate: false }))
.and_call_original
expect(MergeRequests::CreatePipelineWorker).not_to receive(:perform_async)
@@ -86,7 +100,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
it 'passes :allow_duplicate as true' do
expect(MergeRequests::CreatePipelineService)
.to receive(:new)
- .with(hash_including(project: project, current_user: project.first_owner, params: { allow_duplicate: true }))
+ .with(hash_including(project: project, current_user: user, params: { allow_duplicate: true }))
.and_call_original
expect(MergeRequests::CreatePipelineWorker).not_to receive(:perform_async)
@@ -100,7 +114,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
expect(MergeRequests::CreatePipelineService).not_to receive(:new)
expect(MergeRequests::CreatePipelineWorker)
.to receive(:perform_async)
- .with(project.id, project.first_owner.id, merge_request.id, { "allow_duplicate" => false })
+ .with(project.id, user.id, merge_request.id, { "allow_duplicate" => false })
.and_call_original
Sidekiq::Testing.fake! do
@@ -113,7 +127,7 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
expect(MergeRequests::CreatePipelineService).not_to receive(:new)
expect(MergeRequests::CreatePipelineWorker)
.to receive(:perform_async)
- .with(project.id, project.first_owner.id, merge_request.id, { "allow_duplicate" => true })
+ .with(project.id, user.id, merge_request.id, { "allow_duplicate" => true })
.and_call_original
Sidekiq::Testing.fake! do
@@ -123,4 +137,8 @@ RSpec.describe MergeRequests::BaseService, feature_category: :code_review_workfl
end
end
end
+
+ describe '#constructor_container_arg' do
+ it { expect(described_class.constructor_container_arg("some-value")).to eq({ project: "some-value" }) }
+ end
end
diff --git a/spec/services/merge_requests/cleanup_refs_service_spec.rb b/spec/services/merge_requests/cleanup_refs_service_spec.rb
index e8690ae5bf2..960b8101c36 100644
--- a/spec/services/merge_requests/cleanup_refs_service_spec.rb
+++ b/spec/services/merge_requests/cleanup_refs_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::CleanupRefsService do
+RSpec.describe MergeRequests::CleanupRefsService, feature_category: :code_review_workflow do
describe '.schedule' do
let(:merge_request) { create(:merge_request) }
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 2c0817550c6..25c75ae7244 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -88,7 +88,11 @@ RSpec.describe MergeRequests::CloseService, feature_category: :code_review_workf
end
it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do
- expect { execute }
+ expect do
+ execute
+
+ BatchLoader::Executor.clear_current
+ end
.to change { project.open_merge_requests_count }.from(1).to(0)
end
diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb
index 5132eac0158..5eb53b1bcba 100644
--- a/spec/services/merge_requests/conflicts/list_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/list_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Conflicts::ListService do
+RSpec.describe MergeRequests::Conflicts::ListService, feature_category: :code_review_workflow do
describe '#can_be_resolved_in_ui?' do
def create_merge_request(source_branch, target_branch = 'conflict-start')
create(:merge_request, source_branch: source_branch, target_branch: target_branch, merge_status: :unchecked) do |mr|
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index 0abc70f71b0..002a07ff14e 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Conflicts::ResolveService do
+RSpec.describe MergeRequests::Conflicts::ResolveService, feature_category: :code_review_workflow do
include ProjectForksHelper
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
@@ -12,15 +12,22 @@ RSpec.describe MergeRequests::Conflicts::ResolveService do
end
let(:merge_request) do
- create(:merge_request,
- source_branch: 'conflict-resolvable', source_project: project,
- target_branch: 'conflict-start')
+ create(
+ :merge_request,
+ source_branch: 'conflict-resolvable',
+ source_project: project,
+ target_branch: 'conflict-start'
+ )
end
let(:merge_request_from_fork) do
- create(:merge_request,
- source_branch: 'conflict-resolvable-fork', source_project: forked_project,
- target_branch: 'conflict-start', target_project: project)
+ create(
+ :merge_request,
+ source_branch: 'conflict-resolvable-fork',
+ source_project: forked_project,
+ target_branch: 'conflict-start',
+ target_project: project
+ )
end
describe '#execute' do
diff --git a/spec/services/merge_requests/create_approval_event_service_spec.rb b/spec/services/merge_requests/create_approval_event_service_spec.rb
index 3d41ace11a7..4876c992337 100644
--- a/spec/services/merge_requests/create_approval_event_service_spec.rb
+++ b/spec/services/merge_requests/create_approval_event_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::CreateApprovalEventService do
+RSpec.describe MergeRequests::CreateApprovalEventService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb
index f11e3d0d1df..9c2321a2f16 100644
--- a/spec/services/merge_requests/create_pipeline_service_spec.rb
+++ b/spec/services/merge_requests/create_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::CreatePipelineService, :clean_gitlab_redis_cache do
+RSpec.describe MergeRequests::CreatePipelineService, :clean_gitlab_redis_cache, feature_category: :code_review_workflow do
include ProjectForksHelper
let_it_be(:project, refind: true) { create(:project, :repository) }
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 394fc269ac3..7705278f30d 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, feature_category: :code_review_workflow do
include ProjectForksHelper
+ include AfterNextHelpers
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
@@ -27,7 +28,6 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
before do
project.add_maintainer(user)
project.add_developer(user2)
- allow(service).to receive(:execute_hooks)
end
it 'creates an MR' do
@@ -38,13 +38,18 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
expect(merge_request.merge_params['force_remove_source_branch']).to eq('1')
end
- it 'executes hooks with default action' do
- expect(service).to have_received(:execute_hooks).with(merge_request)
+ it 'does not execute hooks' do
+ expect(project).not_to receive(:execute_hooks)
+
+ service.execute
end
it 'refreshes the number of open merge requests', :use_clean_rails_memory_store_caching do
- expect { service.execute }
- .to change { project.open_merge_requests_count }.from(0).to(1)
+ expect do
+ service.execute
+
+ BatchLoader::Executor.clear_current
+ end.to change { project.open_merge_requests_count }.from(0).to(1)
end
it 'creates exactly 1 create MR event', :sidekiq_might_not_need_inline do
@@ -245,10 +250,13 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
context "when branch pipeline was created before a merge request pipline has been created" do
before do
- create(:ci_pipeline, project: merge_request.source_project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch,
- tag: false)
+ create(
+ :ci_pipeline,
+ project: merge_request.source_project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ tag: false
+ )
merge_request
end
@@ -333,6 +341,19 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
end
end
+ context 'with a milestone' do
+ let(:milestone) { create(:milestone, project: project) }
+
+ let(:opts) { { title: 'Awesome merge_request', source_branch: 'feature', target_branch: 'master', milestone_id: milestone.id } }
+
+ it 'deletes the cache key for milestone merge request counter' do
+ expect_next(Milestones::MergeRequestsCountService, milestone)
+ .to receive(:delete_cache).and_call_original
+
+ expect(merge_request).to be_persisted
+ end
+ end
+
it_behaves_like 'reviewer_ids filter' do
let(:execute) { service.execute }
end
@@ -428,19 +449,29 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state, f
}
end
- it 'invalidates open merge request counter for assignees when merge request is assigned' do
+ before do
project.add_maintainer(user2)
+ end
+ it 'invalidates open merge request counter for assignees when merge request is assigned' do
described_class.new(project: project, current_user: user, params: opts).execute
expect(user2.assigned_open_merge_requests_count).to eq 1
end
+
+ it 'records the assignee assignment event', :sidekiq_inline do
+ mr = described_class.new(project: project, current_user: user, params: opts).execute.reload
+
+ expect(mr.assignment_events).to match([have_attributes(user_id: user2.id, action: 'add')])
+ end
end
context "when issuable feature is private" do
before do
- project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
- merge_requests_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(
+ issues_access_level: ProjectFeature::PRIVATE,
+ merge_requests_access_level: ProjectFeature::PRIVATE
+ )
end
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
diff --git a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
index d2070a466b1..d9e60911ada 100644
--- a/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
+++ b/spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe MergeRequests::DeleteNonLatestDiffsService, :clean_gitlab_redis_shared_state do
+RSpec.describe MergeRequests::DeleteNonLatestDiffsService, :clean_gitlab_redis_shared_state,
+ feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
let!(:subject) { described_class.new(merge_request) }
diff --git a/spec/services/merge_requests/execute_approval_hooks_service_spec.rb b/spec/services/merge_requests/execute_approval_hooks_service_spec.rb
index 863c47e8191..9f460648b05 100644
--- a/spec/services/merge_requests/execute_approval_hooks_service_spec.rb
+++ b/spec/services/merge_requests/execute_approval_hooks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ExecuteApprovalHooksService do
+RSpec.describe MergeRequests::ExecuteApprovalHooksService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index 5027acbba0a..f2dbc02f12c 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -2,15 +2,17 @@
require 'spec_helper'
-RSpec.describe MergeRequests::FfMergeService do
+RSpec.describe MergeRequests::FfMergeService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:merge_request) do
- create(:merge_request,
- source_branch: 'flatten-dir',
- target_branch: 'improve/awesome',
- assignees: [user2],
- author: create(:user))
+ create(
+ :merge_request,
+ source_branch: 'flatten-dir',
+ target_branch: 'improve/awesome',
+ assignees: [user2],
+ author: create(:user)
+ )
end
let(:project) { merge_request.project }
diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb
index 5f81e1728fa..31b3e513a51 100644
--- a/spec/services/merge_requests/get_urls_service_spec.rb
+++ b/spec/services/merge_requests/get_urls_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe MergeRequests::GetUrlsService do
+RSpec.describe MergeRequests::GetUrlsService, feature_category: :code_review_workflow do
include ProjectForksHelper
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/services/merge_requests/handle_assignees_change_service_spec.rb b/spec/services/merge_requests/handle_assignees_change_service_spec.rb
index 3db3efedb84..951e59afe7f 100644
--- a/spec/services/merge_requests/handle_assignees_change_service_spec.rb
+++ b/spec/services/merge_requests/handle_assignees_change_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::HandleAssigneesChangeService do
+RSpec.describe MergeRequests::HandleAssigneesChangeService, feature_category: :code_review_workflow do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee) { create(:user) }
diff --git a/spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb b/spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb
index 8437876c3cf..172c2133168 100644
--- a/spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb
+++ b/spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MarkReviewerReviewedService do
+RSpec.describe MergeRequests::MarkReviewerReviewedService, feature_category: :code_review_workflow do
let(:current_user) { create(:user) }
let(:merge_request) { create(:merge_request, reviewers: [current_user]) }
let(:reviewer) { merge_request.merge_request_reviewers.find_by(user_id: current_user.id) }
diff --git a/spec/services/merge_requests/merge_orchestration_service_spec.rb b/spec/services/merge_requests/merge_orchestration_service_spec.rb
index ebcd2f0e277..389956bf258 100644
--- a/spec/services/merge_requests/merge_orchestration_service_spec.rb
+++ b/spec/services/merge_requests/merge_orchestration_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MergeOrchestrationService do
+RSpec.describe MergeRequests::MergeOrchestrationService, feature_category: :code_review_workflow do
let_it_be(:maintainer) { create(:user) }
let(:merge_params) { { sha: merge_request.diff_head_sha } }
@@ -10,8 +10,11 @@ RSpec.describe MergeRequests::MergeOrchestrationService do
let(:service) { described_class.new(project, user, merge_params) }
let!(:merge_request) do
- create(:merge_request, source_project: project, source_branch: 'feature',
- target_project: project, target_branch: 'master')
+ create(
+ :merge_request,
+ source_project: project, source_branch: 'feature',
+ target_project: project, target_branch: 'master'
+ )
end
shared_context 'fresh repository' do
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index d3bf203d6bb..c77cf288f56 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MergeService do
+RSpec.describe MergeRequests::MergeService, feature_category: :code_review_workflow do
include ExclusiveLeaseHelpers
let_it_be(:user) { create(:user) }
@@ -69,12 +69,15 @@ RSpec.describe MergeRequests::MergeService do
let(:merge_request) do
# A merge request with 5 commits
- create(:merge_request, :simple,
- author: user2,
- assignees: [user2],
- squash: true,
- source_branch: 'improve/awesome',
- target_branch: 'fix')
+ create(
+ :merge_request,
+ :simple,
+ author: user2,
+ assignees: [user2],
+ squash: true,
+ source_branch: 'improve/awesome',
+ target_branch: 'fix'
+ )
end
it 'merges the merge request with squashed commits' do
@@ -147,12 +150,15 @@ RSpec.describe MergeRequests::MergeService do
context 'when an invalid sha is passed' do
let(:merge_request) do
- create(:merge_request, :simple,
- author: user2,
- assignees: [user2],
- squash: true,
- source_branch: 'improve/awesome',
- target_branch: 'fix')
+ create(
+ :merge_request,
+ :simple,
+ author: user2,
+ assignees: [user2],
+ squash: true,
+ source_branch: 'improve/awesome',
+ target_branch: 'fix'
+ )
end
let(:merge_params) do
@@ -351,9 +357,12 @@ RSpec.describe MergeRequests::MergeService do
service.execute(merge_request)
expect(merge_request.merge_error).to eq(error_message)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
end
@@ -366,9 +375,12 @@ RSpec.describe MergeRequests::MergeService do
service.execute(merge_request)
expect(merge_request.merge_error).to eq(described_class::GENERIC_ERROR_MESSAGE)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
it 'logs and saves error if user is not authorized' do
@@ -394,9 +406,12 @@ RSpec.describe MergeRequests::MergeService do
service.execute(merge_request)
expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook')
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
it 'logs and saves error if commit is not created' do
@@ -408,9 +423,12 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request).to be_open
expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.merge_error).to include(described_class::GENERIC_ERROR_MESSAGE)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(described_class::GENERIC_ERROR_MESSAGE)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(described_class::GENERIC_ERROR_MESSAGE)
+ )
+ )
end
context 'when squashing is required' do
@@ -429,9 +447,12 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.squash_commit_sha).to be_nil
expect(merge_request.merge_error).to include(error_message)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
end
@@ -452,9 +473,12 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.squash_commit_sha).to be_nil
expect(merge_request.merge_error).to include(error_message)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
it 'logs and saves error if there is an PreReceiveError exception' do
@@ -470,9 +494,12 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.squash_commit_sha).to be_nil
expect(merge_request.merge_error).to include('Something went wrong during merge pre-receive hook')
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
context 'when fast-forward merge is not allowed' do
@@ -494,9 +521,12 @@ RSpec.describe MergeRequests::MergeService do
expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.squash_commit_sha).to be_nil
expect(merge_request.merge_error).to include(error_message)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
end
end
@@ -513,9 +543,12 @@ RSpec.describe MergeRequests::MergeService do
it 'logs and saves error' do
service.execute(merge_request)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
end
@@ -527,9 +560,12 @@ RSpec.describe MergeRequests::MergeService do
it 'logs and saves error' do
service.execute(merge_request)
- expect(Gitlab::AppLogger).to have_received(:error)
- .with(hash_including(merge_request_info: merge_request.to_reference(full: true),
- message: a_string_matching(error_message)))
+ expect(Gitlab::AppLogger).to have_received(:error).with(
+ hash_including(
+ merge_request_info: merge_request.to_reference(full: true),
+ message: a_string_matching(error_message)
+ )
+ )
end
context 'when passing `skip_discussions_check: true` as `options` parameter' do
diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb
index 19fac3b5095..8200f60b072 100644
--- a/spec/services/merge_requests/merge_to_ref_service_spec.rb
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MergeToRefService do
+RSpec.describe MergeRequests::MergeToRefService, feature_category: :code_review_workflow do
shared_examples_for 'MergeService for target ref' do
it 'target_ref has the same state of target branch' do
repo = merge_request.target_project.repository
@@ -210,11 +210,14 @@ RSpec.describe MergeRequests::MergeToRefService do
let(:merge_request) { create(:merge_request, assignees: [user], author: user) }
let(:project) { merge_request.project }
let!(:todo) do
- create(:todo, :assigned,
- project: project,
- author: user,
- user: user,
- target: merge_request)
+ create(
+ :todo,
+ :assigned,
+ project: project,
+ author: user,
+ user: user,
+ target: merge_request
+ )
end
before do
@@ -258,8 +261,10 @@ RSpec.describe MergeRequests::MergeToRefService do
context 'when first merge happens' do
let(:merge_request) do
- create(:merge_request, source_project: project, source_branch: 'feature',
- target_project: project, target_branch: 'master')
+ create(
+ :merge_request, source_project: project, source_branch: 'feature',
+ target_project: project, target_branch: 'master'
+ )
end
it_behaves_like 'successfully merges to ref with merge method' do
@@ -269,8 +274,11 @@ RSpec.describe MergeRequests::MergeToRefService do
context 'when second merge happens' do
let(:merge_request) do
- create(:merge_request, source_project: project, source_branch: 'improve/awesome',
- target_project: project, target_branch: 'master')
+ create(
+ :merge_request,
+ source_project: project, source_branch: 'improve/awesome',
+ target_project: project, target_branch: 'master'
+ )
end
it_behaves_like 'successfully merges to ref with merge method' do
diff --git a/spec/services/merge_requests/mergeability/check_base_service_spec.rb b/spec/services/merge_requests/mergeability/check_base_service_spec.rb
index f07522b43cb..806bde61c23 100644
--- a/spec/services/merge_requests/mergeability/check_base_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckBaseService do
+RSpec.describe MergeRequests::Mergeability::CheckBaseService, feature_category: :code_review_workflow do
subject(:check_base_service) { described_class.new(merge_request: merge_request, params: params) }
let(:merge_request) { double }
diff --git a/spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb b/spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb
index 6cc1079c94a..b6ee1049bb9 100644
--- a/spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckBrokenStatusService do
+RSpec.describe MergeRequests::Mergeability::CheckBrokenStatusService, feature_category: :code_review_workflow do
subject(:check_broken_status) { described_class.new(merge_request: merge_request, params: {}) }
let(:merge_request) { build(:merge_request) }
diff --git a/spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb b/spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb
index def3cb0ca28..cf835cf70a3 100644
--- a/spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckCiStatusService do
+RSpec.describe MergeRequests::Mergeability::CheckCiStatusService, feature_category: :code_review_workflow do
subject(:check_ci_status) { described_class.new(merge_request: merge_request, params: params) }
let(:merge_request) { build(:merge_request) }
diff --git a/spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb b/spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb
index 9f107ce046a..a3b77558ec3 100644
--- a/spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckDiscussionsStatusService do
+RSpec.describe MergeRequests::Mergeability::CheckDiscussionsStatusService, feature_category: :code_review_workflow do
subject(:check_discussions_status) { described_class.new(merge_request: merge_request, params: params) }
let(:merge_request) { build(:merge_request) }
diff --git a/spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb b/spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb
index e9363e5d676..cb624705a02 100644
--- a/spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckDraftStatusService do
+RSpec.describe MergeRequests::Mergeability::CheckDraftStatusService, feature_category: :code_review_workflow do
subject(:check_draft_status) { described_class.new(merge_request: merge_request, params: {}) }
let(:merge_request) { build(:merge_request) }
diff --git a/spec/services/merge_requests/mergeability/check_open_status_service_spec.rb b/spec/services/merge_requests/mergeability/check_open_status_service_spec.rb
index 936524b020a..53ad77ea4df 100644
--- a/spec/services/merge_requests/mergeability/check_open_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/check_open_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::CheckOpenStatusService do
+RSpec.describe MergeRequests::Mergeability::CheckOpenStatusService, feature_category: :code_review_workflow do
subject(:check_open_status) { described_class.new(merge_request: merge_request, params: {}) }
let(:merge_request) { build(:merge_request) }
diff --git a/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb b/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb
index 5722bb79cc5..66bcb948cb6 100644
--- a/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::MergeRequests::Mergeability::DetailedMergeStatusService do
+RSpec.describe ::MergeRequests::Mergeability::DetailedMergeStatusService, feature_category: :code_review_workflow do
subject(:detailed_merge_status) { described_class.new(merge_request: merge_request).execute }
context 'when merge status is cannot_be_merged_rechecking' do
@@ -17,7 +17,19 @@ RSpec.describe ::MergeRequests::Mergeability::DetailedMergeStatusService do
let(:merge_request) { create(:merge_request, merge_status: :preparing) }
it 'returns :checking' do
- expect(detailed_merge_status).to eq(:checking)
+ allow(merge_request.merge_request_diff).to receive(:persisted?).and_return(false)
+
+ expect(detailed_merge_status).to eq(:preparing)
+ end
+ end
+
+ context 'when merge status is preparing and merge request diff is persisted' do
+ let(:merge_request) { create(:merge_request, merge_status: :preparing) }
+
+ it 'returns :checking' do
+ allow(merge_request.merge_request_diff).to receive(:persisted?).and_return(true)
+
+ expect(detailed_merge_status).to eq(:mergeable)
end
end
@@ -72,9 +84,14 @@ RSpec.describe ::MergeRequests::Mergeability::DetailedMergeStatusService do
context 'when pipeline exists' do
before do
- create(:ci_pipeline, ci_status, merge_request: merge_request,
- project: merge_request.project, sha: merge_request.source_branch_sha,
- head_pipeline_of: merge_request)
+ create(
+ :ci_pipeline,
+ ci_status,
+ merge_request: merge_request,
+ project: merge_request.project,
+ sha: merge_request.source_branch_sha,
+ head_pipeline_of: merge_request
+ )
end
context 'when the pipeline is running' do
diff --git a/spec/services/merge_requests/mergeability/logger_spec.rb b/spec/services/merge_requests/mergeability/logger_spec.rb
index 3e2a1e9f9fd..1f56b6bebdb 100644
--- a/spec/services/merge_requests/mergeability/logger_spec.rb
+++ b/spec/services/merge_requests/mergeability/logger_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::Logger, :request_store do
+RSpec.describe MergeRequests::Mergeability::Logger, :request_store, feature_category: :code_review_workflow do
let_it_be(:merge_request) { create(:merge_request) }
subject(:logger) { described_class.new(merge_request: merge_request) }
diff --git a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
index c56b38bccc1..bfff582994b 100644
--- a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::Mergeability::RunChecksService, :clean_gitlab_redis_cache do
+RSpec.describe MergeRequests::Mergeability::RunChecksService, :clean_gitlab_redis_cache, feature_category: :code_review_workflow do
subject(:run_checks) { described_class.new(merge_request: merge_request, params: {}) }
describe '#execute' do
diff --git a/spec/services/merge_requests/mergeability_check_service_spec.rb b/spec/services/merge_requests/mergeability_check_service_spec.rb
index ee23238314e..82ef8440380 100644
--- a/spec/services/merge_requests/mergeability_check_service_spec.rb
+++ b/spec/services/merge_requests/mergeability_check_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shared_state do
+RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shared_state, feature_category: :code_review_workflow do
shared_examples_for 'unmergeable merge request' do
it 'updates or keeps merge status as cannot_be_merged' do
subject
@@ -158,9 +158,11 @@ RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shar
threads = execute_within_threads(amount: 3, retry_lease: false)
results = threads.map { |t| [t.value.status, t.value.message] }
- expect(results).to contain_exactly([:error, 'Failed to obtain a lock'],
- [:error, 'Failed to obtain a lock'],
- [:success, nil])
+ expect(results).to contain_exactly(
+ [:error, 'Failed to obtain a lock'],
+ [:error, 'Failed to obtain a lock'],
+ [:success, nil]
+ )
end
end
end
@@ -183,11 +185,13 @@ RSpec.describe MergeRequests::MergeabilityCheckService, :clean_gitlab_redis_shar
context 'when it cannot be merged on git' do
let(:merge_request) do
- create(:merge_request,
- merge_status: :unchecked,
- source_branch: 'conflict-resolvable',
- source_project: project,
- target_branch: 'conflict-start')
+ create(
+ :merge_request,
+ merge_status: :unchecked,
+ source_branch: 'conflict-resolvable',
+ source_project: project,
+ target_branch: 'conflict-start'
+ )
end
it 'returns ServiceResponse.error and keeps merge status as cannot_be_merged' do
diff --git a/spec/services/merge_requests/migrate_external_diffs_service_spec.rb b/spec/services/merge_requests/migrate_external_diffs_service_spec.rb
index 6ea8626ba73..c7f78dfa992 100644
--- a/spec/services/merge_requests/migrate_external_diffs_service_spec.rb
+++ b/spec/services/merge_requests/migrate_external_diffs_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::MigrateExternalDiffsService do
+RSpec.describe MergeRequests::MigrateExternalDiffsService, feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
let(:diff) { merge_request.merge_request_diff }
diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb
index e486daae15e..f7526c169bd 100644
--- a/spec/services/merge_requests/post_merge_service_spec.rb
+++ b/spec/services/merge_requests/post_merge_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::PostMergeService do
+RSpec.describe MergeRequests::PostMergeService, feature_category: :code_review_workflow do
include ProjectForksHelper
let_it_be(:user) { create(:user) }
@@ -23,7 +23,11 @@ RSpec.describe MergeRequests::PostMergeService do
# Cache the counter before the MR changed state.
project.open_merge_requests_count
- expect { subject }.to change { project.open_merge_requests_count }.from(1).to(0)
+ expect do
+ subject
+
+ BatchLoader::Executor.clear_current
+ end.to change { project.open_merge_requests_count }.from(1).to(0)
end
it 'updates metrics' do
diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb
index 03f3d56cdd2..49ec8b09939 100644
--- a/spec/services/merge_requests/push_options_handler_service_spec.rb
+++ b/spec/services/merge_requests/push_options_handler_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::PushOptionsHandlerService do
+RSpec.describe MergeRequests::PushOptionsHandlerService, feature_category: :source_code_management do
include ProjectForksHelper
let_it_be(:parent_group) { create(:group, :public) }
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 704dc1f9000..c8b9ab5a34e 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -8,10 +8,12 @@ RSpec.describe MergeRequests::RebaseService, feature_category: :source_code_mana
let(:user) { create(:user) }
let(:rebase_jid) { 'fake-rebase-jid' }
let(:merge_request) do
- create :merge_request,
- source_branch: 'feature_conflict',
- target_branch: 'master',
- rebase_jid: rebase_jid
+ create(
+ :merge_request,
+ source_branch: 'feature_conflict',
+ target_branch: 'master',
+ rebase_jid: rebase_jid
+ )
end
let(:project) { merge_request.project }
@@ -102,8 +104,9 @@ RSpec.describe MergeRequests::RebaseService, feature_category: :source_code_mana
end
it 'returns an error' do
- expect(service.execute(merge_request)).to match(status: :error,
- message: described_class::REBASE_ERROR)
+ expect(service.execute(merge_request)).to match(
+ status: :error, message: described_class::REBASE_ERROR
+ )
end
it 'logs the error' do
@@ -154,8 +157,9 @@ RSpec.describe MergeRequests::RebaseService, feature_category: :source_code_mana
end
it 'returns an error' do
- expect(service.execute(merge_request)).to match(status: :error,
- message: described_class::REBASE_ERROR)
+ expect(service.execute(merge_request)).to match(
+ status: :error, message: described_class::REBASE_ERROR
+ )
end
end
@@ -215,9 +219,11 @@ RSpec.describe MergeRequests::RebaseService, feature_category: :source_code_mana
message: 'Add new file to target',
branch_name: 'master')
- create(:merge_request,
- source_branch: 'master', source_project: forked_project,
- target_branch: 'master', target_project: project)
+ create(
+ :merge_request,
+ source_branch: 'master', source_project: forked_project,
+ target_branch: 'master', target_project: project
+ )
end
it 'rebases source branch', :sidekiq_might_not_need_inline do
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 0814942b6b7..4d533b67690 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -19,43 +19,53 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
@project = create(:project, :repository, namespace: group)
@fork_project = fork_project(@project, @user, repository: true)
- @merge_request = create(:merge_request,
- source_project: @project,
- source_branch: 'master',
- target_branch: 'feature',
- target_project: @project,
- auto_merge_enabled: true,
- auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
- merge_user: @user)
-
- @another_merge_request = create(:merge_request,
- source_project: @project,
- source_branch: 'master',
- target_branch: 'test',
- target_project: @project,
- auto_merge_enabled: true,
- auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
- merge_user: @user)
-
- @fork_merge_request = create(:merge_request,
- source_project: @fork_project,
- source_branch: 'master',
- target_branch: 'feature',
- target_project: @project)
-
- @build_failed_todo = create(:todo,
- :build_failed,
- user: @user,
- project: @project,
- target: @merge_request,
- author: @user)
-
- @fork_build_failed_todo = create(:todo,
- :build_failed,
- user: @user,
- project: @project,
- target: @merge_request,
- author: @user)
+ @merge_request = create(
+ :merge_request,
+ source_project: @project,
+ source_branch: 'master',
+ target_branch: 'feature',
+ target_project: @project,
+ auto_merge_enabled: true,
+ auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
+ merge_user: @user
+ )
+
+ @another_merge_request = create(
+ :merge_request,
+ source_project: @project,
+ source_branch: 'master',
+ target_branch: 'test',
+ target_project: @project,
+ auto_merge_enabled: true,
+ auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
+ merge_user: @user
+ )
+
+ @fork_merge_request = create(
+ :merge_request,
+ source_project: @fork_project,
+ source_branch: 'master',
+ target_branch: 'feature',
+ target_project: @project
+ )
+
+ @build_failed_todo = create(
+ :todo,
+ :build_failed,
+ user: @user,
+ project: @project,
+ target: @merge_request,
+ author: @user
+ )
+
+ @fork_build_failed_todo = create(
+ :todo,
+ :build_failed,
+ user: @user,
+ project: @project,
+ target: @merge_request,
+ author: @user
+ )
@commits = @merge_request.commits
@@ -109,6 +119,14 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
expect(@fork_build_failed_todo).to be_done
end
+ it 'triggers mergeRequestMergeStatusUpdated GraphQL subscription conditionally' do
+ expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@merge_request)
+ expect(GraphqlTriggers).to receive(:merge_request_merge_status_updated).with(@another_merge_request)
+ expect(GraphqlTriggers).not_to receive(:merge_request_merge_status_updated).with(@fork_merge_request)
+
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ end
+
context 'when a merge error exists' do
let(:error_message) { 'This is a merge error' }
@@ -298,10 +316,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
context "when branch pipeline was created before a detaced merge request pipeline has been created" do
before do
- create(:ci_pipeline, project: @merge_request.source_project,
- sha: @merge_request.diff_head_sha,
- ref: @merge_request.source_branch,
- tag: false)
+ create(
+ :ci_pipeline,
+ project: @merge_request.source_project,
+ sha: @merge_request.diff_head_sha,
+ ref: @merge_request.source_branch,
+ tag: false
+ )
subject
end
@@ -332,9 +353,11 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
context 'when the pipeline should be skipped' do
it 'saves a skipped detached merge request pipeline' do
- project.repository.create_file(@user, 'new-file.txt', 'A new file',
- message: '[skip ci] This is a test',
- branch_name: 'master')
+ project.repository.create_file(
+ @user, 'new-file.txt', 'A new file',
+ message: '[skip ci] This is a test',
+ branch_name: 'master'
+ )
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
@@ -444,11 +467,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
context 'when an MR to be closed was empty already' do
let!(:empty_fork_merge_request) do
- create(:merge_request,
- source_project: @fork_project,
- source_branch: 'master',
- target_branch: 'master',
- target_project: @project)
+ create(
+ :merge_request,
+ source_project: @fork_project,
+ source_branch: 'master',
+ target_branch: 'master',
+ target_project: @project
+ )
end
before do
@@ -589,23 +614,33 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
context 'forked projects with the same source branch name as target branch' do
let!(:first_commit) do
- @fork_project.repository.create_file(@user, 'test1.txt', 'Test data',
- message: 'Test commit',
- branch_name: 'master')
+ @fork_project.repository.create_file(
+ @user,
+ 'test1.txt',
+ 'Test data',
+ message: 'Test commit',
+ branch_name: 'master'
+ )
end
let!(:second_commit) do
- @fork_project.repository.create_file(@user, 'test2.txt', 'More test data',
- message: 'Second test commit',
- branch_name: 'master')
+ @fork_project.repository.create_file(
+ @user,
+ 'test2.txt',
+ 'More test data',
+ message: 'Second test commit',
+ branch_name: 'master'
+ )
end
let!(:forked_master_mr) do
- create(:merge_request,
- source_project: @fork_project,
- source_branch: 'master',
- target_branch: 'master',
- target_project: @project)
+ create(
+ :merge_request,
+ source_project: @fork_project,
+ source_branch: 'master',
+ target_branch: 'master',
+ target_project: @project
+ )
end
let(:force_push_commit) { @project.commit('feature').id }
@@ -639,9 +674,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
end
it 'does not increase the diff count for a new push to target branch' do
- new_commit = @project.repository.create_file(@user, 'new-file.txt', 'A new file',
- message: 'This is a test',
- branch_name: 'master')
+ new_commit = @project.repository.create_file(
+ @user,
+ 'new-file.txt',
+ 'A new file',
+ message: 'This is a test',
+ branch_name: 'master'
+ )
expect do
service.new(project: @project, current_user: @user).execute(@newrev, new_commit, 'refs/heads/master')
@@ -713,10 +752,12 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
CommitCollection.new(project, [commit], 'close-by-commit')
)
- merge_request = create(:merge_request,
- target_branch: 'master',
- source_branch: 'close-by-commit',
- source_project: project)
+ merge_request = create(
+ :merge_request,
+ target_branch: 'master',
+ source_branch: 'close-by-commit',
+ source_project: project
+ )
refresh_service = service.new(project: project, current_user: user)
allow(refresh_service).to receive(:execute_hooks)
@@ -735,11 +776,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
CommitCollection.new(forked_project, [commit], 'close-by-commit')
)
- merge_request = create(:merge_request,
- target_branch: 'master',
- target_project: project,
- source_branch: 'close-by-commit',
- source_project: forked_project)
+ merge_request = create(
+ :merge_request,
+ target_branch: 'master',
+ target_project: project,
+ source_branch: 'close-by-commit',
+ source_project: forked_project
+ )
refresh_service = service.new(project: forked_project, current_user: user)
allow(refresh_service).to receive(:execute_hooks)
@@ -759,11 +802,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
end
it 'marks the merge request as draft from fixup commits' do
- fixup_merge_request = create(:merge_request,
- source_project: @project,
- source_branch: 'wip',
- target_branch: 'master',
- target_project: @project)
+ fixup_merge_request = create(
+ :merge_request,
+ source_project: @project,
+ source_branch: 'wip',
+ target_branch: 'master',
+ target_project: @project
+ )
commits = fixup_merge_request.commits
oldrev = commits.last.id
newrev = commits.first.id
@@ -778,11 +823,13 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
end
it 'references the commit that caused the draft status' do
- draft_merge_request = create(:merge_request,
- source_project: @project,
- source_branch: 'wip',
- target_branch: 'master',
- target_project: @project)
+ draft_merge_request = create(
+ :merge_request,
+ source_project: @project,
+ source_branch: 'wip',
+ target_branch: 'master',
+ target_project: @project
+ )
commits = draft_merge_request.commits
oldrev = commits.last.id
@@ -896,22 +943,23 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
end
let_it_be(:merge_request, refind: true) do
- create(:merge_request,
- author: author,
- source_project: source_project,
- source_branch: 'feature',
- target_branch: 'master',
- target_project: target_project,
- auto_merge_enabled: true,
- auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
- merge_user: user)
+ create(
+ :merge_request,
+ author: author,
+ source_project: source_project,
+ source_branch: 'feature',
+ target_branch: 'master',
+ target_project: target_project,
+ auto_merge_enabled: true,
+ auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
+ merge_user: user
+ )
end
let_it_be(:newrev) do
- target_project
- .repository
- .create_file(user, 'test1.txt', 'Test data',
- message: 'Test commit', branch_name: 'master')
+ target_project.repository.create_file(
+ user, 'test1.txt', 'Test data', message: 'Test commit', branch_name: 'master'
+ )
end
let_it_be(:oldrev) do
diff --git a/spec/services/merge_requests/reload_diffs_service_spec.rb b/spec/services/merge_requests/reload_diffs_service_spec.rb
index 6cf8af1fcf6..77056cbe541 100644
--- a/spec/services/merge_requests/reload_diffs_service_spec.rb
+++ b/spec/services/merge_requests/reload_diffs_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_caching do
+RSpec.describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_caching,
+ feature_category: :code_review_workflow do
let(:current_user) { create(:user) }
let(:merge_request) { create(:merge_request) }
let(:subject) { described_class.new(merge_request, current_user) }
@@ -18,10 +19,9 @@ RSpec.describe MergeRequests::ReloadDiffsService, :use_clean_rails_memory_store_
new_diff_refs = merge_request.diff_refs
expect(merge_request).to receive(:create_merge_request_diff).and_return(new_diff)
- expect(merge_request).to receive(:update_diff_discussion_positions)
- .with(old_diff_refs: old_diff_refs,
- new_diff_refs: new_diff_refs,
- current_user: current_user)
+ expect(merge_request).to receive(:update_diff_discussion_positions).with(
+ old_diff_refs: old_diff_refs, new_diff_refs: new_diff_refs, current_user: current_user
+ )
subject.execute
end
diff --git a/spec/services/merge_requests/reload_merge_head_diff_service_spec.rb b/spec/services/merge_requests/reload_merge_head_diff_service_spec.rb
index 20b5cf5e3a1..1c315f12221 100644
--- a/spec/services/merge_requests/reload_merge_head_diff_service_spec.rb
+++ b/spec/services/merge_requests/reload_merge_head_diff_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ReloadMergeHeadDiffService do
+RSpec.describe MergeRequests::ReloadMergeHeadDiffService, feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
subject { described_class.new(merge_request).execute }
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index b9df31b6727..7399b29d06e 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ReopenService do
+RSpec.describe MergeRequests::ReopenService, feature_category: :code_review_workflow do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:guest) { create(:user) }
@@ -92,7 +92,11 @@ RSpec.describe MergeRequests::ReopenService do
it 'refreshes the number of open merge requests for a valid MR' do
service = described_class.new(project: project, current_user: user)
- expect { service.execute(merge_request) }
+ expect do
+ service.execute(merge_request)
+
+ BatchLoader::Executor.clear_current
+ end
.to change { project.open_merge_requests_count }.from(0).to(1)
end
diff --git a/spec/services/merge_requests/request_review_service_spec.rb b/spec/services/merge_requests/request_review_service_spec.rb
index 1d3f92b083f..ef96bf11e0b 100644
--- a/spec/services/merge_requests/request_review_service_spec.rb
+++ b/spec/services/merge_requests/request_review_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::RequestReviewService do
+RSpec.describe MergeRequests::RequestReviewService, feature_category: :code_review_workflow do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, reviewers: [user]) }
diff --git a/spec/services/merge_requests/resolve_todos_service_spec.rb b/spec/services/merge_requests/resolve_todos_service_spec.rb
index 53bd259f0f4..de7ddbea8bb 100644
--- a/spec/services/merge_requests/resolve_todos_service_spec.rb
+++ b/spec/services/merge_requests/resolve_todos_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ResolveTodosService do
+RSpec.describe MergeRequests::ResolveTodosService, feature_category: :code_review_workflow do
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb
index 2f191f2ee44..c3a99431dcc 100644
--- a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb
+++ b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::ResolvedDiscussionNotificationService do
+RSpec.describe MergeRequests::ResolvedDiscussionNotificationService, feature_category: :code_review_workflow do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
let(:project) { merge_request.project }
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
index 471bb03f18c..1afca466fb5 100644
--- a/spec/services/merge_requests/squash_service_spec.rb
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::SquashService do
+RSpec.describe MergeRequests::SquashService, feature_category: :source_code_management do
let(:service) { described_class.new(project: project, current_user: user, params: { merge_request: merge_request }) }
let(:user) { project.first_owner }
let(:project) { create(:project, :repository) }
@@ -13,21 +13,27 @@ RSpec.describe MergeRequests::SquashService do
end
let(:merge_request_with_one_commit) do
- create(:merge_request,
- source_branch: 'feature', source_project: project,
- target_branch: 'master', target_project: project)
+ create(
+ :merge_request,
+ source_branch: 'feature', source_project: project,
+ target_branch: 'master', target_project: project
+ )
end
let(:merge_request_with_only_new_files) do
- create(:merge_request,
- source_branch: 'video', source_project: project,
- target_branch: 'master', target_project: project)
+ create(
+ :merge_request,
+ source_branch: 'video', source_project: project,
+ target_branch: 'master', target_project: project
+ )
end
let(:merge_request_with_large_files) do
- create(:merge_request,
- source_branch: 'squash-large-files', source_project: project,
- target_branch: 'master', target_project: project)
+ create(
+ :merge_request,
+ source_branch: 'squash-large-files', source_project: project,
+ target_branch: 'master', target_project: project
+ )
end
shared_examples 'the squash succeeds' do
diff --git a/spec/services/merge_requests/update_assignees_service_spec.rb b/spec/services/merge_requests/update_assignees_service_spec.rb
index 2d80d75a262..85d749de83c 100644
--- a/spec/services/merge_requests/update_assignees_service_spec.rb
+++ b/spec/services/merge_requests/update_assignees_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::UpdateAssigneesService do
+RSpec.describe MergeRequests::UpdateAssigneesService, feature_category: :code_review_workflow do
include AfterNextHelpers
let_it_be(:group) { create(:group, :public) }
@@ -12,13 +12,17 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
let_it_be(:user3) { create(:user) }
let_it_be_with_reload(:merge_request) do
- create(:merge_request, :simple, :unique_branches,
- title: 'Old title',
- description: "FYI #{user2.to_reference}",
- assignee_ids: [user3.id],
- source_project: project,
- target_project: project,
- author: create(:user))
+ create(
+ :merge_request,
+ :simple,
+ :unique_branches,
+ title: 'Old title',
+ description: "FYI #{user2.to_reference}",
+ assignee_ids: [user3.id],
+ source_project: project,
+ target_project: project,
+ author: create(:user)
+ )
end
before do
@@ -107,12 +111,16 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
.with(merge_request, [user3], execute_hooks: true)
end
- other_mr = create(:merge_request, :simple, :unique_branches,
- title: merge_request.title,
- description: merge_request.description,
- assignee_ids: merge_request.assignee_ids,
- source_project: merge_request.project,
- author: merge_request.author)
+ other_mr = create(
+ :merge_request,
+ :simple,
+ :unique_branches,
+ title: merge_request.title,
+ description: merge_request.description,
+ assignee_ids: merge_request.assignee_ids,
+ source_project: merge_request.project,
+ author: merge_request.author
+ )
update_service = ::MergeRequests::UpdateService.new(project: project, current_user: user, params: opts)
diff --git a/spec/services/merge_requests/update_reviewers_service_spec.rb b/spec/services/merge_requests/update_reviewers_service_spec.rb
index 9f935e1cecf..1ae20e8c29c 100644
--- a/spec/services/merge_requests/update_reviewers_service_spec.rb
+++ b/spec/services/merge_requests/update_reviewers_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::UpdateReviewersService do
+RSpec.describe MergeRequests::UpdateReviewersService, feature_category: :code_review_workflow do
include AfterNextHelpers
let_it_be(:group) { create(:group, :public) }
@@ -12,13 +12,17 @@ RSpec.describe MergeRequests::UpdateReviewersService do
let_it_be(:user3) { create(:user) }
let_it_be_with_reload(:merge_request) do
- create(:merge_request, :simple, :unique_branches,
- title: 'Old title',
- description: "FYI #{user2.to_reference}",
- reviewer_ids: [user3.id],
- source_project: project,
- target_project: project,
- author: create(:user))
+ create(
+ :merge_request,
+ :simple,
+ :unique_branches,
+ title: 'Old title',
+ description: "FYI #{user2.to_reference}",
+ reviewer_ids: [user3.id],
+ source_project: project,
+ target_project: project,
+ author: create(:user)
+ )
end
before do
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index e20ebf18e7c..012eb5f6fca 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -15,11 +15,15 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
let(:milestone) { create(:milestone, project: project) }
let(:merge_request) do
- create(:merge_request, :simple, title: 'Old title',
- description: "FYI #{user2.to_reference}",
- assignee_ids: [user3.id],
- source_project: project,
- author: create(:user))
+ create(
+ :merge_request,
+ :simple,
+ title: 'Old title',
+ description: "FYI #{user2.to_reference}",
+ assignee_ids: [user3.id],
+ source_project: project,
+ author: create(:user)
+ )
end
before do
@@ -782,6 +786,27 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
expect(user3.assigned_open_merge_requests_count).to eq 0
expect(user2.assigned_open_merge_requests_count).to eq 1
end
+
+ it 'records the assignment history', :sidekiq_inline do
+ original_assignee = merge_request.assignees.first!
+
+ update_merge_request(assignee_ids: [user2.id])
+
+ expected_events = [
+ have_attributes({
+ merge_request_id: merge_request.id,
+ user_id: original_assignee.id,
+ action: 'remove'
+ }),
+ have_attributes({
+ merge_request_id: merge_request.id,
+ user_id: user2.id,
+ action: 'add'
+ })
+ ]
+
+ expect(merge_request.assignment_events).to match_array(expected_events)
+ end
end
context 'when the target branch changes' do
@@ -1166,10 +1191,12 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
let(:source_project) { fork_project(target_project, nil, repository: true) }
let(:user) { create(:user) }
let(:merge_request) do
- create(:merge_request,
- source_project: source_project,
- source_branch: 'fixes',
- target_project: target_project)
+ create(
+ :merge_request,
+ source_project: source_project,
+ source_branch: 'fixes',
+ target_project: target_project
+ )
end
before do
@@ -1201,10 +1228,12 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
let(:source_project) { fork_project(target_project, nil, repository: true) }
let(:user) { target_project.first_owner }
let(:merge_request) do
- create(:merge_request,
- source_project: source_project,
- source_branch: 'fixes',
- target_project: target_project)
+ create(
+ :merge_request,
+ source_project: source_project,
+ source_branch: 'fixes',
+ target_project: target_project
+ )
end
it "cannot be done by members of the target project when they don't have access" do
@@ -1222,10 +1251,12 @@ RSpec.describe MergeRequests::UpdateService, :mailer, feature_category: :code_re
context 'updating `target_branch`' do
let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: 'mr-b',
- target_branch: 'mr-a')
+ create(
+ :merge_request,
+ source_project: project,
+ source_branch: 'mr-b',
+ target_branch: 'mr-a'
+ )
end
it 'updates to master' do
diff --git a/spec/services/metrics/dashboard/annotations/create_service_spec.rb b/spec/services/metrics/dashboard/annotations/create_service_spec.rb
index 8f5484fcabe..2bcfa54ead7 100644
--- a/spec/services/metrics/dashboard/annotations/create_service_spec.rb
+++ b/spec/services/metrics/dashboard/annotations/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::Annotations::CreateService do
+RSpec.describe Metrics::Dashboard::Annotations::CreateService, feature_category: :metrics do
let_it_be(:user) { create(:user) }
let(:description) { 'test annotation' }
diff --git a/spec/services/metrics/dashboard/annotations/delete_service_spec.rb b/spec/services/metrics/dashboard/annotations/delete_service_spec.rb
index ec2bd3772bf..557d6d95767 100644
--- a/spec/services/metrics/dashboard/annotations/delete_service_spec.rb
+++ b/spec/services/metrics/dashboard/annotations/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::Annotations::DeleteService do
+RSpec.describe Metrics::Dashboard::Annotations::DeleteService, feature_category: :metrics do
let(:user) { create(:user) }
let(:service_instance) { described_class.new(user, annotation) }
diff --git a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
index 47e5557105b..bb11b905a7c 100644
--- a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memory_store_caching, feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
@@ -92,10 +92,6 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter
]
- it_behaves_like 'valid dashboard cloning process',
- ::Metrics::Dashboard::SelfMonitoringDashboardService::DASHBOARD_PATH,
- [::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter]
-
context 'selected branch already exists' do
let(:branch) { 'existing_branch' }
diff --git a/spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb b/spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb
index f2e32d5eb35..beed23a366f 100644
--- a/spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::ClusterDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::ClusterDashboardService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb b/spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb
index dbb89af45d0..5d63505e5cc 100644
--- a/spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::ClusterMetricsEmbedService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::ClusterMetricsEmbedService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb b/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
index afeb1646005..940daa38ae7 100644
--- a/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::CustomDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::CustomDashboardService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
index 127cec6275c..8117296b048 100644
--- a/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::CustomMetricEmbedService do
+RSpec.describe Metrics::Dashboard::CustomMetricEmbedService, feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:project, reload: true) { build(:project) }
diff --git a/spec/services/metrics/dashboard/default_embed_service_spec.rb b/spec/services/metrics/dashboard/default_embed_service_spec.rb
index 647778eadc1..6ef248f6b09 100644
--- a/spec/services/metrics/dashboard/default_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/default_embed_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::DefaultEmbedService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::DefaultEmbedService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:project) { build(:project) }
diff --git a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
index 5eb8f24266c..1643f552a70 100644
--- a/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/dynamic_embed_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::DynamicEmbedService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::DynamicEmbedService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:project) { build(:project) }
diff --git a/spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb b/spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb
index 2905e4599f3..25812a492b2 100644
--- a/spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::GitlabAlertEmbedService do
+RSpec.describe Metrics::Dashboard::GitlabAlertEmbedService, feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:alert) { create(:prometheus_alert) }
diff --git a/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
index 5263fd40a40..877a455ea44 100644
--- a/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::GrafanaMetricEmbedService do
+RSpec.describe Metrics::Dashboard::GrafanaMetricEmbedService, feature_category: :metrics do
include MetricsDashboardHelpers
include ReactiveCachingHelpers
include GrafanaApiHelpers
diff --git a/spec/services/metrics/dashboard/panel_preview_service_spec.rb b/spec/services/metrics/dashboard/panel_preview_service_spec.rb
index 787c61cc918..584be717d7c 100644
--- a/spec/services/metrics/dashboard/panel_preview_service_spec.rb
+++ b/spec/services/metrics/dashboard/panel_preview_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::PanelPreviewService do
+RSpec.describe Metrics::Dashboard::PanelPreviewService, feature_category: :metrics do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:panel_yml) do
@@ -64,18 +64,20 @@ RSpec.describe Metrics::Dashboard::PanelPreviewService do
Gitlab::Config::Loader::Yaml::DataTooLargeError,
Gitlab::Config::Loader::FormatError
].each do |error_class|
- before do
- allow_next_instance_of(::Gitlab::Metrics::Dashboard::Processor) do |processor|
- allow(processor).to receive(:process).and_raise(error_class.new('error'))
+ context "with #{error_class}" do
+ before do
+ allow_next_instance_of(::Gitlab::Metrics::Dashboard::Processor) do |processor|
+ allow(processor).to receive(:process).and_raise(error_class.new('error'))
+ end
end
- end
- it 'returns error service response' do
- expect(service_response.error?).to be_truthy
- end
+ it 'returns error service response' do
+ expect(service_response.error?).to be_truthy
+ end
- it 'returns error message' do
- expect(service_response.message).to eq('error')
+ it 'returns error message' do
+ expect(service_response.message).to eq('error')
+ end
end
end
end
diff --git a/spec/services/metrics/dashboard/pod_dashboard_service_spec.rb b/spec/services/metrics/dashboard/pod_dashboard_service_spec.rb
index 0ea812e93ee..a6fcb6b4842 100644
--- a/spec/services/metrics/dashboard/pod_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/pod_dashboard_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::PodDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::PodDashboardService, :use_clean_rails_memory_store_caching,
+ feature_category: :cell do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb b/spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb
deleted file mode 100644
index d0cefdbeb30..00000000000
--- a/spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Metrics::Dashboard::SelfMonitoringDashboardService, :use_clean_rails_memory_store_caching do
- include MetricsDashboardHelpers
-
- let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project) }
- let_it_be(:environment) { create(:environment, project: project) }
-
- let(:service_params) { [project, user, { environment: environment }] }
-
- before do
- project.add_maintainer(user) if user
- stub_application_setting(self_monitoring_project_id: project.id)
- end
-
- subject do
- described_class.new(service_params)
- end
-
- describe '#raw_dashboard' do
- it_behaves_like '#raw_dashboard raises error if dashboard loading fails'
- end
-
- describe '#get_dashboard' do
- let(:service_call) { subject.get_dashboard }
-
- subject { described_class.new(*service_params) }
-
- it_behaves_like 'valid dashboard service response'
- it_behaves_like 'raises error for users with insufficient permissions'
- it_behaves_like 'caches the unprocessed dashboard for subsequent calls'
- it_behaves_like 'refreshes cache when dashboard_version is changed'
- it_behaves_like 'updates gitlab_metrics_dashboard_processing_time_ms metric'
-
- it_behaves_like 'dashboard_version contains SHA256 hash of dashboard file content' do
- let(:dashboard_path) { described_class::DASHBOARD_PATH }
- let(:dashboard_version) { subject.send(:dashboard_version) }
- end
- end
-
- describe '.all_dashboard_paths' do
- it 'returns the dashboard attributes' do
- all_dashboards = described_class.all_dashboard_paths(project)
-
- expect(all_dashboards).to eq(
- [{
- path: described_class::DASHBOARD_PATH,
- display_name: described_class::DASHBOARD_NAME,
- default: true,
- system_dashboard: true,
- out_of_the_box_dashboard: true
- }]
- )
- end
- end
-
- describe '.valid_params?' do
- subject { described_class.valid_params?(params) }
-
- context 'with environment' do
- let(:params) { { environment: environment } }
-
- it { is_expected.to be_truthy }
- end
-
- context 'with dashboard_path' do
- let(:params) { { dashboard_path: self_monitoring_dashboard_path } }
-
- it { is_expected.to be_truthy }
- end
-
- context 'with a different dashboard selected' do
- let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
- let(:params) { { dashboard_path: dashboard_path, environment: environment } }
-
- it { is_expected.to be_falsey }
- end
-
- context 'missing environment and dashboard_path' do
- let(:params) { {} }
-
- it { is_expected.to be_falsey }
- end
- end
-end
diff --git a/spec/services/metrics/dashboard/system_dashboard_service_spec.rb b/spec/services/metrics/dashboard/system_dashboard_service_spec.rb
index e1c6aaeec66..b08b980e50e 100644
--- a/spec/services/metrics/dashboard/system_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/system_dashboard_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::SystemDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::SystemDashboardService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/metrics/dashboard/transient_embed_service_spec.rb b/spec/services/metrics/dashboard/transient_embed_service_spec.rb
index 53ea83c33d6..1e3ccde6ae3 100644
--- a/spec/services/metrics/dashboard/transient_embed_service_spec.rb
+++ b/spec/services/metrics/dashboard/transient_embed_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::TransientEmbedService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::TransientEmbedService, :use_clean_rails_memory_store_caching,
+ feature_category: :metrics do
let_it_be(:project) { build(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:environment) { create(:environment, project: project) }
diff --git a/spec/services/metrics/dashboard/update_dashboard_service_spec.rb b/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
index 148005480ea..15bbe9f9364 100644
--- a/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/update_dashboard_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_store_caching do
+RSpec.describe Metrics::Dashboard::UpdateDashboardService, :use_clean_rails_memory_store_caching, feature_category: :metrics do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
diff --git a/spec/services/metrics/global_metrics_update_service_spec.rb b/spec/services/metrics/global_metrics_update_service_spec.rb
new file mode 100644
index 00000000000..38c7f9282d9
--- /dev/null
+++ b/spec/services/metrics/global_metrics_update_service_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Metrics::GlobalMetricsUpdateService, :prometheus, feature_category: :metrics do
+ describe '#execute' do
+ it 'sets gitlab_maintenance_mode gauge metric' do
+ metric = subject.maintenance_mode_metric
+ expect(Gitlab).to receive(:maintenance_mode?).and_return(true)
+
+ expect { subject.execute }.to change { metric.get }.from(0).to(1)
+ end
+ end
+end
diff --git a/spec/services/metrics/sample_metrics_service_spec.rb b/spec/services/metrics/sample_metrics_service_spec.rb
index b94345500f0..3442b4303db 100644
--- a/spec/services/metrics/sample_metrics_service_spec.rb
+++ b/spec/services/metrics/sample_metrics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::SampleMetricsService do
+RSpec.describe Metrics::SampleMetricsService, feature_category: :metrics do
describe 'query' do
let(:range_start) { '2019-12-02T23:31:45.000Z' }
let(:range_end) { '2019-12-03T00:01:45.000Z' }
diff --git a/spec/services/metrics/users_starred_dashboards/create_service_spec.rb b/spec/services/metrics/users_starred_dashboards/create_service_spec.rb
index 1435e39e458..e08bdca8410 100644
--- a/spec/services/metrics/users_starred_dashboards/create_service_spec.rb
+++ b/spec/services/metrics/users_starred_dashboards/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::UsersStarredDashboards::CreateService do
+RSpec.describe Metrics::UsersStarredDashboards::CreateService, feature_category: :metrics do
let_it_be(:user) { create(:user) }
let(:dashboard_path) { 'config/prometheus/common_metrics.yml' }
diff --git a/spec/services/metrics/users_starred_dashboards/delete_service_spec.rb b/spec/services/metrics/users_starred_dashboards/delete_service_spec.rb
index 5cdffe681eb..8c4bcecc239 100644
--- a/spec/services/metrics/users_starred_dashboards/delete_service_spec.rb
+++ b/spec/services/metrics/users_starred_dashboards/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Metrics::UsersStarredDashboards::DeleteService do
+RSpec.describe Metrics::UsersStarredDashboards::DeleteService, feature_category: :metrics do
subject(:service_instance) { described_class.new(user, project, dashboard_path) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 53751b40667..f362c8da642 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::CloseService do
+RSpec.describe Milestones::CloseService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
diff --git a/spec/services/milestones/closed_issues_count_service_spec.rb b/spec/services/milestones/closed_issues_count_service_spec.rb
index a3865d08972..f0ed0872c2d 100644
--- a/spec/services/milestones/closed_issues_count_service_spec.rb
+++ b/spec/services/milestones/closed_issues_count_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Milestones::ClosedIssuesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Milestones::ClosedIssuesCountService, :use_clean_rails_memory_store_caching,
+ feature_category: :team_planning do
let(:project) { create(:project) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
index 93ca4ff653f..78cb05532eb 100644
--- a/spec/services/milestones/create_service_spec.rb
+++ b/spec/services/milestones/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::CreateService do
+RSpec.describe Milestones::CreateService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb
index 6c08b7db43a..209177c348b 100644
--- a/spec/services/milestones/destroy_service_spec.rb
+++ b/spec/services/milestones/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::DestroyService do
+RSpec.describe Milestones::DestroyService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) }
diff --git a/spec/services/milestones/find_or_create_service_spec.rb b/spec/services/milestones/find_or_create_service_spec.rb
index 1bcaf578441..8a72778a22a 100644
--- a/spec/services/milestones/find_or_create_service_spec.rb
+++ b/spec/services/milestones/find_or_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::FindOrCreateService do
+RSpec.describe Milestones::FindOrCreateService, feature_category: :team_planning do
describe '#execute' do
subject(:service) { described_class.new(project, user, params) }
diff --git a/spec/services/milestones/issues_count_service_spec.rb b/spec/services/milestones/issues_count_service_spec.rb
index c944055e4e7..a80b27822b6 100644
--- a/spec/services/milestones/issues_count_service_spec.rb
+++ b/spec/services/milestones/issues_count_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Milestones::IssuesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Milestones::IssuesCountService, :use_clean_rails_memory_store_caching,
+ feature_category: :team_planning do
let(:project) { create(:project) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/services/milestones/merge_requests_count_service_spec.rb b/spec/services/milestones/merge_requests_count_service_spec.rb
index aecc7d5ef52..00b7b95aeb6 100644
--- a/spec/services/milestones/merge_requests_count_service_spec.rb
+++ b/spec/services/milestones/merge_requests_count_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Milestones::MergeRequestsCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Milestones::MergeRequestsCountService, :use_clean_rails_memory_store_caching,
+ feature_category: :team_planning do
let_it_be(:project) { create(:project, :empty_repo) }
let_it_be(:milestone) { create(:milestone, project: project) }
diff --git a/spec/services/milestones/promote_service_spec.rb b/spec/services/milestones/promote_service_spec.rb
index 8f4201d8d94..203ac2d3f40 100644
--- a/spec/services/milestones/promote_service_spec.rb
+++ b/spec/services/milestones/promote_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::PromoteService do
+RSpec.describe Milestones::PromoteService, feature_category: :team_planning do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:user) { create(:user) }
diff --git a/spec/services/milestones/transfer_service_spec.rb b/spec/services/milestones/transfer_service_spec.rb
index de02226661c..ea65f713902 100644
--- a/spec/services/milestones/transfer_service_spec.rb
+++ b/spec/services/milestones/transfer_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Milestones::TransferService do
+RSpec.describe Milestones::TransferService, feature_category: :team_planning do
describe '#execute' do
subject(:service) { described_class.new(user, old_group, project) }
diff --git a/spec/services/milestones/update_service_spec.rb b/spec/services/milestones/update_service_spec.rb
index 85fd89c11ac..76110af2514 100644
--- a/spec/services/milestones/update_service_spec.rb
+++ b/spec/services/milestones/update_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Milestones::UpdateService do
+RSpec.describe Milestones::UpdateService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { build(:user) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/services/ml/experiment_tracking/candidate_repository_spec.rb b/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
index e3c05178025..079c36c9613 100644
--- a/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
+++ b/spec/services/ml/experiment_tracking/candidate_repository_spec.rb
@@ -2,23 +2,23 @@
require 'spec_helper'
-RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
+RSpec.describe ::Ml::ExperimentTracking::CandidateRepository, feature_category: :experimentation_activation do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:experiment) { create(:ml_experiments, user: user, project: project) }
- let_it_be(:candidate) { create(:ml_candidates, user: user, experiment: experiment) }
+ let_it_be(:candidate) { create(:ml_candidates, user: user, experiment: experiment, project: project) }
let(:repository) { described_class.new(project, user) }
- describe '#by_iid' do
- let(:iid) { candidate.iid }
+ describe '#by_eid' do
+ let(:eid) { candidate.eid }
- subject { repository.by_iid(iid) }
+ subject { repository.by_eid(eid) }
it { is_expected.to eq(candidate) }
context 'when iid does not exist' do
- let(:iid) { non_existing_record_iid.to_s }
+ let(:eid) { non_existing_record_iid.to_s }
it { is_expected.to be_nil }
end
@@ -38,7 +38,7 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
it 'creates the candidate' do
expect(subject.start_time).to eq(1234)
- expect(subject.iid).not_to be_nil
+ expect(subject.eid).not_to be_nil
expect(subject.end_time).to be_nil
expect(subject.name).to eq('some_candidate')
end
@@ -166,6 +166,14 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
expect { repository.add_tag!(candidate, 'new', props[:value]) }.to raise_error(ActiveRecord::RecordInvalid)
end
end
+
+ context 'when tag starts with gitlab.' do
+ it 'calls HandleCandidateGitlabMetadataService' do
+ expect(Ml::ExperimentTracking::HandleCandidateGitlabMetadataService).to receive(:new).and_call_original
+
+ repository.add_tag!(candidate, 'gitlab.CI_USER_ID', user.id)
+ end
+ end
end
describe "#add_params" do
@@ -291,5 +299,15 @@ RSpec.describe ::Ml::ExperimentTracking::CandidateRepository do
expect { subject }.to change { candidate.reload.metadata.size }.by(1)
end
end
+
+ context 'when tags is nil' do
+ let(:tags) { nil }
+
+ it 'does not handle gitlab tags' do
+ expect(repository).not_to receive(:handle_gitlab_tags)
+
+ subject
+ end
+ end
end
end
diff --git a/spec/services/ml/experiment_tracking/experiment_repository_spec.rb b/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
index c3c716b831a..3c645fa84b4 100644
--- a/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
+++ b/spec/services/ml/experiment_tracking/experiment_repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Ml::ExperimentTracking::ExperimentRepository do
+RSpec.describe ::Ml::ExperimentTracking::ExperimentRepository, feature_category: :experimentation_activation do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:experiment) { create(:ml_experiments, user: user, project: project) }
diff --git a/spec/services/ml/experiment_tracking/handle_candidate_gitlab_metadata_service_spec.rb b/spec/services/ml/experiment_tracking/handle_candidate_gitlab_metadata_service_spec.rb
new file mode 100644
index 00000000000..f0e7c241d5d
--- /dev/null
+++ b/spec/services/ml/experiment_tracking/handle_candidate_gitlab_metadata_service_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Ml::ExperimentTracking::HandleCandidateGitlabMetadataService, feature_category: :experimentation_activation do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let(:metadata) { [] }
+ let(:candidate) { create(:ml_candidates, project: project, user: user) }
+
+ describe 'execute' do
+ subject { described_class.new(candidate, metadata).execute }
+
+ context 'when metadata includes gitlab.CI_JOB_ID', 'and gitlab.CI_JOB_ID is valid' do
+ let(:metadata) do
+ [
+ { key: 'gitlab.CI_JOB_ID', value: build.id.to_s }
+ ]
+ end
+
+ it 'updates candidate correctly', :aggregate_failures do
+ subject
+
+ expect(candidate.ci_build).to eq(build)
+ end
+ end
+
+ context 'when metadata includes gitlab.CI_JOB_ID and gitlab.CI_JOB_ID is invalid' do
+ let(:metadata) { [{ key: 'gitlab.CI_JOB_ID', value: non_existing_record_id.to_s }] }
+
+ it 'raises error' do
+ expect { subject }
+ .to raise_error(ArgumentError, 'gitlab.CI_JOB_ID must refer to an existing build')
+ end
+ end
+ end
+end
diff --git a/spec/services/namespace_settings/update_service_spec.rb b/spec/services/namespace_settings/update_service_spec.rb
index e0f32cb3821..5f1ff6746bc 100644
--- a/spec/services/namespace_settings/update_service_spec.rb
+++ b/spec/services/namespace_settings/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NamespaceSettings::UpdateService do
+RSpec.describe NamespaceSettings::UpdateService, feature_category: :subgroups do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:settings) { {} }
diff --git a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb
index b44c256802f..8a2ecd5c3e0 100644
--- a/spec/services/namespaces/in_product_marketing_emails_service_spec.rb
+++ b/spec/services/namespaces/in_product_marketing_emails_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute' do
+RSpec.describe Namespaces::InProductMarketingEmailsService, '#execute', feature_category: :purchase do
subject(:execute_service) { described_class.new(track, interval).execute }
let(:track) { :create }
diff --git a/spec/services/namespaces/package_settings/update_service_spec.rb b/spec/services/namespaces/package_settings/update_service_spec.rb
index 10926c5ef57..e21c9a8f1b9 100644
--- a/spec/services/namespaces/package_settings/update_service_spec.rb
+++ b/spec/services/namespaces/package_settings/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Namespaces::PackageSettings::UpdateService do
+RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:namespace) { create(:group) }
diff --git a/spec/services/namespaces/statistics_refresher_service_spec.rb b/spec/services/namespaces/statistics_refresher_service_spec.rb
index 2d5f9235bd4..750f98615cc 100644
--- a/spec/services/namespaces/statistics_refresher_service_spec.rb
+++ b/spec/services/namespaces/statistics_refresher_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Namespaces::StatisticsRefresherService, '#execute' do
+RSpec.describe Namespaces::StatisticsRefresherService, '#execute', feature_category: :subgroups do
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:projects) { create_list(:project, 5, namespace: group) }
diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb
index ad244f62292..1cbbb68205d 100644
--- a/spec/services/note_summary_spec.rb
+++ b/spec/services/note_summary_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NoteSummary do
+RSpec.describe NoteSummary, feature_category: :code_review_workflow do
let(:project) { build(:project) }
let(:noteable) { build(:issue) }
let(:user) { build(:user) }
diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb
index 67d8b37f809..b300a1621de 100644
--- a/spec/services/notes/build_service_spec.rb
+++ b/spec/services/notes/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::BuildService do
+RSpec.describe Notes::BuildService, feature_category: :team_planning do
include AdminModeHelper
let_it_be(:project) { create(:project, :repository) }
@@ -67,10 +67,12 @@ RSpec.describe Notes::BuildService do
context 'personal snippet note' do
def reply(note, user = other_user)
- described_class.new(nil,
- user,
- note: 'Test',
- in_reply_to_discussion_id: note.discussion_id).execute
+ described_class.new(
+ nil,
+ user,
+ note: 'Test',
+ in_reply_to_discussion_id: note.discussion_id
+ ).execute
end
let_it_be(:snippet_author) { noteable_author }
diff --git a/spec/services/notes/copy_service_spec.rb b/spec/services/notes/copy_service_spec.rb
index 2fa9a462bb9..a7b5e37ccce 100644
--- a/spec/services/notes/copy_service_spec.rb
+++ b/spec/services/notes/copy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::CopyService do
+RSpec.describe Notes::CopyService, feature_category: :team_planning do
describe '#initialize' do
let_it_be(:noteable) { create(:issue) }
@@ -25,8 +25,10 @@ RSpec.describe Notes::CopyService do
context 'simple notes' do
let!(:notes) do
[
- create(:note, noteable: from_noteable, project: from_noteable.project,
- created_at: 2.weeks.ago, updated_at: 1.week.ago),
+ create(
+ :note, noteable: from_noteable, project: from_noteable.project,
+ created_at: 2.weeks.ago, updated_at: 1.week.ago
+ ),
create(:note, noteable: from_noteable, project: from_noteable.project),
create(:note, system: true, noteable: from_noteable, project: from_noteable.project)
]
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 1ee9e51433e..240d81bb485 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -108,7 +108,6 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { issue.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_comment' }
@@ -124,10 +123,6 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
let(:execute_create_service) { described_class.new(project, user, opts).execute }
- before do
- stub_feature_flags(notes_create_service_tracking: false)
- end
-
it 'tracks commit comment usage data', :clean_gitlab_redis_shared_state do
expect(counter).to receive(:count).with(:create, 'Commit').and_call_original
@@ -141,7 +136,6 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
let(:action) { 'create_commit_comment' }
let(:label) { 'counts.commit_comment' }
let(:namespace) { project.namespace }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase4 }
end
end
@@ -157,7 +151,9 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
.and_call_original
expect do
execute_create_service
- end.to change { counter.unique_events(event_names: event, start_date: 1.day.ago, end_date: 1.day.from_now) }.by(1)
+ end.to change {
+ counter.unique_events(event_names: event, start_date: Date.today.beginning_of_week, end_date: 1.week.from_now)
+ }.by(1)
end
it 'does not track merge request usage data' do
@@ -178,22 +174,45 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
create(:merge_request, source_project: project_with_repo, target_project: project_with_repo)
end
+ let(:new_opts) { opts.merge(noteable_type: 'MergeRequest', noteable_id: merge_request.id) }
+
+ it 'calls MergeRequests::MarkReviewerReviewedService service' do
+ expect_next_instance_of(
+ MergeRequests::MarkReviewerReviewedService,
+ project: project_with_repo, current_user: user
+ ) do |service|
+ expect(service).to receive(:execute).with(merge_request)
+ end
+
+ described_class.new(project_with_repo, user, new_opts).execute
+ end
+
+ it 'does not call MergeRequests::MarkReviewerReviewedService service when skip_set_reviewed is true' do
+ expect(MergeRequests::MarkReviewerReviewedService).not_to receive(:new)
+
+ described_class.new(project_with_repo, user, new_opts).execute(skip_set_reviewed: true)
+ end
+
context 'noteable highlight cache clearing' do
let(:position) do
- Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
- new_path: "files/ruby/popen.rb",
- old_line: nil,
- new_line: 14,
- diff_refs: merge_request.diff_refs)
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: merge_request.diff_refs
+ )
end
let(:new_opts) do
- opts.merge(in_reply_to_discussion_id: nil,
- type: 'DiffNote',
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id,
- position: position.to_h,
- confidential: false)
+ opts.merge(
+ in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h,
+ confidential: false
+ )
end
before do
@@ -223,12 +242,14 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
prev_note =
create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo)
reply_opts =
- opts.merge(in_reply_to_discussion_id: prev_note.discussion_id,
- type: 'DiffNote',
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id,
- position: position.to_h,
- confidential: false)
+ opts.merge(
+ in_reply_to_discussion_id: prev_note.discussion_id,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h,
+ confidential: false
+ )
expect(merge_request).not_to receive(:diffs)
@@ -239,11 +260,13 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
context 'note diff file' do
let(:line_number) { 14 }
let(:position) do
- Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
- new_path: "files/ruby/popen.rb",
- old_line: nil,
- new_line: line_number,
- diff_refs: merge_request.diff_refs)
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: line_number,
+ diff_refs: merge_request.diff_refs
+ )
end
let(:previous_note) do
@@ -255,12 +278,14 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
end
context 'when eligible to have a note diff file' do
let(:new_opts) do
- opts.merge(in_reply_to_discussion_id: nil,
- type: 'DiffNote',
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id,
- position: position.to_h,
- confidential: false)
+ opts.merge(
+ in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h,
+ confidential: false
+ )
end
it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
@@ -331,12 +356,14 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
context 'when DiffNote is a reply' do
let(:new_opts) do
- opts.merge(in_reply_to_discussion_id: previous_note.discussion_id,
- type: 'DiffNote',
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id,
- position: position.to_h,
- confidential: false)
+ opts.merge(
+ in_reply_to_discussion_id: previous_note.discussion_id,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h,
+ confidential: false
+ )
end
it 'note is not associated with a note diff file' do
@@ -350,23 +377,27 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
context 'when DiffNote from an image' do
let(:image_position) do
- Gitlab::Diff::Position.new(old_path: "files/images/6049019_460s.jpg",
- new_path: "files/images/6049019_460s.jpg",
- width: 100,
- height: 100,
- x: 1,
- y: 100,
- diff_refs: merge_request.diff_refs,
- position_type: 'image')
+ Gitlab::Diff::Position.new(
+ old_path: "files/images/6049019_460s.jpg",
+ new_path: "files/images/6049019_460s.jpg",
+ width: 100,
+ height: 100,
+ x: 1,
+ y: 100,
+ diff_refs: merge_request.diff_refs,
+ position_type: 'image'
+ )
end
let(:new_opts) do
- opts.merge(in_reply_to_discussion_id: nil,
- type: 'DiffNote',
- noteable_type: 'MergeRequest',
- noteable_id: merge_request.id,
- position: image_position.to_h,
- confidential: false)
+ opts.merge(
+ in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: image_position.to_h,
+ confidential: false
+ )
end
it 'note is not associated with a note diff file' do
diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb
index 744808525f5..396e23351c9 100644
--- a/spec/services/notes/destroy_service_spec.rb
+++ b/spec/services/notes/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::DestroyService do
+RSpec.describe Notes::DestroyService, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
@@ -38,7 +38,9 @@ RSpec.describe Notes::DestroyService do
.and_call_original
expect do
service_action
- end.to change { counter.unique_events(event_names: property, start_date: 1.day.ago, end_date: 1.day.from_now) }.by(1)
+ end.to change {
+ counter.unique_events(event_names: property, start_date: Date.today.beginning_of_week, end_date: 1.week.from_now)
+ }.by(1)
end
it_behaves_like 'issue_edit snowplow tracking' do
@@ -94,8 +96,12 @@ RSpec.describe Notes::DestroyService do
it 'tracks design comment removal' do
note = create(:note_on_design, project: project)
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_design_comment_removed_action).with(author: note.author,
- project: project)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(
+ :track_issue_design_comment_removed_action
+ ).with(
+ author: note.author,
+ project: project
+ )
described_class.new(project, user).execute(note)
end
diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb
index 17001733c5b..0bcfd6b63d2 100644
--- a/spec/services/notes/post_process_service_spec.rb
+++ b/spec/services/notes/post_process_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::PostProcessService do
+RSpec.describe Notes::PostProcessService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index bca954c3959..c65a077f907 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::QuickActionsService do
+RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
shared_context 'note on noteable' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
@@ -182,7 +182,7 @@ RSpec.describe Notes::QuickActionsService do
context 'on an incident' do
before do
- issue.update!(issue_type: :incident)
+ issue.update!(issue_type: :incident, work_item_type: WorkItems::Type.default_by_type(:incident))
end
it 'leaves the note empty' do
@@ -224,7 +224,7 @@ RSpec.describe Notes::QuickActionsService do
context 'on an incident' do
before do
- issue.update!(issue_type: :incident)
+ issue.update!(issue_type: :incident, work_item_type: WorkItems::Type.default_by_type(:incident))
end
it 'leaves the note empty' do
@@ -250,28 +250,6 @@ RSpec.describe Notes::QuickActionsService do
end
end
- describe '.noteable_update_service_class' do
- include_context 'note on noteable'
-
- it 'returns Issues::UpdateService for a note on an issue' do
- note = create(:note_on_issue, project: project)
-
- expect(described_class.noteable_update_service_class(note)).to eq(Issues::UpdateService)
- end
-
- it 'returns MergeRequests::UpdateService for a note on a merge request' do
- note = create(:note_on_merge_request, project: project)
-
- expect(described_class.noteable_update_service_class(note)).to eq(MergeRequests::UpdateService)
- end
-
- it 'returns Commits::TagService for a note on a commit' do
- note = create(:note_on_commit, project: project)
-
- expect(described_class.noteable_update_service_class(note)).to eq(Commits::TagService)
- end
- end
-
describe '.supported?' do
include_context 'note on noteable'
@@ -322,6 +300,84 @@ RSpec.describe Notes::QuickActionsService do
let(:merge_request) { create(:merge_request, source_project: project) }
let(:note) { build(:note_on_merge_request, project: project, noteable: merge_request) }
end
+
+ context 'note on work item that supports quick actions' do
+ include_context 'note on noteable'
+
+ let_it_be(:work_item, reload: true) { create(:work_item, project: project) }
+
+ let(:note) { build(:note_on_work_item, project: project, noteable: work_item) }
+
+ let!(:labels) { create_pair(:label, project: project) }
+
+ before do
+ note.note = note_text
+ end
+
+ describe 'note with only command' do
+ describe '/close, /label & /assign' do
+ let(:note_text) do
+ %(/close\n/label ~#{labels.first.name} ~#{labels.last.name}\n/assign @#{assignee.username}\n)
+ end
+
+ it 'closes noteable, sets labels, assigns and leave no note' do
+ content = execute(note)
+
+ expect(content).to be_empty
+ expect(note.noteable).to be_closed
+ expect(note.noteable.labels).to match_array(labels)
+ expect(note.noteable.assignees).to eq([assignee])
+ end
+ end
+
+ describe '/reopen' do
+ before do
+ note.noteable.close!
+ expect(note.noteable).to be_closed
+ end
+ let(:note_text) { '/reopen' }
+
+ it 'opens the noteable, and leave no note' do
+ content = execute(note)
+
+ expect(content).to be_empty
+ expect(note.noteable).to be_open
+ end
+ end
+ end
+
+ describe 'note with command & text' do
+ describe '/close, /label, /assign' do
+ let(:note_text) do
+ %(HELLO\n/close\n/label ~#{labels.first.name} ~#{labels.last.name}\n/assign @#{assignee.username}\nWORLD)
+ end
+
+ it 'closes noteable, sets labels, assigns, and sets milestone to noteable' do
+ content = execute(note)
+
+ expect(content).to eq "HELLO\nWORLD"
+ expect(note.noteable).to be_closed
+ expect(note.noteable.labels).to match_array(labels)
+ expect(note.noteable.assignees).to eq([assignee])
+ end
+ end
+
+ describe '/reopen' do
+ before do
+ note.noteable.close
+ expect(note.noteable).to be_closed
+ end
+ let(:note_text) { "HELLO\n/reopen\nWORLD" }
+
+ it 'opens the noteable' do
+ content = execute(note)
+
+ expect(content).to eq "HELLO\nWORLD"
+ expect(note.noteable).to be_open
+ end
+ end
+ end
+ end
end
context 'CE restriction for issue assignees' do
diff --git a/spec/services/notes/render_service_spec.rb b/spec/services/notes/render_service_spec.rb
index 09cd7dc572b..d633cdd0448 100644
--- a/spec/services/notes/render_service_spec.rb
+++ b/spec/services/notes/render_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::RenderService do
+RSpec.describe Notes::RenderService, feature_category: :team_planning do
describe '#execute' do
it 'renders a Note' do
note = double(:note)
@@ -27,12 +27,14 @@ RSpec.describe Notes::RenderService do
.to receive(:render)
.with([note], :note)
- described_class.new(user).execute([note],
- requested_path: 'foo',
- project_wiki: wiki,
- ref: 'bar',
- only_path: nil,
- xhtml: false)
+ described_class.new(user).execute(
+ [note],
+ requested_path: 'foo',
+ project_wiki: wiki,
+ ref: 'bar',
+ only_path: nil,
+ xhtml: false
+ )
end
end
end
diff --git a/spec/services/notes/resolve_service_spec.rb b/spec/services/notes/resolve_service_spec.rb
index 1c5b308aed1..1b5586ee1b3 100644
--- a/spec/services/notes/resolve_service_spec.rb
+++ b/spec/services/notes/resolve_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::ResolveService do
+RSpec.describe Notes::ResolveService, feature_category: :team_planning do
let(:merge_request) { create(:merge_request) }
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project) }
let(:user) { merge_request.author }
diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb
index 05703ac548d..245cc046775 100644
--- a/spec/services/notes/update_service_spec.rb
+++ b/spec/services/notes/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Notes::UpdateService do
+RSpec.describe Notes::UpdateService, feature_category: :team_planning do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, group: group) }
let(:private_group) { create(:group, :private) }
@@ -65,7 +65,7 @@ RSpec.describe Notes::UpdateService do
.and_call_original
expect do
update_note(note: 'new text')
- end.to change { counter.unique_events(event_names: event, start_date: 1.day.ago, end_date: 1.day.from_now) }.by(1)
+ end.to change { counter.unique_events(event_names: event, start_date: Date.today.beginning_of_week, end_date: 1.week.from_now) }.by(1)
end
it_behaves_like 'issue_edit snowplow tracking' do
diff --git a/spec/services/notification_recipients/build_service_spec.rb b/spec/services/notification_recipients/build_service_spec.rb
index 899d23ec641..bfd1dcd7d80 100644
--- a/spec/services/notification_recipients/build_service_spec.rb
+++ b/spec/services/notification_recipients/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NotificationRecipients::BuildService do
+RSpec.describe NotificationRecipients::BuildService, feature_category: :team_planning do
let(:service) { described_class }
let(:assignee) { create(:user) }
let(:project) { create(:project, :public) }
diff --git a/spec/services/notification_recipients/builder/default_spec.rb b/spec/services/notification_recipients/builder/default_spec.rb
index 4d0ddc7c4f7..da991b5951a 100644
--- a/spec/services/notification_recipients/builder/default_spec.rb
+++ b/spec/services/notification_recipients/builder/default_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NotificationRecipients::Builder::Default do
+RSpec.describe NotificationRecipients::Builder::Default, feature_category: :team_planning do
describe '#build!' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, group: group).tap { |p| p.add_developer(project_watcher) if project_watcher } }
diff --git a/spec/services/notification_recipients/builder/new_note_spec.rb b/spec/services/notification_recipients/builder/new_note_spec.rb
index 7d2a4f682c5..e87824f3156 100644
--- a/spec/services/notification_recipients/builder/new_note_spec.rb
+++ b/spec/services/notification_recipients/builder/new_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe NotificationRecipients::Builder::NewNote do
+RSpec.describe NotificationRecipients::Builder::NewNote, feature_category: :team_planning do
describe '#notification_recipients' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, group: group) }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 4161f93cdac..f63f982708d 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -253,6 +253,16 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
it_behaves_like 'participating by assignee notification', check_delivery_jobs_queue: check_delivery_jobs_queue
end
+ shared_examples 'declines the invite' do
+ specify do
+ member = source.members.last
+
+ expect do
+ notification.decline_invite(member)
+ end.to change { ActionMailer::Base.deliveries.size }.by(1)
+ end
+ end
+
describe '.permitted_actions' do
it 'includes public methods' do
expect(described_class.permitted_actions).to include(:access_token_created)
@@ -518,8 +528,8 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
allow(Notify).to receive(:service_desk_new_note_email)
.with(Integer, Integer, String).and_return(mailer)
- allow(::Gitlab::IncomingEmail).to receive(:enabled?) { true }
- allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?) { true }
+ allow(::Gitlab::Email::IncomingEmail).to receive(:enabled?) { true }
+ allow(::Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?) { true }
end
let(:subject) { NotificationService.new }
@@ -3029,7 +3039,7 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
end
end
- describe '#decline_group_invite' do
+ describe '#decline_invite' do
let(:creator) { create(:user) }
let(:group) { create(:group) }
let(:member) { create(:user) }
@@ -3039,12 +3049,8 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
group.add_developer(member, creator)
end
- it do
- group_member = group.members.last
-
- expect do
- notification.decline_group_invite(group_member)
- end.to change { ActionMailer::Base.deliveries.size }.by(1)
+ it_behaves_like 'declines the invite' do
+ let(:source) { group }
end
end
@@ -3201,19 +3207,15 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do
end
end
- describe '#decline_project_invite' do
+ describe '#decline_invite' do
let(:member) { create(:user) }
before do
project.add_developer(member, current_user: project.first_owner)
end
- it do
- project_member = project.members.last
-
- expect do
- notification.decline_project_invite(project_member)
- end.to change { ActionMailer::Base.deliveries.size }.by(1)
+ it_behaves_like 'declines the invite' do
+ let(:source) { project }
end
end
diff --git a/spec/services/onboarding/progress_service_spec.rb b/spec/services/onboarding/progress_service_spec.rb
index 8f3f723613e..e1d6b4cd44b 100644
--- a/spec/services/onboarding/progress_service_spec.rb
+++ b/spec/services/onboarding/progress_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Onboarding::ProgressService do
+RSpec.describe Onboarding::ProgressService, feature_category: :onboarding do
describe '.async' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:action) { :git_pull }
diff --git a/spec/services/packages/cleanup/execute_policy_service_spec.rb b/spec/services/packages/cleanup/execute_policy_service_spec.rb
index 93335c4a821..a083dc0d4ea 100644
--- a/spec/services/packages/cleanup/execute_policy_service_spec.rb
+++ b/spec/services/packages/cleanup/execute_policy_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Cleanup::ExecutePolicyService do
+RSpec.describe Packages::Cleanup::ExecutePolicyService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:policy) { create(:packages_cleanup_policy, project: project) }
diff --git a/spec/services/packages/cleanup/update_policy_service_spec.rb b/spec/services/packages/cleanup/update_policy_service_spec.rb
index a11fbb766f5..8068c351e5f 100644
--- a/spec/services/packages/cleanup/update_policy_service_spec.rb
+++ b/spec/services/packages/cleanup/update_policy_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Cleanup::UpdatePolicyService do
+RSpec.describe Packages::Cleanup::UpdatePolicyService, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:project) { create(:project) }
diff --git a/spec/services/packages/composer/composer_json_service_spec.rb b/spec/services/packages/composer/composer_json_service_spec.rb
index d2187688c4c..15acd79c49e 100644
--- a/spec/services/packages/composer/composer_json_service_spec.rb
+++ b/spec/services/packages/composer/composer_json_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Composer::ComposerJsonService do
+RSpec.describe Packages::Composer::ComposerJsonService, feature_category: :package_registry do
describe '#execute' do
let(:branch) { project.repository.find_branch('master') }
let(:target) { branch.target }
diff --git a/spec/services/packages/composer/create_package_service_spec.rb b/spec/services/packages/composer/create_package_service_spec.rb
index 26429a7b5d9..78d5d76fe4f 100644
--- a/spec/services/packages/composer/create_package_service_spec.rb
+++ b/spec/services/packages/composer/create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Composer::CreatePackageService do
+RSpec.describe Packages::Composer::CreatePackageService, feature_category: :package_registry do
include PackagesManagerApiSpecHelpers
let_it_be(:package_name) { 'composer-package-name' }
diff --git a/spec/services/packages/composer/version_parser_service_spec.rb b/spec/services/packages/composer/version_parser_service_spec.rb
index 69253ff934e..ac50f2e2e55 100644
--- a/spec/services/packages/composer/version_parser_service_spec.rb
+++ b/spec/services/packages/composer/version_parser_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Composer::VersionParserService do
+RSpec.describe Packages::Composer::VersionParserService, feature_category: :package_registry do
let_it_be(:params) { {} }
describe '#execute' do
diff --git a/spec/services/packages/conan/create_package_file_service_spec.rb b/spec/services/packages/conan/create_package_file_service_spec.rb
index e655b8d1f9e..6859e52560a 100644
--- a/spec/services/packages/conan/create_package_file_service_spec.rb
+++ b/spec/services/packages/conan/create_package_file_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Conan::CreatePackageFileService do
+RSpec.describe Packages::Conan::CreatePackageFileService, feature_category: :package_registry do
include WorkhorseHelpers
let_it_be(:package) { create(:conan_package) }
diff --git a/spec/services/packages/conan/create_package_service_spec.rb b/spec/services/packages/conan/create_package_service_spec.rb
index 6f644f5ef95..db06463b7fa 100644
--- a/spec/services/packages/conan/create_package_service_spec.rb
+++ b/spec/services/packages/conan/create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Conan::CreatePackageService do
+RSpec.describe Packages::Conan::CreatePackageService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/packages/conan/search_service_spec.rb b/spec/services/packages/conan/search_service_spec.rb
index 9e8be164d8c..83ece404d5f 100644
--- a/spec/services/packages/conan/search_service_spec.rb
+++ b/spec/services/packages/conan/search_service_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Packages::Conan::SearchService, feature_category: :package_regist
let!(:conan_package) { create(:conan_package, project: project) }
let!(:conan_package2) { create(:conan_package, project: project) }
- subject { described_class.new(user, query: query) }
+ subject { described_class.new(project, user, query: query) }
before do
project.add_developer(user)
@@ -24,7 +24,7 @@ RSpec.describe Packages::Conan::SearchService, feature_category: :package_regist
result = subject.execute
expect(result.status).to eq :success
- expect(result.payload).to eq(results: [conan_package.conan_recipe, conan_package2.conan_recipe])
+ expect(result.payload).to eq(results: [conan_package2.conan_recipe, conan_package.conan_recipe])
end
end
@@ -71,5 +71,29 @@ RSpec.describe Packages::Conan::SearchService, feature_category: :package_regist
expect(result.payload).to eq(results: [])
end
end
+
+ context 'for project' do
+ let_it_be(:project2) { create(:project, :public) }
+ let(:query) { conan_package.name }
+ let!(:conan_package3) { create(:conan_package, name: conan_package.name, project: project2) }
+
+ context 'when passing a project' do
+ it 'returns only packages of the given project' do
+ result = subject.execute
+
+ expect(result.status).to eq :success
+ expect(result[:results]).to match_array([conan_package.conan_recipe])
+ end
+ end
+
+ context 'when passing a project with nil' do
+ it 'returns all packages' do
+ result = described_class.new(nil, user, query: query).execute
+
+ expect(result.status).to eq :success
+ expect(result[:results]).to eq([conan_package3.conan_recipe, conan_package.conan_recipe])
+ end
+ end
+ end
end
end
diff --git a/spec/services/packages/conan/single_package_search_service_spec.rb b/spec/services/packages/conan/single_package_search_service_spec.rb
new file mode 100644
index 00000000000..1d95d1d4f64
--- /dev/null
+++ b/spec/services/packages/conan/single_package_search_service_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Conan::SinglePackageSearchService, feature_category: :package_registry do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+
+ let!(:conan_package) { create(:conan_package, project: project) }
+ let!(:conan_package2) { create(:conan_package, project: project) }
+
+ describe '#execute' do
+ context 'with a valid query and user with permissions' do
+ before do
+ allow_next_instance_of(described_class) do |service|
+ allow(service).to receive(:can_access_project_package?).and_return(true)
+ end
+ end
+
+ it 'returns the correct package' do
+ [conan_package, conan_package2].each do |package|
+ result = described_class.new(package.conan_recipe, user).execute
+
+ expect(result.status).to eq :success
+ expect(result[:results]).to match_array([package.conan_recipe])
+ end
+ end
+ end
+
+ context 'with a user without permissions' do
+ before do
+ allow_next_instance_of(described_class) do |service|
+ allow(service).to receive(:can_access_project_package?).and_return(false)
+ end
+ end
+
+ it 'returns an empty array' do
+ result = described_class.new(conan_package.conan_recipe, user).execute
+
+ expect(result.status).to eq :success
+ expect(result[:results]).to match_array([])
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/create_dependency_service_spec.rb b/spec/services/packages/create_dependency_service_spec.rb
index f95e21cd045..06a7a13bdd9 100644
--- a/spec/services/packages/create_dependency_service_spec.rb
+++ b/spec/services/packages/create_dependency_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::CreateDependencyService do
+RSpec.describe Packages::CreateDependencyService, feature_category: :package_registry do
describe '#execute' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:version) { '1.0.1' }
diff --git a/spec/services/packages/create_event_service_spec.rb b/spec/services/packages/create_event_service_spec.rb
index 58fa68b11fe..45c758ec866 100644
--- a/spec/services/packages/create_event_service_spec.rb
+++ b/spec/services/packages/create_event_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::CreateEventService do
+RSpec.describe Packages::CreateEventService, feature_category: :package_registry do
let(:scope) { 'generic' }
let(:event_name) { 'push_package' }
@@ -15,47 +15,6 @@ RSpec.describe Packages::CreateEventService do
subject { described_class.new(nil, user, params).execute }
describe '#execute' do
- shared_examples 'db package event creation' do |originator_type, expected_scope|
- before do
- allow(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event)
- end
-
- context 'with feature flag disable' do
- before do
- stub_feature_flags(collect_package_events: false)
- end
-
- it 'does not create an event' do
- expect { subject }.not_to change { Packages::Event.count }
- end
- end
-
- context 'with feature flag enabled' do
- before do
- stub_feature_flags(collect_package_events: true)
- end
-
- it 'creates the event' do
- expect { subject }.to change { Packages::Event.count }.by(1)
-
- expect(subject.originator_type).to eq(originator_type)
- expect(subject.originator).to eq(user&.id)
- expect(subject.event_scope).to eq(expected_scope)
- expect(subject.event_type).to eq(event_name)
- end
-
- context 'on a read-only instance' do
- before do
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
- end
-
- it 'does not create an event' do
- expect { subject }.not_to change { Packages::Event.count }
- end
- end
- end
- end
-
shared_examples 'redis package unique event creation' do |originator_type, expected_scope|
it 'tracks the event' do
expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(/package/, values: user.id)
@@ -75,7 +34,6 @@ RSpec.describe Packages::CreateEventService do
context 'with a user' do
let(:user) { create(:user) }
- it_behaves_like 'db package event creation', 'user', 'generic'
it_behaves_like 'redis package unique event creation', 'user', 'generic'
it_behaves_like 'redis package count event creation', 'user', 'generic'
end
@@ -83,7 +41,6 @@ RSpec.describe Packages::CreateEventService do
context 'with a deploy token' do
let(:user) { create(:deploy_token) }
- it_behaves_like 'db package event creation', 'deploy_token', 'generic'
it_behaves_like 'redis package unique event creation', 'deploy_token', 'generic'
it_behaves_like 'redis package count event creation', 'deploy_token', 'generic'
end
@@ -91,7 +48,6 @@ RSpec.describe Packages::CreateEventService do
context 'with no user' do
let(:user) { nil }
- it_behaves_like 'db package event creation', 'guest', 'generic'
it_behaves_like 'redis package count event creation', 'guest', 'generic'
end
@@ -101,14 +57,12 @@ RSpec.describe Packages::CreateEventService do
context 'as guest' do
let(:user) { nil }
- it_behaves_like 'db package event creation', 'guest', 'npm'
it_behaves_like 'redis package count event creation', 'guest', 'npm'
end
context 'with user' do
let(:user) { create(:user) }
- it_behaves_like 'db package event creation', 'user', 'npm'
it_behaves_like 'redis package unique event creation', 'user', 'npm'
it_behaves_like 'redis package count event creation', 'user', 'npm'
end
diff --git a/spec/services/packages/create_package_file_service_spec.rb b/spec/services/packages/create_package_file_service_spec.rb
index 2ff00ea8568..5b4ea3e1530 100644
--- a/spec/services/packages/create_package_file_service_spec.rb
+++ b/spec/services/packages/create_package_file_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::CreatePackageFileService do
+RSpec.describe Packages::CreatePackageFileService, feature_category: :package_registry do
let_it_be(:package) { create(:maven_package) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/packages/create_temporary_package_service_spec.rb b/spec/services/packages/create_temporary_package_service_spec.rb
index 4b8d37401d8..be8b5afc1e0 100644
--- a/spec/services/packages/create_temporary_package_service_spec.rb
+++ b/spec/services/packages/create_temporary_package_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::CreateTemporaryPackageService do
+RSpec.describe Packages::CreateTemporaryPackageService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:params) { {} }
diff --git a/spec/services/packages/debian/create_package_file_service_spec.rb b/spec/services/packages/debian/create_package_file_service_spec.rb
index 43928669eb1..b527bf8c1de 100644
--- a/spec/services/packages/debian/create_package_file_service_spec.rb
+++ b/spec/services/packages/debian/create_package_file_service_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe Packages::Debian::CreatePackageFileService, feature_category: :pa
expect(package_file).to be_valid
expect(package_file.file.read).to start_with('Format: 1.8')
- expect(package_file.size).to eq(2143)
+ expect(package_file.size).to eq(2422)
expect(package_file.file_name).to eq(file_name)
expect(package_file.file_sha1).to eq('54321')
expect(package_file.file_sha256).to eq('543212345')
diff --git a/spec/services/packages/debian/extract_changes_metadata_service_spec.rb b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb
index 4d6acac219b..a22c1fc7acc 100644
--- a/spec/services/packages/debian/extract_changes_metadata_service_spec.rb
+++ b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Packages::Debian::ExtractChangesMetadataService, feature_category
expect(subject[:file_type]).to eq(:changes)
expect(subject[:architecture]).to be_nil
expect(subject[:fields]).to include(expected_fields)
- expect(subject[:files].count).to eq(6)
+ expect(subject[:files].count).to eq(7)
end
end
diff --git a/spec/services/packages/debian/extract_metadata_service_spec.rb b/spec/services/packages/debian/extract_metadata_service_spec.rb
index 412f285152b..1983c49c6b7 100644
--- a/spec/services/packages/debian/extract_metadata_service_spec.rb
+++ b/spec/services/packages/debian/extract_metadata_service_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Packages::Debian::ExtractMetadataService, feature_category: :package_registry do
@@ -6,12 +7,13 @@ RSpec.describe Packages::Debian::ExtractMetadataService, feature_category: :pack
subject { service.execute }
- RSpec.shared_context 'Debian ExtractMetadata Service' do |trait|
+ RSpec.shared_context 'with Debian package file' do |trait|
let(:package_file) { create(:debian_package_file, trait) }
end
RSpec.shared_examples 'Test Debian ExtractMetadata Service' do |expected_file_type, expected_architecture, expected_fields|
- it "returns file_type #{expected_file_type.inspect}, architecture #{expected_architecture.inspect} and fields #{expected_fields.nil? ? '' : 'including '}#{expected_fields.inspect}", :aggregate_failures do
+ it "returns file_type #{expected_file_type.inspect}, architecture #{expected_architecture.inspect} and fields #{expected_fields.nil? ? '' : 'including '}#{expected_fields.inspect}",
+ :aggregate_failures do
expect(subject[:file_type]).to eq(expected_file_type)
expect(subject[:architecture]).to eq(expected_architecture)
@@ -25,29 +27,79 @@ RSpec.describe Packages::Debian::ExtractMetadataService, feature_category: :pack
using RSpec::Parameterized::TableSyntax
- where(:case_name, :trait, :expected_file_type, :expected_architecture, :expected_fields) do
- 'with invalid' | :invalid | :unknown | nil | nil
- 'with source' | :source | :source | nil | nil
- 'with dsc' | :dsc | :dsc | nil | { 'Binary' => 'sample-dev, libsample0, sample-udeb' }
- 'with deb' | :deb | :deb | 'amd64' | { 'Multi-Arch' => 'same' }
- 'with udeb' | :udeb | :udeb | 'amd64' | { 'Package' => 'sample-udeb' }
- 'with buildinfo' | :buildinfo | :buildinfo | nil | { 'Architecture' => 'amd64 source', 'Build-Architecture' => 'amd64' }
- 'with changes' | :changes | :changes | nil | { 'Architecture' => 'source amd64', 'Binary' => 'libsample0 sample-dev sample-udeb' }
+ context 'with valid file types' do
+ where(:case_name, :trait, :expected_file_type, :expected_architecture, :expected_fields) do
+ 'with source' | :source | :source | nil | nil
+ 'with dsc' | :dsc | :dsc | nil | { 'Binary' => 'sample-dev, libsample0, sample-udeb, sample-ddeb' }
+ 'with deb' | :deb | :deb | 'amd64' | { 'Multi-Arch' => 'same' }
+ 'with udeb' | :udeb | :udeb | 'amd64' | { 'Package' => 'sample-udeb' }
+ 'with ddeb' | :ddeb | :ddeb | 'amd64' | { 'Package' => 'sample-ddeb' }
+ 'with buildinfo' | :buildinfo | :buildinfo | nil | { 'Architecture' => 'amd64 source',
+ 'Build-Architecture' => 'amd64' }
+ 'with changes' | :changes | :changes | nil | { 'Architecture' => 'source amd64',
+ 'Binary' => 'libsample0 sample-dev sample-udeb' }
+ end
+
+ with_them do
+ include_context 'with Debian package file', params[:trait] do
+ it_behaves_like 'Test Debian ExtractMetadata Service',
+ params[:expected_file_type],
+ params[:expected_architecture],
+ params[:expected_fields]
+ end
+ end
end
- with_them do
- include_context 'Debian ExtractMetadata Service', params[:trait] do
- it_behaves_like 'Test Debian ExtractMetadata Service',
- params[:expected_file_type],
- params[:expected_architecture],
- params[:expected_fields]
+ context 'with valid source extensions' do
+ where(:ext) do
+ %i[gz bz2 lzma xz]
+ end
+
+ with_them do
+ let(:package_file) do
+ create(:debian_package_file, :source, file_name: "myfile.tar.#{ext}",
+ file_fixture: 'spec/fixtures/packages/debian/sample_1.2.3~alpha2.tar.xz')
+ end
+
+ it_behaves_like 'Test Debian ExtractMetadata Service', :source
+ end
+ end
+
+ context 'with invalid source extensions' do
+ where(:ext) do
+ %i[gzip bzip2]
+ end
+
+ with_them do
+ let(:package_file) do
+ create(:debian_package_file, :source, file_name: "myfile.tar.#{ext}",
+ file_fixture: 'spec/fixtures/packages/debian/sample_1.2.3~alpha2.tar.xz')
+ end
+
+ it 'raises an error' do
+ expect do
+ subject
+ end.to raise_error(described_class::ExtractionError,
+ "unsupported file extension for file #{package_file.file_name}")
+ end
+ end
+ end
+
+ context 'with invalid file name' do
+ let(:package_file) { create(:debian_package_file, :invalid) }
+
+ it 'raises an error' do
+ expect do
+ subject
+ end.to raise_error(described_class::ExtractionError,
+ "unsupported file extension for file #{package_file.file_name}")
end
end
context 'with invalid package file' do
let(:package_file) { create(:conan_package_file) }
- it 'raise error' do
+ it 'raises an error' do
expect { subject }.to raise_error(described_class::ExtractionError, 'invalid package file')
end
end
diff --git a/spec/services/packages/debian/find_or_create_package_service_spec.rb b/spec/services/packages/debian/find_or_create_package_service_spec.rb
index 36f96008582..c2ae3d56864 100644
--- a/spec/services/packages/debian/find_or_create_package_service_spec.rb
+++ b/spec/services/packages/debian/find_or_create_package_service_spec.rb
@@ -4,13 +4,17 @@ require 'spec_helper'
RSpec.describe Packages::Debian::FindOrCreatePackageService, feature_category: :package_registry do
let_it_be(:distribution) { create(:debian_project_distribution, :with_suite) }
+ let_it_be(:distribution2) { create(:debian_project_distribution, :with_suite) }
+
let_it_be(:project) { distribution.project }
let_it_be(:user) { create(:user) }
let(:service) { described_class.new(project, user, params) }
+ let(:params2) { params }
+ let(:service2) { described_class.new(project, user, params2) }
let(:package) { subject.payload[:package] }
- let(:package2) { service.execute.payload[:package] }
+ let(:package2) { service2.execute.payload[:package] }
shared_examples 'find or create Debian package' do
it 'returns the same object' do
@@ -55,11 +59,24 @@ RSpec.describe Packages::Debian::FindOrCreatePackageService, feature_category: :
it_behaves_like 'find or create Debian package'
end
+ context 'with existing package in another distribution' do
+ let(:params) { { name: 'foo', version: '1.0+debian', distribution_name: distribution.codename } }
+ let(:params2) { { name: 'foo', version: '1.0+debian', distribution_name: distribution2.codename } }
+
+ it 'raises ArgumentError' do
+ expect { subject }.to change { ::Packages::Package.count }.by(1)
+
+ expect { package2 }.to raise_error(ArgumentError, "Debian package #{package.name} #{package.version} exists " \
+ "in distribution #{distribution.codename}")
+ end
+ end
+
context 'with non-existing distribution' do
let(:params) { { name: 'foo', version: '1.0+debian', distribution_name: 'not-existing' } }
it 'raises ActiveRecord::RecordNotFound' do
- expect { package }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { package }.to raise_error(ActiveRecord::RecordNotFound,
+ /^Couldn't find Packages::Debian::ProjectDistribution/)
end
end
end
diff --git a/spec/services/packages/debian/generate_distribution_service_spec.rb b/spec/services/packages/debian/generate_distribution_service_spec.rb
index 6d179c791a3..27206b847e4 100644
--- a/spec/services/packages/debian/generate_distribution_service_spec.rb
+++ b/spec/services/packages/debian/generate_distribution_service_spec.rb
@@ -3,20 +3,32 @@
require 'spec_helper'
RSpec.describe Packages::Debian::GenerateDistributionService, feature_category: :package_registry do
- describe '#execute' do
- subject { described_class.new(distribution).execute }
+ include_context 'with published Debian package'
- let(:subject2) { described_class.new(distribution).execute }
- let(:subject3) { described_class.new(distribution).execute }
+ let(:service) { described_class.new(distribution) }
- include_context 'with published Debian package'
+ [:project, :group].each do |container_type|
+ context "for #{container_type}" do
+ include_context 'with Debian distribution', container_type
- [:project, :group].each do |container_type|
- context "for #{container_type}" do
- include_context 'with Debian distribution', container_type
+ describe '#execute' do
+ subject { service.execute }
+
+ let(:subject2) { described_class.new(distribution).execute }
+ let(:subject3) { described_class.new(distribution).execute }
it_behaves_like 'Generate Debian Distribution and component files'
end
+
+ describe '#lease_key' do
+ subject { service.send(:lease_key) }
+
+ let(:prefix) { "packages:debian:generate_distribution_service:" }
+
+ it 'returns an unique key' do
+ is_expected.to eq "#{prefix}#{container_type}_distribution:#{distribution.id}"
+ end
+ end
end
end
end
diff --git a/spec/services/packages/debian/parse_debian822_service_spec.rb b/spec/services/packages/debian/parse_debian822_service_spec.rb
index 35b7ead9209..624d4d95e5a 100644
--- a/spec/services/packages/debian/parse_debian822_service_spec.rb
+++ b/spec/services/packages/debian/parse_debian822_service_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Packages::Debian::ParseDebian822Service, feature_category: :package_registry do
@@ -76,14 +77,19 @@ RSpec.describe Packages::Debian::ParseDebian822Service, feature_category: :packa
'Multi-Arch' => 'same',
'Depends' => '${shlibs:Depends}, ${misc:Depends}',
'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
- },
+ },
'Package: sample-udeb' => {
- 'Package' => 'sample-udeb',
- 'Package-Type' => 'udeb',
- 'Architecture' => 'any',
- 'Depends' => 'installed-base',
- 'Description' => 'Some mostly empty udeb'
- }
+ 'Package' => 'sample-udeb',
+ 'Package-Type' => 'udeb',
+ 'Architecture' => 'any',
+ 'Depends' => 'installed-base',
+ 'Description' => 'Some mostly empty udeb'
+ },
+ 'Package: sample-ddeb' => {
+ 'Package' => 'sample-ddeb',
+ 'Architecture' => 'any',
+ 'Description' => 'Some fake Ubuntu ddeb'
+ }
}
expect(subject.execute.to_s).to eq(expected.to_s)
diff --git a/spec/services/packages/debian/process_changes_service_spec.rb b/spec/services/packages/debian/process_changes_service_spec.rb
index e3ed744377e..dbfcc359f9c 100644
--- a/spec/services/packages/debian/process_changes_service_spec.rb
+++ b/spec/services/packages/debian/process_changes_service_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Packages::Debian::ProcessChangesService, feature_category: :package_registry do
@@ -18,7 +19,7 @@ RSpec.describe Packages::Debian::ProcessChangesService, feature_category: :packa
expect { subject.execute }
.to change { Packages::Package.count }.from(1).to(2)
.and not_change { Packages::PackageFile.count }
- .and change { incoming.package_files.count }.from(7).to(0)
+ .and change { incoming.package_files.count }.from(8).to(0)
.and change { package_file.debian_file_metadatum&.reload&.file_type }.from('unknown').to('changes')
created_package = Packages::Package.last
@@ -55,7 +56,7 @@ RSpec.describe Packages::Debian::ProcessChangesService, feature_category: :packa
it_behaves_like 'raises error with missing field', 'Distribution'
end
- context 'with existing package' do
+ context 'with existing package in the same distribution' do
let_it_be_with_reload(:existing_package) do
create(:debian_package, name: 'sample', version: '1.2.3~alpha2', project: distribution.project, published_in: distribution)
end
@@ -64,10 +65,37 @@ RSpec.describe Packages::Debian::ProcessChangesService, feature_category: :packa
expect { subject.execute }
.to not_change { Packages::Package.count }
.and not_change { Packages::PackageFile.count }
- .and change(package_file, :package).to(existing_package)
+ .and change { package_file.package }.to(existing_package)
+ end
+
+ context 'and marked as pending_destruction' do
+ it 'does not re-use the existing package' do
+ existing_package.pending_destruction!
+
+ expect { subject.execute }
+ .to change { Packages::Package.count }.by(1)
+ .and not_change { Packages::PackageFile.count }
+ end
+ end
+ end
+
+ context 'with existing package in another distribution' do
+ let_it_be_with_reload(:existing_package) do
+ create(:debian_package, name: 'sample', version: '1.2.3~alpha2', project: distribution.project)
+ end
+
+ it 'raise ExtractionError' do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change { Packages::Package.count }
+ .and not_change { Packages::PackageFile.count }
+ .and not_change { incoming.package_files.count }
+ .and raise_error(ArgumentError,
+ "Debian package #{existing_package.name} #{existing_package.version} exists " \
+ "in distribution #{existing_package.debian_distribution.codename}")
end
- context 'marked as pending_destruction' do
+ context 'and marked as pending_destruction' do
it 'does not re-use the existing package' do
existing_package.pending_destruction!
diff --git a/spec/services/packages/debian/process_package_file_service_spec.rb b/spec/services/packages/debian/process_package_file_service_spec.rb
index caf29cfc4fa..7782b5fc1a6 100644
--- a/spec/services/packages/debian/process_package_file_service_spec.rb
+++ b/spec/services/packages/debian/process_package_file_service_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Packages::Debian::ProcessPackageFileService, feature_category: :package_registry do
@@ -19,14 +20,14 @@ RSpec.describe Packages::Debian::ProcessPackageFileService, feature_category: :p
expect { subject.execute }
.to not_change(Packages::Package, :count)
.and not_change(Packages::PackageFile, :count)
- .and change(Packages::Debian::Publication, :count).by(1)
+ .and change { Packages::Debian::Publication.count }.by(1)
.and not_change(package.package_files, :count)
.and change { package.reload.name }.to('sample')
.and change { package.reload.version }.to('1.2.3~alpha2')
.and change { package.reload.status }.from('processing').to('default')
.and change { package.reload.debian_publication }.from(nil)
- .and change(debian_file_metadatum, :file_type).from('unknown').to(expected_file_type)
- .and change(debian_file_metadatum, :component).from(nil).to(component_name)
+ .and change { debian_file_metadatum.file_type }.from('unknown').to(expected_file_type)
+ .and change { debian_file_metadatum.component }.from(nil).to(component_name)
end
end
@@ -35,6 +36,7 @@ RSpec.describe Packages::Debian::ProcessPackageFileService, feature_category: :p
where(:case_name, :expected_file_type, :file_name, :component_name) do
'with a deb' | 'deb' | 'libsample0_1.2.3~alpha2_amd64.deb' | 'main'
'with an udeb' | 'udeb' | 'sample-udeb_1.2.3~alpha2_amd64.udeb' | 'contrib'
+ 'with an ddeb' | 'ddeb' | 'sample-ddeb_1.2.3~alpha2_amd64.ddeb' | 'main'
end
with_them do
@@ -66,21 +68,42 @@ RSpec.describe Packages::Debian::ProcessPackageFileService, feature_category: :p
expect(::Packages::Debian::GenerateDistributionWorker)
.to receive(:perform_async).with(:project, distribution.id)
expect { subject.execute }
- .to change(Packages::Package, :count).from(2).to(1)
- .and change(Packages::PackageFile, :count).from(14).to(8)
+ .to change { Packages::Package.count }.from(2).to(1)
+ .and change { Packages::PackageFile.count }.from(16).to(9)
.and not_change(Packages::Debian::Publication, :count)
- .and change(package.package_files, :count).from(7).to(0)
- .and change(package_file, :package).from(package).to(matching_package)
+ .and change { package.package_files.count }.from(8).to(0)
+ .and change { package_file.package }.from(package).to(matching_package)
.and not_change(matching_package, :name)
.and not_change(matching_package, :version)
- .and change(debian_file_metadatum, :file_type).from('unknown').to(expected_file_type)
- .and change(debian_file_metadatum, :component).from(nil).to(component_name)
+ .and change { debian_file_metadatum.file_type }.from('unknown').to(expected_file_type)
+ .and change { debian_file_metadatum.component }.from(nil).to(component_name)
expect { package.reload }
.to raise_error(ActiveRecord::RecordNotFound)
end
end
+ context 'when there is a matching published package in another distribution' do
+ let!(:matching_package) do
+ create(
+ :debian_package,
+ project: distribution.project,
+ name: 'sample',
+ version: '1.2.3~alpha2'
+ )
+ end
+
+ it 'raise ArgumentError', :aggregate_failures do
+ expect(::Packages::Debian::GenerateDistributionWorker).not_to receive(:perform_async)
+ expect { subject.execute }
+ .to not_change(Packages::Package, :count)
+ .and not_change(Packages::PackageFile, :count)
+ .and not_change(package.package_files, :count)
+ .and raise_error(ArgumentError, "Debian package sample 1.2.3~alpha2 exists " \
+ "in distribution #{matching_package.debian_distribution.codename}")
+ end
+ end
+
context 'when there is a matching published package pending destruction' do
let!(:matching_package) do
create(
diff --git a/spec/services/packages/generic/create_package_file_service_spec.rb b/spec/services/packages/generic/create_package_file_service_spec.rb
index 9d6784b7721..06a78c7820f 100644
--- a/spec/services/packages/generic/create_package_file_service_spec.rb
+++ b/spec/services/packages/generic/create_package_file_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Generic::CreatePackageFileService do
+RSpec.describe Packages::Generic::CreatePackageFileService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:pipeline) { create(:ci_pipeline, user: user) }
@@ -81,9 +81,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
it_behaves_like 'assigns build to package file'
context 'with existing package' do
- before do
- create(:package_file, package: package, file_name: file_name)
- end
+ let_it_be(:duplicate_file) { create(:package_file, package: package, file_name: file_name) }
it { expect { execute_service }.to change { project.package_files.count }.by(1) }
@@ -97,6 +95,16 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
.and change { project.package_files.count }.by(0)
end
+ context 'when the file is pending destruction' do
+ before do
+ duplicate_file.update_column(:status, :pending_destruction)
+ end
+
+ it 'allows creating the file' do
+ expect { execute_service }.to change { project.package_files.count }.by(1)
+ end
+ end
+
context 'when the package name matches the exception regex' do
before do
package.project.namespace.package_settings.update!(generic_duplicate_exception_regex: '.*')
diff --git a/spec/services/packages/generic/find_or_create_package_service_spec.rb b/spec/services/packages/generic/find_or_create_package_service_spec.rb
index 10ec917bc99..07054fe3651 100644
--- a/spec/services/packages/generic/find_or_create_package_service_spec.rb
+++ b/spec/services/packages/generic/find_or_create_package_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Generic::FindOrCreatePackageService do
+RSpec.describe Packages::Generic::FindOrCreatePackageService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:ci_build) { create(:ci_build, :running, user: user) }
diff --git a/spec/services/packages/go/create_package_service_spec.rb b/spec/services/packages/go/create_package_service_spec.rb
index 4ca1119fbaa..f552af81077 100644
--- a/spec/services/packages/go/create_package_service_spec.rb
+++ b/spec/services/packages/go/create_package_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Go::CreatePackageService do
+RSpec.describe Packages::Go::CreatePackageService, feature_category: :package_registry do
let_it_be(:project) { create :project_empty_repo, path: 'my-go-lib' }
let_it_be(:mod) { create :go_module, project: project }
diff --git a/spec/services/packages/go/sync_packages_service_spec.rb b/spec/services/packages/go/sync_packages_service_spec.rb
index 565b0f252ce..2881b6fdac9 100644
--- a/spec/services/packages/go/sync_packages_service_spec.rb
+++ b/spec/services/packages/go/sync_packages_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Go::SyncPackagesService do
+RSpec.describe Packages::Go::SyncPackagesService, feature_category: :package_registry do
include_context 'basic Go module'
let(:params) { { info: true, mod: true, zip: true } }
diff --git a/spec/services/packages/helm/extract_file_metadata_service_spec.rb b/spec/services/packages/helm/extract_file_metadata_service_spec.rb
index f4c61c12344..861d326d12a 100644
--- a/spec/services/packages/helm/extract_file_metadata_service_spec.rb
+++ b/spec/services/packages/helm/extract_file_metadata_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Helm::ExtractFileMetadataService do
+RSpec.describe Packages::Helm::ExtractFileMetadataService, feature_category: :package_registry do
let_it_be(:package_file) { create(:helm_package_file) }
let(:service) { described_class.new(package_file) }
diff --git a/spec/services/packages/helm/process_file_service_spec.rb b/spec/services/packages/helm/process_file_service_spec.rb
index 1be0153a4a5..a1f53e8756c 100644
--- a/spec/services/packages/helm/process_file_service_spec.rb
+++ b/spec/services/packages/helm/process_file_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Helm::ProcessFileService do
+RSpec.describe Packages::Helm::ProcessFileService, feature_category: :package_registry do
let(:package) { create(:helm_package, without_package_files: true, status: 'processing') }
let!(:package_file) { create(:helm_package_file, without_loaded_metadatum: true, package: package) }
let(:channel) { 'stable' }
diff --git a/spec/services/packages/mark_package_files_for_destruction_service_spec.rb b/spec/services/packages/mark_package_files_for_destruction_service_spec.rb
index 66534338003..a00a0b79854 100644
--- a/spec/services/packages/mark_package_files_for_destruction_service_spec.rb
+++ b/spec/services/packages/mark_package_files_for_destruction_service_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe Packages::MarkPackageFilesForDestructionService, :aggregate_failures do
+RSpec.describe Packages::MarkPackageFilesForDestructionService, :aggregate_failures,
+ feature_category: :package_registry do
let(:service) { described_class.new(package_files) }
describe '#execute', :aggregate_failures do
diff --git a/spec/services/packages/mark_package_for_destruction_service_spec.rb b/spec/services/packages/mark_package_for_destruction_service_spec.rb
index 125ec53ad61..d65e62b84a6 100644
--- a/spec/services/packages/mark_package_for_destruction_service_spec.rb
+++ b/spec/services/packages/mark_package_for_destruction_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::MarkPackageForDestructionService do
+RSpec.describe Packages::MarkPackageForDestructionService, feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:package) { create(:npm_package) }
@@ -36,6 +36,12 @@ RSpec.describe Packages::MarkPackageForDestructionService do
end
it 'returns an error ServiceResponse' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(StandardError),
+ project_id: package.project_id,
+ package_id: package.id
+ )
+
response = service.execute
expect(package).not_to receive(:sync_maven_metadata)
diff --git a/spec/services/packages/mark_packages_for_destruction_service_spec.rb b/spec/services/packages/mark_packages_for_destruction_service_spec.rb
index 5c043b89de8..22278f9927d 100644
--- a/spec/services/packages/mark_packages_for_destruction_service_spec.rb
+++ b/spec/services/packages/mark_packages_for_destruction_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline do
+RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:packages) { create_list(:npm_package, 3, project: project) }
@@ -76,6 +76,11 @@ RSpec.describe Packages::MarkPackagesForDestructionService, :sidekiq_inline do
it 'returns an error ServiceResponse' do
expect(::Packages::Maven::Metadata::SyncService).not_to receive(:new)
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(StandardError),
+ package_ids: package_ids
+ )
+
expect { subject }.to not_change { ::Packages::Package.pending_destruction.count }
.and not_change { ::Packages::PackageFile.pending_destruction.count }
diff --git a/spec/services/packages/maven/create_package_service_spec.rb b/spec/services/packages/maven/create_package_service_spec.rb
index 11bf00c1399..2c528c3591e 100644
--- a/spec/services/packages/maven/create_package_service_spec.rb
+++ b/spec/services/packages/maven/create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Maven::CreatePackageService do
+RSpec.describe Packages::Maven::CreatePackageService, feature_category: :package_registry do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:app_name) { 'my-app' }
diff --git a/spec/services/packages/maven/find_or_create_package_service_spec.rb b/spec/services/packages/maven/find_or_create_package_service_spec.rb
index cca074e2fa6..8b84d2541eb 100644
--- a/spec/services/packages/maven/find_or_create_package_service_spec.rb
+++ b/spec/services/packages/maven/find_or_create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Maven::FindOrCreatePackageService do
+RSpec.describe Packages::Maven::FindOrCreatePackageService, feature_category: :package_registry do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
@@ -44,6 +44,15 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
end
end
+ shared_examples 'returning an error' do |with_message: ''|
+ it { expect { subject }.not_to change { project.package_files.count } }
+
+ it 'returns an error', :aggregate_failures do
+ expect(subject.payload).to be_empty
+ expect(subject.errors).to include(with_message)
+ end
+ end
+
context 'path with version' do
# Note that "path with version" and "file type maven metadata xml" only exists for snapshot versions
# In other words, we will never have an metadata xml upload on a path with version for a non snapshot version
@@ -128,11 +137,19 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
let!(:existing_package) { create(:maven_package, name: path, version: version, project: project) }
- it { expect { subject }.not_to change { project.package_files.count } }
+ let(:existing_file_name) { file_name }
+ let(:jar_file) { existing_package.package_files.with_file_name_like('%.jar').first }
- it 'returns an error', :aggregate_failures do
- expect(subject.payload).to be_empty
- expect(subject.errors).to include('Duplicate package is not allowed')
+ before do
+ jar_file.update_column(:file_name, existing_file_name)
+ end
+
+ it_behaves_like 'returning an error', with_message: 'Duplicate package is not allowed'
+
+ context 'for a SNAPSHOT version' do
+ let(:version) { '1.0.0-SNAPSHOT' }
+
+ it_behaves_like 'returning an error', with_message: 'Duplicate package is not allowed'
end
context 'when uploading to the versionless package which contains metadata about all versions' do
@@ -144,8 +161,7 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
context 'when uploading different non-duplicate files to the same package' do
before do
- package_file = existing_package.package_files.find_by(file_name: 'my-app-1.0-20180724.124855-1.jar')
- package_file.destroy!
+ jar_file.destroy!
end
it_behaves_like 'reuse existing package'
@@ -166,6 +182,27 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
it_behaves_like 'reuse existing package'
end
+
+ context 'when uploading a similar package file name with a classifier' do
+ let(:existing_file_name) { 'test.jar' }
+ let(:file_name) { 'test-javadoc.jar' }
+
+ it_behaves_like 'reuse existing package'
+
+ context 'for a SNAPSHOT version' do
+ let(:version) { '1.0.0-SNAPSHOT' }
+ let(:existing_file_name) { 'test-1.0-20230303.163304-1.jar' }
+ let(:file_name) { 'test-1.0-20230303.163304-1-javadoc.jar' }
+
+ it_behaves_like 'reuse existing package'
+ end
+ end
+ end
+
+ context 'with a very large file name' do
+ let(:params) { super().merge(file_name: 'a' * (described_class::MAX_FILE_NAME_LENGTH + 1)) }
+
+ it_behaves_like 'returning an error', with_message: 'File name is too long'
end
end
end
diff --git a/spec/services/packages/maven/metadata/append_package_file_service_spec.rb b/spec/services/packages/maven/metadata/append_package_file_service_spec.rb
index f3a90d31158..f65029f7b64 100644
--- a/spec/services/packages/maven/metadata/append_package_file_service_spec.rb
+++ b/spec/services/packages/maven/metadata/append_package_file_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Packages::Maven::Metadata::AppendPackageFileService do
+RSpec.describe ::Packages::Maven::Metadata::AppendPackageFileService, feature_category: :package_registry do
let_it_be(:package) { create(:maven_package, version: nil) }
let(:service) { described_class.new(package: package, metadata_content: content) }
diff --git a/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb b/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb
index 6fc1087940d..d0ef037b2d9 100644
--- a/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb
+++ b/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Packages::Maven::Metadata::CreatePluginsXmlService do
+RSpec.describe ::Packages::Maven::Metadata::CreatePluginsXmlService, feature_category: :package_registry do
let_it_be(:group_id) { 'my/test' }
let_it_be(:package) { create(:maven_package, name: group_id, version: nil) }
diff --git a/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb b/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb
index 70c2bbad87a..6ae84b5df4e 100644
--- a/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb
+++ b/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Packages::Maven::Metadata::CreateVersionsXmlService do
+RSpec.describe ::Packages::Maven::Metadata::CreateVersionsXmlService, feature_category: :package_registry do
let_it_be(:package) { create(:maven_package, version: nil) }
let(:versions_in_database) { %w[1.3 2.0-SNAPSHOT 1.6 1.4 1.5-SNAPSHOT] }
diff --git a/spec/services/packages/maven/metadata/sync_service_spec.rb b/spec/services/packages/maven/metadata/sync_service_spec.rb
index 9a704d749b3..eaed54d959b 100644
--- a/spec/services/packages/maven/metadata/sync_service_spec.rb
+++ b/spec/services/packages/maven/metadata/sync_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::Packages::Maven::Metadata::SyncService do
+RSpec.describe ::Packages::Maven::Metadata::SyncService, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project) }
diff --git a/spec/services/packages/npm/create_metadata_cache_service_spec.rb b/spec/services/packages/npm/create_metadata_cache_service_spec.rb
new file mode 100644
index 00000000000..75f822f0ddb
--- /dev/null
+++ b/spec/services/packages/npm/create_metadata_cache_service_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Npm::CreateMetadataCacheService, :clean_gitlab_redis_shared_state, feature_category: :package_registry do
+ include ExclusiveLeaseHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:package_name) { "@#{project.root_namespace.path}/npm-test" }
+ let_it_be(:package) { create(:npm_package, version: '1.0.0', project: project, name: package_name) }
+
+ let(:packages) { project.packages }
+ let(:lease_key) { "packages:npm:create_metadata_cache_service:metadata_caches:#{project.id}_#{package_name}" }
+ let(:service) { described_class.new(project, package_name, packages) }
+
+ describe '#execute' do
+ let(:npm_metadata_cache) { Packages::Npm::MetadataCache.last }
+
+ subject { service.execute }
+
+ it 'creates a new metadata cache', :aggregate_failures do
+ expect { subject }.to change { Packages::Npm::MetadataCache.count }.by(1)
+
+ metadata = Gitlab::Json.parse(npm_metadata_cache.file.read)
+
+ expect(npm_metadata_cache.package_name).to eq(package_name)
+ expect(npm_metadata_cache.project_id).to eq(project.id)
+ expect(npm_metadata_cache.size).to eq(metadata.to_json.bytesize)
+ expect(metadata['name']).to eq(package_name)
+ expect(metadata['versions'].keys).to contain_exactly('1.0.0')
+ end
+
+ context 'with existing metadata cache' do
+ let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package_name, project_id: project.id) }
+ let_it_be(:metadata) { Gitlab::Json.parse(npm_metadata_cache.file.read) }
+ let_it_be(:metadata_size) { npm_metadata_cache.size }
+ let_it_be(:tag_name) { 'new-tag' }
+ let_it_be(:tag) { create(:packages_tag, package: package, name: tag_name) }
+
+ it 'does not create a new metadata cache' do
+ expect { subject }.to change { Packages::Npm::MetadataCache.count }.by(0)
+ end
+
+ it 'updates the metadata cache', :aggregate_failures do
+ subject
+
+ new_metadata = Gitlab::Json.parse(npm_metadata_cache.file.read)
+
+ expect(new_metadata).not_to eq(metadata)
+ expect(new_metadata['dist_tags'].keys).to include(tag_name)
+ expect(npm_metadata_cache.reload.size).not_to eq(metadata_size)
+ end
+ end
+
+ it 'obtains a lease to create a new metadata cache' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+
+ subject
+ end
+
+ context 'when the lease is already taken' do
+ before do
+ stub_exclusive_lease_taken(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+ end
+
+ it 'does not create a new metadata cache' do
+ expect { subject }.to change { Packages::Npm::MetadataCache.count }.by(0)
+ end
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe '#lease_key' do
+ subject { service.send(:lease_key) }
+
+ it 'returns an unique key' do
+ is_expected.to eq lease_key
+ end
+ end
+end
diff --git a/spec/services/packages/npm/create_package_service_spec.rb b/spec/services/packages/npm/create_package_service_spec.rb
index ef8cdf2e8ab..a12d86412d8 100644
--- a/spec/services/packages/npm/create_package_service_spec.rb
+++ b/spec/services/packages/npm/create_package_service_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Npm::CreatePackageService do
+RSpec.describe Packages::Npm::CreatePackageService, feature_category: :package_registry do
+ include ExclusiveLeaseHelpers
+
let(:namespace) { create(:namespace) }
let(:project) { create(:project, namespace: namespace) }
let(:user) { create(:user) }
@@ -14,9 +16,11 @@ RSpec.describe Packages::Npm::CreatePackageService do
end
let(:package_name) { "@#{namespace.path}/my-app" }
- let(:version_data) { params.dig('versions', '1.0.1') }
+ let(:version_data) { params.dig('versions', version) }
+ let(:lease_key) { "packages:npm:create_package_service:packages:#{project.id}_#{package_name}_#{version}" }
+ let(:service) { described_class.new(project, user, params) }
- subject { described_class.new(project, user, params).execute }
+ subject { service.execute }
shared_examples 'valid package' do
it 'creates a package' do
@@ -57,17 +61,90 @@ RSpec.describe Packages::Npm::CreatePackageService do
end
end
- context 'with a too large metadata structure' do
- before do
- params[:versions][version][:test] = 'test' * 10000
+ context 'when the npm metadatum creation results in a size error' do
+ shared_examples 'a package json structure size too large error' do
+ it 'does not create the package' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(ActiveRecord::RecordInvalid),
+ field_sizes: expected_field_sizes
+ )
+
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Package json structure is too large')
+ .and not_change { Packages::Package.count }
+ .and not_change { Packages::Package.npm.count }
+ .and not_change { Packages::Tag.count }
+ .and not_change { Packages::Npm::Metadatum.count }
+ end
+ end
+
+ context 'when some of the field sizes are above the error tracking size' do
+ let(:package_json) do
+ params[:versions][version].except(*::Packages::Npm::CreatePackageService::PACKAGE_JSON_NOT_ALLOWED_FIELDS)
+ end
+
+ # Only the fields that exceed the field size limit should be passed to error tracking
+ let(:expected_field_sizes) do
+ {
+ 'test' => ('test' * 10000).size,
+ 'field2' => ('a' * (::Packages::Npm::Metadatum::MIN_PACKAGE_JSON_FIELD_SIZE_FOR_ERROR_TRACKING + 1)).size
+ }
+ end
+
+ before do
+ params[:versions][version][:test] = 'test' * 10000
+ params[:versions][version][:field1] =
+ 'a' * (::Packages::Npm::Metadatum::MIN_PACKAGE_JSON_FIELD_SIZE_FOR_ERROR_TRACKING - 1)
+ params[:versions][version][:field2] =
+ 'a' * (::Packages::Npm::Metadatum::MIN_PACKAGE_JSON_FIELD_SIZE_FOR_ERROR_TRACKING + 1)
+ end
+
+ it_behaves_like 'a package json structure size too large error'
+ end
+
+ context 'when all of the field sizes are below the error tracking size' do
+ let(:package_json) do
+ params[:versions][version].except(*::Packages::Npm::CreatePackageService::PACKAGE_JSON_NOT_ALLOWED_FIELDS)
+ end
+
+ let(:expected_size) { ('a' * (::Packages::Npm::Metadatum::MIN_PACKAGE_JSON_FIELD_SIZE_FOR_ERROR_TRACKING - 1)).size }
+ # Only the five largest fields should be passed to error tracking
+ let(:expected_field_sizes) do
+ {
+ 'field1' => expected_size,
+ 'field2' => expected_size,
+ 'field3' => expected_size,
+ 'field4' => expected_size,
+ 'field5' => expected_size
+ }
+ end
+
+ before do
+ 5.times do |i|
+ params[:versions][version]["field#{i + 1}"] =
+ 'a' * (::Packages::Npm::Metadatum::MIN_PACKAGE_JSON_FIELD_SIZE_FOR_ERROR_TRACKING - 1)
+ end
+ end
+
+ it_behaves_like 'a package json structure size too large error'
end
+ end
+
+ context 'when the npm metadatum creation results in a different error' do
+ it 'does not track the error' do
+ error_message = 'boom'
+ invalid_npm_metadatum_error = ActiveRecord::RecordInvalid.new(
+ build(:npm_metadatum).tap do |metadatum|
+ metadatum.errors.add(:base, error_message)
+ end
+ )
+
+ allow_next_instance_of(::Packages::Package) do |package|
+ allow(package).to receive(:create_npm_metadatum!).and_raise(invalid_npm_metadatum_error)
+ end
- it 'does not create the package' do
- expect { subject }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Package json structure is too large')
- .and not_change { Packages::Package.count }
- .and not_change { Packages::Package.npm.count }
- .and not_change { Packages::Tag.count }
- .and not_change { Packages::Npm::Metadatum.count }
+ expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
+
+ expect { subject }.to raise_error(ActiveRecord::RecordInvalid, /#{error_message}/)
end
end
@@ -216,5 +293,82 @@ RSpec.describe Packages::Npm::CreatePackageService do
it { expect { subject }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Version is invalid') }
end
end
+
+ context 'with empty attachment data' do
+ let(:params) { super().merge({ _attachments: { "#{package_name}-#{version}.tgz" => { data: '' } } }) }
+
+ it { expect(subject[:http_status]).to eq 400 }
+ it { expect(subject[:message]).to eq 'Attachment data is empty.' }
+ end
+
+ it 'obtains a lease to create a new package' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+
+ subject
+ end
+
+ context 'when the lease is already taken' do
+ before do
+ stub_exclusive_lease_taken(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+ end
+
+ it { expect(subject[:http_status]).to eq 400 }
+ it { expect(subject[:message]).to eq 'Could not obtain package lease.' }
+ end
+
+ context 'when many of the same packages are created at the same time', :delete do
+ it 'only creates one package' do
+ expect { create_packages(project, user, params) }.to change { Packages::Package.count }.by(1)
+ end
+ end
+
+ context 'when many packages with different versions are created at the same time', :delete do
+ it 'creates all packages' do
+ expect { create_packages_with_versions(project, user, params) }.to change { Packages::Package.count }.by(5)
+ end
+ end
+
+ def create_packages(project, user, params)
+ with_threads do
+ described_class.new(project, user, params).execute
+ end
+ end
+
+ def create_packages_with_versions(project, user, params)
+ with_threads do |i|
+ # Modify the package's version
+ modified_params = Gitlab::Json.parse(params.to_json
+ .gsub(version, "1.0.#{i}")).with_indifferent_access
+
+ described_class.new(project, user, modified_params).execute
+ end
+ end
+
+ def with_threads(count: 5, &block)
+ return unless block
+
+ # create a race condition - structure from https://blog.arkency.com/2015/09/testing-race-conditions/
+ wait_for_it = true
+
+ threads = Array.new(count) do |i|
+ Thread.new do
+ # A loop to make threads busy until we `join` them
+ true while wait_for_it
+
+ yield(i)
+ end
+ end
+
+ wait_for_it = false
+ threads.each(&:join)
+ end
+ end
+
+ describe '#lease_key' do
+ subject { service.send(:lease_key) }
+
+ it 'returns an unique key' do
+ is_expected.to eq lease_key
+ end
end
end
diff --git a/spec/services/packages/npm/create_tag_service_spec.rb b/spec/services/packages/npm/create_tag_service_spec.rb
index a4b07bf97cc..682effc9f4f 100644
--- a/spec/services/packages/npm/create_tag_service_spec.rb
+++ b/spec/services/packages/npm/create_tag_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Npm::CreateTagService do
+RSpec.describe Packages::Npm::CreateTagService, feature_category: :package_registry do
let(:package) { create(:npm_package) }
let(:tag_name) { 'test-tag' }
diff --git a/spec/services/packages/npm/deprecate_package_service_spec.rb b/spec/services/packages/npm/deprecate_package_service_spec.rb
new file mode 100644
index 00000000000..a3686e3a8b5
--- /dev/null
+++ b/spec/services/packages/npm/deprecate_package_service_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Npm::DeprecatePackageService, feature_category: :package_registry do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, namespace: namespace) }
+
+ let_it_be(:package_name) { "@#{namespace.path}/my-app" }
+ let_it_be_with_reload(:package_1) do
+ create(:npm_package, project: project, name: package_name, version: '1.0.1').tap do |package|
+ create(:npm_metadatum, package: package)
+ end
+ end
+
+ let_it_be(:package_2) do
+ create(:npm_package, project: project, name: package_name, version: '1.0.2').tap do |package|
+ create(:npm_metadatum, package: package)
+ end
+ end
+
+ let(:service) { described_class.new(project, params) }
+
+ subject(:execute) { service.execute }
+
+ describe '#execute' do
+ context 'when passing deprecatation message' do
+ let(:params) do
+ {
+ 'package_name' => package_name,
+ 'versions' => {
+ '1.0.1' => {
+ 'name' => package_name,
+ 'deprecated' => 'This version is deprecated'
+ },
+ '1.0.2' => {
+ 'name' => package_name,
+ 'deprecated' => 'This version is deprecated'
+ }
+ }
+ }
+ end
+
+ before do
+ package_json = package_2.npm_metadatum.package_json
+ package_2.npm_metadatum.update!(package_json: package_json.merge('deprecated' => 'old deprecation message'))
+ end
+
+ it 'adds or updates the deprecated field' do
+ expect { execute }
+ .to change { package_1.reload.npm_metadatum.package_json['deprecated'] }.to('This version is deprecated')
+ .and change { package_2.reload.npm_metadatum.package_json['deprecated'] }
+ .from('old deprecation message').to('This version is deprecated')
+ end
+
+ it 'executes 5 queries' do
+ queries = ActiveRecord::QueryRecorder.new do
+ execute
+ end
+
+ # 1. each_batch lower bound
+ # 2. each_batch upper bound
+ # 3. SELECT packages_packages.id, packages_packages.version FROM packages_packages
+ # 4. SELECT packages_npm_metadata.* FROM packages_npm_metadata
+ # 5. UPDATE packages_npm_metadata SET package_json =
+ expect(queries.count).to eq(5)
+ end
+ end
+
+ context 'when passing deprecated as empty string' do
+ let(:params) do
+ {
+ 'package_name' => package_name,
+ 'versions' => {
+ '1.0.1' => {
+ 'name' => package_name,
+ 'deprecated' => ''
+ }
+ }
+ }
+ end
+
+ before do
+ package_json = package_1.npm_metadatum.package_json
+ package_1.npm_metadatum.update!(package_json: package_json.merge('deprecated' => 'This version is deprecated'))
+ end
+
+ it 'removes the deprecation warning' do
+ expect { execute }
+ .to change { package_1.reload.npm_metadatum.package_json['deprecated'] }
+ .from('This version is deprecated').to(nil)
+ end
+ end
+
+ context 'when passing async: true to execute' do
+ let(:params) do
+ {
+ package_name: package_name,
+ versions: {
+ '1.0.1': {
+ deprecated: 'This version is deprecated'
+ }
+ }
+ }
+ end
+
+ it 'calls the worker and return' do
+ expect(::Packages::Npm::DeprecatePackageWorker).to receive(:perform_async).with(project.id, params)
+ expect(service).not_to receive(:packages)
+
+ service.execute(async: true)
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/npm/generate_metadata_service_spec.rb b/spec/services/packages/npm/generate_metadata_service_spec.rb
new file mode 100644
index 00000000000..1e3b0f71972
--- /dev/null
+++ b/spec/services/packages/npm/generate_metadata_service_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Packages::Npm::GenerateMetadataService, feature_category: :package_registry do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:package_name) { "@#{project.root_namespace.path}/test" }
+ let_it_be(:package1) { create(:npm_package, version: '2.0.4', project: project, name: package_name) }
+ let_it_be(:package2) { create(:npm_package, version: '2.0.6', project: project, name: package_name) }
+ let_it_be(:latest_package) { create(:npm_package, version: '2.0.11', project: project, name: package_name) }
+
+ let(:packages) { project.packages.npm.with_name(package_name).last_of_each_version }
+ let(:metadata) { described_class.new(package_name, packages).execute }
+
+ describe '#versions' do
+ let_it_be(:version_schema) { 'public_api/v4/packages/npm_package_version' }
+ let_it_be(:package_json) do
+ {
+ name: package_name,
+ version: '2.0.4',
+ deprecated: 'warning!',
+ bin: './cli.js',
+ directories: ['lib'],
+ engines: { npm: '^7.5.6' },
+ _hasShrinkwrap: false,
+ dist: {
+ tarball: 'http://localhost/tarball.tgz',
+ shasum: '1234567890'
+ },
+ custom_field: 'foo_bar'
+ }
+ end
+
+ subject { metadata[:versions] }
+
+ where(:has_dependencies, :has_metadatum) do
+ true | true
+ false | true
+ true | false
+ false | false
+ end
+
+ with_them do
+ if params[:has_dependencies]
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type| # rubocop:disable RSpec/UselessDynamicDefinition
+ let_it_be("package_dependency_link_for_#{dependency_type}") do
+ create(:packages_dependency_link, package: package1, dependency_type: dependency_type)
+ end
+ end
+ end
+
+ if params[:has_metadatum]
+ let_it_be(:package_metadatadum) { create(:npm_metadatum, package: package1, package_json: package_json) }
+ end
+
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject[package1.version].with_indifferent_access).to match_schema(version_schema) }
+ it { expect(subject[package2.version].with_indifferent_access).to match_schema(version_schema) }
+ it { expect(subject[package1.version]['custom_field']).to be_blank }
+
+ context 'for dependencies' do
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type|
+ if params[:has_dependencies]
+ it { expect(subject.dig(package1.version, dependency_type.to_s)).to be_any }
+ else
+ it { expect(subject.dig(package1.version, dependency_type)).to be nil }
+ end
+
+ it { expect(subject.dig(package2.version, dependency_type)).to be nil }
+ end
+ end
+
+ context 'for metadatum' do
+ ::Packages::Npm::GenerateMetadataService::PACKAGE_JSON_ALLOWED_FIELDS.each do |metadata_field|
+ if params[:has_metadatum]
+ it { expect(subject.dig(package1.version, metadata_field)).not_to be nil }
+ else
+ it { expect(subject.dig(package1.version, metadata_field)).to be nil }
+ end
+
+ it { expect(subject.dig(package2.version, metadata_field)).to be nil }
+ end
+ end
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one do
+ create_list(:npm_package, 5, project: project, name: package_name).each do |npm_package|
+ next unless has_dependencies
+
+ ::Packages::DependencyLink.dependency_types.each_key do |dependency_type|
+ create(:packages_dependency_link, package: npm_package, dependency_type: dependency_type)
+ end
+ end
+ end
+ end
+ end
+
+ context 'with package files pending destruction' do
+ let_it_be(:package_file_pending_destruction) do
+ create(:package_file, :pending_destruction, package: package2, file_sha1: 'pending_destruction_sha1')
+ end
+
+ let(:shasums) { subject.values.map { |v| v.dig(:dist, :shasum) } }
+
+ it 'does not return them' do
+ expect(shasums).not_to include(package_file_pending_destruction.file_sha1)
+ end
+ end
+ end
+
+ describe '#dist_tags' do
+ subject { metadata[:dist_tags] }
+
+ context 'for packages without tags' do
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject['latest']).to eq(latest_package.version) }
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one(only_dist_tags: true) do
+ create_list(:npm_package, 5, project: project, name: package_name)
+ end
+ end
+ end
+
+ context 'for packages with tags' do
+ let_it_be(:package_tag1) { create(:packages_tag, package: package1, name: 'release_a') }
+ let_it_be(:package_tag2) { create(:packages_tag, package: package1, name: 'test_release') }
+ let_it_be(:package_tag3) { create(:packages_tag, package: package2, name: 'release_b') }
+ let_it_be(:package_tag4) { create(:packages_tag, package: latest_package, name: 'release_c') }
+ let_it_be(:package_tag5) { create(:packages_tag, package: latest_package, name: 'latest') }
+
+ it { is_expected.to be_a(Hash) }
+ it { expect(subject[package_tag1.name]).to eq(package1.version) }
+ it { expect(subject[package_tag2.name]).to eq(package1.version) }
+ it { expect(subject[package_tag3.name]).to eq(package2.version) }
+ it { expect(subject[package_tag4.name]).to eq(latest_package.version) }
+ it { expect(subject[package_tag5.name]).to eq(latest_package.version) }
+
+ it 'avoids N+1 database queries' do
+ check_n_plus_one(only_dist_tags: true) do
+ create_list(:npm_package, 5, project: project, name: package_name).each_with_index do |npm_package, index|
+ create(:packages_tag, package: npm_package, name: "tag_#{index}")
+ end
+ end
+ end
+ end
+ end
+
+ context 'when passing only_dist_tags: true' do
+ subject { described_class.new(package_name, packages).execute(only_dist_tags: true) }
+
+ it 'returns only dist tags' do
+ expect(subject.payload.keys).to contain_exactly(:dist_tags)
+ end
+ end
+
+ def check_n_plus_one(only_dist_tags: false)
+ pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
+ control = ActiveRecord::QueryRecorder.new do
+ described_class.new(package_name, pkgs).execute(only_dist_tags: only_dist_tags)
+ end
+
+ yield
+
+ pkgs = project.packages.npm.with_name(package_name).last_of_each_version.preload_files
+
+ expect do
+ described_class.new(package_name, pkgs).execute(only_dist_tags: only_dist_tags)
+ end.not_to exceed_query_limit(control)
+ end
+end
diff --git a/spec/services/packages/nuget/create_dependency_service_spec.rb b/spec/services/packages/nuget/create_dependency_service_spec.rb
index 268c8837e25..10daec8b871 100644
--- a/spec/services/packages/nuget/create_dependency_service_spec.rb
+++ b/spec/services/packages/nuget/create_dependency_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Nuget::CreateDependencyService do
+RSpec.describe Packages::Nuget::CreateDependencyService, feature_category: :package_registry do
let_it_be(:package, reload: true) { create(:nuget_package) }
describe '#execute' do
diff --git a/spec/services/packages/nuget/metadata_extraction_service_spec.rb b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
index 12bab30b4a7..9177a5379d9 100644
--- a/spec/services/packages/nuget/metadata_extraction_service_spec.rb
+++ b/spec/services/packages/nuget/metadata_extraction_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Nuget::MetadataExtractionService do
+RSpec.describe Packages::Nuget::MetadataExtractionService, feature_category: :package_registry do
let_it_be(:package_file) { create(:nuget_package).package_files.first }
let(:service) { described_class.new(package_file.id) }
diff --git a/spec/services/packages/nuget/search_service_spec.rb b/spec/services/packages/nuget/search_service_spec.rb
index 66c91487a8f..b5f32c9b727 100644
--- a/spec/services/packages/nuget/search_service_spec.rb
+++ b/spec/services/packages/nuget/search_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Nuget::SearchService do
+RSpec.describe Packages::Nuget::SearchService, feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
diff --git a/spec/services/packages/nuget/sync_metadatum_service_spec.rb b/spec/services/packages/nuget/sync_metadatum_service_spec.rb
index 32093c48b76..ae07f312fcc 100644
--- a/spec/services/packages/nuget/sync_metadatum_service_spec.rb
+++ b/spec/services/packages/nuget/sync_metadatum_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Nuget::SyncMetadatumService do
+RSpec.describe Packages::Nuget::SyncMetadatumService, feature_category: :package_registry do
let_it_be(:package, reload: true) { create(:nuget_package) }
let_it_be(:metadata) do
{
diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
index 6a4dbeb10dc..c35863030b0 100644
--- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
+++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_shared_state do
+RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_shared_state, feature_category: :package_registry do
include ExclusiveLeaseHelpers
let!(:package) { create(:nuget_package, :processing, :with_symbol_package) }
@@ -259,11 +259,13 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_
]
invalid_names.each do |invalid_name|
- before do
- allow(service).to receive(:package_name).and_return(invalid_name)
- end
+ context "with #{invalid_name}" do
+ before do
+ allow(service).to receive(:package_name).and_return(invalid_name)
+ end
- it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ end
end
end
@@ -271,18 +273,19 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_
invalid_versions = [
'',
'555',
- '1.2',
'1./2.3',
'../../../../../1.2.3',
'%2e%2e%2f1.2.3'
]
invalid_versions.each do |invalid_version|
- before do
- allow(service).to receive(:package_version).and_return(invalid_version)
- end
+ context "with #{invalid_version}" do
+ before do
+ allow(service).to receive(:package_version).and_return(invalid_version)
+ end
- it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
+ end
end
end
end
diff --git a/spec/services/packages/pypi/create_package_service_spec.rb b/spec/services/packages/pypi/create_package_service_spec.rb
index 6794ab4d9d6..0d278e32e89 100644
--- a/spec/services/packages/pypi/create_package_service_spec.rb
+++ b/spec/services/packages/pypi/create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures do
+RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures, feature_category: :package_registry do
include PackagesManagerApiSpecHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/services/packages/remove_tag_service_spec.rb b/spec/services/packages/remove_tag_service_spec.rb
index 084635824e5..4ad478d487a 100644
--- a/spec/services/packages/remove_tag_service_spec.rb
+++ b/spec/services/packages/remove_tag_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::RemoveTagService do
+RSpec.describe Packages::RemoveTagService, feature_category: :package_registry do
let!(:package_tag) { create(:packages_tag) }
describe '#execute' do
diff --git a/spec/services/packages/rpm/parse_package_service_spec.rb b/spec/services/packages/rpm/parse_package_service_spec.rb
index f330587bfa0..80907d8f43f 100644
--- a/spec/services/packages/rpm/parse_package_service_spec.rb
+++ b/spec/services/packages/rpm/parse_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::ParsePackageService do
+RSpec.describe Packages::Rpm::ParsePackageService, feature_category: :package_registry do
let(:package_file) { File.open('spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm') }
describe 'dynamic private methods' do
diff --git a/spec/services/packages/rpm/repository_metadata/build_filelist_xml_service_spec.rb b/spec/services/packages/rpm/repository_metadata/build_filelist_xml_service_spec.rb
index d93d6ab9fcb..e0d9e192d97 100644
--- a/spec/services/packages/rpm/repository_metadata/build_filelist_xml_service_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/build_filelist_xml_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryMetadata::BuildFilelistXmlService do
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildFilelistXmlService, feature_category: :package_registry do
describe '#execute' do
subject { described_class.new(data).execute }
diff --git a/spec/services/packages/rpm/repository_metadata/build_other_xml_service_spec.rb b/spec/services/packages/rpm/repository_metadata/build_other_xml_service_spec.rb
index 201f9e67ce9..e81a436e006 100644
--- a/spec/services/packages/rpm/repository_metadata/build_other_xml_service_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/build_other_xml_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryMetadata::BuildOtherXmlService do
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildOtherXmlService, feature_category: :package_registry do
describe '#execute' do
subject { described_class.new(data).execute }
diff --git a/spec/services/packages/rpm/repository_metadata/build_primary_xml_service_spec.rb b/spec/services/packages/rpm/repository_metadata/build_primary_xml_service_spec.rb
index 9bbfa5c9863..1e534782841 100644
--- a/spec/services/packages/rpm/repository_metadata/build_primary_xml_service_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/build_primary_xml_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXmlService do
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXmlService, feature_category: :package_registry do
describe '#execute' do
subject { described_class.new(data).execute }
diff --git a/spec/services/packages/rpm/repository_metadata/build_repomd_xml_service_spec.rb b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_service_spec.rb
index cf28301fa2c..99fcf0fabbf 100644
--- a/spec/services/packages/rpm/repository_metadata/build_repomd_xml_service_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryMetadata::BuildRepomdXmlService do
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildRepomdXmlService, feature_category: :package_registry do
describe '#execute' do
subject { described_class.new(data).execute }
diff --git a/spec/services/packages/rpm/repository_metadata/update_xml_service_spec.rb b/spec/services/packages/rpm/repository_metadata/update_xml_service_spec.rb
index e351392ba1c..68a3ac7d82f 100644
--- a/spec/services/packages/rpm/repository_metadata/update_xml_service_spec.rb
+++ b/spec/services/packages/rpm/repository_metadata/update_xml_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rpm::RepositoryMetadata::UpdateXmlService do
+RSpec.describe Packages::Rpm::RepositoryMetadata::UpdateXmlService, feature_category: :package_registry do
describe '#execute' do
subject { described_class.new(filename: filename, xml: xml, data: data).execute }
diff --git a/spec/services/packages/rubygems/create_dependencies_service_spec.rb b/spec/services/packages/rubygems/create_dependencies_service_spec.rb
index b6e12b1cc61..d689bae96ff 100644
--- a/spec/services/packages/rubygems/create_dependencies_service_spec.rb
+++ b/spec/services/packages/rubygems/create_dependencies_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rubygems::CreateDependenciesService do
+RSpec.describe Packages::Rubygems::CreateDependenciesService, feature_category: :package_registry do
include RubygemsHelpers
let_it_be(:package) { create(:rubygems_package) }
diff --git a/spec/services/packages/rubygems/create_gemspec_service_spec.rb b/spec/services/packages/rubygems/create_gemspec_service_spec.rb
index 839fb4d955a..17890100b93 100644
--- a/spec/services/packages/rubygems/create_gemspec_service_spec.rb
+++ b/spec/services/packages/rubygems/create_gemspec_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rubygems::CreateGemspecService do
+RSpec.describe Packages::Rubygems::CreateGemspecService, feature_category: :package_registry do
include RubygemsHelpers
let_it_be(:package_file) { create(:package_file, :gem) }
diff --git a/spec/services/packages/rubygems/dependency_resolver_service_spec.rb b/spec/services/packages/rubygems/dependency_resolver_service_spec.rb
index bb84e0cd361..9a72c51e99c 100644
--- a/spec/services/packages/rubygems/dependency_resolver_service_spec.rb
+++ b/spec/services/packages/rubygems/dependency_resolver_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Rubygems::DependencyResolverService do
+RSpec.describe Packages::Rubygems::DependencyResolverService, feature_category: :package_registry do
let_it_be(:project) { create(:project, :private) }
let_it_be(:package) { create(:package, project: project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/packages/rubygems/metadata_extraction_service_spec.rb b/spec/services/packages/rubygems/metadata_extraction_service_spec.rb
index bbd5b6f3d59..87d63eff311 100644
--- a/spec/services/packages/rubygems/metadata_extraction_service_spec.rb
+++ b/spec/services/packages/rubygems/metadata_extraction_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
require 'rubygems/package'
-RSpec.describe Packages::Rubygems::MetadataExtractionService do
+RSpec.describe Packages::Rubygems::MetadataExtractionService, feature_category: :package_registry do
include RubygemsHelpers
let_it_be(:package) { create(:rubygems_package) }
diff --git a/spec/services/packages/rubygems/process_gem_service_spec.rb b/spec/services/packages/rubygems/process_gem_service_spec.rb
index caff338ef53..a1b4eae9655 100644
--- a/spec/services/packages/rubygems/process_gem_service_spec.rb
+++ b/spec/services/packages/rubygems/process_gem_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Packages::Rubygems::ProcessGemService do
+RSpec.describe Packages::Rubygems::ProcessGemService, feature_category: :package_registry do
include ExclusiveLeaseHelpers
include RubygemsHelpers
diff --git a/spec/services/packages/terraform_module/create_package_service_spec.rb b/spec/services/packages/terraform_module/create_package_service_spec.rb
index f73b5682835..3355dfcf5ec 100644
--- a/spec/services/packages/terraform_module/create_package_service_spec.rb
+++ b/spec/services/packages/terraform_module/create_package_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::TerraformModule::CreatePackageService do
+RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category: :package_registry do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/packages/update_package_file_service_spec.rb b/spec/services/packages/update_package_file_service_spec.rb
index d988049c43a..5d081059105 100644
--- a/spec/services/packages/update_package_file_service_spec.rb
+++ b/spec/services/packages/update_package_file_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::UpdatePackageFileService do
+RSpec.describe Packages::UpdatePackageFileService, feature_category: :package_registry do
let_it_be(:another_package) { create(:package) }
let_it_be(:old_file_name) { 'old_file_name.txt' }
let_it_be(:new_file_name) { 'new_file_name.txt' }
diff --git a/spec/services/packages/update_tags_service_spec.rb b/spec/services/packages/update_tags_service_spec.rb
index c4256699c94..d8f572fff32 100644
--- a/spec/services/packages/update_tags_service_spec.rb
+++ b/spec/services/packages/update_tags_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::UpdateTagsService do
+RSpec.describe Packages::UpdateTagsService, feature_category: :package_registry do
let_it_be(:package, reload: true) { create(:nuget_package) }
let(:tags) { %w(test-tag tag1 tag2 tag3) }
diff --git a/spec/services/pages/delete_service_spec.rb b/spec/services/pages/delete_service_spec.rb
index 8b9e72ac9b1..590378af22b 100644
--- a/spec/services/pages/delete_service_spec.rb
+++ b/spec/services/pages/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Pages::DeleteService do
+RSpec.describe Pages::DeleteService, feature_category: :pages do
let_it_be(:admin) { create(:admin) }
let(:project) { create(:project, path: "my.project") }
diff --git a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb b/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb
index 177467aac85..b18f62c1c28 100644
--- a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb
+++ b/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Pages::MigrateLegacyStorageToDeploymentService do
+RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: :pages do
let(:project) { create(:project, :repository) }
let(:service) { described_class.new(project) }
diff --git a/spec/services/pages/zip_directory_service_spec.rb b/spec/services/pages/zip_directory_service_spec.rb
index 00fe75dbbfd..4917bc65a02 100644
--- a/spec/services/pages/zip_directory_service_spec.rb
+++ b/spec/services/pages/zip_directory_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Pages::ZipDirectoryService do
+RSpec.describe Pages::ZipDirectoryService, feature_category: :pages do
around do |example|
Dir.mktmpdir do |dir|
@work_dir = dir
diff --git a/spec/services/pages_domains/create_acme_order_service_spec.rb b/spec/services/pages_domains/create_acme_order_service_spec.rb
index 35b2cc56973..97534d52c67 100644
--- a/spec/services/pages_domains/create_acme_order_service_spec.rb
+++ b/spec/services/pages_domains/create_acme_order_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PagesDomains::CreateAcmeOrderService do
+RSpec.describe PagesDomains::CreateAcmeOrderService, feature_category: :pages do
include LetsEncryptHelpers
let(:pages_domain) { create(:pages_domain) }
diff --git a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
index ecb445fa441..2377fbcf003 100644
--- a/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
+++ b/spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService do
+RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_category: :pages do
include LetsEncryptHelpers
let(:pages_domain) { create(:pages_domain, :without_certificate, :without_key) }
diff --git a/spec/services/personal_access_tokens/create_service_spec.rb b/spec/services/personal_access_tokens/create_service_spec.rb
index b8a4c8f30d2..d80be5cccce 100644
--- a/spec/services/personal_access_tokens/create_service_spec.rb
+++ b/spec/services/personal_access_tokens/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PersonalAccessTokens::CreateService do
+RSpec.describe PersonalAccessTokens::CreateService, feature_category: :system_access do
shared_examples_for 'a successfully created token' do
it 'creates personal access token record' do
expect(subject.success?).to be true
@@ -40,7 +40,7 @@ RSpec.describe PersonalAccessTokens::CreateService do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:params) { { name: 'Test token', impersonation: false, scopes: [:api], expires_at: Date.today + 1.month } }
- let(:service) { described_class.new(current_user: current_user, target_user: user, params: params) }
+ let(:service) { described_class.new(current_user: current_user, target_user: user, params: params, concatenate_errors: false) }
let(:token) { subject.payload[:personal_access_token] }
context 'when current_user is an administrator' do
@@ -66,5 +66,21 @@ RSpec.describe PersonalAccessTokens::CreateService do
it_behaves_like 'a successfully created token'
end
end
+
+ context 'when invalid scope' do
+ let(:params) { { name: 'Test token', impersonation: false, scopes: [:no_valid], expires_at: Date.today + 1.month } }
+
+ context 'when concatenate_errors: true' do
+ let(:service) { described_class.new(current_user: user, target_user: user, params: params) }
+
+ it { expect(subject.message).to be_an_instance_of(String) }
+ end
+
+ context 'when concatenate_errors: false' do
+ let(:service) { described_class.new(current_user: user, target_user: user, params: params, concatenate_errors: false) }
+
+ it { expect(subject.message).to be_an_instance_of(Array) }
+ end
+ end
end
end
diff --git a/spec/services/personal_access_tokens/last_used_service_spec.rb b/spec/services/personal_access_tokens/last_used_service_spec.rb
index 6fc74e27dd9..20eabc20338 100644
--- a/spec/services/personal_access_tokens/last_used_service_spec.rb
+++ b/spec/services/personal_access_tokens/last_used_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PersonalAccessTokens::LastUsedService do
+RSpec.describe PersonalAccessTokens::LastUsedService, feature_category: :system_access do
describe '#execute' do
subject { described_class.new(personal_access_token).execute }
diff --git a/spec/services/personal_access_tokens/revoke_service_spec.rb b/spec/services/personal_access_tokens/revoke_service_spec.rb
index a9b4df9749f..4c5d106660a 100644
--- a/spec/services/personal_access_tokens/revoke_service_spec.rb
+++ b/spec/services/personal_access_tokens/revoke_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PersonalAccessTokens::RevokeService do
+RSpec.describe PersonalAccessTokens::RevokeService, feature_category: :system_access do
shared_examples_for 'a successfully revoked token' do
it { expect(subject.success?).to be true }
it { expect(service.token.revoked?).to be true }
diff --git a/spec/services/personal_access_tokens/rotate_service_spec.rb b/spec/services/personal_access_tokens/rotate_service_spec.rb
new file mode 100644
index 00000000000..e026b0b6485
--- /dev/null
+++ b/spec/services/personal_access_tokens/rotate_service_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PersonalAccessTokens::RotateService, feature_category: :system_access do
+ describe '#execute' do
+ let_it_be(:token, reload: true) { create(:personal_access_token) }
+
+ subject(:response) { described_class.new(token.user, token).execute }
+
+ it "rotates user's own token", :freeze_time do
+ expect(response).to be_success
+
+ new_token = response.payload[:personal_access_token]
+
+ expect(new_token.token).not_to eq(token.token)
+ expect(new_token.expires_at).to eq(Date.today + 1.week)
+ expect(new_token.user).to eq(token.user)
+ end
+
+ it 'revokes the previous token' do
+ expect { response }.to change { token.reload.revoked? }.from(false).to(true)
+
+ new_token = response.payload[:personal_access_token]
+ expect(new_token).not_to be_revoked
+ end
+
+ context 'when user tries to rotate already revoked token' do
+ let_it_be(:token, reload: true) { create(:personal_access_token, :revoked) }
+
+ it 'returns an error' do
+ expect { response }.not_to change { token.reload.revoked? }.from(true)
+ expect(response).to be_error
+ expect(response.message).to eq('token already revoked')
+ end
+ end
+
+ context 'when revoking previous token fails' do
+ it 'returns an error' do
+ expect(token).to receive(:revoke!).and_return(false)
+
+ expect(response).to be_error
+ end
+ end
+
+ context 'when creating the new token fails' do
+ let(:error_message) { 'boom!' }
+
+ before do
+ allow_next_instance_of(PersonalAccessToken) do |token|
+ allow(token).to receive_message_chain(:errors, :full_messages, :to_sentence).and_return(error_message)
+ allow(token).to receive_message_chain(:errors, :clear)
+ allow(token).to receive_message_chain(:errors, :empty?).and_return(false)
+ end
+ end
+
+ it 'returns an error' do
+ expect(response).to be_error
+ expect(response.message).to eq(error_message)
+ end
+
+ it 'reverts the changes' do
+ expect { response }.not_to change { token.reload.revoked? }.from(false)
+ end
+ end
+ end
+end
diff --git a/spec/services/post_receive_service_spec.rb b/spec/services/post_receive_service_spec.rb
index aa955b3445b..13bd103003f 100644
--- a/spec/services/post_receive_service_spec.rb
+++ b/spec/services/post_receive_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PostReceiveService do
+RSpec.describe PostReceiveService, feature_category: :team_planning do
include GitlabShellHelpers
include Gitlab::Routing
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index d1bc10cfd28..6fa44310ae5 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PreviewMarkdownService do
+RSpec.describe PreviewMarkdownService, feature_category: :team_planning do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -117,6 +117,16 @@ RSpec.describe PreviewMarkdownService do
expect(result[:text]).to eq 'Please do it'
end
+ context 'when render_quick_actions' do
+ it 'keeps quick actions' do
+ params[:render_quick_actions] = true
+
+ result = service.execute
+
+ expect(result[:text]).to eq "Please do it\n\n/assign #{user.to_reference}"
+ end
+ end
+
it 'explains quick actions effect' do
result = service.execute
diff --git a/spec/services/product_analytics/build_activity_graph_service_spec.rb b/spec/services/product_analytics/build_activity_graph_service_spec.rb
index e303656da34..cd1bc42e156 100644
--- a/spec/services/product_analytics/build_activity_graph_service_spec.rb
+++ b/spec/services/product_analytics/build_activity_graph_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProductAnalytics::BuildActivityGraphService do
+RSpec.describe ProductAnalytics::BuildActivityGraphService, feature_category: :product_analytics do
let_it_be(:project) { create(:project) }
let_it_be(:time_now) { Time.zone.now }
let_it_be(:time_ago) { Time.zone.now - 5.days }
diff --git a/spec/services/product_analytics/build_graph_service_spec.rb b/spec/services/product_analytics/build_graph_service_spec.rb
index 933a2bfee92..ee0e2190501 100644
--- a/spec/services/product_analytics/build_graph_service_spec.rb
+++ b/spec/services/product_analytics/build_graph_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProductAnalytics::BuildGraphService do
+RSpec.describe ProductAnalytics::BuildGraphService, feature_category: :product_analytics do
let_it_be(:project) { create(:project) }
let_it_be(:events) do
diff --git a/spec/services/projects/after_rename_service_spec.rb b/spec/services/projects/after_rename_service_spec.rb
index 72bb0adbf56..3097d6d1498 100644
--- a/spec/services/projects/after_rename_service_spec.rb
+++ b/spec/services/projects/after_rename_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::AfterRenameService do
+RSpec.describe Projects::AfterRenameService, feature_category: :projects do
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::Hashed.new(project) }
let!(:path_before_rename) { project.path }
diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb
index aa2ef39bf98..8cd9b5d3e00 100644
--- a/spec/services/projects/alerting/notify_service_spec.rb
+++ b/spec/services/projects/alerting/notify_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Alerting::NotifyService do
+RSpec.describe Projects::Alerting::NotifyService, feature_category: :projects do
let_it_be_with_reload(:project) { create(:project) }
let(:payload) { ActionController::Parameters.new(payload_raw).permit! }
diff --git a/spec/services/projects/all_issues_count_service_spec.rb b/spec/services/projects/all_issues_count_service_spec.rb
index d7e35991940..e8e08a25c45 100644
--- a/spec/services/projects/all_issues_count_service_spec.rb
+++ b/spec/services/projects/all_issues_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::AllIssuesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::AllIssuesCountService, :use_clean_rails_memory_store_caching, feature_category: :projects do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:banned_user) { create(:user, :banned) }
diff --git a/spec/services/projects/all_merge_requests_count_service_spec.rb b/spec/services/projects/all_merge_requests_count_service_spec.rb
index 13954d688aa..ca10fbc00ad 100644
--- a/spec/services/projects/all_merge_requests_count_service_spec.rb
+++ b/spec/services/projects/all_merge_requests_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::AllMergeRequestsCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::AllMergeRequestsCountService, :use_clean_rails_memory_store_caching, feature_category: :projects do
let_it_be(:project) { create(:project) }
subject { described_class.new(project) }
@@ -11,18 +11,9 @@ RSpec.describe Projects::AllMergeRequestsCountService, :use_clean_rails_memory_s
describe '#count' do
it 'returns the number of all merge requests' do
- create(:merge_request,
- :opened,
- source_project: project,
- target_project: project)
- create(:merge_request,
- :closed,
- source_project: project,
- target_project: project)
- create(:merge_request,
- :merged,
- source_project: project,
- target_project: project)
+ create(:merge_request, :opened, source_project: project, target_project: project)
+ create(:merge_request, :closed, source_project: project, target_project: project)
+ create(:merge_request, :merged, source_project: project, target_project: project)
expect(subject.count).to eq(3)
end
diff --git a/spec/services/projects/android_target_platform_detector_service_spec.rb b/spec/services/projects/android_target_platform_detector_service_spec.rb
deleted file mode 100644
index 74fd320bb48..00000000000
--- a/spec/services/projects/android_target_platform_detector_service_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::AndroidTargetPlatformDetectorService do
- let_it_be(:project) { build(:project) }
-
- subject { described_class.new(project).execute }
-
- before do
- allow(Gitlab::FileFinder).to receive(:new) { finder }
- end
-
- context 'when project is not an Android project' do
- let(:finder) { instance_double(Gitlab::FileFinder, find: []) }
-
- it { is_expected.to be_nil }
- end
-
- context 'when project is an Android project' do
- let(:finder) { instance_double(Gitlab::FileFinder) }
-
- before do
- query = described_class::MANIFEST_FILE_SEARCH_QUERY
- allow(finder).to receive(:find).with(query) { [instance_double(Gitlab::Search::FoundBlob)] }
- end
-
- it { is_expected.to eq :android }
- end
-end
diff --git a/spec/services/projects/apple_target_platform_detector_service_spec.rb b/spec/services/projects/apple_target_platform_detector_service_spec.rb
index 6391161824c..787faaa0f79 100644
--- a/spec/services/projects/apple_target_platform_detector_service_spec.rb
+++ b/spec/services/projects/apple_target_platform_detector_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::AppleTargetPlatformDetectorService do
+RSpec.describe Projects::AppleTargetPlatformDetectorService, feature_category: :projects do
let_it_be(:project) { build(:project) }
subject { described_class.new(project).execute }
diff --git a/spec/services/projects/auto_devops/disable_service_spec.rb b/spec/services/projects/auto_devops/disable_service_spec.rb
index 1f161990fb2..fd70362a53f 100644
--- a/spec/services/projects/auto_devops/disable_service_spec.rb
+++ b/spec/services/projects/auto_devops/disable_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::AutoDevops::DisableService, '#execute' do
+RSpec.describe Projects::AutoDevops::DisableService, '#execute', feature_category: :auto_devops do
let(:project) { create(:project, :repository, :auto_devops) }
let(:auto_devops) { project.auto_devops }
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index bc95a1f3c8b..9d3075874a2 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::AutocompleteService do
+RSpec.describe Projects::AutocompleteService, feature_category: :projects do
describe '#issues' do
describe 'confidential issues' do
let(:author) { create(:user) }
diff --git a/spec/services/projects/batch_open_issues_count_service_spec.rb b/spec/services/projects/batch_open_issues_count_service_spec.rb
index 89a4abbf9c9..d29115a697f 100644
--- a/spec/services/projects/batch_open_issues_count_service_spec.rb
+++ b/spec/services/projects/batch_open_issues_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::BatchOpenIssuesCountService do
+RSpec.describe Projects::BatchOpenIssuesCountService, feature_category: :projects do
let!(:project_1) { create(:project) }
let!(:project_2) { create(:project) }
diff --git a/spec/services/projects/batch_open_merge_requests_count_service_spec.rb b/spec/services/projects/batch_open_merge_requests_count_service_spec.rb
new file mode 100644
index 00000000000..96fc6c5e9dd
--- /dev/null
+++ b/spec/services/projects/batch_open_merge_requests_count_service_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::BatchOpenMergeRequestsCountService, feature_category: :code_review_workflow do
+ subject { described_class.new([project_1, project_2]) }
+
+ let_it_be(:project_1) { create(:project) }
+ let_it_be(:project_2) { create(:project) }
+
+ describe '#refresh_cache_and_retrieve_data', :use_clean_rails_memory_store_caching do
+ before do
+ create(:merge_request, source_project: project_1, target_project: project_1)
+ create(:merge_request, source_project: project_2, target_project: project_2)
+ end
+
+ it 'refreshes cache keys correctly when cache is clean', :aggregate_failures do
+ subject.refresh_cache_and_retrieve_data
+
+ expect(Rails.cache.read(get_cache_key(subject, project_1))).to eq(1)
+ expect(Rails.cache.read(get_cache_key(subject, project_2))).to eq(1)
+
+ expect { subject.refresh_cache_and_retrieve_data }.not_to exceed_query_limit(0)
+ end
+ end
+
+ def get_cache_key(subject, project)
+ subject.count_service
+ .new(project)
+ .cache_key
+ end
+end
diff --git a/spec/services/projects/blame_service_spec.rb b/spec/services/projects/blame_service_spec.rb
deleted file mode 100644
index 52b0ed3412d..00000000000
--- a/spec/services/projects/blame_service_spec.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::BlameService, :aggregate_failures do
- subject(:service) { described_class.new(blob, commit, params) }
-
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:commit) { project.repository.commit }
- let_it_be(:blob) { project.repository.blob_at('HEAD', 'README.md') }
-
- let(:params) { { page: page } }
- let(:page) { nil }
-
- before do
- stub_const("#{described_class.name}::PER_PAGE", 2)
- end
-
- describe '#blame' do
- subject { service.blame }
-
- it 'returns a correct Gitlab::Blame object' do
- is_expected.to be_kind_of(Gitlab::Blame)
-
- expect(subject.blob).to eq(blob)
- expect(subject.commit).to eq(commit)
- expect(subject.range).to eq(1..2)
- end
-
- describe 'Pagination range calculation' do
- subject { service.blame.range }
-
- context 'with page = 1' do
- let(:page) { 1 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'with page = 2' do
- let(:page) { 2 }
-
- it { is_expected.to eq(3..4) }
- end
-
- context 'with page = 3 (overlimit)' do
- let(:page) { 3 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'with page = 0 (incorrect)' do
- let(:page) { 0 }
-
- it { is_expected.to eq(1..2) }
- end
-
- context 'when user disabled the pagination' do
- let(:params) { super().merge(no_pagination: 1) }
-
- it { is_expected.to be_nil }
- end
-
- context 'when feature flag disabled' do
- before do
- stub_feature_flags(blame_page_pagination: false)
- end
-
- it { is_expected.to be_nil }
- end
- end
- end
-
- describe '#pagination' do
- subject { service.pagination }
-
- it 'returns a pagination object' do
- is_expected.to be_kind_of(Kaminari::PaginatableArray)
-
- expect(subject.current_page).to eq(1)
- expect(subject.total_pages).to eq(2)
- expect(subject.total_count).to eq(4)
- end
-
- context 'when user disabled the pagination' do
- let(:params) { super().merge(no_pagination: 1) }
-
- it { is_expected.to be_nil }
- end
-
- context 'when feature flag disabled' do
- before do
- stub_feature_flags(blame_page_pagination: false)
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'when per_page is above the global max per page limit' do
- before do
- stub_const("#{described_class.name}::PER_PAGE", 1000)
- allow(blob).to receive_message_chain(:data, :lines, :count) { 500 }
- end
-
- it 'returns a correct pagination object' do
- is_expected.to be_kind_of(Kaminari::PaginatableArray)
-
- expect(subject.current_page).to eq(1)
- expect(subject.total_pages).to eq(1)
- expect(subject.total_count).to eq(500)
- end
- end
-
- describe 'Pagination attributes' do
- using RSpec::Parameterized::TableSyntax
-
- where(:page, :current_page, :total_pages) do
- 1 | 1 | 2
- 2 | 2 | 2
- 3 | 1 | 2 # Overlimit
- 0 | 1 | 2 # Incorrect
- end
-
- with_them do
- it 'returns the correct pagination attributes' do
- expect(subject.current_page).to eq(current_page)
- expect(subject.total_pages).to eq(total_pages)
- end
- end
- end
- end
-end
diff --git a/spec/services/projects/branches_by_mode_service_spec.rb b/spec/services/projects/branches_by_mode_service_spec.rb
index 9a63563b37b..bfe76b34310 100644
--- a/spec/services/projects/branches_by_mode_service_spec.rb
+++ b/spec/services/projects/branches_by_mode_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::BranchesByModeService do
+RSpec.describe Projects::BranchesByModeService, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/services/projects/cleanup_service_spec.rb b/spec/services/projects/cleanup_service_spec.rb
index f2c052d9397..533a09f7bc7 100644
--- a/spec/services/projects/cleanup_service_spec.rb
+++ b/spec/services/projects/cleanup_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::CleanupService do
+RSpec.describe Projects::CleanupService, feature_category: :source_code_management do
subject(:service) { described_class.new(project) }
describe '.enqueue' do
diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
index 8311c4e4d9b..b8ad63d9b8a 100644
--- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::CleanupTagsService do
+RSpec.describe Projects::ContainerRepository::CleanupTagsService, feature_category: :container_registry do
let_it_be_with_reload(:container_repository) { create(:container_repository) }
let_it_be(:user) { container_repository.project.owner }
@@ -77,7 +77,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do
context 'with a migrated repository' do
before do
- container_repository.update_column(:migration_state, :import_done)
+ allow(container_repository).to receive(:migrated?).and_return(true)
end
context 'supporting the gitlab api' do
@@ -99,8 +99,7 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do
context 'with a non migrated repository' do
before do
- container_repository.update_column(:migration_state, :default)
- container_repository.update!(created_at: ContainerRepository::MIGRATION_PHASE_1_ENDED_AT - 1.week)
+ allow(container_repository).to receive(:migrated?).and_return(false)
end
it_behaves_like 'calling service', ::Projects::ContainerRepository::ThirdParty::CleanupTagsService, extra_log_data: { third_party_cleanup_tags_service: true }
diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb
index 9e6849aa514..5b67d614dfb 100644
--- a/spec/services/projects/container_repository/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::DeleteTagsService do
+RSpec.describe Projects::ContainerRepository::DeleteTagsService, feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
include_context 'container repository delete tags service shared context'
diff --git a/spec/services/projects/container_repository/destroy_service_spec.rb b/spec/services/projects/container_repository/destroy_service_spec.rb
index fed1d13daa5..a142360f99d 100644
--- a/spec/services/projects/container_repository/destroy_service_spec.rb
+++ b/spec/services/projects/container_repository/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::DestroyService do
+RSpec.describe Projects::ContainerRepository::DestroyService, feature_category: :container_registry do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:params) { {} }
diff --git a/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
index b06a5709bd5..f662d8bfc0c 100644
--- a/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
+RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService, feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
include_context 'for a cleanup tags service'
@@ -11,11 +11,13 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :private) }
- let(:repository) { create(:container_repository, :root, :import_done, project: project) }
+ let(:repository) { create(:container_repository, :root, project: project) }
let(:service) { described_class.new(container_repository: repository, current_user: user, params: params) }
let(:tags) { %w[latest A Ba Bb C D E] }
before do
+ allow(repository).to receive(:migrated?).and_return(true)
+
project.add_maintainer(user) if user
stub_container_registry_config(enabled: true)
@@ -47,23 +49,23 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
let(:tags_page_size) { 2 }
it_behaves_like 'when regex matching everything is specified',
- delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
+ delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
it_behaves_like 'when regex matching everything is specified and latest is not kept',
- delete_expectations: [%w[latest A], %w[Ba Bb], %w[C D], %w[E]]
+ delete_expectations: [%w[latest A], %w[Ba Bb], %w[C D], %w[E]]
it_behaves_like 'when delete regex matching specific tags is used'
it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex'
it_behaves_like 'with allow regex value',
- delete_expectations: [%w[A], %w[C D], %w[E]]
+ delete_expectations: [%w[A], %w[C D], %w[E]]
it_behaves_like 'when keeping only N tags',
- delete_expectations: [%w[Bb]]
+ delete_expectations: [%w[Bb]]
it_behaves_like 'when not keeping N tags',
- delete_expectations: [%w[A], %w[Ba Bb], %w[C]]
+ delete_expectations: [%w[A], %w[Ba Bb], %w[C]]
context 'when removing keeping only 3' do
let(:params) do
@@ -77,13 +79,13 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
end
it_behaves_like 'when removing older than 1 day',
- delete_expectations: [%w[Ba Bb], %w[C]]
+ delete_expectations: [%w[Ba Bb], %w[C]]
it_behaves_like 'when combining all parameters',
- delete_expectations: [%w[Bb], %w[C]]
+ delete_expectations: [%w[Bb], %w[C]]
it_behaves_like 'when running a container_expiration_policy',
- delete_expectations: [%w[Bb], %w[C]]
+ delete_expectations: [%w[Bb], %w[C]]
context 'with a timeout' do
let(:params) do
@@ -111,7 +113,7 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
end
it_behaves_like 'when regex matching everything is specified',
- delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
+ delete_expectations: [%w[A], %w[Ba Bb], %w[C D], %w[E]]
end
end
end
@@ -120,32 +122,46 @@ RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
let(:tags_page_size) { 1000 }
it_behaves_like 'when regex matching everything is specified',
- delete_expectations: [%w[A Ba Bb C D E]]
+ delete_expectations: [%w[A Ba Bb C D E]]
it_behaves_like 'when delete regex matching specific tags is used'
it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex'
it_behaves_like 'with allow regex value',
- delete_expectations: [%w[A C D E]]
+ delete_expectations: [%w[A C D E]]
it_behaves_like 'when keeping only N tags',
- delete_expectations: [%w[Ba Bb C]]
+ delete_expectations: [%w[Ba Bb C]]
it_behaves_like 'when not keeping N tags',
- delete_expectations: [%w[A Ba Bb C]]
+ delete_expectations: [%w[A Ba Bb C]]
it_behaves_like 'when removing keeping only 3',
- delete_expectations: [%w[Ba Bb C]]
+ delete_expectations: [%w[Ba Bb C]]
it_behaves_like 'when removing older than 1 day',
- delete_expectations: [%w[Ba Bb C]]
+ delete_expectations: [%w[Ba Bb C]]
it_behaves_like 'when combining all parameters',
- delete_expectations: [%w[Ba Bb C]]
+ delete_expectations: [%w[Ba Bb C]]
it_behaves_like 'when running a container_expiration_policy',
- delete_expectations: [%w[Ba Bb C]]
+ delete_expectations: [%w[Ba Bb C]]
+ end
+
+ context 'with no tags page' do
+ let(:tags_page_size) { 1000 }
+ let(:deleted) { [] }
+ let(:params) { {} }
+
+ before do
+ allow(repository.gitlab_api_client)
+ .to receive(:tags)
+ .and_return({})
+ end
+
+ it { is_expected.to eq(expected_service_response(status: :success, deleted: [], original_size: 0)) }
end
end
diff --git a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
index f03912dba80..c4e6c7f4a11 100644
--- a/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
+RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService, feature_category: :container_registry do
include_context 'container repository delete tags service shared context'
let(:service) { described_class.new(repository, tags) }
diff --git a/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
index 7227834b131..836e722eb99 100644
--- a/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/third_party/cleanup_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::ThirdParty::CleanupTagsService, :clean_gitlab_redis_cache do
+RSpec.describe Projects::ContainerRepository::ThirdParty::CleanupTagsService, :clean_gitlab_redis_cache, feature_category: :container_registry do
using RSpec::Parameterized::TableSyntax
include_context 'for a cleanup tags service'
@@ -42,112 +42,112 @@ RSpec.describe Projects::ContainerRepository::ThirdParty::CleanupTagsService, :c
subject { service.execute }
it_behaves_like 'when regex matching everything is specified',
- delete_expectations: [%w[A Ba Bb C D E]],
- service_response_extra: {
- before_truncate_size: 6,
- after_truncate_size: 6,
- before_delete_size: 6,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[A Ba Bb C D E]],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 6,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when regex matching everything is specified and latest is not kept',
- delete_expectations: [%w[A Ba Bb C D E latest]],
- service_response_extra: {
- before_truncate_size: 7,
- after_truncate_size: 7,
- before_delete_size: 7,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[A Ba Bb C D E latest]],
+ service_response_extra: {
+ before_truncate_size: 7,
+ after_truncate_size: 7,
+ before_delete_size: 7,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when delete regex matching specific tags is used',
- service_response_extra: {
- before_truncate_size: 2,
- after_truncate_size: 2,
- before_delete_size: 2,
- cached_tags_count: 0
- },
- supports_caching: true
+ service_response_extra: {
+ before_truncate_size: 2,
+ after_truncate_size: 2,
+ before_delete_size: 2,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex',
- service_response_extra: {
- before_truncate_size: 1,
- after_truncate_size: 1,
- before_delete_size: 1,
- cached_tags_count: 0
- },
- supports_caching: true
+ service_response_extra: {
+ before_truncate_size: 1,
+ after_truncate_size: 1,
+ before_delete_size: 1,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'with allow regex value',
- delete_expectations: [%w[A C D E]],
- service_response_extra: {
- before_truncate_size: 4,
- after_truncate_size: 4,
- before_delete_size: 4,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[A C D E]],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 4,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when keeping only N tags',
- delete_expectations: [%w[Bb Ba C]],
- service_response_extra: {
- before_truncate_size: 4,
- after_truncate_size: 4,
- before_delete_size: 3,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[Bb Ba C]],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when not keeping N tags',
- delete_expectations: [%w[A Ba Bb C]],
- service_response_extra: {
- before_truncate_size: 4,
- after_truncate_size: 4,
- before_delete_size: 4,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[A Ba Bb C]],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 4,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when removing keeping only 3',
- delete_expectations: [%w[Bb Ba C]],
- service_response_extra: {
- before_truncate_size: 6,
- after_truncate_size: 6,
- before_delete_size: 3,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[Bb Ba C]],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when removing older than 1 day',
- delete_expectations: [%w[Ba Bb C]],
- service_response_extra: {
- before_truncate_size: 6,
- after_truncate_size: 6,
- before_delete_size: 3,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[Ba Bb C]],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when combining all parameters',
- delete_expectations: [%w[Bb Ba C]],
- service_response_extra: {
- before_truncate_size: 6,
- after_truncate_size: 6,
- before_delete_size: 3,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[Bb Ba C]],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
it_behaves_like 'when running a container_expiration_policy',
- delete_expectations: [%w[Bb Ba C]],
- service_response_extra: {
- before_truncate_size: 6,
- after_truncate_size: 6,
- before_delete_size: 3,
- cached_tags_count: 0
- },
- supports_caching: true
+ delete_expectations: [%w[Bb Ba C]],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
context 'when running a container_expiration_policy with caching' do
let(:user) { nil }
diff --git a/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb b/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb
index 4de36452684..0c297b6e1f7 100644
--- a/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/third_party/delete_tags_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ContainerRepository::ThirdParty::DeleteTagsService do
+RSpec.describe Projects::ContainerRepository::ThirdParty::DeleteTagsService, feature_category: :container_registry do
include_context 'container repository delete tags service shared context'
let(:service) { described_class.new(repository, tags) }
diff --git a/spec/services/projects/count_service_spec.rb b/spec/services/projects/count_service_spec.rb
index 11b2b57a277..71940fa396e 100644
--- a/spec/services/projects/count_service_spec.rb
+++ b/spec/services/projects/count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::CountService do
+RSpec.describe Projects::CountService, feature_category: :projects do
let(:project) { build(:project, id: 1) }
let(:service) { described_class.new(project) }
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index fba6225b87a..a3fdb258f75 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::CreateFromTemplateService do
+RSpec.describe Projects::CreateFromTemplateService, feature_category: :projects do
let(:user) { create(:user) }
let(:template_name) { 'rails' }
let(:project_params) do
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e435db4efa6..303a98cb35b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -254,6 +254,27 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects
end
it_behaves_like 'has sync-ed traversal_ids'
+
+ context 'when project is an import' do
+ before do
+ stub_application_setting(import_sources: ['gitlab_project'])
+ end
+
+ context 'when user is not allowed to import projects' do
+ let(:group) do
+ create(:group).tap do |group|
+ group.add_developer(user)
+ end
+ end
+
+ it 'does not create the project' do
+ project = create_project(user, opts.merge!(namespace_id: group.id, import_type: 'gitlab_project'))
+
+ expect(project).not_to be_persisted
+ expect(project.errors.messages[:user].first).to eq('is not allowed to import projects')
+ end
+ end
+ end
end
context 'group sharing', :sidekiq_inline do
@@ -339,9 +360,12 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects
before do
group.add_maintainer(group_maintainer)
- create(:group_group_link, shared_group: subgroup_for_projects,
- shared_with_group: subgroup_for_access,
- group_access: share_max_access_level)
+ create(
+ :group_group_link,
+ shared_group: subgroup_for_projects,
+ shared_with_group: subgroup_for_access,
+ group_access: share_max_access_level
+ )
end
context 'membership is higher from group hierarchy' do
@@ -716,16 +740,34 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects
end
end
- context 'and a default_branch_name is specified' do
+ context 'and default_branch is specified' do
before do
- allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return('example_branch')
+ opts[:default_branch] = 'example_branch'
end
it 'creates the correct branch' do
- branches = project.repository.branches
+ expect(project.repository.branch_names).to contain_exactly('example_branch')
+ end
- expect(branches.size).to eq(1)
- expect(branches.collect(&:name)).to contain_exactly('example_branch')
+ it_behaves_like 'a repo with a README.md' do
+ let(:expected_content) do
+ <<~MARKDOWN
+ cd existing_repo
+ git remote add origin #{project.http_url_to_repo}
+ git branch -M example_branch
+ git push -uf origin example_branch
+ MARKDOWN
+ end
+ end
+ end
+
+ context 'and the default branch setting is configured' do
+ before do
+ allow(Gitlab::CurrentSettings).to receive(:default_branch_name).and_return('example_branch')
+ end
+
+ it 'creates the correct branch' do
+ expect(project.repository.branch_names).to contain_exactly('example_branch')
end
it_behaves_like 'a repo with a README.md' do
@@ -956,11 +998,11 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects
receive(:perform_async).and_call_original
)
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
- receive(:bulk_perform_in)
- .with(1.hour,
- array_including([user.id], [other_user.id]),
- batch_delay: 30.seconds, batch_size: 100)
- .and_call_original
+ receive(:bulk_perform_in).with(
+ 1.hour,
+ array_including([user.id], [other_user.id]),
+ batch_delay: 30.seconds, batch_size: 100
+ ).and_call_original
)
project = create_project(user, opts)
diff --git a/spec/services/projects/deploy_tokens/create_service_spec.rb b/spec/services/projects/deploy_tokens/create_service_spec.rb
index 831dbc06588..96458a51fb4 100644
--- a/spec/services/projects/deploy_tokens/create_service_spec.rb
+++ b/spec/services/projects/deploy_tokens/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::DeployTokens::CreateService do
+RSpec.describe Projects::DeployTokens::CreateService, feature_category: :continuous_delivery do
it_behaves_like 'a deploy token creation service' do
let(:entity) { create(:project) }
let(:deploy_token_class) { ProjectDeployToken }
diff --git a/spec/services/projects/deploy_tokens/destroy_service_spec.rb b/spec/services/projects/deploy_tokens/destroy_service_spec.rb
index edb2345aa6c..3d0323c60ba 100644
--- a/spec/services/projects/deploy_tokens/destroy_service_spec.rb
+++ b/spec/services/projects/deploy_tokens/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::DeployTokens::DestroyService do
+RSpec.describe Projects::DeployTokens::DestroyService, feature_category: :continuous_delivery do
it_behaves_like 'a deploy token deletion service' do
let_it_be(:entity) { create(:project) }
let_it_be(:deploy_token_class) { ProjectDeployToken }
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 0689a65c2f4..665f930a0a8 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -207,9 +207,11 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when project has exports' do
let!(:project_with_export) do
create(:project, :repository, namespace: user.namespace).tap do |project|
- create(:import_export_upload,
- project: project,
- export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz'))
+ create(
+ :import_export_upload,
+ project: project,
+ export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')
+ )
end
end
@@ -337,8 +339,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
let(:container_repository) { create(:container_repository) }
before do
- stub_container_registry_tags(repository: project.full_path + '/image',
- tags: ['tag'])
+ stub_container_registry_tags(repository: project.full_path + '/image', tags: ['tag'])
project.container_repositories << container_repository
end
@@ -387,8 +388,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when there are tags for legacy root repository' do
before do
- stub_container_registry_tags(repository: project.full_path,
- tags: ['tag'])
+ stub_container_registry_tags(repository: project.full_path, tags: ['tag'])
end
context 'when image repository tags deletion succeeds' do
@@ -414,8 +414,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when there are no tags for legacy root repository' do
before do
- stub_container_registry_tags(repository: project.full_path,
- tags: [])
+ stub_container_registry_tags(repository: project.full_path, tags: [])
end
it 'does not try to destroy the repository' do
diff --git a/spec/services/projects/detect_repository_languages_service_spec.rb b/spec/services/projects/detect_repository_languages_service_spec.rb
index cf4c7a5024d..5759f8128d0 100644
--- a/spec/services/projects/detect_repository_languages_service_spec.rb
+++ b/spec/services/projects/detect_repository_languages_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_state do
+RSpec.describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_state, feature_category: :projects do
let_it_be(:project, reload: true) { create(:project, :repository) }
subject { described_class.new(project) }
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
index f158b11a9fa..52bdbefe01a 100644
--- a/spec/services/projects/download_service_spec.rb
+++ b/spec/services/projects/download_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::DownloadService do
+RSpec.describe Projects::DownloadService, feature_category: :projects do
describe 'File service' do
before do
@user = create(:user)
diff --git a/spec/services/projects/enable_deploy_key_service_spec.rb b/spec/services/projects/enable_deploy_key_service_spec.rb
index c0b3992037e..59c76a96d07 100644
--- a/spec/services/projects/enable_deploy_key_service_spec.rb
+++ b/spec/services/projects/enable_deploy_key_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::EnableDeployKeyService do
+RSpec.describe Projects::EnableDeployKeyService, feature_category: :continuous_delivery do
let(:deploy_key) { create(:deploy_key, public: true) }
let(:project) { create(:project) }
let(:user) { project.creator }
diff --git a/spec/services/projects/fetch_statistics_increment_service_spec.rb b/spec/services/projects/fetch_statistics_increment_service_spec.rb
index 16121a42c39..9e24e68fa98 100644
--- a/spec/services/projects/fetch_statistics_increment_service_spec.rb
+++ b/spec/services/projects/fetch_statistics_increment_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
module Projects
- RSpec.describe FetchStatisticsIncrementService do
+ RSpec.describe FetchStatisticsIncrementService, feature_category: :projects do
let(:project) { create(:project) }
describe '#execute' do
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 48756cf774b..4ba72b5870d 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ForkService do
+RSpec.describe Projects::ForkService, feature_category: :source_code_management do
include ProjectForksHelper
shared_examples 'forks count cache refresh' do
@@ -22,14 +22,16 @@ RSpec.describe Projects::ForkService do
@from_user = create(:user)
@from_namespace = @from_user.namespace
avatar = fixture_file_upload("spec/fixtures/dk.png", "image/png")
- @from_project = create(:project,
- :repository,
- creator_id: @from_user.id,
- namespace: @from_namespace,
- star_count: 107,
- avatar: avatar,
- description: 'wow such project',
- external_authorization_classification_label: 'classification-label')
+ @from_project = create(
+ :project,
+ :repository,
+ creator_id: @from_user.id,
+ namespace: @from_namespace,
+ star_count: 107,
+ avatar: avatar,
+ description: 'wow such project',
+ external_authorization_classification_label: 'classification-label'
+ )
@to_user = create(:user)
@to_namespace = @to_user.namespace
@from_project.add_member(@to_user, :developer)
@@ -148,12 +150,11 @@ RSpec.describe Projects::ForkService do
context 'project already exists' do
it "fails due to validation, not transaction failure" do
- @existing_project = create(:project, :repository, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace)
+ @existing_project = create(:project, :repository, creator_id: @to_user.id, path: @from_project.path, namespace: @to_namespace)
@to_project = fork_project(@from_project, @to_user, namespace: @to_namespace, using_service: true)
expect(@existing_project).to be_persisted
expect(@to_project).not_to be_persisted
- expect(@to_project.errors[:name]).to eq(['has already been taken'])
expect(@to_project.errors[:path]).to eq(['has already been taken'])
end
end
@@ -258,11 +259,13 @@ RSpec.describe Projects::ForkService do
before do
@group_owner = create(:user)
@developer = create(:user)
- @project = create(:project, :repository,
- creator_id: @group_owner.id,
- star_count: 777,
- description: 'Wow, such a cool project!',
- ci_config_path: 'debian/salsa-ci.yml')
+ @project = create(
+ :project, :repository,
+ creator_id: @group_owner.id,
+ star_count: 777,
+ description: 'Wow, such a cool project!',
+ ci_config_path: 'debian/salsa-ci.yml'
+ )
@group = create(:group)
@group.add_member(@group_owner, GroupMember::OWNER)
@group.add_member(@developer, GroupMember::DEVELOPER)
@@ -297,12 +300,9 @@ RSpec.describe Projects::ForkService do
context 'project already exists in group' do
it 'fails due to validation, not transaction failure' do
- existing_project = create(:project, :repository,
- name: @project.name,
- namespace: @group)
+ existing_project = create(:project, :repository, path: @project.path, namespace: @group)
to_project = fork_project(@project, @group_owner, @opts)
expect(existing_project.persisted?).to be_truthy
- expect(to_project.errors[:name]).to eq(['has already been taken'])
expect(to_project.errors[:path]).to eq(['has already been taken'])
end
end
diff --git a/spec/services/projects/forks/sync_service_spec.rb b/spec/services/projects/forks/sync_service_spec.rb
new file mode 100644
index 00000000000..aeb53992ed4
--- /dev/null
+++ b/spec/services/projects/forks/sync_service_spec.rb
@@ -0,0 +1,185 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Forks::SyncService, feature_category: :source_code_management do
+ include ProjectForksHelper
+ include RepoHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:source_project) { create(:project, :repository, :public) }
+ let_it_be(:project) { fork_project(source_project, user, { repository: true }) }
+
+ let(:fork_branch) { project.default_branch }
+ let(:service) { described_class.new(project, user, fork_branch) }
+
+ def details
+ Projects::Forks::Details.new(project, fork_branch)
+ end
+
+ def expect_to_cancel_exclusive_lease
+ expect(Gitlab::ExclusiveLease).to receive(:cancel)
+ end
+
+ describe '#execute' do
+ context 'when fork is up-to-date with the upstream' do
+ it 'does not perform merge' do
+ expect_to_cancel_exclusive_lease
+ expect(project.repository).not_to receive(:merge_to_branch)
+ expect(project.repository).not_to receive(:ff_merge)
+
+ expect(service.execute).to be_success
+ end
+ end
+
+ context 'when fork is behind the upstream' do
+ let_it_be(:base_commit) { source_project.commit.sha }
+
+ before_all do
+ source_project.repository.commit_files(
+ user,
+ branch_name: source_project.repository.root_ref, message: 'Commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'One more' }]
+ )
+
+ source_project.repository.commit_files(
+ user,
+ branch_name: source_project.repository.root_ref, message: 'Another commit to root ref',
+ actions: [{ action: :create, file_path: 'encoding/NEW-CHANGELOG', content: 'One more time' }]
+ )
+ end
+
+ before do
+ project.repository.create_branch(fork_branch, base_commit)
+ end
+
+ context 'when fork is not ahead of the upstream' do
+ let(:fork_branch) { 'fork-without-new-commits' }
+
+ it 'updates the fork using ff merge' do
+ expect_to_cancel_exclusive_lease
+ expect(project.commit(fork_branch).sha).to eq(base_commit)
+ expect(project.repository).to receive(:ff_merge)
+ .with(user, source_project.commit.sha, fork_branch, target_sha: base_commit)
+ .and_call_original
+
+ expect do
+ expect(service.execute).to be_success
+ end.to change { details.counts }.from({ ahead: 0, behind: 2 }).to({ ahead: 0, behind: 0 })
+ end
+ end
+
+ context 'when fork is ahead of the upstream' do
+ context 'and has conflicts with the upstream', :use_clean_rails_redis_caching do
+ let(:fork_branch) { 'fork-with-conflicts' }
+
+ it 'returns an error' do
+ project.repository.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Committing something',
+ actions: [{ action: :create, file_path: 'encoding/CHANGELOG', content: 'New file' }]
+ )
+
+ expect_to_cancel_exclusive_lease
+ expect(details).not_to have_conflicts
+
+ expect do
+ result = service.execute
+
+ expect(result).to be_error
+ expect(result.message).to eq("9:merging commits: merge: there are conflicting files.")
+ end.not_to change { details.counts }
+
+ expect(details).to have_conflicts
+ end
+ end
+
+ context 'and does not have conflicts with the upstream' do
+ let(:fork_branch) { 'fork-with-new-commits' }
+
+ it 'updates the fork using merge' do
+ project.repository.commit_files(
+ user,
+ branch_name: fork_branch, message: 'Committing completely new changelog',
+ actions: [{ action: :create, file_path: 'encoding/COMPLETELY-NEW-CHANGELOG', content: 'New file' }]
+ )
+
+ commit_message = "Merge branch #{source_project.path}:#{source_project.default_branch} into #{fork_branch}"
+ expect(project.repository).to receive(:merge_to_branch).with(
+ user,
+ source_sha: source_project.commit.sha,
+ target_branch: fork_branch,
+ target_sha: project.commit(fork_branch).sha,
+ message: commit_message
+ ).and_call_original
+ expect_to_cancel_exclusive_lease
+
+ expect do
+ expect(service.execute).to be_success
+ end.to change { details.counts }.from({ ahead: 1, behind: 2 }).to({ ahead: 2, behind: 0 })
+
+ commits = project.repository.commits_between(source_project.commit.sha, project.commit(fork_branch).sha)
+ expect(commits.map(&:message)).to eq([
+ "Committing completely new changelog",
+ commit_message
+ ])
+ end
+ end
+ end
+
+ context 'when a merge cannot happen due to another ongoing merge' do
+ it 'does not merge' do
+ expect(service).to receive(:perform_merge).and_return(nil)
+
+ result = service.execute
+
+ expect(result).to be_error
+ expect(result.message).to eq(described_class::ONGOING_MERGE_ERROR)
+ end
+ end
+
+ context 'when upstream branch contains lfs reference' do
+ let(:source_project) { create(:project, :repository, :public) }
+ let(:project) { fork_project(source_project, user, { repository: true }) }
+ let(:fork_branch) { 'fork-fetches-lfs-pointers' }
+
+ before do
+ source_project.change_head('lfs')
+
+ allow(source_project).to receive(:lfs_enabled?).and_return(true)
+ allow(project).to receive(:lfs_enabled?).and_return(true)
+
+ create_file_in_repo(source_project, 'lfs', 'lfs', 'one.lfs', 'One')
+ create_file_in_repo(source_project, 'lfs', 'lfs', 'two.lfs', 'Two')
+ end
+
+ it 'links fetched lfs objects to the fork project', :aggregate_failures do
+ expect_to_cancel_exclusive_lease
+
+ expect do
+ expect(service.execute).to be_success
+ end.to change { project.reload.lfs_objects.size }.from(0).to(2)
+ .and change { details.counts }.from({ ahead: 0, behind: 3 }).to({ ahead: 0, behind: 0 })
+
+ expect(project.lfs_objects).to match_array(source_project.lfs_objects)
+ end
+
+ context 'and there are too many of them for a single sync' do
+ let(:fork_branch) { 'fork-too-many-lfs-pointers' }
+
+ it 'updates the fork successfully' do
+ expect_to_cancel_exclusive_lease
+ stub_const('Projects::LfsPointers::LfsLinkService::MAX_OIDS', 1)
+
+ expect do
+ result = service.execute
+
+ expect(result).to be_error
+ expect(result.message).to eq('Too many LFS object ids to link, please push them manually')
+ end.not_to change { details.counts }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/forks_count_service_spec.rb b/spec/services/projects/forks_count_service_spec.rb
index 31662f78973..403d8656b7c 100644
--- a/spec/services/projects/forks_count_service_spec.rb
+++ b/spec/services/projects/forks_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ForksCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::ForksCountService, :use_clean_rails_memory_store_caching, feature_category: :source_code_management do
let(:project) { build(:project) }
subject { described_class.new(project) }
diff --git a/spec/services/projects/git_deduplication_service_spec.rb b/spec/services/projects/git_deduplication_service_spec.rb
index e6eff936de7..2b9f0974ae2 100644
--- a/spec/services/projects/git_deduplication_service_spec.rb
+++ b/spec/services/projects/git_deduplication_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GitDeduplicationService do
+RSpec.describe Projects::GitDeduplicationService, feature_category: :source_code_management do
include ExclusiveLeaseHelpers
let(:pool) { create(:pool_repository, :ready) }
@@ -139,7 +139,7 @@ RSpec.describe Projects::GitDeduplicationService do
end
it 'fails when a lease is already out' do
- expect(service).to receive(:log_error).with("Cannot obtain an exclusive lease for #{lease_key}. There must be another instance already in execution.")
+ expect(Gitlab::AppJsonLogger).to receive(:error).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: lease_key, class_name: described_class.name, lease_timeout: lease_timeout })
service.execute
end
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
index d32e720a49f..b1468a40212 100644
--- a/spec/services/projects/gitlab_projects_import_service_spec.rb
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GitlabProjectsImportService do
+RSpec.describe Projects::GitlabProjectsImportService, feature_category: :importers do
let_it_be(:namespace) { create(:namespace) }
let(:path) { 'test-path' }
diff --git a/spec/services/projects/group_links/create_service_spec.rb b/spec/services/projects/group_links/create_service_spec.rb
index eae898b4f68..4f2f480cf1c 100644
--- a/spec/services/projects/group_links/create_service_spec.rb
+++ b/spec/services/projects/group_links/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GroupLinks::CreateService, '#execute' do
+RSpec.describe Projects::GroupLinks::CreateService, '#execute', feature_category: :subgroups do
let_it_be(:user) { create :user }
let_it_be(:group) { create :group }
let_it_be(:project) { create(:project, namespace: create(:namespace, :with_namespace_settings)) }
@@ -69,11 +69,11 @@ RSpec.describe Projects::GroupLinks::CreateService, '#execute' do
.and_call_original
)
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
- receive(:bulk_perform_in)
- .with(1.hour,
- array_including([user.id], [other_user.id]),
- batch_delay: 30.seconds, batch_size: 100)
- .and_call_original
+ receive(:bulk_perform_in).with(
+ 1.hour,
+ array_including([user.id], [other_user.id]),
+ batch_delay: 30.seconds, batch_size: 100
+ ).and_call_original
)
subject.execute
@@ -82,8 +82,7 @@ RSpec.describe Projects::GroupLinks::CreateService, '#execute' do
context 'when sharing outside the hierarchy is disabled' do
let_it_be(:shared_group_parent) do
- create(:group,
- namespace_settings: create(:namespace_settings, prevent_sharing_groups_outside_hierarchy: true))
+ create(:group, namespace_settings: create(:namespace_settings, prevent_sharing_groups_outside_hierarchy: true))
end
let_it_be(:project, reload: true) { create(:project, group: shared_group_parent) }
diff --git a/spec/services/projects/group_links/destroy_service_spec.rb b/spec/services/projects/group_links/destroy_service_spec.rb
index 89865d6bc3b..76bdd536a0d 100644
--- a/spec/services/projects/group_links/destroy_service_spec.rb
+++ b/spec/services/projects/group_links/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GroupLinks::DestroyService, '#execute' do
+RSpec.describe Projects::GroupLinks::DestroyService, '#execute', feature_category: :subgroups do
let_it_be(:user) { create :user }
let_it_be(:project) { create(:project, :private) }
let_it_be(:group) { create(:group) }
@@ -31,10 +31,11 @@ RSpec.describe Projects::GroupLinks::DestroyService, '#execute' do
stub_feature_flags(do_not_run_safety_net_auth_refresh_jobs: false)
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
- receive(:bulk_perform_in)
- .with(1.hour,
- [[user.id]],
- batch_delay: 30.seconds, batch_size: 100)
+ receive(:bulk_perform_in).with(
+ 1.hour,
+ [[user.id]],
+ batch_delay: 30.seconds, batch_size: 100
+ )
)
subject.execute(group_link)
diff --git a/spec/services/projects/group_links/update_service_spec.rb b/spec/services/projects/group_links/update_service_spec.rb
index 1acbb770763..4232412cf54 100644
--- a/spec/services/projects/group_links/update_service_spec.rb
+++ b/spec/services/projects/group_links/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::GroupLinks::UpdateService, '#execute' do
+RSpec.describe Projects::GroupLinks::UpdateService, '#execute', feature_category: :subgroups do
let_it_be(:user) { create :user }
let_it_be(:group) { create :group }
let_it_be(:project) { create :project }
@@ -45,10 +45,11 @@ RSpec.describe Projects::GroupLinks::UpdateService, '#execute' do
stub_feature_flags(do_not_run_safety_net_auth_refresh_jobs: false)
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
- receive(:bulk_perform_in)
- .with(1.hour,
- [[user.id]],
- batch_delay: 30.seconds, batch_size: 100)
+ receive(:bulk_perform_in).with(
+ 1.hour,
+ [[user.id]],
+ batch_delay: 30.seconds, batch_size: 100
+ )
)
subject
diff --git a/spec/services/projects/hashed_storage/base_attachment_service_spec.rb b/spec/services/projects/hashed_storage/base_attachment_service_spec.rb
index 86e3fb3820c..01036fc2d9c 100644
--- a/spec/services/projects/hashed_storage/base_attachment_service_spec.rb
+++ b/spec/services/projects/hashed_storage/base_attachment_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::BaseAttachmentService do
+RSpec.describe Projects::HashedStorage::BaseAttachmentService, feature_category: :projects do
let(:project) { create(:project, :repository, storage_version: 0, skip_disk_validation: true) }
subject(:service) { described_class.new(project: project, old_disk_path: project.full_path, logger: nil) }
diff --git a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
index c8f24c6ce00..39263506bca 100644
--- a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::MigrateAttachmentsService do
+RSpec.describe Projects::HashedStorage::MigrateAttachmentsService, feature_category: :projects do
subject(:service) { described_class.new(project: project, old_disk_path: project.full_path, logger: nil) }
let(:project) { create(:project, :repository, storage_version: 1, skip_disk_validation: true) }
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index eb8d94ebfa5..bcc914e72b5 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::MigrateRepositoryService do
+RSpec.describe Projects::HashedStorage::MigrateRepositoryService, feature_category: :projects do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo, :design_repo) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
diff --git a/spec/services/projects/hashed_storage/migration_service_spec.rb b/spec/services/projects/hashed_storage/migration_service_spec.rb
index ef96c17dd85..89bc55dbaf6 100644
--- a/spec/services/projects/hashed_storage/migration_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migration_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::MigrationService do
+RSpec.describe Projects::HashedStorage::MigrationService, feature_category: :projects do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
let(:logger) { double }
let!(:project_attachment) { build(:file_uploader, project: project) }
@@ -16,9 +16,11 @@ RSpec.describe Projects::HashedStorage::MigrationService do
describe '#execute' do
context 'repository migration' do
let(:repository_service) do
- Projects::HashedStorage::MigrateRepositoryService.new(project: project,
- old_disk_path: project.full_path,
- logger: logger)
+ Projects::HashedStorage::MigrateRepositoryService.new(
+ project: project,
+ old_disk_path: project.full_path,
+ logger: logger
+ )
end
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
@@ -53,9 +55,11 @@ RSpec.describe Projects::HashedStorage::MigrationService do
let(:project) { create(:project, :empty_repo, :wiki_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:attachments_service) do
- Projects::HashedStorage::MigrateAttachmentsService.new(project: project,
- old_disk_path: project.full_path,
- logger: logger)
+ Projects::HashedStorage::MigrateAttachmentsService.new(
+ project: project,
+ old_disk_path: project.full_path,
+ logger: logger
+ )
end
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
diff --git a/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
index d4cb46c82ad..95491d63df2 100644
--- a/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::RollbackAttachmentsService do
+RSpec.describe Projects::HashedStorage::RollbackAttachmentsService, feature_category: :projects do
subject(:service) { described_class.new(project: project, old_disk_path: project.disk_path, logger: nil) }
let(:project) { create(:project, :repository, skip_disk_validation: true) }
diff --git a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
index 385c03e6308..19f1856e39a 100644
--- a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis_shared_state do
+RSpec.describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis_shared_state, feature_category: :projects do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :repository, :wiki_repo, :design_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
diff --git a/spec/services/projects/hashed_storage/rollback_service_spec.rb b/spec/services/projects/hashed_storage/rollback_service_spec.rb
index 0bd63f2da2a..6d047f856ec 100644
--- a/spec/services/projects/hashed_storage/rollback_service_spec.rb
+++ b/spec/services/projects/hashed_storage/rollback_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::HashedStorage::RollbackService do
+RSpec.describe Projects::HashedStorage::RollbackService, feature_category: :projects do
let(:project) { create(:project, :empty_repo, :wiki_repo) }
let(:logger) { double }
let!(:project_attachment) { build(:file_uploader, project: project) }
diff --git a/spec/services/projects/import_error_filter_spec.rb b/spec/services/projects/import_error_filter_spec.rb
index fd31cd52cc4..be07208c7f2 100644
--- a/spec/services/projects/import_error_filter_spec.rb
+++ b/spec/services/projects/import_error_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ImportErrorFilter do
+RSpec.describe Projects::ImportErrorFilter, feature_category: :importers do
it 'filters any full paths' do
message = 'Error importing into /my/folder Permission denied @ unlink_internal - /var/opt/gitlab/gitlab-rails/shared/a/b/c/uploads/file'
diff --git a/spec/services/projects/import_export/relation_export_service_spec.rb b/spec/services/projects/import_export/relation_export_service_spec.rb
index 94f5653ee7d..4b44a37b299 100644
--- a/spec/services/projects/import_export/relation_export_service_spec.rb
+++ b/spec/services/projects/import_export/relation_export_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ImportExport::RelationExportService do
+RSpec.describe Projects::ImportExport::RelationExportService, feature_category: :importers do
using RSpec::Parameterized::TableSyntax
subject(:service) { described_class.new(relation_export, 'jid') }
@@ -49,6 +49,7 @@ RSpec.describe Projects::ImportExport::RelationExportService do
expect(logger).to receive(:error).with(
export_error: '',
message: 'Project relation export failed',
+ relation: relation_export.relation,
project_export_job_id: project_export_job.id,
project_id: project_export_job.project.id,
project_name: project_export_job.project.name
@@ -78,6 +79,7 @@ RSpec.describe Projects::ImportExport::RelationExportService do
expect(logger).to receive(:error).with(
export_error: 'Error!',
message: 'Project relation export failed',
+ relation: relation_export.relation,
project_export_job_id: project_export_job.id,
project_id: project_export_job.project.id,
project_name: project_export_job.project.name
diff --git a/spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb b/spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb
index 4c51c8a4ac8..4ad6fd0edff 100644
--- a/spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb
+++ b/spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::InProductMarketingCampaignEmailsService do
+RSpec.describe Projects::InProductMarketingCampaignEmailsService, feature_category: :experimentation_adoption do
describe '#execute' do
let(:user) { create(:user, email_opted_in: true) }
let(:project) { create(:project) }
diff --git a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
index 80b3c4d0403..0aaaae19f5a 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do
+RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService, feature_category: :source_code_management do
let(:import_url) { 'http://www.gitlab.com/demo/repo.git' }
let(:lfs_endpoint) { "#{import_url}/info/lfs/objects/batch" }
let!(:project) { create(:project, import_url: import_url) }
diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
index c815ad38843..00c156ba538 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::LfsPointers::LfsDownloadService do
+RSpec.describe Projects::LfsPointers::LfsDownloadService, feature_category: :source_code_management do
include StubRequests
let_it_be(:project) { create(:project) }
diff --git a/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
index 32b86ade81e..f1e4db55962 100644
--- a/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_import_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::LfsPointers::LfsImportService do
+RSpec.describe Projects::LfsPointers::LfsImportService, feature_category: :source_code_management do
let(:project) { create(:project) }
let(:user) { project.creator }
let(:import_url) { 'http://www.gitlab.com/demo/repo.git' }
diff --git a/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb
index 0e7d16f18e8..fb3cc9bdac9 100644
--- a/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_link_service_spec.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::LfsPointers::LfsLinkService do
- let!(:project) { create(:project, lfs_enabled: true) }
- let!(:lfs_objects_project) { create_list(:lfs_objects_project, 2, project: project) }
+RSpec.describe Projects::LfsPointers::LfsLinkService, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, lfs_enabled: true) }
+ let_it_be(:lfs_objects_project) { create_list(:lfs_objects_project, 2, project: project) }
+
let(:new_oids) { { 'oid1' => 123, 'oid2' => 125 } }
let(:all_oids) { LfsObject.pluck(:oid, :size).to_h.merge(new_oids) }
let(:new_lfs_object) { create(:lfs_object) }
@@ -17,12 +18,26 @@ RSpec.describe Projects::LfsPointers::LfsLinkService do
describe '#execute' do
it 'raises an error when trying to link too many objects at once' do
+ stub_const("#{described_class}::MAX_OIDS", 5)
+
oids = Array.new(described_class::MAX_OIDS) { |i| "oid-#{i}" }
oids << 'the straw'
expect { subject.execute(oids) }.to raise_error(described_class::TooManyOidsError)
end
+ it 'executes a block after validation and before execution' do
+ block = instance_double(Proc)
+
+ expect(subject).to receive(:validate!).ordered
+ expect(block).to receive(:call).ordered
+ expect(subject).to receive(:link_existing_lfs_objects).ordered
+
+ subject.execute([]) do
+ block.call
+ end
+ end
+
it 'links existing lfs objects to the project' do
expect(project.lfs_objects.count).to eq 2
@@ -41,13 +56,13 @@ RSpec.describe Projects::LfsPointers::LfsLinkService do
it 'links in batches' do
stub_const("#{described_class}::BATCH_SIZE", 3)
- expect(Gitlab::Import::Logger)
- .to receive(:info)
- .with(class: described_class.name,
- project_id: project.id,
- project_path: project.full_path,
- lfs_objects_linked_count: 7,
- iterations: 3)
+ expect(Gitlab::Import::Logger).to receive(:info).with(
+ class: described_class.name,
+ project_id: project.id,
+ project_path: project.full_path,
+ lfs_objects_linked_count: 7,
+ iterations: 3
+ )
lfs_objects = create_list(:lfs_object, 7)
linked = subject.execute(lfs_objects.pluck(:oid))
diff --git a/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
index 59eb1ed7a29..f5dcae05959 100644
--- a/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService do
+RSpec.describe Projects::LfsPointers::LfsObjectDownloadListService, feature_category: :source_code_management do
let(:import_url) { 'http://www.gitlab.com/demo/repo.git' }
let(:default_endpoint) { "#{import_url}/info/lfs/objects/batch" }
let(:group) { create(:group, lfs_enabled: true) }
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
index 45e10c3ca84..b9244002f6c 100644
--- a/spec/services/projects/move_access_service_spec.rb
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveAccessService do
+RSpec.describe Projects::MoveAccessService, feature_category: :projects do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project_with_access) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_deploy_keys_projects_service_spec.rb b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
index 59674a3a4ef..b40eb4a18d1 100644
--- a/spec/services/projects/move_deploy_keys_projects_service_spec.rb
+++ b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveDeployKeysProjectsService do
+RSpec.describe Projects::MoveDeployKeysProjectsService, feature_category: :continuous_delivery do
let!(:user) { create(:user) }
let!(:project_with_deploy_keys) { create(:project, namespace: user.namespace) }
let!(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_forks_service_spec.rb b/spec/services/projects/move_forks_service_spec.rb
index 7d3637b7758..093562207dd 100644
--- a/spec/services/projects/move_forks_service_spec.rb
+++ b/spec/services/projects/move_forks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveForksService do
+RSpec.describe Projects::MoveForksService, feature_category: :source_code_management do
include ProjectForksHelper
let!(:user) { create(:user) }
diff --git a/spec/services/projects/move_lfs_objects_projects_service_spec.rb b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
index e3df5fed9cf..f3cc4014b1c 100644
--- a/spec/services/projects/move_lfs_objects_projects_service_spec.rb
+++ b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveLfsObjectsProjectsService do
+RSpec.describe Projects::MoveLfsObjectsProjectsService, feature_category: :source_code_management do
let!(:user) { create(:user) }
let!(:project_with_lfs_objects) { create(:project, namespace: user.namespace) }
let!(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_notification_settings_service_spec.rb b/spec/services/projects/move_notification_settings_service_spec.rb
index e381ae7590f..5ef6e8a0647 100644
--- a/spec/services/projects/move_notification_settings_service_spec.rb
+++ b/spec/services/projects/move_notification_settings_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveNotificationSettingsService do
+RSpec.describe Projects::MoveNotificationSettingsService, feature_category: :projects do
let(:user) { create(:user) }
let(:project_with_notifications) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
index d47b13ca939..6cd0b056325 100644
--- a/spec/services/projects/move_project_authorizations_service_spec.rb
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveProjectAuthorizationsService do
+RSpec.describe Projects::MoveProjectAuthorizationsService, feature_category: :projects do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb
index 1fca96a0367..cfd4b51b001 100644
--- a/spec/services/projects/move_project_group_links_service_spec.rb
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveProjectGroupLinksService do
+RSpec.describe Projects::MoveProjectGroupLinksService, feature_category: :projects do
let!(:user) { create(:user) }
let(:project_with_groups) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
index 8fbd0ba3270..364fb7faaf2 100644
--- a/spec/services/projects/move_project_members_service_spec.rb
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveProjectMembersService do
+RSpec.describe Projects::MoveProjectMembersService, feature_category: :projects do
let!(:user) { create(:user) }
let(:project_with_users) { create(:project, namespace: user.namespace) }
let(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/move_users_star_projects_service_spec.rb b/spec/services/projects/move_users_star_projects_service_spec.rb
index b580d3d8772..b99e51d954b 100644
--- a/spec/services/projects/move_users_star_projects_service_spec.rb
+++ b/spec/services/projects/move_users_star_projects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::MoveUsersStarProjectsService do
+RSpec.describe Projects::MoveUsersStarProjectsService, feature_category: :projects do
let!(:user) { create(:user) }
let!(:project_with_stars) { create(:project, namespace: user.namespace) }
let!(:target_project) { create(:project, namespace: user.namespace) }
diff --git a/spec/services/projects/open_issues_count_service_spec.rb b/spec/services/projects/open_issues_count_service_spec.rb
index c739fea5ecf..89405f06f5d 100644
--- a/spec/services/projects/open_issues_count_service_spec.rb
+++ b/spec/services/projects/open_issues_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::OpenIssuesCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::OpenIssuesCountService, :use_clean_rails_memory_store_caching, feature_category: :team_planning do
let(:project) { create(:project) }
subject { described_class.new(project) }
diff --git a/spec/services/projects/open_merge_requests_count_service_spec.rb b/spec/services/projects/open_merge_requests_count_service_spec.rb
index 6caef181e77..9d94fff2d20 100644
--- a/spec/services/projects/open_merge_requests_count_service_spec.rb
+++ b/spec/services/projects/open_merge_requests_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::OpenMergeRequestsCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::OpenMergeRequestsCountService, :use_clean_rails_memory_store_caching, feature_category: :code_review_workflow do
let_it_be(:project) { create(:project) }
subject { described_class.new(project) }
@@ -11,10 +11,7 @@ RSpec.describe Projects::OpenMergeRequestsCountService, :use_clean_rails_memory_
describe '#count' do
it 'returns the number of open merge requests' do
- create(:merge_request,
- :opened,
- source_project: project,
- target_project: project)
+ create(:merge_request, :opened, source_project: project, target_project: project)
expect(subject.count).to eq(1)
end
diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb
index 95f2176dbc0..7babaf4d0d8 100644
--- a/spec/services/projects/operations/update_service_spec.rb
+++ b/spec/services/projects/operations/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Operations::UpdateService do
+RSpec.describe Projects::Operations::UpdateService, feature_category: :projects do
let_it_be_with_refind(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb
index 7038910508f..b4faf45a1cb 100644
--- a/spec/services/projects/overwrite_project_service_spec.rb
+++ b/spec/services/projects/overwrite_project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::OverwriteProjectService do
+RSpec.describe Projects::OverwriteProjectService, feature_category: :projects do
include ProjectForksHelper
let(:user) { create(:user) }
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index fc745cd669f..bd297343879 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ParticipantsService do
+RSpec.describe Projects::ParticipantsService, feature_category: :projects do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
index 43d23023d83..0feac6c3e72 100644
--- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Prometheus::Alerts::NotifyService do
+RSpec.describe Projects::Prometheus::Alerts::NotifyService, feature_category: :metrics do
include PrometheusHelpers
using RSpec::Parameterized::TableSyntax
@@ -45,10 +45,8 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
end
before do
- create(:clusters_integrations_prometheus,
- cluster: prd_cluster, alert_manager_token: token)
- create(:clusters_integrations_prometheus,
- cluster: stg_cluster, alert_manager_token: nil)
+ create(:clusters_integrations_prometheus, cluster: prd_cluster, alert_manager_token: token)
+ create(:clusters_integrations_prometheus, cluster: stg_cluster, alert_manager_token: nil)
end
context 'without token' do
@@ -78,10 +76,12 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
cluster.update!(enabled: cluster_enabled)
unless integration_enabled.nil?
- create(:clusters_integrations_prometheus,
- cluster: cluster,
- enabled: integration_enabled,
- alert_manager_token: configured_token)
+ create(
+ :clusters_integrations_prometheus,
+ cluster: cluster,
+ enabled: integration_enabled,
+ alert_manager_token: configured_token
+ )
end
end
@@ -118,9 +118,11 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
create(:prometheus_integration, project: project)
if alerting_setting
- create(:project_alerting_setting,
- project: project,
- token: configured_token)
+ create(
+ :project_alerting_setting,
+ project: project,
+ token: configured_token
+ )
end
end
diff --git a/spec/services/projects/prometheus/metrics/destroy_service_spec.rb b/spec/services/projects/prometheus/metrics/destroy_service_spec.rb
index b4af81f2c87..4c2a959a149 100644
--- a/spec/services/projects/prometheus/metrics/destroy_service_spec.rb
+++ b/spec/services/projects/prometheus/metrics/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::Prometheus::Metrics::DestroyService do
+RSpec.describe Projects::Prometheus::Metrics::DestroyService, feature_category: :metrics do
let(:metric) { create(:prometheus_metric) }
subject { described_class.new(metric) }
diff --git a/spec/services/projects/protect_default_branch_service_spec.rb b/spec/services/projects/protect_default_branch_service_spec.rb
index 9f9e89ff8f8..a4fdd9983b8 100644
--- a/spec/services/projects/protect_default_branch_service_spec.rb
+++ b/spec/services/projects/protect_default_branch_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ProtectDefaultBranchService do
+RSpec.describe Projects::ProtectDefaultBranchService, feature_category: :source_code_management do
let(:service) { described_class.new(project) }
let(:project) { create(:project) }
@@ -247,6 +247,7 @@ RSpec.describe Projects::ProtectDefaultBranchService do
context 'when feature flag `group_protected_branches` disabled' do
before do
stub_feature_flags(group_protected_branches: false)
+ stub_feature_flags(allow_protected_branches_for_group: false)
end
it 'return false' do
@@ -257,6 +258,7 @@ RSpec.describe Projects::ProtectDefaultBranchService do
context 'when feature flag `group_protected_branches` enabled' do
before do
stub_feature_flags(group_protected_branches: true)
+ stub_feature_flags(allow_protected_branches_for_group: true)
end
it 'return true' do
diff --git a/spec/services/projects/readme_renderer_service_spec.rb b/spec/services/projects/readme_renderer_service_spec.rb
index 14cdcf67640..842d75e82ee 100644
--- a/spec/services/projects/readme_renderer_service_spec.rb
+++ b/spec/services/projects/readme_renderer_service_spec.rb
@@ -2,14 +2,14 @@
require 'spec_helper'
-RSpec.describe Projects::ReadmeRendererService, '#execute' do
+RSpec.describe Projects::ReadmeRendererService, '#execute', feature_category: :projects do
using RSpec::Parameterized::TableSyntax
subject(:service) { described_class.new(project, nil, opts) }
let_it_be(:project) { create(:project, title: 'My Project', description: '_custom_description_') }
- let(:opts) { {} }
+ let(:opts) { { default_branch: 'master' } }
it 'renders the an ERB readme template' do
expect(service.execute).to start_with(<<~MARKDOWN)
diff --git a/spec/services/projects/record_target_platforms_service_spec.rb b/spec/services/projects/record_target_platforms_service_spec.rb
index 22ff325a62e..17aa7fd7009 100644
--- a/spec/services/projects/record_target_platforms_service_spec.rb
+++ b/spec/services/projects/record_target_platforms_service_spec.rb
@@ -2,41 +2,30 @@
require 'spec_helper'
-RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
+RSpec.describe Projects::RecordTargetPlatformsService, '#execute', feature_category: :projects do
let_it_be(:project) { create(:project) }
let(:detector_service) { Projects::AppleTargetPlatformDetectorService }
subject(:execute) { described_class.new(project, detector_service).execute }
- context 'when detector returns target platform values' do
- let(:detector_result) { [:ios, :osx] }
- let(:service_result) { detector_result.map(&:to_s) }
+ context 'when project is an XCode project' do
+ def project_setting
+ ProjectSetting.find_by_project_id(project.id)
+ end
before do
- double = instance_double(detector_service, execute: detector_result)
- allow(detector_service).to receive(:new) { double }
+ double = instance_double(detector_service, execute: [:ios, :osx])
+ allow(Projects::AppleTargetPlatformDetectorService).to receive(:new) { double }
end
- shared_examples 'saves and returns detected target platforms' do
- it 'creates a new setting record for the project', :aggregate_failures do
- expect { execute }.to change { ProjectSetting.count }.from(0).to(1)
- expect(ProjectSetting.last.target_platforms).to match_array(service_result)
- end
-
- it 'returns the array of stored target platforms' do
- expect(execute).to match_array service_result
- end
+ it 'creates a new setting record for the project', :aggregate_failures do
+ expect { execute }.to change { ProjectSetting.count }.from(0).to(1)
+ expect(ProjectSetting.last.target_platforms).to match_array(%w(ios osx))
end
- it_behaves_like 'saves and returns detected target platforms'
-
- context 'when detector returns a non-array value' do
- let(:detector_service) { Projects::AndroidTargetPlatformDetectorService }
- let(:detector_result) { :android }
- let(:service_result) { [detector_result.to_s] }
-
- it_behaves_like 'saves and returns detected target platforms'
+ it 'returns array of detected target platforms' do
+ expect(execute).to match_array %w(ios osx)
end
context 'when a project has an existing setting record' do
@@ -44,10 +33,6 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
create(:project_setting, project: project, target_platforms: saved_target_platforms)
end
- def project_setting
- ProjectSetting.find_by_project_id(project.id)
- end
-
context 'when target platforms changed' do
let(:saved_target_platforms) { %w(tvos) }
@@ -98,44 +83,23 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute' do
it_behaves_like 'tracks experiment assignment event'
end
- shared_examples 'does not send email' do
- it 'does not execute a Projects::InProductMarketingCampaignEmailsService' do
- expect(Projects::InProductMarketingCampaignEmailsService).not_to receive(:new)
-
- execute
- end
- end
-
context 'experiment control' do
before do
stub_experiments(build_ios_app_guide_email: :control)
end
- it_behaves_like 'does not send email'
- it_behaves_like 'tracks experiment assignment event'
- end
-
- context 'when project is not an iOS project' do
- let(:detector_service) { Projects::AppleTargetPlatformDetectorService }
- let(:detector_result) { :android }
-
- before do
- stub_experiments(build_ios_app_guide_email: :candidate)
- end
-
- it_behaves_like 'does not send email'
-
- it 'does not track experiment assignment event', :experiment do
- expect(experiment(:build_ios_app_guide_email))
- .not_to track(:assignment)
+ it 'does not execute a Projects::InProductMarketingCampaignEmailsService' do
+ expect(Projects::InProductMarketingCampaignEmailsService).not_to receive(:new)
execute
end
+
+ it_behaves_like 'tracks experiment assignment event'
end
end
end
- context 'when detector does not return any target platform values' do
+ context 'when project is not an XCode project' do
before do
double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: [])
allow(Projects::AppleTargetPlatformDetectorService).to receive(:new).with(project) { double }
diff --git a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
index 62330441d2f..591cd1cba8d 100644
--- a/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
+++ b/spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitlab_redis_shared_state do
+RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsService, :clean_gitlab_redis_shared_state, feature_category: :build_artifacts do
let(:service) { described_class.new }
describe '#execute' do
diff --git a/spec/services/projects/repository_languages_service_spec.rb b/spec/services/projects/repository_languages_service_spec.rb
index 50d5fba6b84..a02844309b2 100644
--- a/spec/services/projects/repository_languages_service_spec.rb
+++ b/spec/services/projects/repository_languages_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::RepositoryLanguagesService do
+RSpec.describe Projects::RepositoryLanguagesService, feature_category: :source_code_management do
let(:service) { described_class.new(project, project.first_owner) }
context 'when detected_repository_languages flag is set' do
diff --git a/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb
index 76830396104..3d89f6efa6f 100644
--- a/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb
+++ b/spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::ScheduleBulkRepositoryShardMovesService do
+RSpec.describe Projects::ScheduleBulkRepositoryShardMovesService, feature_category: :source_code_management do
it_behaves_like 'moves repository shard in bulk' do
let_it_be_with_reload(:container) { create(:project, :repository) }
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 32818535146..48d5935f22f 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::TransferService do
+RSpec.describe Projects::TransferService, feature_category: :projects do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:group_integration) { create(:integrations_slack, :group, group: group, webhook: 'http://group.slack.com') }
@@ -20,12 +20,32 @@ RSpec.describe Projects::TransferService do
subject(:transfer_service) { described_class.new(project, user) }
- let!(:package) { create(:npm_package, project: project) }
+ let!(:package) { create(:npm_package, project: project, name: "@testscope/test") }
context 'with a root namespace change' do
+ it 'allow the transfer' do
+ expect(transfer_service.execute(group)).to be true
+ expect(project.errors[:new_namespace]).to be_empty
+ end
+ end
+
+ context 'with pending destruction package' do
+ before do
+ package.pending_destruction!
+ end
+
+ it 'allow the transfer' do
+ expect(transfer_service.execute(group)).to be true
+ expect(project.errors[:new_namespace]).to be_empty
+ end
+ end
+
+ context 'with namespaced packages present' do
+ let!(:package) { create(:npm_package, project: project, name: "@#{project.root_namespace.path}/test") }
+
it 'does not allow the transfer' do
expect(transfer_service.execute(group)).to be false
- expect(project.errors[:new_namespace]).to include("Root namespace can't be updated if project has NPM packages")
+ expect(project.errors[:new_namespace]).to include("Root namespace can't be updated if the project has NPM packages scoped to the current root level namespace.")
end
end
@@ -39,7 +59,7 @@ RSpec.describe Projects::TransferService do
other_group.add_owner(user)
end
- it 'does allow the transfer' do
+ it 'allow the transfer' do
expect(transfer_service.execute(other_group)).to be true
expect(project.errors[:new_namespace]).to be_empty
end
@@ -667,10 +687,11 @@ RSpec.describe Projects::TransferService do
user_ids = [user.id, member_of_old_group.id, member_of_new_group.id].map { |id| [id] }
expect(AuthorizedProjectUpdate::UserRefreshFromReplicaWorker).to(
- receive(:bulk_perform_in)
- .with(1.hour,
- user_ids,
- batch_delay: 30.seconds, batch_size: 100)
+ receive(:bulk_perform_in).with(
+ 1.hour,
+ user_ids,
+ batch_delay: 30.seconds, batch_size: 100
+ )
)
subject
@@ -694,10 +715,15 @@ RSpec.describe Projects::TransferService do
project.design_repository
end
+ def clear_design_repo_memoization
+ project.design_management_repository.clear_memoization(:repository)
+ project.clear_memoization(:design_repository)
+ end
+
it 'does not create a design repository' do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository.exists?).to be false
end
@@ -713,7 +739,7 @@ RSpec.describe Projects::TransferService do
it 'moves the repository' do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: new_full_path,
@@ -725,7 +751,7 @@ RSpec.describe Projects::TransferService do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_full_path,
@@ -742,7 +768,7 @@ RSpec.describe Projects::TransferService do
expect(subject.execute(group)).to be true
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
@@ -756,7 +782,7 @@ RSpec.describe Projects::TransferService do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
- project.clear_memoization(:design_repository)
+ clear_design_repo_memoization
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
index d939a79b7e9..872e38aba1d 100644
--- a/spec/services/projects/unlink_fork_service_spec.rb
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::UnlinkForkService, :use_clean_rails_memory_store_caching do
+RSpec.describe Projects::UnlinkForkService, :use_clean_rails_memory_store_caching, feature_category: :source_code_management do
include ProjectForksHelper
subject { described_class.new(forked_project, user) }
@@ -116,8 +116,10 @@ RSpec.describe Projects::UnlinkForkService, :use_clean_rails_memory_store_cachin
expect(project.fork_network_member).to be_nil
expect(project.fork_network).to be_nil
- expect(forked_project.fork_network).to have_attributes(root_project_id: nil,
- deleted_root_project_name: project.full_name)
+ expect(forked_project.fork_network).to have_attributes(
+ root_project_id: nil,
+ deleted_root_project_name: project.full_name
+ )
expect(project.forked_to_members.count).to eq(0)
expect(forked_project.forked_to_members.count).to eq(1)
expect(fork_of_fork.forked_to_members.count).to eq(0)
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index d908a169898..a97369c4b08 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -2,19 +2,22 @@
require "spec_helper"
-RSpec.describe Projects::UpdatePagesService do
+RSpec.describe Projects::UpdatePagesService, feature_category: :pages do
let_it_be(:project, refind: true) { create(:project, :repository) }
let_it_be(:old_pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
- let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
+ let(:options) { {} }
+ let(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD', options: options) }
let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') }
let(:file) { fixture_file_upload("spec/fixtures/pages.zip") }
+ let(:custom_root_file) { fixture_file_upload("spec/fixtures/pages_with_custom_root.zip") }
let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.zip") }
let(:empty_metadata_filename) { "spec/fixtures/pages_empty.zip.meta" }
let(:metadata_filename) { "spec/fixtures/pages.zip.meta" }
+ let(:custom_root_file_metadata) { "spec/fixtures/pages_with_custom_root.zip.meta" }
let(:metadata) { fixture_file_upload(metadata_filename) if File.exist?(metadata_filename) }
subject { described_class.new(project, build) }
@@ -97,6 +100,7 @@ RSpec.describe Projects::UpdatePagesService do
expect(deployment.file_sha256).to eq(artifacts_archive.file_sha256)
expect(project.pages_metadatum.reload.pages_deployment_id).to eq(deployment.id)
expect(deployment.ci_build_id).to eq(build.id)
+ expect(deployment.root_directory).to be_nil
end
it 'does not fail if pages_metadata is absent' do
@@ -116,9 +120,11 @@ RSpec.describe Projects::UpdatePagesService do
it 'schedules a destruction of older deployments' do
expect(DestroyPagesDeploymentsWorker).to(
- receive(:perform_in).with(described_class::OLD_DEPLOYMENTS_DESTRUCTION_DELAY,
- project.id,
- instance_of(Integer))
+ receive(:perform_in).with(
+ described_class::OLD_DEPLOYMENTS_DESTRUCTION_DELAY,
+ project.id,
+ instance_of(Integer)
+ )
)
execute
@@ -140,7 +146,45 @@ RSpec.describe Projects::UpdatePagesService do
it 'returns an error' do
expect(execute).not_to eq(:success)
- expect(GenericCommitStatus.last.description).to eq("Error: The `public/` folder is missing, or not declared in `.gitlab-ci.yml`.")
+ expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`")
+ end
+ end
+
+ context 'when there is a custom root config' do
+ let(:file) { custom_root_file }
+ let(:metadata_filename) { custom_root_file_metadata }
+
+ context 'when the directory specified with `publish` is included in the artifacts' do
+ let(:options) { { publish: 'foo' } }
+
+ it 'creates pages_deployment and saves it in the metadata' do
+ expect(execute).to eq(:success)
+
+ deployment = project.pages_deployments.last
+ expect(deployment.root_directory).to eq(options[:publish])
+ end
+ end
+
+ context 'when the directory specified with `publish` is not included in the artifacts' do
+ let(:options) { { publish: 'bar' } }
+
+ it 'returns an error' do
+ expect(execute).not_to eq(:success)
+
+ expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`")
+ end
+ end
+
+ context 'when there is a folder named `public`, but `publish` specifies a different one' do
+ let(:options) { { publish: 'foo' } }
+ let(:file) { fixture_file_upload("spec/fixtures/pages.zip") }
+ let(:metadata_filename) { "spec/fixtures/pages.zip.meta" }
+
+ it 'returns an error' do
+ expect(execute).not_to eq(:success)
+
+ expect(GenericCommitStatus.last.description).to eq("Error: You need to either include a `public/` folder in your artifacts, or specify which one to use for Pages using `publish` in `.gitlab-ci.yml`")
+ end
end
end
@@ -322,10 +366,14 @@ RSpec.describe Projects::UpdatePagesService do
context 'when retrying the job' do
let(:stage) { create(:ci_stage, position: 1_000_000, name: 'deploy', pipeline: pipeline) }
let!(:older_deploy_job) do
- create(:generic_commit_status, :failed, pipeline: pipeline,
- ref: build.ref,
- ci_stage: stage,
- name: 'pages:deploy')
+ create(
+ :generic_commit_status,
+ :failed,
+ pipeline: pipeline,
+ ref: build.ref,
+ ci_stage: stage,
+ name: 'pages:deploy'
+ )
end
before do
diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb
index 547641867bc..b65f7a50e4c 100644
--- a/spec/services/projects/update_remote_mirror_service_spec.rb
+++ b/spec/services/projects/update_remote_mirror_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::UpdateRemoteMirrorService do
+RSpec.describe Projects::UpdateRemoteMirrorService, feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository, lfs_enabled: true) }
let_it_be(:remote_project) { create(:forked_project_with_submodules) }
let_it_be(:remote_mirror) { create(:remote_mirror, project: project, enabled: true) }
diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb
index ee8f7fb2ef2..af920d51776 100644
--- a/spec/services/projects/update_repository_storage_service_spec.rb
+++ b/spec/services/projects/update_repository_storage_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::UpdateRepositoryStorageService do
+RSpec.describe Projects::UpdateRepositoryStorageService, feature_category: :source_code_management do
include Gitlab::ShellAdapter
subject { described_class.new(repository_storage_move) }
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 3cda6bc2627..8f55ee705ab 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::UpdateService do
+RSpec.describe Projects::UpdateService, feature_category: :projects do
include ExternalAuthorizationServiceHelpers
include ProjectForksHelper
@@ -227,48 +227,16 @@ RSpec.describe Projects::UpdateService do
let(:user) { project.first_owner }
let(:forked_project) { fork_project(project) }
- context 'and unlink forks feature flag is off' do
- before do
- stub_feature_flags(unlink_fork_network_upon_visibility_decrease: false)
- end
-
- it 'updates forks visibility level when parent set to more restrictive' do
- opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
-
- expect(project).to be_internal
- expect(forked_project).to be_internal
-
- expect(update_project(project, user, opts)).to eq({ status: :success })
+ it 'does not change visibility of forks' do
+ opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
- expect(project).to be_private
- expect(forked_project.reload).to be_private
- end
-
- it 'does not update forks visibility level when parent set to less restrictive' do
- opts = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
-
- expect(project).to be_internal
- expect(forked_project).to be_internal
+ expect(project).to be_internal
+ expect(forked_project).to be_internal
- expect(update_project(project, user, opts)).to eq({ status: :success })
+ expect(update_project(project, user, opts)).to eq({ status: :success })
- expect(project).to be_public
- expect(forked_project.reload).to be_internal
- end
- end
-
- context 'and unlink forks feature flag is on' do
- it 'does not change visibility of forks' do
- opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
-
- expect(project).to be_internal
- expect(forked_project).to be_internal
-
- expect(update_project(project, user, opts)).to eq({ status: :success })
-
- expect(project).to be_private
- expect(forked_project.reload).to be_internal
- end
+ expect(project).to be_private
+ expect(forked_project.reload).to be_internal
end
end
@@ -358,7 +326,9 @@ RSpec.describe Projects::UpdateService do
it 'logs an error and creates a metric when wiki can not be created' do
project.project_feature.update!(wiki_access_level: ProjectFeature::DISABLED)
- expect_any_instance_of(ProjectWiki).to receive(:create_wiki_repository).and_raise(Wiki::CouldNotCreateWikiError)
+ expect_next_instance_of(ProjectWiki) do |project_wiki|
+ expect(project_wiki).to receive(:create_wiki_repository).and_raise(Wiki::CouldNotCreateWikiError)
+ end
expect_any_instance_of(described_class).to receive(:log_error).with("Could not create wiki for #{project.full_name}")
counter = double(:counter)
@@ -387,24 +357,26 @@ RSpec.describe Projects::UpdateService do
# Using some sample features for testing.
# Not using all the features because some of them must be enabled/disabled together
%w[issues wiki forking].each do |feature_name|
- let(:feature) { "#{feature_name}_access_level" }
- let(:params) do
- { project_feature_attributes: { feature => ProjectFeature::ENABLED } }
- end
+ context "with feature_name:#{feature_name}" do
+ let(:feature) { "#{feature_name}_access_level" }
+ let(:params) do
+ { project_feature_attributes: { feature => ProjectFeature::ENABLED } }
+ end
- before do
- project.project_feature.update!(feature => ProjectFeature::DISABLED)
- end
+ before do
+ project.project_feature.update!(feature => ProjectFeature::DISABLED)
+ end
- it 'publishes Projects::ProjectFeaturesChangedEvent' do
- expect { update_project(project, user, params) }
- .to publish_event(Projects::ProjectFeaturesChangedEvent)
- .with(
- project_id: project.id,
- namespace_id: project.namespace_id,
- root_namespace_id: project.root_namespace.id,
- features: ["updated_at", feature]
- )
+ it 'publishes Projects::ProjectFeaturesChangedEvent' do
+ expect { update_project(project, user, params) }
+ .to publish_event(Projects::ProjectFeaturesChangedEvent)
+ .with(
+ project_id: project.id,
+ namespace_id: project.namespace_id,
+ root_namespace_id: project.root_namespace.id,
+ features: array_including(feature, "updated_at")
+ )
+ end
end
end
end
@@ -548,6 +520,25 @@ RSpec.describe Projects::UpdateService do
end
end
+ context 'when updating #runner_registration_enabled' do
+ it 'updates the attribute' do
+ expect { update_project(project, user, runner_registration_enabled: false) }
+ .to change { project.runner_registration_enabled }
+ .to(false)
+ end
+
+ context 'when runner registration is disabled for all projects' do
+ before do
+ stub_application_setting(valid_runner_registrars: [])
+ end
+
+ it 'restricts updating the attribute' do
+ expect { update_project(project, user, runner_registration_enabled: false) }
+ .not_to change { project.runner_registration_enabled }
+ end
+ end
+ end
+
context 'when updating runners settings' do
let(:settings) do
{ instance_runners_enabled: true, namespace_traversal_ids: [123] }
@@ -653,17 +644,19 @@ RSpec.describe Projects::UpdateService do
context 'when updating nested attributes for prometheus integration' do
context 'prometheus integration exists' do
let(:prometheus_integration_attributes) do
- attributes_for(:prometheus_integration,
- project: project,
- properties: { api_url: "http://new.prometheus.com", manual_configuration: "0" }
- )
+ attributes_for(
+ :prometheus_integration,
+ project: project,
+ properties: { api_url: "http://new.prometheus.com", manual_configuration: "0" }
+ )
end
let!(:prometheus_integration) do
- create(:prometheus_integration,
- project: project,
- properties: { api_url: "http://old.prometheus.com", manual_configuration: "0" }
- )
+ create(
+ :prometheus_integration,
+ project: project,
+ properties: { api_url: "http://old.prometheus.com", manual_configuration: "0" }
+ )
end
it 'updates existing record' do
@@ -677,10 +670,11 @@ RSpec.describe Projects::UpdateService do
context 'prometheus integration does not exist' do
context 'valid parameters' do
let(:prometheus_integration_attributes) do
- attributes_for(:prometheus_integration,
- project: project,
- properties: { api_url: "http://example.prometheus.com", manual_configuration: "0" }
- )
+ attributes_for(
+ :prometheus_integration,
+ project: project,
+ properties: { api_url: "http://example.prometheus.com", manual_configuration: "0" }
+ )
end
it 'creates new record' do
@@ -693,10 +687,11 @@ RSpec.describe Projects::UpdateService do
context 'invalid parameters' do
let(:prometheus_integration_attributes) do
- attributes_for(:prometheus_integration,
- project: project,
- properties: { api_url: nil, manual_configuration: "1" }
- )
+ attributes_for(
+ :prometheus_integration,
+ project: project,
+ properties: { api_url: nil, manual_configuration: "1" }
+ )
end
it 'does not create new record' do
@@ -794,6 +789,112 @@ RSpec.describe Projects::UpdateService do
expect(project.topic_list).to eq(%w[tag_list])
end
end
+
+ describe 'when updating pages unique domain', feature_category: :pages do
+ let(:group) { create(:group, path: 'group') }
+ let(:project) { create(:project, path: 'project', group: group) }
+
+ context 'with pages_unique_domain feature flag disabled' do
+ before do
+ stub_feature_flags(pages_unique_domain: false)
+ end
+
+ it 'does not change pages unique domain' do
+ expect(project)
+ .to receive(:update)
+ .with({ project_setting_attributes: { has_confluence: true } })
+ .and_call_original
+
+ expect do
+ update_project(project, user, project_setting_attributes: {
+ has_confluence: true,
+ pages_unique_domain_enabled: true
+ })
+ end.not_to change { project.project_setting.pages_unique_domain_enabled }
+ end
+
+ it 'does not remove other attributes' do
+ expect(project)
+ .to receive(:update)
+ .with({ name: 'True' })
+ .and_call_original
+
+ update_project(project, user, name: 'True')
+ end
+ end
+
+ context 'with pages_unique_domain feature flag enabled' do
+ before do
+ stub_feature_flags(pages_unique_domain: true)
+ end
+
+ it 'updates project pages unique domain' do
+ expect do
+ update_project(project, user, project_setting_attributes: {
+ pages_unique_domain_enabled: true
+ })
+ end.to change { project.project_setting.pages_unique_domain_enabled }
+
+ expect(project.project_setting.pages_unique_domain_enabled).to eq true
+ expect(project.project_setting.pages_unique_domain).to match %r{project-group-\w+}
+ end
+
+ it 'does not changes unique domain when it already exists' do
+ project.project_setting.update!(
+ pages_unique_domain_enabled: false,
+ pages_unique_domain: 'unique-domain'
+ )
+
+ expect do
+ update_project(project, user, project_setting_attributes: {
+ pages_unique_domain_enabled: true
+ })
+ end.to change { project.project_setting.pages_unique_domain_enabled }
+
+ expect(project.project_setting.pages_unique_domain_enabled).to eq true
+ expect(project.project_setting.pages_unique_domain).to eq 'unique-domain'
+ end
+
+ it 'does not changes unique domain when it disabling unique domain' do
+ project.project_setting.update!(
+ pages_unique_domain_enabled: true,
+ pages_unique_domain: 'unique-domain'
+ )
+
+ expect do
+ update_project(project, user, project_setting_attributes: {
+ pages_unique_domain_enabled: false
+ })
+ end.not_to change { project.project_setting.pages_unique_domain }
+
+ expect(project.project_setting.pages_unique_domain_enabled).to eq false
+ expect(project.project_setting.pages_unique_domain).to eq 'unique-domain'
+ end
+
+ context 'when there is another project with the unique domain' do
+ it 'fails pages unique domain already exists' do
+ create(
+ :project_setting,
+ pages_unique_domain_enabled: true,
+ pages_unique_domain: 'unique-domain'
+ )
+
+ allow(Gitlab::Pages::RandomDomain)
+ .to receive(:generate)
+ .and_return('unique-domain')
+
+ result = update_project(project, user, project_setting_attributes: {
+ pages_unique_domain_enabled: true
+ })
+
+ expect(result).to eq(
+ status: :error,
+ message: 'Project setting pages unique domain has already been taken'
+ )
+ end
+ end
+ end
+ end
end
describe '#run_auto_devops_pipeline?' do
diff --git a/spec/services/projects/update_statistics_service_spec.rb b/spec/services/projects/update_statistics_service_spec.rb
index 1cc69e7e2fe..f685b86acc0 100644
--- a/spec/services/projects/update_statistics_service_spec.rb
+++ b/spec/services/projects/update_statistics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Projects::UpdateStatisticsService do
+RSpec.describe Projects::UpdateStatisticsService, feature_category: :projects do
using RSpec::Parameterized::TableSyntax
let(:service) { described_class.new(project, nil, statistics: statistics) }
@@ -27,7 +27,7 @@ RSpec.describe Projects::UpdateStatisticsService do
['repository_size'] | [:size]
[:repository_size] | [:size]
[:lfs_objects_size] | nil
- [:commit_count] | [:commit_count] # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ [:commit_count] | [:commit_count]
[:repository_size, :commit_count] | %i(size commit_count)
[:repository_size, :commit_count, :lfs_objects_size] | %i(size commit_count)
end
diff --git a/spec/services/prometheus/proxy_service_spec.rb b/spec/services/prometheus/proxy_service_spec.rb
index b78683cace7..f71662f62ad 100644
--- a/spec/services/prometheus/proxy_service_spec.rb
+++ b/spec/services/prometheus/proxy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Prometheus::ProxyService do
+RSpec.describe Prometheus::ProxyService, feature_category: :metrics do
include ReactiveCachingHelpers
let_it_be(:project) { create(:project) }
diff --git a/spec/services/prometheus/proxy_variable_substitution_service_spec.rb b/spec/services/prometheus/proxy_variable_substitution_service_spec.rb
index d8c1fdffb98..fbee4b9c7d7 100644
--- a/spec/services/prometheus/proxy_variable_substitution_service_spec.rb
+++ b/spec/services/prometheus/proxy_variable_substitution_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Prometheus::ProxyVariableSubstitutionService do
+RSpec.describe Prometheus::ProxyVariableSubstitutionService, feature_category: :metrics do
describe '#execute' do
let_it_be(:environment) { create(:environment) }
diff --git a/spec/services/protected_branches/api_service_spec.rb b/spec/services/protected_branches/api_service_spec.rb
index c98e253267b..f7f5f451a49 100644
--- a/spec/services/protected_branches/api_service_spec.rb
+++ b/spec/services/protected_branches/api_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedBranches::ApiService do
+RSpec.describe ProtectedBranches::ApiService, feature_category: :compliance_management do
shared_examples 'execute with entity' do
it 'creates a protected branch with prefilled defaults' do
expect(::ProtectedBranches::CreateService).to receive(:new).with(
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
index ea434922661..0abf8a673f9 100644
--- a/spec/services/protected_branches/cache_service_spec.rb
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -3,7 +3,7 @@
#
require 'spec_helper'
-RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
+RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache, feature_category: :compliance_management do
shared_examples 'execute with entity' do
subject(:service) { described_class.new(entity, user) }
@@ -145,6 +145,7 @@ RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
context 'when feature flag disabled' do
before do
stub_feature_flags(group_protected_branches: false)
+ stub_feature_flags(allow_protected_branches_for_group: false)
end
it_behaves_like 'execute with entity'
diff --git a/spec/services/protected_branches/destroy_service_spec.rb b/spec/services/protected_branches/destroy_service_spec.rb
index 421d4aae5bb..e02b4475c02 100644
--- a/spec/services/protected_branches/destroy_service_spec.rb
+++ b/spec/services/protected_branches/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedBranches::DestroyService do
+RSpec.describe ProtectedBranches::DestroyService, feature_category: :compliance_management do
shared_examples 'execute with entity' do
subject(:service) { described_class.new(entity, user) }
diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb
index c70cc032a6a..8b11604aa15 100644
--- a/spec/services/protected_branches/update_service_spec.rb
+++ b/spec/services/protected_branches/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedBranches::UpdateService do
+RSpec.describe ProtectedBranches::UpdateService, feature_category: :compliance_management do
shared_examples 'execute with entity' do
let(:params) { { name: new_name } }
diff --git a/spec/services/protected_tags/create_service_spec.rb b/spec/services/protected_tags/create_service_spec.rb
index a0b99b595e3..78b14aae029 100644
--- a/spec/services/protected_tags/create_service_spec.rb
+++ b/spec/services/protected_tags/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedTags::CreateService do
+RSpec.describe ProtectedTags::CreateService, feature_category: :compliance_management do
let(:project) { create(:project) }
let(:user) { project.first_owner }
let(:params) do
diff --git a/spec/services/protected_tags/destroy_service_spec.rb b/spec/services/protected_tags/destroy_service_spec.rb
index 658a4f5557e..fcb30d39520 100644
--- a/spec/services/protected_tags/destroy_service_spec.rb
+++ b/spec/services/protected_tags/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedTags::DestroyService do
+RSpec.describe ProtectedTags::DestroyService, feature_category: :compliance_management do
let(:protected_tag) { create(:protected_tag) }
let(:project) { protected_tag.project }
let(:user) { project.first_owner }
diff --git a/spec/services/protected_tags/update_service_spec.rb b/spec/services/protected_tags/update_service_spec.rb
index 4b6e726bb6e..2fb6cf84719 100644
--- a/spec/services/protected_tags/update_service_spec.rb
+++ b/spec/services/protected_tags/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ProtectedTags::UpdateService do
+RSpec.describe ProtectedTags::UpdateService, feature_category: :compliance_management do
let(:protected_tag) { create(:protected_tag) }
let(:project) { protected_tag.project }
let(:user) { project.first_owner }
diff --git a/spec/services/push_event_payload_service_spec.rb b/spec/services/push_event_payload_service_spec.rb
index de2bec21a3c..50da5ca9b24 100644
--- a/spec/services/push_event_payload_service_spec.rb
+++ b/spec/services/push_event_payload_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe PushEventPayloadService do
+RSpec.describe PushEventPayloadService, feature_category: :source_code_management do
let(:event) { create(:push_event) }
describe '#execute' do
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 257e7eb972b..966782aca98 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -746,10 +746,10 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
it 'assigns to users with escaped underscores' do
user = create(:user)
base = user.username
- user.update!(username: "#{base}_")
+ user.update!(username: "#{base}_new")
issuable.project.add_developer(user)
- cmd = "/assign @#{base}\\_"
+ cmd = "/assign @#{base}\\_new"
_, updates, _ = service.execute(cmd, issuable)
@@ -1399,34 +1399,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
let(:issuable) { issue }
end
- # /draft is a toggle (ff disabled)
- it_behaves_like 'draft command' do
- let(:content) { '/draft' }
- let(:issuable) { merge_request }
-
- before do
- stub_feature_flags(draft_quick_action_non_toggle: false)
- end
- end
-
- # /draft is a toggle (ff disabled)
- it_behaves_like 'ready command' do
- let(:content) { '/draft' }
- let(:issuable) { merge_request }
-
- before do
- stub_feature_flags(draft_quick_action_non_toggle: false)
- issuable.update!(title: issuable.draft_title)
- end
- end
-
- # /draft is one way (ff enabled)
it_behaves_like 'draft command' do
let(:content) { '/draft' }
let(:issuable) { merge_request }
end
- # /draft is one way (ff enabled)
it_behaves_like 'draft/ready command no action' do
let(:content) { '/draft' }
let(:issuable) { merge_request }
@@ -2150,116 +2127,8 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
end
- context 'relate command' do
- let_it_be_with_refind(:group) { create(:group) }
-
- shared_examples 'relate command' do
- it 'relates issues' do
- service.execute(content, issue)
-
- expect(IssueLink.where(source: issue).map(&:target)).to match_array(issues_related)
- end
- end
-
- context 'user is member of group' do
- before do
- group.add_developer(developer)
- end
-
- context 'relate a single issue' do
- let(:other_issue) { create(:issue, project: project) }
- let(:issues_related) { [other_issue] }
- let(:content) { "/relate #{other_issue.to_reference}" }
-
- it_behaves_like 'relate command'
- end
-
- context 'relate multiple issues at once' do
- let(:second_issue) { create(:issue, project: project) }
- let(:third_issue) { create(:issue, project: project) }
- let(:issues_related) { [second_issue, third_issue] }
- let(:content) { "/relate #{second_issue.to_reference} #{third_issue.to_reference}" }
-
- it_behaves_like 'relate command'
- end
-
- context 'when quick action target is unpersisted' do
- let(:issue) { build(:issue, project: project) }
- let(:other_issue) { create(:issue, project: project) }
- let(:issues_related) { [other_issue] }
- let(:content) { "/relate #{other_issue.to_reference}" }
-
- it 'relates the issues after the issue is persisted' do
- service.execute(content, issue)
-
- issue.save!
-
- expect(IssueLink.where(source: issue).map(&:target)).to match_array(issues_related)
- end
- end
-
- context 'empty relate command' do
- let(:issues_related) { [] }
- let(:content) { '/relate' }
-
- it_behaves_like 'relate command'
- end
-
- context 'already having related issues' do
- let(:second_issue) { create(:issue, project: project) }
- let(:third_issue) { create(:issue, project: project) }
- let(:issues_related) { [second_issue, third_issue] }
- let(:content) { "/relate #{third_issue.to_reference(project)}" }
-
- before do
- create(:issue_link, source: issue, target: second_issue)
- end
-
- it_behaves_like 'relate command'
- end
-
- context 'cross project' do
- let(:another_group) { create(:group, :public) }
- let(:other_project) { create(:project, group: another_group) }
-
- before do
- another_group.add_developer(developer)
- end
-
- context 'relate a cross project issue' do
- let(:other_issue) { create(:issue, project: other_project) }
- let(:issues_related) { [other_issue] }
- let(:content) { "/relate #{other_issue.to_reference(project)}" }
-
- it_behaves_like 'relate command'
- end
-
- context 'relate multiple cross projects issues at once' do
- let(:second_issue) { create(:issue, project: other_project) }
- let(:third_issue) { create(:issue, project: other_project) }
- let(:issues_related) { [second_issue, third_issue] }
- let(:content) { "/relate #{second_issue.to_reference(project)} #{third_issue.to_reference(project)}" }
-
- it_behaves_like 'relate command'
- end
-
- context 'relate a non-existing issue' do
- let(:issues_related) { [] }
- let(:content) { "/relate imaginary##{non_existing_record_iid}" }
-
- it_behaves_like 'relate command'
- end
-
- context 'relate a private issue' do
- let(:private_project) { create(:project, :private) }
- let(:other_issue) { create(:issue, project: private_project) }
- let(:issues_related) { [] }
- let(:content) { "/relate #{other_issue.to_reference(project)}" }
-
- it_behaves_like 'relate command'
- end
- end
- end
+ it_behaves_like 'issues link quick action', :relate do
+ let(:user) { developer }
end
context 'invite_email command' do
@@ -2507,6 +2376,55 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
expect(message).to eq("Added ~\"Bug\" label.")
end
end
+
+ describe 'type command' do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let(:command) { '/type Task' }
+
+ context 'when user has sufficient permissions to create new type' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :create_task, work_item).and_return(true)
+ end
+
+ it 'populates :issue_type: and :work_item_type' do
+ _, updates, message = service.execute(command, work_item)
+
+ expect(message).to eq(_('Type changed successfully.'))
+ expect(updates).to eq({ issue_type: 'task', work_item_type: WorkItems::Type.default_by_type(:task) })
+ end
+
+ it 'returns error with an invalid type' do
+ _, updates, message = service.execute('/type foo', work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: Provided type is not supported."))
+ expect(updates).to eq({})
+ end
+
+ it 'returns error with same type' do
+ _, updates, message = service.execute('/type Issue', work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: Types are the same."))
+ expect(updates).to eq({})
+ end
+ end
+
+ context 'when user has insufficient permissions to create new type' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :create_task, work_item).and_return(false)
+ end
+
+ it 'returns error' do
+ _, updates, message = service.execute(command, work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: You have insufficient permissions."))
+ expect(updates).to eq({})
+ end
+ end
+ end
end
describe '#explain' do
@@ -2517,8 +2435,9 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
let(:content) { '/close' }
it 'includes issuable name' do
- _, explanations = service.explain(content, issue)
+ content_result, explanations = service.explain(content, issue)
+ expect(content_result).to eq('')
expect(explanations).to eq(['Closes this issue.'])
end
end
@@ -2704,27 +2623,6 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
end
- describe 'draft command toggle (deprecated)' do
- let(:content) { '/draft' }
-
- before do
- stub_feature_flags(draft_quick_action_non_toggle: false)
- end
-
- it 'includes the new status' do
- _, explanations = service.explain(content, merge_request)
-
- expect(explanations).to match_array(['Marks this merge request as a draft.'])
- end
-
- it 'sets the ready status on a draft' do
- merge_request.update!(title: merge_request.draft_title)
- _, explanations = service.explain(content, merge_request)
-
- expect(explanations).to match_array(["Marks this merge request as ready."])
- end
- end
-
describe 'draft command set' do
let(:content) { '/draft' }
@@ -2946,6 +2844,61 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
end
end
end
+
+ context 'with keep_actions' do
+ let(:content) { '/close' }
+
+ it 'keeps quick actions' do
+ content_result, explanations = service.explain(content, issue, keep_actions: true)
+
+ expect(content_result).to eq("\n/close")
+ expect(explanations).to eq(['Closes this issue.'])
+ end
+
+ it 'removes the quick action' do
+ content_result, explanations = service.explain(content, issue, keep_actions: false)
+
+ expect(content_result).to eq('')
+ expect(explanations).to eq(['Closes this issue.'])
+ end
+ end
+
+ describe 'type command' do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:work_item) { create(:work_item, :task, project: project) }
+
+ let(:command) { '/type Issue' }
+
+ it 'has command available' do
+ _, explanations = service.explain(command, work_item)
+
+ expect(explanations)
+ .to contain_exactly("Converts work item to Issue. Widgets not supported in new type are removed.")
+ end
+
+ context 'when feature flag work_items_mvc_2 is disabled' do
+ before do
+ stub_feature_flags(work_items_mvc_2: false)
+ end
+
+ it 'does not have the command available' do
+ _, explanations = service.explain(command, work_item)
+
+ expect(explanations).to be_empty
+ end
+ end
+ end
+
+ describe 'relate command' do
+ let_it_be(:other_issue) { create(:issue, project: project) }
+ let(:content) { "/relate #{other_issue.to_reference}" }
+
+ it 'includes explain message' do
+ _, explanations = service.explain(content, issue)
+
+ expect(explanations).to eq(["Marks this issue as related to #{other_issue.to_reference}."])
+ end
+ end
end
describe '#available_commands' do
diff --git a/spec/services/quick_actions/target_service_spec.rb b/spec/services/quick_actions/target_service_spec.rb
index 1b0a5d4ae73..5f4e92cf955 100644
--- a/spec/services/quick_actions/target_service_spec.rb
+++ b/spec/services/quick_actions/target_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe QuickActions::TargetService do
+RSpec.describe QuickActions::TargetService, feature_category: :team_planning do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
diff --git a/spec/services/releases/create_evidence_service_spec.rb b/spec/services/releases/create_evidence_service_spec.rb
index 0ac15a7291d..75d0a2b9c0e 100644
--- a/spec/services/releases/create_evidence_service_spec.rb
+++ b/spec/services/releases/create_evidence_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Releases::CreateEvidenceService do
+RSpec.describe Releases::CreateEvidenceService, feature_category: :release_orchestration do
let_it_be(:project) { create(:project) }
let(:release) { create(:release, project: project) }
diff --git a/spec/services/releases/create_service_spec.rb b/spec/services/releases/create_service_spec.rb
index 9768ceb12e8..ca5dd912e77 100644
--- a/spec/services/releases/create_service_spec.rb
+++ b/spec/services/releases/create_service_spec.rb
@@ -55,6 +55,26 @@ RSpec.describe Releases::CreateService, feature_category: :continuous_integratio
end
end
+ context 'when project is a catalog resource' do
+ let(:ref) { 'master' }
+ let!(:catalog_resource) { create(:catalog_resource, project: project) }
+
+ context 'and it is valid' do
+ let_it_be(:project) { create(:project, :repository, description: 'our components') }
+
+ it_behaves_like 'a successful release creation'
+ end
+
+ context 'and it is invalid' do
+ it 'raises an error and does not update the release' do
+ result = service.execute
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('Project must have a description')
+ end
+ end
+ end
+
context 'when ref is provided' do
let(:ref) { 'master' }
let(:tag_name) { 'foobar' }
diff --git a/spec/services/releases/destroy_service_spec.rb b/spec/services/releases/destroy_service_spec.rb
index 46550ac5bef..953490ac379 100644
--- a/spec/services/releases/destroy_service_spec.rb
+++ b/spec/services/releases/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Releases::DestroyService do
+RSpec.describe Releases::DestroyService, feature_category: :release_orchestration do
let(:project) { create(:project, :repository) }
let(:mainatiner) { create(:user) }
let(:repoter) { create(:user) }
diff --git a/spec/services/releases/links/create_service_spec.rb b/spec/services/releases/links/create_service_spec.rb
new file mode 100644
index 00000000000..9928d2162d7
--- /dev/null
+++ b/spec/services/releases/links/create_service_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::Links::CreateService, feature_category: :release_orchestration do
+ let(:service) { described_class.new(release, user, params) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:release) { create(:release, project: project, author: user, tag: 'v1.1.0') }
+
+ let(:params) { { name: name, url: url, direct_asset_path: direct_asset_path, link_type: link_type } }
+ let(:name) { 'link' }
+ let(:url) { 'https://example.com' }
+ let(:direct_asset_path) { '/path' }
+ let(:link_type) { 'other' }
+
+ before do
+ project.add_developer(user)
+ end
+
+ describe '#execute' do
+ subject(:execute) { service.execute }
+
+ let(:link) { subject.payload[:link] }
+
+ it 'successfully creates a release link' do
+ expect { execute }.to change { Releases::Link.count }.by(1)
+
+ expect(link).to have_attributes(
+ name: name,
+ url: url,
+ filepath: direct_asset_path,
+ link_type: link_type
+ )
+ end
+
+ context 'when user does not have access to create release link' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns an error' do
+ expect { execute }.not_to change { Releases::Link.count }
+
+ is_expected.to be_error
+ expect(execute.message).to include('Access Denied')
+ expect(execute.reason).to eq(:forbidden)
+ end
+ end
+
+ context 'when url is invalid' do
+ let(:url) { 'not_a_url' }
+
+ it 'returns an error' do
+ expect { execute }.not_to change { Releases::Link.count }
+
+ is_expected.to be_error
+ expect(execute.message[0]).to include('Url is blocked')
+ expect(execute.reason).to eq(:bad_request)
+ end
+ end
+
+ context 'when both direct_asset_path and filepath are provided' do
+ let(:params) { super().merge(filepath: '/filepath') }
+
+ it 'prefers direct_asset_path' do
+ is_expected.to be_success
+
+ expect(link.filepath).to eq(direct_asset_path)
+ end
+ end
+
+ context 'when only filepath is set' do
+ let(:params) { super().merge(filepath: '/filepath') }
+ let(:direct_asset_path) { nil }
+
+ it 'uses filepath' do
+ is_expected.to be_success
+
+ expect(link.filepath).to eq('/filepath')
+ end
+ end
+ end
+end
diff --git a/spec/services/releases/links/destroy_service_spec.rb b/spec/services/releases/links/destroy_service_spec.rb
new file mode 100644
index 00000000000..a248932eada
--- /dev/null
+++ b/spec/services/releases/links/destroy_service_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::Links::DestroyService, feature_category: :release_orchestration do
+ let(:service) { described_class.new(release, user, {}) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:release) { create(:release, project: project, author: user, tag: 'v1.1.0') }
+
+ let!(:release_link) do
+ create(
+ :release_link,
+ release: release,
+ name: 'awesome-app.dmg',
+ url: 'https://example.com/download/awesome-app.dmg'
+ )
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ describe '#execute' do
+ subject(:execute) { service.execute(release_link) }
+
+ it 'successfully deletes a release link' do
+ expect { execute }.to change { release.links.count }.by(-1)
+
+ is_expected.to be_success
+ end
+
+ context 'when user does not have access to delete release link' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns an error' do
+ expect { execute }.not_to change { release.links.count }
+
+ is_expected.to be_error
+ expect(execute.message).to include('Access Denied')
+ expect(execute.reason).to eq(:forbidden)
+ end
+ end
+
+ context 'when release link does not exist' do
+ let(:release_link) { nil }
+
+ it 'returns an error' do
+ expect { execute }.not_to change { release.links.count }
+
+ is_expected.to be_error
+ expect(execute.message).to eq('Link does not exist')
+ expect(execute.reason).to eq(:not_found)
+ end
+ end
+
+ context 'when release link deletion failed' do
+ before do
+ allow(release_link).to receive(:destroy).and_return(false)
+ end
+
+ it 'returns an error' do
+ expect { execute }.not_to change { release.links.count }
+
+ is_expected.to be_error
+ expect(execute.reason).to eq(:bad_request)
+ end
+ end
+ end
+end
diff --git a/spec/services/releases/links/update_service_spec.rb b/spec/services/releases/links/update_service_spec.rb
new file mode 100644
index 00000000000..3f48985cf60
--- /dev/null
+++ b/spec/services/releases/links/update_service_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Releases::Links::UpdateService, feature_category: :release_orchestration do
+ let(:service) { described_class.new(release, user, params) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:release) { create(:release, project: project, author: user, tag: 'v1.1.0') }
+
+ let(:release_link) do
+ create(
+ :release_link,
+ release: release,
+ name: 'awesome-app.dmg',
+ url: 'https://example.com/download/awesome-app.dmg'
+ )
+ end
+
+ let(:params) { { name: name, url: url, direct_asset_path: direct_asset_path, link_type: link_type } }
+ let(:name) { 'link' }
+ let(:url) { 'https://example.com' }
+ let(:direct_asset_path) { '/path' }
+ let(:link_type) { 'other' }
+
+ before do
+ project.add_developer(user)
+ end
+
+ describe '#execute' do
+ subject(:execute) { service.execute(release_link) }
+
+ let(:updated_link) { execute.payload[:link] }
+
+ it 'successfully updates a release link' do
+ is_expected.to be_success
+
+ expect(updated_link).to have_attributes(
+ name: name,
+ url: url,
+ filepath: direct_asset_path,
+ link_type: link_type
+ )
+ end
+
+ context 'when user does not have access to update release link' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns an error' do
+ is_expected.to be_error
+ expect(execute.message).to include('Access Denied')
+ expect(execute.reason).to eq(:forbidden)
+ end
+ end
+
+ context 'when url is invalid' do
+ let(:url) { 'not_a_url' }
+
+ it 'returns an error' do
+ is_expected.to be_error
+ expect(execute.message[0]).to include('Url is blocked')
+ expect(execute.reason).to eq(:bad_request)
+ end
+ end
+
+ context 'when both direct_asset_path and filepath are provided' do
+ let(:params) { super().merge(filepath: '/filepath') }
+
+ it 'prefers direct_asset_path' do
+ is_expected.to be_success
+
+ expect(updated_link.filepath).to eq(direct_asset_path)
+ end
+ end
+
+ context 'when only filepath is set' do
+ let(:params) { super().merge(filepath: '/filepath') }
+ let(:direct_asset_path) { nil }
+
+ it 'uses filepath' do
+ is_expected.to be_success
+
+ expect(updated_link.filepath).to eq('/filepath')
+ end
+ end
+ end
+end
diff --git a/spec/services/repositories/changelog_service_spec.rb b/spec/services/repositories/changelog_service_spec.rb
index 42b586637ad..1b5300672e3 100644
--- a/spec/services/repositories/changelog_service_spec.rb
+++ b/spec/services/repositories/changelog_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Repositories::ChangelogService do
+RSpec.describe Repositories::ChangelogService, feature_category: :source_code_management do
describe '#execute' do
let!(:project) { create(:project, :empty_repo) }
let!(:creator) { project.creator }
diff --git a/spec/services/repositories/destroy_service_spec.rb b/spec/services/repositories/destroy_service_spec.rb
index 565a18d501a..b3bad4fd84d 100644
--- a/spec/services/repositories/destroy_service_spec.rb
+++ b/spec/services/repositories/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Repositories::DestroyService do
+RSpec.describe Repositories::DestroyService, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace) }
diff --git a/spec/services/repository_archive_clean_up_service_spec.rb b/spec/services/repository_archive_clean_up_service_spec.rb
index 8db1a6858fa..1ce68080c73 100644
--- a/spec/services/repository_archive_clean_up_service_spec.rb
+++ b/spec/services/repository_archive_clean_up_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe RepositoryArchiveCleanUpService do
+RSpec.describe RepositoryArchiveCleanUpService, feature_category: :source_code_management do
subject(:service) { described_class.new }
describe '#execute (new archive locations)' do
diff --git a/spec/services/reset_project_cache_service_spec.rb b/spec/services/reset_project_cache_service_spec.rb
index 165b38ee338..6ae516a5f07 100644
--- a/spec/services/reset_project_cache_service_spec.rb
+++ b/spec/services/reset_project_cache_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResetProjectCacheService do
+RSpec.describe ResetProjectCacheService, feature_category: :projects do
let(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/services/resource_access_tokens/create_service_spec.rb b/spec/services/resource_access_tokens/create_service_spec.rb
index a8c8d41ca09..59d582f038a 100644
--- a/spec/services/resource_access_tokens/create_service_spec.rb
+++ b/spec/services/resource_access_tokens/create_service_spec.rb
@@ -2,13 +2,16 @@
require 'spec_helper'
-RSpec.describe ResourceAccessTokens::CreateService do
+RSpec.describe ResourceAccessTokens::CreateService, feature_category: :system_access do
subject { described_class.new(user, resource, params).execute }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:params) { {} }
+ let_it_be(:max_pat_access_token_lifetime) do
+ PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date.freeze
+ end
before do
stub_config_setting(host: 'example.com')
@@ -99,12 +102,20 @@ RSpec.describe ResourceAccessTokens::CreateService do
end
end
- context 'bot email' do
- it 'check email domain' do
- response = subject
- access_token = response.payload[:access_token]
+ context 'bot username and email' do
+ include_examples 'username and email pair is generated by Gitlab::Utils::UsernameAndEmailGenerator' do
+ subject do
+ response = described_class.new(user, resource, params).execute
+ response.payload[:access_token].user
+ end
- expect(access_token.user.email).to end_with("@noreply.#{Gitlab.config.gitlab.host}")
+ let(:username_prefix) do
+ "#{resource.class.name.downcase}_#{resource.id}_bot"
+ end
+
+ let(:email_domain) do
+ "noreply.#{Gitlab.config.gitlab.host}"
+ end
end
end
@@ -119,9 +130,7 @@ RSpec.describe ResourceAccessTokens::CreateService do
end
end
- context 'when user specifies an access level' do
- let_it_be(:params) { { access_level: Gitlab::Access::DEVELOPER } }
-
+ shared_examples 'bot with access level' do
it 'adds the bot user with the specified access level in the resource' do
response = subject
access_token = response.payload[:access_token]
@@ -131,6 +140,18 @@ RSpec.describe ResourceAccessTokens::CreateService do
end
end
+ context 'when user specifies an access level' do
+ let_it_be(:params) { { access_level: Gitlab::Access::DEVELOPER } }
+
+ it_behaves_like 'bot with access level'
+ end
+
+ context 'with DEVELOPER access_level, in string format' do
+ let_it_be(:params) { { access_level: Gitlab::Access::DEVELOPER.to_s } }
+
+ it_behaves_like 'bot with access level'
+ end
+
context 'when user is external' do
before do
user.update!(external: true)
@@ -167,20 +188,51 @@ RSpec.describe ResourceAccessTokens::CreateService do
context 'expires_at' do
context 'when no expiration value is passed' do
- it 'uses nil expiration value' do
- response = subject
- access_token = response.payload[:access_token]
+ context 'when default_pat_expiration feature flag is true' do
+ it 'defaults to PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS' do
+ freeze_time do
+ response = subject
+ access_token = response.payload[:access_token]
+
+ expect(access_token.expires_at).to eq(
+ max_pat_access_token_lifetime.to_date
+ )
+ end
+ end
- expect(access_token.expires_at).to eq(nil)
+ context 'expiry of the project bot member' do
+ it 'project bot membership does not expire' do
+ response = subject
+ access_token = response.payload[:access_token]
+ project_bot = access_token.user
+
+ expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(
+ max_pat_access_token_lifetime.to_date
+ )
+ end
+ end
end
- context 'expiry of the project bot member' do
- it 'project bot membership does not expire' do
+ context 'when default_pat_expiration feature flag is false' do
+ before do
+ stub_feature_flags(default_pat_expiration: false)
+ end
+
+ it 'uses nil expiration value' do
response = subject
access_token = response.payload[:access_token]
- project_bot = access_token.user
- expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(nil)
+ expect(access_token.expires_at).to eq(nil)
+ end
+
+ context 'expiry of the project bot member' do
+ it 'project bot membership expires' do
+ response = subject
+ access_token = response.payload[:access_token]
+ project_bot = access_token.user
+
+ expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(nil)
+ end
end
end
end
@@ -201,7 +253,7 @@ RSpec.describe ResourceAccessTokens::CreateService do
access_token = response.payload[:access_token]
project_bot = access_token.user
- expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(params[:expires_at])
+ expect(resource.members.find_by(user_id: project_bot.id).expires_at).to eq(access_token.expires_at)
end
end
end
@@ -219,17 +271,31 @@ RSpec.describe ResourceAccessTokens::CreateService do
let_it_be(:bot_user) { create(:user, :project_bot) }
let(:unpersisted_member) { build(:project_member, source: resource, user: bot_user) }
- let(:error_message) { 'Could not provision maintainer access to project access token' }
+ let(:error_message) { 'Could not provision maintainer access to the access token. ERROR: error message' }
before do
allow_next_instance_of(ResourceAccessTokens::CreateService) do |service|
allow(service).to receive(:create_user).and_return(bot_user)
allow(service).to receive(:create_membership).and_return(unpersisted_member)
end
+
+ allow(unpersisted_member).to receive_message_chain(:errors, :full_messages, :to_sentence)
+ .and_return('error message')
end
- it_behaves_like 'token creation fails'
- it_behaves_like 'correct error message'
+ context 'with MAINTAINER access_level, in integer format' do
+ let_it_be(:params) { { access_level: Gitlab::Access::MAINTAINER } }
+
+ it_behaves_like 'token creation fails'
+ it_behaves_like 'correct error message'
+ end
+
+ context 'with MAINTAINER access_level, in string format' do
+ let_it_be(:params) { { access_level: Gitlab::Access::MAINTAINER.to_s } }
+
+ it_behaves_like 'token creation fails'
+ it_behaves_like 'correct error message'
+ end
end
end
diff --git a/spec/services/resource_access_tokens/revoke_service_spec.rb b/spec/services/resource_access_tokens/revoke_service_spec.rb
index 28f173f1bc7..c00146961e3 100644
--- a/spec/services/resource_access_tokens/revoke_service_spec.rb
+++ b/spec/services/resource_access_tokens/revoke_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceAccessTokens::RevokeService do
+RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_access do
subject { described_class.new(user, resource, access_token).execute }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/resource_events/change_labels_service_spec.rb b/spec/services/resource_events/change_labels_service_spec.rb
index d94b49de9d7..8393ce78df8 100644
--- a/spec/services/resource_events/change_labels_service_spec.rb
+++ b/spec/services/resource_events/change_labels_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
# feature category is shared among plan(issues, epics), monitor(incidents), create(merge request) stages
-RSpec.describe ResourceEvents::ChangeLabelsService, feature_category: :shared do
+RSpec.describe ResourceEvents::ChangeLabelsService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:author) { create(:user) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/services/resource_events/change_milestone_service_spec.rb b/spec/services/resource_events/change_milestone_service_spec.rb
index 425d5b19907..077058df1d5 100644
--- a/spec/services/resource_events/change_milestone_service_spec.rb
+++ b/spec/services/resource_events/change_milestone_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::ChangeMilestoneService do
+RSpec.describe ResourceEvents::ChangeMilestoneService, feature_category: :team_planning do
let_it_be(:timebox) { create(:milestone) }
let(:created_at_time) { Time.utc(2019, 12, 30) }
diff --git a/spec/services/resource_events/change_state_service_spec.rb b/spec/services/resource_events/change_state_service_spec.rb
index b679943073c..a63b4302635 100644
--- a/spec/services/resource_events/change_state_service_spec.rb
+++ b/spec/services/resource_events/change_state_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::ChangeStateService do
+RSpec.describe ResourceEvents::ChangeStateService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/resource_events/merge_into_notes_service_spec.rb b/spec/services/resource_events/merge_into_notes_service_spec.rb
index ebfd942066f..6eb6780d704 100644
--- a/spec/services/resource_events/merge_into_notes_service_spec.rb
+++ b/spec/services/resource_events/merge_into_notes_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::MergeIntoNotesService do
+RSpec.describe ResourceEvents::MergeIntoNotesService, feature_category: :team_planning do
def create_event(params)
event_params = { action: :add, label: label, issue: resource,
user: user }
diff --git a/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
index 71b1d0993ee..3396abaff9e 100644
--- a/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
+++ b/spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::SyntheticLabelNotesBuilderService do
+RSpec.describe ResourceEvents::SyntheticLabelNotesBuilderService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
index f368e107c60..20537aa3685 100644
--- a/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
+++ b/spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
+RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, author: user) }
@@ -11,7 +11,8 @@ RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
let_it_be(:events) do
[
create(:resource_milestone_event, issue: issue, milestone: milestone, action: :add, created_at: '2020-01-01 04:00'),
- create(:resource_milestone_event, issue: issue, milestone: milestone, action: :remove, created_at: '2020-01-02 08:00')
+ create(:resource_milestone_event, issue: issue, milestone: milestone, action: :remove, created_at: '2020-01-02 08:00'),
+ create(:resource_milestone_event, issue: issue, milestone: nil, action: :remove, created_at: '2020-01-02 08:00')
]
end
@@ -22,7 +23,8 @@ RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
expect(notes.map(&:note)).to eq(
[
"changed milestone to %#{milestone.iid}",
- 'removed milestone'
+ "removed milestone %#{milestone.iid}",
+ "removed milestone "
])
end
diff --git a/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb b/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
index 79500f3768b..9f838660f92 100644
--- a/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
+++ b/spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ResourceEvents::SyntheticStateNotesBuilderService do
+RSpec.describe ResourceEvents::SyntheticStateNotesBuilderService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb
index e8716ef4d90..6250d32574f 100644
--- a/spec/services/search/global_service_spec.rb
+++ b/spec/services/search/global_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Search::GlobalService do
+RSpec.describe Search::GlobalService, feature_category: :global_search do
let(:user) { create(:user) }
let(:internal_user) { create(:user) }
diff --git a/spec/services/search/group_service_spec.rb b/spec/services/search/group_service_spec.rb
index c9bfa7cb7b4..e8a4a228f8f 100644
--- a/spec/services/search/group_service_spec.rb
+++ b/spec/services/search/group_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Search::GroupService do
+RSpec.describe Search::GroupService, feature_category: :global_search do
shared_examples_for 'group search' do
context 'finding projects by name' do
let(:user) { create(:user) }
diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb
index d204f626635..d60b60d28e4 100644
--- a/spec/services/search/snippet_service_spec.rb
+++ b/spec/services/search/snippet_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Search::SnippetService do
+RSpec.describe Search::SnippetService, feature_category: :global_search do
let_it_be(:author) { create(:author) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb b/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb
index df76750efc8..a56fbb026c1 100644
--- a/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb
+++ b/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Security::CiConfiguration::ContainerScanningCreateService, :snowplow do
+RSpec.describe Security::CiConfiguration::ContainerScanningCreateService, :snowplow, feature_category: :container_scanning do
subject(:result) { described_class.new(project, user).execute }
let(:branch_name) { 'set-container-scanning-config-1' }
diff --git a/spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb b/spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb
index 719a2cf24e9..7ac2249642a 100644
--- a/spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb
+++ b/spec/services/security/ci_configuration/dependency_scanning_create_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Security::CiConfiguration::DependencyScanningCreateService, :snowplow,
- feature_category: :dependency_scanning do
+ feature_category: :software_composition_analysis do
subject(:result) { described_class.new(project, user).execute }
let(:branch_name) { 'set-dependency-scanning-config-1' }
diff --git a/spec/services/security/ci_configuration/sast_iac_create_service_spec.rb b/spec/services/security/ci_configuration/sast_iac_create_service_spec.rb
index deb10732b37..7f1ad543f7c 100644
--- a/spec/services/security/ci_configuration/sast_iac_create_service_spec.rb
+++ b/spec/services/security/ci_configuration/sast_iac_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Security::CiConfiguration::SastIacCreateService, :snowplow do
+RSpec.describe Security::CiConfiguration::SastIacCreateService, :snowplow, feature_category: :static_application_security_testing do
subject(:result) { described_class.new(project, user).execute }
let(:branch_name) { 'set-sast-iac-config-1' }
diff --git a/spec/services/security/ci_configuration/sast_parser_service_spec.rb b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
index 9211beb76f8..051bbcd194b 100644
--- a/spec/services/security/ci_configuration/sast_parser_service_spec.rb
+++ b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Security::CiConfiguration::SastParserService do
+RSpec.describe Security::CiConfiguration::SastParserService, feature_category: :static_application_security_testing do
describe '#configuration' do
include_context 'read ci configuration for sast enabled project'
diff --git a/spec/services/security/ci_configuration/secret_detection_create_service_spec.rb b/spec/services/security/ci_configuration/secret_detection_create_service_spec.rb
index c1df3ebdca5..6cbeb219d11 100644
--- a/spec/services/security/ci_configuration/secret_detection_create_service_spec.rb
+++ b/spec/services/security/ci_configuration/secret_detection_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Security::CiConfiguration::SecretDetectionCreateService, :snowplow do
+RSpec.describe Security::CiConfiguration::SecretDetectionCreateService, :snowplow, feature_category: :container_scanning do
subject(:result) { described_class.new(project, user).execute }
let(:branch_name) { 'set-secret-detection-config-1' }
diff --git a/spec/services/security/merge_reports_service_spec.rb b/spec/services/security/merge_reports_service_spec.rb
index 249f4da5f34..809d0b27c20 100644
--- a/spec/services/security/merge_reports_service_spec.rb
+++ b/spec/services/security/merge_reports_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
# rubocop: disable RSpec/MultipleMemoizedHelpers
-RSpec.describe Security::MergeReportsService, '#execute' do
+RSpec.describe Security::MergeReportsService, '#execute', feature_category: :code_review_workflow do
let(:scanner_1) { build(:ci_reports_security_scanner, external_id: 'scanner-1', name: 'Scanner 1') }
let(:scanner_2) { build(:ci_reports_security_scanner, external_id: 'scanner-2', name: 'Scanner 2') }
let(:scanner_3) { build(:ci_reports_security_scanner, external_id: 'scanner-3', name: 'Scanner 3') }
diff --git a/spec/services/serverless/associate_domain_service_spec.rb b/spec/services/serverless/associate_domain_service_spec.rb
deleted file mode 100644
index 2f45806589e..00000000000
--- a/spec/services/serverless/associate_domain_service_spec.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Serverless::AssociateDomainService do
- let_it_be(:sdc_pages_domain) { create(:pages_domain, :instance_serverless) }
- let_it_be(:sdc_cluster) { create(:cluster, :with_installed_helm, :provided_by_gcp) }
- let_it_be(:sdc_knative) { create(:clusters_applications_knative, cluster: sdc_cluster) }
- let_it_be(:sdc_creator) { create(:user) }
-
- let(:sdc) do
- create(:serverless_domain_cluster,
- knative: sdc_knative,
- creator: sdc_creator,
- pages_domain: sdc_pages_domain)
- end
-
- let(:knative) { sdc.knative }
- let(:creator) { sdc.creator }
- let(:pages_domain_id) { sdc.pages_domain_id }
-
- subject { described_class.new(knative, pages_domain_id: pages_domain_id, creator: creator) }
-
- context 'when the domain is unchanged' do
- let(:creator) { create(:user) }
-
- it 'does not update creator' do
- expect { subject.execute }.not_to change { sdc.reload.creator }
- end
- end
-
- context 'when domain is changed to nil' do
- let_it_be(:creator) { create(:user) }
- let_it_be(:pages_domain_id) { nil }
-
- it 'removes the association between knative and the domain' do
- expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
- end
-
- it 'does not attempt to update creator' do
- expect { subject.execute }.not_to raise_error
- end
- end
-
- context 'when a new domain is associated' do
- let_it_be(:creator) { create(:user) }
- let_it_be(:pages_domain_id) { create(:pages_domain, :instance_serverless).id }
-
- it 'creates an association with the domain' do
- expect { subject.execute }.to change { knative.reload.pages_domain.id }
- .from(sdc.pages_domain.id)
- .to(pages_domain_id)
- end
-
- it 'updates creator' do
- expect { subject.execute }.to change { sdc.reload.creator }.from(sdc.creator).to(creator)
- end
- end
-
- context 'when knative is not authorized to use the pages domain' do
- let_it_be(:pages_domain_id) { create(:pages_domain).id }
-
- before do
- expect(knative).to receive(:available_domains).and_return(PagesDomain.none)
- end
-
- it 'sets pages_domain_id to nil' do
- expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
- end
- end
-
- describe 'for new knative application' do
- let_it_be(:cluster) { create(:cluster, :with_installed_helm, :provided_by_gcp) }
-
- context 'when knative hostname is nil' do
- let(:knative) { build(:clusters_applications_knative, cluster: cluster, hostname: nil) }
-
- it 'sets hostname to a placeholder value' do
- expect { subject.execute }.to change { knative.hostname }.to('example.com')
- end
- end
-
- context 'when knative hostname exists' do
- let(:knative) { build(:clusters_applications_knative, cluster: cluster, hostname: 'hostname.com') }
-
- it 'does not change hostname' do
- expect { subject.execute }.not_to change { knative.hostname }
- end
- end
- end
-end
diff --git a/spec/services/service_desk_settings/update_service_spec.rb b/spec/services/service_desk_settings/update_service_spec.rb
index 72134af1369..342fb2b6b7a 100644
--- a/spec/services/service_desk_settings/update_service_spec.rb
+++ b/spec/services/service_desk_settings/update_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe ServiceDeskSettings::UpdateService do
+RSpec.describe ServiceDeskSettings::UpdateService, feature_category: :service_desk do
describe '#execute' do
let_it_be(:settings) { create(:service_desk_setting, outgoing_name: 'original name') }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/service_ping/submit_service_ping_service_spec.rb b/spec/services/service_ping/submit_service_ping_service_spec.rb
index b02f1e84d25..2248febda5c 100644
--- a/spec/services/service_ping/submit_service_ping_service_spec.rb
+++ b/spec/services/service_ping/submit_service_ping_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ServicePing::SubmitService do
+RSpec.describe ServicePing::SubmitService, feature_category: :service_ping do
include StubRequests
include UsageDataHelpers
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
index 58dd2fd4c5e..6171ca1a8a6 100644
--- a/spec/services/service_response_spec.rb
+++ b/spec/services/service_response_spec.rb
@@ -7,7 +7,7 @@ require 're2'
require_relative '../../app/services/service_response'
require_relative '../../lib/gitlab/error_tracking'
-RSpec.describe ServiceResponse do
+RSpec.describe ServiceResponse, feature_category: :shared do
describe '.success' do
it 'creates a successful response without a message' do
expect(described_class.success).to be_success
diff --git a/spec/services/snippets/bulk_destroy_service_spec.rb b/spec/services/snippets/bulk_destroy_service_spec.rb
index 4142aa349e4..208386aee48 100644
--- a/spec/services/snippets/bulk_destroy_service_spec.rb
+++ b/spec/services/snippets/bulk_destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::BulkDestroyService do
+RSpec.describe Snippets::BulkDestroyService, feature_category: :source_code_management do
let_it_be(:project) { create(:project) }
let(:user) { create(:user) }
diff --git a/spec/services/snippets/count_service_spec.rb b/spec/services/snippets/count_service_spec.rb
index 5ce637d0bac..4ad9b07d518 100644
--- a/spec/services/snippets/count_service_spec.rb
+++ b/spec/services/snippets/count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::CountService do
+RSpec.describe Snippets::CountService, feature_category: :source_code_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
diff --git a/spec/services/snippets/create_service_spec.rb b/spec/services/snippets/create_service_spec.rb
index 0eb73c8edd2..725f1b165a2 100644
--- a/spec/services/snippets/create_service_spec.rb
+++ b/spec/services/snippets/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::CreateService do
+RSpec.describe Snippets::CreateService, feature_category: :source_code_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
diff --git a/spec/services/snippets/destroy_service_spec.rb b/spec/services/snippets/destroy_service_spec.rb
index 23765243dd6..ace9847185e 100644
--- a/spec/services/snippets/destroy_service_spec.rb
+++ b/spec/services/snippets/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::DestroyService do
+RSpec.describe Snippets::DestroyService, feature_category: :source_code_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:other_user) { create(:user) }
@@ -144,7 +144,7 @@ RSpec.describe Snippets::DestroyService do
end
end
- context 'when the repository does not exists' do
+ context 'when the repository does not exist' do
let(:snippet) { create(:personal_snippet, author: user) }
it 'does not schedule anything and return success' do
diff --git a/spec/services/snippets/repository_validation_service_spec.rb b/spec/services/snippets/repository_validation_service_spec.rb
index 8166ce144e1..c9cd9f21481 100644
--- a/spec/services/snippets/repository_validation_service_spec.rb
+++ b/spec/services/snippets/repository_validation_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::RepositoryValidationService do
+RSpec.describe Snippets::RepositoryValidationService, feature_category: :source_code_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:snippet) { create(:personal_snippet, :empty_repo, author: user) }
diff --git a/spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb b/spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb
index 9286d73ed4a..e88969ccf2d 100644
--- a/spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb
+++ b/spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::ScheduleBulkRepositoryShardMovesService do
+RSpec.describe Snippets::ScheduleBulkRepositoryShardMovesService, feature_category: :source_code_management do
it_behaves_like 'moves repository shard in bulk' do
let_it_be_with_reload(:container) { create(:snippet, :repository) }
diff --git a/spec/services/snippets/update_repository_storage_service_spec.rb b/spec/services/snippets/update_repository_storage_service_spec.rb
index 9874189f73a..c417fbfd8b1 100644
--- a/spec/services/snippets/update_repository_storage_service_spec.rb
+++ b/spec/services/snippets/update_repository_storage_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::UpdateRepositoryStorageService do
+RSpec.describe Snippets::UpdateRepositoryStorageService, feature_category: :source_code_management do
subject { described_class.new(repository_storage_move) }
describe "#execute" do
diff --git a/spec/services/snippets/update_service_spec.rb b/spec/services/snippets/update_service_spec.rb
index 67cc258b4b6..99bb70a3077 100644
--- a/spec/services/snippets/update_service_spec.rb
+++ b/spec/services/snippets/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::UpdateService do
+RSpec.describe Snippets::UpdateService, feature_category: :source_code_management do
describe '#execute', :aggregate_failures do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create :user, admin: true }
diff --git a/spec/services/snippets/update_statistics_service_spec.rb b/spec/services/snippets/update_statistics_service_spec.rb
index 27ae054676a..2d1872a09c4 100644
--- a/spec/services/snippets/update_statistics_service_spec.rb
+++ b/spec/services/snippets/update_statistics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Snippets::UpdateStatisticsService do
+RSpec.describe Snippets::UpdateStatisticsService, feature_category: :source_code_management do
describe '#execute' do
subject { described_class.new(snippet).execute }
diff --git a/spec/services/spam/akismet_mark_as_spam_service_spec.rb b/spec/services/spam/akismet_mark_as_spam_service_spec.rb
index 12666e23e47..f07fa8d262b 100644
--- a/spec/services/spam/akismet_mark_as_spam_service_spec.rb
+++ b/spec/services/spam/akismet_mark_as_spam_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::AkismetMarkAsSpamService do
+RSpec.describe Spam::AkismetMarkAsSpamService, feature_category: :instance_resiliency do
let(:user_agent_detail) { build(:user_agent_detail) }
let(:spammable) { build(:issue, user_agent_detail: user_agent_detail) }
let(:fake_akismet_service) { double(:akismet_service, submit_spam: true) }
diff --git a/spec/services/spam/akismet_service_spec.rb b/spec/services/spam/akismet_service_spec.rb
index d9f62258a53..4d6a1650327 100644
--- a/spec/services/spam/akismet_service_spec.rb
+++ b/spec/services/spam/akismet_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::AkismetService do
+RSpec.describe Spam::AkismetService, feature_category: :instance_resiliency do
let(:fake_akismet_client) { double(:akismet_client) }
let(:ip) { '1.2.3.4' }
let(:user_agent) { 'some user_agent' }
diff --git a/spec/services/spam/ham_service_spec.rb b/spec/services/spam/ham_service_spec.rb
index 0101a8e7704..00906bc4b3d 100644
--- a/spec/services/spam/ham_service_spec.rb
+++ b/spec/services/spam/ham_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::HamService do
+RSpec.describe Spam::HamService, feature_category: :instance_resiliency do
let_it_be(:user) { create(:user) }
let!(:spam_log) { create(:spam_log, user: user, submitted_as_ham: false) }
diff --git a/spec/services/spam/spam_action_service_spec.rb b/spec/services/spam/spam_action_service_spec.rb
index 4dfec9735ba..e2cc2ea7ce3 100644
--- a/spec/services/spam/spam_action_service_spec.rb
+++ b/spec/services/spam/spam_action_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::SpamActionService do
+RSpec.describe Spam::SpamActionService, feature_category: :instance_resiliency do
include_context 'includes Spam constants'
let(:issue) { create(:issue, project: project, author: author) }
@@ -53,6 +53,16 @@ RSpec.describe Spam::SpamActionService do
end
end
+ shared_examples 'allows user' do
+ it 'does not perform spam check' do
+ expect(Spam::SpamVerdictService).not_to receive(:new)
+
+ response = subject
+
+ expect(response.message).to match(/user was allowlisted/)
+ end
+ end
+
shared_examples 'creates a spam log' do |target_type|
it do
expect { subject }
@@ -73,7 +83,6 @@ RSpec.describe Spam::SpamActionService do
shared_examples 'execute spam action service' do |target_type|
let(:fake_captcha_verification_service) { double(:captcha_verification_service) }
let(:fake_verdict_service) { double(:spam_verdict_service) }
- let(:allowlisted) { false }
let(:verdict_service_opts) do
{
@@ -101,7 +110,6 @@ RSpec.describe Spam::SpamActionService do
subject do
described_service = described_class.new(spammable: target, spam_params: spam_params, extra_features:
extra_features, user: user, action: :create)
- allow(described_service).to receive(:allowlisted?).and_return(allowlisted)
described_service.execute
end
@@ -158,16 +166,20 @@ RSpec.describe Spam::SpamActionService do
target.description = 'Lovely Spam! Wonderful Spam!'
end
- context 'when allowlisted' do
- let(:allowlisted) { true }
-
- it 'does not perform spam check' do
- expect(Spam::SpamVerdictService).not_to receive(:new)
+ context 'when user is a gitlab bot' do
+ before do
+ allow(user).to receive(:gitlab_bot?).and_return(true)
+ end
- response = subject
+ it_behaves_like 'allows user'
+ end
- expect(response.message).to match(/user was allowlisted/)
+ context 'when user is a gitlab service user' do
+ before do
+ allow(user).to receive(:gitlab_service_user?).and_return(true)
end
+
+ it_behaves_like 'allows user'
end
context 'when disallowed by the spam verdict service' do
diff --git a/spec/services/spam/spam_params_spec.rb b/spec/services/spam/spam_params_spec.rb
index 7e74641c0fa..39c3b303529 100644
--- a/spec/services/spam/spam_params_spec.rb
+++ b/spec/services/spam/spam_params_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::SpamParams do
+RSpec.describe Spam::SpamParams, feature_category: :instance_resiliency do
shared_examples 'constructs from a request' do
it 'constructs from a request' do
expected = ::Spam::SpamParams.new(
diff --git a/spec/services/spam/spam_verdict_service_spec.rb b/spec/services/spam/spam_verdict_service_spec.rb
index dde93aa6b93..00e320ed56c 100644
--- a/spec/services/spam/spam_verdict_service_spec.rb
+++ b/spec/services/spam/spam_verdict_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Spam::SpamVerdictService do
+RSpec.describe Spam::SpamVerdictService, feature_category: :instance_resiliency do
include_context 'includes Spam constants'
let(:fake_ip) { '1.2.3.4' }
@@ -14,6 +14,22 @@ RSpec.describe Spam::SpamVerdictService do
'HTTP_REFERER' => fake_referer }
end
+ let(:verdict_value) { ::Spamcheck::SpamVerdict::Verdict::ALLOW }
+ let(:verdict_score) { 0.01 }
+ let(:verdict_evaluated) { true }
+
+ let(:response) do
+ response = ::Spamcheck::SpamVerdict.new
+ response.verdict = verdict_value
+ response.score = verdict_score
+ response.evaluated = verdict_evaluated
+ response
+ end
+
+ let(:spam_client_result) do
+ Gitlab::Spamcheck::Result.new(response)
+ end
+
let(:check_for_spam) { true }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, author: user) }
@@ -23,43 +39,38 @@ RSpec.describe Spam::SpamVerdictService do
described_class.new(user: user, target: target, options: {})
end
- let(:attribs) do
- extra_attributes = { "monitorMode" => "false" }
- extra_attributes
- end
-
shared_examples 'execute spam verdict service' do
- subject { service.execute }
+ subject(:execute) { service.execute }
before do
- allow(service).to receive(:akismet_verdict).and_return(nil)
- allow(service).to receive(:spamcheck_verdict).and_return([nil, attribs])
+ allow(service).to receive(:get_akismet_verdict).and_return(nil)
+ allow(service).to receive(:get_spamcheck_verdict).and_return(nil)
end
context 'if all services return nil' do
it 'renders ALLOW verdict' do
- expect(subject).to eq ALLOW
+ is_expected.to eq ALLOW
end
end
context 'if only one service returns a verdict' do
context 'and it is supported' do
before do
- allow(service).to receive(:akismet_verdict).and_return(DISALLOW)
+ allow(service).to receive(:get_akismet_verdict).and_return(DISALLOW)
end
it 'renders that verdict' do
- expect(subject).to eq DISALLOW
+ is_expected.to eq DISALLOW
end
end
context 'and it is unexpected' do
before do
- allow(service).to receive(:akismet_verdict).and_return("unexpected")
+ allow(service).to receive(:get_akismet_verdict).and_return("unexpected")
end
it 'allows' do
- expect(subject).to eq ALLOW
+ is_expected.to eq ALLOW
end
end
end
@@ -67,50 +78,34 @@ RSpec.describe Spam::SpamVerdictService do
context 'if more than one service returns a verdict' do
context 'and they are supported' do
before do
- allow(service).to receive(:akismet_verdict).and_return(DISALLOW)
- allow(service).to receive(:spamcheck_verdict).and_return([BLOCK_USER, attribs])
+ allow(service).to receive(:get_akismet_verdict).and_return(DISALLOW)
+ allow(service).to receive(:get_spamcheck_verdict).and_return(BLOCK_USER)
end
it 'renders the more restrictive verdict' do
- expect(subject).to eq BLOCK_USER
+ is_expected.to eq BLOCK_USER
end
end
context 'and one is supported' do
before do
- allow(service).to receive(:akismet_verdict).and_return('nonsense')
- allow(service).to receive(:spamcheck_verdict).and_return([BLOCK_USER, attribs])
+ allow(service).to receive(:get_akismet_verdict).and_return('nonsense')
+ allow(service).to receive(:get_spamcheck_verdict).and_return(BLOCK_USER)
end
it 'renders the more restrictive verdict' do
- expect(subject).to eq BLOCK_USER
+ is_expected.to eq BLOCK_USER
end
end
context 'and none are supported' do
before do
- allow(service).to receive(:akismet_verdict).and_return('nonsense')
- allow(service).to receive(:spamcheck_verdict).and_return(['rubbish', attribs])
- end
-
- it 'renders the more restrictive verdict' do
- expect(subject).to eq ALLOW
- end
- end
-
- context 'and attribs - monitorMode is true' do
- let(:attribs) do
- extra_attributes = { "monitorMode" => "true" }
- extra_attributes
- end
-
- before do
- allow(service).to receive(:akismet_verdict).and_return(DISALLOW)
- allow(service).to receive(:spamcheck_verdict).and_return([BLOCK_USER, attribs])
+ allow(service).to receive(:get_akismet_verdict).and_return('nonsense')
+ allow(service).to receive(:get_spamcheck_verdict).and_return('rubbish')
end
it 'renders the more restrictive verdict' do
- expect(subject).to eq(DISALLOW)
+ is_expected.to eq ALLOW
end
end
end
@@ -122,21 +117,21 @@ RSpec.describe Spam::SpamVerdictService do
context 'and a service returns a verdict that should be overridden' do
before do
- allow(service).to receive(:spamcheck_verdict).and_return([BLOCK_USER, attribs])
+ allow(service).to receive(:get_spamcheck_verdict).and_return(BLOCK_USER)
end
it 'overrides and renders the override verdict' do
- expect(subject).to eq OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM
+ is_expected.to eq OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM
end
end
context 'and a service returns a verdict that does not need to be overridden' do
before do
- allow(service).to receive(:spamcheck_verdict).and_return([ALLOW, attribs])
+ allow(service).to receive(:get_spamcheck_verdict).and_return(ALLOW)
end
it 'does not override and renders the original verdict' do
- expect(subject).to eq ALLOW
+ is_expected.to eq ALLOW
end
end
end
@@ -146,24 +141,23 @@ RSpec.describe Spam::SpamVerdictService do
using RSpec::Parameterized::TableSyntax
- where(:verdict, :error, :label) do
- Spam::SpamConstants::ALLOW | false | 'ALLOW'
- Spam::SpamConstants::ALLOW | true | 'ERROR'
- Spam::SpamConstants::CONDITIONAL_ALLOW | false | 'CONDITIONAL_ALLOW'
- Spam::SpamConstants::BLOCK_USER | false | 'BLOCK'
- Spam::SpamConstants::DISALLOW | false | 'DISALLOW'
- Spam::SpamConstants::NOOP | false | 'NOOP'
+ where(:verdict, :label) do
+ Spam::SpamConstants::ALLOW | 'ALLOW'
+ Spam::SpamConstants::CONDITIONAL_ALLOW | 'CONDITIONAL_ALLOW'
+ Spam::SpamConstants::BLOCK_USER | 'BLOCK'
+ Spam::SpamConstants::DISALLOW | 'DISALLOW'
+ Spam::SpamConstants::NOOP | 'NOOP'
end
with_them do
before do
allow(Gitlab::Metrics).to receive(:histogram).with(:gitlab_spamcheck_request_duration_seconds, anything).and_return(histogram)
- allow(service).to receive(:spamcheck_verdict).and_return([verdict, attribs, error])
+ allow(service).to receive(:get_spamcheck_verdict).and_return(verdict)
end
it 'records duration with labels' do
expect(histogram).to receive(:observe).with(a_hash_including(result: label), anything)
- subject
+ execute
end
end
end
@@ -171,7 +165,8 @@ RSpec.describe Spam::SpamVerdictService do
shared_examples 'akismet verdict' do
let(:target) { issue }
- subject { service.send(:akismet_verdict) }
+
+ subject(:get_akismet_verdict) { service.send(:get_akismet_verdict) }
context 'if Akismet is enabled' do
before do
@@ -190,7 +185,7 @@ RSpec.describe Spam::SpamVerdictService do
end
it 'returns conditionally allow verdict' do
- expect(subject).to eq CONDITIONAL_ALLOW
+ is_expected.to eq CONDITIONAL_ALLOW
end
end
@@ -200,7 +195,7 @@ RSpec.describe Spam::SpamVerdictService do
end
it 'renders disallow verdict' do
- expect(subject).to eq DISALLOW
+ is_expected.to eq DISALLOW
end
end
end
@@ -209,7 +204,7 @@ RSpec.describe Spam::SpamVerdictService do
let(:akismet_result) { false }
it 'renders allow verdict' do
- expect(subject).to eq ALLOW
+ is_expected.to eq ALLOW
end
end
end
@@ -220,13 +215,13 @@ RSpec.describe Spam::SpamVerdictService do
end
it 'renders allow verdict' do
- expect(subject).to eq ALLOW
+ is_expected.to eq ALLOW
end
end
end
shared_examples 'spamcheck verdict' do
- subject { service.send(:spamcheck_verdict) }
+ subject(:get_spamcheck_verdict) { service.send(:get_spamcheck_verdict) }
context 'if a Spam Check endpoint enabled and set to a URL' do
let(:spam_check_body) { {} }
@@ -242,45 +237,50 @@ RSpec.describe Spam::SpamVerdictService do
end
context 'if the endpoint is accessible' do
- let(:error) { '' }
- let(:verdict) { nil }
-
- let(:attribs) do
- extra_attributes = { "monitorMode" => "false" }
- extra_attributes
- end
-
before do
allow(service).to receive(:spamcheck_client).and_return(spam_client)
- allow(spam_client).to receive(:spam?).and_return([verdict, attribs, error])
+ allow(spam_client).to receive(:spam?).and_return(spam_client_result)
end
context 'if the result is a NOOP verdict' do
- let(:verdict) { NOOP }
+ let(:verdict_evaluated) { false }
+ let(:verdict_value) { ::Spamcheck::SpamVerdict::Verdict::NOOP }
it 'returns the verdict' do
- expect(subject).to eq([NOOP, attribs])
+ is_expected.to eq(NOOP)
+ expect(user.spam_score).to eq(0.0)
end
end
- context 'if attribs - monitorMode is true' do
- let(:attribs) do
- extra_attributes = { "monitorMode" => "true" }
- extra_attributes
+ context 'the result is a valid verdict' do
+ let(:verdict_score) { 0.05 }
+ let(:verdict_value) { ::Spamcheck::SpamVerdict::Verdict::ALLOW }
+
+ context 'the result was evaluated' do
+ it 'returns the verdict and updates the spam score' do
+ is_expected.to eq(ALLOW)
+ expect(user.spam_score).to be_within(0.000001).of(verdict_score)
+ end
end
- let(:verdict) { ALLOW }
+ context 'the result was not evaluated' do
+ let(:verdict_evaluated) { false }
- it 'returns the verdict' do
- expect(subject).to eq([ALLOW, attribs])
+ it 'returns the verdict and does not update the spam score' do
+ expect(subject).to eq(ALLOW)
+ expect(user.spam_score).to eq(0.0)
+ end
end
- end
- context 'the result is a valid verdict' do
- let(:verdict) { ALLOW }
+ context 'user spam score feature is disabled' do
+ before do
+ stub_feature_flags(user_spam_scores: false)
+ end
- it 'returns the verdict' do
- expect(subject).to eq([ALLOW, attribs])
+ it 'returns the verdict and does not update the spam score' do
+ expect(subject).to eq(ALLOW)
+ expect(user.spam_score).to eq(0.0)
+ end
end
end
@@ -291,20 +291,17 @@ RSpec.describe Spam::SpamVerdictService do
using RSpec::Parameterized::TableSyntax
- # rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
- where(:verdict_value, :expected) do
- ::Spam::SpamConstants::ALLOW | ::Spam::SpamConstants::ALLOW
- ::Spam::SpamConstants::CONDITIONAL_ALLOW | ::Spam::SpamConstants::CONDITIONAL_ALLOW
- ::Spam::SpamConstants::DISALLOW | ::Spam::SpamConstants::DISALLOW
- ::Spam::SpamConstants::BLOCK_USER | ::Spam::SpamConstants::BLOCK_USER
+ where(:verdict_value, :expected, :verdict_score) do
+ ::Spamcheck::SpamVerdict::Verdict::ALLOW | ::Spam::SpamConstants::ALLOW | 0.1
+ ::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW | ::Spam::SpamConstants::CONDITIONAL_ALLOW | 0.5
+ ::Spamcheck::SpamVerdict::Verdict::DISALLOW | ::Spam::SpamConstants::DISALLOW | 0.8
+ ::Spamcheck::SpamVerdict::Verdict::BLOCK | ::Spam::SpamConstants::BLOCK_USER | 0.9
end
- # rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
with_them do
- let(:verdict) { verdict_value }
-
- it "returns expected spam constant" do
- expect(subject).to eq([expected, attribs])
+ it "returns expected spam constant and updates the spam score" do
+ is_expected.to eq(expected)
+ expect(user.spam_score).to be_within(0.000001).of(verdict_score)
end
end
end
@@ -314,54 +311,23 @@ RSpec.describe Spam::SpamVerdictService do
allow(Gitlab::Recaptcha).to receive(:enabled?).and_return(false)
end
- [::Spam::SpamConstants::ALLOW,
- ::Spam::SpamConstants::CONDITIONAL_ALLOW,
- ::Spam::SpamConstants::DISALLOW,
- ::Spam::SpamConstants::BLOCK_USER].each do |verdict_value|
- let(:verdict) { verdict_value }
- let(:expected) { [verdict_value, attribs] }
-
- it "returns expected spam constant" do
- expect(subject).to eq(expected)
- end
- end
- end
-
- context 'the verdict is an unexpected value' do
- let(:verdict) { :this_is_fine }
-
- it 'returns the string' do
- expect(subject).to eq([verdict, attribs])
- end
- end
-
- context 'the verdict is an empty string' do
- let(:verdict) { '' }
-
- it 'returns nil' do
- expect(subject).to eq([verdict, attribs])
- end
- end
-
- context 'the verdict is nil' do
- let(:verdict) { nil }
+ using RSpec::Parameterized::TableSyntax
- it 'returns nil' do
- expect(subject).to eq([nil, attribs])
+ where(:verdict_value, :expected) do
+ ::Spamcheck::SpamVerdict::Verdict::ALLOW | ::Spam::SpamConstants::ALLOW
+ ::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW | ::Spam::SpamConstants::CONDITIONAL_ALLOW
+ ::Spamcheck::SpamVerdict::Verdict::DISALLOW | ::Spam::SpamConstants::DISALLOW
+ ::Spamcheck::SpamVerdict::Verdict::BLOCK | ::Spam::SpamConstants::BLOCK_USER
end
- end
- context 'there is an error' do
- let(:error) { "Sorry Dave, I can't do that" }
-
- it 'returns nil' do
- expect(subject).to eq([nil, attribs])
+ with_them do
+ it "returns expected spam constant" do
+ is_expected.to eq(expected)
+ end
end
end
context 'the requested is aborted' do
- let(:attribs) { nil }
-
before do
allow(spam_client).to receive(:spam?).and_raise(GRPC::Aborted)
end
@@ -370,22 +336,11 @@ RSpec.describe Spam::SpamVerdictService do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
an_instance_of(GRPC::Aborted), error: ::Spam::SpamConstants::ERROR_TYPE
)
- expect(subject).to eq([ALLOW, attribs, true])
- end
- end
-
- context 'the confused API endpoint returns both an error and a verdict' do
- let(:verdict) { 'disallow' }
- let(:error) { 'oh noes!' }
-
- it 'renders the verdict' do
- expect(subject).to eq [DISALLOW, attribs]
+ is_expected.to be_nil
end
end
context 'if the endpoint times out' do
- let(:attribs) { nil }
-
before do
allow(spam_client).to receive(:spam?).and_raise(GRPC::DeadlineExceeded)
end
@@ -394,7 +349,7 @@ RSpec.describe Spam::SpamVerdictService do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
an_instance_of(GRPC::DeadlineExceeded), error: ::Spam::SpamConstants::ERROR_TYPE
)
- expect(subject).to eq([ALLOW, attribs, true])
+ is_expected.to be_nil
end
end
end
@@ -406,7 +361,7 @@ RSpec.describe Spam::SpamVerdictService do
end
it 'returns nil' do
- expect(subject).to be_nil
+ is_expected.to be_nil
end
end
@@ -416,7 +371,7 @@ RSpec.describe Spam::SpamVerdictService do
end
it 'returns nil' do
- expect(subject).to be_nil
+ is_expected.to be_nil
end
end
end
@@ -435,7 +390,7 @@ RSpec.describe Spam::SpamVerdictService do
end
end
- describe '#akismet_verdict' do
+ describe '#get_akismet_verdict' do
describe 'issue' do
let(:target) { issue }
@@ -449,7 +404,7 @@ RSpec.describe Spam::SpamVerdictService do
end
end
- describe '#spamcheck_verdict' do
+ describe '#get_spamcheck_verdict' do
describe 'issue' do
let(:target) { issue }
diff --git a/spec/services/submodules/update_service_spec.rb b/spec/services/submodules/update_service_spec.rb
index 1a53da7b9fe..aeaf8ec1c7b 100644
--- a/spec/services/submodules/update_service_spec.rb
+++ b/spec/services/submodules/update_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Submodules::UpdateService do
+RSpec.describe Submodules::UpdateService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user, :commit_email) }
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index 41ccd8523fa..6e2c623035e 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Suggestions::ApplyService do
+RSpec.describe Suggestions::ApplyService, feature_category: :code_suggestions do
include ProjectForksHelper
def build_position(**optional_args)
diff --git a/spec/services/suggestions/create_service_spec.rb b/spec/services/suggestions/create_service_spec.rb
index a4e62431128..a8bc3cba697 100644
--- a/spec/services/suggestions/create_service_spec.rb
+++ b/spec/services/suggestions/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Suggestions::CreateService do
+RSpec.describe Suggestions::CreateService, feature_category: :code_suggestions do
let(:project_with_repo) { create(:project, :repository) }
let(:merge_request) do
create(:merge_request, source_project: project_with_repo,
diff --git a/spec/services/suggestions/outdate_service_spec.rb b/spec/services/suggestions/outdate_service_spec.rb
index e8891f88548..7bd70866bf7 100644
--- a/spec/services/suggestions/outdate_service_spec.rb
+++ b/spec/services/suggestions/outdate_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Suggestions::OutdateService do
+RSpec.describe Suggestions::OutdateService, feature_category: :code_suggestions do
describe '#execute' do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.target_project }
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 5d60b6e0487..883a7d3a2ce 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SystemHooksService do
+RSpec.describe SystemHooksService, feature_category: :webhooks do
describe '#execute_hooks_for' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 38b6943b12a..1eb11c80264 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -692,10 +692,10 @@ RSpec.describe SystemNoteService, feature_category: :shared do
it 'calls IssuableService' do
expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
- expect(service).to receive(:change_issue_type)
+ expect(service).to receive(:change_issue_type).with('issue')
end
- described_class.change_issue_type(incident, author)
+ described_class.change_issue_type(incident, author, 'issue')
end
end
diff --git a/spec/services/system_notes/alert_management_service_spec.rb b/spec/services/system_notes/alert_management_service_spec.rb
index 039975c1bf6..4d40a6a6cfd 100644
--- a/spec/services/system_notes/alert_management_service_spec.rb
+++ b/spec/services/system_notes/alert_management_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::AlertManagementService do
+RSpec.describe ::SystemNotes::AlertManagementService, feature_category: :projects do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:noteable) { create(:alert_management_alert, :with_incident, :acknowledged, project: project) }
diff --git a/spec/services/system_notes/base_service_spec.rb b/spec/services/system_notes/base_service_spec.rb
index efb165f8e4c..6ea4751b613 100644
--- a/spec/services/system_notes/base_service_spec.rb
+++ b/spec/services/system_notes/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SystemNotes::BaseService do
+RSpec.describe SystemNotes::BaseService, feature_category: :projects do
let(:noteable) { double }
let(:project) { double }
let(:author) { double }
diff --git a/spec/services/system_notes/commit_service_spec.rb b/spec/services/system_notes/commit_service_spec.rb
index 0399603980d..8dfb83f63fe 100644
--- a/spec/services/system_notes/commit_service_spec.rb
+++ b/spec/services/system_notes/commit_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SystemNotes::CommitService do
+RSpec.describe SystemNotes::CommitService, feature_category: :code_review_workflow do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:author) { create(:user) }
@@ -13,7 +13,7 @@ RSpec.describe SystemNotes::CommitService do
subject { commit_service.add_commits(new_commits, old_commits, oldrev) }
let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
- let(:new_commits) { noteable.commits }
+ let(:new_commits) { create_commits(10) }
let(:old_commits) { [] }
let(:oldrev) { nil }
@@ -43,6 +43,48 @@ RSpec.describe SystemNotes::CommitService do
expect(decoded_note_content).to include("<li>#{commit.short_id} - #{commit.title}</li>")
end
end
+
+ context 'with HTML content' do
+ let(:new_commits) { [double(title: '<pre>This is a test</pre>', short_id: '12345678')] }
+
+ it 'escapes HTML titles' do
+ expect(note_lines[1]).to eq("<ul><li>12345678 - &lt;pre&gt;This is a test&lt;/pre&gt;</li></ul>")
+ end
+ end
+
+ context 'with one commit exceeding the NEW_COMMIT_DISPLAY_LIMIT' do
+ let(:new_commits) { create_commits(11) }
+ let(:earlier_commit_summary_line) { note_lines[1] }
+
+ it 'includes the truncated new commits summary' do
+ expect(earlier_commit_summary_line).to start_with("<ul><li>#{new_commits[0].short_id} - 1 earlier commit")
+ end
+
+ context 'with oldrev' do
+ let(:oldrev) { '12345678abcd' }
+
+ it 'includes the truncated new commits summary with the oldrev' do
+ expect(earlier_commit_summary_line).to start_with("<ul><li>#{new_commits[0].short_id} - 1 earlier commit")
+ end
+ end
+ end
+
+ context 'with multiple commits exceeding the NEW_COMMIT_DISPLAY_LIMIT' do
+ let(:new_commits) { create_commits(13) }
+ let(:earlier_commit_summary_line) { note_lines[1] }
+
+ it 'includes the truncated new commits summary' do
+ expect(earlier_commit_summary_line).to start_with("<ul><li>#{new_commits[0].short_id}..#{new_commits[2].short_id} - 3 earlier commits")
+ end
+
+ context 'with oldrev' do
+ let(:oldrev) { '12345678abcd' }
+
+ it 'includes the truncated new commits summary with the oldrev' do
+ expect(earlier_commit_summary_line).to start_with("<ul><li>12345678...#{new_commits[2].short_id} - 3 earlier commits")
+ end
+ end
+ end
end
describe 'summary line for existing commits' do
@@ -54,6 +96,15 @@ RSpec.describe SystemNotes::CommitService do
it 'includes the existing commit' do
expect(summary_line).to start_with("<ul><li>#{old_commits.first.short_id} - 1 commit from branch <code>feature</code>")
end
+
+ context 'with new commits exceeding the display limit' do
+ let(:summary_line) { note_lines[1] }
+ let(:new_commits) { create_commits(13) }
+
+ it 'includes the existing commit as well as the truncated new commit summary' do
+ expect(summary_line).to start_with("<ul><li>#{old_commits.first.short_id} - 1 commit from branch <code>feature</code></li><li>#{old_commits.last.short_id}...#{new_commits[2].short_id} - 3 earlier commits")
+ end
+ end
end
context 'with multiple existing commits' do
@@ -66,6 +117,15 @@ RSpec.describe SystemNotes::CommitService do
expect(summary_line)
.to start_with("<ul><li>#{Commit.truncate_sha(oldrev)}...#{old_commits.last.short_id} - 26 commits from branch <code>feature</code>")
end
+
+ context 'with new commits exceeding the display limit' do
+ let(:new_commits) { create_commits(13) }
+
+ it 'includes the existing commit as well as the truncated new commit summary' do
+ expect(summary_line)
+ .to start_with("<ul><li>#{Commit.truncate_sha(oldrev)}...#{old_commits.last.short_id} - 26 commits from branch <code>feature</code></li><li>#{old_commits.last.short_id}...#{new_commits[2].short_id} - 3 earlier commits")
+ end
+ end
end
context 'without oldrev' do
@@ -73,6 +133,15 @@ RSpec.describe SystemNotes::CommitService do
expect(summary_line)
.to start_with("<ul><li>#{old_commits[0].short_id}..#{old_commits[-1].short_id} - 26 commits from branch <code>feature</code>")
end
+
+ context 'with new commits exceeding the display limit' do
+ let(:new_commits) { create_commits(13) }
+
+ it 'includes the existing commit as well as the truncated new commit summary' do
+ expect(summary_line)
+ .to start_with("<ul><li>#{old_commits.first.short_id}..#{old_commits.last.short_id} - 26 commits from branch <code>feature</code></li><li>#{old_commits.last.short_id}...#{new_commits[2].short_id} - 3 earlier commits")
+ end
+ end
end
context 'on a fork' do
@@ -106,12 +175,9 @@ RSpec.describe SystemNotes::CommitService do
end
end
- describe '#new_commit_summary' do
- it 'escapes HTML titles' do
- commit = double(title: '<pre>This is a test</pre>', short_id: '12345678')
- escaped = '&lt;pre&gt;This is a test&lt;/pre&gt;'
-
- expect(described_class.new.new_commit_summary([commit])).to all(match(/- #{escaped}/))
+ def create_commits(count)
+ Array.new(count) do |i|
+ double(title: "Test commit #{i}", short_id: "abcd00#{i}")
end
end
end
diff --git a/spec/services/system_notes/design_management_service_spec.rb b/spec/services/system_notes/design_management_service_spec.rb
index 19e1f338eb8..92568890c6f 100644
--- a/spec/services/system_notes/design_management_service_spec.rb
+++ b/spec/services/system_notes/design_management_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SystemNotes::DesignManagementService do
+RSpec.describe SystemNotes::DesignManagementService, feature_category: :design_management do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
diff --git a/spec/services/system_notes/incident_service_spec.rb b/spec/services/system_notes/incident_service_spec.rb
index 5de352ad8fa..0e9828c0a81 100644
--- a/spec/services/system_notes/incident_service_spec.rb
+++ b/spec/services/system_notes/incident_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::IncidentService do
+RSpec.describe ::SystemNotes::IncidentService, feature_category: :incident_management do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:noteable) { create(:incident, project: project) }
diff --git a/spec/services/system_notes/incidents_service_spec.rb b/spec/services/system_notes/incidents_service_spec.rb
index 6439f9fae93..5452d51dfc0 100644
--- a/spec/services/system_notes/incidents_service_spec.rb
+++ b/spec/services/system_notes/incidents_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe SystemNotes::IncidentsService do
+RSpec.describe SystemNotes::IncidentsService, feature_category: :incident_management do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:author) { create(:user) }
diff --git a/spec/services/system_notes/issuables_service_spec.rb b/spec/services/system_notes/issuables_service_spec.rb
index 3263e410d3c..af660a9b72e 100644
--- a/spec/services/system_notes/issuables_service_spec.rb
+++ b/spec/services/system_notes/issuables_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::IssuablesService do
+RSpec.describe ::SystemNotes::IssuablesService, feature_category: :team_planning do
include ProjectForksHelper
let_it_be(:group) { create(:group) }
@@ -861,15 +861,29 @@ RSpec.describe ::SystemNotes::IssuablesService do
end
describe '#change_issue_type' do
- let(:noteable) { create(:incident, project: project) }
+ context 'with issue' do
+ let_it_be_with_reload(:noteable) { create(:issue, project: project) }
- subject { service.change_issue_type }
+ subject { service.change_issue_type('incident') }
- it_behaves_like 'a system note' do
- let(:action) { 'issue_type' }
+ it_behaves_like 'a system note' do
+ let(:action) { 'issue_type' }
+ end
+
+ it { expect(subject.note).to eq "changed type from incident to issue" }
end
- it { expect(subject.note).to eq "changed issue type to incident" }
+ context 'with work item' do
+ let_it_be_with_reload(:noteable) { create(:work_item, project: project) }
+
+ subject { service.change_issue_type('task') }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'issue_type' }
+ end
+
+ it { expect(subject.note).to eq "changed type from task to issue" }
+ end
end
describe '#hierarchy_changed' do
diff --git a/spec/services/system_notes/merge_requests_service_spec.rb b/spec/services/system_notes/merge_requests_service_spec.rb
index 3e66ccef106..7ddcd799a55 100644
--- a/spec/services/system_notes/merge_requests_service_spec.rb
+++ b/spec/services/system_notes/merge_requests_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::MergeRequestsService do
+RSpec.describe ::SystemNotes::MergeRequestsService, feature_category: :code_review_workflow do
include Gitlab::Routing
let_it_be(:group) { create(:group) }
diff --git a/spec/services/system_notes/time_tracking_service_spec.rb b/spec/services/system_notes/time_tracking_service_spec.rb
index c856caa3f3e..71228050085 100644
--- a/spec/services/system_notes/time_tracking_service_spec.rb
+++ b/spec/services/system_notes/time_tracking_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::TimeTrackingService do
+RSpec.describe ::SystemNotes::TimeTrackingService, feature_category: :team_planning do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -37,7 +37,7 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
end
it 'sets the correct note message' do
- expect(note.note).to eq('removed start date and removed due date')
+ expect(note.note).to eq("removed start date #{start_date.to_s(:long)} and removed due date #{due_date.to_s(:long)}")
end
end
@@ -52,7 +52,7 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
let(:changed_dates) { { 'due_date' => [nil, due_date], 'start_date' => [start_date, nil] } }
it 'sets the correct note message' do
- expect(note.note).to eq("removed start date and changed due date to #{due_date.to_s(:long)}")
+ expect(note.note).to eq("removed start date #{start_date.to_s(:long)} and changed due date to #{due_date.to_s(:long)}")
end
end
end
@@ -80,7 +80,7 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
let(:changed_dates) { { 'due_date' => [due_date, nil], 'start_date' => [nil, start_date] } }
it 'sets the correct note message' do
- expect(note.note).to eq("changed start date to #{start_date.to_s(:long)} and removed due date")
+ expect(note.note).to eq("changed start date to #{start_date.to_s(:long)} and removed due date #{due_date.to_s(:long)}")
end
end
end
diff --git a/spec/services/system_notes/zoom_service_spec.rb b/spec/services/system_notes/zoom_service_spec.rb
index 986324c9664..b46b4113e12 100644
--- a/spec/services/system_notes/zoom_service_spec.rb
+++ b/spec/services/system_notes/zoom_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe ::SystemNotes::ZoomService do
+RSpec.describe ::SystemNotes::ZoomService, feature_category: :integrations do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:author) { create(:user) }
diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb
index bbf6fe62959..51b8bace626 100644
--- a/spec/services/tags/create_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Tags::CreateService do
+RSpec.describe Tags::CreateService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user) }
diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb
index 6160f337552..343a87785ad 100644
--- a/spec/services/tags/destroy_service_spec.rb
+++ b/spec/services/tags/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Tags::DestroyService do
+RSpec.describe Tags::DestroyService, feature_category: :source_code_management do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:user) { create(:user) }
diff --git a/spec/services/task_list_toggle_service_spec.rb b/spec/services/task_list_toggle_service_spec.rb
index f889f298213..5d55c1ca8de 100644
--- a/spec/services/task_list_toggle_service_spec.rb
+++ b/spec/services/task_list_toggle_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TaskListToggleService do
+RSpec.describe TaskListToggleService, feature_category: :team_planning do
let(:markdown) do
<<-EOT.strip_heredoc
* [ ] Task 1
@@ -18,6 +18,8 @@ RSpec.describe TaskListToggleService do
with an embedded paragraph
+ [ ] No-break space (U+00A0)
+
+ 2) [ ] Another item
EOT
end
@@ -53,6 +55,11 @@ RSpec.describe TaskListToggleService do
<input type="checkbox" class="task-list-item-checkbox" disabled=""> No-break space (U+00A0)
</li>
</ul>
+ <ol start="2" data-sourcepos="15:1-15:19" class="task-list" dir="auto">
+ <li data-sourcepos="15:1-15:19" class="task-list-item">
+ <input type="checkbox" class="task-list-item-checkbox" disabled> Another item
+ </li>
+ </ol>
EOT
end
@@ -92,10 +99,20 @@ RSpec.describe TaskListToggleService do
line_source: '+ [ ] No-break space (U+00A0)', line_number: 13)
expect(toggler.execute).to be_truthy
- expect(toggler.updated_markdown.lines[12]).to eq "+ [x] No-break space (U+00A0)"
+ expect(toggler.updated_markdown.lines[12]).to eq "+ [x] No-break space (U+00A0)\n"
expect(toggler.updated_markdown_html).to include('disabled checked> No-break space (U+00A0)')
end
+ it 'checks Another item' do
+ toggler = described_class.new(markdown, markdown_html,
+ toggle_as_checked: true,
+ line_source: '2) [ ] Another item', line_number: 15)
+
+ expect(toggler.execute).to be_truthy
+ expect(toggler.updated_markdown.lines[14]).to eq "2) [x] Another item"
+ expect(toggler.updated_markdown_html).to include('disabled checked> Another item')
+ end
+
it 'returns false if line_source does not match the text' do
toggler = described_class.new(markdown, markdown_html,
toggle_as_checked: false,
diff --git a/spec/services/tasks_to_be_done/base_service_spec.rb b/spec/services/tasks_to_be_done/base_service_spec.rb
index cfeff36cc0d..3ca9d140197 100644
--- a/spec/services/tasks_to_be_done/base_service_spec.rb
+++ b/spec/services/tasks_to_be_done/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TasksToBeDone::BaseService do
+RSpec.describe TasksToBeDone::BaseService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
let_it_be(:assignee_one) { create(:user) }
@@ -33,9 +33,9 @@ RSpec.describe TasksToBeDone::BaseService do
add_labels: label.title
}
- expect(Issues::BuildService)
+ expect(Issues::CreateService)
.to receive(:new)
- .with(container: project, current_user: current_user, params: params)
+ .with(container: project, current_user: current_user, params: params, spam_params: nil)
.and_call_original
expect { service.execute }.to change(Issue, :count).by(1)
diff --git a/spec/services/terraform/remote_state_handler_spec.rb b/spec/services/terraform/remote_state_handler_spec.rb
index 369309e4d5a..4590a9ad0e9 100644
--- a/spec/services/terraform/remote_state_handler_spec.rb
+++ b/spec/services/terraform/remote_state_handler_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Terraform::RemoteStateHandler do
+RSpec.describe Terraform::RemoteStateHandler, feature_category: :infrastructure_as_code do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user, developer_projects: [project]) }
let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
@@ -85,6 +85,7 @@ RSpec.describe Terraform::RemoteStateHandler do
end
expect(record.reload.name).to eq 'new-name'
+ expect(record.reload.project).to eq project
end
it 'raises exception if lock has not been acquired before' do
diff --git a/spec/services/terraform/states/destroy_service_spec.rb b/spec/services/terraform/states/destroy_service_spec.rb
index 5acf32cd73c..3515a758827 100644
--- a/spec/services/terraform/states/destroy_service_spec.rb
+++ b/spec/services/terraform/states/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Terraform::States::DestroyService do
+RSpec.describe Terraform::States::DestroyService, feature_category: :infrastructure_as_code do
let_it_be(:state) { create(:terraform_state, :with_version, :deletion_in_progress) }
let(:file) { instance_double(Terraform::StateUploader, relative_path: 'path') }
diff --git a/spec/services/terraform/states/trigger_destroy_service_spec.rb b/spec/services/terraform/states/trigger_destroy_service_spec.rb
index 459f4c3bdb9..0b37d962353 100644
--- a/spec/services/terraform/states/trigger_destroy_service_spec.rb
+++ b/spec/services/terraform/states/trigger_destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Terraform::States::TriggerDestroyService do
+RSpec.describe Terraform::States::TriggerDestroyService, feature_category: :infrastructure_as_code do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, maintainer_projects: [project]) }
diff --git a/spec/services/test_hooks/project_service_spec.rb b/spec/services/test_hooks/project_service_spec.rb
index 13f863dbbdb..31f97edbd08 100644
--- a/spec/services/test_hooks/project_service_spec.rb
+++ b/spec/services/test_hooks/project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TestHooks::ProjectService do
+RSpec.describe TestHooks::ProjectService, feature_category: :code_testing do
include AfterNextHelpers
let(:current_user) { create(:user) }
diff --git a/spec/services/test_hooks/system_service_spec.rb b/spec/services/test_hooks/system_service_spec.rb
index e94ea4669c6..4c5009fea54 100644
--- a/spec/services/test_hooks/system_service_spec.rb
+++ b/spec/services/test_hooks/system_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TestHooks::SystemService do
+RSpec.describe TestHooks::SystemService, feature_category: :code_testing do
include AfterNextHelpers
describe '#execute' do
diff --git a/spec/services/timelogs/delete_service_spec.rb b/spec/services/timelogs/delete_service_spec.rb
index ee1133af6b3..c0543bafcec 100644
--- a/spec/services/timelogs/delete_service_spec.rb
+++ b/spec/services/timelogs/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Timelogs::DeleteService do
+RSpec.describe Timelogs::DeleteService, feature_category: :team_planning do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project) }
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index f73eae70d3c..1ec6a3250fc 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TodoService do
+RSpec.describe TodoService, feature_category: :team_planning do
include AfterNextHelpers
let_it_be(:project) { create(:project, :repository) }
@@ -211,7 +211,6 @@ RSpec.describe TodoService do
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:namespace) { project.namespace }
let(:category) { described_class.to_s }
let(:action) { 'incident_management_incident_todo' }
@@ -395,6 +394,39 @@ RSpec.describe TodoService do
end
end
+ describe '#resolve_todos_with_attributes_for_target' do
+ it 'marks related pending todos to the target for all the users as done' do
+ first_todo = create(:todo, :assigned, user: member, project: project, target: issue, author: author)
+ second_todo = create(:todo, :review_requested, user: john_doe, project: project, target: issue, author: author)
+ another_todo = create(:todo, :assigned, user: john_doe, project: project, target: project, author: author)
+
+ service.resolve_todos_with_attributes_for_target(issue, {})
+
+ expect(first_todo.reload).to be_done
+ expect(second_todo.reload).to be_done
+ expect(another_todo.reload).to be_pending
+ end
+
+ it 'marks related only filtered pending todos to the target for all the users as done' do
+ first_todo = create(:todo, :assigned, user: member, project: project, target: issue, author: author)
+ second_todo = create(:todo, :review_requested, user: john_doe, project: project, target: issue, author: author)
+ another_todo = create(:todo, :assigned, user: john_doe, project: project, target: project, author: author)
+
+ service.resolve_todos_with_attributes_for_target(issue, { action: Todo::ASSIGNED })
+
+ expect(first_todo.reload).to be_done
+ expect(second_todo.reload).to be_pending
+ expect(another_todo.reload).to be_pending
+ end
+
+ it 'fetches the pending todos with users preloaded' do
+ expect(PendingTodosFinder).to receive(:new)
+ .with(a_hash_including(preload_user_association: true)).and_call_original
+
+ service.resolve_todos_with_attributes_for_target(issue, { action: Todo::ASSIGNED })
+ end
+ end
+
describe '#new_note' do
let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) }
@@ -1225,20 +1257,64 @@ RSpec.describe TodoService do
end
describe '#resolve_access_request_todos' do
- let_it_be(:source) { create(:group, :public) }
- let_it_be(:requester) { create(:group_member, :access_request, group: source, user: assignee) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:group_requester) { create(:group_member, :access_request, group: group, user: assignee) }
+ let_it_be(:project_requester) { create(:project_member, :access_request, project: project, user: non_member) }
+ let_it_be(:another_pending_todo) { create(:todo, state: :pending, user: john_doe) }
+ # access request by another user
+ let_it_be(:another_group_todo) do
+ create(:todo, state: :pending, target: group, action: Todo::MEMBER_ACCESS_REQUESTED)
+ end
- it 'marks the todos for request handler as done' do
- request_handler_todo = create(:todo,
- user: member,
- state: :pending,
- action: Todo::MEMBER_ACCESS_REQUESTED,
- author: requester.user,
- target: source)
+ let_it_be(:another_project_todo) do
+ create(:todo, state: :pending, target: project, action: Todo::MEMBER_ACCESS_REQUESTED)
+ end
- service.resolve_access_request_todos(member, requester)
+ it 'marks the todos for group access request handlers as done' do
+ access_request_todos = [member, john_doe].map do |group_user|
+ create(:todo,
+ user: group_user,
+ state: :pending,
+ action: Todo::MEMBER_ACCESS_REQUESTED,
+ author: group_requester.user,
+ target: group
+ )
+ end
- expect(request_handler_todo.reload).to be_done
+ expect do
+ service.resolve_access_request_todos(group_requester)
+ end.to change {
+ Todo.pending.where(target: group).for_author(group_requester.user)
+ .for_action(Todo::MEMBER_ACCESS_REQUESTED).count
+ }.from(2).to(0)
+
+ expect(access_request_todos.each(&:reload)).to all be_done
+ expect(another_pending_todo.reload).not_to be_done
+ expect(another_group_todo.reload).not_to be_done
+ end
+
+ it 'marks the todos for project access request handlers as done' do
+ # The project has 1 owner already. Adding another owner here
+ project.add_member(john_doe, Gitlab::Access::OWNER)
+
+ access_request_todo = create(:todo,
+ user: john_doe,
+ state: :pending,
+ action: Todo::MEMBER_ACCESS_REQUESTED,
+ author: project_requester.user,
+ target: project
+ )
+
+ expect do
+ service.resolve_access_request_todos(project_requester)
+ end.to change {
+ Todo.pending.where(target: project).for_author(project_requester.user)
+ .for_action(Todo::MEMBER_ACCESS_REQUESTED).count
+ }.from(2).to(0) # The original owner todo was created with the pending access request
+
+ expect(access_request_todo.reload).to be_done
+ expect(another_pending_todo.reload).to be_pending
+ expect(another_project_todo.reload).to be_pending
end
end
diff --git a/spec/services/todos/allowed_target_filter_service_spec.rb b/spec/services/todos/allowed_target_filter_service_spec.rb
index 1d2b1b044db..3929e3788d0 100644
--- a/spec/services/todos/allowed_target_filter_service_spec.rb
+++ b/spec/services/todos/allowed_target_filter_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::AllowedTargetFilterService do
+RSpec.describe Todos::AllowedTargetFilterService, feature_category: :team_planning do
include DesignManagementTestHelpers
let_it_be(:authorized_group) { create(:group, :private) }
diff --git a/spec/services/todos/destroy/confidential_issue_service_spec.rb b/spec/services/todos/destroy/confidential_issue_service_spec.rb
index e3dcc2bae95..9de71faf8bf 100644
--- a/spec/services/todos/destroy/confidential_issue_service_spec.rb
+++ b/spec/services/todos/destroy/confidential_issue_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::Destroy::ConfidentialIssueService do
+RSpec.describe Todos::Destroy::ConfidentialIssueService, feature_category: :team_planning do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
let(:author) { create(:user) }
diff --git a/spec/services/todos/destroy/design_service_spec.rb b/spec/services/todos/destroy/design_service_spec.rb
index 92b25d94dc6..628398e7062 100644
--- a/spec/services/todos/destroy/design_service_spec.rb
+++ b/spec/services/todos/destroy/design_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::Destroy::DesignService do
+RSpec.describe Todos::Destroy::DesignService, feature_category: :design_management do
let_it_be(:user) { create(:user) }
let_it_be(:user_2) { create(:user) }
let_it_be(:design) { create(:design) }
diff --git a/spec/services/todos/destroy/destroyed_issuable_service_spec.rb b/spec/services/todos/destroy/destroyed_issuable_service_spec.rb
index 6d6abe06d1c..63ff189ede5 100644
--- a/spec/services/todos/destroy/destroyed_issuable_service_spec.rb
+++ b/spec/services/todos/destroy/destroyed_issuable_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::Destroy::DestroyedIssuableService do
+RSpec.describe Todos::Destroy::DestroyedIssuableService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/todos/destroy/project_private_service_spec.rb b/spec/services/todos/destroy/project_private_service_spec.rb
index 1d1c010535d..cc15f6eab8b 100644
--- a/spec/services/todos/destroy/project_private_service_spec.rb
+++ b/spec/services/todos/destroy/project_private_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::Destroy::ProjectPrivateService do
+RSpec.describe Todos::Destroy::ProjectPrivateService, feature_category: :team_planning do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, group: group) }
let(:user) { create(:user) }
diff --git a/spec/services/todos/destroy/unauthorized_features_service_spec.rb b/spec/services/todos/destroy/unauthorized_features_service_spec.rb
index 5f6c9b0cdf0..c02c0dfd5c8 100644
--- a/spec/services/todos/destroy/unauthorized_features_service_spec.rb
+++ b/spec/services/todos/destroy/unauthorized_features_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Todos::Destroy::UnauthorizedFeaturesService do
+RSpec.describe Todos::Destroy::UnauthorizedFeaturesService, feature_category: :team_planning do
let_it_be(:project, reload: true) { create(:project, :public, :repository) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:mr) { create(:merge_request, source_project: project) }
diff --git a/spec/services/topics/merge_service_spec.rb b/spec/services/topics/merge_service_spec.rb
index 98247250a61..705b222b39e 100644
--- a/spec/services/topics/merge_service_spec.rb
+++ b/spec/services/topics/merge_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Topics::MergeService do
+RSpec.describe Topics::MergeService, feature_category: :shared do
let_it_be(:source_topic) { create(:topic, name: 'source_topic') }
let_it_be(:target_topic) { create(:topic, name: 'target_topic') }
let_it_be(:project_1) { create(:project, :public, topic_list: source_topic.name) }
@@ -47,7 +47,7 @@ RSpec.describe Topics::MergeService do
where(:source_topic_parameter, :target_topic_parameter, :expected_message) do
nil | ref(:target_topic) | 'The source topic is not a topic.'
ref(:source_topic) | nil | 'The target topic is not a topic.'
- ref(:target_topic) | ref(:target_topic) | 'The source topic and the target topic are identical.' # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ ref(:target_topic) | ref(:target_topic) | 'The source topic and the target topic are identical.'
end
with_them do
diff --git a/spec/services/two_factor/destroy_service_spec.rb b/spec/services/two_factor/destroy_service_spec.rb
index 30c189520fd..0811ce336c8 100644
--- a/spec/services/two_factor/destroy_service_spec.rb
+++ b/spec/services/two_factor/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe TwoFactor::DestroyService do
+RSpec.describe TwoFactor::DestroyService, feature_category: :system_access do
let_it_be(:current_user) { create(:user) }
subject { described_class.new(current_user, user: user).execute }
diff --git a/spec/services/update_container_registry_info_service_spec.rb b/spec/services/update_container_registry_info_service_spec.rb
index 64071e79508..416b08bd04b 100644
--- a/spec/services/update_container_registry_info_service_spec.rb
+++ b/spec/services/update_container_registry_info_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe UpdateContainerRegistryInfoService do
+RSpec.describe UpdateContainerRegistryInfoService, feature_category: :container_registry do
let_it_be(:application_settings) { Gitlab::CurrentSettings }
let_it_be(:api_url) { 'http://registry.gitlab' }
diff --git a/spec/services/update_merge_request_metrics_service_spec.rb b/spec/services/update_merge_request_metrics_service_spec.rb
index a07fcee91e4..f30836fbaf5 100644
--- a/spec/services/update_merge_request_metrics_service_spec.rb
+++ b/spec/services/update_merge_request_metrics_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequestMetricsService do
+RSpec.describe MergeRequestMetricsService, feature_category: :code_review_workflow do
let(:metrics) { create(:merge_request).metrics }
describe '#merge' do
diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb
index 48aa65451f3..518d12d5b41 100644
--- a/spec/services/upload_service_spec.rb
+++ b/spec/services/upload_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe UploadService do
+RSpec.describe UploadService, feature_category: :shared do
describe 'File service' do
before do
@user = create(:user)
diff --git a/spec/services/uploads/destroy_service_spec.rb b/spec/services/uploads/destroy_service_spec.rb
index bb58da231b6..76ac2ec245e 100644
--- a/spec/services/uploads/destroy_service_spec.rb
+++ b/spec/services/uploads/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Uploads::DestroyService do
+RSpec.describe Uploads::DestroyService, feature_category: :shared do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:upload) { create(:upload, :issuable_upload, model: project) }
diff --git a/spec/services/user_preferences/update_service_spec.rb b/spec/services/user_preferences/update_service_spec.rb
index 59089a4a7af..601a023c30d 100644
--- a/spec/services/user_preferences/update_service_spec.rb
+++ b/spec/services/user_preferences/update_service_spec.rb
@@ -2,9 +2,9 @@
require 'spec_helper'
-RSpec.describe UserPreferences::UpdateService do
+RSpec.describe UserPreferences::UpdateService, feature_category: :user_profile do
let(:user) { create(:user) }
- let(:params) { { view_diffs_file_by_file: false } }
+ let(:params) { { view_diffs_file_by_file: false, pass_user_identities_to_ci_jwt: true } }
describe '#execute' do
subject(:service) { described_class.new(user, params) }
@@ -15,6 +15,8 @@ RSpec.describe UserPreferences::UpdateService do
expect(result.status).to eq(:success)
expect(result.payload[:preferences].view_diffs_file_by_file).to eq(params[:view_diffs_file_by_file])
+ expect(result.payload[:preferences].pass_user_identities_to_ci_jwt
+ ).to eq(params[:pass_user_identities_to_ci_jwt])
end
end
diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb
index 356675d55f2..563af8e7e9e 100644
--- a/spec/services/user_project_access_changed_service_spec.rb
+++ b/spec/services/user_project_access_changed_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe UserProjectAccessChangedService, feature_category: :authentication_and_authorization do
+RSpec.describe UserProjectAccessChangedService, feature_category: :system_access do
describe '#execute' do
it 'permits high-priority operation' do
expect(AuthorizedProjectsWorker).to receive(:bulk_perform_async)
diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb
index 6c0d93f568a..e2141f9bf6f 100644
--- a/spec/services/users/activity_service_spec.rb
+++ b/spec/services/users/activity_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::ActivityService do
+RSpec.describe Users::ActivityService, feature_category: :user_profile do
include ExclusiveLeaseHelpers
let(:user) { create(:user, last_activity_on: last_activity_on) }
@@ -57,7 +57,6 @@ RSpec.describe Users::ActivityService do
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
subject(:record_activity) { described_class.new(author: user, namespace: namespace, project: project).execute }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase3 }
let(:category) { described_class.name }
let(:action) { 'perform_action' }
let(:label) { 'redis_hll_counters.manage.unique_active_users_monthly' }
diff --git a/spec/services/users/approve_service_spec.rb b/spec/services/users/approve_service_spec.rb
index 34eb5b18ff6..09379857c38 100644
--- a/spec/services/users/approve_service_spec.rb
+++ b/spec/services/users/approve_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::ApproveService do
+RSpec.describe Users::ApproveService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
let(:user) { create(:user, :blocked_pending_approval) }
@@ -75,6 +75,24 @@ RSpec.describe Users::ApproveService do
expect { subject }.to have_enqueued_mail(DeviseMailer, :user_admin_approval)
end
+ context 'when the user was created via sign up' do
+ it 'does not send a password reset email' do
+ expect { subject }.not_to have_enqueued_mail(Notify, :new_user_email)
+ end
+ end
+
+ context 'when the user was created by an admin' do
+ let(:user) { create(:user, :blocked_pending_approval, created_by_id: current_user.id) }
+
+ it 'sends a password reset email' do
+ allow(user).to receive(:generate_reset_token).and_return(:reset_token)
+
+ expect(Notify).to receive(:new_user_email).with(user.id, :reset_token).and_call_original
+
+ expect { subject }.to have_enqueued_mail(Notify, :new_user_email)
+ end
+ end
+
context 'email confirmation status' do
context 'user is unconfirmed' do
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
diff --git a/spec/services/users/authorized_build_service_spec.rb b/spec/services/users/authorized_build_service_spec.rb
index 57a122cbf35..7eed6833cba 100644
--- a/spec/services/users/authorized_build_service_spec.rb
+++ b/spec/services/users/authorized_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::AuthorizedBuildService do
+RSpec.describe Users::AuthorizedBuildService, feature_category: :user_management do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
diff --git a/spec/services/users/ban_service_spec.rb b/spec/services/users/ban_service_spec.rb
index 3f9c7ebf067..5be5de82e91 100644
--- a/spec/services/users/ban_service_spec.rb
+++ b/spec/services/users/ban_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::BanService do
+RSpec.describe Users::BanService, feature_category: :user_management do
let(:user) { create(:user) }
let_it_be(:current_user) { create(:admin) }
diff --git a/spec/services/users/banned_user_base_service_spec.rb b/spec/services/users/banned_user_base_service_spec.rb
index 29a549f0f49..65b24e08d80 100644
--- a/spec/services/users/banned_user_base_service_spec.rb
+++ b/spec/services/users/banned_user_base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::BannedUserBaseService do
+RSpec.describe Users::BannedUserBaseService, feature_category: :user_management do
let(:admin) { create(:admin) }
let(:base_service) { described_class.new(admin) }
diff --git a/spec/services/users/batch_status_cleaner_service_spec.rb b/spec/services/users/batch_status_cleaner_service_spec.rb
index 46a004542d8..8feec761fd0 100644
--- a/spec/services/users/batch_status_cleaner_service_spec.rb
+++ b/spec/services/users/batch_status_cleaner_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::BatchStatusCleanerService do
+RSpec.describe Users::BatchStatusCleanerService, feature_category: :user_management do
let_it_be(:user_status_1) { create(:user_status, emoji: 'coffee', message: 'msg1', clear_status_at: 1.year.ago) }
let_it_be(:user_status_2) { create(:user_status, emoji: 'coffee', message: 'msg1', clear_status_at: 1.year.from_now) }
let_it_be(:user_status_3) { create(:user_status, emoji: 'coffee', message: 'msg1', clear_status_at: 2.years.ago) }
diff --git a/spec/services/users/block_service_spec.rb b/spec/services/users/block_service_spec.rb
index 7ff9a887f38..63aa375c8af 100644
--- a/spec/services/users/block_service_spec.rb
+++ b/spec/services/users/block_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::BlockService do
+RSpec.describe Users::BlockService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb
index 98fe6d9b5ba..f3236d40412 100644
--- a/spec/services/users/build_service_spec.rb
+++ b/spec/services/users/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::BuildService do
+RSpec.describe Users::BuildService, feature_category: :user_management do
using RSpec::Parameterized::TableSyntax
describe '#execute' do
diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb
index f3c9701c556..eac4faa2042 100644
--- a/spec/services/users/create_service_spec.rb
+++ b/spec/services/users/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::CreateService do
+RSpec.describe Users::CreateService, feature_category: :user_management do
describe '#execute' do
let(:password) { User.random_password }
let(:admin_user) { create(:admin) }
diff --git a/spec/services/users/deactivate_service_spec.rb b/spec/services/users/deactivate_service_spec.rb
new file mode 100644
index 00000000000..0bb6e51a3b1
--- /dev/null
+++ b/spec/services/users/deactivate_service_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::DeactivateService, feature_category: :user_management do
+ let_it_be(:current_user) { build(:admin) }
+ let_it_be(:user) { build(:user) }
+
+ subject(:service) { described_class.new(current_user) }
+
+ describe '#execute' do
+ subject(:operation) { service.execute(user) }
+
+ context 'when successful', :enable_admin_mode do
+ let(:user) { create(:user) }
+
+ it 'returns success status' do
+ expect(operation[:status]).to eq(:success)
+ end
+
+ it "changes the user's state" do
+ expect { operation }.to change { user.state }.to('deactivated')
+ end
+
+ it 'creates a log entry' do
+ expect(Gitlab::AppLogger).to receive(:info).with(message: "User deactivated", user: user.username,
+ email: user.email, deactivated_by: current_user.username, ip_address: current_user.current_sign_in_ip.to_s)
+
+ operation
+ end
+ end
+
+ context 'when the user is already deactivated', :enable_admin_mode do
+ let(:user) { create(:user, :deactivated) }
+
+ it 'returns error result' do
+ aggregate_failures 'error result' do
+ expect(operation[:status]).to eq(:success)
+ expect(operation[:message]).to eq('User has already been deactivated')
+ end
+ end
+
+ it "does not change the user's state" do
+ expect { operation }.not_to change { user.state }
+ end
+ end
+
+ context 'when internal user', :enable_admin_mode do
+ let(:user) { create(:user, :bot) }
+
+ it 'returns an error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq('Internal users cannot be deactivated')
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when user is blocked', :enable_admin_mode do
+ let(:user) { create(:user, :blocked) }
+
+ it 'returns an error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq('Error occurred. A blocked user cannot be deactivated')
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when user is not an admin' do
+ it 'returns permissions error message' do
+ expect(operation[:status]).to eq(:error)
+ expect(operation[:message]).to eq("You are not authorized to perform this action")
+ expect(operation.reason).to eq :forbidden
+ end
+ end
+
+ context 'when skip_authorization is true' do
+ let(:non_admin_user) { create(:user) }
+ let(:user_to_deactivate) { create(:user) }
+ let(:skip_authorization_service) { described_class.new(non_admin_user, skip_authorization: true) }
+
+ it 'deactivates the user even if the current user is not an admin' do
+ expect(skip_authorization_service.execute(user_to_deactivate)[:status]).to eq(:success)
+ end
+ end
+ end
+end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 18ad946b289..5cd11efe942 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::DestroyService do
+RSpec.describe Users::DestroyService, feature_category: :user_management do
let!(:user) { create(:user) }
let!(:admin) { create(:admin) }
let!(:namespace) { user.namespace }
diff --git a/spec/services/users/dismiss_callout_service_spec.rb b/spec/services/users/dismiss_callout_service_spec.rb
index 6ba9f180444..776388ef5f1 100644
--- a/spec/services/users/dismiss_callout_service_spec.rb
+++ b/spec/services/users/dismiss_callout_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::DismissCalloutService do
+RSpec.describe Users::DismissCalloutService, feature_category: :user_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
diff --git a/spec/services/users/dismiss_group_callout_service_spec.rb b/spec/services/users/dismiss_group_callout_service_spec.rb
index d74602a7606..a653fa7ee00 100644
--- a/spec/services/users/dismiss_group_callout_service_spec.rb
+++ b/spec/services/users/dismiss_group_callout_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::DismissGroupCalloutService do
+RSpec.describe Users::DismissGroupCalloutService, feature_category: :user_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
diff --git a/spec/services/users/dismiss_project_callout_service_spec.rb b/spec/services/users/dismiss_project_callout_service_spec.rb
index 73e50a4c37d..7bcb11e4dbc 100644
--- a/spec/services/users/dismiss_project_callout_service_spec.rb
+++ b/spec/services/users/dismiss_project_callout_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::DismissProjectCalloutService do
+RSpec.describe Users::DismissProjectCalloutService, feature_category: :user_management do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/services/users/email_verification/generate_token_service_spec.rb b/spec/services/users/email_verification/generate_token_service_spec.rb
index e7aa1bf8306..8b2b57cbf62 100644
--- a/spec/services/users/email_verification/generate_token_service_spec.rb
+++ b/spec/services/users/email_verification/generate_token_service_spec.rb
@@ -2,12 +2,13 @@
require 'spec_helper'
-RSpec.describe Users::EmailVerification::GenerateTokenService do
+RSpec.describe Users::EmailVerification::GenerateTokenService, feature_category: :system_access do
using RSpec::Parameterized::TableSyntax
- let(:service) { described_class.new(attr: attr) }
+ let(:user) { build_stubbed(:user) }
+ let(:service) { described_class.new(attr: attr, user: user) }
let(:token) { 'token' }
- let(:digest) { Devise.token_generator.digest(User, attr, token) }
+ let(:digest) { service.send(:digest) }
describe '#execute' do
context 'with a valid attribute' do
@@ -33,5 +34,21 @@ RSpec.describe Users::EmailVerification::GenerateTokenService do
expect { service.execute }.to raise_error(ArgumentError, 'Invalid attribute')
end
end
+
+ context 'when similar tokens are generated' do
+ let(:attr) { :confirmation_token }
+
+ before do
+ allow_next_instance_of(described_class) do |service|
+ allow(service).to receive(:generate_token).and_return(token)
+ end
+ end
+
+ it 'generates a unique digest' do
+ second_service = described_class.new(attr: attr, user: build_stubbed(:user))
+
+ expect(service.execute[1]).not_to eq(second_service.execute[1])
+ end
+ end
end
end
diff --git a/spec/services/users/email_verification/validate_token_service_spec.rb b/spec/services/users/email_verification/validate_token_service_spec.rb
index 44af4a4d36f..c8924bc20b7 100644
--- a/spec/services/users/email_verification/validate_token_service_spec.rb
+++ b/spec/services/users/email_verification/validate_token_service_spec.rb
@@ -2,15 +2,16 @@
require 'spec_helper'
-RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_redis_rate_limiting do
+RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_redis_rate_limiting, feature_category: :system_access do
using RSpec::Parameterized::TableSyntax
let(:service) { described_class.new(attr: attr, user: user, token: token) }
+ let(:email) { build_stubbed(:user).email }
let(:token) { 'token' }
- let(:encrypted_token) { Devise.token_generator.digest(User, attr, token) }
+ let(:encrypted_token) { Devise.token_generator.digest(User, email, token) }
let(:generated_at_attr) { attr == :unlock_token ? :locked_at : :confirmation_sent_at }
let(:token_generated_at) { 1.minute.ago }
- let(:user) { build(:user, attr => encrypted_token, generated_at_attr => token_generated_at) }
+ let(:user) { build(:user, email: email, attr => encrypted_token, generated_at_attr => token_generated_at) }
describe '#execute' do
context 'with a valid attribute' do
diff --git a/spec/services/users/in_product_marketing_email_records_spec.rb b/spec/services/users/in_product_marketing_email_records_spec.rb
index 0b9400dcd12..059f0890b53 100644
--- a/spec/services/users/in_product_marketing_email_records_spec.rb
+++ b/spec/services/users/in_product_marketing_email_records_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::InProductMarketingEmailRecords do
+RSpec.describe Users::InProductMarketingEmailRecords, feature_category: :onboarding do
let_it_be(:user) { create :user }
subject(:records) { described_class.new }
diff --git a/spec/services/users/keys_count_service_spec.rb b/spec/services/users/keys_count_service_spec.rb
index 607d2946b2c..258fe351e4b 100644
--- a/spec/services/users/keys_count_service_spec.rb
+++ b/spec/services/users/keys_count_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::KeysCountService, :use_clean_rails_memory_store_caching do
+RSpec.describe Users::KeysCountService, :use_clean_rails_memory_store_caching, feature_category: :system_access do
let(:user) { create(:user) }
subject { described_class.new(user) }
diff --git a/spec/services/users/last_push_event_service_spec.rb b/spec/services/users/last_push_event_service_spec.rb
index 5b755db407f..fe61f12fe1a 100644
--- a/spec/services/users/last_push_event_service_spec.rb
+++ b/spec/services/users/last_push_event_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::LastPushEventService do
+RSpec.describe Users::LastPushEventService, feature_category: :source_code_management do
let(:user) { build(:user, id: 1) }
let(:project) { build(:project, id: 2) }
let(:event) { build(:push_event, id: 3, author: user, project: project) }
diff --git a/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb b/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb
index 107ff82016c..0b9f92a868e 100644
--- a/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb
+++ b/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::MigrateRecordsToGhostUserInBatchesService do
+RSpec.describe Users::MigrateRecordsToGhostUserInBatchesService, feature_category: :user_management do
let(:service) { described_class.new }
let_it_be(:ghost_user_migration) { create(:ghost_user_migration) }
diff --git a/spec/services/users/migrate_records_to_ghost_user_service_spec.rb b/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
index 827d6f652a4..cfa0ddff04d 100644
--- a/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
+++ b/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::MigrateRecordsToGhostUserService do
+RSpec.describe Users::MigrateRecordsToGhostUserService, feature_category: :user_management do
include BatchDestroyDependentAssociationsHelper
let!(:user) { create(:user) }
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index e33886d2add..55b27954a74 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::RefreshAuthorizedProjectsService do
+RSpec.describe Users::RefreshAuthorizedProjectsService, feature_category: :user_management do
include ExclusiveLeaseHelpers
# We're using let! here so that any expectations for the service class are not
diff --git a/spec/services/users/registrations_build_service_spec.rb b/spec/services/users/registrations_build_service_spec.rb
index fa53a4cc604..736db855fe0 100644
--- a/spec/services/users/registrations_build_service_spec.rb
+++ b/spec/services/users/registrations_build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::RegistrationsBuildService do
+RSpec.describe Users::RegistrationsBuildService, feature_category: :system_access do
describe '#execute' do
let(:base_params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) }
let(:skip_param) { {} }
diff --git a/spec/services/users/reject_service_spec.rb b/spec/services/users/reject_service_spec.rb
index 37d003c5dac..f72666d8a63 100644
--- a/spec/services/users/reject_service_spec.rb
+++ b/spec/services/users/reject_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::RejectService do
+RSpec.describe Users::RejectService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
let(:user) { create(:user, :blocked_pending_approval) }
diff --git a/spec/services/users/repair_ldap_blocked_service_spec.rb b/spec/services/users/repair_ldap_blocked_service_spec.rb
index 54540d68af2..424c14ccdbc 100644
--- a/spec/services/users/repair_ldap_blocked_service_spec.rb
+++ b/spec/services/users/repair_ldap_blocked_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::RepairLdapBlockedService do
+RSpec.describe Users::RepairLdapBlockedService, feature_category: :system_access do
let(:user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
let(:identity) { user.ldap_identity }
diff --git a/spec/services/users/respond_to_terms_service_spec.rb b/spec/services/users/respond_to_terms_service_spec.rb
index 1997dcd0e04..dc33f98535a 100644
--- a/spec/services/users/respond_to_terms_service_spec.rb
+++ b/spec/services/users/respond_to_terms_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::RespondToTermsService do
+RSpec.describe Users::RespondToTermsService, feature_category: :user_profile do
let(:user) { create(:user) }
let(:term) { create(:term) }
diff --git a/spec/services/users/saved_replies/create_service_spec.rb b/spec/services/users/saved_replies/create_service_spec.rb
index e01b6248308..ee42a53a220 100644
--- a/spec/services/users/saved_replies/create_service_spec.rb
+++ b/spec/services/users/saved_replies/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::SavedReplies::CreateService do
+RSpec.describe Users::SavedReplies::CreateService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
let_it_be(:saved_reply) { create(:saved_reply, user: current_user) }
diff --git a/spec/services/users/saved_replies/destroy_service_spec.rb b/spec/services/users/saved_replies/destroy_service_spec.rb
index cb97fac7b7c..41c2013e3df 100644
--- a/spec/services/users/saved_replies/destroy_service_spec.rb
+++ b/spec/services/users/saved_replies/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::SavedReplies::DestroyService do
+RSpec.describe Users::SavedReplies::DestroyService, feature_category: :team_planning do
describe '#execute' do
let!(:saved_reply) { create(:saved_reply) }
diff --git a/spec/services/users/saved_replies/update_service_spec.rb b/spec/services/users/saved_replies/update_service_spec.rb
index bdb54d7c8f7..c18b7395040 100644
--- a/spec/services/users/saved_replies/update_service_spec.rb
+++ b/spec/services/users/saved_replies/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::SavedReplies::UpdateService do
+RSpec.describe Users::SavedReplies::UpdateService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
let_it_be(:saved_reply) { create(:saved_reply, user: current_user) }
diff --git a/spec/services/users/set_status_service_spec.rb b/spec/services/users/set_status_service_spec.rb
index 76e86506d94..b75c558785f 100644
--- a/spec/services/users/set_status_service_spec.rb
+++ b/spec/services/users/set_status_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::SetStatusService do
+RSpec.describe Users::SetStatusService, feature_category: :user_management do
let(:current_user) { create(:user) }
subject(:service) { described_class.new(current_user, params) }
diff --git a/spec/services/users/signup_service_spec.rb b/spec/services/users/signup_service_spec.rb
index ef532e01d0b..29663411346 100644
--- a/spec/services/users/signup_service_spec.rb
+++ b/spec/services/users/signup_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::SignupService do
+RSpec.describe Users::SignupService, feature_category: :system_access do
let(:user) { create(:user, setup_for_company: true) }
describe '#execute' do
@@ -48,11 +48,7 @@ RSpec.describe Users::SignupService do
expect(user.reload.setup_for_company).to be(false)
end
- context 'when on .com' do
- before do
- allow(Gitlab).to receive(:com?).and_return(true)
- end
-
+ context 'when on SaaS', :saas do
it 'returns an error result when setup_for_company is missing' do
result = update_user(user, setup_for_company: '')
diff --git a/spec/services/users/unban_service_spec.rb b/spec/services/users/unban_service_spec.rb
index 3dcb8450e7b..20fe40b370f 100644
--- a/spec/services/users/unban_service_spec.rb
+++ b/spec/services/users/unban_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::UnbanService do
+RSpec.describe Users::UnbanService, feature_category: :user_management do
let(:user) { create(:user) }
let_it_be(:current_user) { create(:admin) }
diff --git a/spec/services/users/unblock_service_spec.rb b/spec/services/users/unblock_service_spec.rb
index 25ee99427ab..95a077d6100 100644
--- a/spec/services/users/unblock_service_spec.rb
+++ b/spec/services/users/unblock_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::UnblockService do
+RSpec.describe Users::UnblockService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
diff --git a/spec/services/users/update_canonical_email_service_spec.rb b/spec/services/users/update_canonical_email_service_spec.rb
index 1dead13d338..d3c414f6db4 100644
--- a/spec/services/users/update_canonical_email_service_spec.rb
+++ b/spec/services/users/update_canonical_email_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::UpdateCanonicalEmailService do
+RSpec.describe Users::UpdateCanonicalEmailService, feature_category: :user_profile do
let(:other_email) { "differentaddress@includeddomain.com" }
before do
@@ -92,23 +92,25 @@ RSpec.describe Users::UpdateCanonicalEmailService do
context 'when the user email is not processable' do
[nil, 'nonsense'].each do |invalid_address|
- before do
- user.email = invalid_address
- end
+ context "with #{invalid_address}" do
+ before do
+ user.email = invalid_address
+ end
- specify do
- subject.execute
+ specify do
+ subject.execute
- expect(user.user_canonical_email).to be_nil
- end
+ expect(user.user_canonical_email).to be_nil
+ end
- it 'preserves any existing record' do
- user.email = nil
- user.user_canonical_email = build(:user_canonical_email, canonical_email: other_email)
+ it 'preserves any existing record' do
+ user.email = nil
+ user.user_canonical_email = build(:user_canonical_email, canonical_email: other_email)
- subject.execute
+ subject.execute
- expect(user.user_canonical_email.canonical_email).to eq other_email
+ expect(user.user_canonical_email.canonical_email).to eq other_email
+ end
end
end
end
diff --git a/spec/services/users/update_highest_member_role_service_spec.rb b/spec/services/users/update_highest_member_role_service_spec.rb
index 89ddd635bb6..06f4d787d72 100644
--- a/spec/services/users/update_highest_member_role_service_spec.rb
+++ b/spec/services/users/update_highest_member_role_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::UpdateHighestMemberRoleService do
+RSpec.describe Users::UpdateHighestMemberRoleService, feature_category: :user_management do
let(:user) { create(:user) }
let(:execute_service) { described_class.new(user).execute }
diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb
index f4ea757f81a..9ff3d9208fa 100644
--- a/spec/services/users/update_service_spec.rb
+++ b/spec/services/users/update_service_spec.rb
@@ -2,18 +2,17 @@
require 'spec_helper'
-RSpec.describe Users::UpdateService do
+RSpec.describe Users::UpdateService, feature_category: :user_profile do
let(:password) { User.random_password }
let(:user) { create(:user, password: password, password_confirmation: password) }
describe '#execute' do
it 'updates time preferences' do
- result = update_user(user, timezone: 'Europe/Warsaw', time_display_relative: true, time_format_in_24h: false)
+ result = update_user(user, timezone: 'Europe/Warsaw', time_display_relative: true)
expect(result).to eq(status: :success)
expect(user.reload.timezone).to eq('Europe/Warsaw')
expect(user.time_display_relative).to eq(true)
- expect(user.time_format_in_24h).to eq(false)
end
it 'returns an error result when record cannot be updated' do
@@ -185,6 +184,49 @@ RSpec.describe Users::UpdateService do
end.not_to raise_error
end
+ describe 'updates the enabled_following' do
+ let(:user) { create(:user) }
+
+ before do
+ 3.times do
+ user.follow(create(:user))
+ create(:user).follow(user)
+ end
+ user.reload
+ end
+
+ it 'removes followers and followees' do
+ expect do
+ update_user(user, enabled_following: false)
+ end.to change { user.followed_users.count }.from(3).to(0)
+ .and change { user.following_users.count }.from(3).to(0)
+ expect(user.enabled_following).to eq(false)
+ end
+
+ it 'does not remove followers/followees if feature flag is off' do
+ stub_feature_flags(disable_follow_users: false)
+
+ expect do
+ update_user(user, enabled_following: false)
+ end.to not_change { user.followed_users.count }
+ .and not_change { user.following_users.count }
+ end
+
+ context 'when there is more followers/followees then batch limit' do
+ before do
+ stub_env('BATCH_SIZE', 1)
+ end
+
+ it 'removes followers and followees' do
+ expect do
+ update_user(user, enabled_following: false)
+ end.to change { user.followed_users.count }.from(3).to(0)
+ .and change { user.following_users.count }.from(3).to(0)
+ expect(user.enabled_following).to eq(false)
+ end
+ end
+ end
+
def update_user(user, opts)
described_class.new(user, opts.merge(user: user)).execute
end
diff --git a/spec/services/users/update_todo_count_cache_service_spec.rb b/spec/services/users/update_todo_count_cache_service_spec.rb
index 3d96af928df..eec637cf5b4 100644
--- a/spec/services/users/update_todo_count_cache_service_spec.rb
+++ b/spec/services/users/update_todo_count_cache_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::UpdateTodoCountCacheService do
+RSpec.describe Users::UpdateTodoCountCacheService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
diff --git a/spec/services/users/upsert_credit_card_validation_service_spec.rb b/spec/services/users/upsert_credit_card_validation_service_spec.rb
index ac7e619612f..ebd2502398d 100644
--- a/spec/services/users/upsert_credit_card_validation_service_spec.rb
+++ b/spec/services/users/upsert_credit_card_validation_service_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe Users::UpsertCreditCardValidationService do
- let_it_be(:user) { create(:user, requires_credit_card_verification: true) }
+RSpec.describe Users::UpsertCreditCardValidationService, feature_category: :user_profile do
+ let_it_be(:user) { create(:user) }
let(:user_id) { user.id }
let(:credit_card_validated_time) { Time.utc(2020, 1, 1) }
@@ -21,7 +21,7 @@ RSpec.describe Users::UpsertCreditCardValidationService do
end
describe '#execute' do
- subject(:service) { described_class.new(params, user) }
+ subject(:service) { described_class.new(params) }
context 'successfully set credit card validation record for the user' do
context 'when user does not have credit card validation record' do
@@ -42,10 +42,6 @@ RSpec.describe Users::UpsertCreditCardValidationService do
expiration_date: Date.new(expiration_year, 1, 31)
)
end
-
- it 'sets the requires_credit_card_verification attribute on the user to false' do
- expect { service.execute }.to change { user.reload.requires_credit_card_verification }.to(false)
- end
end
context 'when user has credit card validation record' do
diff --git a/spec/services/users/validate_manual_otp_service_spec.rb b/spec/services/users/validate_manual_otp_service_spec.rb
index d71735814f2..9a6083bc41c 100644
--- a/spec/services/users/validate_manual_otp_service_spec.rb
+++ b/spec/services/users/validate_manual_otp_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::ValidateManualOtpService do
+RSpec.describe Users::ValidateManualOtpService, feature_category: :user_profile do
let_it_be(:user) { create(:user) }
let(:otp_code) { 42 }
@@ -32,6 +32,20 @@ RSpec.describe Users::ValidateManualOtpService do
validate
end
+
+ it 'handles unexpected error' do
+ error_message = "boom!"
+
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy|
+ expect(strategy).to receive(:validate).with(otp_code).once.and_raise(StandardError, error_message)
+ end
+ expect(Gitlab::ErrorTracking).to receive(:log_exception)
+
+ result = validate
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
end
context 'FortiTokenCloud' do
@@ -49,16 +63,23 @@ RSpec.describe Users::ValidateManualOtpService do
end
end
- context 'unexpected error' do
+ context 'DuoAuth' do
before do
- stub_feature_flags(forti_authenticator: user)
- allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
+ allow(::Gitlab.config.duo_auth).to receive(:enabled).and_return(true)
end
- it 'returns error' do
+ it 'calls DuoAuth strategy' do
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::DuoAuth::ManualOtp) do |strategy|
+ expect(strategy).to receive(:validate).with(otp_code).once
+ end
+
+ validate
+ end
+
+ it "handles unexpected error" do
error_message = "boom!"
- expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy|
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::DuoAuth::ManualOtp) do |strategy|
expect(strategy).to receive(:validate).with(otp_code).once.and_raise(StandardError, error_message)
end
expect(Gitlab::ErrorTracking).to receive(:log_exception)
diff --git a/spec/services/users/validate_push_otp_service_spec.rb b/spec/services/users/validate_push_otp_service_spec.rb
index 960b6bcd3bb..4ef374cbb7f 100644
--- a/spec/services/users/validate_push_otp_service_spec.rb
+++ b/spec/services/users/validate_push_otp_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::ValidatePushOtpService do
+RSpec.describe Users::ValidatePushOtpService, feature_category: :user_profile do
let_it_be(:user) { create(:user) }
subject(:validate) { described_class.new(user).execute }
diff --git a/spec/services/verify_pages_domain_service_spec.rb b/spec/services/verify_pages_domain_service_spec.rb
index 42f7ebc85f9..d66d584d3d0 100644
--- a/spec/services/verify_pages_domain_service_spec.rb
+++ b/spec/services/verify_pages_domain_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe VerifyPagesDomainService do
+RSpec.describe VerifyPagesDomainService, feature_category: :pages do
using RSpec::Parameterized::TableSyntax
include EmailHelpers
diff --git a/spec/services/web_hooks/destroy_service_spec.rb b/spec/services/web_hooks/destroy_service_spec.rb
index ca8cb8a1b75..642c25ab312 100644
--- a/spec/services/web_hooks/destroy_service_spec.rb
+++ b/spec/services/web_hooks/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WebHooks::DestroyService do
+RSpec.describe WebHooks::DestroyService, feature_category: :webhooks do
let_it_be(:user) { create(:user) }
subject { described_class.new(user) }
diff --git a/spec/services/web_hooks/log_destroy_service_spec.rb b/spec/services/web_hooks/log_destroy_service_spec.rb
index 7634726e5a4..b0444b659ba 100644
--- a/spec/services/web_hooks/log_destroy_service_spec.rb
+++ b/spec/services/web_hooks/log_destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WebHooks::LogDestroyService do
+RSpec.describe WebHooks::LogDestroyService, feature_category: :webhooks do
subject(:service) { described_class.new(hook.id) }
describe '#execute' do
diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb
index 8a845f60ad2..f56c07386fa 100644
--- a/spec/services/web_hooks/log_execution_service_spec.rb
+++ b/spec/services/web_hooks/log_execution_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WebHooks::LogExecutionService do
+RSpec.describe WebHooks::LogExecutionService, feature_category: :webhooks do
include ExclusiveLeaseHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/services/webauthn/authenticate_service_spec.rb b/spec/services/webauthn/authenticate_service_spec.rb
index b40f9465b63..ca940dff0eb 100644
--- a/spec/services/webauthn/authenticate_service_spec.rb
+++ b/spec/services/webauthn/authenticate_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'webauthn/fake_client'
-RSpec.describe Webauthn::AuthenticateService do
+RSpec.describe Webauthn::AuthenticateService, feature_category: :system_access do
let(:client) { WebAuthn::FakeClient.new(origin) }
let(:user) { create(:user) }
let(:challenge) { Base64.strict_encode64(SecureRandom.random_bytes(32)) }
diff --git a/spec/services/webauthn/register_service_spec.rb b/spec/services/webauthn/register_service_spec.rb
index bb9fa2080d2..2286d261e94 100644
--- a/spec/services/webauthn/register_service_spec.rb
+++ b/spec/services/webauthn/register_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'webauthn/fake_client'
-RSpec.describe Webauthn::RegisterService do
+RSpec.describe Webauthn::RegisterService, feature_category: :system_access do
let(:client) { WebAuthn::FakeClient.new(origin) }
let(:user) { create(:user) }
let(:challenge) { Base64.strict_encode64(SecureRandom.random_bytes(32)) }
diff --git a/spec/services/wiki_pages/base_service_spec.rb b/spec/services/wiki_pages/base_service_spec.rb
index 6ccc796014c..f434dc689ef 100644
--- a/spec/services/wiki_pages/base_service_spec.rb
+++ b/spec/services/wiki_pages/base_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WikiPages::BaseService do
+RSpec.describe WikiPages::BaseService, feature_category: :wiki do
let(:project) { double('project') }
let(:user) { double('user') }
diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb
index fd3776f4207..ca2d38ad70d 100644
--- a/spec/services/wiki_pages/create_service_spec.rb
+++ b/spec/services/wiki_pages/create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WikiPages::CreateService do
+RSpec.describe WikiPages::CreateService, feature_category: :wiki do
it_behaves_like 'WikiPages::CreateService#execute', :project
describe '#execute' do
diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb
index 9384ea1cd43..ff29fc59b3e 100644
--- a/spec/services/wiki_pages/destroy_service_spec.rb
+++ b/spec/services/wiki_pages/destroy_service_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-RSpec.describe WikiPages::DestroyService do
+RSpec.describe WikiPages::DestroyService, feature_category: :wiki do
it_behaves_like 'WikiPages::DestroyService#execute', :project
end
diff --git a/spec/services/wiki_pages/event_create_service_spec.rb b/spec/services/wiki_pages/event_create_service_spec.rb
index 8476f872e98..cbc2bd82a98 100644
--- a/spec/services/wiki_pages/event_create_service_spec.rb
+++ b/spec/services/wiki_pages/event_create_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WikiPages::EventCreateService do
+RSpec.describe WikiPages::EventCreateService, feature_category: :wiki do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb
index 62881817e32..79b2b55907b 100644
--- a/spec/services/wiki_pages/update_service_spec.rb
+++ b/spec/services/wiki_pages/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WikiPages::UpdateService do
+RSpec.describe WikiPages::UpdateService, feature_category: :wiki do
it_behaves_like 'WikiPages::UpdateService#execute', :project
describe '#execute' do
diff --git a/spec/services/wikis/create_attachment_service_spec.rb b/spec/services/wikis/create_attachment_service_spec.rb
index 22e34e1f373..fccdbd3040b 100644
--- a/spec/services/wikis/create_attachment_service_spec.rb
+++ b/spec/services/wikis/create_attachment_service_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Wikis::CreateAttachmentService do
+RSpec.describe Wikis::CreateAttachmentService, feature_category: :wiki do
let(:container) { create(:project, :wiki_repo) }
let(:user) { create(:user) }
let(:file_name) { 'filename.txt' }
diff --git a/spec/services/work_items/build_service_spec.rb b/spec/services/work_items/build_service_spec.rb
index 405b4414fc2..3ecf78e0659 100644
--- a/spec/services/work_items/build_service_spec.rb
+++ b/spec/services/work_items/build_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::BuildService do
+RSpec.describe WorkItems::BuildService, feature_category: :team_planning do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/services/work_items/create_from_task_service_spec.rb b/spec/services/work_items/create_from_task_service_spec.rb
index 7c5430f038c..b2f81f1dc54 100644
--- a/spec/services/work_items/create_from_task_service_spec.rb
+++ b/spec/services/work_items/create_from_task_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::CreateFromTaskService do
+RSpec.describe WorkItems::CreateFromTaskService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user) }
let_it_be(:list_work_item, refind: true) { create(:work_item, project: project, description: "- [ ] Item to be converted\n second line\n third line") }
diff --git a/spec/services/work_items/create_service_spec.rb b/spec/services/work_items/create_service_spec.rb
index 1b134c308f2..46e598c3f11 100644
--- a/spec/services/work_items/create_service_spec.rb
+++ b/spec/services/work_items/create_service_spec.rb
@@ -2,200 +2,255 @@
require 'spec_helper'
-RSpec.describe WorkItems::CreateService do
+RSpec.describe WorkItems::CreateService, feature_category: :team_planning do
include AfterNextHelpers
- let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:parent) { create(:work_item, project: project) }
- let_it_be(:guest) { create(:user) }
- let_it_be(:reporter) { create(:user) }
- let_it_be(:user_with_no_access) { create(:user) }
-
- let(:widget_params) { {} }
- let(:spam_params) { double }
- let(:current_user) { guest }
- let(:opts) do
- {
- title: 'Awesome work_item',
- description: 'please fix'
- }
- end
-
- before_all do
- project.add_guest(guest)
- project.add_reporter(reporter)
- end
+ RSpec.shared_examples 'creates work item in container' do |container_type|
+ let_it_be_with_reload(:project) { create(:project) }
+ let_it_be_with_reload(:group) { create(:group) }
- describe '#execute' do
- let(:service) do
- described_class.new(
- container: project,
- current_user: current_user,
- params: opts,
- spam_params: spam_params,
- widget_params: widget_params
- )
+ let_it_be(:container) do
+ case container_type
+ when :project then project
+ when :project_namespace then project.project_namespace
+ when :group then group
+ end
end
- subject(:service_result) { service.execute }
+ let_it_be(:container_args) do
+ case container_type
+ when :project, :project_namespace then { project: project }
+ when :group then { namespace: group }
+ end
+ end
- before do
- stub_spam_services
+ let_it_be(:parent) { create(:work_item, **container_args) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:user_with_no_access) { create(:user) }
+
+ let(:widget_params) { {} }
+ let(:spam_params) { double }
+ let(:current_user) { guest }
+ let(:opts) do
+ {
+ title: 'Awesome work_item',
+ description: 'please fix'
+ }
end
- context 'when user is not allowed to create a work item in the project' do
- let(:current_user) { user_with_no_access }
+ before_all do
+ memberships_container = container.is_a?(Namespaces::ProjectNamespace) ? container.reload.project : container
+ memberships_container.add_guest(guest)
+ memberships_container.add_reporter(reporter)
+ end
- it { is_expected.to be_error }
+ describe '#execute' do
+ shared_examples 'fails creating work item and returns errors' do
+ it 'does not create new work item if parent can not be set' do
+ expect { service_result }.not_to change(WorkItem, :count)
- it 'returns an access error' do
- expect(service_result.errors).to contain_exactly('Operation not allowed')
+ expect(service_result[:status]).to be(:error)
+ expect(service_result[:message]).to match(error_message)
+ end
end
- end
- context 'when params are valid' do
- it 'created instance is a WorkItem' do
- expect(Issuable::CommonSystemNotesService).to receive_message_chain(:new, :execute)
+ let(:service) do
+ described_class.new(
+ container: container,
+ current_user: current_user,
+ params: opts,
+ spam_params: spam_params,
+ widget_params: widget_params
+ )
+ end
- work_item = service_result[:work_item]
+ subject(:service_result) { service.execute }
- expect(work_item).to be_persisted
- expect(work_item).to be_a(::WorkItem)
- expect(work_item.title).to eq('Awesome work_item')
- expect(work_item.description).to eq('please fix')
- expect(work_item.work_item_type.base_type).to eq('issue')
+ before do
+ stub_spam_services
end
- it 'calls NewIssueWorker with correct arguments' do
- expect(NewIssueWorker).to receive(:perform_async).with(Integer, current_user.id, 'WorkItem')
+ context 'when user is not allowed to create a work item in the container' do
+ let(:current_user) { user_with_no_access }
- service_result
+ it { is_expected.to be_error }
+
+ it 'returns an access error' do
+ expect(service_result.errors).to contain_exactly('Operation not allowed')
+ end
end
- end
- context 'when params are invalid' do
- let(:opts) { { title: '' } }
+ context 'when applying quick actions' do
+ let(:work_item) { service_result[:work_item] }
+ let(:opts) do
+ {
+ title: 'My work item',
+ work_item_type: work_item_type,
+ description: '/shrug'
+ }
+ end
- it { is_expected.to be_error }
+ context 'when work item type is not the default Issue' do
+ let(:work_item_type) { create(:work_item_type, :task, namespace: group) }
- it 'returns validation errors' do
- expect(service_result.errors).to contain_exactly("Title can't be blank")
- end
+ it 'saves the work item without applying the quick action' do
+ expect(service_result).to be_success
+ expect(work_item).to be_persisted
+ expect(work_item.description).to eq('/shrug')
+ end
+ end
- it 'does not execute after-create transaction widgets' do
- expect(service).to receive(:create).and_call_original
- expect(service).not_to receive(:execute_widgets)
- .with(callback: :after_create_in_transaction, widget_params: widget_params)
+ context 'when work item type is the default Issue' do
+ let(:work_item_type) { WorkItems::Type.default_by_type(:issue) }
- service_result
+ it 'saves the work item and applies the quick action' do
+ expect(service_result).to be_success
+ expect(work_item).to be_persisted
+ expect(work_item.description).to eq(' ¯\_(ツ)_/¯')
+ end
+ end
end
- end
- context 'checking spam' do
- it 'executes SpamActionService' do
- expect_next_instance_of(
- Spam::SpamActionService,
- {
- spammable: kind_of(WorkItem),
- spam_params: spam_params,
- user: an_instance_of(User),
- action: :create
- }
- ) do |instance|
- expect(instance).to receive(:execute)
+ context 'when params are valid' do
+ it 'created instance is a WorkItem' do
+ expect(Issuable::CommonSystemNotesService).to receive_message_chain(:new, :execute)
+
+ work_item = service_result[:work_item]
+
+ expect(work_item).to be_persisted
+ expect(work_item).to be_a(::WorkItem)
+ expect(work_item.title).to eq('Awesome work_item')
+ expect(work_item.description).to eq('please fix')
+ expect(work_item.work_item_type.base_type).to eq('issue')
end
- service_result
- end
- end
+ it 'calls NewIssueWorker with correct arguments' do
+ expect(NewIssueWorker).to receive(:perform_async).with(Integer, current_user.id, 'WorkItem')
- it_behaves_like 'work item widgetable service' do
- let(:widget_params) do
- {
- hierarchy_widget: { parent: parent }
- }
+ service_result
+ end
end
- let(:service) do
- described_class.new(
- container: project,
- current_user: current_user,
- params: opts,
- spam_params: spam_params,
- widget_params: widget_params
- )
+ context 'when params are invalid' do
+ let(:opts) { { title: '' } }
+
+ it { is_expected.to be_error }
+
+ it 'returns validation errors' do
+ expect(service_result.errors).to contain_exactly("Title can't be blank")
+ end
+
+ it 'does not execute after-create transaction widgets' do
+ expect(service).to receive(:create).and_call_original
+ expect(service).not_to receive(:execute_widgets)
+ .with(callback: :after_create_in_transaction, widget_params: widget_params)
+
+ service_result
+ end
end
- let(:service_execute) { service.execute }
+ context 'checking spam' do
+ it 'executes SpamActionService' do
+ expect_next_instance_of(
+ Spam::SpamActionService,
+ {
+ spammable: kind_of(WorkItem),
+ spam_params: spam_params,
+ user: an_instance_of(User),
+ action: :create
+ }
+ ) do |instance|
+ expect(instance).to receive(:execute)
+ end
+
+ service_result
+ end
+ end
- let(:supported_widgets) do
- [
+ it_behaves_like 'work item widgetable service' do
+ let(:widget_params) do
{
- klass: WorkItems::Widgets::HierarchyService::CreateService,
- callback: :after_create_in_transaction,
- params: { parent: parent }
+ hierarchy_widget: { parent: parent }
}
- ]
- end
- end
+ end
- describe 'hierarchy widget' do
- let(:widget_params) { { hierarchy_widget: { parent: parent } } }
+ let(:service) do
+ described_class.new(
+ container: container,
+ current_user: current_user,
+ params: opts,
+ spam_params: spam_params,
+ widget_params: widget_params
+ )
+ end
- shared_examples 'fails creating work item and returns errors' do
- it 'does not create new work item if parent can not be set' do
- expect { service_result }.not_to change(WorkItem, :count)
+ let(:service_execute) { service.execute }
- expect(service_result[:status]).to be(:error)
- expect(service_result[:message]).to match(error_message)
+ let(:supported_widgets) do
+ [
+ {
+ klass: WorkItems::Widgets::HierarchyService::CreateService,
+ callback: :after_create_in_transaction,
+ params: { parent: parent }
+ }
+ ]
end
end
- context 'when user can admin parent link' do
- let(:current_user) { reporter }
+ describe 'hierarchy widget' do
+ let(:widget_params) { { hierarchy_widget: { parent: parent } } }
- context 'when parent is valid work item' do
- let(:opts) do
- {
- title: 'Awesome work_item',
- description: 'please fix',
- work_item_type: WorkItems::Type.default_by_type(:task)
- }
- end
+ context 'when user can admin parent link' do
+ let(:current_user) { reporter }
- it 'creates new work item and sets parent reference' do
- expect { service_result }.to change(
- WorkItem, :count).by(1).and(change(
- WorkItems::ParentLink, :count).by(1))
+ context 'when parent is valid work item' do
+ let(:opts) do
+ {
+ title: 'Awesome work_item',
+ description: 'please fix',
+ work_item_type: WorkItems::Type.default_by_type(:task)
+ }
+ end
- expect(service_result[:status]).to be(:success)
+ it 'creates new work item and sets parent reference' do
+ expect { service_result }.to change(WorkItem, :count).by(1).and(
+ change(WorkItems::ParentLink, :count).by(1)
+ )
+
+ expect(service_result[:status]).to be(:success)
+ end
end
- end
- context 'when parent type is invalid' do
- let_it_be(:parent) { create(:work_item, :task, project: project) }
+ context 'when parent type is invalid' do
+ let_it_be(:parent) { create(:work_item, :task, **container_args) }
- it_behaves_like 'fails creating work item and returns errors' do
- let(:error_message) { 'is not allowed to add this type of parent' }
+ it_behaves_like 'fails creating work item and returns errors' do
+ let(:error_message) { 'is not allowed to add this type of parent' }
+ end
end
end
- end
- context 'when user cannot admin parent link' do
- let(:current_user) { guest }
+ context 'when user cannot admin parent link' do
+ let(:current_user) { guest }
- let(:opts) do
- {
- title: 'Awesome work_item',
- description: 'please fix',
- work_item_type: WorkItems::Type.default_by_type(:task)
- }
- end
+ let(:opts) do
+ {
+ title: 'Awesome work_item',
+ description: 'please fix',
+ work_item_type: WorkItems::Type.default_by_type(:task)
+ }
+ end
- it_behaves_like 'fails creating work item and returns errors' do
- let(:error_message) { 'No matching work item found. Make sure that you are adding a valid work item ID.' }
+ it_behaves_like 'fails creating work item and returns errors' do
+ let(:error_message) { 'No matching work item found. Make sure that you are adding a valid work item ID.' }
+ end
end
end
end
end
+
+ it_behaves_like 'creates work item in container', :project
+ it_behaves_like 'creates work item in container', :project_namespace
+ it_behaves_like 'creates work item in container', :group
end
diff --git a/spec/services/work_items/delete_service_spec.rb b/spec/services/work_items/delete_service_spec.rb
index 69ae881a12f..ac72815a57e 100644
--- a/spec/services/work_items/delete_service_spec.rb
+++ b/spec/services/work_items/delete_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::DeleteService do
+RSpec.describe WorkItems::DeleteService, feature_category: :team_planning do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:guest) { create(:user) }
let_it_be(:work_item, refind: true) { create(:work_item, project: project, author: guest) }
diff --git a/spec/services/work_items/delete_task_service_spec.rb b/spec/services/work_items/delete_task_service_spec.rb
index 07a0d8d6c1a..dc01da65771 100644
--- a/spec/services/work_items/delete_task_service_spec.rb
+++ b/spec/services/work_items/delete_task_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::DeleteTaskService do
+RSpec.describe WorkItems::DeleteTaskService, feature_category: :team_planning do
let_it_be(:project) { create(:project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be_with_refind(:task) { create(:work_item, project: project, author: developer) }
diff --git a/spec/services/work_items/export_csv_service_spec.rb b/spec/services/work_items/export_csv_service_spec.rb
index 0718d3b686a..948ff89245e 100644
--- a/spec/services/work_items/export_csv_service_spec.rb
+++ b/spec/services/work_items/export_csv_service_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe WorkItems::ExportCsvService, :with_license, feature_category: :te
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, group: group) }
- let_it_be(:work_item_1) { create(:work_item, project: project) }
+ let_it_be(:work_item_1) { create(:work_item, description: 'test', project: project) }
let_it_be(:work_item_2) { create(:work_item, :incident, project: project) }
subject { described_class.new(WorkItem.all, project) }
@@ -30,9 +30,8 @@ RSpec.describe WorkItems::ExportCsvService, :with_license, feature_category: :te
end
describe '#email' do
- # TODO - will be implemented as part of https://gitlab.com/gitlab-org/gitlab/-/issues/379082
- xit 'emails csv' do
- expect { subject.email(user) }.o change { ActionMailer::Base.deliveries.count }.from(0).to(1)
+ it 'emails csv' do
+ expect { subject.email(user) }.to change { ActionMailer::Base.deliveries.count }.from(0).to(1)
end
end
@@ -65,6 +64,11 @@ RSpec.describe WorkItems::ExportCsvService, :with_license, feature_category: :te
expect(csv[0]['Created At (UTC)']).to eq(work_item_1.created_at.to_s(:csv))
end
+ specify 'description' do
+ expect(csv[0]['Description']).to be_present
+ expect(csv[0]['Description']).to eq(work_item_1.description)
+ end
+
it 'preloads fields to avoid N+1 queries' do
control = ActiveRecord::QueryRecorder.new { subject.csv_data }
@@ -74,4 +78,20 @@ RSpec.describe WorkItems::ExportCsvService, :with_license, feature_category: :te
end
it_behaves_like 'a service that returns invalid fields from selection'
+
+ # TODO - once we have a UI for this feature
+ # we can turn these into feature specs.
+ # more info at: https://gitlab.com/gitlab-org/gitlab/-/issues/396943
+ context 'when importing an exported file' do
+ context 'for work item of type issue' do
+ it_behaves_like 'a exported file that can be imported' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:origin_project) { create(:project) }
+ let_it_be(:target_project) { create(:project) }
+ let_it_be(:work_item) { create(:work_item, project: origin_project) }
+
+ let(:expected_matching_fields) { %w[title work_item_type] }
+ end
+ end
+ end
end
diff --git a/spec/services/work_items/import_csv_service_spec.rb b/spec/services/work_items/import_csv_service_spec.rb
new file mode 100644
index 00000000000..3c710640f4a
--- /dev/null
+++ b/spec/services/work_items/import_csv_service_spec.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::ImportCsvService, feature_category: :team_planning do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:author) { create(:user, username: 'csv_author') }
+ let(:file) { fixture_file_upload('spec/fixtures/work_items_valid_types.csv') }
+ let(:service) do
+ uploader = FileUploader.new(project)
+ uploader.store!(file)
+
+ described_class.new(user, project, uploader)
+ end
+
+ let_it_be(:issue_type) { ::WorkItems::Type.default_issue_type }
+
+ let(:work_items) { ::WorkItems::WorkItemsFinder.new(user, project: project).execute }
+ let(:email_method) { :import_work_items_csv_email }
+
+ subject { service.execute }
+
+ describe '#execute', :aggregate_failures do
+ context 'when user has permission' do
+ before do
+ project.add_reporter(user)
+ end
+
+ it_behaves_like 'importer with email notification'
+
+ context 'when file format is valid' do
+ context 'when work item types are available' do
+ it 'creates the expected number of work items' do
+ expect { subject }.to change { work_items.count }.by 2
+ end
+
+ it 'sets work item attributes' do
+ result = subject
+
+ expect(work_items.reload).to contain_exactly(
+ have_attributes(
+ title: 'Valid issue',
+ work_item_type_id: issue_type.id
+ ),
+ have_attributes(
+ title: 'Valid issue with alternate case',
+ work_item_type_id: issue_type.id
+ )
+ )
+
+ expect(result[:success]).to eq(2)
+ expect(result[:error_lines]).to eq([])
+ expect(result[:type_errors]).to be_nil
+ expect(result[:parse_error]).to eq(false)
+ end
+ end
+
+ context 'when csv contains work item types that are missing or not available' do
+ let(:file) { fixture_file_upload('spec/fixtures/work_items_invalid_types.csv') }
+
+ it 'creates no work items' do
+ expect { subject }.not_to change { work_items.count }
+ end
+
+ it 'returns the correct result' do
+ result = subject
+
+ expect(result[:success]).to eq(0)
+ expect(result[:error_lines]).to be_empty # there are problematic lines detailed below
+ expect(result[:parse_error]).to eq(false)
+ expect(result[:type_errors]).to match({
+ blank: [4],
+ disallowed: {}, # tested in the EE version
+ missing: {
+ "isssue" => [2],
+ "issue!!" => [3]
+ }
+ })
+ end
+ end
+ end
+
+ context 'when file is missing necessary headers' do
+ let(:file) { fixture_file_upload('spec/fixtures/work_items_missing_header.csv') }
+
+ it 'creates no records' do
+ result = subject
+
+ expect(result[:success]).to eq(0)
+ expect(result[:error_lines]).to eq([1])
+ expect(result[:type_errors]).to be_nil
+ expect(result[:parse_error]).to eq(true)
+ end
+
+ it 'creates no work items' do
+ expect { subject }.not_to change { work_items.count }
+ end
+ end
+
+ context 'when import_export_work_items_csv feature flag is off' do
+ before do
+ stub_feature_flags(import_export_work_items_csv: false)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(/This feature is currently behind a feature flag and it is not available./)
+ end
+ end
+ end
+
+ context 'when user does not have permission' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(/You do not have permission to import work items in this project/)
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/parent_links/base_service_spec.rb b/spec/services/work_items/parent_links/base_service_spec.rb
new file mode 100644
index 00000000000..dbdbc774d3c
--- /dev/null
+++ b/spec/services/work_items/parent_links/base_service_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+module WorkItems
+ class ParentLinksService < WorkItems::ParentLinks::BaseService; end
+end
+
+RSpec.describe WorkItems::ParentLinks::BaseService, feature_category: :portfolio_management do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:work_item) { create(:work_item, :objective, project: project) }
+ let_it_be(:target_work_item) { create(:work_item, :objective, project: project) }
+
+ let(:params) { { target_issuable: target_work_item } }
+ let(:described_class_descendant) { WorkItems::ParentLinksService }
+
+ before do
+ project.add_reporter(user)
+ end
+
+ describe '#execute' do
+ subject { described_class_descendant.new(work_item, user, params).execute }
+
+ context 'when user has sufficient permissions' do
+ it 'raises NotImplementedError' do
+ expect { subject }.to raise_error(NotImplementedError)
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb
index 5884847eac3..41ae6398614 100644
--- a/spec/services/work_items/parent_links/create_service_spec.rb
+++ b/spec/services/work_items/parent_links/create_service_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
let_it_be(:project) { create(:project) }
let_it_be(:work_item) { create(:work_item, project: project) }
let_it_be(:task) { create(:work_item, :task, project: project) }
- let_it_be(:task1) { create(:work_item, :task, project: project) }
- let_it_be(:task2) { create(:work_item, :task, project: project) }
+ let_it_be_with_reload(:task1) { create(:work_item, :task, project: project) }
+ let_it_be_with_reload(:task2) { create(:work_item, :task, project: project) }
let_it_be(:guest_task) { create(:work_item, :task) }
let_it_be(:invalid_task) { build_stubbed(:work_item, :task, id: non_existing_record_id) }
let_it_be(:another_project) { (create :project) }
@@ -68,6 +68,40 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
end
end
+ context 'when adjacent is already in place' do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:parent_item) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:current_item) { create(:work_item, :objective, project: project) }
+
+ let_it_be_with_reload(:adjacent) do
+ create(:work_item, :objective, project: project)
+ end
+
+ let_it_be_with_reload(:link_to_adjacent) do
+ create(:parent_link, work_item_parent: parent_item, work_item: adjacent)
+ end
+
+ subject { described_class.new(parent_item, user, { target_issuable: current_item }).execute }
+
+ where(:adjacent_position, :expected_order) do
+ -100 | lazy { [adjacent, current_item] }
+ 0 | lazy { [adjacent, current_item] }
+ 100 | lazy { [adjacent, current_item] }
+ end
+
+ with_them do
+ before do
+ link_to_adjacent.update!(relative_position: adjacent_position)
+ end
+
+ it 'sets relative positions' do
+ expect { subject }.to change(parent_link_class, :count).by(1)
+ expect(parent_item.work_item_children_by_relative_position).to eq(expected_order)
+ end
+ end
+ end
+
context 'when there are tasks to relate' do
let(:params) { { issuable_references: [task1, task2] } }
@@ -84,26 +118,74 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
expect(subject[:created_references].map(&:work_item_id)).to match_array([task1.id, task2.id])
end
- it 'creates notes', :aggregate_failures do
- subject
+ it 'creates notes and records the events', :aggregate_failures do
+ expect { subject }.to change(WorkItems::ResourceLinkEvent, :count).by(2)
work_item_notes = work_item.notes.last(2)
+ resource_link_events = WorkItems::ResourceLinkEvent.last(2)
expect(work_item_notes.first.note).to eq("added #{task1.to_reference} as child task")
expect(work_item_notes.last.note).to eq("added #{task2.to_reference} as child task")
expect(task1.notes.last.note).to eq("added #{work_item.to_reference} as parent issue")
expect(task2.notes.last.note).to eq("added #{work_item.to_reference} as parent issue")
+ expect(resource_link_events.first).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task1.id,
+ action: "add",
+ system_note_metadata_id: task1.notes.last.system_note_metadata.id
+ )
+ expect(resource_link_events.last).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task2.id,
+ action: "add",
+ system_note_metadata_id: task2.notes.last.system_note_metadata.id
+ )
+ end
+
+ context 'when note creation fails for some reason' do
+ let(:params) { { issuable_references: [task1] } }
+
+ [Note.new, nil].each do |relate_child_note|
+ it 'still records the link event', :aggregate_failures do
+ allow_next_instance_of(WorkItems::ParentLinks::CreateService) do |instance|
+ allow(instance).to receive(:create_notes).and_return(relate_child_note)
+ end
+
+ expect { subject }
+ .to change(WorkItems::ResourceLinkEvent, :count).by(1)
+ .and not_change(Note, :count)
+
+ expect(WorkItems::ResourceLinkEvent.last).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task1.id,
+ action: "add",
+ system_note_metadata_id: nil
+ )
+ end
+ end
end
context 'when task is already assigned' do
let(:params) { { issuable_references: [task, task2] } }
it 'creates links only for non related tasks', :aggregate_failures do
- expect { subject }.to change(parent_link_class, :count).by(1)
+ expect { subject }
+ .to change(parent_link_class, :count).by(1)
+ .and change(WorkItems::ResourceLinkEvent, :count).by(1)
expect(subject[:created_references].map(&:work_item_id)).to match_array([task2.id])
expect(work_item.notes.last.note).to eq("added #{task2.to_reference} as child task")
expect(task2.notes.last.note).to eq("added #{work_item.to_reference} as parent issue")
expect(task.notes).to be_empty
+ expect(WorkItems::ResourceLinkEvent.last).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task2.id,
+ action: "add",
+ system_note_metadata_id: task2.notes.last.system_note_metadata.id
+ )
end
end
@@ -160,7 +242,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService, feature_category: :portfol
end
context 'when params include invalid ids' do
- let(:params) { { issuable_references: [task1, invalid_task] } }
+ let(:params) { { issuable_references: [task1, guest_task] } }
it 'creates links only for valid IDs' do
expect { subject }.to change(parent_link_class, :count).by(1)
diff --git a/spec/services/work_items/parent_links/destroy_service_spec.rb b/spec/services/work_items/parent_links/destroy_service_spec.rb
index 654a03ef6f7..7e2e3949b73 100644
--- a/spec/services/work_items/parent_links/destroy_service_spec.rb
+++ b/spec/services/work_items/parent_links/destroy_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::ParentLinks::DestroyService do
+RSpec.describe WorkItems::ParentLinks::DestroyService, feature_category: :team_planning do
describe '#execute' do
let_it_be(:reporter) { create(:user) }
let_it_be(:guest) { create(:user) }
@@ -24,23 +24,53 @@ RSpec.describe WorkItems::ParentLinks::DestroyService do
let(:user) { reporter }
it 'removes relation and creates notes', :aggregate_failures do
- expect { subject }.to change(parent_link_class, :count).by(-1)
+ expect { subject }
+ .to change(parent_link_class, :count).by(-1)
+ .and change(WorkItems::ResourceLinkEvent, :count).by(1)
expect(work_item.notes.last.note).to eq("removed child task #{task.to_reference}")
expect(task.notes.last.note).to eq("removed parent issue #{work_item.to_reference}")
+ expect(WorkItems::ResourceLinkEvent.last).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task.id,
+ action: "remove",
+ system_note_metadata_id: task.notes.last.system_note_metadata.id
+ )
end
it 'returns success message' do
is_expected.to eq(message: 'Relation was removed', status: :success)
end
+
+ context 'when note creation fails for some reason' do
+ [Note.new, nil].each do |unrelate_child_note|
+ it 'still records the link event', :aggregate_failures do
+ allow(SystemNoteService).to receive(:unrelate_work_item).and_return(unrelate_child_note)
+
+ expect { subject }
+ .to change(WorkItems::ResourceLinkEvent, :count).by(1)
+ .and not_change(Note, :count)
+
+ expect(WorkItems::ResourceLinkEvent.last).to have_attributes(
+ user_id: user.id,
+ issue_id: work_item.id,
+ child_work_item_id: task.id,
+ action: "remove",
+ system_note_metadata_id: nil
+ )
+ end
+ end
+ end
end
context 'when user has insufficient permissions' do
let(:user) { guest }
it 'does not remove relation', :aggregate_failures do
- expect { subject }.not_to change(parent_link_class, :count).from(1)
-
+ expect { subject }
+ .to not_change(parent_link_class, :count).from(1)
+ .and not_change(WorkItems::ResourceLinkEvent, :count)
expect(SystemNoteService).not_to receive(:unrelate_work_item)
end
diff --git a/spec/services/work_items/parent_links/reorder_service_spec.rb b/spec/services/work_items/parent_links/reorder_service_spec.rb
new file mode 100644
index 00000000000..0448429d2bb
--- /dev/null
+++ b/spec/services/work_items/parent_links/reorder_service_spec.rb
@@ -0,0 +1,176 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::ParentLinks::ReorderService, feature_category: :portfolio_management do
+ describe '#execute' do
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:work_item) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:top_adjacent) { create(:work_item, :objective, project: project) }
+ let_it_be_with_reload(:last_adjacent) { create(:work_item, :objective, project: project) }
+
+ let(:parent_link_class) { WorkItems::ParentLink }
+ let(:user) { reporter }
+ let(:params) { { target_issuable: work_item } }
+ let(:relative_range) { [top_adjacent, last_adjacent].map(&:parent_link).map(&:relative_position) }
+
+ subject { described_class.new(parent, user, params).execute }
+
+ before do
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+
+ create(:parent_link, work_item: top_adjacent, work_item_parent: parent)
+ create(:parent_link, work_item: last_adjacent, work_item_parent: parent)
+ end
+
+ shared_examples 'raises a service error' do |message, status = 409|
+ it { is_expected.to eq(service_error(message, http_status: status)) }
+ end
+
+ shared_examples 'returns not found error' do
+ it 'returns error' do
+ error = "No matching work item found. Make sure that you are adding a valid work item ID."
+
+ is_expected.to eq(service_error(error))
+ end
+
+ it 'creates no relationship' do
+ expect { subject }.not_to change { parent_link_class.count }
+ end
+ end
+
+ shared_examples 'returns conflict error' do
+ it_behaves_like 'raises a service error', 'Work item(s) already assigned'
+
+ it 'creates no relationship' do
+ expect { subject }.to not_change { parent_link_class.count }
+ end
+ end
+
+ shared_examples 'processes ordered hierarchy' do
+ it 'returns success status and processed links', :aggregate_failures do
+ expect(subject.keys).to match_array([:status, :created_references])
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:created_references].map(&:work_item_id)).to match_array([work_item.id])
+ end
+
+ it 'orders hierarchy' do
+ subject
+
+ expect(last_adjacent.parent_link.relative_position).to be_between(*relative_range)
+ end
+ end
+
+ context 'when user has insufficient permissions' do
+ let(:user) { guest }
+
+ it_behaves_like 'returns not found error'
+
+ context 'when user is a guest assigned to the work item' do
+ before do
+ work_item.assignees = [guest]
+ end
+
+ it_behaves_like 'returns not found error'
+ end
+ end
+
+ context 'when child and parent are already linked' do
+ before do
+ create(:parent_link, work_item: work_item, work_item_parent: parent)
+ end
+
+ it_behaves_like 'returns conflict error'
+
+ context 'when adjacents are already in place and the user has sufficient permissions' do
+ let(:base_param) { { target_issuable: work_item } }
+
+ shared_examples 'updates hierarchy order without notes' do
+ it_behaves_like 'processes ordered hierarchy'
+
+ it 'keeps relationships', :aggregate_failures do
+ expect { subject }.to not_change { parent_link_class.count }
+
+ expect(parent_link_class.where(work_item: work_item).last.work_item_parent).to eq(parent)
+ end
+
+ it 'does not create notes', :aggregate_failures do
+ expect { subject }.to not_change { work_item.notes.count }.and(not_change { work_item.notes.count })
+ end
+ end
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order without notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order without notes'
+ end
+ end
+ end
+
+ context 'when new parent is assigned' do
+ shared_examples 'updates hierarchy order and creates notes' do
+ it_behaves_like 'processes ordered hierarchy'
+
+ it 'creates notes', :aggregate_failures do
+ subject
+
+ expect(parent.notes.last.note).to eq("added #{work_item.to_reference} as child objective")
+ expect(work_item.notes.last.note).to eq("added #{parent.to_reference} as parent objective")
+ end
+ end
+
+ context 'when adjacents are already in place and the user has sufficient permissions' do
+ let(:base_param) { { target_issuable: work_item } }
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when previous parent was in place' do
+ before do
+ create(:parent_link, work_item: work_item,
+ work_item_parent: create(:work_item, :objective, project: project))
+ end
+
+ context 'when moving before adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: last_adjacent, relative_position: 'BEFORE' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+
+ context 'when moving after adjacent work item' do
+ let(:params) { base_param.merge({ adjacent_work_item: top_adjacent, relative_position: 'AFTER' }) }
+
+ it_behaves_like 'updates hierarchy order and creates notes'
+ end
+ end
+ end
+ end
+ end
+
+ def service_error(message, http_status: 404)
+ {
+ message: message,
+ status: :error,
+ http_status: http_status
+ }
+ end
+end
diff --git a/spec/services/work_items/prepare_import_csv_service_spec.rb b/spec/services/work_items/prepare_import_csv_service_spec.rb
new file mode 100644
index 00000000000..6a657120690
--- /dev/null
+++ b/spec/services/work_items/prepare_import_csv_service_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::PrepareImportCsvService, feature_category: :team_planning do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ let(:file) { double }
+ let(:upload_service) { double }
+ let(:uploader) { double }
+ let(:upload) { double }
+
+ let(:subject) do
+ described_class.new(project, user, file: file).execute
+ end
+
+ context 'when file is uploaded correctly' do
+ let(:upload_id) { 99 }
+
+ before do
+ mock_upload
+ end
+
+ it 'returns a success message' do
+ result = subject
+
+ expect(result[:status]).to eq(:success)
+ expect(result[:message]).to eq(
+ "Your work items are being imported. Once finished, you'll receive a confirmation email.")
+ end
+
+ it 'enqueues the ImportWorkItemsCsvWorker' do
+ expect(WorkItems::ImportWorkItemsCsvWorker).to receive(:perform_async).with(user.id, project.id, upload_id)
+
+ subject
+ end
+ end
+
+ context 'when file upload fails' do
+ before do
+ mock_upload(false)
+ end
+
+ it 'returns an error message' do
+ result = subject
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('File upload error.')
+ end
+ end
+end
diff --git a/spec/services/work_items/task_list_reference_removal_service_spec.rb b/spec/services/work_items/task_list_reference_removal_service_spec.rb
index 91b7814ae92..4e87ce66c21 100644
--- a/spec/services/work_items/task_list_reference_removal_service_spec.rb
+++ b/spec/services/work_items/task_list_reference_removal_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::TaskListReferenceRemovalService do
+RSpec.describe WorkItems::TaskListReferenceRemovalService, feature_category: :team_planning do
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository).tap { |project| project.add_developer(developer) } }
let_it_be(:task) { create(:work_item, project: project, title: 'Task title') }
diff --git a/spec/services/work_items/task_list_reference_replacement_service_spec.rb b/spec/services/work_items/task_list_reference_replacement_service_spec.rb
index 965c5f1d554..8f696109fa1 100644
--- a/spec/services/work_items/task_list_reference_replacement_service_spec.rb
+++ b/spec/services/work_items/task_list_reference_replacement_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::TaskListReferenceReplacementService do
+RSpec.describe WorkItems::TaskListReferenceReplacementService, feature_category: :team_planning do
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository).tap { |project| project.add_developer(developer) } }
let_it_be(:single_line_work_item, refind: true) { create(:work_item, project: project, description: '- [ ] single line', lock_version: 3) }
diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb
index 435995c6570..2cf52ee853a 100644
--- a/spec/services/work_items/update_service_spec.rb
+++ b/spec/services/work_items/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::UpdateService do
+RSpec.describe WorkItems::UpdateService, feature_category: :team_planning do
let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -44,6 +44,33 @@ RSpec.describe WorkItems::UpdateService do
end
end
+ context 'when applying quick actions' do
+ let(:opts) { { description: "/shrug" } }
+
+ context 'when work item type is not the default Issue' do
+ before do
+ task_type = WorkItems::Type.default_by_type(:task)
+ work_item.update_columns(issue_type: task_type.base_type, work_item_type_id: task_type.id)
+ end
+
+ it 'does not apply the quick action' do
+ expect do
+ update_work_item
+ end.to change(work_item, :description).to('/shrug')
+ end
+ end
+
+ context 'when work item type is the default Issue' do
+ let(:issue) { create(:work_item, :issue, description: '') }
+
+ it 'applies the quick action' do
+ expect do
+ update_work_item
+ end.to change(work_item, :description).to(' ¯\_(ツ)_/¯')
+ end
+ end
+ end
+
context 'when title is changed' do
let(:opts) { { title: 'changed' } }
diff --git a/spec/services/work_items/widgets/assignees_service/update_service_spec.rb b/spec/services/work_items/widgets/assignees_service/update_service_spec.rb
index 0ab2c85f078..66e30e2f882 100644
--- a/spec/services/work_items/widgets/assignees_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/assignees_service/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::Widgets::AssigneesService::UpdateService, :freeze_time do
+RSpec.describe WorkItems::Widgets::AssigneesService::UpdateService, :freeze_time, feature_category: :portfolio_management do
let_it_be(:reporter) { create(:user) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:new_assignee) { create(:user) }
@@ -21,10 +21,9 @@ RSpec.describe WorkItems::Widgets::AssigneesService::UpdateService, :freeze_time
end
describe '#before_update_in_transaction' do
- subject do
- described_class.new(widget: widget, current_user: current_user)
- .before_update_in_transaction(params: params)
- end
+ let(:service) { described_class.new(widget: widget, current_user: current_user) }
+
+ subject { service.before_update_in_transaction(params: params) }
it 'updates the assignees and sets updated_at to the current time' do
subject
@@ -112,5 +111,20 @@ RSpec.describe WorkItems::Widgets::AssigneesService::UpdateService, :freeze_time
expect(work_item.updated_at).to be_like_time(1.day.ago)
end
end
+
+ context 'when widget does not exist in new type' do
+ let(:params) { {} }
+
+ before do
+ allow(service).to receive(:new_type_excludes_widget?).and_return(true)
+ work_item.assignee_ids = [new_assignee.id]
+ end
+
+ it "resets the work item's assignees" do
+ subject
+
+ expect(work_item.assignee_ids).to be_empty
+ end
+ end
end
end
diff --git a/spec/services/work_items/widgets/award_emoji_service/update_service_spec.rb b/spec/services/work_items/widgets/award_emoji_service/update_service_spec.rb
new file mode 100644
index 00000000000..186e4d56cc4
--- /dev/null
+++ b/spec/services/work_items/widgets/award_emoji_service/update_service_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::AwardEmojiService::UpdateService, feature_category: :team_planning do
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:unauthorized_user) { create(:user) }
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let(:current_user) { reporter }
+ let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::AwardEmoji) } }
+
+ before_all do
+ project.add_reporter(reporter)
+ end
+
+ describe '#before_update_in_transaction' do
+ subject do
+ described_class.new(widget: widget, current_user: current_user)
+ .before_update_in_transaction(params: params)
+ end
+
+ shared_examples 'raises a WidgetError' do
+ it { expect { subject }.to raise_error(described_class::WidgetError, message) }
+ end
+
+ context 'when awarding an emoji' do
+ let(:params) { { action: :add, name: 'star' } }
+
+ context 'when user has no access' do
+ let(:current_user) { unauthorized_user }
+
+ it 'does not award the emoji' do
+ expect { subject }.not_to change { AwardEmoji.count }
+ end
+ end
+
+ context 'when user has access' do
+ it 'awards the emoji to the work item' do
+ expect { subject }.to change { AwardEmoji.count }.by(1)
+
+ emoji = AwardEmoji.last
+
+ expect(emoji.name).to eq('star')
+ expect(emoji.awardable_id).to eq(work_item.id)
+ expect(emoji.user).to eq(current_user)
+ end
+
+ context 'when the name is incorrect' do
+ let(:params) { { action: :add, name: 'foo' } }
+
+ it_behaves_like 'raises a WidgetError' do
+ let(:message) { 'Name is not a valid emoji name' }
+ end
+ end
+
+ context 'when the action is incorrect' do
+ let(:params) { { action: :foo, name: 'star' } }
+
+ it_behaves_like 'raises a WidgetError' do
+ let(:message) { 'foo is not a valid action.' }
+ end
+ end
+ end
+ end
+
+ context 'when removing emoji' do
+ let(:params) { { action: :remove, name: 'thumbsup' } }
+
+ context 'when user has no access' do
+ let(:current_user) { unauthorized_user }
+
+ it 'does not remove the emoji' do
+ expect { subject }.not_to change { AwardEmoji.count }
+ end
+ end
+
+ context 'when user has access' do
+ it 'removes existing emoji' do
+ create(:award_emoji, :upvote, awardable: work_item, user: current_user)
+
+ expect { subject }.to change { AwardEmoji.count }.by(-1)
+ end
+
+ context 'when work item does not have the emoji' do
+ let(:params) { { action: :remove, name: 'star' } }
+
+ it_behaves_like 'raises a WidgetError' do
+ let(:message) { 'User has not awarded emoji of type star on the awardable' }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb b/spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb
new file mode 100644
index 00000000000..85b7e7a70df
--- /dev/null
+++ b/spec/services/work_items/widgets/current_user_todos_service/update_service_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::CurrentUserTodosService::UpdateService, feature_category: :team_planning do
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:current_user) { reporter }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let_it_be(:pending_todo1) do
+ create(:todo, state: :pending, target: work_item, target_type: work_item.class.name, user: current_user)
+ end
+
+ let_it_be(:pending_todo2) do
+ create(:todo, state: :pending, target: work_item, target_type: work_item.class.name, user: current_user)
+ end
+
+ let_it_be(:done_todo) do
+ create(:todo, state: :done, target: work_item, target_type: work_item.class.name, user: current_user)
+ end
+
+ let_it_be(:other_work_item_todo) { create(:todo, state: :pending, target: create(:work_item), user: current_user) }
+ let_it_be(:other_user_todo) do
+ create(:todo, state: :pending, target: work_item, target_type: work_item.class.name, user: create(:user))
+ end
+
+ let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::CurrentUserTodos) } }
+
+ before_all do
+ project.add_reporter(reporter)
+ end
+
+ describe '#before_update_in_transaction' do
+ subject do
+ described_class.new(widget: widget, current_user: current_user)
+ .before_update_in_transaction(params: params)
+ end
+
+ context 'when adding a todo' do
+ let(:params) { { action: "add" } }
+
+ context 'when user has no access' do
+ let(:current_user) { create(:user) }
+
+ it 'does add a todo' do
+ expect { subject }.not_to change { Todo.count }
+ end
+ end
+
+ context 'when user has access' do
+ let(:params) { { action: "add" } }
+
+ it 'creates a new todo for the user and the work item' do
+ expect { subject }.to change { current_user.todos.count }.by(1)
+
+ todo = current_user.todos.last
+
+ expect(todo.target).to eq(work_item)
+ expect(todo).to be_pending
+ end
+ end
+ end
+
+ context 'when marking as done' do
+ let(:params) { { action: "mark_as_done" } }
+
+ context 'when user has no access' do
+ let(:current_user) { create(:user) }
+
+ it 'does not change todo status' do
+ subject
+
+ expect(pending_todo1.reload).to be_pending
+ expect(pending_todo2.reload).to be_pending
+ expect(other_work_item_todo.reload).to be_pending
+ expect(other_user_todo.reload).to be_pending
+ end
+ end
+
+ context 'when resolving all todos of the work item', :aggregate_failures do
+ it 'resolves todos of the user for the work item' do
+ subject
+
+ expect(pending_todo1.reload).to be_done
+ expect(pending_todo2.reload).to be_done
+ expect(other_work_item_todo.reload).to be_pending
+ expect(other_user_todo.reload).to be_pending
+ end
+ end
+
+ context 'when resolving a specific todo', :aggregate_failures do
+ let(:params) { { action: "mark_as_done", todo_id: pending_todo1.id } }
+
+ it 'resolves todos of the user for the work item' do
+ subject
+
+ expect(pending_todo1.reload).to be_done
+ expect(pending_todo2.reload).to be_pending
+ expect(other_work_item_todo.reload).to be_pending
+ expect(other_user_todo.reload).to be_pending
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/widgets/description_service/update_service_spec.rb b/spec/services/work_items/widgets/description_service/update_service_spec.rb
index 4275950e720..7da5b24a3b7 100644
--- a/spec/services/work_items/widgets/description_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/description_service/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
+RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService, feature_category: :portfolio_management do
let_it_be(:random_user) { create(:user) }
let_it_be(:author) { create(:user) }
let_it_be(:guest) { create(:user) }
@@ -20,7 +20,9 @@ RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Description) } }
describe '#update' do
- subject { described_class.new(widget: widget, current_user: current_user).before_update_callback(params: params) }
+ let(:service) { described_class.new(widget: widget, current_user: current_user) }
+
+ subject(:before_update_callback) { service.before_update_callback(params: params) }
shared_examples 'sets work item description' do
it 'correctly sets work item description value' do
@@ -78,6 +80,23 @@ RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
it_behaves_like 'does not set work item description'
end
+
+ context 'when widget does not exist in new type' do
+ let(:current_user) { author }
+ let(:params) { {} }
+
+ before do
+ allow(service).to receive(:new_type_excludes_widget?).and_return(true)
+ work_item.update!(description: 'test')
+ end
+
+ it "resets the work item's description" do
+ expect { before_update_callback }
+ .to change { work_item.description }
+ .from('test')
+ .to(nil)
+ end
+ end
end
context 'when user does not have permission to update description' do
diff --git a/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb
new file mode 100644
index 00000000000..8d834c9a4f8
--- /dev/null
+++ b/spec/services/work_items/widgets/hierarchy_service/create_service_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::HierarchyService::CreateService, feature_category: :portfolio_management do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:parent_item) { create(:work_item, project: project) }
+
+ let(:widget) { parent_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } }
+
+ shared_examples 'raises a WidgetError' do
+ it { expect { subject }.to raise_error(described_class::WidgetError, message) }
+ end
+
+ before(:all) do
+ project.add_developer(user)
+ end
+
+ describe '#create' do
+ subject { described_class.new(widget: widget, current_user: user).after_create_in_transaction(params: params) }
+
+ context 'when invalid params are present' do
+ let(:params) { { other_parent: 'parent_work_item' } }
+
+ it_behaves_like 'raises a WidgetError' do
+ let(:message) { 'One or more arguments are invalid: other_parent.' }
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
index 6285b43311d..229ba81d676 100644
--- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb
@@ -14,7 +14,13 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Hierarchy) } }
let(:not_found_error) { 'No matching work item found. Make sure that you are adding a valid work item ID.' }
- shared_examples 'raises a WidgetError' do
+ shared_examples 'raises a WidgetError' do |message|
+ it { expect { subject }.to raise_error(described_class::WidgetError, message) }
+ end
+
+ shared_examples 'raises a WidgetError with message' do
+ let(:message) { not_found_error }
+
it { expect { subject }.to raise_error(described_class::WidgetError, message) }
end
@@ -24,16 +30,30 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
context 'when parent and children params are present' do
let(:params) { { parent: parent_work_item, children: [child_work_item] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'A Work Item can be a parent or a child, but not both.' }
- end
+ it_behaves_like 'raises a WidgetError', 'A Work Item can be a parent or a child, but not both.'
end
context 'when invalid params are present' do
let(:params) { { other_parent: parent_work_item } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'One or more arguments are invalid: other_parent.' }
+ it_behaves_like 'raises a WidgetError', 'One or more arguments are invalid: other_parent.'
+ end
+
+ context 'when relative position params are incomplete' do
+ context 'when only adjacent_work_item is present' do
+ let(:params) do
+ { parent: parent_work_item, adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::INVALID_RELATIVE_POSITION_ERROR
+ end
+
+ context 'when only relative_position is present' do
+ let(:params) do
+ { parent: parent_work_item, relative_position: 'AFTER' }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::INVALID_RELATIVE_POSITION_ERROR
end
end
@@ -45,7 +65,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
context 'when user has insufficient permissions to link work items' do
let(:params) { { children: [child_work_item4] } }
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises a WidgetError with message' do
let(:message) { not_found_error }
end
end
@@ -55,7 +75,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
project.add_developer(user)
end
- context 'with valid params' do
+ context 'with valid children params' do
let(:params) { { children: [child_work_item2, child_work_item3] } }
it 'correctly sets work item parent' do
@@ -64,14 +84,30 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
expect(work_item.reload.work_item_children)
.to contain_exactly(child_work_item, child_work_item2, child_work_item3)
end
+
+ context 'when relative_position and adjacent_work_item are given' do
+ context 'with BEFORE value' do
+ let(:params) do
+ { children: [child_work_item3], relative_position: 'BEFORE', adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::CHILDREN_REORDERING_ERROR
+ end
+
+ context 'with AFTER value' do
+ let(:params) do
+ { children: [child_work_item2], relative_position: 'AFTER', adjacent_work_item: child_work_item }
+ end
+
+ it_behaves_like 'raises a WidgetError', described_class::CHILDREN_REORDERING_ERROR
+ end
+ end
end
context 'when child is already assigned' do
let(:params) { { children: [child_work_item] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) { 'Work item(s) already assigned' }
- end
+ it_behaves_like 'raises a WidgetError', 'Work item(s) already assigned'
end
context 'when child type is invalid' do
@@ -79,10 +115,8 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { children: [child_issue] } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) do
- "#{child_issue.to_reference} cannot be added: is not allowed to add this type of parent"
- end
+ it_behaves_like 'raises a WidgetError with message' do
+ let(:message) { "#{child_issue.to_reference} cannot be added: is not allowed to add this type of parent" }
end
end
end
@@ -94,7 +128,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { parent: parent_work_item } }
context 'when user has insufficient permissions to link work items' do
- it_behaves_like 'raises a WidgetError' do
+ it_behaves_like 'raises a WidgetError with message' do
let(:message) { not_found_error }
end
end
@@ -121,7 +155,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
end.to change(work_item, :work_item_parent).from(parent_work_item).to(nil)
end
- it 'returns success status if parent not present', :aggregate_failure do
+ it 'returns success status if parent not present', :aggregate_failures do
work_item.update!(work_item_parent: nil)
expect(subject[:status]).to eq(:success)
@@ -134,10 +168,34 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService, feature_cate
let(:params) { { parent: parent_task } }
- it_behaves_like 'raises a WidgetError' do
- let(:message) do
- "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent"
+ it_behaves_like 'raises a WidgetError with message' do
+ let(:message) { "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent" }
+ end
+ end
+
+ context 'with positioning arguments' do
+ let_it_be_with_reload(:adjacent) { create(:work_item, :task, project: project) }
+
+ let_it_be_with_reload(:adjacent_link) do
+ create(:parent_link, work_item: adjacent, work_item_parent: parent_work_item)
+ end
+
+ let(:params) { { parent: parent_work_item, adjacent_work_item: adjacent, relative_position: 'AFTER' } }
+
+ it 'correctly sets new parent and position' do
+ expect(subject[:status]).to eq(:success)
+ expect(work_item.work_item_parent).to eq(parent_work_item)
+ expect(work_item.parent_link.relative_position).to be > adjacent_link.relative_position
+ end
+
+ context 'when other hierarchy adjacent is provided' do
+ let_it_be(:other_hierarchy_adjacent) { create(:parent_link).work_item }
+
+ let(:params) do
+ { parent: parent_work_item, adjacent_work_item: other_hierarchy_adjacent, relative_position: 'AFTER' }
end
+
+ it_behaves_like 'raises a WidgetError', described_class::UNRELATED_ADJACENT_HIERARCHY_ERROR
end
end
end
diff --git a/spec/services/work_items/widgets/labels_service/update_service_spec.rb b/spec/services/work_items/widgets/labels_service/update_service_spec.rb
new file mode 100644
index 00000000000..17daec2b1ea
--- /dev/null
+++ b/spec/services/work_items/widgets/labels_service/update_service_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::LabelsService::UpdateService, feature_category: :team_planning do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:label1) { create(:label, project: project) }
+ let_it_be(:label2) { create(:label, project: project) }
+ let_it_be(:label3) { create(:label, project: project) }
+ let_it_be(:current_user) { create(:user) }
+
+ let(:work_item) { create(:work_item, project: project, labels: [label1, label2]) }
+ let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Labels) } }
+ let(:service) { described_class.new(widget: widget, current_user: current_user) }
+
+ describe '#prepare_update_params' do
+ context 'when params are set' do
+ let(:params) { { add_label_ids: [label1.id], remove_label_ids: [label2.id] } }
+
+ it "sets params correctly" do
+ expect(service.prepare_update_params(params: params)).to include(
+ {
+ add_label_ids: match_array([label1.id]),
+ remove_label_ids: match_array([label2.id])
+ }
+ )
+ end
+ end
+
+ context 'when widget does not exist in new type' do
+ let(:params) { {} }
+
+ before do
+ allow(service).to receive(:new_type_excludes_widget?).and_return(true)
+ end
+
+ it "sets correct params to remove work item labels" do
+ expect(service.prepare_update_params(params: params)).to include(
+ {
+ remove_label_ids: match_array([label1.id, label2.id]),
+ add_label_ids: []
+ }
+ )
+ end
+ end
+ end
+end
diff --git a/spec/services/work_items/widgets/milestone_service/create_service_spec.rb b/spec/services/work_items/widgets/milestone_service/create_service_spec.rb
deleted file mode 100644
index 3f90784b703..00000000000
--- a/spec/services/work_items/widgets/milestone_service/create_service_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe WorkItems::Widgets::MilestoneService::CreateService do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :private, group: group) }
- let_it_be(:project_milestone) { create(:milestone, project: project) }
- let_it_be(:group_milestone) { create(:milestone, group: group) }
- let_it_be(:guest) { create(:user) }
-
- let(:current_user) { guest }
- let(:work_item) { build(:work_item, project: project, updated_at: 1.day.ago) }
- let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Milestone) } }
- let(:service) { described_class.new(widget: widget, current_user: current_user) }
-
- before do
- project.add_guest(guest)
- end
-
- describe '#before_create_callback' do
- it_behaves_like "setting work item's milestone" do
- subject(:execute_callback) do
- service.before_create_callback(params: params)
- end
- end
- end
-end
diff --git a/spec/services/work_items/widgets/milestone_service/update_service_spec.rb b/spec/services/work_items/widgets/milestone_service/update_service_spec.rb
deleted file mode 100644
index f3a7fc156b9..00000000000
--- a/spec/services/work_items/widgets/milestone_service/update_service_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe WorkItems::Widgets::MilestoneService::UpdateService do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :private, group: group) }
- let_it_be(:project_milestone) { create(:milestone, project: project) }
- let_it_be(:group_milestone) { create(:milestone, group: group) }
- let_it_be(:reporter) { create(:user) }
- let_it_be(:guest) { create(:user) }
-
- let(:work_item) { create(:work_item, project: project, updated_at: 1.day.ago) }
- let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Milestone) } }
- let(:service) { described_class.new(widget: widget, current_user: current_user) }
-
- before do
- project.add_reporter(reporter)
- project.add_guest(guest)
- end
-
- describe '#before_update_callback' do
- context 'when current user is not allowed to set work item metadata' do
- let(:current_user) { guest }
- let(:params) { { milestone_id: group_milestone.id } }
-
- it "does not set the work item's milestone" do
- expect { service.before_update_callback(params: params) }
- .to not_change(work_item, :milestone)
- end
- end
-
- context "when current user is allowed to set work item metadata" do
- let(:current_user) { reporter }
-
- it_behaves_like "setting work item's milestone" do
- subject(:execute_callback) do
- service.before_update_callback(params: params)
- end
- end
-
- context 'when unsetting a milestone' do
- let(:params) { { milestone_id: nil } }
-
- before do
- work_item.update!(milestone: project_milestone)
- end
-
- it "sets the work item's milestone" do
- expect { service.before_update_callback(params: params) }
- .to change(work_item, :milestone)
- .from(project_milestone)
- .to(nil)
- end
- end
- end
- end
-end
diff --git a/spec/services/work_items/widgets/notifications_service/update_service_spec.rb b/spec/services/work_items/widgets/notifications_service/update_service_spec.rb
new file mode 100644
index 00000000000..9615020fe49
--- /dev/null
+++ b/spec/services/work_items/widgets/notifications_service/update_service_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::NotificationsService::UpdateService, feature_category: :team_planning do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :private, group: group) }
+ let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let_it_be(:author) { create(:user).tap { |u| project.add_guest(u) } }
+ let_it_be_with_reload(:work_item) { create(:work_item, project: project, author: author) }
+ let_it_be(:current_user) { guest }
+
+ let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Notifications) } }
+ let(:service) { described_class.new(widget: widget, current_user: current_user) }
+
+ describe '#before_update_in_transaction' do
+ let(:expected) { params[:subscribed] }
+
+ subject(:update_notifications) { service.before_update_in_transaction(params: params) }
+
+ shared_examples 'failing to update subscription' do
+ context 'when user is subscribed with a subscription record' do
+ let_it_be(:subscription) { create_subscription(:subscribed) }
+
+ it "does not update the work item's subscription" do
+ expect do
+ update_notifications
+ subscription.reload
+ end.to not_change { subscription.subscribed }
+ .and(not_change { work_item.subscribed?(current_user, project) })
+ end
+ end
+
+ context 'when user is subscribed by being a participant' do
+ let_it_be(:current_user) { author }
+
+ it 'does not create subscription record or change subscription state' do
+ expect { update_notifications }
+ .to not_change { Subscription.count }
+ .and(not_change { work_item.subscribed?(current_user, project) })
+ end
+ end
+ end
+
+ shared_examples 'updating notifications subscription successfully' do
+ it 'updates existing subscription record' do
+ expect do
+ update_notifications
+ subscription.reload
+ end.to change { subscription.subscribed }.to(expected)
+ .and(change { work_item.subscribed?(current_user, project) }.to(expected))
+ end
+ end
+
+ context 'when update fails' do
+ context 'when user lack update_subscription permissions' do
+ let_it_be(:params) { { subscribed: false } }
+
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(current_user, :update_subscription, work_item)
+ .and_return(false)
+ end
+
+ it_behaves_like 'failing to update subscription'
+ end
+
+ context 'when notifications params are not present' do
+ let_it_be(:params) { {} }
+
+ it_behaves_like 'failing to update subscription'
+ end
+ end
+
+ context 'when update is successful' do
+ context 'when subscribing' do
+ let_it_be(:subscription) { create_subscription(:unsubscribed) }
+ let(:params) { { subscribed: true } }
+
+ it_behaves_like 'updating notifications subscription successfully'
+ end
+
+ context 'when unsubscribing' do
+ let(:params) { { subscribed: false } }
+
+ context 'when user is subscribed with a subscription record' do
+ let_it_be(:subscription) { create_subscription(:subscribed) }
+
+ it_behaves_like 'updating notifications subscription successfully'
+ end
+
+ context 'when user is subscribed by being a participant' do
+ let_it_be(:current_user) { author }
+
+ it 'creates a subscription with expected value' do
+ expect { update_notifications }
+ .to change { Subscription.count }.by(1)
+ .and(change { work_item.subscribed?(current_user, project) }.to(expected))
+
+ expect(Subscription.last.subscribed).to eq(expected)
+ end
+ end
+ end
+ end
+ end
+
+ def create_subscription(state)
+ create(
+ :subscription,
+ project: project,
+ user: current_user,
+ subscribable: work_item,
+ subscribed: (state == :subscribed)
+ )
+ end
+end
diff --git a/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb b/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb
index d328c541fc7..0196e7c2b02 100644
--- a/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/start_and_due_date_service/update_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService do
+RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService, feature_category: :portfolio_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:work_item) { create(:work_item, project: project) }
@@ -12,10 +12,9 @@ RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService do
describe '#before_update_callback' do
let(:start_date) { Date.today }
let(:due_date) { 1.week.from_now.to_date }
+ let(:service) { described_class.new(widget: widget, current_user: user) }
- subject(:update_params) do
- described_class.new(widget: widget, current_user: user).before_update_callback(params: params)
- end
+ subject(:update_params) { service.before_update_callback(params: params) }
context 'when start and due date params are present' do
let(:params) { { start_date: Date.today, due_date: 1.week.from_now.to_date } }
@@ -58,5 +57,22 @@ RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService do
end
end
end
+
+ context 'when widget does not exist in new type' do
+ let(:params) { {} }
+
+ before do
+ allow(service).to receive(:new_type_excludes_widget?).and_return(true)
+ work_item.update!(start_date: start_date, due_date: due_date)
+ end
+
+ it 'sets both dates to null' do
+ expect do
+ update_params
+ end.to change(work_item, :start_date).from(start_date).to(nil).and(
+ change(work_item, :due_date).from(due_date).to(nil)
+ )
+ end
+ end
end
end
diff --git a/spec/services/x509_certificate_revoke_service_spec.rb b/spec/services/x509_certificate_revoke_service_spec.rb
index ff5d2dc058b..460381afd79 100644
--- a/spec/services/x509_certificate_revoke_service_spec.rb
+++ b/spec/services/x509_certificate_revoke_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe X509CertificateRevokeService do
+RSpec.describe X509CertificateRevokeService, feature_category: :system_access do
describe '#execute' do
let(:service) { described_class.new }
let!(:x509_signature_1) { create(:x509_commit_signature, x509_certificate: x509_certificate, verification_status: :verified) }