diff options
Diffstat (limited to 'spec/support')
85 files changed, 1715 insertions, 2001 deletions
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index ac35662ec93..14ef0f1b7e0 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -28,6 +28,8 @@ JS_CONSOLE_FILTER = Regexp.union([ CAPYBARA_WINDOW_SIZE = [1366, 768].freeze +SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 255 : 99 + # Run Workhorse on the given host and port, proxying to Puma on a UNIX socket, # for a closer-to-production experience Capybara.register_server :puma_via_workhorse do |app, port, host, **options| @@ -113,7 +115,7 @@ Capybara.enable_aria_label = true Capybara::Screenshot.append_timestamp = false Capybara::Screenshot.register_filename_prefix_formatter(:rspec) do |example| - example.full_description.downcase.parameterize(separator: "_")[0..99] + example.full_description.downcase.parameterize(separator: "_")[0..SCREENSHOT_FILENAME_LENGTH] end # Keep only the screenshots generated from the last failing test suite Capybara::Screenshot.prune_strategy = :keep_last_run diff --git a/spec/support/database/cross-database-modification-allowlist.yml b/spec/support/database/cross-database-modification-allowlist.yml index 627967f65f3..d05812a64eb 100644 --- a/spec/support/database/cross-database-modification-allowlist.yml +++ b/spec/support/database/cross-database-modification-allowlist.yml @@ -1,1343 +1,90 @@ -- "./ee/spec/controllers/admin/geo/nodes_controller_spec.rb" -- "./ee/spec/controllers/admin/geo/projects_controller_spec.rb" -- "./ee/spec/controllers/admin/projects_controller_spec.rb" -- "./ee/spec/controllers/concerns/internal_redirect_spec.rb" -- "./ee/spec/controllers/ee/projects/jobs_controller_spec.rb" -- "./ee/spec/controllers/oauth/geo_auth_controller_spec.rb" -- "./ee/spec/controllers/projects/approver_groups_controller_spec.rb" -- "./ee/spec/controllers/projects/approvers_controller_spec.rb" -- "./ee/spec/controllers/projects/merge_requests_controller_spec.rb" -- "./ee/spec/controllers/projects/merge_requests/creations_controller_spec.rb" - "./ee/spec/controllers/projects/settings/access_tokens_controller_spec.rb" -- "./ee/spec/controllers/projects/subscriptions_controller_spec.rb" -- "./ee/spec/features/account_recovery_regular_check_spec.rb" -- "./ee/spec/features/admin/admin_audit_logs_spec.rb" -- "./ee/spec/features/admin/admin_credentials_inventory_spec.rb" -- "./ee/spec/features/admin/admin_dashboard_spec.rb" -- "./ee/spec/features/admin/admin_dev_ops_report_spec.rb" -- "./ee/spec/features/admin/admin_merge_requests_approvals_spec.rb" -- "./ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb" -- "./ee/spec/features/admin/admin_sends_notification_spec.rb" -- "./ee/spec/features/admin/admin_settings_spec.rb" -- "./ee/spec/features/admin/admin_show_new_user_signups_cap_alert_spec.rb" -- "./ee/spec/features/admin/admin_users_spec.rb" -- "./ee/spec/features/admin/geo/admin_geo_nodes_spec.rb" -- "./ee/spec/features/admin/geo/admin_geo_projects_spec.rb" -- "./ee/spec/features/admin/geo/admin_geo_replication_nav_spec.rb" -- "./ee/spec/features/admin/geo/admin_geo_sidebar_spec.rb" -- "./ee/spec/features/admin/geo/admin_geo_uploads_spec.rb" -- "./ee/spec/features/admin/groups/admin_changes_plan_spec.rb" -- "./ee/spec/features/admin/licenses/admin_uploads_license_spec.rb" -- "./ee/spec/features/admin/licenses/show_user_count_threshold_spec.rb" -- "./ee/spec/features/admin/subscriptions/admin_views_subscription_spec.rb" -- "./ee/spec/features/analytics/code_analytics_spec.rb" -- "./ee/spec/features/billings/billing_plans_spec.rb" -- "./ee/spec/features/billings/extend_reactivate_trial_spec.rb" -- "./ee/spec/features/billings/qrtly_reconciliation_alert_spec.rb" -- "./ee/spec/features/boards/boards_licensed_features_spec.rb" -- "./ee/spec/features/boards/boards_spec.rb" -- "./ee/spec/features/boards/group_boards/board_deletion_spec.rb" -- "./ee/spec/features/boards/group_boards/multiple_boards_spec.rb" -- "./ee/spec/features/boards/new_issue_spec.rb" -- "./ee/spec/features/boards/scoped_issue_board_spec.rb" -- "./ee/spec/features/boards/sidebar_spec.rb" -- "./ee/spec/features/boards/swimlanes/epics_swimlanes_drag_drop_spec.rb" -- "./ee/spec/features/boards/swimlanes/epics_swimlanes_filtering_spec.rb" -- "./ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_labels_spec.rb" -- "./ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_spec.rb" -- "./ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb" -- "./ee/spec/features/boards/user_adds_lists_to_board_spec.rb" -- "./ee/spec/features/boards/user_visits_board_spec.rb" -- "./ee/spec/features/burndown_charts_spec.rb" -- "./ee/spec/features/burnup_charts_spec.rb" -- "./ee/spec/features/ci/ci_minutes_spec.rb" -- "./ee/spec/features/ci_shared_runner_warnings_spec.rb" -- "./ee/spec/features/clusters/create_agent_spec.rb" -- "./ee/spec/features/dashboards/activity_spec.rb" -- "./ee/spec/features/dashboards/groups_spec.rb" -- "./ee/spec/features/dashboards/issues_spec.rb" -- "./ee/spec/features/dashboards/merge_requests_spec.rb" -- "./ee/spec/features/dashboards/operations_spec.rb" -- "./ee/spec/features/dashboards/projects_spec.rb" -- "./ee/spec/features/dashboards/todos_spec.rb" -- "./ee/spec/features/discussion_comments/epic_quick_actions_spec.rb" -- "./ee/spec/features/discussion_comments/epic_spec.rb" -- "./ee/spec/features/epic_boards/epic_boards_sidebar_spec.rb" -- "./ee/spec/features/epic_boards/epic_boards_spec.rb" -- "./ee/spec/features/epic_boards/multiple_epic_boards_spec.rb" -- "./ee/spec/features/epic_boards/new_epic_spec.rb" -- "./ee/spec/features/epics/delete_epic_spec.rb" -- "./ee/spec/features/epics/epic_issues_spec.rb" -- "./ee/spec/features/epics/epic_labels_spec.rb" -- "./ee/spec/features/epics/epic_show_spec.rb" -- "./ee/spec/features/epics/epics_list_spec.rb" -- "./ee/spec/features/epics/filtered_search/visual_tokens_spec.rb" -- "./ee/spec/features/epics/gfm_autocomplete_spec.rb" -- "./ee/spec/features/epics/issue_promotion_spec.rb" -- "./ee/spec/features/epics/referencing_epics_spec.rb" -- "./ee/spec/features/epics/shortcuts_epic_spec.rb" -- "./ee/spec/features/epics/todo_spec.rb" -- "./ee/spec/features/epics/update_epic_spec.rb" -- "./ee/spec/features/epics/user_uses_quick_actions_spec.rb" -- "./ee/spec/features/geo_node_spec.rb" -- "./ee/spec/features/groups/analytics/ci_cd_analytics_spec.rb" -- "./ee/spec/features/groups/analytics/cycle_analytics/charts_spec.rb" -- "./ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb" -- "./ee/spec/features/groups/analytics/cycle_analytics/multiple_value_streams_spec.rb" -- "./ee/spec/features/groups/audit_events_spec.rb" -- "./ee/spec/features/groups/billing_spec.rb" -- "./ee/spec/features/groups/contribution_analytics_spec.rb" -- "./ee/spec/features/groups/group_overview_spec.rb" -- "./ee/spec/features/groups/group_roadmap_spec.rb" -- "./ee/spec/features/groups/group_settings_spec.rb" -- "./ee/spec/features/groups/groups_security_credentials_spec.rb" -- "./ee/spec/features/groups/hooks/user_tests_hooks_spec.rb" -- "./ee/spec/features/groups/insights_spec.rb" -- "./ee/spec/features/groups/issues_spec.rb" -- "./ee/spec/features/groups/iterations/iterations_list_spec.rb" -- "./ee/spec/features/groups/iteration_spec.rb" -- "./ee/spec/features/groups/iterations/user_creates_iteration_in_cadence_spec.rb" -- "./ee/spec/features/groups/iterations/user_edits_iteration_cadence_spec.rb" -- "./ee/spec/features/groups/iterations/user_edits_iteration_spec.rb" -- "./ee/spec/features/groups/iterations/user_views_iteration_cadence_spec.rb" -- "./ee/spec/features/groups/iterations/user_views_iteration_spec.rb" -- "./ee/spec/features/groups/ldap_group_links_spec.rb" -- "./ee/spec/features/groups/ldap_settings_spec.rb" -- "./ee/spec/features/groups/members/leave_group_spec.rb" -- "./ee/spec/features/groups/members/list_members_spec.rb" -- "./ee/spec/features/groups/members/override_ldap_memberships_spec.rb" -- "./ee/spec/features/groups/new_spec.rb" -- "./ee/spec/features/groups/push_rules_spec.rb" -- "./ee/spec/features/groups/saml_providers_spec.rb" -- "./ee/spec/features/groups/scim_token_spec.rb" -- "./ee/spec/features/groups/seat_usage/seat_usage_spec.rb" -- "./ee/spec/features/groups/security/compliance_dashboards_spec.rb" -- "./ee/spec/features/groups/settings/user_configures_insights_spec.rb" -- "./ee/spec/features/groups/settings/user_searches_in_settings_spec.rb" -- "./ee/spec/features/groups/sso_spec.rb" -- "./ee/spec/features/groups/wikis_spec.rb" -- "./ee/spec/features/groups/wiki/user_views_wiki_empty_spec.rb" -- "./ee/spec/features/ide/user_commits_changes_spec.rb" -- "./ee/spec/features/ide/user_opens_ide_spec.rb" -- "./ee/spec/features/integrations/jira/jira_issues_list_spec.rb" -- "./ee/spec/features/issues/blocking_issues_spec.rb" -- "./ee/spec/features/issues/epic_in_issue_sidebar_spec.rb" -- "./ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb" -- "./ee/spec/features/issues/filtered_search/filter_issues_epic_spec.rb" -- "./ee/spec/features/issues/filtered_search/filter_issues_weight_spec.rb" -- "./ee/spec/features/issues/form_spec.rb" -- "./ee/spec/features/issues/gfm_autocomplete_ee_spec.rb" -- "./ee/spec/features/issues/issue_actions_spec.rb" -- "./ee/spec/features/issues/issue_sidebar_spec.rb" -- "./ee/spec/features/issues/move_issue_resource_weight_events_spec.rb" -- "./ee/spec/features/issues/related_issues_spec.rb" -- "./ee/spec/features/issues/resource_weight_events_spec.rb" -- "./ee/spec/features/issues/user_bulk_edits_issues_spec.rb" -- "./ee/spec/features/issues/user_edits_issue_spec.rb" -- "./ee/spec/features/issues/user_uses_quick_actions_spec.rb" -- "./ee/spec/features/issues/user_views_issues_spec.rb" -- "./ee/spec/features/labels_hierarchy_spec.rb" -- "./ee/spec/features/markdown/metrics_spec.rb" -- "./ee/spec/features/merge_requests/user_filters_by_approvers_spec.rb" -- "./ee/spec/features/merge_requests/user_resets_approvers_spec.rb" -- "./ee/spec/features/merge_requests/user_views_all_merge_requests_spec.rb" -- "./ee/spec/features/merge_request/user_approves_with_password_spec.rb" -- "./ee/spec/features/merge_request/user_creates_merge_request_spec.rb" -- "./ee/spec/features/merge_request/user_creates_merge_request_with_blocking_mrs_spec.rb" -- "./ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb" -- "./ee/spec/features/merge_request/user_creates_multiple_reviewers_mr_spec.rb" -- "./ee/spec/features/merge_request/user_edits_approval_rules_mr_spec.rb" -- "./ee/spec/features/merge_request/user_edits_merge_request_blocking_mrs_spec.rb" -- "./ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb" -- "./ee/spec/features/merge_request/user_edits_multiple_reviewers_mr_spec.rb" -- "./ee/spec/features/merge_request/user_merges_immediately_spec.rb" -- "./ee/spec/features/merge_request/user_merges_with_push_rules_spec.rb" -- "./ee/spec/features/merge_request/user_sees_approval_widget_spec.rb" -- "./ee/spec/features/merge_request/user_sees_closing_issues_message_spec.rb" -- "./ee/spec/features/merge_request/user_sees_merge_widget_spec.rb" -- "./ee/spec/features/merge_request/user_sees_status_checks_widget_spec.rb" -- "./ee/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb" -- "./ee/spec/features/merge_request/user_sets_approval_rules_spec.rb" -- "./ee/spec/features/merge_request/user_sets_approvers_spec.rb" -- "./ee/spec/features/merge_request/user_uses_slash_commands_spec.rb" -- "./ee/spec/features/merge_request/user_views_blocked_merge_request_spec.rb" -- "./ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb" -- "./ee/spec/features/merge_trains/user_adds_to_merge_train_when_pipeline_succeeds_spec.rb" -- "./ee/spec/features/oncall_schedules/user_creates_schedule_spec.rb" -- "./ee/spec/features/operations_nav_link_spec.rb" -- "./ee/spec/features/profiles/account_spec.rb" -- "./ee/spec/features/profiles/billing_spec.rb" -- "./ee/spec/features/projects/audit_events_spec.rb" -- "./ee/spec/features/projects/cluster_agents_spec.rb" -- "./ee/spec/features/projects/custom_projects_template_spec.rb" -- "./ee/spec/features/projects/environments/environments_spec.rb" -- "./ee/spec/features/projects/feature_flags/feature_flag_issues_spec.rb" -- "./ee/spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb" -- "./ee/spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb" -- "./ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb" -- "./ee/spec/features/projects/insights_spec.rb" -- "./ee/spec/features/projects/integrations/user_activates_jira_spec.rb" -- "./ee/spec/features/projects/issues/user_creates_issue_spec.rb" -- "./ee/spec/features/projects/iterations/iteration_cadences_list_spec.rb" -- "./ee/spec/features/projects/iterations/iterations_list_spec.rb" -- "./ee/spec/features/projects/iterations/user_views_iteration_spec.rb" -- "./ee/spec/features/projects/jobs_spec.rb" -- "./ee/spec/features/projects/kerberos_clone_instructions_spec.rb" -- "./ee/spec/features/projects/licenses/maintainer_views_policies_spec.rb" -- "./ee/spec/features/projects/members/member_is_removed_from_project_spec.rb" -- "./ee/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb" -- "./ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb" -- "./ee/spec/features/projects/mirror_spec.rb" -- "./ee/spec/features/projects/new_project_from_template_spec.rb" -- "./ee/spec/features/projects/new_project_spec.rb" -- "./ee/spec/features/projects/path_locks_spec.rb" -- "./ee/spec/features/projects/pipelines/pipeline_spec.rb" -- "./ee/spec/features/projects/push_rules_spec.rb" -- "./ee/spec/features/projects/quality/test_case_create_spec.rb" -- "./ee/spec/features/projects/quality/test_case_list_spec.rb" -- "./ee/spec/features/projects/quality/test_case_show_spec.rb" -- "./ee/spec/features/projects/releases/user_views_release_spec.rb" -- "./ee/spec/features/projects/requirements_management/requirements_list_spec.rb" -- "./ee/spec/features/projects/security/dast_scanner_profiles_spec.rb" -- "./ee/spec/features/projects/security/dast_site_profiles_spec.rb" -- "./ee/spec/features/projects/security/user_creates_on_demand_scan_spec.rb" -- "./ee/spec/features/projects/security/user_views_security_configuration_spec.rb" -- "./ee/spec/features/projects/services/prometheus_custom_metrics_spec.rb" -- "./ee/spec/features/projects/services/user_activates_github_spec.rb" -- "./ee/spec/features/projects/settings/disable_merge_trains_setting_spec.rb" -- "./ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb" -- "./ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb" -- "./ee/spec/features/projects/settings/issues_settings_spec.rb" -- "./ee/spec/features/projects/settings/merge_request_approvals_settings_spec.rb" -- "./ee/spec/features/projects/settings/merge_requests_settings_spec.rb" -- "./ee/spec/features/projects/settings/pipeline_subscriptions_spec.rb" -- "./ee/spec/features/projects/settings/protected_environments_spec.rb" -- "./ee/spec/features/projects/settings/user_manages_merge_pipelines_spec.rb" -- "./ee/spec/features/projects/settings/user_manages_merge_trains_spec.rb" -- "./ee/spec/features/projects_spec.rb" -- "./ee/spec/features/projects/user_applies_custom_file_template_spec.rb" -- "./ee/spec/features/projects/view_blob_with_code_owners_spec.rb" -- "./ee/spec/features/projects/wiki/user_views_wiki_empty_spec.rb" -- "./ee/spec/features/promotion_spec.rb" -- "./ee/spec/features/protected_branches_spec.rb" -- "./ee/spec/features/protected_tags_spec.rb" -- "./ee/spec/features/registrations/combined_registration_spec.rb" -- "./ee/spec/features/registrations/trial_during_signup_flow_spec.rb" -- "./ee/spec/features/registrations/user_sees_new_onboarding_flow_spec.rb" -- "./ee/spec/features/registrations/welcome_spec.rb" -- "./ee/spec/features/search/elastic/global_search_spec.rb" -- "./ee/spec/features/search/elastic/group_search_spec.rb" -- "./ee/spec/features/search/elastic/project_search_spec.rb" -- "./ee/spec/features/search/elastic/snippet_search_spec.rb" -- "./ee/spec/features/search/user_searches_for_epics_spec.rb" -- "./ee/spec/features/subscriptions/groups/edit_spec.rb" -- "./ee/spec/features/trial_registrations/signup_spec.rb" -- "./ee/spec/features/trials/capture_lead_spec.rb" -- "./ee/spec/features/trials/select_namespace_spec.rb" -- "./ee/spec/features/trials/show_trial_banner_spec.rb" -- "./ee/spec/features/users/login_spec.rb" -- "./ee/spec/finders/geo/attachment_legacy_registry_finder_spec.rb" -- "./ee/spec/finders/geo/container_repository_registry_finder_spec.rb" -- "./ee/spec/finders/geo/lfs_object_registry_finder_spec.rb" -- "./ee/spec/finders/geo/merge_request_diff_registry_finder_spec.rb" -- "./ee/spec/finders/geo/package_file_registry_finder_spec.rb" -- "./ee/spec/finders/geo/pages_deployment_registry_finder_spec.rb" -- "./ee/spec/finders/geo/pipeline_artifact_registry_finder_spec.rb" -- "./ee/spec/finders/geo/project_registry_finder_spec.rb" -- "./ee/spec/finders/merge_requests/by_approvers_finder_spec.rb" -- "./ee/spec/frontend/fixtures/analytics/value_streams.rb" -- "./ee/spec/graphql/mutations/dast_on_demand_scans/create_spec.rb" -- "./ee/spec/graphql/mutations/dast/profiles/create_spec.rb" -- "./ee/spec/graphql/mutations/dast/profiles/run_spec.rb" -- "./ee/spec/graphql/mutations/dast/profiles/update_spec.rb" -- "./ee/spec/graphql/mutations/merge_requests/accept_spec.rb" -- "./ee/spec/graphql/resolvers/geo/group_wiki_repository_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/lfs_object_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/merge_request_diff_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/package_file_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/pages_deployment_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/pipeline_artifact_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/snippet_repository_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/terraform_state_version_registries_resolver_spec.rb" -- "./ee/spec/graphql/resolvers/geo/upload_registries_resolver_spec.rb" -- "./ee/spec/helpers/application_helper_spec.rb" -- "./ee/spec/helpers/ee/geo_helper_spec.rb" -- "./ee/spec/lib/analytics/devops_adoption/snapshot_calculator_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/backfill_version_data_from_gitaly_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/create_security_setting_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/fix_ruby_object_in_audit_events_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/migrate_devops_segments_to_groups_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/migrate_security_scans_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/move_epic_issues_after_epics_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_any_approval_rule_for_merge_requests_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_any_approval_rule_for_projects_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_namespace_statistics_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/populate_vulnerability_historical_statistics_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/prune_orphaned_geo_events_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_duplicate_cs_findings_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_inaccessible_epic_todos_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_undefined_occurrence_confidence_level_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_undefined_occurrence_severity_level_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_undefined_vulnerability_confidence_level_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/remove_undefined_vulnerability_severity_level_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/update_vulnerabilities_to_dismissed_spec.rb" -- "./ee/spec/lib/ee/gitlab/background_migration/update_vulnerability_confidence_spec.rb" -- "./ee/spec/lib/ee/gitlab/database/connection_spec.rb" -- "./ee/spec/lib/ee/gitlab/database_spec.rb" -- "./ee/spec/lib/ee/gitlab/middleware/read_only_spec.rb" -- "./ee/spec/lib/ee/gitlab/usage_data_spec.rb" -- "./ee/spec/lib/gitlab/background_migration/fix_orphan_promoted_issues_spec.rb" -- "./ee/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb" - "./ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb" -- "./ee/spec/lib/gitlab/geo/base_request_spec.rb" -- "./ee/spec/lib/gitlab/geo/database_tasks_spec.rb" -- "./ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb" -- "./ee/spec/lib/gitlab/geo/geo_tasks_spec.rb" -- "./ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb" -- "./ee/spec/lib/gitlab/geo/log_cursor/events/design_repository_updated_event_spec.rb" -- "./ee/spec/lib/gitlab/geo/log_cursor/events/job_artifact_deleted_event_spec.rb" -- "./ee/spec/lib/gitlab/geo/log_cursor/events/repository_created_event_spec.rb" -- "./ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb" -- "./ee/spec/lib/gitlab/geo/oauth/login_state_spec.rb" -- "./ee/spec/lib/gitlab/geo/oauth/logout_token_spec.rb" -- "./ee/spec/lib/gitlab/geo/oauth/session_spec.rb" -- "./ee/spec/lib/gitlab/geo/registry_batcher_spec.rb" -- "./ee/spec/lib/gitlab/geo/replicable_model_spec.rb" -- "./ee/spec/lib/gitlab/geo/replication/blob_downloader_spec.rb" -- "./ee/spec/lib/gitlab/geo/replication/file_transfer_spec.rb" -- "./ee/spec/lib/gitlab/geo/replicator_spec.rb" -- "./ee/spec/lib/gitlab/git_access_spec.rb" -- "./ee/spec/lib/pseudonymizer/dumper_spec.rb" -- "./ee/spec/lib/system_check/geo/geo_database_configured_check_spec.rb" -- "./ee/spec/lib/system_check/geo/http_connection_check_spec.rb" -- "./ee/spec/lib/system_check/rake_task/geo_task_spec.rb" - "./ee/spec/mailers/notify_spec.rb" -- "./ee/spec/migrations/20190926180443_schedule_epic_issues_after_epics_move_spec.rb" -- "./ee/spec/migrations/add_non_null_constraint_for_escalation_rule_on_pending_alert_escalations_spec.rb" -- "./ee/spec/migrations/add_unique_constraint_to_software_licenses_spec.rb" -- "./ee/spec/migrations/backfill_namespace_statistics_with_wiki_size_spec.rb" -- "./ee/spec/migrations/backfill_operations_feature_flags_iid_spec.rb" -- "./ee/spec/migrations/backfill_software_licenses_spdx_identifiers_spec.rb" -- "./ee/spec/migrations/backfill_version_author_and_created_at_spec.rb" -- "./ee/spec/migrations/cleanup_deploy_access_levels_for_removed_groups_spec.rb" -- "./ee/spec/migrations/create_elastic_reindexing_subtasks_spec.rb" -- "./ee/spec/migrations/fix_any_approver_rule_for_projects_spec.rb" -- "./ee/spec/migrations/migrate_design_notes_mentions_to_db_spec.rb" -- "./ee/spec/migrations/migrate_epic_mentions_to_db_spec.rb" -- "./ee/spec/migrations/migrate_epic_notes_mentions_to_db_spec.rb" -- "./ee/spec/migrations/migrate_license_management_artifacts_to_license_scanning_spec.rb" -- "./ee/spec/migrations/migrate_saml_identities_to_scim_identities_spec.rb" -- "./ee/spec/migrations/migrate_scim_identities_to_saml_for_new_users_spec.rb" -- "./ee/spec/migrations/migrate_vulnerability_dismissal_feedback_spec.rb" -- "./ee/spec/migrations/migrate_vulnerability_dismissals_spec.rb" -- "./ee/spec/migrations/nullify_feature_flag_plaintext_tokens_spec.rb" -- "./ee/spec/migrations/populate_vulnerability_historical_statistics_for_year_spec.rb" -- "./ee/spec/migrations/remove_creations_in_gitlab_subscription_histories_spec.rb" -- "./ee/spec/migrations/remove_cycle_analytics_total_stage_data_spec.rb" -- "./ee/spec/migrations/remove_duplicated_cs_findings_spec.rb" -- "./ee/spec/migrations/remove_duplicated_cs_findings_without_vulnerability_id_spec.rb" -- "./ee/spec/migrations/remove_schedule_and_status_null_constraints_from_pending_escalations_alert_spec.rb" -- "./ee/spec/migrations/schedule_fix_orphan_promoted_issues_spec.rb" -- "./ee/spec/migrations/schedule_fix_ruby_object_in_audit_events_spec.rb" -- "./ee/spec/migrations/schedule_merge_request_any_approval_rule_migration_spec.rb" -- "./ee/spec/migrations/schedule_populate_dismissed_state_for_vulnerabilities_spec.rb" -- "./ee/spec/migrations/schedule_populate_resolved_on_default_branch_column_spec.rb" -- "./ee/spec/migrations/schedule_populate_vulnerability_historical_statistics_spec.rb" -- "./ee/spec/migrations/schedule_project_any_approval_rule_migration_spec.rb" -- "./ee/spec/migrations/schedule_remove_inaccessible_epic_todos_spec.rb" -- "./ee/spec/migrations/schedule_sync_blocking_issues_count_spec.rb" -- "./ee/spec/migrations/schedule_uuid_population_for_security_findings2_spec.rb" -- "./ee/spec/migrations/set_report_type_for_vulnerabilities_spec.rb" -- "./ee/spec/migrations/set_resolved_state_on_vulnerabilities_spec.rb" -- "./ee/spec/migrations/update_cs_vulnerability_confidence_column_spec.rb" -- "./ee/spec/migrations/update_gitlab_subscriptions_start_at_post_eoa_spec.rb" -- "./ee/spec/migrations/update_location_fingerprint_column_for_cs_spec.rb" -- "./ee/spec/migrations/update_occurrence_severity_column_spec.rb" -- "./ee/spec/migrations/update_undefined_confidence_from_occurrences_spec.rb" -- "./ee/spec/migrations/update_undefined_confidence_from_vulnerabilities_spec.rb" -- "./ee/spec/migrations/update_vulnerability_severity_column_spec.rb" -- "./ee/spec/models/analytics/cycle_analytics/group_level_spec.rb" -- "./ee/spec/models/approval_merge_request_rule_spec.rb" -- "./ee/spec/models/approval_project_rule_spec.rb" -- "./ee/spec/models/approval_state_spec.rb" -- "./ee/spec/models/approval_wrapped_code_owner_rule_spec.rb" -- "./ee/spec/models/approval_wrapped_rule_spec.rb" -- "./ee/spec/models/approver_group_spec.rb" - "./ee/spec/models/ci/bridge_spec.rb" - "./ee/spec/models/ci/build_spec.rb" - "./ee/spec/models/ci/minutes/additional_pack_spec.rb" -- "./ee/spec/models/ci/pipeline_spec.rb" -- "./ee/spec/models/ci/subscriptions/project_spec.rb" -- "./ee/spec/models/concerns/approval_rule_like_spec.rb" -- "./ee/spec/models/concerns/approver_migrate_hook_spec.rb" -- "./ee/spec/models/dora/daily_metrics_spec.rb" - "./ee/spec/models/ee/ci/job_artifact_spec.rb" -- "./ee/spec/models/ee/ci/pipeline_artifact_spec.rb" -- "./ee/spec/models/ee/ci/runner_spec.rb" -- "./ee/spec/models/ee/merge_request_diff_spec.rb" -- "./ee/spec/models/ee/pages_deployment_spec.rb" -- "./ee/spec/models/ee/terraform/state_version_spec.rb" -- "./ee/spec/models/geo/container_repository_registry_spec.rb" -- "./ee/spec/models/geo/deleted_project_spec.rb" -- "./ee/spec/models/geo/design_registry_spec.rb" -- "./ee/spec/models/geo/job_artifact_registry_spec.rb" -- "./ee/spec/models/geo_node_namespace_link_spec.rb" -- "./ee/spec/models/geo_node_spec.rb" -- "./ee/spec/models/geo_node_status_spec.rb" -- "./ee/spec/models/geo/package_file_registry_spec.rb" -- "./ee/spec/models/geo/project_registry_spec.rb" - "./ee/spec/models/group_member_spec.rb" -- "./ee/spec/models/group_wiki_repository_spec.rb" -- "./ee/spec/models/merge_request_spec.rb" -- "./ee/spec/models/packages/package_file_spec.rb" -- "./ee/spec/models/project_spec.rb" -- "./ee/spec/models/requirements_management/requirement_spec.rb" -- "./ee/spec/models/snippet_repository_spec.rb" -- "./ee/spec/models/upload_spec.rb" -- "./ee/spec/models/visible_approvable_spec.rb" -- "./ee/spec/policies/ci/build_policy_spec.rb" -- "./ee/spec/presenters/approval_rule_presenter_spec.rb" -- "./ee/spec/presenters/merge_request_presenter_spec.rb" - "./ee/spec/replicators/geo/pipeline_artifact_replicator_spec.rb" - "./ee/spec/replicators/geo/terraform_state_version_replicator_spec.rb" -- "./ee/spec/requests/api/ci/pipelines_spec.rb" -- "./ee/spec/requests/api/geo_nodes_spec.rb" -- "./ee/spec/requests/api/geo_replication_spec.rb" -- "./ee/spec/requests/api/graphql/mutations/dast_on_demand_scans/create_spec.rb" -- "./ee/spec/requests/api/graphql/mutations/dast/profiles/create_spec.rb" -- "./ee/spec/requests/api/graphql/mutations/dast/profiles/run_spec.rb" -- "./ee/spec/requests/api/graphql/mutations/dast/profiles/update_spec.rb" -- "./ee/spec/requests/api/graphql/project/pipeline/dast_profile_spec.rb" -- "./ee/spec/requests/api/merge_request_approval_rules_spec.rb" -- "./ee/spec/requests/api/merge_requests_spec.rb" -- "./ee/spec/requests/api/project_approval_rules_spec.rb" -- "./ee/spec/requests/api/project_approval_settings_spec.rb" -- "./ee/spec/requests/api/project_approvals_spec.rb" -- "./ee/spec/requests/api/project_snapshots_spec.rb" -- "./ee/spec/requests/api/status_checks_spec.rb" -- "./ee/spec/requests/api/vulnerability_findings_spec.rb" -- "./ee/spec/requests/projects/merge_requests_controller_spec.rb" -- "./ee/spec/routing/admin_routing_spec.rb" -- "./ee/spec/serializers/dashboard_operations_project_entity_spec.rb" -- "./ee/spec/serializers/ee/evidences/release_entity_spec.rb" -- "./ee/spec/serializers/ee/user_serializer_spec.rb" -- "./ee/spec/serializers/evidences/evidence_entity_spec.rb" -- "./ee/spec/serializers/merge_request_widget_entity_spec.rb" -- "./ee/spec/serializers/pipeline_serializer_spec.rb" -- "./ee/spec/services/approval_rules/create_service_spec.rb" -- "./ee/spec/services/approval_rules/finalize_service_spec.rb" -- "./ee/spec/services/approval_rules/merge_request_rule_destroy_service_spec.rb" -- "./ee/spec/services/approval_rules/params_filtering_service_spec.rb" -- "./ee/spec/services/approval_rules/project_rule_destroy_service_spec.rb" -- "./ee/spec/services/approval_rules/update_service_spec.rb" -- "./ee/spec/services/app_sec/dast/profiles/create_service_spec.rb" -- "./ee/spec/services/app_sec/dast/profiles/update_service_spec.rb" -- "./ee/spec/services/app_sec/dast/scans/create_service_spec.rb" -- "./ee/spec/services/app_sec/dast/scans/run_service_spec.rb" -- "./ee/spec/services/ci/compare_license_scanning_reports_service_spec.rb" -- "./ee/spec/services/ci/compare_metrics_reports_service_spec.rb" -- "./ee/spec/services/ci/create_pipeline_service/dast_configuration_spec.rb" - "./ee/spec/services/ci/destroy_pipeline_service_spec.rb" -- "./ee/spec/services/ci/minutes/track_live_consumption_service_spec.rb" -- "./ee/spec/services/ci/minutes/update_build_minutes_service_spec.rb" -- "./ee/spec/services/ci/register_job_service_spec.rb" - "./ee/spec/services/ci/retry_build_service_spec.rb" -- "./ee/spec/services/ci/run_dast_scan_service_spec.rb" - "./ee/spec/services/ci/subscribe_bridge_service_spec.rb" -- "./ee/spec/services/ci/sync_reports_to_approval_rules_service_spec.rb" -- "./ee/spec/services/ci/trigger_downstream_subscription_service_spec.rb" -- "./ee/spec/services/dast_on_demand_scans/create_service_spec.rb" - "./ee/spec/services/deployments/auto_rollback_service_spec.rb" - "./ee/spec/services/ee/ci/job_artifacts/destroy_all_expired_service_spec.rb" -- "./ee/spec/services/ee/ci/job_artifacts/destroy_batch_service_spec.rb" -- "./ee/spec/services/ee/integrations/test/project_service_spec.rb" -- "./ee/spec/services/ee/issuable/destroy_service_spec.rb" -- "./ee/spec/services/ee/merge_requests/refresh_service_spec.rb" -- "./ee/spec/services/ee/merge_requests/update_service_spec.rb" -- "./ee/spec/services/ee/notification_service_spec.rb" -- "./ee/spec/services/ee/post_receive_service_spec.rb" -- "./ee/spec/services/ee/releases/create_evidence_service_spec.rb" - "./ee/spec/services/ee/users/destroy_service_spec.rb" -- "./ee/spec/services/external_status_checks/create_service_spec.rb" -- "./ee/spec/services/external_status_checks/destroy_service_spec.rb" -- "./ee/spec/services/external_status_checks/update_service_spec.rb" -- "./ee/spec/services/geo/container_repository_sync_service_spec.rb" -- "./ee/spec/services/geo/hashed_storage_migrated_event_store_spec.rb" -- "./ee/spec/services/geo/hashed_storage_migration_service_spec.rb" -- "./ee/spec/services/geo/node_create_service_spec.rb" -- "./ee/spec/services/geo/node_status_request_service_spec.rb" -- "./ee/spec/services/geo/node_update_service_spec.rb" -- "./ee/spec/services/geo/project_housekeeping_service_spec.rb" -- "./ee/spec/services/geo/registry_consistency_service_spec.rb" -- "./ee/spec/services/geo/repositories_changed_event_store_spec.rb" -- "./ee/spec/services/geo/repository_updated_event_store_spec.rb" -- "./ee/spec/services/geo/repository_verification_reset_spec.rb" -- "./ee/spec/services/geo/repository_verification_secondary_service_spec.rb" -- "./ee/spec/services/merge_requests/merge_service_spec.rb" -- "./ee/spec/services/merge_requests/reset_approvals_service_spec.rb" -- "./ee/spec/services/merge_requests/sync_report_approver_approval_rules_spec.rb" - "./ee/spec/services/projects/transfer_service_spec.rb" - "./ee/spec/services/security/security_orchestration_policies/rule_schedule_service_spec.rb" -- "./ee/spec/services/todo_service_spec.rb" -- "./ee/spec/services/vulnerability_feedback/create_service_spec.rb" -- "./ee/spec/services/wiki_pages/create_service_spec.rb" -- "./ee/spec/services/wiki_pages/destroy_service_spec.rb" -- "./ee/spec/services/wiki_pages/update_service_spec.rb" -- "./ee/spec/support/shared_examples/fixtures/analytics_value_streams_shared_examples.rb" -- "./ee/spec/support/shared_examples/graphql/geo/geo_registries_resolver_shared_examples.rb" -- "./ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scans_shared_examples.rb" -- "./ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scan_with_user_abilities_shared_examples.rb" -- "./ee/spec/support/shared_examples/lib/gitlab/geo/geo_log_cursor_event_shared_examples.rb" -- "./ee/spec/support/shared_examples/lib/gitlab/geo/geo_logs_event_source_info_shared_examples.rb" -- "./ee/spec/support/shared_examples/models/concerns/blob_replicator_strategy_shared_examples.rb" -- "./ee/spec/support/shared_examples/models/concerns/replicable_model_shared_examples.rb" -- "./ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb" -- "./ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb" -- "./ee/spec/support/shared_examples/requests/api/project_approval_rules_api_shared_examples.rb" -- "./ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb" -- "./ee/spec/support/shared_examples/services/build_execute_shared_examples.rb" -- "./ee/spec/support/shared_examples/services/dast_on_demand_scans_shared_examples.rb" -- "./ee/spec/support/shared_examples/services/geo_event_store_shared_examples.rb" -- "./ee/spec/tasks/geo_rake_spec.rb" -- "./ee/spec/tasks/gitlab/geo_rake_spec.rb" -- "./ee/spec/workers/geo/file_download_dispatch_worker_spec.rb" -- "./ee/spec/workers/geo/metrics_update_worker_spec.rb" -- "./ee/spec/workers/geo/prune_event_log_worker_spec.rb" -- "./ee/spec/workers/geo/registry_sync_worker_spec.rb" -- "./ee/spec/workers/geo/repository_cleanup_worker_spec.rb" -- "./ee/spec/workers/geo/repository_sync_worker_spec.rb" -- "./ee/spec/workers/geo/repository_verification/secondary/scheduler_worker_spec.rb" -- "./ee/spec/workers/geo/repository_verification/secondary/single_worker_spec.rb" -- "./ee/spec/workers/geo/verification_worker_spec.rb" -- "./ee/spec/workers/refresh_license_compliance_checks_worker_spec.rb" - "./spec/controllers/abuse_reports_controller_spec.rb" - "./spec/controllers/admin/spam_logs_controller_spec.rb" - "./spec/controllers/admin/users_controller_spec.rb" - "./spec/controllers/omniauth_callbacks_controller_spec.rb" - "./spec/controllers/projects/issues_controller_spec.rb" -- "./spec/controllers/projects/jobs_controller_spec.rb" -- "./spec/controllers/projects/merge_requests/content_controller_spec.rb" -- "./spec/controllers/projects/merge_requests_controller_spec.rb" - "./spec/controllers/projects/pipelines_controller_spec.rb" -- "./spec/controllers/projects/pipelines/tests_controller_spec.rb" - "./spec/controllers/projects/settings/access_tokens_controller_spec.rb" -- "./spec/controllers/projects/tags_controller_spec.rb" -- "./spec/controllers/sent_notifications_controller_spec.rb" -- "./spec/factories_spec.rb" -- "./spec/features/action_cable_logging_spec.rb" -- "./spec/features/admin/admin_abuse_reports_spec.rb" -- "./spec/features/admin/admin_appearance_spec.rb" -- "./spec/features/admin/admin_broadcast_messages_spec.rb" -- "./spec/features/admin/admin_builds_spec.rb" -- "./spec/features/admin/admin_dev_ops_report_spec.rb" -- "./spec/features/admin/admin_disables_git_access_protocol_spec.rb" -- "./spec/features/admin/admin_disables_two_factor_spec.rb" -- "./spec/features/admin/admin_groups_spec.rb" -- "./spec/features/admin/admin_hooks_spec.rb" -- "./spec/features/admin/admin_labels_spec.rb" -- "./spec/features/admin/admin_mode/login_spec.rb" -- "./spec/features/admin/admin_mode/logout_spec.rb" -- "./spec/features/admin/admin_mode_spec.rb" -- "./spec/features/admin/admin_mode/workers_spec.rb" -- "./spec/features/admin/admin_projects_spec.rb" -- "./spec/features/admin/admin_runners_spec.rb" -- "./spec/features/admin/admin_search_settings_spec.rb" -- "./spec/features/admin/admin_serverless_domains_spec.rb" -- "./spec/features/admin/admin_settings_spec.rb" -- "./spec/features/admin/admin_users_impersonation_tokens_spec.rb" -- "./spec/features/admin/admin_uses_repository_checks_spec.rb" -- "./spec/features/admin/clusters/eks_spec.rb" -- "./spec/features/admin/dashboard_spec.rb" -- "./spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb" -- "./spec/features/admin/users/user_spec.rb" -- "./spec/features/admin/users/users_spec.rb" -- "./spec/features/alert_management/alert_details_spec.rb" -- "./spec/features/alert_management/alert_management_list_spec.rb" -- "./spec/features/alert_management_spec.rb" -- "./spec/features/alert_management/user_filters_alerts_by_status_spec.rb" -- "./spec/features/alert_management/user_searches_alerts_spec.rb" -- "./spec/features/alert_management/user_updates_alert_status_spec.rb" -- "./spec/features/alerts_settings/user_views_alerts_settings_spec.rb" -- "./spec/features/atom/dashboard_spec.rb" -- "./spec/features/boards/boards_spec.rb" -- "./spec/features/boards/focus_mode_spec.rb" -- "./spec/features/boards/issue_ordering_spec.rb" -- "./spec/features/boards/keyboard_shortcut_spec.rb" -- "./spec/features/boards/multiple_boards_spec.rb" -- "./spec/features/boards/new_issue_spec.rb" -- "./spec/features/boards/reload_boards_on_browser_back_spec.rb" -- "./spec/features/boards/sidebar_due_date_spec.rb" -- "./spec/features/boards/sidebar_labels_in_namespaces_spec.rb" -- "./spec/features/boards/sidebar_labels_spec.rb" -- "./spec/features/boards/sidebar_milestones_spec.rb" -- "./spec/features/boards/sidebar_spec.rb" -- "./spec/features/boards/user_adds_lists_to_board_spec.rb" -- "./spec/features/boards/user_visits_board_spec.rb" -- "./spec/features/broadcast_messages_spec.rb" -- "./spec/features/calendar_spec.rb" -- "./spec/features/callouts/registration_enabled_spec.rb" -- "./spec/features/clusters/cluster_detail_page_spec.rb" -- "./spec/features/clusters/cluster_health_dashboard_spec.rb" -- "./spec/features/commit_spec.rb" -- "./spec/features/commits_spec.rb" -- "./spec/features/commits/user_uses_quick_actions_spec.rb" -- "./spec/features/contextual_sidebar_spec.rb" -- "./spec/features/cycle_analytics_spec.rb" -- "./spec/features/dashboard/activity_spec.rb" -- "./spec/features/dashboard/archived_projects_spec.rb" -- "./spec/features/dashboard/datetime_on_tooltips_spec.rb" -- "./spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb" -- "./spec/features/dashboard/groups_list_spec.rb" -- "./spec/features/dashboard/group_spec.rb" -- "./spec/features/dashboard/issues_filter_spec.rb" -- "./spec/features/dashboard/issues_spec.rb" -- "./spec/features/dashboard/label_filter_spec.rb" -- "./spec/features/dashboard/merge_requests_spec.rb" -- "./spec/features/dashboard/milestones_spec.rb" -- "./spec/features/dashboard/project_member_activity_index_spec.rb" -- "./spec/features/dashboard/projects_spec.rb" -- "./spec/features/dashboard/root_spec.rb" -- "./spec/features/dashboard/shortcuts_spec.rb" -- "./spec/features/dashboard/snippets_spec.rb" -- "./spec/features/dashboard/todos/todos_filtering_spec.rb" -- "./spec/features/dashboard/todos/todos_spec.rb" -- "./spec/features/dashboard/user_filters_projects_spec.rb" -- "./spec/features/discussion_comments/commit_spec.rb" -- "./spec/features/discussion_comments/issue_spec.rb" -- "./spec/features/discussion_comments/merge_request_spec.rb" -- "./spec/features/discussion_comments/snippets_spec.rb" -- "./spec/features/error_pages_spec.rb" -- "./spec/features/error_tracking/user_filters_errors_by_status_spec.rb" -- "./spec/features/error_tracking/user_searches_sentry_errors_spec.rb" -- "./spec/features/error_tracking/user_sees_error_details_spec.rb" -- "./spec/features/error_tracking/user_sees_error_index_spec.rb" -- "./spec/features/expand_collapse_diffs_spec.rb" -- "./spec/features/explore/groups_list_spec.rb" -- "./spec/features/explore/groups_spec.rb" -- "./spec/features/explore/user_explores_projects_spec.rb" -- "./spec/features/file_uploads/attachment_spec.rb" -- "./spec/features/file_uploads/ci_artifact_spec.rb" -- "./spec/features/file_uploads/git_lfs_spec.rb" -- "./spec/features/file_uploads/graphql_add_design_spec.rb" -- "./spec/features/file_uploads/group_import_spec.rb" -- "./spec/features/file_uploads/maven_package_spec.rb" -- "./spec/features/file_uploads/multipart_invalid_uploads_spec.rb" -- "./spec/features/file_uploads/nuget_package_spec.rb" -- "./spec/features/file_uploads/project_import_spec.rb" -- "./spec/features/file_uploads/rubygem_package_spec.rb" -- "./spec/features/file_uploads/user_avatar_spec.rb" -- "./spec/features/frequently_visited_projects_and_groups_spec.rb" -- "./spec/features/gitlab_experiments_spec.rb" -- "./spec/features/global_search_spec.rb" -- "./spec/features/groups/activity_spec.rb" -- "./spec/features/groups/board_sidebar_spec.rb" -- "./spec/features/groups/board_spec.rb" -- "./spec/features/groups/clusters/eks_spec.rb" -- "./spec/features/groups/clusters/user_spec.rb" -- "./spec/features/groups/container_registry_spec.rb" -- "./spec/features/groups/dependency_proxy_spec.rb" -- "./spec/features/groups/empty_states_spec.rb" -- "./spec/features/groups/import_export/connect_instance_spec.rb" -- "./spec/features/groups/import_export/export_file_spec.rb" -- "./spec/features/groups/import_export/import_file_spec.rb" -- "./spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb" -- "./spec/features/groups/issues_spec.rb" -- "./spec/features/groups/labels/index_spec.rb" -- "./spec/features/groups/labels/search_labels_spec.rb" -- "./spec/features/groups/labels/sort_labels_spec.rb" -- "./spec/features/groups/labels/subscription_spec.rb" -- "./spec/features/groups/members/filter_members_spec.rb" -- "./spec/features/groups/members/leave_group_spec.rb" -- "./spec/features/groups/members/list_members_spec.rb" -- "./spec/features/groups/members/manage_groups_spec.rb" -- "./spec/features/groups/members/manage_members_spec.rb" -- "./spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb" -- "./spec/features/groups/members/master_manages_access_requests_spec.rb" -- "./spec/features/groups/members/search_members_spec.rb" -- "./spec/features/groups/members/sort_members_spec.rb" -- "./spec/features/groups/members/tabs_spec.rb" -- "./spec/features/groups/merge_requests_spec.rb" -- "./spec/features/groups/milestones/gfm_autocomplete_spec.rb" -- "./spec/features/groups/milestone_spec.rb" -- "./spec/features/groups/milestones_sorting_spec.rb" -- "./spec/features/groups/packages_spec.rb" -- "./spec/features/groups/settings/group_badges_spec.rb" -- "./spec/features/groups/settings/packages_and_registries_spec.rb" -- "./spec/features/groups/settings/repository_spec.rb" -- "./spec/features/groups/settings/user_searches_in_settings_spec.rb" -- "./spec/features/groups/show_spec.rb" -- "./spec/features/groups_spec.rb" -- "./spec/features/groups/user_browse_projects_group_page_spec.rb" -- "./spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb" -- "./spec/features/help_pages_spec.rb" -- "./spec/features/ide_spec.rb" -- "./spec/features/ide/user_commits_changes_spec.rb" -- "./spec/features/ide/user_opens_merge_request_spec.rb" -- "./spec/features/import/manifest_import_spec.rb" -- "./spec/features/incidents/incident_details_spec.rb" -- "./spec/features/incidents/incidents_list_spec.rb" -- "./spec/features/incidents/user_creates_new_incident_spec.rb" -- "./spec/features/incidents/user_filters_incidents_by_status_spec.rb" -- "./spec/features/incidents/user_searches_incidents_spec.rb" -- "./spec/features/incidents/user_views_incident_spec.rb" -- "./spec/features/issuables/issuable_list_spec.rb" -- "./spec/features/issuables/markdown_references/internal_references_spec.rb" -- "./spec/features/issuables/markdown_references/jira_spec.rb" -- "./spec/features/issuables/sorting_list_spec.rb" -- "./spec/features/issuables/user_sees_sidebar_spec.rb" -- "./spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb" -- "./spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb" -- "./spec/features/issues/csv_spec.rb" -- "./spec/features/issues/discussion_lock_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_assignee_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_author_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_base_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_emoji_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_hint_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_label_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_milestone_spec.rb" -- "./spec/features/issues/filtered_search/dropdown_release_spec.rb" -- "./spec/features/issues/filtered_search/filter_issues_spec.rb" -- "./spec/features/issues/filtered_search/recent_searches_spec.rb" -- "./spec/features/issues/filtered_search/search_bar_spec.rb" -- "./spec/features/issues/filtered_search/visual_tokens_spec.rb" -- "./spec/features/issues/form_spec.rb" -- "./spec/features/issues/gfm_autocomplete_spec.rb" -- "./spec/features/issues/group_label_sidebar_spec.rb" -- "./spec/features/issues/incident_issue_spec.rb" - "./spec/features/issues/issue_detail_spec.rb" -- "./spec/features/issues/issue_header_spec.rb" -- "./spec/features/issues/issue_sidebar_spec.rb" -- "./spec/features/issues/keyboard_shortcut_spec.rb" -- "./spec/features/issues/markdown_toolbar_spec.rb" -- "./spec/features/issues/move_spec.rb" -- "./spec/features/issues/note_polling_spec.rb" -- "./spec/features/issues/notes_on_issues_spec.rb" -- "./spec/features/issues/related_issues_spec.rb" -- "./spec/features/issues/resource_label_events_spec.rb" -- "./spec/features/issues/service_desk_spec.rb" -- "./spec/features/issues/spam_issues_spec.rb" -- "./spec/features/issues/todo_spec.rb" -- "./spec/features/issues/user_bulk_edits_issues_labels_spec.rb" -- "./spec/features/issues/user_bulk_edits_issues_spec.rb" -- "./spec/features/issues/user_comments_on_issue_spec.rb" -- "./spec/features/issues/user_creates_branch_and_merge_request_spec.rb" -- "./spec/features/issues/user_creates_confidential_merge_request_spec.rb" -- "./spec/features/issues/user_creates_issue_by_email_spec.rb" -- "./spec/features/issues/user_creates_issue_spec.rb" -- "./spec/features/issues/user_edits_issue_spec.rb" -- "./spec/features/issues/user_filters_issues_spec.rb" -- "./spec/features/issues/user_interacts_with_awards_spec.rb" -- "./spec/features/issues/user_invites_from_a_comment_spec.rb" -- "./spec/features/issues/user_resets_their_incoming_email_token_spec.rb" -- "./spec/features/issues/user_sees_empty_state_spec.rb" -- "./spec/features/issues/user_sees_live_update_spec.rb" -- "./spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb" -- "./spec/features/issues/user_sorts_issue_comments_spec.rb" -- "./spec/features/issues/user_sorts_issues_spec.rb" -- "./spec/features/issues/user_toggles_subscription_spec.rb" -- "./spec/features/issues/user_uses_quick_actions_spec.rb" -- "./spec/features/issues/user_views_issue_spec.rb" -- "./spec/features/issues/user_views_issues_spec.rb" -- "./spec/features/jira_connect/branches_spec.rb" -- "./spec/features/labels_hierarchy_spec.rb" -- "./spec/features/markdown/copy_as_gfm_spec.rb" -- "./spec/features/markdown/gitlab_flavored_markdown_spec.rb" -- "./spec/features/markdown/keyboard_shortcuts_spec.rb" -- "./spec/features/markdown/math_spec.rb" -- "./spec/features/markdown/mermaid_spec.rb" -- "./spec/features/markdown/metrics_spec.rb" -- "./spec/features/merge_request/batch_comments_spec.rb" -- "./spec/features/merge_request/close_reopen_report_toggle_spec.rb" -- "./spec/features/merge_request/maintainer_edits_fork_spec.rb" -- "./spec/features/merge_request/merge_request_discussion_lock_spec.rb" -- "./spec/features/merge_requests/filters_generic_behavior_spec.rb" -- "./spec/features/merge_requests/user_exports_as_csv_spec.rb" -- "./spec/features/merge_requests/user_filters_by_approvals_spec.rb" -- "./spec/features/merge_requests/user_filters_by_assignees_spec.rb" -- "./spec/features/merge_requests/user_filters_by_deployments_spec.rb" -- "./spec/features/merge_requests/user_filters_by_draft_spec.rb" -- "./spec/features/merge_requests/user_filters_by_labels_spec.rb" -- "./spec/features/merge_requests/user_filters_by_milestones_spec.rb" -- "./spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb" -- "./spec/features/merge_requests/user_filters_by_target_branch_spec.rb" -- "./spec/features/merge_requests/user_mass_updates_spec.rb" -- "./spec/features/merge_request/user_accepts_merge_request_spec.rb" -- "./spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb" -- "./spec/features/merge_request/user_approves_spec.rb" -- "./spec/features/merge_request/user_assigns_themselves_spec.rb" -- "./spec/features/merge_request/user_awards_emoji_spec.rb" -- "./spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb" -- "./spec/features/merge_request/user_comments_on_commit_spec.rb" -- "./spec/features/merge_request/user_comments_on_diff_spec.rb" -- "./spec/features/merge_request/user_comments_on_merge_request_spec.rb" -- "./spec/features/merge_request/user_creates_image_diff_notes_spec.rb" -- "./spec/features/merge_request/user_creates_merge_request_spec.rb" -- "./spec/features/merge_request/user_creates_mr_spec.rb" -- "./spec/features/merge_request/user_customizes_merge_commit_message_spec.rb" -- "./spec/features/merge_request/user_edits_assignees_sidebar_spec.rb" -- "./spec/features/merge_request/user_edits_merge_request_spec.rb" -- "./spec/features/merge_request/user_edits_mr_spec.rb" -- "./spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb" -- "./spec/features/merge_request/user_expands_diff_spec.rb" -- "./spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb" -- "./spec/features/merge_request/user_invites_from_a_comment_spec.rb" -- "./spec/features/merge_request/user_jumps_to_discussion_spec.rb" -- "./spec/features/merge_request/user_locks_discussion_spec.rb" -- "./spec/features/merge_request/user_manages_subscription_spec.rb" -- "./spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb" -- "./spec/features/merge_request/user_merges_immediately_spec.rb" -- "./spec/features/merge_request/user_merges_merge_request_spec.rb" -- "./spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb" -- "./spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb" -- "./spec/features/merge_request/user_posts_diff_notes_spec.rb" -- "./spec/features/merge_request/user_posts_notes_spec.rb" -- "./spec/features/merge_request/user_rebases_merge_request_spec.rb" -- "./spec/features/merge_request/user_resolves_conflicts_spec.rb" -- "./spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb" -- "./spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb" -- "./spec/features/merge_request/user_resolves_wip_mr_spec.rb" -- "./spec/features/merge_request/user_reverts_merge_request_spec.rb" -- "./spec/features/merge_request/user_reviews_image_spec.rb" -- "./spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb" -- "./spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb" -- "./spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb" -- "./spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb" -- "./spec/features/merge_request/user_sees_closing_issues_message_spec.rb" -- "./spec/features/merge_request/user_sees_deleted_target_branch_spec.rb" -- "./spec/features/merge_request/user_sees_deployment_widget_spec.rb" -- "./spec/features/merge_request/user_sees_diff_spec.rb" -- "./spec/features/merge_request/user_sees_discussions_spec.rb" -- "./spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb" -- "./spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb" -- "./spec/features/merge_request/user_sees_merge_widget_spec.rb" -- "./spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb" -- "./spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb" -- "./spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb" -- "./spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb" -- "./spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb" -- "./spec/features/merge_request/user_sees_pipelines_spec.rb" -- "./spec/features/merge_request/user_sees_suggest_pipeline_spec.rb" -- "./spec/features/merge_request/user_sees_system_notes_spec.rb" -- "./spec/features/merge_request/user_sees_versions_spec.rb" -- "./spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb" -- "./spec/features/merge_request/user_squashes_merge_request_spec.rb" -- "./spec/features/merge_request/user_suggests_changes_on_diff_spec.rb" -- "./spec/features/merge_request/user_toggles_whitespace_changes_spec.rb" -- "./spec/features/merge_request/user_uses_quick_actions_spec.rb" -- "./spec/features/merge_request/user_views_auto_expanding_diff_spec.rb" -- "./spec/features/merge_request/user_views_diffs_commit_spec.rb" -- "./spec/features/merge_request/user_views_diffs_file_by_file_spec.rb" -- "./spec/features/merge_request/user_views_diffs_spec.rb" -- "./spec/features/merge_request/user_views_open_merge_request_spec.rb" -- "./spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb" -- "./spec/features/milestone_spec.rb" -- "./spec/features/milestones/user_creates_milestone_spec.rb" -- "./spec/features/milestones/user_deletes_milestone_spec.rb" -- "./spec/features/milestones/user_edits_milestone_spec.rb" -- "./spec/features/milestones/user_views_milestone_spec.rb" -- "./spec/features/milestones/user_views_milestones_spec.rb" -- "./spec/features/nav/top_nav_responsive_spec.rb" -- "./spec/features/oauth_login_spec.rb" -- "./spec/features/participants_autocomplete_spec.rb" -- "./spec/features/populate_new_pipeline_vars_with_params_spec.rb" -- "./spec/features/profiles/account_spec.rb" -- "./spec/features/profiles/active_sessions_spec.rb" -- "./spec/features/profiles/keys_spec.rb" -- "./spec/features/profiles/oauth_applications_spec.rb" -- "./spec/features/profile_spec.rb" -- "./spec/features/profiles/personal_access_tokens_spec.rb" -- "./spec/features/profiles/user_changes_notified_of_own_activity_spec.rb" -- "./spec/features/profiles/user_edit_preferences_spec.rb" -- "./spec/features/profiles/user_edit_profile_spec.rb" -- "./spec/features/profiles/user_search_settings_spec.rb" -- "./spec/features/profiles/user_visits_notifications_tab_spec.rb" -- "./spec/features/profiles/user_visits_profile_preferences_page_spec.rb" -- "./spec/features/profiles/user_visits_profile_spec.rb" -- "./spec/features/project_group_variables_spec.rb" -- "./spec/features/projects/activity/user_sees_activity_spec.rb" -- "./spec/features/projects/activity/user_sees_design_activity_spec.rb" -- "./spec/features/projects/activity/user_sees_design_comment_spec.rb" -- "./spec/features/projects/activity/user_sees_private_activity_spec.rb" -- "./spec/features/projects/artifacts/file_spec.rb" -- "./spec/features/projects/artifacts/raw_spec.rb" -- "./spec/features/projects/artifacts/user_browses_artifacts_spec.rb" -- "./spec/features/projects/badges/list_spec.rb" -- "./spec/features/projects/badges/pipeline_badge_spec.rb" -- "./spec/features/projects/blobs/balsamiq_spec.rb" -- "./spec/features/projects/blobs/blob_line_permalink_updater_spec.rb" -- "./spec/features/projects/blobs/blob_show_spec.rb" -- "./spec/features/projects/blobs/edit_spec.rb" -- "./spec/features/projects/blobs/shortcuts_blob_spec.rb" -- "./spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb" -- "./spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb" -- "./spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb" -- "./spec/features/projects/branches/new_branch_ref_dropdown_spec.rb" -- "./spec/features/projects/branches_spec.rb" -- "./spec/features/projects/branches/user_creates_branch_spec.rb" -- "./spec/features/projects/branches/user_deletes_branch_spec.rb" -- "./spec/features/projects/branches/user_views_branches_spec.rb" -- "./spec/features/projects/ci/editor_spec.rb" -- "./spec/features/projects/clusters/eks_spec.rb" -- "./spec/features/projects/clusters/gcp_spec.rb" -- "./spec/features/projects/clusters_spec.rb" -- "./spec/features/projects/clusters/user_spec.rb" -- "./spec/features/projects/commit/builds_spec.rb" -- "./spec/features/projects/commit/cherry_pick_spec.rb" -- "./spec/features/projects/commit/comments/user_adds_comment_spec.rb" -- "./spec/features/projects/commit/comments/user_deletes_comments_spec.rb" -- "./spec/features/projects/commit/comments/user_edits_comments_spec.rb" -- "./spec/features/projects/commit/diff_notes_spec.rb" -- "./spec/features/projects/commit/mini_pipeline_graph_spec.rb" -- "./spec/features/projects/commits/user_browses_commits_spec.rb" -- "./spec/features/projects/commit/user_comments_on_commit_spec.rb" -- "./spec/features/projects/commit/user_reverts_commit_spec.rb" -- "./spec/features/projects/commit/user_views_user_status_on_commit_spec.rb" -- "./spec/features/projects/compare_spec.rb" -- "./spec/features/projects/container_registry_spec.rb" -- "./spec/features/projects/deploy_keys_spec.rb" -- "./spec/features/projects/diffs/diff_show_spec.rb" -- "./spec/features/projects/environments/environment_metrics_spec.rb" -- "./spec/features/projects/environments/environment_spec.rb" -- "./spec/features/projects/environments/environments_spec.rb" -- "./spec/features/projects/environments_pod_logs_spec.rb" -- "./spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb" -- "./spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb" -- "./spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb" -- "./spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb" -- "./spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb" -- "./spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb" -- "./spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb" -- "./spec/features/projects/features_visibility_spec.rb" -- "./spec/features/projects/files/dockerfile_dropdown_spec.rb" -- "./spec/features/projects/files/edit_file_soft_wrap_spec.rb" -- "./spec/features/projects/files/files_sort_submodules_with_folders_spec.rb" -- "./spec/features/projects/files/find_file_keyboard_spec.rb" -- "./spec/features/projects/files/gitignore_dropdown_spec.rb" -- "./spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb" -- "./spec/features/projects/files/project_owner_creates_license_file_spec.rb" -- "./spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb" -- "./spec/features/projects/files/template_selector_menu_spec.rb" -- "./spec/features/projects/files/template_type_dropdown_spec.rb" -- "./spec/features/projects/files/undo_template_spec.rb" -- "./spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb" -- "./spec/features/projects/files/user_browses_files_spec.rb" -- "./spec/features/projects/files/user_browses_lfs_files_spec.rb" -- "./spec/features/projects/files/user_creates_directory_spec.rb" -- "./spec/features/projects/files/user_creates_files_spec.rb" -- "./spec/features/projects/files/user_deletes_files_spec.rb" -- "./spec/features/projects/files/user_edits_files_spec.rb" -- "./spec/features/projects/files/user_find_file_spec.rb" -- "./spec/features/projects/files/user_reads_pipeline_status_spec.rb" -- "./spec/features/projects/files/user_replaces_files_spec.rb" -- "./spec/features/projects/files/user_uploads_files_spec.rb" -- "./spec/features/projects/fork_spec.rb" -- "./spec/features/projects/gfm_autocomplete_load_spec.rb" -- "./spec/features/projects/graph_spec.rb" -- "./spec/features/projects/import_export/export_file_spec.rb" -- "./spec/features/projects/import_export/import_file_spec.rb" -- "./spec/features/projects/infrastructure_registry_spec.rb" -- "./spec/features/projects/integrations/user_activates_asana_spec.rb" -- "./spec/features/projects/integrations/user_activates_assembla_spec.rb" -- "./spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb" -- "./spec/features/projects/integrations/user_activates_flowdock_spec.rb" -- "./spec/features/projects/integrations/user_activates_jira_spec.rb" -- "./spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb" -- "./spec/features/projects/integrations/user_uses_inherited_settings_spec.rb" -- "./spec/features/projects/issuable_templates_spec.rb" -- "./spec/features/projects/issues/design_management/user_paginates_designs_spec.rb" -- "./spec/features/projects/issues/design_management/user_permissions_upload_spec.rb" -- "./spec/features/projects/issues/design_management/user_uploads_designs_spec.rb" -- "./spec/features/projects/issues/design_management/user_views_design_spec.rb" -- "./spec/features/projects/issues/design_management/user_views_designs_spec.rb" -- "./spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb" -- "./spec/features/projects/issues/email_participants_spec.rb" -- "./spec/features/projects/jobs/permissions_spec.rb" -- "./spec/features/projects/jobs_spec.rb" -- "./spec/features/projects/jobs/user_browses_job_spec.rb" -- "./spec/features/projects/jobs/user_browses_jobs_spec.rb" -- "./spec/features/projects/labels/issues_sorted_by_priority_spec.rb" -- "./spec/features/projects/labels/search_labels_spec.rb" -- "./spec/features/projects/labels/sort_labels_spec.rb" -- "./spec/features/projects/labels/subscription_spec.rb" -- "./spec/features/projects/labels/update_prioritization_spec.rb" -- "./spec/features/projects/labels/user_removes_labels_spec.rb" -- "./spec/features/projects/members/anonymous_user_sees_members_spec.rb" -- "./spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb" -- "./spec/features/projects/members/group_members_spec.rb" -- "./spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb" -- "./spec/features/projects/members/groups_with_access_list_spec.rb" -- "./spec/features/projects/members/invite_group_spec.rb" -- "./spec/features/projects/members/list_spec.rb" -- "./spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb" -- "./spec/features/projects/members/master_manages_access_requests_spec.rb" -- "./spec/features/projects/members/sorting_spec.rb" -- "./spec/features/projects/members/tabs_spec.rb" -- "./spec/features/projects/members/user_requests_access_spec.rb" -- "./spec/features/projects/merge_request_button_spec.rb" -- "./spec/features/projects/milestones/gfm_autocomplete_spec.rb" -- "./spec/features/projects/milestones/milestones_sorting_spec.rb" -- "./spec/features/projects/milestones/new_spec.rb" -- "./spec/features/projects/milestones/user_interacts_with_labels_spec.rb" -- "./spec/features/projects/network_graph_spec.rb" -- "./spec/features/projects/new_project_from_template_spec.rb" -- "./spec/features/projects/new_project_spec.rb" -- "./spec/features/projects/packages_spec.rb" -- "./spec/features/projects/pages/user_adds_domain_spec.rb" -- "./spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb" -- "./spec/features/projects/pages/user_edits_settings_spec.rb" -- "./spec/features/projects/pipeline_schedules_spec.rb" - "./spec/features/projects/pipelines/pipeline_spec.rb" -- "./spec/features/projects/pipelines/pipelines_spec.rb" -- "./spec/features/projects/product_analytics/graphs_spec.rb" -- "./spec/features/projects/releases/user_creates_release_spec.rb" -- "./spec/features/projects/releases/user_views_edit_release_spec.rb" -- "./spec/features/projects/releases/user_views_release_spec.rb" -- "./spec/features/projects/releases/user_views_releases_spec.rb" -- "./spec/features/projects/remote_mirror_spec.rb" -- "./spec/features/projects/serverless/functions_spec.rb" -- "./spec/features/projects/services/disable_triggers_spec.rb" -- "./spec/features/projects/services/prometheus_external_alerts_spec.rb" -- "./spec/features/projects/services/user_activates_emails_on_push_spec.rb" -- "./spec/features/projects/services/user_activates_irker_spec.rb" -- "./spec/features/projects/services/user_activates_issue_tracker_spec.rb" -- "./spec/features/projects/services/user_activates_jetbrains_teamcity_ci_spec.rb" -- "./spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb" -- "./spec/features/projects/services/user_activates_packagist_spec.rb" -- "./spec/features/projects/services/user_activates_prometheus_spec.rb" -- "./spec/features/projects/services/user_activates_pushover_spec.rb" -- "./spec/features/projects/services/user_activates_slack_notifications_spec.rb" -- "./spec/features/projects/services/user_activates_slack_slash_command_spec.rb" -- "./spec/features/projects/services/user_views_services_spec.rb" -- "./spec/features/projects/settings/access_tokens_spec.rb" -- "./spec/features/projects/settings/lfs_settings_spec.rb" -- "./spec/features/projects/settings/monitor_settings_spec.rb" -- "./spec/features/projects/settings/packages_settings_spec.rb" -- "./spec/features/projects/settings/project_badges_spec.rb" -- "./spec/features/projects/settings/project_settings_spec.rb" -- "./spec/features/projects/settings/registry_settings_spec.rb" -- "./spec/features/projects/settings/repository_settings_spec.rb" -- "./spec/features/projects/settings/service_desk_setting_spec.rb" -- "./spec/features/projects/settings/user_changes_default_branch_spec.rb" -- "./spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb" -- "./spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb" -- "./spec/features/projects/settings/user_manages_project_members_spec.rb" -- "./spec/features/projects/settings/user_searches_in_settings_spec.rb" -- "./spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb" -- "./spec/features/projects/settings/user_tags_project_spec.rb" -- "./spec/features/projects/settings/user_transfers_a_project_spec.rb" -- "./spec/features/projects/settings/visibility_settings_spec.rb" -- "./spec/features/projects/settings/webhooks_settings_spec.rb" -- "./spec/features/projects/show/schema_markup_spec.rb" -- "./spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb" -- "./spec/features/projects/show/user_interacts_with_stars_spec.rb" -- "./spec/features/projects/show/user_manages_notifications_spec.rb" -- "./spec/features/projects/show/user_sees_collaboration_links_spec.rb" -- "./spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb" -- "./spec/features/projects/show/user_sees_readme_spec.rb" -- "./spec/features/projects/show/user_uploads_files_spec.rb" -- "./spec/features/projects/snippets/create_snippet_spec.rb" -- "./spec/features/projects/snippets/show_spec.rb" -- "./spec/features/projects/snippets/user_comments_on_snippet_spec.rb" -- "./spec/features/projects/snippets/user_deletes_snippet_spec.rb" -- "./spec/features/projects/snippets/user_updates_snippet_spec.rb" -- "./spec/features/projects_spec.rb" -- "./spec/features/projects/sub_group_issuables_spec.rb" -- "./spec/features/projects/tags/user_edits_tags_spec.rb" -- "./spec/features/projects/terraform_spec.rb" -- "./spec/features/projects/tree/create_directory_spec.rb" -- "./spec/features/projects/tree/create_file_spec.rb" -- "./spec/features/projects/tree/tree_show_spec.rb" -- "./spec/features/projects/tree/upload_file_spec.rb" -- "./spec/features/projects/user_changes_project_visibility_spec.rb" -- "./spec/features/projects/user_creates_project_spec.rb" -- "./spec/features/projects/user_sees_sidebar_spec.rb" -- "./spec/features/projects/user_sees_user_popover_spec.rb" -- "./spec/features/projects/user_uses_shortcuts_spec.rb" -- "./spec/features/projects/user_views_empty_project_spec.rb" -- "./spec/features/projects/view_on_env_spec.rb" -- "./spec/features/projects/wikis_spec.rb" -- "./spec/features/projects/wiki/user_views_wiki_empty_spec.rb" -- "./spec/features/project_variables_spec.rb" -- "./spec/features/promotion_spec.rb" -- "./spec/features/protected_branches_spec.rb" -- "./spec/features/protected_tags_spec.rb" -- "./spec/features/reportable_note/commit_spec.rb" -- "./spec/features/reportable_note/issue_spec.rb" -- "./spec/features/reportable_note/merge_request_spec.rb" -- "./spec/features/reportable_note/snippets_spec.rb" -- "./spec/features/runners_spec.rb" -- "./spec/features/search/user_searches_for_code_spec.rb" -- "./spec/features/search/user_searches_for_commits_spec.rb" -- "./spec/features/search/user_searches_for_issues_spec.rb" -- "./spec/features/search/user_searches_for_merge_requests_spec.rb" -- "./spec/features/search/user_searches_for_milestones_spec.rb" -- "./spec/features/search/user_searches_for_projects_spec.rb" -- "./spec/features/search/user_searches_for_users_spec.rb" -- "./spec/features/search/user_searches_for_wiki_pages_spec.rb" -- "./spec/features/search/user_uses_header_search_field_spec.rb" -- "./spec/features/search/user_uses_search_filters_spec.rb" - "./spec/features/signed_commits_spec.rb" -- "./spec/features/snippets/embedded_snippet_spec.rb" -- "./spec/features/snippets/internal_snippet_spec.rb" -- "./spec/features/snippets/notes_on_personal_snippets_spec.rb" -- "./spec/features/snippets/private_snippets_spec.rb" -- "./spec/features/snippets/public_snippets_spec.rb" -- "./spec/features/snippets/show_spec.rb" -- "./spec/features/snippets/user_creates_snippet_spec.rb" -- "./spec/features/snippets/user_deletes_snippet_spec.rb" -- "./spec/features/snippets/user_edits_snippet_spec.rb" -- "./spec/features/tags/developer_creates_tag_spec.rb" -- "./spec/features/tags/developer_deletes_tag_spec.rb" -- "./spec/features/tags/developer_updates_tag_spec.rb" -- "./spec/features/task_lists_spec.rb" -- "./spec/features/triggers_spec.rb" -- "./spec/features/u2f_spec.rb" -- "./spec/features/uploads/user_uploads_avatar_to_profile_spec.rb" -- "./spec/features/uploads/user_uploads_file_to_note_spec.rb" -- "./spec/features/user_can_display_performance_bar_spec.rb" -- "./spec/features/user_opens_link_to_comment_spec.rb" -- "./spec/features/user_sees_revert_modal_spec.rb" -- "./spec/features/users/login_spec.rb" -- "./spec/features/users/logout_spec.rb" -- "./spec/features/users/overview_spec.rb" -- "./spec/features/users/signup_spec.rb" -- "./spec/features/users/snippets_spec.rb" -- "./spec/features/users/terms_spec.rb" -- "./spec/features/users/user_browses_projects_on_user_page_spec.rb" -- "./spec/features/webauthn_spec.rb" -- "./spec/features/whats_new_spec.rb" -- "./spec/finders/ci/pipeline_schedules_finder_spec.rb" -- "./spec/finders/ci/pipelines_finder_spec.rb" -- "./spec/finders/ci/pipelines_for_merge_request_finder_spec.rb" -- "./spec/finders/projects_finder_spec.rb" -- "./spec/finders/releases/evidence_pipeline_finder_spec.rb" -- "./spec/frontend/fixtures/analytics.rb" -- "./spec/frontend/fixtures/jobs.rb" -- "./spec/frontend/fixtures/pipeline_schedules.rb" -- "./spec/frontend/fixtures/pipelines.rb" -- "./spec/graphql/mutations/design_management/upload_spec.rb" -- "./spec/graphql/mutations/merge_requests/accept_spec.rb" -- "./spec/graphql/resolvers/ci/test_report_summary_resolver_spec.rb" - "./spec/helpers/issuables_helper_spec.rb" -- "./spec/initializers/active_record_locking_spec.rb" -- "./spec/initializers/database_config_spec.rb" - "./spec/lib/gitlab/auth_spec.rb" -- "./spec/lib/gitlab/ci/badge/pipeline/status_spec.rb" -- "./spec/lib/gitlab/ci/build/policy/changes_spec.rb" -- "./spec/lib/gitlab/ci/charts_spec.rb" -- "./spec/lib/gitlab/ci/config_spec.rb" - "./spec/lib/gitlab/ci/pipeline/chain/create_spec.rb" - "./spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb" - "./spec/lib/gitlab/ci/pipeline/seed/build_spec.rb" -- "./spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb" -- "./spec/lib/gitlab/ci/status/stage/common_spec.rb" -- "./spec/lib/gitlab/ci/status/stage/factory_spec.rb" -- "./spec/lib/gitlab/ci/status/stage/play_manual_spec.rb" - "./spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb" -- "./spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb" - "./spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb" - "./spec/lib/gitlab/ci/templates/Jobs/deploy_gitlab_ci_yaml_spec.rb" +- "./spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb" - "./spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb" -- "./spec/lib/gitlab/database/bulk_update_spec.rb" -- "./spec/lib/gitlab/database/connection_spec.rb" -- "./spec/lib/gitlab/database/load_balancing/host_spec.rb" -- "./spec/lib/gitlab/database/load_balancing_spec.rb" -- "./spec/lib/gitlab/database/postgresql_adapter/force_disconnectable_mixin_spec.rb" -- "./spec/lib/gitlab/database/postgresql_adapter/type_map_cache_spec.rb" -- "./spec/lib/gitlab/database/schema_migrations/context_spec.rb" -- "./spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb" -- "./spec/lib/gitlab/database/with_lock_retries_spec.rb" -- "./spec/lib/gitlab/data_builder/pipeline_spec.rb" - "./spec/lib/gitlab/email/handler/create_issue_handler_spec.rb" - "./spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb" - "./spec/lib/gitlab/email/handler/create_note_handler_spec.rb" - "./spec/lib/gitlab/email/handler/create_note_on_issuable_handler_spec.rb" -- "./spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb" -- "./spec/lib/gitlab/usage_data_spec.rb" - "./spec/lib/peek/views/active_record_spec.rb" -- "./spec/mailers/emails/pipelines_spec.rb" -- "./spec/migrations/20210205174154_remove_bad_dependency_proxy_manifests_spec.rb" -- "./spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb" -- "./spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb" -- "./spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb" -- "./spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb" -- "./spec/models/ci/bridge_spec.rb" - "./spec/models/ci/build_need_spec.rb" -- "./spec/models/ci/build_spec.rb" - "./spec/models/ci/build_trace_chunk_spec.rb" -- "./spec/models/ci/commit_with_pipeline_spec.rb" -- "./spec/models/ci/group_spec.rb" - "./spec/models/ci/group_variable_spec.rb" -- "./spec/models/ci/instance_variable_spec.rb" - "./spec/models/ci/job_artifact_spec.rb" - "./spec/models/ci/job_variable_spec.rb" -- "./spec/models/ci/legacy_stage_spec.rb" -- "./spec/models/ci/pipeline_schedule_spec.rb" - "./spec/models/ci/pipeline_spec.rb" -- "./spec/models/ci/runner_namespace_spec.rb" -- "./spec/models/ci/runner_project_spec.rb" - "./spec/models/ci/runner_spec.rb" -- "./spec/models/ci/running_build_spec.rb" -- "./spec/models/ci/stage_spec.rb" - "./spec/models/ci/variable_spec.rb" -- "./spec/models/clusters/applications/jupyter_spec.rb" - "./spec/models/clusters/applications/runner_spec.rb" -- "./spec/models/commit_collection_spec.rb" - "./spec/models/commit_status_spec.rb" - "./spec/models/concerns/batch_destroy_dependent_associations_spec.rb" - "./spec/models/concerns/bulk_insertable_associations_spec.rb" -- "./spec/models/concerns/cron_schedulable_spec.rb" - "./spec/models/concerns/has_environment_scope_spec.rb" -- "./spec/models/concerns/schedulable_spec.rb" - "./spec/models/concerns/token_authenticatable_spec.rb" - "./spec/models/design_management/version_spec.rb" -- "./spec/models/environment_status_spec.rb" - "./spec/models/hooks/system_hook_spec.rb" -- "./spec/models/issue_spec.rb" - "./spec/models/members/project_member_spec.rb" -- "./spec/models/merge_request_spec.rb" -- "./spec/models/plan_spec.rb" -- "./spec/models/project_feature_usage_spec.rb" -- "./spec/models/project_spec.rb" - "./spec/models/spam_log_spec.rb" - "./spec/models/user_spec.rb" - "./spec/models/user_status_spec.rb" -- "./spec/policies/ci/build_policy_spec.rb" -- "./spec/policies/ci/pipeline_policy_spec.rb" -- "./spec/presenters/ci/stage_presenter_spec.rb" -- "./spec/requests/api/admin/ci/variables_spec.rb" -- "./spec/requests/api/admin/plan_limits_spec.rb" -- "./spec/requests/api/ci/jobs_spec.rb" - "./spec/requests/api/ci/pipeline_schedules_spec.rb" - "./spec/requests/api/ci/pipelines_spec.rb" -- "./spec/requests/api/ci/runner/runners_post_spec.rb" -- "./spec/requests/api/ci/runners_spec.rb" -- "./spec/requests/api/commits_spec.rb" - "./spec/requests/api/commit_statuses_spec.rb" -- "./spec/requests/api/graphql/ci/runner_spec.rb" +- "./spec/requests/api/commits_spec.rb" - "./spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb" -- "./spec/requests/api/graphql/project/issues_spec.rb" -- "./spec/requests/api/graphql/project/merge_request_spec.rb" -- "./spec/requests/api/graphql/project_query_spec.rb" -- "./spec/requests/api/issues/issues_spec.rb" -- "./spec/requests/api/merge_requests_spec.rb" -- "./spec/requests/api/projects_spec.rb" - "./spec/requests/api/resource_access_tokens_spec.rb" - "./spec/requests/api/users_spec.rb" -- "./spec/requests/lfs_http_spec.rb" -- "./spec/requests/projects/cycle_analytics_events_spec.rb" -- "./spec/serializers/ci/downloadable_artifact_entity_spec.rb" -- "./spec/serializers/ci/downloadable_artifact_serializer_spec.rb" -- "./spec/serializers/ci/pipeline_entity_spec.rb" -- "./spec/serializers/merge_request_poll_cached_widget_entity_spec.rb" -- "./spec/serializers/merge_request_poll_widget_entity_spec.rb" -- "./spec/serializers/merge_request_widget_entity_spec.rb" -- "./spec/serializers/pipeline_details_entity_spec.rb" -- "./spec/serializers/pipeline_serializer_spec.rb" -- "./spec/serializers/stage_entity_spec.rb" -- "./spec/serializers/stage_serializer_spec.rb" -- "./spec/serializers/test_report_entity_spec.rb" -- "./spec/serializers/test_report_summary_entity_spec.rb" -- "./spec/serializers/test_suite_entity_spec.rb" -- "./spec/serializers/test_suite_summary_entity_spec.rb" -- "./spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb" -- "./spec/services/ci/compare_accessibility_reports_service_spec.rb" -- "./spec/services/ci/compare_codequality_reports_service_spec.rb" -- "./spec/services/ci/compare_reports_base_service_spec.rb" -- "./spec/services/ci/compare_test_reports_service_spec.rb" - "./spec/services/ci/create_pipeline_service/environment_spec.rb" - "./spec/services/ci/create_pipeline_service_spec.rb" - "./spec/services/ci/destroy_pipeline_service_spec.rb" -- "./spec/services/ci/disable_user_pipeline_schedules_service_spec.rb" - "./spec/services/ci/ensure_stage_service_spec.rb" - "./spec/services/ci/expire_pipeline_cache_service_spec.rb" -- "./spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb" -- "./spec/services/ci/generate_coverage_reports_service_spec.rb" - "./spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb" - "./spec/services/ci/job_artifacts/destroy_associations_service_spec.rb" -- "./spec/services/ci/job_artifacts/destroy_batch_service_spec.rb" -- "./spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb" -- "./spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb" - "./spec/services/ci/pipeline_bridge_status_service_spec.rb" -- "./spec/services/ci/pipeline_processing/shared_processing_service.rb" - "./spec/services/ci/pipelines/add_job_service_spec.rb" -- "./spec/services/ci/pipeline_schedule_service_spec.rb" -- "./spec/services/ci/pipeline_trigger_service_spec.rb" -- "./spec/services/ci/register_job_service_spec.rb" - "./spec/services/ci/retry_build_service_spec.rb" -- "./spec/services/ci/test_failure_history_service_spec.rb" -- "./spec/services/ci/update_instance_variables_service_spec.rb" -- "./spec/services/deployments/update_environment_service_spec.rb" -- "./spec/services/design_management/save_designs_service_spec.rb" -- "./spec/services/environments/stop_service_spec.rb" - "./spec/services/groups/transfer_service_spec.rb" -- "./spec/services/integrations/test/project_service_spec.rb" -- "./spec/services/issuable/destroy_service_spec.rb" -- "./spec/services/issue_links/list_service_spec.rb" -- "./spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb" -- "./spec/services/merge_requests/mergeability_check_service_spec.rb" -- "./spec/services/merge_requests/post_merge_service_spec.rb" -- "./spec/services/merge_requests/refresh_service_spec.rb" -- "./spec/services/pages/migrate_from_legacy_storage_service_spec.rb" - "./spec/services/projects/destroy_service_spec.rb" +- "./spec/services/projects/overwrite_project_service_spec.rb" - "./spec/services/projects/transfer_service_spec.rb" -- "./spec/services/projects/update_service_spec.rb" -- "./spec/services/releases/create_service_spec.rb" - "./spec/services/resource_access_tokens/revoke_service_spec.rb" -- "./spec/services/todo_service_spec.rb" -- "./spec/services/users/activity_service_spec.rb" - "./spec/services/users/destroy_service_spec.rb" - "./spec/services/users/reject_service_spec.rb" -- "./spec/support/shared_contexts/email_shared_context.rb" -- "./spec/support/shared_examples/controllers/access_tokens_controller_shared_examples.rb" -- "./spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb" -- "./spec/support/shared_examples/integrations/test_examples.rb" -- "./spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb" -- "./spec/support/shared_examples/models/cluster_application_status_shared_examples.rb" -- "./spec/support/shared_examples/models/cluster_application_version_shared_examples.rb" -- "./spec/support/shared_examples/models/concerns/cron_schedulable_shared_examples.rb" -- "./spec/support/shared_examples/models/concerns/limitable_shared_examples.rb" -- "./spec/support/shared_examples/models/update_highest_role_shared_examples.rb" -- "./spec/support/shared_examples/models/update_project_statistics_shared_examples.rb" -- "./spec/support/shared_examples/models/with_uploads_shared_examples.rb" -- "./spec/support/shared_examples/requests/api/status_shared_examples.rb" -- "./spec/support/shared_examples/requests/lfs_http_shared_examples.rb" -- "./spec/support/shared_examples/services/destroy_label_links_shared_examples.rb" -- "./spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb" -- "./spec/support/shared_examples/services/notification_service_shared_examples.rb" -- "./spec/support/shared_examples/services/wiki_pages/create_service_shared_examples.rb" -- "./spec/support/shared_examples/services/wiki_pages/destroy_service_shared_examples.rb" -- "./spec/support/shared_examples/services/wiki_pages/update_service_shared_examples.rb" -- "./spec/support/shared_examples/workers/idempotency_shared_examples.rb" -- "./spec/views/projects/artifacts/_artifact.html.haml_spec.rb" -- "./spec/views/projects/commits/_commit.html.haml_spec.rb" -- "./spec/views/projects/jobs/_build.html.haml_spec.rb" -- "./spec/views/projects/jobs/_generic_commit_status.html.haml_spec.rb" -- "./spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb" -- "./spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb" -- "./spec/views/shared/runners/_runner_details.html.haml_spec.rb" -- "./spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb" -- "./spec/workers/ci/pipeline_artifacts/create_quality_report_worker_spec.rb" -- "./spec/workers/container_expiration_policy_worker_spec.rb" - "./spec/workers/merge_requests/create_pipeline_worker_spec.rb" -- "./spec/workers/pipeline_metrics_worker_spec.rb" -- "./spec/workers/pipeline_schedule_worker_spec.rb" -- "./spec/workers/releases/create_evidence_worker_spec.rb" - "./spec/workers/remove_expired_members_worker_spec.rb" - "./spec/workers/repository_cleanup_worker_spec.rb" -- "./spec/workers/stage_update_worker_spec.rb" -- "./spec/workers/stuck_merge_jobs_worker_spec.rb" -- "./ee/spec/requests/api/graphql/project/pipelines/dast_profile_spec.rb" -- "./spec/services/projects/overwrite_project_service_spec.rb" diff --git a/spec/support/database/cross-join-allowlist.yml b/spec/support/database/cross-join-allowlist.yml index c209d275fc8..19b1ce30d5f 100644 --- a/spec/support/database/cross-join-allowlist.yml +++ b/spec/support/database/cross-join-allowlist.yml @@ -1,58 +1,6 @@ -- "./ee/spec/features/ci/ci_minutes_spec.rb" -- "./ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb" -- "./ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb" -- "./ee/spec/finders/ee/namespaces/projects_finder_spec.rb" -- "./ee/spec/graphql/ee/resolvers/namespace_projects_resolver_spec.rb" -- "./ee/spec/models/ci/minutes/project_monthly_usage_spec.rb" -- "./ee/spec/models/project_spec.rb" -- "./ee/spec/models/security/finding_spec.rb" -- "./ee/spec/models/security/scan_spec.rb" -- "./ee/spec/requests/api/ci/minutes_spec.rb" -- "./ee/spec/requests/api/graphql/ci/minutes/usage_spec.rb" -- "./ee/spec/requests/api/namespaces_spec.rb" -- "./ee/spec/services/ci/minutes/additional_packs/change_namespace_service_spec.rb" -- "./ee/spec/services/ci/minutes/additional_packs/create_service_spec.rb" -- "./ee/spec/services/ci/minutes/refresh_cached_data_service_spec.rb" -- "./spec/controllers/admin/runners_controller_spec.rb" -- "./spec/controllers/groups/settings/ci_cd_controller_spec.rb" -- "./spec/controllers/projects/settings/ci_cd_controller_spec.rb" -- "./spec/features/admin/admin_runners_spec.rb" -- "./spec/features/ide/user_opens_merge_request_spec.rb" -- "./spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb" -- "./spec/features/projects/infrastructure_registry_spec.rb" -- "./spec/finders/ci/pipelines_for_merge_request_finder_spec.rb" -- "./spec/finders/ci/runners_finder_spec.rb" -- "./spec/frontend/fixtures/runner.rb" -- "./spec/graphql/resolvers/ci/group_runners_resolver_spec.rb" -- "./spec/lib/api/entities/package_spec.rb" - "./spec/lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans_spec.rb" - "./spec/lib/gitlab/background_migration/migrate_pages_metadata_spec.rb" - "./spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb" - "./spec/migrations/associate_existing_dast_builds_with_variables_spec.rb" +- "./spec/migrations/disable_job_token_scope_when_unused_spec.rb" - "./spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb" -- "./spec/migrations/schedule_pages_metadata_migration_spec.rb" -- "./spec/models/ci/pipeline_spec.rb" -- "./spec/models/ci/runner_spec.rb" -- "./spec/models/merge_request_spec.rb" -- "./spec/models/project_spec.rb" -- "./spec/models/user_spec.rb" -- "./spec/presenters/packages/detail/package_presenter_spec.rb" -- "./spec/requests/api/ci/runner/runners_post_spec.rb" -- "./spec/requests/api/ci/runners_spec.rb" -- "./spec/requests/api/graphql/ci/runner_spec.rb" -- "./spec/requests/api/graphql/group_query_spec.rb" -- "./spec/requests/api/graphql/packages/composer_spec.rb" -- "./spec/requests/api/graphql/packages/conan_spec.rb" -- "./spec/requests/api/graphql/packages/maven_spec.rb" -- "./spec/requests/api/graphql/packages/nuget_spec.rb" -- "./spec/requests/api/graphql/packages/package_spec.rb" -- "./spec/requests/api/graphql/packages/pypi_spec.rb" -- "./spec/requests/api/package_files_spec.rb" -- "./spec/services/environments/stop_service_spec.rb" -- "./spec/services/merge_requests/post_merge_service_spec.rb" -- "./spec/support/shared_examples/features/packages_shared_examples.rb" -- "./spec/support/shared_examples/models/concerns/limitable_shared_examples.rb" -- "./spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb" -- "./spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb" -- "./spec/support/shared_examples/requests/graphql_shared_examples.rb" -- "./spec/support/shared_examples/services/packages_shared_examples.rb" diff --git a/spec/support/database/gitlab_schema.rb b/spec/support/database/gitlab_schema.rb deleted file mode 100644 index fe05fb998e6..00000000000 --- a/spec/support/database/gitlab_schema.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -# This module gathes information about table to schema mapping -# to understand table affinity -module Database - module GitlabSchema - def self.table_schemas(tables) - tables.map { |table| table_schema(table) }.to_set - end - - def self.table_schema(name) - tables_to_schema[name] || :undefined - end - - def self.tables_to_schema - @tables_to_schema ||= all_classes_with_schema.to_h do |klass| - [klass.table_name, klass.gitlab_schema] - end - end - - def self.all_classes_with_schema - ActiveRecord::Base.descendants.reject(&:abstract_class?).select(&:gitlab_schema?) # rubocop:disable Database/MultipleDatabases - end - end -end diff --git a/spec/support/database/multiple_databases.rb b/spec/support/database/multiple_databases.rb index 5e1ae60536f..9e72ea589e3 100644 --- a/spec/support/database/multiple_databases.rb +++ b/spec/support/database/multiple_databases.rb @@ -6,6 +6,18 @@ module Database skip 'Skipping because multiple databases not set up' unless Gitlab::Database.has_config?(:ci) end + def reconfigure_db_connection(name: nil, config_hash: {}, model: ActiveRecord::Base, config_model: nil) + db_config = (config_model || model).connection_db_config + + new_db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new( + db_config.env_name, + name ? name.to_s : db_config.name, + db_config.configuration_hash.merge(config_hash) + ) + + model.establish_connection(new_db_config) + end + # The usage of this method switches temporarily used `connection_handler` # allowing full manipulation of ActiveRecord::Base connections without # having side effects like: @@ -56,6 +68,21 @@ RSpec.configure do |config| example.run end end + + config.around(:each, :mocked_ci_connection) do |example| + with_reestablished_active_record_base(reconnect: true) do + reconfigure_db_connection( + name: :ci, + model: Ci::ApplicationRecord, + config_model: ActiveRecord::Base + ) + + example.run + + # Cleanup connection_specification_name for Ci::ApplicationRecord + Ci::ApplicationRecord.remove_connection + end + end end ActiveRecord::Base.singleton_class.prepend(::Database::ActiveRecordBaseEstablishConnection) # rubocop:disable Database/MultipleDatabases diff --git a/spec/support/database/prevent_cross_database_modification.rb b/spec/support/database/prevent_cross_database_modification.rb index 7ded85b65ce..c509aecf9b8 100644 --- a/spec/support/database/prevent_cross_database_modification.rb +++ b/spec/support/database/prevent_cross_database_modification.rb @@ -1,123 +1,31 @@ # frozen_string_literal: true -module Database - module PreventCrossDatabaseModification - CrossDatabaseModificationAcrossUnsupportedTablesError = Class.new(StandardError) - - module GitlabDatabaseMixin - def allow_cross_database_modification_within_transaction(url:) - cross_database_context = Database::PreventCrossDatabaseModification.cross_database_context - return yield unless cross_database_context && cross_database_context[:enabled] - - transaction_tracker_enabled_was = cross_database_context[:enabled] - cross_database_context[:enabled] = false - - yield - ensure - cross_database_context[:enabled] = transaction_tracker_enabled_was if cross_database_context - end - end - - module SpecHelpers - def with_cross_database_modification_prevented - subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, start, finish, id, payload| - PreventCrossDatabaseModification.prevent_cross_database_modification!(payload[:connection], payload[:sql]) - end - - PreventCrossDatabaseModification.reset_cross_database_context! - PreventCrossDatabaseModification.cross_database_context.merge!(enabled: true, subscriber: subscriber) - - yield if block_given? - ensure - cleanup_with_cross_database_modification_prevented if block_given? - end - - def cleanup_with_cross_database_modification_prevented - if PreventCrossDatabaseModification.cross_database_context - ActiveSupport::Notifications.unsubscribe(PreventCrossDatabaseModification.cross_database_context[:subscriber]) - PreventCrossDatabaseModification.cross_database_context[:enabled] = false - end - end - end - - def self.cross_database_context - Thread.current[:transaction_tracker] - end - - def self.reset_cross_database_context! - Thread.current[:transaction_tracker] = initial_data - end - - def self.initial_data - { - enabled: false, - transaction_depth_by_db: Hash.new { |h, k| h[k] = 0 }, - modified_tables_by_db: Hash.new { |h, k| h[k] = Set.new } - } - end - - def self.prevent_cross_database_modification!(connection, sql) - return unless cross_database_context - return unless cross_database_context[:enabled] - - return if connection.pool.instance_of?(ActiveRecord::ConnectionAdapters::NullPool) - - database = connection.pool.db_config.name - - if sql.start_with?('SAVEPOINT') - cross_database_context[:transaction_depth_by_db][database] += 1 - - return - elsif sql.start_with?('RELEASE SAVEPOINT', 'ROLLBACK TO SAVEPOINT') - cross_database_context[:transaction_depth_by_db][database] -= 1 - if cross_database_context[:transaction_depth_by_db][database] <= 0 - cross_database_context[:modified_tables_by_db][database].clear - end - - return - end - - return if cross_database_context[:transaction_depth_by_db].values.all?(&:zero?) - - # PgQuery might fail in some cases due to limited nesting: - # https://github.com/pganalyze/pg_query/issues/209 - parsed_query = PgQuery.parse(sql) - tables = sql.downcase.include?(' for update') ? parsed_query.tables : parsed_query.dml_tables - - return if tables.empty? - - cross_database_context[:modified_tables_by_db][database].merge(tables) - - all_tables = cross_database_context[:modified_tables_by_db].values.map(&:to_a).flatten - schemas = Database::GitlabSchema.table_schemas(all_tables) - - if schemas.many? - raise Database::PreventCrossDatabaseModification::CrossDatabaseModificationAcrossUnsupportedTablesError, - "Cross-database data modification of '#{schemas.to_a.join(", ")}' were detected within " \ - "a transaction modifying the '#{all_tables.to_a.join(", ")}' tables." \ - "Please refer to https://docs.gitlab.com/ee/development/database/multiple_databases.html#removing-cross-database-transactions for details on how to resolve this exception." - end - end - end +module PreventCrossDatabaseModificationSpecHelpers + delegate :with_cross_database_modification_prevented, + :allow_cross_database_modification_within_transaction, + to: :'::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification' end -Gitlab::Database.singleton_class.prepend( - Database::PreventCrossDatabaseModification::GitlabDatabaseMixin) - CROSS_DB_MODIFICATION_ALLOW_LIST = Set.new(YAML.load_file(File.join(__dir__, 'cross-database-modification-allowlist.yml'))).freeze RSpec.configure do |config| - config.include(::Database::PreventCrossDatabaseModification::SpecHelpers) + config.include(PreventCrossDatabaseModificationSpecHelpers) + + # By default allow cross-modifications as we want to observe only transactions + # within a specific block of execution which is defined be `before(:each)` and `after(:each)` + config.before(:all) do + ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.suppress = true + end # Using before and after blocks because the around block causes problems with the let_it_be # record creations. It makes an extra savepoint which breaks the transaction count logic. config.before do |example_file| - if CROSS_DB_MODIFICATION_ALLOW_LIST.exclude?(example_file.file_path) - with_cross_database_modification_prevented - end + ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.suppress = + CROSS_DB_MODIFICATION_ALLOW_LIST.include?(example_file.file_path_rerun_argument) end + # Reset after execution to preferred state config.after do |example_file| - cleanup_with_cross_database_modification_prevented + ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.suppress = true end end diff --git a/spec/support/database/prevent_cross_joins.rb b/spec/support/database/prevent_cross_joins.rb index f5ed2a8f22e..e69374fbc70 100644 --- a/spec/support/database/prevent_cross_joins.rb +++ b/spec/support/database/prevent_cross_joins.rb @@ -35,7 +35,7 @@ module Database # https://github.com/pganalyze/pg_query/issues/209 tables = PgQuery.parse(sql).tables - schemas = Database::GitlabSchema.table_schemas(tables) + schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables) if schemas.include?(:gitlab_ci) && schemas.include?(:gitlab_main) Thread.current[:has_cross_join_exception] = true @@ -96,7 +96,7 @@ RSpec.configure do |config| config.around do |example| Thread.current[:has_cross_join_exception] = false - if ALLOW_LIST.include?(example.file_path) + if ALLOW_LIST.include?(example.file_path_rerun_argument) example.run else with_cross_joins_prevented { example.run } diff --git a/spec/support/database/query_analyzer.rb b/spec/support/database/query_analyzer.rb new file mode 100644 index 00000000000..85fa55f81ef --- /dev/null +++ b/spec/support/database/query_analyzer.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# With the usage of `describe '...', query_analyzers: false` +# can be disabled selectively + +RSpec.configure do |config| + config.around do |example| + if example.metadata.fetch(:query_analyzers, true) + ::Gitlab::Database::QueryAnalyzer.instance.within { example.run } + else + example.run + end + end +end diff --git a/spec/support/database_load_balancing.rb b/spec/support/database_load_balancing.rb index 014575e8a82..d2902ddcc7c 100644 --- a/spec/support/database_load_balancing.rb +++ b/spec/support/database_load_balancing.rb @@ -2,17 +2,17 @@ RSpec.configure do |config| config.around(:each, :database_replica) do |example| - old_proxies = [] + old_proxies = {} Gitlab::Database::LoadBalancing.base_models.each do |model| + old_proxies[model] = [model.load_balancer, model.connection, model.sticking] + config = Gitlab::Database::LoadBalancing::Configuration .new(model, [model.connection_db_config.configuration_hash[:host]]) - lb = Gitlab::Database::LoadBalancing::LoadBalancer.new(config) - - old_proxies << [model, model.connection] - model.connection = - Gitlab::Database::LoadBalancing::ConnectionProxy.new(lb) + model.load_balancer = Gitlab::Database::LoadBalancing::LoadBalancer.new(config) + model.sticking = Gitlab::Database::LoadBalancing::Sticking.new(model.load_balancer) + model.connection = Gitlab::Database::LoadBalancing::ConnectionProxy.new(model.load_balancer) end Gitlab::Database::LoadBalancing::Session.clear_session @@ -23,8 +23,8 @@ RSpec.configure do |config| Gitlab::Database::LoadBalancing::Session.clear_session redis_shared_state_cleanup! - old_proxies.each do |(model, proxy)| - model.connection = proxy + old_proxies.each do |model, proxy| + model.load_balancer, model.connection, model.sticking = proxy end end end diff --git a/spec/support/flaky_tests.rb b/spec/support/flaky_tests.rb new file mode 100644 index 00000000000..30a064d8705 --- /dev/null +++ b/spec/support/flaky_tests.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +return unless ENV['CI'] +return unless ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "true" +return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests') + +require_relative '../../tooling/rspec_flaky/report' + +RSpec.configure do |config| + $flaky_test_example_ids = begin # rubocop:disable Style/GlobalVars + raise "$SUITE_FLAKY_RSPEC_REPORT_PATH is empty." if ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'].to_s.empty? + raise "#{ENV['SUITE_FLAKY_RSPEC_REPORT_PATH']} doesn't exist" unless File.exist?(ENV['SUITE_FLAKY_RSPEC_REPORT_PATH']) + + RspecFlaky::Report.load(ENV['SUITE_FLAKY_RSPEC_REPORT_PATH']).map { |_, flaky_test_data| flaky_test_data["example_id"] } + rescue => e # rubocop:disable Style/RescueStandardError + puts e + [] + end + $skipped_flaky_tests_report = [] # rubocop:disable Style/GlobalVars + + config.around do |example| + # Skip flaky tests automatically + if $flaky_test_example_ids.include?(example.id) # rubocop:disable Style/GlobalVars + puts "Skipping #{example.id} '#{example.full_description}' because it's flaky." + $skipped_flaky_tests_report << example.id # rubocop:disable Style/GlobalVars + else + example.run + end + end + + config.after(:suite) do + next unless ENV['SKIPPED_FLAKY_TESTS_REPORT_PATH'] + + File.write(ENV['SKIPPED_FLAKY_TESTS_REPORT_PATH'], "#{$skipped_flaky_tests_report.join("\n")}\n") # rubocop:disable Style/GlobalVars + end +end diff --git a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb index de9735df546..4624a8ac82a 100644 --- a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb +++ b/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb @@ -6,7 +6,9 @@ RSpec.shared_examples 'a correct instrumented metric value' do |params| let(:metric) { described_class.new(time_frame: time_frame, options: options) } before do - allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false) + if described_class.respond_to?(:relation) && described_class.relation.respond_to?(:connection) + allow(described_class.relation.connection).to receive(:transaction_open?).and_return(false) + end end it 'has correct value' do diff --git a/spec/support/graphql/fake_query_type.rb b/spec/support/graphql/fake_query_type.rb new file mode 100644 index 00000000000..ffd851a6e6a --- /dev/null +++ b/spec/support/graphql/fake_query_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Graphql + class FakeQueryType < Types::BaseObject + graphql_name 'FakeQuery' + + field :hello_world, String, null: true do + argument :message, String, required: false + end + + def hello_world(message: "world") + "Hello #{message}!" + end + end +end diff --git a/spec/support/graphql/fake_tracer.rb b/spec/support/graphql/fake_tracer.rb new file mode 100644 index 00000000000..c2fb7ed12d8 --- /dev/null +++ b/spec/support/graphql/fake_tracer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Graphql + class FakeTracer + def initialize(trace_callback) + @trace_callback = trace_callback + end + + def trace(*args) + @trace_callback.call(*args) + + yield + end + end +end diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 3ec52f8c832..722d484609c 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -63,6 +63,10 @@ module CycleAnalyticsHelpers wait_for_requests end + def click_save_value_stream_button + click_button(_('Save value stream')) + end + def create_custom_value_stream(custom_value_stream_name) toggle_value_stream_dropdown page.find_button(_('Create new Value Stream')).click diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb index 69ba20c1ca4..3502558b2c2 100644 --- a/spec/support/helpers/features/invite_members_modal_helper.rb +++ b/spec/support/helpers/features/invite_members_modal_helper.rb @@ -8,7 +8,7 @@ module Spec def invite_member(name, role: 'Guest', expires_at: nil, area_of_focus: false) click_on 'Invite members' - page.within '#invite-members-modal' do + page.within '[data-testid="invite-members-modal"]' do find('[data-testid="members-token-select-input"]').set(name) wait_for_requests diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb index 5cfd03ecea8..8a329c2f9dd 100644 --- a/spec/support/helpers/gitaly_setup.rb +++ b/spec/support/helpers/gitaly_setup.rb @@ -98,7 +98,7 @@ module GitalySetup end def build_gitaly - system(env, 'make', chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection + system(env.merge({ 'GIT_VERSION' => nil }), 'make all git', chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection end def start_gitaly diff --git a/spec/support/helpers/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb index 813c6176317..81e669aab57 100644 --- a/spec/support/helpers/gpg_helpers.rb +++ b/spec/support/helpers/gpg_helpers.rb @@ -4,6 +4,7 @@ module GpgHelpers SIGNED_COMMIT_SHA = '8a852d50dda17cc8fd1408d2fd0c5b0f24c76ca4' SIGNED_AND_AUTHORED_SHA = '3c1d9a0266cb0c62d926f4a6c649beed561846f5' DIFFERING_EMAIL_SHA = 'a17a9f66543673edf0a3d1c6b93bdda3fe600f32' + MULTIPLE_SIGNATURES_SHA = 'c7794c14268d67ad8a2d5f066d706539afc75a96' module User1 extend self diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 6f17d3cb496..ee4621deb2d 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -522,8 +522,7 @@ module GraphqlHelpers end end - # See note at graphql_data about memoization and multiple requests - def graphql_errors(body = json_response) + def graphql_errors(body = fresh_response_data) case body when Hash # regular query body['errors'] diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb index 7799e49d4c1..0c5bf09f6b7 100644 --- a/spec/support/helpers/migrations_helpers.rb +++ b/spec/support/helpers/migrations_helpers.rb @@ -2,7 +2,7 @@ module MigrationsHelpers def active_record_base - ActiveRecord::Base + Gitlab::Database.database_base_models.fetch(self.class.metadata[:database] || :main) end def table(name) @@ -34,7 +34,7 @@ module MigrationsHelpers end def migrations_paths - ActiveRecord::Migrator.migrations_paths + active_record_base.connection.migrations_paths end def migration_context @@ -52,7 +52,7 @@ module MigrationsHelpers end def foreign_key_exists?(source, target = nil, column: nil) - ActiveRecord::Base.connection.foreign_keys(source).any? do |key| + active_record_base.connection.foreign_keys(source).any? do |key| if column key.options[:column].to_s == column.to_s else diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb index 96e79427278..c2ec82155cd 100644 --- a/spec/support/helpers/navbar_structure_helper.rb +++ b/spec/support/helpers/navbar_structure_helper.rb @@ -29,6 +29,19 @@ module NavbarStructureHelper ) end + def insert_customer_relations_nav(within) + insert_after_nav_item( + within, + new_nav_item: { + nav_item: _('Customer relations'), + nav_sub_items: [ + _('Contacts'), + _('Organizations') + ] + } + ) + end + def insert_container_nav insert_after_sub_nav_item( _('Package Registry'), diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb index 4b4285f251e..84b5dbc1d23 100644 --- a/spec/support/helpers/project_forks_helper.rb +++ b/spec/support/helpers/project_forks_helper.rb @@ -28,11 +28,15 @@ module ProjectForksHelper unless params[:target_project] || params[:using_service] target_level = [project.visibility_level, namespace.visibility_level].min visibility_level = Gitlab::VisibilityLevel.closest_allowed_level(target_level) + # Builds and MRs can't have higher visibility level than repository access level. + builds_access_level = [project.builds_access_level, project.repository_access_level].min params[:target_project] = create(:project, (:repository if create_repository), - visibility_level: visibility_level, creator: user, namespace: namespace) + visibility_level: visibility_level, + builds_access_level: builds_access_level, + creator: user, namespace: namespace) end service = Projects::ForkService.new(project, user, params) diff --git a/spec/support/helpers/require_migration.rb b/spec/support/helpers/require_migration.rb index de3a8a81ab5..ee28f8e504c 100644 --- a/spec/support/helpers/require_migration.rb +++ b/spec/support/helpers/require_migration.rb @@ -15,7 +15,7 @@ class RequireMigration end MIGRATION_FOLDERS = %w[db/migrate db/post_migrate].freeze - SPEC_FILE_PATTERN = %r{.+/(?<file_name>.+)_spec\.rb}.freeze + SPEC_FILE_PATTERN = %r{.+/(?:\d+_)?(?<file_name>.+)_spec\.rb}.freeze class << self def require_migration!(file_name) @@ -26,10 +26,12 @@ class RequireMigration end def search_migration_file(file_name) + migration_file_pattern = /\A\d+_#{file_name}\.rb\z/ + migration_folders.flat_map do |path| migration_path = Rails.root.join(path).to_s - Find.find(migration_path).select { |m| File.basename(m).match? /\A\d+_#{file_name}\.rb\z/ } + Find.find(migration_path).select { |m| migration_file_pattern.match? File.basename(m) } end end diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb index 6f530d57caf..ef3c39c83c2 100644 --- a/spec/support/helpers/stub_gitlab_calls.rb +++ b/spec/support/helpers/stub_gitlab_calls.rb @@ -92,9 +92,16 @@ module StubGitlabCalls end def stub_commonmark_sourcepos_disabled + render_options = + if Feature.enabled?(:use_cmark_renderer) + Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_C + else + Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS_RUBY + end + allow_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) .to receive(:render_options) - .and_return(Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS) + .and_return(render_options) end private diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index 56177d445d6..5e86b08aa45 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -4,7 +4,6 @@ module StubObjectStorage def stub_dependency_proxy_object_storage(**params) stub_object_storage_uploader(config: ::Gitlab.config.dependency_proxy.object_store, uploader: ::DependencyProxy::FileUploader, - remote_directory: 'dependency_proxy', **params) end @@ -16,7 +15,6 @@ module StubObjectStorage def stub_object_storage_uploader( config:, uploader:, - remote_directory:, enabled: true, proxy_download: false, background_upload: false, @@ -40,7 +38,7 @@ module StubObjectStorage return unless enabled stub_object_storage(connection_params: uploader.object_store_credentials, - remote_directory: remote_directory) + remote_directory: config.remote_directory) end def stub_object_storage(connection_params:, remote_directory:) @@ -60,56 +58,48 @@ module StubObjectStorage def stub_artifacts_object_storage(uploader = JobArtifactUploader, **params) stub_object_storage_uploader(config: Gitlab.config.artifacts.object_store, uploader: uploader, - remote_directory: 'artifacts', **params) end def stub_external_diffs_object_storage(uploader = described_class, **params) stub_object_storage_uploader(config: Gitlab.config.external_diffs.object_store, uploader: uploader, - remote_directory: 'external-diffs', **params) end def stub_lfs_object_storage(**params) stub_object_storage_uploader(config: Gitlab.config.lfs.object_store, uploader: LfsObjectUploader, - remote_directory: 'lfs-objects', **params) end def stub_package_file_object_storage(**params) stub_object_storage_uploader(config: Gitlab.config.packages.object_store, uploader: ::Packages::PackageFileUploader, - remote_directory: 'packages', **params) end def stub_composer_cache_object_storage(**params) stub_object_storage_uploader(config: Gitlab.config.packages.object_store, uploader: ::Packages::Composer::CacheUploader, - remote_directory: 'packages', **params) end def stub_uploads_object_storage(uploader = described_class, **params) stub_object_storage_uploader(config: Gitlab.config.uploads.object_store, uploader: uploader, - remote_directory: 'uploads', **params) end def stub_terraform_state_object_storage(**params) stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store, uploader: Terraform::StateUploader, - remote_directory: 'terraform', **params) end def stub_pages_object_storage(uploader = described_class, **params) stub_object_storage_uploader(config: Gitlab.config.pages.object_store, uploader: uploader, - remote_directory: 'pages', **params) end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index badd4e8212c..acbc15f7b62 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -9,7 +9,7 @@ module TestEnv # When developing the seed repository, comment out the branch you will modify. BRANCH_SHA = { - 'signed-commits' => '6101e87', + 'signed-commits' => 'c7794c1', 'not-merged-branch' => 'b83d6e3', 'branch-merged' => '498214d', 'empty-branch' => '7efb185', @@ -53,7 +53,7 @@ module TestEnv 'wip' => 'b9238ee', 'csv' => '3dd0896', 'v1.1.0' => 'b83d6e3', - 'add-ipython-files' => 'f6b7a70', + 'add-ipython-files' => '2b5ef814', 'add-pdf-file' => 'e774ebd', 'squash-large-files' => '54cec52', 'add-pdf-text-binary' => '79faa7b', diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb index 5ead1813439..5865bafd382 100644 --- a/spec/support/helpers/usage_data_helpers.rb +++ b/spec/support/helpers/usage_data_helpers.rb @@ -162,6 +162,8 @@ module UsageDataHelpers def stub_usage_data_connections allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false) + allow(::Ci::ApplicationRecord.connection).to receive(:transaction_open?).and_return(false) if ::Ci::ApplicationRecord.connection_class? + allow(Gitlab::Prometheus::Internal).to receive(:prometheus_enabled?).and_return(false) end diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb index cd8387de686..83bda6e03b1 100644 --- a/spec/support/helpers/workhorse_helpers.rb +++ b/spec/support/helpers/workhorse_helpers.rb @@ -24,7 +24,12 @@ module WorkhorseHelpers # workhorse_post_with_file will transform file_key inside params as if it was disk accelerated by workhorse def workhorse_post_with_file(url, file_key:, params:) - workhorse_request_with_file(:post, url, + workhorse_form_with_file(url, method: :post, file_key: file_key, params: params) + end + + # workhorse_form_with_file will transform file_key inside params as if it was disk accelerated by workhorse + def workhorse_form_with_file(url, file_key:, params:, method: :post) + workhorse_request_with_file(method, url, file_key: file_key, params: params, env: { 'CONTENT_TYPE' => 'multipart/form-data' }, diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index acf5fb0944f..1b460fbdbf7 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -52,7 +52,7 @@ module AccessMatchers emulate_user(user, @membership) visit(url) - status_code == 200 && !current_path.in?([new_user_session_path, new_admin_session_path]) + [200, 204].include?(status_code) && !current_path.in?([new_user_session_path, new_admin_session_path]) end chain :of do |membership| diff --git a/spec/support/matchers/project_namespace_matcher.rb b/spec/support/matchers/project_namespace_matcher.rb new file mode 100644 index 00000000000..95aa5429679 --- /dev/null +++ b/spec/support/matchers/project_namespace_matcher.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec::Matchers.define :be_in_sync_with_project do |project| + match do |project_namespace| + # if project is not persisted make sure we do not have a persisted project_namespace for it + break false if project.new_record? && project_namespace&.persisted? + # don't really care if project is not in sync if the project was never persisted. + break true if project.new_record? && !project_namespace.present? + + project_namespace.present? && + project.name == project_namespace.name && + project.path == project_namespace.path && + project.namespace == project_namespace.parent && + project.visibility_level == project_namespace.visibility_level && + project.shared_runners_enabled == project_namespace.shared_runners_enabled + end + + failure_message_when_negated do |project_namespace| + if project.new_record? && project_namespace&.persisted? + "expected that a non persisted project #{project} does not have a persisted project namespace #{project_namespace}" + else + <<-MSG + expected that the project's attributes name, path, namespace_id, visibility_level, shared_runners_enabled + are in sync with the corresponding project namespace attributes + MSG + end + end +end diff --git a/spec/support/patches/rspec_example_prepended_methods.rb b/spec/support/patches/rspec_example_prepended_methods.rb new file mode 100644 index 00000000000..ea918b1e08f --- /dev/null +++ b/spec/support/patches/rspec_example_prepended_methods.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module RSpec + module Core + module ExamplePrependedMethods + # Based on https://github.com/rspec/rspec-core/blob/d57c371ee92b16211b80ac7b0b025968438f5297/lib/rspec/core/example.rb#L96-L104, + # Same as location_rerun_argument but with line number + def file_path_rerun_argument + loaded_spec_files = RSpec.configuration.loaded_spec_files + + RSpec::Core::Metadata.ascending(metadata) do |meta| + break meta[:file_path] if loaded_spec_files.include?(meta[:absolute_file_path]) + end + end + end + + module ExampleProcsyPrependedMethods + def file_path_rerun_argument + example.file_path_rerun_argument + end + end + end +end + +RSpec::Core::Example.prepend(RSpec::Core::ExamplePrependedMethods) +RSpec::Core::Example::Procsy.prepend(RSpec::Core::ExampleProcsyPrependedMethods) diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb index dd916aea3e8..72b3a72f9d4 100644 --- a/spec/support/redis/redis_shared_examples.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -87,6 +87,43 @@ RSpec.shared_examples "redis_shared_examples" do end end + describe '.store' do + let(:rails_env) { 'development' } + + subject { described_class.new(rails_env).store } + + shared_examples 'redis store' do + it 'instantiates Redis::Store' do + is_expected.to be_a(::Redis::Store) + expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database}") + end + + context 'with the namespace' do + let(:namespace) { 'namespace_name' } + + subject { described_class.new(rails_env).store(namespace: namespace) } + + it "uses specified namespace" do + expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}") + end + end + end + + context 'with old format' do + it_behaves_like 'redis store' do + let(:config_file_name) { config_old_format_host } + let(:host) { "localhost:#{redis_port}" } + end + end + + context 'with new format' do + it_behaves_like 'redis store' do + let(:config_file_name) { config_new_format_host } + let(:host) { "development-host:#{redis_port}" } + end + end + end + describe '.params' do subject { described_class.new(rails_env).params } diff --git a/spec/support/retriable.rb b/spec/support/retriable.rb new file mode 100644 index 00000000000..be4c2d62752 --- /dev/null +++ b/spec/support/retriable.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Retriable.configure do |config| + config.multiplier = 1.0 + config.rand_factor = 0.0 + config.base_interval = 0 +end diff --git a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb index 645ea742f07..9ac3d4a04f9 100644 --- a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb +++ b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb @@ -10,6 +10,7 @@ RSpec.shared_context 'package details setup' do let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } let(:package_files) { all_graphql_fields_for('PackageFile') } let(:dependency_links) { all_graphql_fields_for('PackageDependencyLink') } + let(:pipelines) { all_graphql_fields_for('Pipeline', max_depth: 1) } let(:user) { project.owner } let(:package_details) { graphql_data_at(:package) } let(:metadata_response) { graphql_data_at(:package, :metadata) } @@ -34,6 +35,11 @@ RSpec.shared_context 'package details setup' do #{dependency_links} } } + pipelines { + nodes { + #{pipelines} + } + } FIELDS end end diff --git a/spec/support/shared_contexts/lib/gitlab/database/background_migration_job_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/database/background_migration_job_shared_context.rb deleted file mode 100644 index 382eb796f8e..00000000000 --- a/spec/support/shared_contexts/lib/gitlab/database/background_migration_job_shared_context.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_context 'background migration job class' do - let!(:job_class_name) { 'TestJob' } - let!(:job_class) { Class.new } - let!(:job_perform_method) do - ->(*arguments) do - Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded( - # Value is 'TestJob' defined by :job_class_name in the let! above. - # Scoping prohibits us from directly referencing job_class_name. - RSpec.current_example.example_group_instance.job_class_name, - arguments - ) - end - end - - before do - job_class.define_method(:perform, job_perform_method) - expect(Gitlab::BackgroundMigration).to receive(:migration_class_for).with(job_class_name).at_least(:once) { job_class } - end -end diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index 2abc52fce85..bcc6abdc308 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -119,7 +119,7 @@ RSpec.shared_context 'project navbar structure' do _('Repository'), _('CI/CD'), _('Monitor'), - (s_('UsageQuota|Usage Quotas') if Feature.enabled?(:project_storage_ui, default_enabled: :yaml)) + s_('UsageQuota|Usage Quotas') ] } ].compact diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb index d7e4864cb08..8a90f887381 100644 --- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb @@ -15,7 +15,7 @@ RSpec.shared_context 'ProjectPolicy context' do let(:base_guest_permissions) do %i[ - award_emoji create_issue create_incident create_merge_request_in create_note + award_emoji create_issue create_merge_request_in create_note create_project read_issue_board read_issue read_issue_iid read_issue_link read_label read_issue_board_list read_milestone read_note read_project read_project_for_iids read_project_member read_release read_snippet @@ -25,10 +25,11 @@ RSpec.shared_context 'ProjectPolicy context' do let(:base_reporter_permissions) do %i[ - admin_issue admin_issue_link admin_label admin_issue_board_list create_snippet - daily_statistics download_code download_wiki_code fork_project metrics_dashboard - read_build read_commit_status read_confidential_issues - read_container_image read_deployment read_environment read_merge_request + admin_issue admin_issue_link admin_label admin_issue_board_list + create_snippet create_incident daily_statistics download_code + download_wiki_code fork_project metrics_dashboard read_build + read_commit_status read_confidential_issues read_container_image + read_deployment read_environment read_merge_request read_metrics_dashboard_annotation read_pipeline read_prometheus read_sentry_issue update_issue ] diff --git a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb new file mode 100644 index 00000000000..95b8b7ed9f8 --- /dev/null +++ b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +RSpec.shared_context 'Debian repository shared context' do |container_type, can_freeze| + include_context 'workhorse headers' + + before do + stub_feature_flags(debian_packages: true, debian_group_packages: true) + end + + let_it_be(:private_container, freeze: can_freeze) { create(container_type, :private) } + let_it_be(:public_container, freeze: can_freeze) { create(container_type, :public) } + let_it_be(:user, freeze: true) { create(:user) } + let_it_be(:personal_access_token, freeze: true) { create(:personal_access_token, user: user) } + + let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: private_container, codename: 'existing-codename') } + let_it_be(:private_distribution_key, freeze: true) { create("debian_#{container_type}_distribution_key", distribution: private_distribution) } + let_it_be(:private_component, freeze: true) { create("debian_#{container_type}_component", distribution: private_distribution, name: 'existing-component') } + let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') } + let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') } + let_it_be(:private_component_file) { create("debian_#{container_type}_component_file", component: private_component, architecture: private_architecture) } + + let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: public_container, codename: 'existing-codename') } + let_it_be(:public_distribution_key, freeze: true) { create("debian_#{container_type}_distribution_key", distribution: public_distribution) } + let_it_be(:public_component, freeze: true) { create("debian_#{container_type}_component", distribution: public_distribution, name: 'existing-component') } + let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') } + let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') } + let_it_be(:public_component_file) { create("debian_#{container_type}_component_file", component: public_component, architecture: public_architecture) } + + if container_type == :group + let_it_be(:private_project) { create(:project, :private, group: private_container) } + let_it_be(:public_project) { create(:project, :public, group: public_container) } + let_it_be(:private_project_distribution) { create(:debian_project_distribution, container: private_project, codename: 'existing-codename') } + let_it_be(:public_project_distribution) { create(:debian_project_distribution, container: public_project, codename: 'existing-codename') } + + let(:project) { { private: private_project, public: public_project }[visibility_level] } + else + let_it_be(:private_project) { private_container } + let_it_be(:public_project) { public_container } + let_it_be(:private_project_distribution) { private_distribution } + let_it_be(:public_project_distribution) { public_distribution } + end + + let_it_be(:private_package) { create(:debian_package, project: private_project, published_in: private_project_distribution) } + let_it_be(:public_package) { create(:debian_package, project: public_project, published_in: public_project_distribution) } + + let(:visibility_level) { :public } + + let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] } + let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] } + let(:component) { { private: private_component, public: public_component }[visibility_level] } + let(:component_file) { { private: private_component_file, public: public_component_file }[visibility_level] } + let(:package) { { private: private_package, public: public_package }[visibility_level] } + let(:letter) { package.name[0..2] == 'lib' ? package.name[0..3] : package.name[0] } + + let(:method) { :get } + + let(:workhorse_params) do + if method == :put + file_upload = fixture_file_upload("spec/fixtures/packages/debian/#{file_name}") + { file: file_upload } + else + {} + end + end + + let(:api_params) { workhorse_params } + + let(:auth_headers) { {} } + let(:wh_headers) do + if method == :put + workhorse_headers + else + {} + end + end + + let(:headers) { auth_headers.merge(wh_headers) } + + let(:send_rewritten_field) { true } + + subject do + if method == :put + workhorse_finalize( + api(url), + method: method, + file_key: :file, + params: api_params, + headers: headers, + send_rewritten_field: send_rewritten_field + ) + else + send method, api(url), headers: headers, params: api_params + end + end +end + +RSpec.shared_context 'Debian repository auth headers' do |user_type, auth_method = :private_token| + let(:token) { user_type == :invalid_token ? 'wrong' : personal_access_token.token } + + let(:auth_headers) do + if user_type == :anonymous + {} + elsif auth_method == :private_token + { 'Private-Token' => token } + else + basic_auth_header(user.username, token) + end + end +end + +RSpec.shared_context 'Debian repository access' do |visibility_level, user_type, auth_method| + include_context 'Debian repository auth headers', user_type, auth_method do + let(:containers) { { private: private_container, public: public_container } } + let(:container) { containers[visibility_level] } + + before do + container.send("add_#{user_type}", user) if user_type != :anonymous && user_type != :not_a_member && user_type != :invalid_token + end + end +end diff --git a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb index 80f011f622b..21be989d697 100644 --- a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb +++ b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb @@ -31,14 +31,14 @@ RSpec.shared_context 'container repository delete tags service shared context' d end end - def stub_put_manifest_request(tag, status = 200, headers = { 'docker-content-digest' => 'sha256:dummy' }) + def stub_put_manifest_request(tag, status = 200, headers = { DependencyProxy::Manifest::DIGEST_HEADER => 'sha256:dummy' }) stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/#{tag}") .to_return(status: status, body: '', headers: headers) end def stub_tag_digest(tag, digest) stub_request(:head, "http://registry.gitlab/v2/#{repository.path}/manifests/#{tag}") - .to_return(status: 200, body: '', headers: { 'docker-content-digest' => digest }) + .to_return(status: 200, body: '', headers: { DependencyProxy::Manifest::DIGEST_HEADER => digest }) end def stub_digest_config(digest, created_at) diff --git a/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb b/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb index 2b810e790f0..e1d864213b5 100644 --- a/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb +++ b/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb @@ -38,6 +38,11 @@ RSpec.shared_context 'stubbed service ping metrics definitions' do ) end + after do |example| + Gitlab::Usage::Metric.instance_variable_set(:@all, nil) + Gitlab::Usage::MetricDefinition.instance_variable_set(:@all, nil) + end + def metric_attributes(key_path, category, value_type = 'string') { 'key_path' => key_path, diff --git a/spec/support/shared_contexts/url_shared_context.rb b/spec/support/shared_contexts/url_shared_context.rb index f3d227b6e2b..da1d6e0049c 100644 --- a/spec/support/shared_contexts/url_shared_context.rb +++ b/spec/support/shared_contexts/url_shared_context.rb @@ -1,19 +1,32 @@ # frozen_string_literal: true +RSpec.shared_context 'valid urls with CRLF' do + let(:valid_urls_with_CRLF) do + [ + "http://example.com/pa%0dth", + "http://example.com/pa%0ath", + "http://example.com/pa%0d%0th", + "http://example.com/pa%0D%0Ath", + "http://gitlab.com/path?param=foo%0Abar", + "https://gitlab.com/path?param=foo%0Dbar", + "http://example.org:1024/path?param=foo%0D%0Abar", + "https://storage.googleapis.com/bucket/import_export_upload/import_file/57265/express.tar.gz?GoogleAccessId=hello@example.org&Signature=ABCD%0AEFGHik&Expires=1634663304" + ] + end +end + RSpec.shared_context 'invalid urls' do let(:urls_with_CRLF) do - ["http://127.0.0.1:333/pa\rth", - "http://127.0.0.1:333/pa\nth", - "http://127.0a.0.1:333/pa\r\nth", - "http://127.0.0.1:333/path?param=foo\r\nbar", - "http://127.0.0.1:333/path?param=foo\rbar", - "http://127.0.0.1:333/path?param=foo\nbar", - "http://127.0.0.1:333/pa%0dth", - "http://127.0.0.1:333/pa%0ath", - "http://127.0a.0.1:333/pa%0d%0th", - "http://127.0.0.1:333/pa%0D%0Ath", - "http://127.0.0.1:333/path?param=foo%0Abar", - "http://127.0.0.1:333/path?param=foo%0Dbar", - "http://127.0.0.1:333/path?param=foo%0D%0Abar"] + [ + "git://example.com/pa%0dth", + "git://example.com/pa%0ath", + "git://example.com/pa%0d%0th", + "http://example.com/pa\rth", + "http://example.com/pa\nth", + "http://example.com/pa\r\nth", + "http://example.com/path?param=foo\r\nbar", + "http://example.com/path?param=foo\rbar", + "http://example.com/path?param=foo\nbar" + ] end end diff --git a/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb b/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb new file mode 100644 index 00000000000..e8cc666605b --- /dev/null +++ b/spec/support/shared_examples/bulk_imports/common/pipelines/wiki_pipeline_examples.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'wiki pipeline imports a wiki for an entity' do + describe '#run' do + let_it_be(:bulk_import_configuration) { create(:bulk_import_configuration, bulk_import: bulk_import) } + + let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } + let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } + + let(:extracted_data) { BulkImports::Pipeline::ExtractedData.new(data: {}) } + + context 'successfully imports wiki for an entity' do + subject { described_class.new(context) } + + before do + allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor| + allow(extractor).to receive(:extract).and_return(extracted_data) + end + end + + it 'imports new wiki into destination project' do + expect_next_instance_of(Gitlab::GitalyClient::RepositoryService) do |repository_service| + url = "https://oauth2:token@gitlab.example/#{entity.source_full_path}.wiki.git" + expect(repository_service).to receive(:fetch_remote).with(url, any_args).and_return 0 + end + + subject.run + end + end + end +end diff --git a/spec/support/shared_examples/controllers/concerns/integrations_actions_shared_examples.rb b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb index 748a3acf17b..a8aed0c1f0b 100644 --- a/spec/support/shared_examples/controllers/concerns/integrations_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples IntegrationsActions do +RSpec.shared_examples Integrations::Actions do let(:integration) do create(:datadog_integration, integration_attributes.merge( diff --git a/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb b/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb index 74a98c20383..8affe4ac8f5 100644 --- a/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb +++ b/spec/support/shared_examples/controllers/create_notes_rate_limit_shared_examples.rb @@ -6,39 +6,41 @@ # - request_full_path RSpec.shared_examples 'request exceeding rate limit' do - before do - stub_application_setting(notes_create_limit: 2) - 2.times { post :create, params: params } - end + context 'with rate limiter', :freeze_time, :clean_gitlab_redis_rate_limiting do + before do + stub_application_setting(notes_create_limit: 2) + 2.times { post :create, params: params } + end - it 'prevents from creating more notes', :request_store do - expect { post :create, params: params } - .to change { Note.count }.by(0) + it 'prevents from creating more notes' do + expect { post :create, params: params } + .to change { Note.count }.by(0) - expect(response).to have_gitlab_http_status(:too_many_requests) - expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.')) - end + expect(response).to have_gitlab_http_status(:too_many_requests) + expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.')) + end - it 'logs the event in auth.log' do - attributes = { - message: 'Application_Rate_Limiter_Request', - env: :notes_create_request_limit, - remote_ip: '0.0.0.0', - request_method: 'POST', - path: request_full_path, - user_id: user.id, - username: user.username - } + it 'logs the event in auth.log' do + attributes = { + message: 'Application_Rate_Limiter_Request', + env: :notes_create_request_limit, + remote_ip: '0.0.0.0', + request_method: 'POST', + path: request_full_path, + user_id: user.id, + username: user.username + } - expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once - post :create, params: params - end + expect(Gitlab::AuthLogger).to receive(:error).with(attributes).once + post :create, params: params + end - it 'allows user in allow-list to create notes, even if the case is different' do - user.update_attribute(:username, user.username.titleize) - stub_application_setting(notes_create_limit_allowlist: ["#{user.username.downcase}"]) + it 'allows user in allow-list to create notes, even if the case is different' do + user.update_attribute(:username, user.username.titleize) + stub_application_setting(notes_create_limit_allowlist: ["#{user.username.downcase}"]) - post :create, params: params - expect(response).to have_gitlab_http_status(:found) + post :create, params: params + expect(response).to have_gitlab_http_status(:found) + end end end diff --git a/spec/support/shared_examples/features/2fa_shared_examples.rb b/spec/support/shared_examples/features/2fa_shared_examples.rb index ddc03e178ba..94c91556ea7 100644 --- a/spec/support/shared_examples/features/2fa_shared_examples.rb +++ b/spec/support/shared_examples/features/2fa_shared_examples.rb @@ -18,6 +18,7 @@ RSpec.shared_examples 'hardware device for 2fa' do |device_type| let(:user) { create(:user) } before do + stub_feature_flags(bootstrap_confirmation_modals: false) gitlab_sign_in(user) user.update_attribute(:otp_required_for_login, true) end diff --git a/spec/support/shared_examples/features/dependency_proxy_shared_examples.rb b/spec/support/shared_examples/features/dependency_proxy_shared_examples.rb index d29c677a962..5d1488502d2 100644 --- a/spec/support/shared_examples/features/dependency_proxy_shared_examples.rb +++ b/spec/support/shared_examples/features/dependency_proxy_shared_examples.rb @@ -26,7 +26,7 @@ RSpec.shared_examples 'a successful manifest pull' do subject expect(response).to have_gitlab_http_status(:ok) - expect(response.headers['Docker-Content-Digest']).to eq(manifest.digest) + expect(response.headers[DependencyProxy::Manifest::DIGEST_HEADER]).to eq(manifest.digest) expect(response.headers['Content-Length']).to eq(manifest.size) expect(response.headers['Docker-Distribution-Api-Version']).to eq(DependencyProxy::DISTRIBUTION_API_VERSION) expect(response.headers['Etag']).to eq("\"#{manifest.digest}\"") diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb index 0161899cb76..27d50c67f24 100644 --- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb +++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb @@ -18,6 +18,7 @@ RSpec.shared_examples 'manage applications' do click_on 'Save application' validate_application(application_name, 'Yes') + expect(page).to have_link('Continue', href: index_path) application = Doorkeeper::Application.find_by(name: application_name) expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy') @@ -33,6 +34,7 @@ RSpec.shared_examples 'manage applications' do click_on 'Save application' validate_application(application_name_changed, 'No') + expect(page).not_to have_link('Continue') visit_applications_path diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb index 96be30b9f1f..d14b4638ca5 100644 --- a/spec/support/shared_examples/features/packages_shared_examples.rb +++ b/spec/support/shared_examples/features/packages_shared_examples.rb @@ -21,10 +21,6 @@ end RSpec.shared_examples 'package details link' do |property| let(:package) { packages.first } - before do - stub_feature_flags(packages_details_one_column: false) - end - it 'navigates to the correct url' do page.within(packages_table_selector) do click_link package.name @@ -32,7 +28,7 @@ RSpec.shared_examples 'package details link' do |property| expect(page).to have_current_path(project_package_path(package.project, package)) - expect(page).to have_css('.packages-app h1[data-testid="title"]', text: package.name) + expect(page).to have_css('.packages-app h2[data-testid="title"]', text: package.name) expect(page).to have_content('Installation') expect(page).to have_content('Registry setup') @@ -94,16 +90,24 @@ def packages_table_selector end def click_sort_option(option, ascending) - page.within('.gl-sorting') do - # Reset the sort direction - click_button 'Sort direction' if page.has_selector?('svg[aria-label="Sorting Direction: Ascending"]', wait: 0) + wait_for_requests - find('button.gl-dropdown-toggle').click + # Reset the sort direction + if page.has_selector?('button[aria-label="Sorting Direction: Ascending"]', wait: 0) && !ascending + click_button 'Sort direction' - page.within('.dropdown-menu') do - click_button option - end + wait_for_requests + end + + find('button.gl-dropdown-toggle').click + + page.within('.dropdown-menu') do + click_button option + end + + if ascending + wait_for_requests - click_button 'Sort direction' if ascending + click_button 'Sort direction' end end diff --git a/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb b/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb index 6d44a6fde85..337b3f3cbd0 100644 --- a/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb +++ b/spec/support/shared_examples/features/resolving_discussions_in_issues_shared_examples.rb @@ -1,43 +1,29 @@ # frozen_string_literal: true RSpec.shared_examples 'creating an issue for a thread' do - it 'shows an issue with the title filled in' do + it 'shows an issue creation form' do + # Title field is filled in title_field = page.find_field('issue[title]') - expect(title_field.value).to include(merge_request.title) - end - it 'has a mention of the discussion in the description' do - description_field = page.find_field('issue[description]') + # Has a hidden field for the merge request + merge_request_field = find('#merge_request_to_resolve_discussions_of', visible: false) + expect(merge_request_field.value).to eq(merge_request.iid.to_s) + # Has a mention of the discussion in the description + description_field = page.find_field('issue[description]') expect(description_field.value).to include(discussion.first_note.note) end - it 'can create a new issue for the project' do + it 'creates a new issue for the project' do + # Actually creates an issue for the project expect { click_button 'Create issue' }.to change { project.issues.reload.size }.by(1) - end - - it 'resolves the discussion in the merge request' do - click_button 'Create issue' + # Resolves the discussion in the merge request discussion.first_note.reload - expect(discussion.resolved?).to eq(true) - end - - it 'shows a flash messaage after resolving a discussion' do - click_button 'Create issue' - - page.within '.flash-notice' do - # Only check for the word 'Resolved' since the spec might have resolved - # multiple discussions - expect(page).to have_content('Resolved') - end - end - - it 'has a hidden field for the merge request' do - merge_request_field = find('#merge_request_to_resolve_discussions_of', visible: false) - expect(merge_request_field.value).to eq(merge_request.iid.to_s) + # Issue title inludes MR title + expect(page).to have_content(%Q(Follow-up from "#{merge_request.title}")) end end diff --git a/spec/support/shared_examples/features/sidebar_shared_examples.rb b/spec/support/shared_examples/features/sidebar_shared_examples.rb index 5bfe929e957..d509d124de0 100644 --- a/spec/support/shared_examples/features/sidebar_shared_examples.rb +++ b/spec/support/shared_examples/features/sidebar_shared_examples.rb @@ -52,16 +52,17 @@ RSpec.shared_examples 'issue boards sidebar' do it 'shows toggle as on then as off as user toggles to subscribe and unsubscribe', :aggregate_failures do wait_for_requests + subscription_button = find('[data-testid="subscription-toggle"]') - click_button 'Notifications' + subscription_button.click - expect(page).to have_button('Notifications', class: 'is-checked') + expect(subscription_button).to have_css("button.is-checked") - click_button 'Notifications' + subscription_button.click wait_for_requests - expect(page).not_to have_button('Notifications', class: 'is-checked') + expect(subscription_button).to have_css("button:not(.is-checked)") end context 'when notifications have been disabled' do @@ -73,7 +74,7 @@ RSpec.shared_examples 'issue boards sidebar' do it 'displays a message that notifications have been disabled' do page.within('[data-testid="sidebar-notifications"]') do - expect(page).to have_button('Notifications', class: 'is-disabled') + expect(page).to have_selector('[data-testid="subscription-toggle"]', class: 'is-disabled') expect(page).to have_content('Disabled by project owner') end end diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb index fb598b978f6..56b6dc682eb 100644 --- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb +++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb @@ -66,20 +66,22 @@ RSpec.shared_examples 'a Note mutation when the given resource id is not for a N end RSpec.shared_examples 'a Note mutation when there are rate limit validation errors' do - before do - stub_application_setting(notes_create_limit: 3) - 3.times { post_graphql_mutation(mutation, current_user: current_user) } - end - - it_behaves_like 'a Note mutation that does not create a Note' - it_behaves_like 'a mutation that returns top-level errors', - errors: ['This endpoint has been requested too many times. Try again later.'] - - context 'when the user is in the allowlist' do + context 'with rate limiter', :freeze_time, :clean_gitlab_redis_rate_limiting do before do - stub_application_setting(notes_create_limit_allowlist: ["#{current_user.username}"]) + stub_application_setting(notes_create_limit: 3) + 3.times { post_graphql_mutation(mutation, current_user: current_user) } end - it_behaves_like 'a Note mutation that creates a Note' + it_behaves_like 'a Note mutation that does not create a Note' + it_behaves_like 'a mutation that returns top-level errors', + errors: ['This endpoint has been requested too many times. Try again later.'] + + context 'when the user is in the allowlist' do + before do + stub_application_setting(notes_create_limit_allowlist: ["#{current_user.username}"]) + end + + it_behaves_like 'a Note mutation that creates a Note' + end end end diff --git a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb index 8b4ecd7d5ae..a3c67210a4a 100644 --- a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb @@ -35,8 +35,8 @@ RSpec.shared_examples 'common trace features' do stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: trace.job.project) end - it 'calls ::ApplicationRecord.sticking.unstick_or_continue_sticking' do - expect(::ApplicationRecord.sticking).to receive(:unstick_or_continue_sticking) + it 'calls ::Ci::Build.sticking.unstick_or_continue_sticking' do + expect(::Ci::Build.sticking).to receive(:unstick_or_continue_sticking) .with(described_class::LOAD_BALANCING_STICKING_NAMESPACE, trace.job.id) .and_call_original @@ -49,8 +49,8 @@ RSpec.shared_examples 'common trace features' do stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: false) end - it 'does not call ::ApplicationRecord.sticking.unstick_or_continue_sticking' do - expect(::ApplicationRecord.sticking).not_to receive(:unstick_or_continue_sticking) + it 'does not call ::Ci::Build.sticking.unstick_or_continue_sticking' do + expect(::Ci::Build.sticking).not_to receive(:unstick_or_continue_sticking) trace.read { |stream| stream } end @@ -305,8 +305,8 @@ RSpec.shared_examples 'common trace features' do stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: trace.job.project) end - it 'calls ::ApplicationRecord.sticking.stick' do - expect(::ApplicationRecord.sticking).to receive(:stick) + it 'calls ::Ci::Build.sticking.stick' do + expect(::Ci::Build.sticking).to receive(:stick) .with(described_class::LOAD_BALANCING_STICKING_NAMESPACE, trace.job.id) .and_call_original @@ -319,8 +319,8 @@ RSpec.shared_examples 'common trace features' do stub_feature_flags(gitlab_ci_archived_trace_consistent_reads: false) end - it 'does not call ::ApplicationRecord.sticking.stick' do - expect(::ApplicationRecord.sticking).not_to receive(:stick) + it 'does not call ::Ci::Build.sticking.stick' do + expect(::Ci::Build.sticking).not_to receive(:stick) subject end @@ -808,7 +808,19 @@ RSpec.shared_examples 'trace with enabled live trace feature' do create(:ci_job_artifact, :trace, job: build) end - it { is_expected.to be_truthy } + it 'is truthy' do + is_expected.to be_truthy + end + end + + context 'when archived trace record exists but file is not stored' do + before do + create(:ci_job_artifact, :unarchived_trace_artifact, job: build) + end + + it 'is falsy' do + is_expected.to be_falsy + end end context 'when live trace exists' do @@ -872,13 +884,35 @@ RSpec.shared_examples 'trace with enabled live trace feature' do build.reload expect(build.trace.exist?).to be_truthy - expect(build.job_artifacts_trace).to be_nil Gitlab::Ci::Trace::ChunkedIO.new(build) do |stream| expect(stream.read).to eq(trace_raw) end end end + shared_examples 'a pre-commit error' do |error:| + it_behaves_like 'source trace in ChunkedIO stays intact', error: error + + it 'does not save the trace artifact' do + expect { subject }.to raise_error(error) + + build.reload + expect(build.job_artifacts_trace).to be_nil + end + end + + shared_examples 'a post-commit error' do |error:| + it_behaves_like 'source trace in ChunkedIO stays intact', error: error + + it 'saves the trace artifact but not the file' do + expect { subject }.to raise_error(error) + + build.reload + expect(build.job_artifacts_trace).to be_present + expect(build.job_artifacts_trace.file.exists?).to be_falsy + end + end + context 'when job does not have trace artifact' do context 'when trace is stored in ChunkedIO' do let!(:build) { create(:ci_build, :success, :trace_live) } @@ -892,7 +926,7 @@ RSpec.shared_examples 'trace with enabled live trace feature' do allow(IO).to receive(:copy_stream).and_return(0) end - it_behaves_like 'source trace in ChunkedIO stays intact', error: Gitlab::Ci::Trace::ArchiveError + it_behaves_like 'a pre-commit error', error: Gitlab::Ci::Trace::ArchiveError end context 'when failed to create job artifact record' do @@ -902,7 +936,16 @@ RSpec.shared_examples 'trace with enabled live trace feature' do .and_return(%w[Error Error]) end - it_behaves_like 'source trace in ChunkedIO stays intact', error: ActiveRecord::RecordInvalid + it_behaves_like 'a pre-commit error', error: ActiveRecord::RecordInvalid + end + + context 'when storing the file raises an error' do + before do + stub_artifacts_object_storage(direct_upload: true) + allow_any_instance_of(Ci::JobArtifact).to receive(:store_file!).and_raise(Excon::Error::BadGateway, 'S3 is down lol') + end + + it_behaves_like 'a post-commit error', error: Excon::Error::BadGateway end end end diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb index 6342064beb8..bea7cca2744 100644 --- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb +++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb @@ -6,7 +6,7 @@ shared_examples 'deployment metrics examples' do environment = project.environments.production.first || create(:environment, :production, project: project) create(:deployment, :success, args.merge(environment: environment)) - # this is needed for the dora_deployment_frequency_in_vsa feature flag so we have aggregated data + # this is needed for the DORA API so we have aggregated data ::Dora::DailyMetrics::RefreshWorker.new.perform(environment.id, Time.current.to_date.to_s) if Gitlab.ee? end diff --git a/spec/support/shared_examples/lib/gitlab/database/cte_materialized_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/cte_materialized_shared_examples.rb index a617342ff8c..df795723874 100644 --- a/spec/support/shared_examples/lib/gitlab/database/cte_materialized_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/database/cte_materialized_shared_examples.rb @@ -11,7 +11,7 @@ RSpec.shared_examples 'CTE with MATERIALIZED keyword examples' do context 'when PG version is <12' do it 'does not add MATERIALIZE keyword' do - allow(Gitlab::Database.main).to receive(:version).and_return('11.1') + allow(ApplicationRecord.database).to receive(:version).and_return('11.1') expect(query).to include(expected_query_block_without_materialized) end @@ -19,14 +19,14 @@ RSpec.shared_examples 'CTE with MATERIALIZED keyword examples' do context 'when PG version is >=12' do it 'adds MATERIALIZE keyword' do - allow(Gitlab::Database.main).to receive(:version).and_return('12.1') + allow(ApplicationRecord.database).to receive(:version).and_return('12.1') expect(query).to include(expected_query_block_with_materialized) end context 'when version is higher than 12' do it 'adds MATERIALIZE keyword' do - allow(Gitlab::Database.main).to receive(:version).and_return('15.1') + allow(ApplicationRecord.database).to receive(:version).and_return('15.1') expect(query).to include(expected_query_block_with_materialized) end diff --git a/spec/support/shared_examples/lib/gitlab/import_export/attributes_permitter_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/import_export/attributes_permitter_shared_examples.rb index 5ce698c4701..41d3d76b66b 100644 --- a/spec/support/shared_examples/lib/gitlab/import_export/attributes_permitter_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/import_export/attributes_permitter_shared_examples.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attributes| +RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attributes, additional_attributes = []| let(:prohibited_attributes) { %i[remote_url my_attributes my_ids token my_id test] } let(:import_export_config) { Gitlab::ImportExport::Config.new.to_h } @@ -26,7 +26,7 @@ RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attrib end it 'does not contain attributes that would be cleaned with AttributeCleaner' do - expect(cleaned_hash.keys).to include(*permitted_hash.keys) + expect(cleaned_hash.keys + additional_attributes.to_a).to include(*permitted_hash.keys) end it 'does not contain prohibited attributes that are not related to given relation' do diff --git a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb index 708bc71ae96..ff03051ed37 100644 --- a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb @@ -2,7 +2,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| let(:fake_duplicate_job) do - instance_double(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob) + instance_double(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, duplicate_key_ttl: Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob::DEFAULT_DUPLICATE_KEY_TTL) end let(:expected_message) { "dropped #{strategy_name.to_s.humanize.downcase}" } @@ -11,14 +11,14 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| describe '#schedule' do before do - allow(Gitlab::SidekiqLogging::DeduplicationLogger.instance).to receive(:log) + allow(Gitlab::SidekiqLogging::DeduplicationLogger.instance).to receive(:deduplicated_log) end it 'checks for duplicates before yielding' do expect(fake_duplicate_job).to receive(:scheduled?).twice.ordered.and_return(false) expect(fake_duplicate_job).to( receive(:check!) - .with(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob::DUPLICATE_KEY_TTL) + .with(fake_duplicate_job.duplicate_key_ttl) .ordered .and_return('a jid')) expect(fake_duplicate_job).to receive(:duplicate?).ordered.and_return(false) @@ -40,6 +40,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| allow(fake_duplicate_job).to receive(:check!).and_return('the jid') allow(fake_duplicate_job).to receive(:idempotent?).and_return(true) allow(fake_duplicate_job).to receive(:update_latest_wal_location!) + allow(fake_duplicate_job).to receive(:set_deduplicated_flag!) allow(fake_duplicate_job).to receive(:options).and_return({}) job_hash = {} @@ -61,10 +62,11 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| allow(fake_duplicate_job).to receive(:options).and_return({ including_scheduled: true }) allow(fake_duplicate_job).to( receive(:check!) - .with(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob::DUPLICATE_KEY_TTL) + .with(fake_duplicate_job.duplicate_key_ttl) .and_return('the jid')) allow(fake_duplicate_job).to receive(:idempotent?).and_return(true) allow(fake_duplicate_job).to receive(:update_latest_wal_location!) + allow(fake_duplicate_job).to receive(:set_deduplicated_flag!) job_hash = {} expect(fake_duplicate_job).to receive(:duplicate?).and_return(true) @@ -83,9 +85,10 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| allow(fake_duplicate_job).to receive(:scheduled_at).and_return(Time.now + time_diff) allow(fake_duplicate_job).to receive(:options).and_return({ including_scheduled: true }) allow(fake_duplicate_job).to( - receive(:check!).with(time_diff.to_i).and_return('the jid')) + receive(:check!).with(time_diff.to_i + fake_duplicate_job.duplicate_key_ttl).and_return('the jid')) allow(fake_duplicate_job).to receive(:idempotent?).and_return(true) allow(fake_duplicate_job).to receive(:update_latest_wal_location!) + allow(fake_duplicate_job).to receive(:set_deduplicated_flag!) job_hash = {} expect(fake_duplicate_job).to receive(:duplicate?).and_return(true) @@ -100,6 +103,26 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| end end + context "when the job is not duplicate" do + before do + allow(fake_duplicate_job).to receive(:scheduled?).and_return(false) + allow(fake_duplicate_job).to receive(:check!).and_return('the jid') + allow(fake_duplicate_job).to receive(:duplicate?).and_return(false) + allow(fake_duplicate_job).to receive(:options).and_return({}) + allow(fake_duplicate_job).to receive(:existing_jid).and_return('the jid') + end + + it 'does not return false nor drop the job' do + schedule_result = nil + + expect(fake_duplicate_job).not_to receive(:set_deduplicated_flag!) + + expect { |b| schedule_result = strategy.schedule({}, &b) }.to yield_control + + expect(schedule_result).to be_nil + end + end + context "when the job is droppable" do before do allow(fake_duplicate_job).to receive(:scheduled?).and_return(false) @@ -109,6 +132,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| allow(fake_duplicate_job).to receive(:existing_jid).and_return('the jid') allow(fake_duplicate_job).to receive(:idempotent?).and_return(true) allow(fake_duplicate_job).to receive(:update_latest_wal_location!) + allow(fake_duplicate_job).to receive(:set_deduplicated_flag!) end it 'updates latest wal location' do @@ -117,10 +141,11 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| strategy.schedule({ 'jid' => 'new jid' }) {} end - it 'drops the job' do + it 'returns false to drop the job' do schedule_result = nil expect(fake_duplicate_job).to receive(:idempotent?).and_return(true) + expect(fake_duplicate_job).to receive(:set_deduplicated_flag!).once expect { |b| schedule_result = strategy.schedule({}, &b) }.not_to yield_control expect(schedule_result).to be(false) @@ -130,7 +155,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| fake_logger = instance_double(Gitlab::SidekiqLogging::DeduplicationLogger) expect(Gitlab::SidekiqLogging::DeduplicationLogger).to receive(:instance).and_return(fake_logger) - expect(fake_logger).to receive(:log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, {}) + expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, {}) strategy.schedule({ 'jid' => 'new jid' }) {} end @@ -140,7 +165,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| expect(Gitlab::SidekiqLogging::DeduplicationLogger).to receive(:instance).and_return(fake_logger) allow(fake_duplicate_job).to receive(:options).and_return({ foo: :bar }) - expect(fake_logger).to receive(:log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, { foo: :bar }) + expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, { foo: :bar }) strategy.schedule({ 'jid' => 'new jid' }) {} end @@ -159,6 +184,9 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name| before do allow(fake_duplicate_job).to receive(:delete!) + allow(fake_duplicate_job).to receive(:scheduled?) { false } + allow(fake_duplicate_job).to receive(:options) { {} } + allow(fake_duplicate_job).to receive(:should_reschedule?) { false } allow(fake_duplicate_job).to receive(:latest_wal_locations).and_return( wal_locations ) end diff --git a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb new file mode 100644 index 00000000000..d3fd28727b5 --- /dev/null +++ b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'ZenTao menu with CE version' do + let(:project) { create(:project, has_external_issue_tracker: true) } + let(:user) { project.owner } + let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) } + let(:zentao_integration) { create(:zentao_integration, project: project) } + + subject { described_class.new(context) } + + describe '#render?' do + context 'when issues integration is disabled' do + before do + zentao_integration.update!(active: false) + end + + it 'returns false' do + expect(subject.render?).to eq false + end + end + + context 'when issues integration is enabled' do + before do + zentao_integration.update!(active: true) + end + + it 'returns true' do + expect(subject.render?).to eq true + end + + it 'renders menu link' do + expect(subject.link).to eq zentao_integration.url + end + + it 'contains only open ZenTao item' do + expect(subject.renderable_items.map(&:item_id)).to match_array [:open_zentao] + end + end + end +end diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb new file mode 100644 index 00000000000..7ccd9533811 --- /dev/null +++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'it has loose foreign keys' do + let(:factory_name) { nil } + let(:table_name) { described_class.table_name } + let(:connection) { described_class.connection } + + it 'includes the LooseForeignKey module' do + expect(described_class.ancestors).to include(LooseForeignKey) + end + + it 'responds to #loose_foreign_key_definitions' do + expect(described_class).to respond_to(:loose_foreign_key_definitions) + end + + it 'has at least one loose foreign key definition' do + expect(described_class.loose_foreign_key_definitions.size).to be > 0 + end + + it 'has the deletion trigger present' do + sql = <<-SQL + SELECT trigger_name + FROM information_schema.triggers + WHERE event_object_table = '#{table_name}' + SQL + + triggers = connection.execute(sql) + + expected_trigger_name = "#{table_name}_loose_fk_trigger" + expect(triggers.pluck('trigger_name')).to include(expected_trigger_name) + end + + it 'records record deletions' do + model = create(factory_name) # rubocop: disable Rails/SaveBang + model.destroy! + + deleted_record = LooseForeignKeys::DeletedRecord.find_by(fully_qualified_table_name: "#{connection.current_schema}.#{table_name}", primary_key_value: model.id) + + expect(deleted_record).not_to be_nil + end + + it 'cleans up record deletions' do + model = create(factory_name) # rubocop: disable Rails/SaveBang + + expect { model.destroy! }.to change { LooseForeignKeys::DeletedRecord.count }.by(1) + + LooseForeignKeys::ProcessDeletedRecordsService.new(connection: connection).execute + + expect(LooseForeignKeys::DeletedRecord.status_pending.count).to be(0) + expect(LooseForeignKeys::DeletedRecord.status_processed.count).to be(1) + end +end diff --git a/spec/support/shared_examples/metrics/transaction_metrics_with_labels_shared_examples.rb b/spec/support/shared_examples/metrics/transaction_metrics_with_labels_shared_examples.rb new file mode 100644 index 00000000000..286c60f1f4f --- /dev/null +++ b/spec/support/shared_examples/metrics/transaction_metrics_with_labels_shared_examples.rb @@ -0,0 +1,219 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'transaction metrics with labels' do + let(:sensitive_tags) do + { + path: 'private', + branch: 'sensitive' + } + end + + around do |example| + described_class.reload_metric! + example.run + described_class.reload_metric! + end + + describe '.prometheus_metric' do + let(:prometheus_metric) { instance_double(Prometheus::Client::Histogram, observe: nil, base_labels: {}) } + + it 'adds a metric' do + expect(::Gitlab::Metrics).to receive(:histogram).with( + :meow_observe, 'Meow observe histogram', hash_including(*described_class::BASE_LABEL_KEYS), be_a(Array) + ).and_return(prometheus_metric) + + expect do |block| + metric = described_class.prometheus_metric(:meow_observe, :histogram, &block) + expect(metric).to be(prometheus_metric) + end.to yield_control + end + end + + describe '#method_call_for' do + it 'returns a MethodCall' do + method = transaction_obj.method_call_for('Foo#bar', :Foo, '#bar') + + expect(method).to be_an_instance_of(Gitlab::Metrics::MethodCall) + end + end + + describe '#add_event' do + let(:prometheus_metric) { instance_double(Prometheus::Client::Counter, increment: nil, base_labels: {}) } + + it 'adds a metric' do + expect(prometheus_metric).to receive(:increment).with(labels) + expect(described_class).to receive(:fetch_metric).with(:counter, :gitlab_transaction_event_meow_total).and_return(prometheus_metric) + + transaction_obj.add_event(:meow) + end + + it 'allows tracking of custom tags' do + expect(prometheus_metric).to receive(:increment).with(labels.merge(animal: "dog")) + expect(described_class).to receive(:fetch_metric).with(:counter, :gitlab_transaction_event_bau_total).and_return(prometheus_metric) + + transaction_obj.add_event(:bau, animal: 'dog') + end + + context 'with sensitive tags' do + it 'filters tags' do + expect(described_class).to receive(:fetch_metric).with(:counter, :gitlab_transaction_event_bau_total).and_return(prometheus_metric) + expect(prometheus_metric).not_to receive(:increment).with(hash_including(sensitive_tags)) + + transaction_obj.add_event(:bau, **sensitive_tags.merge(sane: 'yes')) + end + end + end + + describe '#increment' do + let(:prometheus_metric) { instance_double(Prometheus::Client::Counter, increment: nil, base_labels: {}) } + + it 'adds a metric' do + expect(::Gitlab::Metrics).to receive(:counter).with( + :meow, 'Meow counter', hash_including(*described_class::BASE_LABEL_KEYS) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:increment).with(labels, 1) + + transaction_obj.increment(:meow, 1) + end + + context 'with block' do + it 'overrides docstring' do + expect(::Gitlab::Metrics).to receive(:counter).with( + :block_docstring, 'test', hash_including(*described_class::BASE_LABEL_KEYS) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:increment).with(labels, 1) + + transaction_obj.increment(:block_docstring, 1) do + docstring 'test' + end + end + + it 'overrides labels' do + expect(::Gitlab::Metrics).to receive(:counter).with( + :block_labels, 'Block labels counter', hash_including(*described_class::BASE_LABEL_KEYS) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:increment).with(labels.merge(sane: 'yes'), 1) + + transaction_obj.increment(:block_labels, 1, sane: 'yes') do + label_keys %i(sane) + end + end + + it 'filters sensitive tags' do + labels_keys = sensitive_tags.keys + + expect(::Gitlab::Metrics).to receive(:counter).with( + :metric_with_sensitive_block, 'Metric with sensitive block counter', hash_excluding(labels_keys) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:increment).with(labels, 1) + + transaction_obj.increment(:metric_with_sensitive_block, 1, sensitive_tags) do + label_keys labels_keys + end + end + end + end + + describe '#set' do + let(:prometheus_metric) { instance_double(Prometheus::Client::Gauge, set: nil, base_labels: {}) } + + it 'adds a metric' do + expect(::Gitlab::Metrics).to receive(:gauge).with( + :meow_set, 'Meow set gauge', hash_including(*described_class::BASE_LABEL_KEYS), :all + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:set).with(labels, 99) + + transaction_obj.set(:meow_set, 99) + end + + context 'with block' do + it 'overrides docstring' do + expect(::Gitlab::Metrics).to receive(:gauge).with( + :block_docstring_set, 'test', hash_including(*described_class::BASE_LABEL_KEYS), :all + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:set).with(labels, 99) + + transaction_obj.set(:block_docstring_set, 99) do + docstring 'test' + end + end + + it 'overrides labels' do + expect(::Gitlab::Metrics).to receive(:gauge).with( + :block_labels_set, 'Block labels set gauge', hash_including(*described_class::BASE_LABEL_KEYS), :all + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:set).with(labels.merge(sane: 'yes'), 99) + + transaction_obj.set(:block_labels_set, 99, sane: 'yes') do + label_keys %i(sane) + end + end + + it 'filters sensitive tags' do + labels_keys = sensitive_tags.keys + + expect(::Gitlab::Metrics).to receive(:gauge).with( + :metric_set_with_sensitive_block, 'Metric set with sensitive block gauge', hash_excluding(*labels_keys), :all + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:set).with(labels, 99) + + transaction_obj.set(:metric_set_with_sensitive_block, 99, sensitive_tags) do + label_keys label_keys + end + end + end + end + + describe '#observe' do + let(:prometheus_metric) { instance_double(Prometheus::Client::Histogram, observe: nil, base_labels: {}) } + + it 'adds a metric' do + expect(::Gitlab::Metrics).to receive(:histogram).with( + :meow_observe, 'Meow observe histogram', hash_including(*described_class::BASE_LABEL_KEYS), kind_of(Array) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:observe).with(labels, 2.0) + + transaction_obj.observe(:meow_observe, 2.0) + end + + context 'with block' do + it 'overrides docstring' do + expect(::Gitlab::Metrics).to receive(:histogram).with( + :block_docstring_observe, 'test', hash_including(*described_class::BASE_LABEL_KEYS), kind_of(Array) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:observe).with(labels, 2.0) + + transaction_obj.observe(:block_docstring_observe, 2.0) do + docstring 'test' + end + end + + it 'overrides labels' do + expect(::Gitlab::Metrics).to receive(:histogram).with( + :block_labels_observe, 'Block labels observe histogram', hash_including(*described_class::BASE_LABEL_KEYS), kind_of(Array) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:observe).with(labels.merge(sane: 'yes'), 2.0) + + transaction_obj.observe(:block_labels_observe, 2.0, sane: 'yes') do + label_keys %i(sane) + end + end + + it 'filters sensitive tags' do + labels_keys = sensitive_tags.keys + + expect(::Gitlab::Metrics).to receive(:histogram).with( + :metric_observe_with_sensitive_block, + 'Metric observe with sensitive block histogram', + hash_excluding(labels_keys), + kind_of(Array) + ).and_return(prometheus_metric) + expect(prometheus_metric).to receive(:observe).with(labels, 2.0) + + transaction_obj.observe(:metric_observe_with_sensitive_block, 2.0, sensitive_tags) do + label_keys label_keys + end + end + end + end +end diff --git a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb index f928fb1eb43..d823e7ac221 100644 --- a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb +++ b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb @@ -12,6 +12,7 @@ RSpec.shared_examples 'StageEventModel' do project_id: 4, author_id: 5, milestone_id: 6, + state_id: 1, start_event_timestamp: time, end_event_timestamp: time }, @@ -22,6 +23,7 @@ RSpec.shared_examples 'StageEventModel' do project_id: 11, author_id: 12, milestone_id: 13, + state_id: 1, start_event_timestamp: time, end_event_timestamp: time } @@ -34,8 +36,9 @@ RSpec.shared_examples 'StageEventModel' do described_class.issuable_id_column, :group_id, :project_id, - :milestone_id, :author_id, + :milestone_id, + :state_id, :start_event_timestamp, :end_event_timestamp ] @@ -59,10 +62,120 @@ RSpec.shared_examples 'StageEventModel' do upsert_data output_data = described_class.all.map do |record| - column_order.map { |column| record[column] } + column_order.map do |column| + if column == :state_id + described_class.states[record[column]] + else + record[column] + end + end end.sort expect(input_data.map(&:values).sort).to eq(output_data) end end + + describe 'scopes' do + def attributes(array) + array.map(&:attributes) + end + + RSpec::Matchers.define :match_attributes do |expected| + match do |actual| + actual.map(&:attributes) == expected.map(&:attributes) + end + end + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:user) } + let_it_be(:milestone) { create(:milestone) } + let_it_be(:issuable_with_assignee) { create(issuable_factory, assignees: [user])} + + let_it_be(:record) { create(stage_event_factory, start_event_timestamp: 3.years.ago.to_date, end_event_timestamp: 2.years.ago.to_date) } + let_it_be(:record_with_author) { create(stage_event_factory, author_id: user.id) } + let_it_be(:record_with_project) { create(stage_event_factory, project_id: project.id) } + let_it_be(:record_with_group) { create(stage_event_factory, group_id: project.namespace_id) } + let_it_be(:record_with_assigned_issuable) { create(stage_event_factory, described_class.issuable_id_column => issuable_with_assignee.id) } + let_it_be(:record_with_milestone) { create(stage_event_factory, milestone_id: milestone.id) } + + it 'filters by stage_event_hash_id' do + records = described_class.by_stage_event_hash_id(record.stage_event_hash_id) + + expect(records).to match_attributes([record]) + end + + it 'filters by project_id' do + records = described_class.by_project_id(project.id) + + expect(records).to match_attributes([record_with_project]) + end + + it 'filters by group_id' do + records = described_class.by_group_id(project.namespace_id) + + expect(records).to match_attributes([record_with_group]) + end + + it 'filters by author_id' do + records = described_class.authored(user) + + expect(records).to match_attributes([record_with_author]) + end + + it 'filters by assignee' do + records = described_class.assigned_to(user) + + expect(records).to match_attributes([record_with_assigned_issuable]) + end + + it 'filters by milestone_id' do + records = described_class.with_milestone_id(milestone.id) + + expect(records).to match_attributes([record_with_milestone]) + end + + describe 'start_event_timestamp filtering' do + it 'when range is given' do + records = described_class + .start_event_timestamp_after(4.years.ago) + .start_event_timestamp_before(2.years.ago) + + expect(records).to match_attributes([record]) + end + + it 'when specifying upper bound' do + records = described_class.start_event_timestamp_before(2.years.ago) + + expect(attributes(records)).to include(attributes([record]).first) + end + + it 'when specifying the lower bound' do + records = described_class.start_event_timestamp_after(4.years.ago) + + expect(attributes(records)).to include(attributes([record]).first) + end + end + + describe 'end_event_timestamp filtering' do + it 'when range is given' do + records = described_class + .end_event_timestamp_after(3.years.ago) + .end_event_timestamp_before(1.year.ago) + + expect(records).to match_attributes([record]) + end + + it 'when specifying upper bound' do + records = described_class.end_event_timestamp_before(1.year.ago) + + expect(attributes(records)).to include(attributes([record]).first) + end + + it 'when specifying the lower bound' do + records = described_class.end_event_timestamp_after(3.years.ago) + + expect(attributes(records)).to include(attributes([record]).first) + end + end + end end diff --git a/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb b/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb index a4e0d6c871e..2d08de297a3 100644 --- a/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/ttl_expirable_shared_examples.rb @@ -11,18 +11,18 @@ RSpec.shared_examples 'ttl_expirable' do it { is_expected.to validate_presence_of(:status) } end - describe '.updated_before' do + describe '.read_before' do # rubocop:disable Rails/SaveBang let_it_be_with_reload(:item1) { create(class_symbol) } let_it_be(:item2) { create(class_symbol) } # rubocop:enable Rails/SaveBang before do - item1.update_column(:updated_at, 1.month.ago) + item1.update_column(:read_at, 1.month.ago) end it 'returns items with created at older than the supplied number of days' do - expect(described_class.updated_before(10)).to contain_exactly(item1) + expect(described_class.read_before(10)).to contain_exactly(item1) end end @@ -48,4 +48,13 @@ RSpec.shared_examples 'ttl_expirable' do expect(described_class.lock_next_by(:created_at)).to contain_exactly(item3) end end + + describe '#read', :freeze_time do + let_it_be(:old_read_at) { 1.day.ago } + let_it_be(:item1) { create(class_symbol, read_at: old_read_at) } + + it 'updates read_at' do + expect { item1.read! }.to change { item1.reload.read_at } + end + end end diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb index 56c202cb228..a2909c66e22 100644 --- a/spec/support/shared_examples/models/member_shared_examples.rb +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -299,6 +299,22 @@ RSpec.shared_examples_for "member creation" do end end end + + context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do + before do + stub_experiments(invite_members_for_task: true) + end + + it 'creates a member_task with the correct attributes', :aggregate_failures do + task_project = source.is_a?(Group) ? create(:project, group: source) : source + described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute + + member = source.members.last + + expect(member.tasks_to_be_done).to match_array([:ci, :code]) + expect(member.member_task.project).to eq(task_project) + end + end end end @@ -379,5 +395,20 @@ RSpec.shared_examples_for "bulk member creation" do expect(members).to all(be_persisted) end end + + context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do + before do + stub_experiments(invite_members_for_task: true) + end + + it 'creates a member_task with the correct attributes', :aggregate_failures do + task_project = source.is_a?(Group) ? create(:project, group: source) : source + members = described_class.add_users(source, [user1], :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id) + member = members.last + + expect(member.tasks_to_be_done).to match_array([:ci, :code]) + expect(member.member_task.project).to eq(task_project) + end + end end end diff --git a/spec/support/shared_examples/models/reviewer_state_shared_examples.rb b/spec/support/shared_examples/models/reviewer_state_shared_examples.rb new file mode 100644 index 00000000000..f1392768b06 --- /dev/null +++ b/spec/support/shared_examples/models/reviewer_state_shared_examples.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'having reviewer state' do + describe 'mr_attention_requests feature flag is disabled' do + before do + stub_feature_flags(mr_attention_requests: false) + end + + it { is_expected.to have_attributes(state: 'unreviewed') } + end + + describe 'mr_attention_requests feature flag is enabled' do + it { is_expected.to have_attributes(state: 'attention_requested') } + end +end diff --git a/spec/support/shared_examples/namespaces/traversal_examples.rb b/spec/support/shared_examples/namespaces/traversal_examples.rb index d126b242fb0..ac6a843663f 100644 --- a/spec/support/shared_examples/namespaces/traversal_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_examples.rb @@ -22,6 +22,8 @@ RSpec.shared_examples 'namespace traversal' do let_it_be(:deep_nested_group) { create(:group, parent: nested_group) } let_it_be(:very_deep_nested_group) { create(:group, parent: deep_nested_group) } let_it_be(:groups) { [group, nested_group, deep_nested_group, very_deep_nested_group] } + let_it_be(:project) { create(:project, group: nested_group) } + let_it_be(:project_namespace) { project.project_namespace } describe '#root_ancestor' do it 'returns the correct root ancestor' do @@ -65,6 +67,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestors).to contain_exactly(group, nested_group) expect(nested_group.ancestors).to contain_exactly(group) expect(group.ancestors).to eq([]) + expect(project_namespace.ancestors).to be_empty end context 'with asc hierarchy_order' do @@ -73,6 +76,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestors(hierarchy_order: :asc)).to eq [nested_group, group] expect(nested_group.ancestors(hierarchy_order: :asc)).to eq [group] expect(group.ancestors(hierarchy_order: :asc)).to eq([]) + expect(project_namespace.ancestors(hierarchy_order: :asc)).to be_empty end end @@ -82,6 +86,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestors(hierarchy_order: :desc)).to eq [group, nested_group] expect(nested_group.ancestors(hierarchy_order: :desc)).to eq [group] expect(group.ancestors(hierarchy_order: :desc)).to eq([]) + expect(project_namespace.ancestors(hierarchy_order: :desc)).to be_empty end end @@ -98,6 +103,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestor_ids).to contain_exactly(group.id, nested_group.id) expect(nested_group.ancestor_ids).to contain_exactly(group.id) expect(group.ancestor_ids).to be_empty + expect(project_namespace.ancestor_ids).to be_empty end context 'with asc hierarchy_order' do @@ -106,6 +112,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestor_ids(hierarchy_order: :asc)).to eq [nested_group.id, group.id] expect(nested_group.ancestor_ids(hierarchy_order: :asc)).to eq [group.id] expect(group.ancestor_ids(hierarchy_order: :asc)).to eq([]) + expect(project_namespace.ancestor_ids(hierarchy_order: :asc)).to eq([]) end end @@ -115,6 +122,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.ancestor_ids(hierarchy_order: :desc)).to eq [group.id, nested_group.id] expect(nested_group.ancestor_ids(hierarchy_order: :desc)).to eq [group.id] expect(group.ancestor_ids(hierarchy_order: :desc)).to eq([]) + expect(project_namespace.ancestor_ids(hierarchy_order: :desc)).to eq([]) end end @@ -131,6 +139,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestors).to contain_exactly(group, nested_group, deep_nested_group) expect(nested_group.self_and_ancestors).to contain_exactly(group, nested_group) expect(group.self_and_ancestors).to contain_exactly(group) + expect(project_namespace.self_and_ancestors).to contain_exactly(project_namespace) end context 'with asc hierarchy_order' do @@ -139,6 +148,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestors(hierarchy_order: :asc)).to eq [deep_nested_group, nested_group, group] expect(nested_group.self_and_ancestors(hierarchy_order: :asc)).to eq [nested_group, group] expect(group.self_and_ancestors(hierarchy_order: :asc)).to eq([group]) + expect(project_namespace.self_and_ancestors(hierarchy_order: :asc)).to eq([project_namespace]) end end @@ -148,6 +158,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestors(hierarchy_order: :desc)).to eq [group, nested_group, deep_nested_group] expect(nested_group.self_and_ancestors(hierarchy_order: :desc)).to eq [group, nested_group] expect(group.self_and_ancestors(hierarchy_order: :desc)).to eq([group]) + expect(project_namespace.self_and_ancestors(hierarchy_order: :desc)).to eq([project_namespace]) end end @@ -164,6 +175,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestor_ids).to contain_exactly(group.id, nested_group.id, deep_nested_group.id) expect(nested_group.self_and_ancestor_ids).to contain_exactly(group.id, nested_group.id) expect(group.self_and_ancestor_ids).to contain_exactly(group.id) + expect(project_namespace.self_and_ancestor_ids).to contain_exactly(project_namespace.id) end context 'with asc hierarchy_order' do @@ -172,6 +184,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestor_ids(hierarchy_order: :asc)).to eq [deep_nested_group.id, nested_group.id, group.id] expect(nested_group.self_and_ancestor_ids(hierarchy_order: :asc)).to eq [nested_group.id, group.id] expect(group.self_and_ancestor_ids(hierarchy_order: :asc)).to eq([group.id]) + expect(project_namespace.self_and_ancestor_ids(hierarchy_order: :asc)).to eq([project_namespace.id]) end end @@ -181,6 +194,7 @@ RSpec.shared_examples 'namespace traversal' do expect(deep_nested_group.self_and_ancestor_ids(hierarchy_order: :desc)).to eq [group.id, nested_group.id, deep_nested_group.id] expect(nested_group.self_and_ancestor_ids(hierarchy_order: :desc)).to eq [group.id, nested_group.id] expect(group.self_and_ancestor_ids(hierarchy_order: :desc)).to eq([group.id]) + expect(project_namespace.self_and_ancestor_ids(hierarchy_order: :desc)).to eq([project_namespace.id]) end end @@ -205,6 +219,10 @@ RSpec.shared_examples 'namespace traversal' do describe '#recursive_descendants' do it_behaves_like 'recursive version', :descendants end + + it 'does not include project namespaces' do + expect(group.descendants.to_a).not_to include(project_namespace) + end end describe '#self_and_descendants' do @@ -223,6 +241,10 @@ RSpec.shared_examples 'namespace traversal' do it_behaves_like 'recursive version', :self_and_descendants end + + it 'does not include project namespaces' do + expect(group.self_and_descendants.to_a).not_to include(project_namespace) + end end describe '#self_and_descendant_ids' do diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index 74b1bacc560..4c09c1c2a3b 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -25,12 +25,6 @@ RSpec.shared_examples 'namespace traversal scopes' do it { is_expected.to contain_exactly(group_1.id, group_2.id) } end - describe '.without_sti_condition' do - subject { described_class.without_sti_condition } - - it { expect(subject.where_values_hash).not_to have_key(:type) } - end - describe '.order_by_depth' do subject { described_class.where(id: [group_1, nested_group_1, deep_nested_group_1]).order_by_depth(direction) } @@ -55,6 +49,53 @@ RSpec.shared_examples 'namespace traversal scopes' do it { is_expected.to eq described_class.column_names } end + shared_examples '.roots' do + context 'with only sub-groups' do + subject { described_class.where(id: [deep_nested_group_1, nested_group_1, deep_nested_group_2]).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + + context 'with only root groups' do + subject { described_class.where(id: [group_1, group_2]).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + + context 'with all groups' do + subject { described_class.where(id: groups).roots } + + it { is_expected.to contain_exactly(group_1, group_2) } + end + end + + describe '.roots' do + context "use_traversal_ids_roots feature flag is true" do + before do + stub_feature_flags(use_traversal_ids: true) + stub_feature_flags(use_traversal_ids_roots: true) + end + + it_behaves_like '.roots' + + it 'not make recursive queries' do + expect { described_class.where(id: [nested_group_1]).roots.load }.not_to make_queries_matching(/WITH RECURSIVE/) + end + end + + context "use_traversal_ids_roots feature flag is false" do + before do + stub_feature_flags(use_traversal_ids_roots: false) + end + + it_behaves_like '.roots' + + it 'make recursive queries' do + expect { described_class.where(id: [nested_group_1]).roots.load }.to make_queries_matching(/WITH RECURSIVE/) + end + end + end + shared_examples '.self_and_ancestors' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_ancestors } @@ -156,7 +197,7 @@ RSpec.shared_examples 'namespace traversal scopes' do end end - describe '.self_and_descendants' do + shared_examples '.self_and_descendants' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_descendants } it { is_expected.to contain_exactly(nested_group_1, deep_nested_group_1, nested_group_2, deep_nested_group_2) } @@ -174,7 +215,19 @@ RSpec.shared_examples 'namespace traversal scopes' do end end - describe '.self_and_descendant_ids' do + describe '.self_and_descendants' do + include_examples '.self_and_descendants' + + context 'with traversal_ids_btree feature flag disabled' do + before do + stub_feature_flags(traversal_ids_btree: false) + end + + include_examples '.self_and_descendants' + end + end + + shared_examples '.self_and_descendant_ids' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_descendant_ids.pluck(:id) } it { is_expected.to contain_exactly(nested_group_1.id, deep_nested_group_1.id, nested_group_2.id, deep_nested_group_2.id) } @@ -190,4 +243,16 @@ RSpec.shared_examples 'namespace traversal scopes' do it { is_expected.to contain_exactly(deep_nested_group_1.id, deep_nested_group_2.id) } end end + + describe '.self_and_descendant_ids' do + include_examples '.self_and_descendant_ids' + + context 'with traversal_ids_btree feature flag disabled' do + before do + stub_feature_flags(traversal_ids_btree: false) + end + + include_examples '.self_and_descendant_ids' + end + end end diff --git a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb new file mode 100644 index 00000000000..5167d27f8b9 --- /dev/null +++ b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'promote_to_incident quick action' do + describe '/promote_to_incident' do + context 'when issue can be promoted' do + it 'promotes issue to incident' do + add_note('/promote_to_incident') + + expect(issue.reload.issue_type).to eq('incident') + expect(page).to have_content('Issue has been promoted to incident') + end + end + + context 'when issue is already an incident' do + let(:issue) { create(:incident, project: project) } + + it 'does not promote the issue' do + add_note('/promote_to_incident') + + expect(page).to have_content('Could not apply promote_to_incident command') + end + end + + context 'when user does not have permissions' do + let(:guest) { create(:user) } + + before do + sign_in(guest) + visit project_issue_path(project, issue) + wait_for_all_requests + end + + it 'does not promote the issue' do + add_note('/promote_to_incident') + + expect(page).to have_content('Could not apply promote_to_incident command') + end + end + end +end diff --git a/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb new file mode 100644 index 00000000000..e0225070986 --- /dev/null +++ b/spec/support/shared_examples/requests/api/debian_common_shared_examples.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'rejects Debian access with unknown container id' do |anonymous_status, auth_method| + context 'with an unknown container' do + let(:container) { double(id: non_existing_record_id) } + + context 'as anonymous' do + it_behaves_like 'Debian packages GET request', anonymous_status, nil + end + + context 'as authenticated user' do + include_context 'Debian repository auth headers', :not_a_member, auth_method do + it_behaves_like 'Debian packages GET request', :not_found, nil + end + end + end +end diff --git a/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb new file mode 100644 index 00000000000..5cd63c33936 --- /dev/null +++ b/spec/support/shared_examples/requests/api/debian_distributions_shared_examples.rb @@ -0,0 +1,192 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'Debian distributions GET request' do |status, body = nil| + and_body = body.nil? ? '' : ' and expected body' + + it "returns #{status}#{and_body}" do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end +end + +RSpec.shared_examples 'Debian distributions PUT request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :success + it 'updates distribution', :aggregate_failures do + expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original + + expect { subject } + .to not_change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count } + .and not_change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count } + .and not_change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count } + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'Debian distributions DELETE request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :success + it 'updates distribution', :aggregate_failures do + expect { subject } + .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(-1) + .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(-1) + .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(-2) + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'Debian distributions POST request' do |status, body| + and_body = body.nil? ? '' : ' and expected body' + + if status == :created + it 'creates distribution', :aggregate_failures do + expect(::Packages::Debian::CreateDistributionService).to receive(:new).with(container, user, api_params).and_call_original + + expect { subject } + .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(1) + .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(1) + .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(2) + + expect(response).to have_gitlab_http_status(status) + expect(response.media_type).to eq('application/json') + + unless body.nil? + expect(response.body).to match(body) + end + end + else + it "returns #{status}#{and_body}", :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(status) + + unless body.nil? + expect(response.body).to match(body) + end + end + end +end + +RSpec.shared_examples 'Debian distributions read endpoint' do |desired_behavior, success_status, success_body| + context 'with valid container' do + using RSpec::Parameterized::TableSyntax + + where(:visibility_level, :user_type, :auth_method, :expected_status, :expected_body) do + :public | :guest | :private_token | success_status | success_body + :public | :not_a_member | :private_token | success_status | success_body + :public | :anonymous | :private_token | success_status | success_body + :public | :invalid_token | :private_token | :unauthorized | nil + :private | :developer | :private_token | success_status | success_body + :private | :developer | :basic | :not_found | nil + :private | :guest | :private_token | :forbidden | nil + :private | :not_a_member | :private_token | :not_found | nil + :private | :anonymous | :private_token | :not_found | nil + :private | :invalid_token | :private_token | :unauthorized | nil + end + + with_them do + include_context 'Debian repository access', params[:visibility_level], params[:user_type], params[:auth_method] do + it_behaves_like "Debian distributions #{desired_behavior} request", params[:expected_status], params[:expected_body] + end + end + end + + it_behaves_like 'rejects Debian access with unknown container id', :not_found, :private_token +end + +RSpec.shared_examples 'Debian distributions write endpoint' do |desired_behavior, success_status, success_body| + context 'with valid container' do + using RSpec::Parameterized::TableSyntax + + where(:visibility_level, :user_type, :auth_method, :expected_status, :expected_body) do + :public | :developer | :private_token | success_status | success_body + :public | :developer | :basic | :unauthorized | nil + :public | :guest | :private_token | :forbidden | nil + :public | :not_a_member | :private_token | :forbidden | nil + :public | :anonymous | :private_token | :unauthorized | nil + :public | :invalid_token | :private_token | :unauthorized | nil + :private | :developer | :private_token | success_status | success_body + :private | :guest | :private_token | :forbidden | nil + :private | :not_a_member | :private_token | :not_found | nil + :private | :anonymous | :private_token | :not_found | nil + :private | :invalid_token | :private_token | :unauthorized | nil + end + + with_them do + include_context 'Debian repository access', params[:visibility_level], params[:user_type], params[:auth_method] do + it_behaves_like "Debian distributions #{desired_behavior} request", params[:expected_status], params[:expected_body] + end + end + end + + it_behaves_like 'rejects Debian access with unknown container id', :not_found, :private_token +end + +RSpec.shared_examples 'Debian distributions maintainer write endpoint' do |desired_behavior, success_status, success_body| + context 'with valid container' do + using RSpec::Parameterized::TableSyntax + + where(:visibility_level, :user_type, :auth_method, :expected_status, :expected_body) do + :public | :maintainer | :private_token | success_status | success_body + :public | :maintainer | :basic | :unauthorized | nil + :public | :developer | :private_token | :forbidden | nil + :public | :not_a_member | :private_token | :forbidden | nil + :public | :anonymous | :private_token | :unauthorized | nil + :public | :invalid_token | :private_token | :unauthorized | nil + :private | :maintainer | :private_token | success_status | success_body + :private | :developer | :private_token | :forbidden | nil + :private | :not_a_member | :private_token | :not_found | nil + :private | :anonymous | :private_token | :not_found | nil + :private | :invalid_token | :private_token | :unauthorized | nil + end + + with_them do + include_context 'Debian repository access', params[:visibility_level], params[:user_type], params[:auth_method] do + it_behaves_like "Debian distributions #{desired_behavior} request", params[:expected_status], params[:expected_body] + end + end + end + + it_behaves_like 'rejects Debian access with unknown container id', :not_found, :private_token +end diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb index a3ed74085fb..2fd5e6a5f91 100644 --- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb @@ -1,127 +1,6 @@ # frozen_string_literal: true -RSpec.shared_context 'Debian repository shared context' do |container_type, can_freeze| - include_context 'workhorse headers' - - before do - stub_feature_flags(debian_packages: true, debian_group_packages: true) - end - - let_it_be(:private_container, freeze: can_freeze) { create(container_type, :private) } - let_it_be(:public_container, freeze: can_freeze) { create(container_type, :public) } - let_it_be(:user, freeze: true) { create(:user) } - let_it_be(:personal_access_token, freeze: true) { create(:personal_access_token, user: user) } - - let_it_be(:private_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: private_container, codename: 'existing-codename') } - let_it_be(:private_component, freeze: true) { create("debian_#{container_type}_component", distribution: private_distribution, name: 'existing-component') } - let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') } - let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') } - let_it_be(:private_component_file) { create("debian_#{container_type}_component_file", component: private_component, architecture: private_architecture) } - - let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: public_container, codename: 'existing-codename') } - let_it_be(:public_component, freeze: true) { create("debian_#{container_type}_component", distribution: public_distribution, name: 'existing-component') } - let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') } - let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') } - let_it_be(:public_component_file) { create("debian_#{container_type}_component_file", component: public_component, architecture: public_architecture) } - - if container_type == :group - let_it_be(:private_project) { create(:project, :private, group: private_container) } - let_it_be(:public_project) { create(:project, :public, group: public_container) } - let_it_be(:private_project_distribution) { create(:debian_project_distribution, container: private_project, codename: 'existing-codename') } - let_it_be(:public_project_distribution) { create(:debian_project_distribution, container: public_project, codename: 'existing-codename') } - - let(:project) { { private: private_project, public: public_project }[visibility_level] } - else - let_it_be(:private_project) { private_container } - let_it_be(:public_project) { public_container } - let_it_be(:private_project_distribution) { private_distribution } - let_it_be(:public_project_distribution) { public_distribution } - end - - let_it_be(:private_package) { create(:debian_package, project: private_project, published_in: private_project_distribution) } - let_it_be(:public_package) { create(:debian_package, project: public_project, published_in: public_project_distribution) } - - let(:visibility_level) { :public } - - let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] } - let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] } - let(:component) { { private: private_component, public: public_component }[visibility_level] } - let(:component_file) { { private: private_component_file, public: public_component_file }[visibility_level] } - let(:package) { { private: private_package, public: public_package }[visibility_level] } - let(:letter) { package.name[0..2] == 'lib' ? package.name[0..3] : package.name[0] } - - let(:method) { :get } - - let(:workhorse_params) do - if method == :put - file_upload = fixture_file_upload("spec/fixtures/packages/debian/#{file_name}") - { file: file_upload } - else - {} - end - end - - let(:api_params) { workhorse_params } - - let(:auth_headers) { {} } - let(:wh_headers) do - if method == :put - workhorse_headers - else - {} - end - end - - let(:headers) { auth_headers.merge(wh_headers) } - - let(:send_rewritten_field) { true } - - subject do - if method == :put - workhorse_finalize( - api(url), - method: method, - file_key: :file, - params: api_params, - headers: headers, - send_rewritten_field: send_rewritten_field - ) - else - send method, api(url), headers: headers, params: api_params - end - end -end - -RSpec.shared_context 'with file_name' do |file_name| - let(:file_name) { file_name } -end - -RSpec.shared_context 'Debian repository auth headers' do |user_role, user_token, auth_method = :token| - let(:token) { user_token ? personal_access_token.token : 'wrong' } - - let(:auth_headers) do - if user_role == :anonymous - {} - elsif auth_method == :token - { 'Private-Token' => token } - else - basic_auth_header(user.username, token) - end - end -end - -RSpec.shared_context 'Debian repository access' do |visibility_level, user_role, add_member, user_token, auth_method| - include_context 'Debian repository auth headers', user_role, user_token, auth_method do - let(:containers) { { private: private_container, public: public_container } } - let(:container) { containers[visibility_level] } - - before do - container.send("add_#{user_role}", user) if add_member && user_role != :anonymous - end - end -end - -RSpec.shared_examples 'Debian repository GET request' do |status, body = nil| +RSpec.shared_examples 'Debian packages GET request' do |status, body = nil| and_body = body.nil? ? '' : ' and expected body' it "returns #{status}#{and_body}" do @@ -135,7 +14,7 @@ RSpec.shared_examples 'Debian repository GET request' do |status, body = nil| end end -RSpec.shared_examples 'Debian repository upload request' do |status, body = nil| +RSpec.shared_examples 'Debian packages upload request' do |status, body = nil| and_body = body.nil? ? '' : ' and expected body' if status == :created @@ -175,7 +54,7 @@ RSpec.shared_examples 'Debian repository upload request' do |status, body = nil| end end -RSpec.shared_examples 'Debian repository upload authorize request' do |status, body = nil| +RSpec.shared_examples 'Debian packages upload authorize request' do |status, body = nil| and_body = body.nil? ? '' : ' and expected body' if status == :created @@ -221,237 +100,57 @@ RSpec.shared_examples 'Debian repository upload authorize request' do |status, b end end -RSpec.shared_examples 'Debian repository POST distribution request' do |status, body| - and_body = body.nil? ? '' : ' and expected body' - - if status == :created - it 'creates distribution', :aggregate_failures do - expect(::Packages::Debian::CreateDistributionService).to receive(:new).with(container, user, api_params).and_call_original - - expect { subject } - .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(1) - .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(1) - .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(2) - - expect(response).to have_gitlab_http_status(status) - expect(response.media_type).to eq('application/json') - - unless body.nil? - expect(response.body).to match(body) - end - end - else - it "returns #{status}#{and_body}", :aggregate_failures do - subject - - expect(response).to have_gitlab_http_status(status) - - unless body.nil? - expect(response.body).to match(body) - end - end - end -end - -RSpec.shared_examples 'Debian repository PUT distribution request' do |status, body| - and_body = body.nil? ? '' : ' and expected body' - - if status == :success - it 'updates distribution', :aggregate_failures do - expect(::Packages::Debian::UpdateDistributionService).to receive(:new).with(distribution, api_params.except(:codename)).and_call_original - - expect { subject } - .to not_change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count } - .and not_change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count } - .and not_change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count } - - expect(response).to have_gitlab_http_status(status) - expect(response.media_type).to eq('application/json') - - unless body.nil? - expect(response.body).to match(body) - end - end - else - it "returns #{status}#{and_body}", :aggregate_failures do - subject - - expect(response).to have_gitlab_http_status(status) - - unless body.nil? - expect(response.body).to match(body) - end - end - end -end - -RSpec.shared_examples 'Debian repository DELETE distribution request' do |status, body| - and_body = body.nil? ? '' : ' and expected body' - - if status == :success - it 'updates distribution', :aggregate_failures do - expect { subject } - .to change { Packages::Debian::GroupDistribution.all.count + Packages::Debian::ProjectDistribution.all.count }.by(-1) - .and change { Packages::Debian::GroupComponent.all.count + Packages::Debian::ProjectComponent.all.count }.by(-1) - .and change { Packages::Debian::GroupArchitecture.all.count + Packages::Debian::ProjectArchitecture.all.count }.by(-2) - - expect(response).to have_gitlab_http_status(status) - expect(response.media_type).to eq('application/json') - - unless body.nil? - expect(response.body).to match(body) - end - end - else - it "returns #{status}#{and_body}", :aggregate_failures do - subject - - expect(response).to have_gitlab_http_status(status) - - unless body.nil? - expect(response.body).to match(body) - end - end - end -end - -RSpec.shared_examples 'rejects Debian access with unknown container id' do |hidden_status| - context 'with an unknown container' do - let(:container) { double(id: non_existing_record_id) } - - context 'as anonymous' do - it_behaves_like 'Debian repository GET request', hidden_status, nil - end - - context 'as authenticated user' do - subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) } - - it_behaves_like 'Debian repository GET request', :not_found, nil - end - end -end - -RSpec.shared_examples 'Debian repository read endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| - hidden_status = if authenticate_non_public - :unauthorized - else - :not_found - end - - context 'with valid container' do - using RSpec::Parameterized::TableSyntax - - where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do - :public | :developer | true | true | success_status | success_body - :public | :guest | true | true | success_status | success_body - :public | :developer | true | false | :unauthorized | nil - :public | :guest | true | false | :unauthorized | nil - :public | :developer | false | true | success_status | success_body - :public | :guest | false | true | success_status | success_body - :public | :developer | false | false | :unauthorized | nil - :public | :guest | false | false | :unauthorized | nil - :public | :anonymous | false | true | success_status | success_body - :private | :developer | true | true | success_status | success_body - :private | :guest | true | true | :forbidden | nil - :private | :developer | true | false | :unauthorized | nil - :private | :guest | true | false | :unauthorized | nil - :private | :developer | false | true | :not_found | nil - :private | :guest | false | true | :not_found | nil - :private | :developer | false | false | :unauthorized | nil - :private | :guest | false | false | :unauthorized | nil - :private | :anonymous | false | true | hidden_status | nil - end - - with_them do - include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do - it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body] - end - end - end - - it_behaves_like 'rejects Debian access with unknown container id', hidden_status -end - -RSpec.shared_examples 'Debian repository write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| - hidden_status = if authenticate_non_public - :unauthorized - else - :not_found - end - +RSpec.shared_examples 'Debian packages read endpoint' do |desired_behavior, success_status, success_body| context 'with valid container' do using RSpec::Parameterized::TableSyntax - where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do - :public | :developer | true | true | success_status | success_body - :public | :guest | true | true | :forbidden | nil - :public | :developer | true | false | :unauthorized | nil - :public | :guest | true | false | :unauthorized | nil - :public | :developer | false | true | :forbidden | nil - :public | :guest | false | true | :forbidden | nil - :public | :developer | false | false | :unauthorized | nil - :public | :guest | false | false | :unauthorized | nil - :public | :anonymous | false | true | :unauthorized | nil - :private | :developer | true | true | success_status | success_body - :private | :guest | true | true | :forbidden | nil - :private | :developer | true | false | :unauthorized | nil - :private | :guest | true | false | :unauthorized | nil - :private | :developer | false | true | :not_found | nil - :private | :guest | false | true | :not_found | nil - :private | :developer | false | false | :unauthorized | nil - :private | :guest | false | false | :unauthorized | nil - :private | :anonymous | false | true | hidden_status | nil + where(:visibility_level, :user_type, :auth_method, :expected_status, :expected_body) do + :public | :guest | :basic | success_status | success_body + :public | :not_a_member | :basic | success_status | success_body + :public | :anonymous | :basic | success_status | success_body + :public | :invalid_token | :basic | :unauthorized | nil + :private | :developer | :basic | success_status | success_body + :private | :developer | :private_token | :unauthorized | nil + :private | :guest | :basic | :forbidden | nil + :private | :not_a_member | :basic | :not_found | nil + :private | :anonymous | :basic | :unauthorized | nil + :private | :invalid_token | :basic | :unauthorized | nil end with_them do - include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do - it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body] + include_context 'Debian repository access', params[:visibility_level], params[:user_type], params[:auth_method] do + it_behaves_like "Debian packages #{desired_behavior} request", params[:expected_status], params[:expected_body] end end end - it_behaves_like 'rejects Debian access with unknown container id', hidden_status + it_behaves_like 'rejects Debian access with unknown container id', :unauthorized, :basic end -RSpec.shared_examples 'Debian repository maintainer write endpoint' do |desired_behavior, success_status, success_body, authenticate_non_public: true| - hidden_status = if authenticate_non_public - :unauthorized - else - :not_found - end - +RSpec.shared_examples 'Debian packages write endpoint' do |desired_behavior, success_status, success_body| context 'with valid container' do using RSpec::Parameterized::TableSyntax - where(:visibility_level, :user_role, :member, :user_token, :expected_status, :expected_body) do - :public | :maintainer | true | true | success_status | success_body - :public | :developer | true | true | :forbidden | nil - :public | :guest | true | true | :forbidden | nil - :public | :maintainer | true | false | :unauthorized | nil - :public | :guest | true | false | :unauthorized | nil - :public | :maintainer | false | true | :forbidden | nil - :public | :guest | false | true | :forbidden | nil - :public | :maintainer | false | false | :unauthorized | nil - :public | :guest | false | false | :unauthorized | nil - :public | :anonymous | false | true | :unauthorized | nil - :private | :maintainer | true | true | success_status | success_body - :private | :developer | true | true | :forbidden | nil - :private | :guest | true | true | :forbidden | nil - :private | :maintainer | true | false | :unauthorized | nil - :private | :guest | true | false | :unauthorized | nil - :private | :maintainer | false | true | :not_found | nil - :private | :guest | false | true | :not_found | nil - :private | :maintainer | false | false | :unauthorized | nil - :private | :guest | false | false | :unauthorized | nil - :private | :anonymous | false | true | hidden_status | nil + where(:visibility_level, :user_type, :auth_method, :expected_status, :expected_body) do + :public | :developer | :basic | success_status | success_body + :public | :developer | :private_token | :unauthorized | nil + :public | :guest | :basic | :forbidden | nil + :public | :not_a_member | :basic | :forbidden | nil + :public | :anonymous | :basic | :unauthorized | nil + :public | :invalid_token | :basic | :unauthorized | nil + :private | :developer | :basic | success_status | success_body + :private | :guest | :basic | :forbidden | nil + :private | :not_a_member | :basic | :not_found | nil + :private | :anonymous | :basic | :unauthorized | nil + :private | :invalid_token | :basic | :unauthorized | nil end with_them do - include_context 'Debian repository access', params[:visibility_level], params[:user_role], params[:member], params[:user_token], :basic do - it_behaves_like "Debian repository #{desired_behavior}", params[:expected_status], params[:expected_body] + include_context 'Debian repository access', params[:visibility_level], params[:user_type], params[:auth_method] do + it_behaves_like "Debian packages #{desired_behavior} request", params[:expected_status], params[:expected_body] end end end - it_behaves_like 'rejects Debian access with unknown container id', hidden_status + it_behaves_like 'rejects Debian access with unknown container id', :unauthorized, :basic end diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb index 0cec67ff541..dca152223fb 100644 --- a/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/mutations/destroy_list_shared_examples.rb @@ -28,7 +28,7 @@ RSpec.shared_examples 'board lists destroy request' do it 'returns an error' do subject - expect(graphql_errors.first['message']).to include("The resource that you are attempting to access does not exist or you don't have permission to perform this action") + expect(graphql_errors.first['message']).to include(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) end end diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb index 41a61ba5fd7..d576a5874fd 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb @@ -2,12 +2,26 @@ RSpec.shared_examples 'a package detail' do it_behaves_like 'a working graphql query' do - it 'matches the JSON schema' do - expect(package_details).to match_schema('graphql/packages/package_details') + it_behaves_like 'matching the package details schema' + end + + context 'with pipelines' do + let_it_be(:build_info1) { create(:package_build_info, :with_pipeline, package: package) } + let_it_be(:build_info2) { create(:package_build_info, :with_pipeline, package: package) } + let_it_be(:build_info3) { create(:package_build_info, :with_pipeline, package: package) } + + it_behaves_like 'a working graphql query' do + it_behaves_like 'matching the package details schema' end end end +RSpec.shared_examples 'matching the package details schema' do + it 'matches the JSON schema' do + expect(package_details).to match_schema('graphql/packages/package_details') + end +end + RSpec.shared_examples 'a package with files' do it 'has the right amount of files' do expect(package_files_response.length).to be(package.package_files.length) diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb index 40799688144..0434d0beb7e 100644 --- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb @@ -281,7 +281,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| end end - context 'when request exceeds the rate limit' do + context 'when request exceeds the rate limit', :freeze_time, :clean_gitlab_redis_rate_limiting do before do stub_application_setting(notes_create_limit: 1) allow(::Gitlab::ApplicationRateLimiter).to receive(:increment).and_return(2) diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb index 2af7b616659..19677e92001 100644 --- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb @@ -8,6 +8,8 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project| let_it_be(:package_dependency_link3) { create(:packages_dependency_link, package: package, dependency_type: :bundleDependencies) } let_it_be(:package_dependency_link4) { create(:packages_dependency_link, package: package, dependency_type: :peerDependencies) } + let_it_be(:package_metadatum) { create(:npm_metadatum, package: package) } + let(:headers) { {} } subject { get(url, headers: headers) } @@ -39,6 +41,19 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project| # query count can slightly change between the examples so we're using a custom threshold expect { get(url, headers: headers) }.not_to exceed_query_limit(control).with_threshold(4) end + + context 'with packages_npm_abbreviated_metadata disabled' do + before do + stub_feature_flags(packages_npm_abbreviated_metadata: false) + end + + it 'calls the presenter without including metadata' do + expect(::Packages::Npm::PackagePresenter) + .to receive(:new).with(anything, anything, include_metadata: false).and_call_original + + subject + end + end end shared_examples 'reject metadata request' do |status:| diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb index ed6d9ed43c8..06c51add438 100644 --- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb @@ -167,7 +167,7 @@ end RSpec.shared_examples 'rejects PyPI access with unknown project id' do context 'with an unknown project' do - let(:project) { OpenStruct.new(id: 1234567890) } + let(:project) { double('access', id: 1234567890) } it_behaves_like 'unknown PyPI scope id' end @@ -175,7 +175,7 @@ end RSpec.shared_examples 'rejects PyPI access with unknown group id' do context 'with an unknown project' do - let(:group) { OpenStruct.new(id: 1234567890) } + let(:group) { double('access', id: 1234567890) } it_behaves_like 'unknown PyPI scope id' end diff --git a/spec/support/shared_examples/requests/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb index 8207190b1dc..40843ccbd15 100644 --- a/spec/support/shared_examples/requests/api/status_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb @@ -76,3 +76,32 @@ RSpec.shared_examples '412 response' do end end end + +RSpec.shared_examples '422 response' do + let(:message) { nil } + + before do + # Fires the request + request + end + + it 'returns 422' do + expect(response).to have_gitlab_http_status(:unprocessable_entity) + expect(json_response).to be_an Object + + if message.present? + expect(json_response['message']).to eq(message) + end + end +end + +RSpec.shared_examples '503 response' do + before do + # Fires the request + request + end + + it 'returns 503' do + expect(response).to have_gitlab_http_status(:service_unavailable) + end +end diff --git a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb new file mode 100644 index 00000000000..8f852d42c2c --- /dev/null +++ b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'applications controller - GET #show' do + describe 'GET #show' do + it 'renders template' do + get show_path + + expect(response).to render_template :show + end + + context 'when application is viewed after being created' do + before do + create_application + end + + it 'sets `@created` instance variable to `true`' do + get show_path + + expect(assigns[:created]).to eq(true) + end + end + + context 'when application is reviewed' do + it 'sets `@created` instance variable to `false`' do + get show_path + + expect(assigns[:created]).to eq(false) + end + end + end +end + +RSpec.shared_examples 'applications controller - POST #create' do + it "sets `#{OauthApplications::CREATED_SESSION_KEY}` session key to `true`" do + create_application + + expect(session[OauthApplications::CREATED_SESSION_KEY]).to eq(true) + end +end + +def create_application + create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api']) + post create_path, params: { doorkeeper_application: create_params } +end diff --git a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb index ff87fc5d8df..f8a752a5673 100644 --- a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb +++ b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb @@ -39,6 +39,10 @@ end # let(:status_api) { status_create_self_monitoring_project_admin_application_settings_path } # subject { post create_self_monitoring_project_admin_application_settings_path } RSpec.shared_examples 'triggers async worker, returns sidekiq job_id with response accepted' do + before do + allow(worker_class).to receive(:with_status).and_return(worker_class) + end + it 'returns sidekiq job_id of expected length' do subject diff --git a/spec/support/shared_examples/requests/snippet_shared_examples.rb b/spec/support/shared_examples/requests/snippet_shared_examples.rb index dae3a3e74be..b13c4da0bed 100644 --- a/spec/support/shared_examples/requests/snippet_shared_examples.rb +++ b/spec/support/shared_examples/requests/snippet_shared_examples.rb @@ -86,6 +86,7 @@ RSpec.shared_examples 'snippet blob content' do expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true' expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:') + expect(response.parsed_body).to be_empty end context 'when snippet repository is empty' do diff --git a/spec/support/shared_examples/service_desk_issue_templates_examples.rb b/spec/support/shared_examples/service_desk_issue_templates_examples.rb index fd9645df7a3..ed6c5199936 100644 --- a/spec/support/shared_examples/service_desk_issue_templates_examples.rb +++ b/spec/support/shared_examples/service_desk_issue_templates_examples.rb @@ -3,10 +3,10 @@ RSpec.shared_examples 'issue description templates from current project only' do it 'loads issue description templates from the project only' do within('#service-desk-template-select') do - expect(page).to have_content('project-issue-bar') - expect(page).to have_content('project-issue-foo') - expect(page).not_to have_content('group-issue-bar') - expect(page).not_to have_content('group-issue-foo') + expect(page).to have_content(:all, 'project-issue-bar') + expect(page).to have_content(:all, 'project-issue-foo') + expect(page).not_to have_content(:all, 'group-issue-bar') + expect(page).not_to have_content(:all, 'group-issue-foo') end end end diff --git a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb index 92a7d7ab3a3..ca86cb082a7 100644 --- a/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb +++ b/spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb @@ -3,7 +3,10 @@ # This shared_example requires the following variables: # - `service`, the service which includes AlertManagement::AlertProcessing RSpec.shared_examples 'creates an alert management alert or errors' do - it { is_expected.to be_success } + specify do + expect(subject).to be_success + expect(subject.payload).to match(alerts: all(a_kind_of(AlertManagement::Alert))) + end it 'creates AlertManagement::Alert' do expect(Gitlab::AppLogger).not_to receive(:warn) @@ -89,6 +92,7 @@ RSpec.shared_examples 'adds an alert management alert event' do expect { subject }.to change { alert.reload.events }.by(1) expect(subject).to be_success + expect(subject.payload).to match(alerts: all(a_kind_of(AlertManagement::Alert))) end it_behaves_like 'does not create an alert management alert' diff --git a/spec/support/shared_examples/services/jira/requests/base_shared_examples.rb b/spec/support/shared_examples/services/jira/requests/base_shared_examples.rb index 56a6d24d557..c4f6273b46c 100644 --- a/spec/support/shared_examples/services/jira/requests/base_shared_examples.rb +++ b/spec/support/shared_examples/services/jira/requests/base_shared_examples.rb @@ -26,11 +26,14 @@ RSpec.shared_examples 'a service that handles Jira API errors' do expect(subject).to be_a(ServiceResponse) expect(subject).to be_error - expect(subject.message).to include(expected_message) + expect(subject.message).to start_with(expected_message) end end context 'when the JSON in JIRA::HTTPError is unsafe' do + config_docs_link_url = Rails.application.routes.url_helpers.help_page_path('integration/jira/configure') + let(:docs_link_start) { '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: config_docs_link_url } } + before do stub_client_and_raise(JIRA::HTTPError, error) end @@ -39,7 +42,8 @@ RSpec.shared_examples 'a service that handles Jira API errors' do let(:error) { '{"errorMessages":' } it 'returns the default error message' do - expect(subject.message).to eq('An error occurred while requesting data from Jira. Check your Jira integration configuration and try again.') + error_message = 'An error occurred while requesting data from Jira. Check your %{docs_link_start}Jira integration configuration</a> and try again.' % { docs_link_start: docs_link_start } + expect(subject.message).to eq(error_message) end end @@ -47,7 +51,8 @@ RSpec.shared_examples 'a service that handles Jira API errors' do let(:error) { '{"errorMessages":["<script>alert(true)</script>foo"]}' } it 'sanitizes it' do - expect(subject.message).to eq('An error occurred while requesting data from Jira: foo. Check your Jira integration configuration and try again.') + error_message = 'An error occurred while requesting data from Jira: foo. Check your %{docs_link_start}Jira integration configuration</a> and try again.' % { docs_link_start: docs_link_start } + expect(subject.message).to eq(error_message) end end end diff --git a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb new file mode 100644 index 00000000000..716bee39fca --- /dev/null +++ b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'filters by paginated notes' do |event_type| + let(:event) { create(event_type) } # rubocop:disable Rails/SaveBang + + before do + create(event_type, issue: event.issue) + end + + it 'only returns given notes' do + paginated_notes = { event_type.to_s.pluralize => [double(id: event.id)] } + notes = described_class.new(event.issue, user, paginated_notes: paginated_notes).execute + + expect(notes.size).to eq(1) + expect(notes.first.event).to eq(event) + end + + context 'when paginated notes is empty' do + it 'does not return any notes' do + notes = described_class.new(event.issue, user, paginated_notes: {}).execute + + expect(notes.size).to eq(0) + end + end +end diff --git a/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb b/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb index 89c0841fbd6..e6da96e12ec 100644 --- a/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb +++ b/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb @@ -17,7 +17,7 @@ end RSpec.shared_examples 'returns in_progress based on Sidekiq::Status' do it 'returns true when job is enqueued' do - jid = described_class.perform_async + jid = described_class.with_status.perform_async expect(described_class.in_progress?(jid)).to eq(true) end diff --git a/spec/support/stub_snowplow.rb b/spec/support/stub_snowplow.rb index a21ce2399d7..c6e3b40972f 100644 --- a/spec/support/stub_snowplow.rb +++ b/spec/support/stub_snowplow.rb @@ -8,8 +8,6 @@ module StubSnowplow host = 'localhost' # rubocop:disable RSpec/AnyInstanceOf - allow_any_instance_of(Gitlab::Tracking::Destinations::ProductAnalytics).to receive(:event) - allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow) .to receive(:emitter) .and_return(SnowplowTracker::Emitter.new(host, buffer_size: buffer_size)) diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb index 18b40a20cf1..85483062958 100644 --- a/spec/support/test_reports/test_reports_helper.rb +++ b/spec/support/test_reports/test_reports_helper.rb @@ -95,9 +95,9 @@ module TestReportsHelper <<-EOF.strip_heredoc junit.framework.AssertionFailedError: expected:<1> but was:<3> at CalculatorTest.subtractExpression(Unknown Source) - at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/jdk.internal.database.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.database.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.database.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) EOF end end diff --git a/spec/support/time_travel.rb b/spec/support/time_travel.rb new file mode 100644 index 00000000000..9dfbfd20524 --- /dev/null +++ b/spec/support/time_travel.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'active_support/testing/time_helpers' + +RSpec.configure do |config| + config.include ActiveSupport::Testing::TimeHelpers + + config.around(:example, :freeze_time) do |example| + freeze_time { example.run } + end + + config.around(:example, :time_travel_to) do |example| + date_or_time = example.metadata[:time_travel_to] + + unless date_or_time.respond_to?(:to_time) && date_or_time.to_time.present? + raise 'The time_travel_to RSpec metadata must have a Date or Time value.' + end + + travel_to(date_or_time) { example.run } + end +end |