summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
commita09983ae35713f5a2bbb100981116d31ce99826e (patch)
tree2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/models
parent18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff)
downloadgitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ability_spec.rb2
-rw-r--r--spec/models/abuse_report_spec.rb2
-rw-r--r--spec/models/alert_management/alert_assignee_spec.rb2
-rw-r--r--spec/models/alert_management/alert_spec.rb119
-rw-r--r--spec/models/alert_management/alert_user_mention_spec.rb2
-rw-r--r--spec/models/alerting/project_alerting_setting_spec.rb2
-rw-r--r--spec/models/analytics/cycle_analytics/project_stage_spec.rb2
-rw-r--r--spec/models/appearance_spec.rb2
-rw-r--r--spec/models/application_record_spec.rb9
-rw-r--r--spec/models/application_setting/term_spec.rb2
-rw-r--r--spec/models/application_setting_spec.rb2
-rw-r--r--spec/models/approval_spec.rb16
-rw-r--r--spec/models/award_emoji_spec.rb2
-rw-r--r--spec/models/aws/role_spec.rb2
-rw-r--r--spec/models/badge_spec.rb2
-rw-r--r--spec/models/badges/group_badge_spec.rb2
-rw-r--r--spec/models/badges/project_badge_spec.rb2
-rw-r--r--spec/models/blob_spec.rb2
-rw-r--r--spec/models/blob_viewer/base_spec.rb2
-rw-r--r--spec/models/blob_viewer/changelog_spec.rb2
-rw-r--r--spec/models/blob_viewer/composer_json_spec.rb2
-rw-r--r--spec/models/blob_viewer/gemspec_spec.rb2
-rw-r--r--spec/models/blob_viewer/gitlab_ci_yml_spec.rb2
-rw-r--r--spec/models/blob_viewer/go_mod_spec.rb2
-rw-r--r--spec/models/blob_viewer/license_spec.rb2
-rw-r--r--spec/models/blob_viewer/metrics_dashboard_yml_spec.rb2
-rw-r--r--spec/models/blob_viewer/package_json_spec.rb2
-rw-r--r--spec/models/blob_viewer/podspec_json_spec.rb2
-rw-r--r--spec/models/blob_viewer/podspec_spec.rb2
-rw-r--r--spec/models/blob_viewer/readme_spec.rb2
-rw-r--r--spec/models/blob_viewer/route_map_spec.rb2
-rw-r--r--spec/models/blob_viewer/server_side_spec.rb2
-rw-r--r--spec/models/board_group_recent_visit_spec.rb2
-rw-r--r--spec/models/board_project_recent_visit_spec.rb2
-rw-r--r--spec/models/board_spec.rb2
-rw-r--r--spec/models/broadcast_message_spec.rb2
-rw-r--r--spec/models/chat_name_spec.rb2
-rw-r--r--spec/models/chat_team_spec.rb2
-rw-r--r--spec/models/ci/artifact_blob_spec.rb2
-rw-r--r--spec/models/ci/bridge_spec.rb6
-rw-r--r--spec/models/ci/build_dependencies_spec.rb2
-rw-r--r--spec/models/ci/build_metadata_spec.rb31
-rw-r--r--spec/models/ci/build_need_spec.rb20
-rw-r--r--spec/models/ci/build_report_result_spec.rb2
-rw-r--r--spec/models/ci/build_runner_session_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb97
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunks/database_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunks/fog_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunks/redis_spec.rb2
-rw-r--r--spec/models/ci/build_trace_section_name_spec.rb2
-rw-r--r--spec/models/ci/build_trace_section_spec.rb2
-rw-r--r--spec/models/ci/build_trace_spec.rb32
-rw-r--r--spec/models/ci/daily_build_group_report_result_spec.rb2
-rw-r--r--spec/models/ci/freeze_period_status_spec.rb2
-rw-r--r--spec/models/ci/group_spec.rb2
-rw-r--r--spec/models/ci/group_variable_spec.rb2
-rw-r--r--spec/models/ci/instance_variable_spec.rb17
-rw-r--r--spec/models/ci/job_artifact_spec.rb125
-rw-r--r--spec/models/ci/job_variable_spec.rb2
-rw-r--r--spec/models/ci/legacy_stage_spec.rb2
-rw-r--r--spec/models/ci/persistent_ref_spec.rb2
-rw-r--r--spec/models/ci/pipeline_config_spec.rb2
-rw-r--r--spec/models/ci/pipeline_message_spec.rb53
-rw-r--r--spec/models/ci/pipeline_schedule_spec.rb2
-rw-r--r--spec/models/ci/pipeline_schedule_variable_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb255
-rw-r--r--spec/models/ci/pipeline_variable_spec.rb2
-rw-r--r--spec/models/ci/processable_spec.rb2
-rw-r--r--spec/models/ci/ref_spec.rb31
-rw-r--r--spec/models/ci/resource_group_spec.rb2
-rw-r--r--spec/models/ci/resource_spec.rb2
-rw-r--r--spec/models/ci/runner_spec.rb42
-rw-r--r--spec/models/ci/sources/pipeline_spec.rb2
-rw-r--r--spec/models/ci/stage_spec.rb4
-rw-r--r--spec/models/ci/trigger_request_spec.rb2
-rw-r--r--spec/models/ci/trigger_spec.rb2
-rw-r--r--spec/models/ci/variable_spec.rb2
-rw-r--r--spec/models/clusters/applications/cert_manager_spec.rb2
-rw-r--r--spec/models/clusters/applications/cilium_spec.rb17
-rw-r--r--spec/models/clusters/applications/crossplane_spec.rb2
-rw-r--r--spec/models/clusters/applications/elastic_stack_spec.rb16
-rw-r--r--spec/models/clusters/applications/fluentd_spec.rb2
-rw-r--r--spec/models/clusters/applications/helm_spec.rb2
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb2
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb2
-rw-r--r--spec/models/clusters/applications/knative_spec.rb2
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb2
-rw-r--r--spec/models/clusters/applications/runner_spec.rb2
-rw-r--r--spec/models/clusters/cluster_spec.rb104
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb2
-rw-r--r--spec/models/clusters/group_spec.rb2
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb48
-rw-r--r--spec/models/clusters/project_spec.rb2
-rw-r--r--spec/models/clusters/providers/aws_spec.rb2
-rw-r--r--spec/models/clusters/providers/gcp_spec.rb2
-rw-r--r--spec/models/commit_collection_spec.rb14
-rw-r--r--spec/models/commit_range_spec.rb2
-rw-r--r--spec/models/commit_spec.rb13
-rw-r--r--spec/models/commit_status_spec.rb2
-rw-r--r--spec/models/commit_with_pipeline_spec.rb2
-rw-r--r--spec/models/compare_spec.rb2
-rw-r--r--spec/models/concerns/access_requestable_spec.rb2
-rw-r--r--spec/models/concerns/approvable_base_spec.rb34
-rw-r--r--spec/models/concerns/atomic_internal_id_spec.rb2
-rw-r--r--spec/models/concerns/avatarable_spec.rb2
-rw-r--r--spec/models/concerns/awardable_spec.rb2
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb2
-rw-r--r--spec/models/concerns/blob_language_from_git_attributes_spec.rb2
-rw-r--r--spec/models/concerns/blocks_json_serialization_spec.rb2
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb179
-rw-r--r--spec/models/concerns/bulk_insertable_associations_spec.rb2
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb2
-rw-r--r--spec/models/concerns/cacheable_attributes_spec.rb2
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb2
-rw-r--r--spec/models/concerns/checksummable_spec.rb2
-rw-r--r--spec/models/concerns/chronic_duration_attribute_spec.rb8
-rw-r--r--spec/models/concerns/ci/has_ref_spec.rb2
-rw-r--r--spec/models/concerns/ci/has_status_spec.rb (renamed from spec/models/concerns/has_status_spec.rb)2
-rw-r--r--spec/models/concerns/ci/has_variable_spec.rb2
-rw-r--r--spec/models/concerns/ci/maskable_spec.rb2
-rw-r--r--spec/models/concerns/delete_with_limit_spec.rb2
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb237
-rw-r--r--spec/models/concerns/deprecated_assignee_spec.rb2
-rw-r--r--spec/models/concerns/discussion_on_diff_spec.rb2
-rw-r--r--spec/models/concerns/each_batch_spec.rb2
-rw-r--r--spec/models/concerns/editable_spec.rb2
-rw-r--r--spec/models/concerns/expirable_spec.rb2
-rw-r--r--spec/models/concerns/faster_cache_keys_spec.rb2
-rw-r--r--spec/models/concerns/featurable_spec.rb2
-rw-r--r--spec/models/concerns/feature_gate_spec.rb2
-rw-r--r--spec/models/concerns/from_union_spec.rb2
-rw-r--r--spec/models/concerns/group_descendant_spec.rb2
-rw-r--r--spec/models/concerns/has_environment_scope_spec.rb2
-rw-r--r--spec/models/concerns/has_user_type_spec.rb2
-rw-r--r--spec/models/concerns/ignorable_columns_spec.rb2
-rw-r--r--spec/models/concerns/issuable_spec.rb23
-rw-r--r--spec/models/concerns/limitable_spec.rb2
-rw-r--r--spec/models/concerns/loaded_in_group_list_spec.rb2
-rw-r--r--spec/models/concerns/manual_inverse_association_spec.rb2
-rw-r--r--spec/models/concerns/mentionable_spec.rb14
-rw-r--r--spec/models/concerns/milestoneable_spec.rb2
-rw-r--r--spec/models/concerns/milestoneish_spec.rb2
-rw-r--r--spec/models/concerns/noteable_spec.rb42
-rw-r--r--spec/models/concerns/optionally_search_spec.rb2
-rw-r--r--spec/models/concerns/participable_spec.rb2
-rw-r--r--spec/models/concerns/partitioned_table_spec.rb35
-rw-r--r--spec/models/concerns/presentable_spec.rb2
-rw-r--r--spec/models/concerns/project_api_compatibility_spec.rb2
-rw-r--r--spec/models/concerns/project_features_compatibility_spec.rb2
-rw-r--r--spec/models/concerns/prometheus_adapter_spec.rb2
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb2
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb36
-rw-r--r--spec/models/concerns/redactable_spec.rb2
-rw-r--r--spec/models/concerns/redis_cacheable_spec.rb2
-rw-r--r--spec/models/concerns/resolvable_discussion_spec.rb2
-rw-r--r--spec/models/concerns/resolvable_note_spec.rb2
-rw-r--r--spec/models/concerns/routable_spec.rb4
-rw-r--r--spec/models/concerns/safe_url_spec.rb2
-rw-r--r--spec/models/concerns/schedulable_spec.rb2
-rw-r--r--spec/models/concerns/sha256_attribute_spec.rb2
-rw-r--r--spec/models/concerns/sha_attribute_spec.rb2
-rw-r--r--spec/models/concerns/sortable_spec.rb2
-rw-r--r--spec/models/concerns/spammable_spec.rb2
-rw-r--r--spec/models/concerns/stepable_spec.rb2
-rw-r--r--spec/models/concerns/strip_attribute_spec.rb2
-rw-r--r--spec/models/concerns/subscribable_spec.rb2
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb10
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/base_spec.rb2
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb2
-rw-r--r--spec/models/concerns/uniquify_spec.rb2
-rw-r--r--spec/models/concerns/usage_statistics_spec.rb2
-rw-r--r--spec/models/concerns/where_composite_spec.rb2
-rw-r--r--spec/models/concerns/x509_serial_number_attribute_spec.rb2
-rw-r--r--spec/models/container_registry/event_spec.rb2
-rw-r--r--spec/models/container_repository_spec.rb2
-rw-r--r--spec/models/custom_emoji_spec.rb31
-rw-r--r--spec/models/cycle_analytics/code_spec.rb2
-rw-r--r--spec/models/cycle_analytics/issue_spec.rb2
-rw-r--r--spec/models/cycle_analytics/plan_spec.rb2
-rw-r--r--spec/models/cycle_analytics/production_spec.rb2
-rw-r--r--spec/models/cycle_analytics/project_level_spec.rb2
-rw-r--r--spec/models/cycle_analytics/review_spec.rb2
-rw-r--r--spec/models/cycle_analytics/staging_spec.rb2
-rw-r--r--spec/models/cycle_analytics/test_spec.rb2
-rw-r--r--spec/models/deploy_key_spec.rb2
-rw-r--r--spec/models/deploy_keys_project_spec.rb17
-rw-r--r--spec/models/deploy_token_spec.rb2
-rw-r--r--spec/models/deployment_cluster_spec.rb2
-rw-r--r--spec/models/deployment_merge_request_spec.rb2
-rw-r--r--spec/models/deployment_metrics_spec.rb2
-rw-r--r--spec/models/deployment_spec.rb2
-rw-r--r--spec/models/description_version_spec.rb2
-rw-r--r--spec/models/design_management/action_spec.rb2
-rw-r--r--spec/models/design_management/design_action_spec.rb2
-rw-r--r--spec/models/design_management/design_at_version_spec.rb2
-rw-r--r--spec/models/design_management/design_collection_spec.rb2
-rw-r--r--spec/models/design_management/design_spec.rb2
-rw-r--r--spec/models/design_management/repository_spec.rb2
-rw-r--r--spec/models/design_management/version_spec.rb2
-rw-r--r--spec/models/design_user_mention_spec.rb2
-rw-r--r--spec/models/dev_ops_score/metric_spec.rb2
-rw-r--r--spec/models/diff_discussion_spec.rb2
-rw-r--r--spec/models/diff_note_position_spec.rb2
-rw-r--r--spec/models/diff_note_spec.rb2
-rw-r--r--spec/models/diff_viewer/base_spec.rb2
-rw-r--r--spec/models/diff_viewer/server_side_spec.rb2
-rw-r--r--spec/models/discussion_spec.rb2
-rw-r--r--spec/models/draft_note_spec.rb2
-rw-r--r--spec/models/email_spec.rb2
-rw-r--r--spec/models/environment_spec.rb17
-rw-r--r--spec/models/environment_status_spec.rb2
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb2
-rw-r--r--spec/models/event_collection_spec.rb51
-rw-r--r--spec/models/event_spec.rb20
-rw-r--r--spec/models/external_issue_spec.rb2
-rw-r--r--spec/models/external_pull_request_spec.rb2
-rw-r--r--spec/models/fork_network_member_spec.rb2
-rw-r--r--spec/models/fork_network_spec.rb2
-rw-r--r--spec/models/generic_commit_status_spec.rb2
-rw-r--r--spec/models/gpg_key_spec.rb2
-rw-r--r--spec/models/gpg_key_subkey_spec.rb2
-rw-r--r--spec/models/grafana_integration_spec.rb2
-rw-r--r--spec/models/group_custom_attribute_spec.rb2
-rw-r--r--spec/models/group_deploy_key_spec.rb2
-rw-r--r--spec/models/group_group_link_spec.rb2
-rw-r--r--spec/models/group_import_state_spec.rb2
-rw-r--r--spec/models/group_label_spec.rb2
-rw-r--r--spec/models/group_spec.rb229
-rw-r--r--spec/models/guest_spec.rb2
-rw-r--r--spec/models/hooks/active_hook_filter_spec.rb2
-rw-r--r--spec/models/hooks/project_hook_spec.rb2
-rw-r--r--spec/models/hooks/service_hook_spec.rb2
-rw-r--r--spec/models/hooks/system_hook_spec.rb2
-rw-r--r--spec/models/hooks/web_hook_log_spec.rb2
-rw-r--r--spec/models/hooks/web_hook_spec.rb2
-rw-r--r--spec/models/identity_spec.rb2
-rw-r--r--spec/models/import_export_upload_spec.rb2
-rw-r--r--spec/models/import_failure_spec.rb2
-rw-r--r--spec/models/incident_management/project_incident_management_setting_spec.rb40
-rw-r--r--spec/models/instance_configuration_spec.rb2
-rw-r--r--spec/models/integration_spec.rb11
-rw-r--r--spec/models/internal_id_spec.rb2
-rw-r--r--spec/models/issue/metrics_spec.rb2
-rw-r--r--spec/models/issue_assignee_spec.rb35
-rw-r--r--spec/models/issue_collection_spec.rb2
-rw-r--r--spec/models/issue_spec.rb51
-rw-r--r--spec/models/iteration_spec.rb29
-rw-r--r--spec/models/jira_import_state_spec.rb2
-rw-r--r--spec/models/key_spec.rb2
-rw-r--r--spec/models/label_link_spec.rb2
-rw-r--r--spec/models/label_note_spec.rb2
-rw-r--r--spec/models/label_priority_spec.rb2
-rw-r--r--spec/models/label_spec.rb2
-rw-r--r--spec/models/legacy_diff_discussion_spec.rb2
-rw-r--r--spec/models/lfs_download_object_spec.rb2
-rw-r--r--spec/models/lfs_file_lock_spec.rb2
-rw-r--r--spec/models/lfs_object_spec.rb2
-rw-r--r--spec/models/lfs_objects_project_spec.rb2
-rw-r--r--spec/models/license_template_spec.rb2
-rw-r--r--spec/models/list_spec.rb2
-rw-r--r--spec/models/list_user_preference_spec.rb2
-rw-r--r--spec/models/member_spec.rb24
-rw-r--r--spec/models/members/group_member_spec.rb58
-rw-r--r--spec/models/members/project_member_spec.rb2
-rw-r--r--spec/models/merge_request/metrics_spec.rb2
-rw-r--r--spec/models/merge_request_assignee_spec.rb24
-rw-r--r--spec/models/merge_request_context_commit_diff_file_spec.rb2
-rw-r--r--spec/models/merge_request_context_commit_spec.rb2
-rw-r--r--spec/models/merge_request_diff_commit_spec.rb2
-rw-r--r--spec/models/merge_request_diff_file_spec.rb2
-rw-r--r--spec/models/merge_request_diff_spec.rb115
-rw-r--r--spec/models/merge_request_spec.rb122
-rw-r--r--spec/models/metrics/dashboard/annotation_spec.rb2
-rw-r--r--spec/models/metrics/users_starred_dashboard_spec.rb2
-rw-r--r--spec/models/milestone_note_spec.rb6
-rw-r--r--spec/models/milestone_release_spec.rb2
-rw-r--r--spec/models/milestone_spec.rb2
-rw-r--r--spec/models/namespace/root_storage_size_spec.rb67
-rw-r--r--spec/models/namespace/root_storage_statistics_spec.rb47
-rw-r--r--spec/models/namespace/traversal_hierarchy_spec.rb63
-rw-r--r--spec/models/namespace_setting_spec.rb7
-rw-r--r--spec/models/namespace_spec.rb51
-rw-r--r--spec/models/network/graph_spec.rb2
-rw-r--r--spec/models/note_diff_file_spec.rb2
-rw-r--r--spec/models/note_spec.rb2
-rw-r--r--spec/models/notification_recipient_spec.rb2
-rw-r--r--spec/models/oauth_access_grant_spec.rb2
-rw-r--r--spec/models/oauth_access_token_spec.rb2
-rw-r--r--spec/models/packages/composer/metadatum_spec.rb14
-rw-r--r--spec/models/packages/conan/file_metadatum_spec.rb106
-rw-r--r--spec/models/packages/conan/metadatum_spec.rb90
-rw-r--r--spec/models/packages/dependency_link_spec.rb56
-rw-r--r--spec/models/packages/dependency_spec.rb113
-rw-r--r--spec/models/packages/go/module_spec.rb59
-rw-r--r--spec/models/packages/go/module_version_spec.rb114
-rw-r--r--spec/models/packages/maven/metadatum_spec.rb40
-rw-r--r--spec/models/packages/nuget/dependency_link_metadatum_spec.rb32
-rw-r--r--spec/models/packages/nuget/metadatum_spec.rb44
-rw-r--r--spec/models/packages/package_file_spec.rb69
-rw-r--r--spec/models/packages/package_spec.rb485
-rw-r--r--spec/models/packages/pypi/metadatum_spec.rb22
-rw-r--r--spec/models/packages/sem_ver_spec.rb42
-rw-r--r--spec/models/packages/tag_spec.rb62
-rw-r--r--spec/models/pages/lookup_path_spec.rb2
-rw-r--r--spec/models/pages/virtual_domain_spec.rb2
-rw-r--r--spec/models/pages_domain_acme_order_spec.rb2
-rw-r--r--spec/models/pages_domain_spec.rb2
-rw-r--r--spec/models/performance_monitoring/prometheus_dashboard_spec.rb16
-rw-r--r--spec/models/performance_monitoring/prometheus_metric_spec.rb2
-rw-r--r--spec/models/performance_monitoring/prometheus_panel_group_spec.rb2
-rw-r--r--spec/models/performance_monitoring/prometheus_panel_spec.rb2
-rw-r--r--spec/models/personal_access_token_spec.rb19
-rw-r--r--spec/models/personal_snippet_spec.rb2
-rw-r--r--spec/models/plan_limits_spec.rb210
-rw-r--r--spec/models/plan_spec.rb14
-rw-r--r--spec/models/pool_repository_spec.rb2
-rw-r--r--spec/models/postgresql/replication_slot_spec.rb2
-rw-r--r--spec/models/product_analytics_event_spec.rb24
-rw-r--r--spec/models/programming_language_spec.rb2
-rw-r--r--spec/models/project_authorization_spec.rb2
-rw-r--r--spec/models/project_auto_devops_spec.rb2
-rw-r--r--spec/models/project_ci_cd_setting_spec.rb2
-rw-r--r--spec/models/project_custom_attribute_spec.rb2
-rw-r--r--spec/models/project_daily_statistic_spec.rb2
-rw-r--r--spec/models/project_export_job_spec.rb2
-rw-r--r--spec/models/project_feature_spec.rb2
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_import_data_spec.rb2
-rw-r--r--spec/models/project_import_state_spec.rb2
-rw-r--r--spec/models/project_label_spec.rb2
-rw-r--r--spec/models/project_metrics_setting_spec.rb2
-rw-r--r--spec/models/project_repository_spec.rb2
-rw-r--r--spec/models/project_services/alerts_service_spec.rb2
-rw-r--r--spec/models/project_services/asana_service_spec.rb2
-rw-r--r--spec/models/project_services/assembla_service_spec.rb2
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb2
-rw-r--r--spec/models/project_services/bugzilla_service_spec.rb47
-rw-r--r--spec/models/project_services/buildkite_service_spec.rb2
-rw-r--r--spec/models/project_services/campfire_service_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/alert_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/base_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/deployment_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/issue_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/merge_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/note_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/pipeline_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/push_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_message/wiki_page_message_spec.rb2
-rw-r--r--spec/models/project_services/chat_notification_service_spec.rb2
-rw-r--r--spec/models/project_services/confluence_service_spec.rb90
-rw-r--r--spec/models/project_services/custom_issue_tracker_service_spec.rb63
-rw-r--r--spec/models/project_services/data_fields_spec.rb2
-rw-r--r--spec/models/project_services/discord_service_spec.rb2
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb2
-rw-r--r--spec/models/project_services/emails_on_push_service_spec.rb2
-rw-r--r--spec/models/project_services/external_wiki_service_spec.rb2
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb2
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb47
-rw-r--r--spec/models/project_services/hangouts_chat_service_spec.rb2
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb2
-rw-r--r--spec/models/project_services/irker_service_spec.rb2
-rw-r--r--spec/models/project_services/issue_tracker_data_spec.rb2
-rw-r--r--spec/models/project_services/issue_tracker_service_spec.rb2
-rw-r--r--spec/models/project_services/jira_service_spec.rb94
-rw-r--r--spec/models/project_services/jira_tracker_data_spec.rb4
-rw-r--r--spec/models/project_services/mattermost_service_spec.rb2
-rw-r--r--spec/models/project_services/mattermost_slash_commands_service_spec.rb2
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb2
-rw-r--r--spec/models/project_services/open_project_service_spec.rb2
-rw-r--r--spec/models/project_services/open_project_tracker_data_spec.rb2
-rw-r--r--spec/models/project_services/packagist_service_spec.rb2
-rw-r--r--spec/models/project_services/pipelines_email_service_spec.rb2
-rw-r--r--spec/models/project_services/pivotaltracker_service_spec.rb2
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb18
-rw-r--r--spec/models/project_services/pushover_service_spec.rb2
-rw-r--r--spec/models/project_services/redmine_service_spec.rb47
-rw-r--r--spec/models/project_services/slack_service_spec.rb2
-rw-r--r--spec/models/project_services/slack_slash_commands_service_spec.rb2
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb2
-rw-r--r--spec/models/project_services/unify_circuit_service_spec.rb2
-rw-r--r--spec/models/project_services/webex_teams_service_spec.rb2
-rw-r--r--spec/models/project_services/youtrack_service_spec.rb47
-rw-r--r--spec/models/project_setting_spec.rb2
-rw-r--r--spec/models/project_snippet_spec.rb2
-rw-r--r--spec/models/project_spec.rb242
-rw-r--r--spec/models/project_statistics_spec.rb87
-rw-r--r--spec/models/project_team_spec.rb2
-rw-r--r--spec/models/project_wiki_spec.rb2
-rw-r--r--spec/models/prometheus_alert_event_spec.rb2
-rw-r--r--spec/models/prometheus_alert_spec.rb6
-rw-r--r--spec/models/prometheus_metric_spec.rb3
-rw-r--r--spec/models/protectable_dropdown_spec.rb2
-rw-r--r--spec/models/protected_branch/merge_access_level_spec.rb2
-rw-r--r--spec/models/protected_branch/push_access_level_spec.rb2
-rw-r--r--spec/models/protected_branch_spec.rb2
-rw-r--r--spec/models/protected_tag_spec.rb2
-rw-r--r--spec/models/push_event_payload_spec.rb2
-rw-r--r--spec/models/push_event_spec.rb2
-rw-r--r--spec/models/readme_blob_spec.rb2
-rw-r--r--spec/models/redirect_route_spec.rb2
-rw-r--r--spec/models/releases/evidence_spec.rb2
-rw-r--r--spec/models/releases/link_spec.rb2
-rw-r--r--spec/models/releases/source_spec.rb2
-rw-r--r--spec/models/remote_mirror_spec.rb2
-rw-r--r--spec/models/repository_language_spec.rb2
-rw-r--r--spec/models/repository_spec.rb55
-rw-r--r--spec/models/resource_milestone_event_spec.rb2
-rw-r--r--spec/models/resource_state_event_spec.rb2
-rw-r--r--spec/models/review_spec.rb2
-rw-r--r--spec/models/route_spec.rb2
-rw-r--r--spec/models/sent_notification_spec.rb2
-rw-r--r--spec/models/sentry_issue_spec.rb2
-rw-r--r--spec/models/serverless/domain_cluster_spec.rb2
-rw-r--r--spec/models/serverless/domain_spec.rb2
-rw-r--r--spec/models/serverless/function_spec.rb2
-rw-r--r--spec/models/service_desk_setting_spec.rb37
-rw-r--r--spec/models/service_spec.rb44
-rw-r--r--spec/models/shard_spec.rb2
-rw-r--r--spec/models/snippet_blob_spec.rb2
-rw-r--r--spec/models/snippet_input_action_collection_spec.rb2
-rw-r--r--spec/models/snippet_input_action_spec.rb16
-rw-r--r--spec/models/snippet_repository_spec.rb2
-rw-r--r--spec/models/snippet_spec.rb39
-rw-r--r--spec/models/snippet_statistics_spec.rb149
-rw-r--r--spec/models/spam_log_spec.rb2
-rw-r--r--spec/models/ssh_host_key_spec.rb2
-rw-r--r--spec/models/state_note_spec.rb54
-rw-r--r--spec/models/subscription_spec.rb2
-rw-r--r--spec/models/suggestion_spec.rb106
-rw-r--r--spec/models/system_note_metadata_spec.rb2
-rw-r--r--spec/models/term_agreement_spec.rb2
-rw-r--r--spec/models/terraform/state_spec.rb2
-rw-r--r--spec/models/todo_spec.rb2
-rw-r--r--spec/models/tree_spec.rb2
-rw-r--r--spec/models/trending_project_spec.rb2
-rw-r--r--spec/models/upload_spec.rb2
-rw-r--r--spec/models/uploads/fog_spec.rb2
-rw-r--r--spec/models/uploads/local_spec.rb2
-rw-r--r--spec/models/user_agent_detail_spec.rb2
-rw-r--r--spec/models/user_callout_spec.rb2
-rw-r--r--spec/models/user_canonical_email_spec.rb2
-rw-r--r--spec/models/user_custom_attribute_spec.rb2
-rw-r--r--spec/models/user_detail_spec.rb33
-rw-r--r--spec/models/user_highest_role_spec.rb2
-rw-r--r--spec/models/user_interacted_project_spec.rb2
-rw-r--r--spec/models/user_mentions/commit_user_mention_spec.rb2
-rw-r--r--spec/models/user_mentions/issue_user_mention_spec.rb2
-rw-r--r--spec/models/user_mentions/merge_request_user_mention_spec.rb2
-rw-r--r--spec/models/user_mentions/snippet_user_mention_spec.rb2
-rw-r--r--spec/models/user_preference_spec.rb2
-rw-r--r--spec/models/user_spec.rb125
-rw-r--r--spec/models/user_status_spec.rb2
-rw-r--r--spec/models/users_statistics_spec.rb2
-rw-r--r--spec/models/web_ide_terminal_spec.rb2
-rw-r--r--spec/models/wiki_page/meta_spec.rb2
-rw-r--r--spec/models/wiki_page/slug_spec.rb2
-rw-r--r--spec/models/wiki_page_spec.rb20
-rw-r--r--spec/models/zoom_meeting_spec.rb2
459 files changed, 5259 insertions, 1373 deletions
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 9ef77da6f43..9206f14fd37 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ability do
+RSpec.describe Ability do
context 'using a nil subject' do
it 'has no permissions' do
expect(described_class.policy_for(nil, nil)).to be_banned
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index 2c4fa398636..a97574fa524 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AbuseReport do
+RSpec.describe AbuseReport do
let_it_be(:report, reload: true) { create(:abuse_report) }
let_it_be(:user, reload: true) { create(:admin) }
subject { report }
diff --git a/spec/models/alert_management/alert_assignee_spec.rb b/spec/models/alert_management/alert_assignee_spec.rb
index c51a5d543ab..c50a3ec0d01 100644
--- a/spec/models/alert_management/alert_assignee_spec.rb
+++ b/spec/models/alert_management/alert_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AlertManagement::AlertAssignee do
+RSpec.describe AlertManagement::AlertAssignee do
describe 'associations' do
it { is_expected.to belong_to(:alert) }
it { is_expected.to belong_to(:assignee) }
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index 27b8bb48073..becc5475c15 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -2,10 +2,12 @@
require 'spec_helper'
-describe AlertManagement::Alert do
+RSpec.describe AlertManagement::Alert do
describe 'associations' do
it { is_expected.to belong_to(:project) }
- it { is_expected.to belong_to(:issue) }
+ it { is_expected.to belong_to(:issue).optional }
+ it { is_expected.to belong_to(:prometheus_alert).optional }
+ it { is_expected.to belong_to(:environment).optional }
it { is_expected.to have_many(:assignees).through(:alert_assignees) }
it { is_expected.to have_many(:notes) }
it { is_expected.to have_many(:ordered_notes) }
@@ -81,21 +83,50 @@ describe AlertManagement::Alert do
end
describe 'fingerprint' do
+ let_it_be(:project) { create(:project) }
let_it_be(:fingerprint) { 'fingerprint' }
- let_it_be(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) }
let(:new_alert) { build(:alert_management_alert, fingerprint: fingerprint, project: project) }
subject { new_alert }
context 'adding an alert with the same fingerprint' do
- context 'same project' do
- let(:project) { existing_alert.project }
-
- it { is_expected.not_to be_valid }
+ context 'same project, various states' do
+ using RSpec::Parameterized::TableSyntax
+
+ # We are only validating uniqueness for non-resolved alerts
+ where(:existing_status, :new_status, :valid) do
+ :resolved | :triggered | true
+ :resolved | :acknowledged | true
+ :resolved | :ignored | true
+ :resolved | :resolved | true
+ :triggered | :triggered | false
+ :triggered | :acknowledged | false
+ :triggered | :ignored | false
+ :triggered | :resolved | true
+ :acknowledged | :triggered | false
+ :acknowledged | :acknowledged | false
+ :acknowledged | :ignored | false
+ :acknowledged | :resolved | true
+ :ignored | :triggered | false
+ :ignored | :acknowledged | false
+ :ignored | :ignored | false
+ :ignored | :resolved | true
+ end
+
+ with_them do
+ let!(:existing_alert) { create(:alert_management_alert, existing_status, fingerprint: fingerprint, project: project) }
+ let(:new_alert) { build(:alert_management_alert, new_status, fingerprint: fingerprint, project: project) }
+
+ if params[:valid]
+ it { is_expected.to be_valid }
+ else
+ it { is_expected.to be_invalid }
+ end
+ end
end
context 'different project' do
- let(:project) { create(:project) }
+ let!(:existing_alert) { create(:alert_management_alert, fingerprint: fingerprint) }
it { is_expected.to be_valid }
end
@@ -163,6 +194,15 @@ describe AlertManagement::Alert do
it { is_expected.to contain_exactly(alert_with_fingerprint) }
end
+ describe '.for_environment' do
+ let(:environment) { create(:environment, project: project) }
+ let!(:env_alert) { create(:alert_management_alert, project: project, environment: environment) }
+
+ subject { described_class.for_environment(environment) }
+
+ it { is_expected.to match_array(env_alert) }
+ end
+
describe '.counts_by_status' do
subject { described_class.counts_by_status }
@@ -174,6 +214,51 @@ describe AlertManagement::Alert do
)
end
end
+
+ describe '.counts_by_project_id' do
+ subject { described_class.counts_by_project_id }
+
+ let!(:alert_other_project) { create(:alert_management_alert) }
+
+ it do
+ is_expected.to eq(
+ project.id => 3,
+ alert_other_project.project.id => 1
+ )
+ end
+ end
+
+ describe '.open' do
+ subject { described_class.open }
+
+ let!(:acknowledged_alert) { create(:alert_management_alert, :acknowledged, project: project)}
+
+ it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert) }
+ end
+
+ describe '.not_resolved' do
+ subject { described_class.not_resolved }
+
+ let!(:acknowledged_alert) { create(:alert_management_alert, :acknowledged, project: project) }
+
+ it { is_expected.to contain_exactly(acknowledged_alert, triggered_alert, ignored_alert) }
+ end
+ end
+
+ describe '.last_prometheus_alert_by_project_id' do
+ subject { described_class.last_prometheus_alert_by_project_id }
+
+ let(:project_1) { create(:project) }
+ let!(:alert_1) { create(:alert_management_alert, project: project_1) }
+ let!(:alert_2) { create(:alert_management_alert, project: project_1) }
+
+ let(:project_2) { create(:project) }
+ let!(:alert_3) { create(:alert_management_alert, project: project_2) }
+ let!(:alert_4) { create(:alert_management_alert, project: project_2) }
+
+ it 'returns the latest alert for each project' do
+ expect(subject).to contain_exactly(alert_2, alert_4)
+ end
end
describe '.search' do
@@ -337,4 +422,22 @@ describe AlertManagement::Alert do
expect { subject }.to change { alert.events }.by(1)
end
end
+
+ describe '#present' do
+ context 'when alert is generic' do
+ let(:alert) { build(:alert_management_alert) }
+
+ it 'uses generic alert presenter' do
+ expect(alert.present).to be_kind_of(AlertManagement::AlertPresenter)
+ end
+ end
+
+ context 'when alert is Prometheus specific' do
+ let(:alert) { build(:alert_management_alert, :prometheus) }
+
+ it 'uses Prometheus Alert presenter' do
+ expect(alert.present).to be_kind_of(AlertManagement::PrometheusAlertPresenter)
+ end
+ end
+ end
end
diff --git a/spec/models/alert_management/alert_user_mention_spec.rb b/spec/models/alert_management/alert_user_mention_spec.rb
index cce090a2231..27c3d290dde 100644
--- a/spec/models/alert_management/alert_user_mention_spec.rb
+++ b/spec/models/alert_management/alert_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AlertManagement::AlertUserMention do
+RSpec.describe AlertManagement::AlertUserMention do
describe 'associations' do
it { is_expected.to belong_to(:alert_management_alert) }
it { is_expected.to belong_to(:note) }
diff --git a/spec/models/alerting/project_alerting_setting_spec.rb b/spec/models/alerting/project_alerting_setting_spec.rb
index 40fbe3a6e78..90c5f8313b0 100644
--- a/spec/models/alerting/project_alerting_setting_spec.rb
+++ b/spec/models/alerting/project_alerting_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Alerting::ProjectAlertingSetting do
+RSpec.describe Alerting::ProjectAlertingSetting do
let_it_be(:project) { create(:project) }
subject { create(:project_alerting_setting, project: project) }
diff --git a/spec/models/analytics/cycle_analytics/project_stage_spec.rb b/spec/models/analytics/cycle_analytics/project_stage_spec.rb
index 9850bfde30e..2e024011553 100644
--- a/spec/models/analytics/cycle_analytics/project_stage_spec.rb
+++ b/spec/models/analytics/cycle_analytics/project_stage_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Analytics::CycleAnalytics::ProjectStage do
+RSpec.describe Analytics::CycleAnalytics::ProjectStage do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index 2c32028c3e5..37eddf9a22a 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Appearance do
+RSpec.describe Appearance do
subject { build(:appearance) }
it { include(CacheableAttributes) }
diff --git a/spec/models/application_record_spec.rb b/spec/models/application_record_spec.rb
index 74573d0941c..cc314d9077d 100644
--- a/spec/models/application_record_spec.rb
+++ b/spec/models/application_record_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ApplicationRecord do
+RSpec.describe ApplicationRecord do
describe '#id_in' do
let(:records) { create_list(:user, 3) }
@@ -58,4 +58,11 @@ describe ApplicationRecord do
expect(MergeRequest.underscore).to eq('merge_request')
end
end
+
+ describe '.at_most' do
+ it 'limits the number of records returned' do
+ create_list(:user, 3)
+ expect(User.at_most(2).count).to eq(2)
+ end
+ end
end
diff --git a/spec/models/application_setting/term_spec.rb b/spec/models/application_setting/term_spec.rb
index dd263335b81..82347453437 100644
--- a/spec/models/application_setting/term_spec.rb
+++ b/spec/models/application_setting/term_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ApplicationSetting::Term do
+RSpec.describe ApplicationSetting::Term do
describe 'validations' do
it { is_expected.to validate_presence_of(:terms) }
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 96bf19439a1..f618e13ca26 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ApplicationSetting do
+RSpec.describe ApplicationSetting do
using RSpec::Parameterized::TableSyntax
subject(:setting) { described_class.create_from_defaults }
diff --git a/spec/models/approval_spec.rb b/spec/models/approval_spec.rb
new file mode 100644
index 00000000000..e2c0d5faa07
--- /dev/null
+++ b/spec/models/approval_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Approval do
+ context 'presence validation' do
+ it { is_expected.to validate_presence_of(:merge_request_id) }
+ it { is_expected.to validate_presence_of(:user_id) }
+ end
+
+ context 'uniqueness validation' do
+ let!(:existing_record) { create(:approval) }
+
+ it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:merge_request_id]) }
+ end
+end
diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb
index 8b370e80c9b..f268408c095 100644
--- a/spec/models/award_emoji_spec.rb
+++ b/spec/models/award_emoji_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AwardEmoji do
+RSpec.describe AwardEmoji do
describe 'Associations' do
it { is_expected.to belong_to(:awardable) }
it { is_expected.to belong_to(:user) }
diff --git a/spec/models/aws/role_spec.rb b/spec/models/aws/role_spec.rb
index d4165567146..612868f6eb0 100644
--- a/spec/models/aws/role_spec.rb
+++ b/spec/models/aws/role_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Aws::Role do
+RSpec.describe Aws::Role do
it { is_expected.to belong_to(:user) }
it { is_expected.to validate_length_of(:role_external_id).is_at_least(1).is_at_most(64) }
diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb
index fba8f40e99b..f3c95332ca0 100644
--- a/spec/models/badge_spec.rb
+++ b/spec/models/badge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Badge do
+RSpec.describe Badge do
let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{default_branch}/%{commit_sha}' }
describe 'validations' do
diff --git a/spec/models/badges/group_badge_spec.rb b/spec/models/badges/group_badge_spec.rb
index c297bc957ea..8913a2e6f17 100644
--- a/spec/models/badges/group_badge_spec.rb
+++ b/spec/models/badges/group_badge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupBadge do
+RSpec.describe GroupBadge do
describe 'associations' do
it { is_expected.to belong_to(:group) }
end
diff --git a/spec/models/badges/project_badge_spec.rb b/spec/models/badges/project_badge_spec.rb
index c0e85d3de87..9b9836129a6 100644
--- a/spec/models/badges/project_badge_spec.rb
+++ b/spec/models/badges/project_badge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectBadge do
+RSpec.describe ProjectBadge do
let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{default_branch}/%{commit_sha}' }
describe 'associations' do
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index c2d6406c3fb..bd4832bd978 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Blob do
+RSpec.describe Blob do
include FakeBlobHelpers
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/blob_viewer/base_spec.rb b/spec/models/blob_viewer/base_spec.rb
index 39c7a34f052..682b6dc3b1d 100644
--- a/spec/models/blob_viewer/base_spec.rb
+++ b/spec/models/blob_viewer/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::Base do
+RSpec.describe BlobViewer::Base do
include FakeBlobHelpers
let(:project) { build(:project) }
diff --git a/spec/models/blob_viewer/changelog_spec.rb b/spec/models/blob_viewer/changelog_spec.rb
index b71531ff3c2..5346483cfc8 100644
--- a/spec/models/blob_viewer/changelog_spec.rb
+++ b/spec/models/blob_viewer/changelog_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::Changelog do
+RSpec.describe BlobViewer::Changelog do
include FakeBlobHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/models/blob_viewer/composer_json_spec.rb b/spec/models/blob_viewer/composer_json_spec.rb
index a6bb64ba121..8d66e9e951f 100644
--- a/spec/models/blob_viewer/composer_json_spec.rb
+++ b/spec/models/blob_viewer/composer_json_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::ComposerJson do
+RSpec.describe BlobViewer::ComposerJson do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/gemspec_spec.rb b/spec/models/blob_viewer/gemspec_spec.rb
index 291d14e2d72..b6f3e059c7e 100644
--- a/spec/models/blob_viewer/gemspec_spec.rb
+++ b/spec/models/blob_viewer/gemspec_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::Gemspec do
+RSpec.describe BlobViewer::Gemspec do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
index e645733e02d..cd885d312dc 100644
--- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
+++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::GitlabCiYml do
+RSpec.describe BlobViewer::GitlabCiYml do
include FakeBlobHelpers
include RepoHelpers
diff --git a/spec/models/blob_viewer/go_mod_spec.rb b/spec/models/blob_viewer/go_mod_spec.rb
index ba6038533ea..21e84d39a54 100644
--- a/spec/models/blob_viewer/go_mod_spec.rb
+++ b/spec/models/blob_viewer/go_mod_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::GoMod do
+RSpec.describe BlobViewer::GoMod do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/license_spec.rb b/spec/models/blob_viewer/license_spec.rb
index b0426401932..bc970136503 100644
--- a/spec/models/blob_viewer/license_spec.rb
+++ b/spec/models/blob_viewer/license_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::License do
+RSpec.describe BlobViewer::License do
include FakeBlobHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
index f5b8586975d..057f0f32158 100644
--- a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
+++ b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::MetricsDashboardYml do
+RSpec.describe BlobViewer::MetricsDashboardYml do
include FakeBlobHelpers
include RepoHelpers
diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb
index 7f7b1dcfcb3..d2e8ab6575f 100644
--- a/spec/models/blob_viewer/package_json_spec.rb
+++ b/spec/models/blob_viewer/package_json_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::PackageJson do
+RSpec.describe BlobViewer::PackageJson do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/podspec_json_spec.rb b/spec/models/blob_viewer/podspec_json_spec.rb
index dd5ed03b77d..61d2602c413 100644
--- a/spec/models/blob_viewer/podspec_json_spec.rb
+++ b/spec/models/blob_viewer/podspec_json_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::PodspecJson do
+RSpec.describe BlobViewer::PodspecJson do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/podspec_spec.rb b/spec/models/blob_viewer/podspec_spec.rb
index 2d9b184c5cb..0a0fbcaebd4 100644
--- a/spec/models/blob_viewer/podspec_spec.rb
+++ b/spec/models/blob_viewer/podspec_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::Podspec do
+RSpec.describe BlobViewer::Podspec do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/readme_spec.rb b/spec/models/blob_viewer/readme_spec.rb
index 89bc5be94fb..4c5f11d55ff 100644
--- a/spec/models/blob_viewer/readme_spec.rb
+++ b/spec/models/blob_viewer/readme_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::Readme do
+RSpec.describe BlobViewer::Readme do
include FakeBlobHelpers
let(:project) { create(:project, :repository, :wiki_repo) }
diff --git a/spec/models/blob_viewer/route_map_spec.rb b/spec/models/blob_viewer/route_map_spec.rb
index 6c703df5c4c..bb0284d7868 100644
--- a/spec/models/blob_viewer/route_map_spec.rb
+++ b/spec/models/blob_viewer/route_map_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::RouteMap do
+RSpec.describe BlobViewer::RouteMap do
include FakeBlobHelpers
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/blob_viewer/server_side_spec.rb b/spec/models/blob_viewer/server_side_spec.rb
index f95305abe78..284ac4524f7 100644
--- a/spec/models/blob_viewer/server_side_spec.rb
+++ b/spec/models/blob_viewer/server_side_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobViewer::ServerSide do
+RSpec.describe BlobViewer::ServerSide do
include FakeBlobHelpers
let(:project) { build(:project) }
diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb
index 558be61824f..4d16e1ff839 100644
--- a/spec/models/board_group_recent_visit_spec.rb
+++ b/spec/models/board_group_recent_visit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BoardGroupRecentVisit do
+RSpec.describe BoardGroupRecentVisit do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:board) { create(:board, group: group) }
diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb
index e404fb3bbdb..8e74405fd8c 100644
--- a/spec/models/board_project_recent_visit_spec.rb
+++ b/spec/models/board_project_recent_visit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BoardProjectRecentVisit do
+RSpec.describe BoardProjectRecentVisit do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:board) { create(:board, project: project) }
diff --git a/spec/models/board_spec.rb b/spec/models/board_spec.rb
index 2d5309b4d23..d309b4dbdb9 100644
--- a/spec/models/board_spec.rb
+++ b/spec/models/board_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Board do
+RSpec.describe Board do
let(:project) { create(:project) }
let(:other_project) { create(:project) }
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 8032f913d86..fc463c6af52 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BroadcastMessage do
+RSpec.describe BroadcastMessage do
subject { build(:broadcast_message) }
it { is_expected.to be_valid }
diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb
index 02594b98665..623e55aad21 100644
--- a/spec/models/chat_name_spec.rb
+++ b/spec/models/chat_name_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatName do
+RSpec.describe ChatName do
let_it_be(:chat_name) { create(:chat_name) }
subject { chat_name }
diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb
index 107fdaccc68..08fd05324aa 100644
--- a/spec/models/chat_team_spec.rb
+++ b/spec/models/chat_team_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatTeam do
+RSpec.describe ChatTeam do
let_it_be(:chat_team) { create(:chat_team) }
subject { chat_team }
diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb
index 99983686670..44f895cc1c5 100644
--- a/spec/models/ci/artifact_blob_spec.rb
+++ b/spec/models/ci/artifact_blob_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::ArtifactBlob do
+RSpec.describe Ci::ArtifactBlob do
let_it_be(:project) { create(:project, :public) }
let_it_be(:build) { create(:ci_build, :artifacts, project: project) }
let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') }
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 385261e0ee9..3a459e5897a 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Bridge do
+RSpec.describe Ci::Bridge do
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project, name: 'project', namespace: create(:namespace, name: 'my')) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
@@ -47,8 +47,8 @@ describe Ci::Bridge do
CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA
CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG
CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
- CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID
- CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
+ CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PROJECT_ROOT_NAMESPACE
+ CI_PIPELINE_IID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
]
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index 8f2199ac360..4fa1b3eb5a5 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildDependencies do
+RSpec.describe Ci::BuildDependencies do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :repository) }
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index 588e5872cc8..e4d71632957 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildMetadata do
+RSpec.describe Ci::BuildMetadata do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group, build_timeout: 2000) }
@@ -92,4 +92,33 @@ describe Ci::BuildMetadata do
end
end
end
+
+ describe 'validations' do
+ context 'when attributes are valid' do
+ it 'returns no errors' do
+ metadata.secrets = {
+ DATABASE_PASSWORD: {
+ vault: {
+ engine: { name: 'kv-v2', path: 'kv-v2' },
+ path: 'production/db',
+ field: 'password'
+ }
+ }
+ }
+
+ expect(metadata).to be_valid
+ end
+ end
+
+ context 'when data is invalid' do
+ it 'returns errors' do
+ metadata.secrets = { DATABASE_PASSWORD: { vault: {} } }
+
+ aggregate_failures do
+ expect(metadata).to be_invalid
+ expect(metadata.errors.full_messages).to eq(["Secrets must be a valid json schema"])
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_need_spec.rb b/spec/models/ci/build_need_spec.rb
index d1186fa981d..43cce073918 100644
--- a/spec/models/ci/build_need_spec.rb
+++ b/spec/models/ci/build_need_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildNeed, model: true do
+RSpec.describe Ci::BuildNeed, model: true do
let(:build_need) { build(:ci_build_need) }
it { is_expected.to belong_to(:build) }
@@ -17,4 +17,22 @@ describe Ci::BuildNeed, model: true do
it { expect(described_class.artifacts).to contain_exactly(with_artifacts) }
end
+
+ describe 'BulkInsertSafe' do
+ let(:ci_build) { build(:ci_build) }
+
+ it "bulk inserts from Ci::Build model" do
+ ci_build.needs_attributes = [
+ { name: "build", artifacts: true },
+ { name: "build2", artifacts: true },
+ { name: "build3", artifacts: true }
+ ]
+
+ expect(described_class).to receive(:bulk_insert!).and_call_original
+
+ BulkInsertableAssociations.with_bulk_insert do
+ ci_build.save!
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_report_result_spec.rb b/spec/models/ci/build_report_result_spec.rb
index 078b0d100a1..e78f602feef 100644
--- a/spec/models/ci/build_report_result_spec.rb
+++ b/spec/models/ci/build_report_result_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildReportResult do
+RSpec.describe Ci::BuildReportResult do
let(:build_report_result) { build(:ci_build_report_result, :with_junit_success) }
describe 'associations' do
diff --git a/spec/models/ci/build_runner_session_spec.rb b/spec/models/ci/build_runner_session_spec.rb
index 3e520407884..601c6ad26f9 100644
--- a/spec/models/ci/build_runner_session_spec.rb
+++ b/spec/models/ci/build_runner_session_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildRunnerSession, model: true do
+RSpec.describe Ci::BuildRunnerSession, model: true do
let!(:build) { create(:ci_build, :with_runner_session) }
let(:url) { 'https://new.example.com' }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 6fdd8463329..857b238981b 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Build do
+RSpec.describe Ci::Build do
let_it_be(:user) { create(:user) }
let_it_be(:group, reload: true) { create(:group) }
let_it_be(:project, reload: true) { create(:project, :repository, group: group) }
@@ -1811,6 +1811,50 @@ describe Ci::Build do
end
end
+ describe '.keep_artifacts!' do
+ let!(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) }
+ let!(:builds_for_update) do
+ Ci::Build.where(id: create_list(:ci_build, 3, artifacts_expire_at: Time.current + 7.days).map(&:id))
+ end
+
+ it 'resets expire_at' do
+ builds_for_update.keep_artifacts!
+
+ builds_for_update.each do |build|
+ expect(build.reload.artifacts_expire_at).to be_nil
+ end
+ end
+
+ it 'does not reset expire_at for other builds' do
+ builds_for_update.keep_artifacts!
+
+ expect(build.reload.artifacts_expire_at).to be_present
+ end
+
+ context 'when having artifacts files' do
+ let!(:artifact) { create(:ci_job_artifact, job: build, expire_in: '7 days') }
+ let!(:artifacts_for_update) do
+ builds_for_update.map do |build|
+ create(:ci_job_artifact, job: build, expire_in: '7 days')
+ end
+ end
+
+ it 'resets dependent objects' do
+ builds_for_update.keep_artifacts!
+
+ artifacts_for_update.each do |artifact|
+ expect(artifact.reload.expire_at).to be_nil
+ end
+ end
+
+ it 'does not reset dependent object for other builds' do
+ builds_for_update.keep_artifacts!
+
+ expect(artifact.reload.expire_at).to be_present
+ end
+ end
+ end
+
describe '#keep_artifacts!' do
let(:build) { create(:ci_build, artifacts_expire_at: Time.current + 7.days) }
@@ -2336,6 +2380,7 @@ describe Ci::Build do
{ key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false },
{ key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false },
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false },
+ { key: 'CI_PROJECT_ROOT_NAMESPACE', value: project.namespace.root_ancestor.path, public: true, masked: false },
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
{ key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: project.repository_languages.map(&:name).join(',').downcase, public: true, masked: false },
@@ -2929,19 +2974,6 @@ describe Ci::Build do
it { is_expected.to include(deployment_variable) }
end
- context 'when build has a freeze period' do
- let(:freeze_variable) { { key: 'CI_DEPLOY_FREEZE', value: 'true', masked: false, public: true } }
-
- before do
- expect_next_instance_of(Ci::FreezePeriodStatus) do |freeze_period|
- expect(freeze_period).to receive(:execute)
- .and_return(true)
- end
- end
-
- it { is_expected.to include(freeze_variable) }
- end
-
context 'when project has default CI config path' do
let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: '.gitlab-ci.yml', public: true, masked: false } }
@@ -3269,17 +3301,6 @@ describe Ci::Build do
expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1')
end
end
-
- context 'when CI instance variables are disabled' do
- before do
- create(:ci_instance_variable, key: 'MY_VAR', value: 'my value 1')
- stub_feature_flags(ci_instance_level_variables: false)
- end
-
- it 'does not include instance level variables' do
- expect(build.scoped_variables_hash).not_to include('MY_VAR': 'my value 1')
- end
- end
end
describe '#any_unmet_prerequisites?' do
@@ -4050,6 +4071,10 @@ describe Ci::Build do
it 'parses blobs and add the results to the terraform report' do
expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error
+ terraform_reports.plans.each do |key, hash_value|
+ expect(hash_value.keys).to match_array(%w[create delete job_id job_name job_path update])
+ end
+
expect(terraform_reports.plans).to match(
a_hash_including(
build.id.to_s => a_hash_including(
@@ -4068,9 +4093,19 @@ describe Ci::Build do
create(:ci_job_artifact, :terraform_with_corrupted_data, job: build, project: build.project)
end
- it 'raises an error' do
- expect { build.collect_terraform_reports!(terraform_reports) }.to raise_error(
- Gitlab::Ci::Parsers::Terraform::Tfplan::TfplanParserError
+ it 'adds invalid plan report' do
+ expect { build.collect_terraform_reports!(terraform_reports) }.not_to raise_error
+
+ terraform_reports.plans.each do |key, hash_value|
+ expect(hash_value.keys).to match_array(%w[job_id job_name job_path tf_report_error])
+ end
+
+ expect(terraform_reports.plans).to match(
+ a_hash_including(
+ build.id.to_s => a_hash_including(
+ 'tf_report_error' => :invalid_json_format
+ )
+ )
)
end
end
@@ -4258,15 +4293,15 @@ describe Ci::Build do
end
end
- context 'when `release_steps` feature is required by build' do
+ context 'when `multi_build_steps` feature is required by build' do
before do
expect(build).to receive(:runner_required_feature_names) do
- [:release_steps]
+ [:multi_build_steps]
end
end
context 'when runner provides given feature' do
- let(:runner_features) { { release_steps: true } }
+ let(:runner_features) { { multi_build_steps: true } }
it { is_expected.to be_truthy }
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 85873847fca..dab523f67ab 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
+RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
include ExclusiveLeaseHelpers
let_it_be(:build) { create(:ci_build, :running) }
diff --git a/spec/models/ci/build_trace_chunks/database_spec.rb b/spec/models/ci/build_trace_chunks/database_spec.rb
index eb94d7dae38..245625b8046 100644
--- a/spec/models/ci/build_trace_chunks/database_spec.rb
+++ b/spec/models/ci/build_trace_chunks/database_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceChunks::Database do
+RSpec.describe Ci::BuildTraceChunks::Database do
let(:data_store) { described_class.new }
describe '#available?' do
diff --git a/spec/models/ci/build_trace_chunks/fog_spec.rb b/spec/models/ci/build_trace_chunks/fog_spec.rb
index b8d78bcd069..7ef3018d87b 100644
--- a/spec/models/ci/build_trace_chunks/fog_spec.rb
+++ b/spec/models/ci/build_trace_chunks/fog_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceChunks::Fog do
+RSpec.describe Ci::BuildTraceChunks::Fog do
let(:data_store) { described_class.new }
before do
diff --git a/spec/models/ci/build_trace_chunks/redis_spec.rb b/spec/models/ci/build_trace_chunks/redis_spec.rb
index 6cff33d24fa..c37b8697a4d 100644
--- a/spec/models/ci/build_trace_chunks/redis_spec.rb
+++ b/spec/models/ci/build_trace_chunks/redis_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do
+RSpec.describe Ci::BuildTraceChunks::Redis, :clean_gitlab_redis_shared_state do
let(:data_store) { described_class.new }
describe '#available?' do
diff --git a/spec/models/ci/build_trace_section_name_spec.rb b/spec/models/ci/build_trace_section_name_spec.rb
index 11e2d27ff79..b220e67d48e 100644
--- a/spec/models/ci/build_trace_section_name_spec.rb
+++ b/spec/models/ci/build_trace_section_name_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceSectionName, model: true do
+RSpec.describe Ci::BuildTraceSectionName, model: true do
subject { build(:ci_build_trace_section_name) }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/ci/build_trace_section_spec.rb b/spec/models/ci/build_trace_section_spec.rb
index 5bd3a953ec0..640bd202b3a 100644
--- a/spec/models/ci/build_trace_section_spec.rb
+++ b/spec/models/ci/build_trace_section_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTraceSection, model: true do
+RSpec.describe Ci::BuildTraceSection, model: true do
it { is_expected.to belong_to(:build)}
it { is_expected.to belong_to(:project)}
it { is_expected.to belong_to(:section_name)}
diff --git a/spec/models/ci/build_trace_spec.rb b/spec/models/ci/build_trace_spec.rb
index 2471a6fa827..3beca0565c6 100644
--- a/spec/models/ci/build_trace_spec.rb
+++ b/spec/models/ci/build_trace_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::BuildTrace do
+RSpec.describe Ci::BuildTrace do
let(:build) { build_stubbed(:ci_build) }
let(:state) { nil }
let(:data) { StringIO.new('the-stream') }
@@ -11,7 +11,7 @@ describe Ci::BuildTrace do
Gitlab::Ci::Trace::Stream.new { data }
end
- subject { described_class.new(build: build, stream: stream, state: state, content_format: content_format) }
+ subject { described_class.new(build: build, stream: stream, state: state) }
shared_examples 'delegates methods' do
it { is_expected.to delegate_method(:state).to(:trace) }
@@ -25,29 +25,11 @@ describe Ci::BuildTrace do
it { is_expected.to delegate_method(:complete?).to(:build).with_prefix }
end
- context 'with :json content format' do
- let(:content_format) { :json }
+ it_behaves_like 'delegates methods'
- it_behaves_like 'delegates methods'
-
- it { is_expected.to be_json }
-
- it 'returns formatted trace' do
- expect(subject.trace.lines).to eq([
- { offset: 0, content: [{ text: 'the-stream' }] }
- ])
- end
- end
-
- context 'with :html content format' do
- let(:content_format) { :html }
-
- it_behaves_like 'delegates methods'
-
- it { is_expected.to be_html }
-
- it 'returns formatted trace' do
- expect(subject.trace.html).to eq('<span>the-stream</span>')
- end
+ it 'returns formatted trace' do
+ expect(subject.lines).to eq([
+ { offset: 0, content: [{ text: 'the-stream' }] }
+ ])
end
end
diff --git a/spec/models/ci/daily_build_group_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb
index f2ce1b5775f..059a5b76b9a 100644
--- a/spec/models/ci/daily_build_group_report_result_spec.rb
+++ b/spec/models/ci/daily_build_group_report_result_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::DailyBuildGroupReportResult do
+RSpec.describe Ci::DailyBuildGroupReportResult do
let(:daily_build_group_report_result) { build(:ci_daily_build_group_report_result)}
describe 'associations' do
diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb
index b700ec8c45f..831895cb528 100644
--- a/spec/models/ci/freeze_period_status_spec.rb
+++ b/spec/models/ci/freeze_period_status_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe Ci::FreezePeriodStatus do
+RSpec.describe Ci::FreezePeriodStatus do
let(:project) { create :project }
# '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
let(:friday_2300) { '0 23 * * 5' }
diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb
index 868382e3756..dc9aee906ea 100644
--- a/spec/models/ci/group_spec.rb
+++ b/spec/models/ci/group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Group do
+RSpec.describe Ci::Group do
let_it_be(:project) { create(:project) }
let!(:jobs) { build_list(:ci_build, 1, :success, project: project) }
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index 610db9bf0e5..c8eac4d8765 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::GroupVariable do
+RSpec.describe Ci::GroupVariable do
subject { build(:ci_group_variable) }
it_behaves_like "CI variable"
diff --git a/spec/models/ci/instance_variable_spec.rb b/spec/models/ci/instance_variable_spec.rb
index 4d69b7ac2f8..344ba5bfafd 100644
--- a/spec/models/ci/instance_variable_spec.rb
+++ b/spec/models/ci/instance_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::InstanceVariable do
+RSpec.describe Ci::InstanceVariable do
subject { build(:ci_instance_variable) }
it_behaves_like "CI variable"
@@ -15,21 +15,6 @@ describe Ci::InstanceVariable do
subject { build(:ci_instance_variable) }
end
- context 'with instance level variable feature flag disabled' do
- let(:plan_limits) { create(:plan_limits, :default_plan) }
-
- before do
- stub_feature_flags(ci_instance_level_variables_limit: false)
- plan_limits.update(described_class.limit_name => 1)
- create(:ci_instance_variable)
- end
-
- it 'can create new models exceeding the plan limits', :aggregate_failures do
- expect { subject.save }.to change { described_class.count }
- expect(subject.errors[:base]).to be_empty
- end
- end
-
describe '.unprotected' do
subject { described_class.unprotected }
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 17e00533ac3..b5f9128b7c5 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::JobArtifact do
+RSpec.describe Ci::JobArtifact do
let(:artifact) { create(:ci_job_artifact, :archive) }
describe "Associations" do
@@ -110,6 +110,21 @@ describe Ci::JobArtifact do
end
end
+ describe '.associated_file_types_for' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { Ci::JobArtifact.associated_file_types_for(file_type) }
+
+ where(:file_type, :result) do
+ 'codequality' | %w(codequality)
+ 'quality' | nil
+ end
+
+ with_them do
+ it { is_expected.to eq result }
+ end
+ end
+
describe '.erasable' do
subject { described_class.erasable }
@@ -174,18 +189,6 @@ describe Ci::JobArtifact do
end
end
- describe '.for_ref' do
- let(:first_pipeline) { create(:ci_pipeline, ref: 'first_ref') }
- let(:second_pipeline) { create(:ci_pipeline, ref: 'second_ref', project: first_pipeline.project) }
- let!(:first_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: first_pipeline)) }
- let!(:second_artifact) { create(:ci_job_artifact, job: create(:ci_build, pipeline: second_pipeline)) }
-
- it 'returns job artifacts for a given pipeline ref' do
- expect(described_class.for_ref(first_pipeline.ref, first_pipeline.project.id)).to eq([first_artifact])
- expect(described_class.for_ref(second_pipeline.ref, first_pipeline.project.id)).to eq([second_artifact])
- end
- end
-
describe '.for_job_name' do
it 'returns job artifacts for a given job name' do
first_job = create(:ci_build, name: 'first')
@@ -501,4 +504,100 @@ describe Ci::JobArtifact do
end
end
end
+
+ describe '.file_types' do
+ context 'all file types have corresponding limit' do
+ let_it_be(:plan_limits) { create(:plan_limits) }
+
+ where(:file_type) do
+ described_class.file_types.keys
+ end
+
+ with_them do
+ let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{file_type}" }
+
+ it { expect(plan_limits.attributes).to include(limit_name), file_type_limit_failure_message(file_type, limit_name) }
+ end
+ end
+ end
+
+ describe '.max_artifact_size' do
+ let(:build) { create(:ci_build) }
+
+ subject(:max_size) { described_class.max_artifact_size(type: artifact_type, project: build.project) }
+
+ context 'when file type is supported' do
+ let(:project_closest_setting) { 1024 }
+ let(:artifact_type) { 'junit' }
+
+ before do
+ stub_feature_flags(ci_max_artifact_size_per_type: flag_enabled)
+ allow(build.project).to receive(:closest_setting).with(:max_artifacts_size).and_return(project_closest_setting)
+ end
+
+ shared_examples_for 'basing off the project closest setting' do
+ it { is_expected.to eq(project_closest_setting.megabytes.to_i) }
+ end
+
+ shared_examples_for 'basing off the plan limit' do
+ it { is_expected.to eq(max_size_for_type.megabytes.to_i) }
+ end
+
+ context 'and feature flag for custom max size per type is enabled' do
+ let(:flag_enabled) { true }
+ let(:limit_name) { "#{described_class::PLAN_LIMIT_PREFIX}#{artifact_type}" }
+
+ let!(:plan_limits) { create(:plan_limits, :default_plan) }
+
+ context 'and plan limit is disabled for the given artifact type' do
+ before do
+ plan_limits.update!(limit_name => 0)
+ end
+
+ it_behaves_like 'basing off the project closest setting'
+
+ context 'and project closest setting results to zero' do
+ let(:project_closest_setting) { 0 }
+
+ it { is_expected.to eq(0) }
+ end
+ end
+
+ context 'and plan limit is enabled for the given artifact type' do
+ before do
+ plan_limits.update!(limit_name => max_size_for_type)
+ end
+
+ context 'and plan limit is smaller than project setting' do
+ let(:max_size_for_type) { project_closest_setting - 1 }
+
+ it_behaves_like 'basing off the plan limit'
+ end
+
+ context 'and plan limit is smaller than project setting' do
+ let(:max_size_for_type) { project_closest_setting + 1 }
+
+ it_behaves_like 'basing off the project closest setting'
+ end
+ end
+ end
+
+ context 'and feature flag for custom max size per type is disabled' do
+ let(:flag_enabled) { false }
+
+ it_behaves_like 'basing off the project closest setting'
+ end
+ end
+ end
+
+ def file_type_limit_failure_message(type, limit_name)
+ <<~MSG
+ The artifact type `#{type}` is missing its counterpart plan limit which is expected to be named `#{limit_name}`.
+
+ Please refer to https://docs.gitlab.com/ee/development/application_limits.html on how to add new plan limit columns.
+
+ Take note that while existing max size plan limits default to 0, succeeding new limits are recommended to have
+ non-zero default values.
+ MSG
+ end
end
diff --git a/spec/models/ci/job_variable_spec.rb b/spec/models/ci/job_variable_spec.rb
index b94a914c784..4aebd3283f0 100644
--- a/spec/models/ci/job_variable_spec.rb
+++ b/spec/models/ci/job_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::JobVariable do
+RSpec.describe Ci::JobVariable do
subject { build(:ci_job_variable) }
it_behaves_like "CI variable"
diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb
index f503fc10c08..c53f6abb037 100644
--- a/spec/models/ci/legacy_stage_spec.rb
+++ b/spec/models/ci/legacy_stage_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::LegacyStage do
+RSpec.describe Ci::LegacyStage do
let(:stage) { build(:ci_stage) }
let(:pipeline) { stage.pipeline }
let(:stage_name) { stage.name }
diff --git a/spec/models/ci/persistent_ref_spec.rb b/spec/models/ci/persistent_ref_spec.rb
index 89dd9b05331..18552317025 100644
--- a/spec/models/ci/persistent_ref_spec.rb
+++ b/spec/models/ci/persistent_ref_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::PersistentRef do
+RSpec.describe Ci::PersistentRef do
it 'cleans up persistent refs after pipeline finished' do
pipeline = create(:ci_pipeline, :running)
diff --git a/spec/models/ci/pipeline_config_spec.rb b/spec/models/ci/pipeline_config_spec.rb
index 25f514ee5ab..3d033d33df3 100644
--- a/spec/models/ci/pipeline_config_spec.rb
+++ b/spec/models/ci/pipeline_config_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::PipelineConfig, type: :model do
+RSpec.describe Ci::PipelineConfig, type: :model do
it { is_expected.to belong_to(:pipeline) }
it { is_expected.to validate_presence_of(:pipeline) }
diff --git a/spec/models/ci/pipeline_message_spec.rb b/spec/models/ci/pipeline_message_spec.rb
new file mode 100644
index 00000000000..6c97a025625
--- /dev/null
+++ b/spec/models/ci/pipeline_message_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::PipelineMessage do
+ describe 'validations' do
+ subject { described_class.new(pipeline: pipeline, content: content) }
+
+ let(:pipeline) { create(:ci_pipeline) }
+
+ context 'when message content is longer than the limit' do
+ let(:content) { 'x' * (described_class::MAX_CONTENT_LENGTH + 1) }
+
+ it 'is truncated with ellipsis' do
+ subject.save!
+
+ expect(subject.content).to end_with('x...')
+ expect(subject.content.length).to eq(described_class::MAX_CONTENT_LENGTH)
+ end
+ end
+
+ context 'when message is not present' do
+ let(:content) { '' }
+
+ it 'returns an error' do
+ expect(subject.save).to be_falsey
+ expect(subject.errors[:content]).to be_present
+ end
+ end
+
+ context 'when message content is valid' do
+ let(:content) { 'valid message content' }
+
+ it 'is saved with default error severity' do
+ subject.save!
+
+ expect(subject.content).to eq(content)
+ expect(subject.severity).to eq('error')
+ expect(subject).to be_error
+ end
+
+ it 'is persist the defined severity' do
+ subject.severity = :warning
+
+ subject.save!
+
+ expect(subject.content).to eq(content)
+ expect(subject.severity).to eq('warning')
+ expect(subject).to be_warning
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index 4ba70552f01..949d5f7bd04 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::PipelineSchedule do
+RSpec.describe Ci::PipelineSchedule do
subject { build(:ci_pipeline_schedule) }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/ci/pipeline_schedule_variable_spec.rb b/spec/models/ci/pipeline_schedule_variable_spec.rb
index c96a24d5042..fd6b1d3dce0 100644
--- a/spec/models/ci/pipeline_schedule_variable_spec.rb
+++ b/spec/models/ci/pipeline_schedule_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::PipelineScheduleVariable do
+RSpec.describe Ci::PipelineScheduleVariable do
subject { build(:ci_pipeline_schedule_variable) }
it_behaves_like "CI variable"
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 782a4206c36..ed2466d6413 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Pipeline, :mailer do
+RSpec.describe Ci::Pipeline, :mailer do
include ProjectForksHelper
include StubRequests
@@ -219,6 +219,50 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '.outside_pipeline_family' do
+ subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) }
+
+ let(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let(:child_pipeline) { create(:ci_pipeline, project: project) }
+
+ let!(:other_pipeline) { create(:ci_pipeline, project: project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: upstream_pipeline),
+ source_project: project,
+ pipeline: child_pipeline,
+ project: project)
+ end
+
+ it 'only returns pipelines outside pipeline family' do
+ expect(outside_pipeline_family).to contain_exactly(other_pipeline)
+ end
+ end
+
+ describe '.before_pipeline' do
+ subject(:before_pipeline) { described_class.before_pipeline(child_pipeline) }
+
+ let!(:older_other_pipeline) { create(:ci_pipeline, project: project) }
+
+ let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:child_pipeline) { create(:ci_pipeline, project: project) }
+
+ let!(:other_pipeline) { create(:ci_pipeline, project: project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: upstream_pipeline),
+ source_project: project,
+ pipeline: child_pipeline,
+ project: project)
+ end
+
+ it 'only returns older pipelines outside pipeline family' do
+ expect(before_pipeline).to contain_exactly(older_other_pipeline)
+ end
+ end
+
describe '#merge_request?' do
let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) }
let(:merge_request) { create(:merge_request) }
@@ -1488,6 +1532,35 @@ describe Ci::Pipeline, :mailer do
sha: project.commit.sha)
end
+ describe '#lazy_ref_commit' do
+ let(:another) do
+ create(:ci_pipeline,
+ project: project,
+ ref: 'feature',
+ sha: project.commit('feature').sha)
+ end
+
+ let(:unicode) do
+ create(:ci_pipeline,
+ project: project,
+ ref: 'ü/unicode/multi-byte')
+ end
+
+ it 'returns the latest commit for a ref lazily' do
+ expect(project.repository)
+ .to receive(:list_commits_by_ref_name).once
+ .and_call_original
+
+ pipeline.lazy_ref_commit
+ another.lazy_ref_commit
+ unicode.lazy_ref_commit
+
+ expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha
+ expect(another.lazy_ref_commit.id).to eq another.sha
+ expect(unicode.lazy_ref_commit).to be_nil
+ end
+ end
+
describe '#latest?' do
context 'with latest sha' do
it 'returns true' do
@@ -1496,17 +1569,26 @@ describe Ci::Pipeline, :mailer do
end
context 'with a branch name as the ref' do
- it 'looks up commit with the full ref name' do
- expect(pipeline.project).to receive(:commit).with('refs/heads/master').and_call_original
+ it 'looks up a commit for a branch' do
+ expect(pipeline.ref).to eq 'master'
+ expect(pipeline).to be_latest
+ end
+ end
+
+ context 'with a tag name as a ref' do
+ it 'looks up a commit for a tag' do
+ expect(project.repository.branch_names).not_to include 'v1.0.0'
+ pipeline.update(sha: project.commit('v1.0.0').sha, ref: 'v1.0.0', tag: true)
+
+ expect(pipeline).to be_tag
expect(pipeline).to be_latest
end
end
context 'with not latest sha' do
before do
- pipeline.update(
- sha: project.commit("#{project.default_branch}~1").sha)
+ pipeline.update(sha: project.commit("#{project.default_branch}~1").sha)
end
it 'returns false' do
@@ -1932,6 +2014,23 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '.last_finished_for_ref_id' do
+ let(:project) { create(:project, :repository) }
+ let(:branch) { project.default_branch }
+ let(:ref) { project.ci_refs.take }
+ let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
+ let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) }
+ let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) }
+ let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) }
+ let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) }
+ let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) }
+
+ it 'returns the expected pipeline' do
+ result = described_class.last_finished_for_ref_id(ref.id)
+ expect(result).to eq(pipeline4)
+ end
+ end
+
describe '.internal_sources' do
subject { described_class.internal_sources }
@@ -2087,7 +2186,7 @@ describe Ci::Pipeline, :mailer do
it 'raises an exception' do
expect { pipeline.update_legacy_status }
- .to raise_error(HasStatus::UnknownStatusError)
+ .to raise_error(Ci::HasStatus::UnknownStatusError)
end
end
end
@@ -2580,6 +2679,55 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#same_family_pipeline_ids' do
+ subject(:same_family_pipeline_ids) { pipeline.same_family_pipeline_ids }
+
+ context 'when pipeline is not child nor parent' do
+ it 'returns just the pipeline id' do
+ expect(same_family_pipeline_ids).to contain_exactly(pipeline.id)
+ end
+ end
+
+ context 'when pipeline is child' do
+ let(:parent) { create(:ci_pipeline, project: pipeline.project) }
+ let(:sibling) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: parent),
+ source_project: parent.project,
+ pipeline: pipeline,
+ project: pipeline.project)
+
+ create(:ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: parent),
+ source_project: parent.project,
+ pipeline: sibling,
+ project: sibling.project)
+ end
+
+ it 'returns parent sibling and self ids' do
+ expect(same_family_pipeline_ids).to contain_exactly(parent.id, pipeline.id, sibling.id)
+ end
+ end
+
+ context 'when pipeline is parent' do
+ let(:child) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: pipeline),
+ source_project: pipeline.project,
+ pipeline: child,
+ project: child.project)
+ end
+
+ it 'returns self and child ids' do
+ expect(same_family_pipeline_ids).to contain_exactly(pipeline.id, child.id)
+ end
+ end
+ end
+
describe '#stuck?' do
before do
create(:ci_build, :pending, pipeline: pipeline)
@@ -2602,6 +2750,28 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#add_error_message' do
+ let(:pipeline) { build_stubbed(:ci_pipeline) }
+
+ it 'adds a new pipeline error message' do
+ pipeline.add_error_message('The error message')
+
+ expect(pipeline.messages.map(&:content)).to contain_exactly('The error message')
+ end
+
+ context 'when feature flag ci_store_pipeline_messages is disabled' do
+ before do
+ stub_feature_flags(ci_store_pipeline_messages: false)
+ end
+
+ it ' does not add pipeline error message' do
+ pipeline.add_error_message('The error message')
+
+ expect(pipeline.messages).to be_empty
+ end
+ end
+ end
+
describe '#has_yaml_errors?' do
context 'when yaml_errors is set' do
before do
@@ -2825,6 +2995,16 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#batch_lookup_report_artifact_for_file_type' do
+ context 'with code quality report artifact' do
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_report, project: project) }
+
+ it "returns the code quality artifact" do
+ expect(pipeline.batch_lookup_report_artifact_for_file_type(:codequality)).to eq(pipeline.job_artifacts.sample)
+ end
+ end
+ end
+
describe '#latest_report_builds' do
it 'returns build with test artifacts' do
test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
@@ -2891,6 +3071,39 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#test_report_summary' do
+ subject { pipeline.test_report_summary }
+
+ context 'when pipeline has multiple builds with report results' do
+ let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+
+ before do
+ create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline, project: project)
+ create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline, project: project)
+ end
+
+ it 'returns test report summary with collected data', :aggregate_failures do
+ expect(subject.total_time).to be(0.84)
+ expect(subject.total_count).to be(4)
+ expect(subject.success_count).to be(0)
+ expect(subject.failed_count).to be(0)
+ expect(subject.error_count).to be(4)
+ expect(subject.skipped_count).to be(0)
+ end
+ end
+
+ context 'when pipeline does not have any builds with report results' do
+ it 'returns empty test report sumary', :aggregate_failures do
+ expect(subject.total_time).to be(0)
+ expect(subject.total_count).to be(0)
+ expect(subject.success_count).to be(0)
+ expect(subject.failed_count).to be(0)
+ expect(subject.error_count).to be(0)
+ expect(subject.skipped_count).to be(0)
+ end
+ end
+ end
+
describe '#test_reports' do
subject { pipeline.test_reports }
@@ -3069,6 +3282,32 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ context 'when transitioning to success' do
+ context 'when feature is enabled' do
+ before do
+ stub_feature_flags(keep_latest_artifacts_for_ref: true)
+ end
+
+ it 'calls the PipelineSuccessUnlockArtifactsWorker' do
+ expect(Ci::PipelineSuccessUnlockArtifactsWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.succeed!
+ end
+ end
+
+ context 'when feature is disabled' do
+ before do
+ stub_feature_flags(keep_latest_artifacts_for_ref: false)
+ end
+
+ it 'does not call the PipelineSuccessUnlockArtifactsWorker' do
+ expect(Ci::PipelineSuccessUnlockArtifactsWorker).not_to receive(:perform_async)
+
+ pipeline.succeed!
+ end
+ end
+ end
end
describe '#default_branch?' do
@@ -3133,8 +3372,8 @@ describe Ci::Pipeline, :mailer do
end
end
- describe '#error_messages' do
- subject { pipeline.error_messages }
+ describe '#full_error_messages' do
+ subject { pipeline.full_error_messages }
before do
pipeline.valid?
diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb
index e8c7ce088e2..04fcaab4c2d 100644
--- a/spec/models/ci/pipeline_variable_spec.rb
+++ b/spec/models/ci/pipeline_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::PipelineVariable do
+RSpec.describe Ci::PipelineVariable do
subject { build(:ci_pipeline_variable) }
it_behaves_like "CI variable"
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index e67f740279b..35764e2bbbe 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Processable do
+RSpec.describe Ci::Processable do
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
diff --git a/spec/models/ci/ref_spec.rb b/spec/models/ci/ref_spec.rb
index 3d75cb63141..fd4742a8ad2 100644
--- a/spec/models/ci/ref_spec.rb
+++ b/spec/models/ci/ref_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Ref do
+RSpec.describe Ci::Ref do
it { is_expected.to belong_to(:project) }
describe '.ensure_for' do
@@ -62,6 +62,35 @@ describe Ci::Ref do
end
end
+ describe '#last_finished_pipeline_id' do
+ let(:pipeline_status) { :running }
+ let(:config_source) { Ci::PipelineEnums.config_sources[:repository_source] }
+ let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) }
+ let(:ci_ref) { pipeline.ci_ref }
+
+ context 'when there are no finished pipelines' do
+ it 'returns nil' do
+ expect(ci_ref.last_finished_pipeline_id).to be_nil
+ end
+ end
+
+ context 'when there are finished pipelines' do
+ let(:pipeline_status) { :success }
+
+ it 'returns the pipeline id' do
+ expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id)
+ end
+
+ context 'when the pipeline is not a ci_source' do
+ let(:config_source) { Ci::PipelineEnums.config_sources[:parameter_source] }
+
+ it 'returns nil' do
+ expect(ci_ref.last_finished_pipeline_id).to be_nil
+ end
+ end
+ end
+ end
+
describe '#update_status_by!' do
subject { ci_ref.update_status_by!(pipeline) }
diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb
index ce8b03282bc..9f72d1a82e5 100644
--- a/spec/models/ci/resource_group_spec.rb
+++ b/spec/models/ci/resource_group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::ResourceGroup do
+RSpec.describe Ci::ResourceGroup do
describe 'validation' do
it 'valids when key includes allowed character' do
resource_group = build(:ci_resource_group, key: 'test')
diff --git a/spec/models/ci/resource_spec.rb b/spec/models/ci/resource_spec.rb
index 27e512e2c45..90f26ef2b31 100644
--- a/spec/models/ci/resource_spec.rb
+++ b/spec/models/ci/resource_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Resource do
+RSpec.describe Ci::Resource do
describe '.free' do
subject { described_class.free }
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 296240b1602..8247ebf1144 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Runner do
+RSpec.describe Ci::Runner do
it_behaves_like 'having unique enum values'
describe 'validation' do
@@ -713,6 +713,46 @@ describe Ci::Runner do
end
end
+ describe '#belongs_to_more_than_one_project?' do
+ context 'project runner' do
+ let(:project1) { create(:project) }
+ let(:project2) { create(:project) }
+
+ context 'two projects assigned to runner' do
+ let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) }
+
+ it 'returns true' do
+ expect(runner.belongs_to_more_than_one_project?).to be_truthy
+ end
+ end
+
+ context 'one project assigned to runner' do
+ let(:runner) { create(:ci_runner, :project, projects: [project1]) }
+
+ it 'returns false' do
+ expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ end
+ end
+ end
+
+ context 'group runner' do
+ let(:group) { create(:group) }
+ let(:runner) { create(:ci_runner, :group, groups: [group]) }
+
+ it 'returns false' do
+ expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ end
+ end
+
+ context 'shared runner' do
+ let(:runner) { create(:ci_runner, :instance) }
+
+ it 'returns false' do
+ expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ end
+ end
+ end
+
describe '#has_tags?' do
context 'when runner has tags' do
subject { create(:ci_runner, tag_list: ['tag']) }
diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb
index 5023747b487..ccf3140650b 100644
--- a/spec/models/ci/sources/pipeline_spec.rb
+++ b/spec/models/ci/sources/pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Sources::Pipeline do
+RSpec.describe Ci::Sources::Pipeline do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:pipeline) }
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index a1549532559..3d873a1b9c1 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Stage, :models do
+RSpec.describe Ci::Stage, :models do
let_it_be(:pipeline) { create(:ci_empty_pipeline) }
let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) }
@@ -172,7 +172,7 @@ describe Ci::Stage, :models do
it 'raises an exception' do
expect { stage.update_legacy_status }
- .to raise_error(HasStatus::UnknownStatusError)
+ .to raise_error(Ci::HasStatus::UnknownStatusError)
end
end
end
diff --git a/spec/models/ci/trigger_request_spec.rb b/spec/models/ci/trigger_request_spec.rb
index d04349bec92..0d462741089 100644
--- a/spec/models/ci/trigger_request_spec.rb
+++ b/spec/models/ci/trigger_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::TriggerRequest do
+RSpec.describe Ci::TriggerRequest do
describe 'validation' do
it 'be invalid if saving a variable' do
trigger = build(:ci_trigger_request, variables: { TRIGGER_KEY_1: 'TRIGGER_VALUE_1' } )
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 5b0815f8156..4ba6c6e50f7 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Trigger do
+RSpec.describe Ci::Trigger do
let(:project) { create :project }
describe 'associations' do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 810a0ddfd2e..26a7a2596af 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Variable do
+RSpec.describe Ci::Variable do
subject { build(:ci_variable) }
it_behaves_like "CI variable"
diff --git a/spec/models/clusters/applications/cert_manager_spec.rb b/spec/models/clusters/applications/cert_manager_spec.rb
index d7fd0d06b05..7ca7f533a27 100644
--- a/spec/models/clusters/applications/cert_manager_spec.rb
+++ b/spec/models/clusters/applications/cert_manager_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::CertManager do
+RSpec.describe Clusters::Applications::CertManager do
let(:cert_manager) { create(:clusters_applications_cert_manager) }
include_examples 'cluster application core specs', :clusters_applications_cert_manager
diff --git a/spec/models/clusters/applications/cilium_spec.rb b/spec/models/clusters/applications/cilium_spec.rb
new file mode 100644
index 00000000000..8b01502d5c0
--- /dev/null
+++ b/spec/models/clusters/applications/cilium_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Applications::Cilium do
+ let(:cilium) { create(:clusters_applications_cilium) }
+
+ include_examples 'cluster application core specs', :clusters_applications_cilium
+ include_examples 'cluster application status specs', :clusters_applications_cilium
+ include_examples 'cluster application initial status specs'
+
+ describe '#allowed_to_uninstall?' do
+ subject { cilium.allowed_to_uninstall? }
+
+ it { is_expected.to be false }
+ end
+end
diff --git a/spec/models/clusters/applications/crossplane_spec.rb b/spec/models/clusters/applications/crossplane_spec.rb
index ebc675497f4..a41c5f6586b 100644
--- a/spec/models/clusters/applications/crossplane_spec.rb
+++ b/spec/models/clusters/applications/crossplane_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Crossplane do
+RSpec.describe Clusters::Applications::Crossplane do
let(:crossplane) { create(:clusters_applications_crossplane) }
include_examples 'cluster application core specs', :clusters_applications_crossplane
diff --git a/spec/models/clusters/applications/elastic_stack_spec.rb b/spec/models/clusters/applications/elastic_stack_spec.rb
index 50042a4e29a..62123ffa542 100644
--- a/spec/models/clusters/applications/elastic_stack_spec.rb
+++ b/spec/models/clusters/applications/elastic_stack_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::ElasticStack do
+RSpec.describe Clusters::Applications::ElasticStack do
include KubernetesHelpers
include_examples 'cluster application core specs', :clusters_applications_elastic_stack
@@ -27,6 +27,20 @@ describe Clusters::Applications::ElasticStack do
expect(subject.preinstall).to be_empty
end
+ context 'within values.yaml' do
+ let(:values_yaml_content) {subject.files[:"values.yaml"]}
+
+ it 'contains the disabled index lifecycle management' do
+ expect(values_yaml_content).to include "setup.ilm.enabled: false"
+ end
+
+ it 'contains daily indices with respective template' do
+ expect(values_yaml_content).to include "index: \"filebeat-%{[agent.version]}-%{+yyyy.MM.dd}\""
+ expect(values_yaml_content).to include "setup.template.name: 'filebeat'"
+ expect(values_yaml_content).to include "setup.template.pattern: 'filebeat-*'"
+ end
+ end
+
context 'on a non rbac enabled cluster' do
before do
elastic_stack.cluster.platform_kubernetes.abac!
diff --git a/spec/models/clusters/applications/fluentd_spec.rb b/spec/models/clusters/applications/fluentd_spec.rb
index 4e9548990ed..be7b4a87947 100644
--- a/spec/models/clusters/applications/fluentd_spec.rb
+++ b/spec/models/clusters/applications/fluentd_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Fluentd do
+RSpec.describe Clusters::Applications::Fluentd do
let(:waf_log_enabled) { true }
let(:cilium_log_enabled) { true }
let(:fluentd) { create(:clusters_applications_fluentd, waf_log_enabled: waf_log_enabled, cilium_log_enabled: cilium_log_enabled) }
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index 87454e1d3e2..6d2ecaa6d47 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Helm do
+RSpec.describe Clusters::Applications::Helm do
include_examples 'cluster application core specs', :clusters_applications_helm
describe '.available' do
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 8aee4eec0d3..d1138f5fa2d 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Ingress do
+RSpec.describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress) }
it_behaves_like 'having unique enum values'
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 937db9217f3..3cf24f1a9ef 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Jupyter do
+RSpec.describe Clusters::Applications::Jupyter do
include_examples 'cluster application core specs', :clusters_applications_jupyter
include_examples 'cluster application status specs', :clusters_applications_jupyter
include_examples 'cluster application version specs', :clusters_applications_jupyter
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 7ff7644e703..b14161ce8e6 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Knative do
+RSpec.describe Clusters::Applications::Knative do
let(:knative) { create(:clusters_applications_knative) }
include_examples 'cluster application core specs', :clusters_applications_knative
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 1ed9e207b6b..1215b38a9a2 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Prometheus do
+RSpec.describe Clusters::Applications::Prometheus do
include KubernetesHelpers
include StubRequests
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 6ee6711ec4b..fbabfd25b2f 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Applications::Runner do
+RSpec.describe Clusters::Applications::Runner do
let(:ci_runner) { create(:ci_runner) }
include_examples 'cluster application core specs', :clusters_applications_runner
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 4dd74976028..4807957152c 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
+RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
include KubernetesHelpers
@@ -10,6 +10,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
subject { build(:cluster) }
+ it { is_expected.to include_module(HasEnvironmentScope) }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:management_project).class_name('::Project') }
it { is_expected.to have_many(:cluster_projects) }
@@ -289,6 +290,79 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
describe 'validations' do
subject { cluster.valid? }
+ context 'when validates unique_environment_scope' do
+ context 'for a project cluster' do
+ let(:project) { create(:project) }
+
+ before do
+ create(:cluster, projects: [project], environment_scope: 'product/*')
+ end
+
+ context 'when identical environment scope exists in project' do
+ let(:cluster) { build(:cluster, projects: [project], environment_scope: 'product/*') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when identical environment scope does not exist in project' do
+ let(:cluster) { build(:cluster, projects: [project], environment_scope: '*') }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when identical environment scope exists in different project' do
+ let(:project2) { create(:project) }
+ let(:cluster) { build(:cluster, projects: [project2], environment_scope: 'product/*') }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'for a group cluster' do
+ let(:group) { create(:group) }
+
+ before do
+ create(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*')
+ end
+
+ context 'when identical environment scope exists in group' do
+ let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: 'product/*') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when identical environment scope does not exist in group' do
+ let(:cluster) { build(:cluster, cluster_type: :group_type, groups: [group], environment_scope: '*') }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when identical environment scope exists in different group' do
+ let(:cluster) { build(:cluster, :group, environment_scope: 'product/*') }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'for an instance cluster' do
+ before do
+ create(:cluster, :instance, environment_scope: 'product/*')
+ end
+
+ context 'identical environment scope exists' do
+ let(:cluster) { build(:cluster, :instance, environment_scope: 'product/*') }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'identical environment scope does not exist' do
+ let(:cluster) { build(:cluster, :instance, environment_scope: '*') }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+
context 'when validates name' do
context 'when provided by user' do
let!(:cluster) { build(:cluster, :provided_by_user, name: name) }
@@ -1111,13 +1185,23 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
context 'cluster is enabled' do
let(:cluster) { create(:cluster, :provided_by_user, :group) }
+ let(:gl_k8s_node_double) { double(Gitlab::Kubernetes::Node) }
+ let(:expected_nodes) { nil }
before do
- stub_kubeclient_nodes_and_nodes_metrics(cluster.platform.api_url)
+ stub_kubeclient_discover(cluster.platform.api_url)
+ allow(Gitlab::Kubernetes::Node).to receive(:new).with(cluster).and_return(gl_k8s_node_double)
+ allow(gl_k8s_node_double).to receive(:all).and_return([])
end
context 'connection to the cluster is successful' do
- it { is_expected.to eq(connection_status: :connected, nodes: [kube_node.merge(kube_node_metrics)]) }
+ before do
+ allow(gl_k8s_node_double).to receive(:all).and_return(expected_nodes)
+ end
+
+ let(:expected_nodes) { [kube_node.merge(kube_node_metrics)] }
+
+ it { is_expected.to eq(connection_status: :connected, nodes: expected_nodes) }
end
context 'cluster cannot be reached' do
@@ -1126,7 +1210,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
.and_raise(SocketError)
end
- it { is_expected.to eq(connection_status: :unreachable, nodes: nil) }
+ it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) }
end
context 'cluster cannot be authenticated to' do
@@ -1135,7 +1219,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
.and_raise(OpenSSL::X509::CertificateError.new("Certificate error"))
end
- it { is_expected.to eq(connection_status: :authentication_failure, nodes: nil) }
+ it { is_expected.to eq(connection_status: :authentication_failure, nodes: expected_nodes) }
end
describe 'Kubeclient::HttpError' do
@@ -1147,18 +1231,18 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
.and_raise(Kubeclient::HttpError.new(error_code, error_message, nil))
end
- it { is_expected.to eq(connection_status: :authentication_failure, nodes: nil) }
+ it { is_expected.to eq(connection_status: :authentication_failure, nodes: expected_nodes) }
context 'generic timeout' do
let(:error_message) { 'Timed out connecting to server'}
- it { is_expected.to eq(connection_status: :unreachable, nodes: nil) }
+ it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) }
end
context 'gateway timeout' do
let(:error_message) { '504 Gateway Timeout for GET https://kubernetes.example.com/api/v1'}
- it { is_expected.to eq(connection_status: :unreachable, nodes: nil) }
+ it { is_expected.to eq(connection_status: :unreachable, nodes: expected_nodes) }
end
end
@@ -1168,12 +1252,12 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
.and_raise(StandardError)
end
- it { is_expected.to eq(connection_status: :unknown_failure, nodes: nil) }
+ it { is_expected.to eq(connection_status: :unknown_failure, nodes: expected_nodes) }
it 'notifies Sentry' do
expect(Gitlab::ErrorTracking).to receive(:track_exception)
.with(instance_of(StandardError), hash_including(cluster_id: cluster.id))
- .twice
+ .once
subject
end
diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb
index 1957e1fc5ee..5ac561eb2d0 100644
--- a/spec/models/clusters/clusters_hierarchy_spec.rb
+++ b/spec/models/clusters/clusters_hierarchy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::ClustersHierarchy do
+RSpec.describe Clusters::ClustersHierarchy do
describe '#base_and_ancestors' do
def base_and_ancestors(clusterable, include_management_project: true)
described_class.new(clusterable, include_management_project: include_management_project).base_and_ancestors
diff --git a/spec/models/clusters/group_spec.rb b/spec/models/clusters/group_spec.rb
index ba145342cb8..3b541c40938 100644
--- a/spec/models/clusters/group_spec.rb
+++ b/spec/models/clusters/group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Group do
+RSpec.describe Clusters::Group do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to belong_to(:group) }
end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index f0e6dd53664..adccc72d13d 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Platforms::Kubernetes do
+RSpec.describe Clusters::Platforms::Kubernetes do
include KubernetesHelpers
it { is_expected.to belong_to(:cluster) }
@@ -204,6 +204,52 @@ describe Clusters::Platforms::Kubernetes do
end
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) }
+
+ context 'ca_pem is a single certificate' do
+ let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem')) }
+ let(:kubernetes) do
+ build(:cluster_platform_kubernetes,
+ :configured,
+ namespace: 'a-namespace',
+ cluster: cluster,
+ ca_pem: ca_pem)
+ end
+
+ it 'adds it to cert_store' do
+ cert = OpenSSL::X509::Certificate.new(ca_pem)
+ cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store]
+
+ expect(cert_store.verify(cert)).to be true
+ end
+ end
+
+ context 'ca_pem is a chain' do
+ let(:cert_chain) { File.read(Rails.root.join('spec/fixtures/clusters/chain_certificates.pem')) }
+ let(:kubernetes) do
+ build(:cluster_platform_kubernetes,
+ :configured,
+ namespace: 'a-namespace',
+ cluster: cluster,
+ ca_pem: cert_chain)
+ end
+
+ it 'includes chain of certificates' do
+ cert1_file = File.read(Rails.root.join('spec/fixtures/clusters/root_certificate.pem'))
+ cert1 = OpenSSL::X509::Certificate.new(cert1_file)
+
+ cert2_file = File.read(Rails.root.join('spec/fixtures/clusters/intermediate_certificate.pem'))
+ cert2 = OpenSSL::X509::Certificate.new(cert2_file)
+
+ cert3_file = File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem'))
+ cert3 = OpenSSL::X509::Certificate.new(cert3_file)
+
+ cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store]
+
+ expect(cert_store.verify(cert1)).to be true
+ expect(cert_store.verify(cert2)).to be true
+ expect(cert_store.verify(cert3)).to be true
+ end
+ end
end
describe '#rbac?' do
diff --git a/spec/models/clusters/project_spec.rb b/spec/models/clusters/project_spec.rb
index 671af085d10..e16dfa47898 100644
--- a/spec/models/clusters/project_spec.rb
+++ b/spec/models/clusters/project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Project do
+RSpec.describe Clusters::Project do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:kubernetes_namespaces) }
diff --git a/spec/models/clusters/providers/aws_spec.rb b/spec/models/clusters/providers/aws_spec.rb
index 05d6e63288e..3b4a48cc5be 100644
--- a/spec/models/clusters/providers/aws_spec.rb
+++ b/spec/models/clusters/providers/aws_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Providers::Aws do
+RSpec.describe Clusters::Providers::Aws do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to validate_length_of(:key_name).is_at_least(1).is_at_most(255) }
diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb
index e2fd777d131..ad9ada04875 100644
--- a/spec/models/clusters/providers/gcp_spec.rb
+++ b/spec/models/clusters/providers/gcp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Clusters::Providers::Gcp do
+RSpec.describe Clusters::Providers::Gcp do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to validate_presence_of(:zone) }
diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb
index d49b71db5f8..f4e86f3292b 100644
--- a/spec/models/commit_collection_spec.rb
+++ b/spec/models/commit_collection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CommitCollection do
+RSpec.describe CommitCollection do
let(:project) { create(:project, :repository) }
let(:commit) { project.commit("c1c67abbaf91f624347bb3ae96eabe3a1b742478") }
@@ -75,6 +75,18 @@ describe CommitCollection do
end
end
+ describe '#with_markdown_cache' do
+ let(:commits) { [commit] }
+ let(:collection) { described_class.new(project, commits) }
+
+ it 'preloads commits cache markdown' do
+ aggregate_failures do
+ expect(Commit).to receive(:preload_markdown_cache!).with(commits)
+ expect(collection.with_markdown_cache).to eq(collection)
+ end
+ end
+ end
+
describe 'enrichment methods' do
let(:gitaly_commit) { commit }
let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) }
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index 245e47fa17b..3fb8708c884 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CommitRange do
+RSpec.describe CommitRange do
describe 'modules' do
subject { described_class }
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ddda04faaf1..cfa87b3e39e 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Commit do
+RSpec.describe Commit do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet, :repository) }
let_it_be(:project_snippet) { create(:project_snippet, :repository) }
@@ -675,7 +675,10 @@ eos
end
describe '#work_in_progress?' do
- ['squash! ', 'fixup! ', 'wip: ', 'WIP: ', '[WIP] '].each do |wip_prefix|
+ [
+ 'squash! ', 'fixup! ', 'wip: ', 'WIP: ', '[WIP] ',
+ 'draft: ', 'Draft - ', '[Draft] ', '(draft) ', 'Draft: '
+ ].each do |wip_prefix|
it "detects the '#{wip_prefix}' prefix" do
commit.message = "#{wip_prefix}#{commit.message}"
@@ -689,6 +692,12 @@ eos
expect(commit).to be_work_in_progress
end
+ it "detects WIP for a commit just saying 'draft'" do
+ commit.message = "draft"
+
+ expect(commit).to be_work_in_progress
+ end
+
it "doesn't detect WIP for a commit that begins with 'FIXUP! '" do
commit.message = "FIXUP! #{commit.message}"
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 85fc503a1ca..cd0110a787b 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CommitStatus do
+RSpec.describe CommitStatus do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) do
diff --git a/spec/models/commit_with_pipeline_spec.rb b/spec/models/commit_with_pipeline_spec.rb
index e0bb29fec7b..ff451527929 100644
--- a/spec/models/commit_with_pipeline_spec.rb
+++ b/spec/models/commit_with_pipeline_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CommitWithPipeline do
+RSpec.describe CommitWithPipeline do
let(:project) { create(:project, :public, :repository) }
let(:commit) { described_class.new(project.commit) }
diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb
index 43c3580bed2..d395aa359e5 100644
--- a/spec/models/compare_spec.rb
+++ b/spec/models/compare_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Compare do
+RSpec.describe Compare do
include RepoHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb
index 5c1694e3737..24eb3e8a1e6 100644
--- a/spec/models/concerns/access_requestable_spec.rb
+++ b/spec/models/concerns/access_requestable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AccessRequestable do
+RSpec.describe AccessRequestable do
describe 'Group' do
describe '#request_access' do
let(:group) { create(:group, :public) }
diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb
new file mode 100644
index 00000000000..8fda8bccf09
--- /dev/null
+++ b/spec/models/concerns/approvable_base_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ApprovableBase do
+ describe '#approved_by?' do
+ let(:merge_request) { create(:merge_request) }
+ let(:user) { create(:user) }
+
+ subject { merge_request.approved_by?(user) }
+
+ context 'when a user has not approved' do
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'when a user has approved' do
+ let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
+
+ it 'returns false' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when a user is nil' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/atomic_internal_id_spec.rb b/spec/models/concerns/atomic_internal_id_spec.rb
index 93bf7ec10dd..8c3537f1dcc 100644
--- a/spec/models/concerns/atomic_internal_id_spec.rb
+++ b/spec/models/concerns/atomic_internal_id_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AtomicInternalId do
+RSpec.describe AtomicInternalId do
let(:milestone) { build(:milestone) }
let(:iid) { double('iid', to_i: 42) }
let(:external_iid) { 100 }
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index 96e867dbc97..8a8eeea39dc 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Avatarable do
+RSpec.describe Avatarable do
let(:project) { create(:project, :with_avatar) }
let(:gitlab_host) { "https://gitlab.example.com" }
diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb
index 29f911fcb04..b5b3772ecb6 100644
--- a/spec/models/concerns/awardable_spec.rb
+++ b/spec/models/concerns/awardable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Awardable do
+RSpec.describe Awardable do
let!(:issue) { create(:issue) }
let!(:award_emoji) { create(:award_emoji, :downvote, awardable: issue) }
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
index d2373926802..a8fcb714c64 100644
--- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BatchDestroyDependentAssociations do
+RSpec.describe BatchDestroyDependentAssociations do
class TestProject < ActiveRecord::Base
self.table_name = 'projects'
diff --git a/spec/models/concerns/blob_language_from_git_attributes_spec.rb b/spec/models/concerns/blob_language_from_git_attributes_spec.rb
index 4cb8f042b1d..c07ee15e841 100644
--- a/spec/models/concerns/blob_language_from_git_attributes_spec.rb
+++ b/spec/models/concerns/blob_language_from_git_attributes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlobLanguageFromGitAttributes do
+RSpec.describe BlobLanguageFromGitAttributes do
include FakeBlobHelpers
let(:project) { build(:project, :repository) }
diff --git a/spec/models/concerns/blocks_json_serialization_spec.rb b/spec/models/concerns/blocks_json_serialization_spec.rb
index 32870461019..d811b47fa35 100644
--- a/spec/models/concerns/blocks_json_serialization_spec.rb
+++ b/spec/models/concerns/blocks_json_serialization_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BlocksJsonSerialization do
+RSpec.describe BlocksJsonSerialization do
before do
stub_const('DummyModel', Class.new)
DummyModel.class_eval do
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index 07d6cee487f..82b0c00b396 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -2,57 +2,7 @@
require 'spec_helper'
-describe BulkInsertSafe do
- class BulkInsertItem < ActiveRecord::Base
- include BulkInsertSafe
- include ShaAttribute
-
- validates :name, :enum_value, :secret_value, :sha_value, :jsonb_value, presence: true
-
- ENUM_VALUES = {
- case_1: 1
- }.freeze
-
- sha_attribute :sha_value
-
- enum enum_value: ENUM_VALUES
-
- attr_encrypted :secret_value,
- mode: :per_attribute_iv,
- algorithm: 'aes-256-gcm',
- key: Settings.attr_encrypted_db_key_base_32,
- insecure_mode: false
-
- default_value_for :enum_value, 'case_1'
- default_value_for :secret_value, 'my-secret'
- default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'
- default_value_for :jsonb_value, { "key" => "value" }
-
- def self.valid_list(count)
- Array.new(count) { |n| new(name: "item-#{n}") }
- end
-
- def self.invalid_list(count)
- Array.new(count) { new }
- end
- end
-
- module InheritedUnsafeMethods
- extend ActiveSupport::Concern
-
- included do
- after_save -> { "unsafe" }
- end
- end
-
- module InheritedSafeMethods
- extend ActiveSupport::Concern
-
- included do
- after_initialize -> { "safe" }
- end
- end
-
+RSpec.describe BulkInsertSafe do
before(:all) do
ActiveRecord::Schema.define do
create_table :bulk_insert_items, force: true do |t|
@@ -66,100 +16,155 @@ describe BulkInsertSafe do
t.index :name, unique: true
end
end
-
- BulkInsertItem.reset_column_information
end
after(:all) do
ActiveRecord::Schema.define do
drop_table :bulk_insert_items, force: true
end
+ end
+
+ let_it_be(:bulk_insert_item_class) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'bulk_insert_items'
- BulkInsertItem.reset_column_information
+ include BulkInsertSafe
+ include ShaAttribute
+
+ validates :name, :enum_value, :secret_value, :sha_value, :jsonb_value, presence: true
+
+ sha_attribute :sha_value
+
+ enum enum_value: { case_1: 1 }
+
+ attr_encrypted :secret_value,
+ mode: :per_attribute_iv,
+ algorithm: 'aes-256-gcm',
+ key: Settings.attr_encrypted_db_key_base_32,
+ insecure_mode: false
+
+ default_value_for :enum_value, 'case_1'
+ default_value_for :secret_value, 'my-secret'
+ default_value_for :sha_value, '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'
+ default_value_for :jsonb_value, { "key" => "value" }
+
+ def self.name
+ 'BulkInsertItem'
+ end
+
+ def self.valid_list(count)
+ Array.new(count) { |n| new(name: "item-#{n}") }
+ end
+
+ def self.invalid_list(count)
+ Array.new(count) { new }
+ end
+ end
end
- describe BulkInsertItem do
- it_behaves_like 'a BulkInsertSafe model', described_class do
- let(:valid_items_for_bulk_insertion) { described_class.valid_list(10) }
- let(:invalid_items_for_bulk_insertion) { described_class.invalid_list(10) }
+ describe 'BulkInsertItem' do
+ it_behaves_like 'a BulkInsertSafe model' do
+ let(:target_class) { bulk_insert_item_class.dup }
+ let(:valid_items_for_bulk_insertion) { target_class.valid_list(10) }
+ let(:invalid_items_for_bulk_insertion) { target_class.invalid_list(10) }
end
context 'when inheriting class methods' do
+ let(:inherited_unsafe_methods_module) do
+ Module.new do
+ extend ActiveSupport::Concern
+
+ included do
+ after_save -> { "unsafe" }
+ end
+ end
+ end
+
+ let(:inherited_safe_methods_module) do
+ Module.new do
+ extend ActiveSupport::Concern
+
+ included do
+ after_initialize -> { "safe" }
+ end
+ end
+ end
+
it 'raises an error when method is not bulk-insert safe' do
- expect { described_class.include(InheritedUnsafeMethods) }
- .to raise_error(described_class::MethodNotAllowedError)
+ expect { bulk_insert_item_class.include(inherited_unsafe_methods_module) }
+ .to raise_error(bulk_insert_item_class::MethodNotAllowedError)
end
it 'does not raise an error when method is bulk-insert safe' do
- expect { described_class.include(InheritedSafeMethods) }.not_to raise_error
+ expect { bulk_insert_item_class.include(inherited_safe_methods_module) }.not_to raise_error
end
end
context 'primary keys' do
it 'raises error if primary keys are set prior to insertion' do
- item = described_class.new(name: 'valid', id: 10)
+ item = bulk_insert_item_class.new(name: 'valid', id: 10)
- expect { described_class.bulk_insert!([item]) }
- .to raise_error(described_class::PrimaryKeySetError)
+ expect { bulk_insert_item_class.bulk_insert!([item]) }
+ .to raise_error(bulk_insert_item_class::PrimaryKeySetError)
end
end
describe '.bulk_insert!' do
it 'inserts items in the given number of batches' do
- items = described_class.valid_list(10)
+ items = bulk_insert_item_class.valid_list(10)
expect(ActiveRecord::InsertAll).to receive(:new).twice.and_call_original
- described_class.bulk_insert!(items, batch_size: 5)
+ bulk_insert_item_class.bulk_insert!(items, batch_size: 5)
end
it 'items can be properly fetched from database' do
- items = described_class.valid_list(10)
+ items = bulk_insert_item_class.valid_list(10)
- described_class.bulk_insert!(items)
+ bulk_insert_item_class.bulk_insert!(items)
- attribute_names = described_class.attribute_names - %w[id created_at updated_at]
- expect(described_class.last(items.size).pluck(*attribute_names)).to eq(
+ attribute_names = bulk_insert_item_class.attribute_names - %w[id created_at updated_at]
+ expect(bulk_insert_item_class.last(items.size).pluck(*attribute_names)).to eq(
items.pluck(*attribute_names))
end
it 'rolls back the transaction when any item is invalid' do
# second batch is bad
- all_items = described_class.valid_list(10) +
- described_class.invalid_list(10)
+ all_items = bulk_insert_item_class.valid_list(10) +
+ bulk_insert_item_class.invalid_list(10)
expect do
- described_class.bulk_insert!(all_items, batch_size: 2) rescue nil
- end.not_to change { described_class.count }
+ bulk_insert_item_class.bulk_insert!(all_items, batch_size: 2) rescue nil
+ end.not_to change { bulk_insert_item_class.count }
end
it 'does nothing and returns an empty array when items are empty' do
- expect(described_class.bulk_insert!([])).to eq([])
- expect(described_class.count).to eq(0)
+ expect(bulk_insert_item_class.bulk_insert!([])).to eq([])
+ expect(bulk_insert_item_class.count).to eq(0)
end
context 'with returns option set' do
context 'when is set to :ids' do
it 'return an array with the primary key values for all inserted records' do
- items = described_class.valid_list(1)
+ items = bulk_insert_item_class.valid_list(1)
- expect(described_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer))
+ expect(bulk_insert_item_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer))
end
end
context 'when is set to nil' do
it 'returns an empty array' do
- items = described_class.valid_list(1)
+ items = bulk_insert_item_class.valid_list(1)
- expect(described_class.bulk_insert!(items, returns: nil)).to eq([])
+ expect(bulk_insert_item_class.bulk_insert!(items, returns: nil)).to eq([])
end
end
context 'when is set to anything else' do
it 'raises an error' do
- items = described_class.valid_list(1)
+ items = bulk_insert_item_class.valid_list(1)
- expect { described_class.bulk_insert!([items], returns: [:id, :name]) }
+ expect { bulk_insert_item_class.bulk_insert!([items], returns: [:id, :name]) }
.to raise_error(ArgumentError, "returns needs to be :ids or nil")
end
end
@@ -167,20 +172,20 @@ describe BulkInsertSafe do
end
context 'when duplicate items are to be inserted' do
- let!(:existing_object) { described_class.create!(name: 'duplicate', secret_value: 'old value') }
- let(:new_object) { described_class.new(name: 'duplicate', secret_value: 'new value') }
+ let!(:existing_object) { bulk_insert_item_class.create!(name: 'duplicate', secret_value: 'old value') }
+ let(:new_object) { bulk_insert_item_class.new(name: 'duplicate', secret_value: 'new value') }
describe '.bulk_insert!' do
context 'when skip_duplicates is set to false' do
it 'raises an exception' do
- expect { described_class.bulk_insert!([new_object], skip_duplicates: false) }
+ expect { bulk_insert_item_class.bulk_insert!([new_object], skip_duplicates: false) }
.to raise_error(ActiveRecord::RecordNotUnique)
end
end
context 'when skip_duplicates is set to true' do
it 'does not update existing object' do
- described_class.bulk_insert!([new_object], skip_duplicates: true)
+ bulk_insert_item_class.bulk_insert!([new_object], skip_duplicates: true)
expect(existing_object.reload.secret_value).to eq('old value')
end
@@ -189,7 +194,7 @@ describe BulkInsertSafe do
describe '.bulk_upsert!' do
it 'updates existing object' do
- described_class.bulk_upsert!([new_object], unique_by: %w[name])
+ bulk_insert_item_class.bulk_upsert!([new_object], unique_by: %w[name])
expect(existing_object.reload.secret_value).to eq('new value')
end
diff --git a/spec/models/concerns/bulk_insertable_associations_spec.rb b/spec/models/concerns/bulk_insertable_associations_spec.rb
index 6359b2c57ef..5a40639e493 100644
--- a/spec/models/concerns/bulk_insertable_associations_spec.rb
+++ b/spec/models/concerns/bulk_insertable_associations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BulkInsertableAssociations do
+RSpec.describe BulkInsertableAssociations do
class BulkFoo < ApplicationRecord
include BulkInsertSafe
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index c46ebcf324c..5f8c65e429e 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CacheMarkdownField, :clean_gitlab_redis_cache do
+RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
let(:ar_class) do
Class.new(ActiveRecord::Base) do
self.table_name = 'issues'
diff --git a/spec/models/concerns/cacheable_attributes_spec.rb b/spec/models/concerns/cacheable_attributes_spec.rb
index 6694b2aba22..f2877bed9cf 100644
--- a/spec/models/concerns/cacheable_attributes_spec.rb
+++ b/spec/models/concerns/cacheable_attributes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CacheableAttributes do
+RSpec.describe CacheableAttributes do
let(:minimal_test_class) do
Class.new do
include ActiveModel::Model
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index 9819f656f0d..521b47c63fd 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CaseSensitivity do
+RSpec.describe CaseSensitivity do
describe '.iwhere' do
let(:connection) { ActiveRecord::Base.connection }
let(:model) do
diff --git a/spec/models/concerns/checksummable_spec.rb b/spec/models/concerns/checksummable_spec.rb
index 017077bd297..b469b2e5c18 100644
--- a/spec/models/concerns/checksummable_spec.rb
+++ b/spec/models/concerns/checksummable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Checksummable do
+RSpec.describe Checksummable do
describe ".hexdigest" do
let(:fake_class) do
Class.new do
diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb
index e41d75568f7..e6dbf403b63 100644
--- a/spec/models/concerns/chronic_duration_attribute_spec.rb
+++ b/spec/models/concerns/chronic_duration_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-shared_examples 'ChronicDurationAttribute reader' do
+RSpec.shared_examples 'ChronicDurationAttribute reader' do
it 'contains dynamically created reader method' do
expect(subject.class).to be_public_method_defined(virtual_field)
end
@@ -22,7 +22,7 @@ shared_examples 'ChronicDurationAttribute reader' do
end
end
-shared_examples 'ChronicDurationAttribute writer' do
+RSpec.shared_examples 'ChronicDurationAttribute writer' do
it 'contains dynamically created writer method' do
expect(subject.class).to be_public_method_defined("#{virtual_field}=")
end
@@ -94,7 +94,7 @@ shared_examples 'ChronicDurationAttribute writer' do
end
end
-describe 'ChronicDurationAttribute' do
+RSpec.describe 'ChronicDurationAttribute' do
context 'when default value is not set' do
let(:source_field) {:maximum_timeout}
let(:virtual_field) {:maximum_timeout_human_readable}
@@ -118,7 +118,7 @@ describe 'ChronicDurationAttribute' do
end
end
-describe 'ChronicDurationAttribute - reader' do
+RSpec.describe 'ChronicDurationAttribute - reader' do
let(:source_field) {:timeout}
let(:virtual_field) {:timeout_human_readable}
diff --git a/spec/models/concerns/ci/has_ref_spec.rb b/spec/models/concerns/ci/has_ref_spec.rb
index b98f915018b..69f2fdb21e1 100644
--- a/spec/models/concerns/ci/has_ref_spec.rb
+++ b/spec/models/concerns/ci/has_ref_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::HasRef do
+RSpec.describe Ci::HasRef do
describe '#branch?' do
let(:build) { create(:ci_build) }
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/ci/has_status_spec.rb
index 68047f24ec3..fe46b63781d 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/ci/has_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe HasStatus do
+RSpec.describe Ci::HasStatus do
describe '.slow_composite_status' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/concerns/ci/has_variable_spec.rb b/spec/models/concerns/ci/has_variable_spec.rb
index c132fe47c3c..b5390281064 100644
--- a/spec/models/concerns/ci/has_variable_spec.rb
+++ b/spec/models/concerns/ci/has_variable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::HasVariable do
+RSpec.describe Ci::HasVariable do
subject { build(:ci_variable) }
it { is_expected.to validate_presence_of(:key) }
diff --git a/spec/models/concerns/ci/maskable_spec.rb b/spec/models/concerns/ci/maskable_spec.rb
index 01861b39165..840a08b6060 100644
--- a/spec/models/concerns/ci/maskable_spec.rb
+++ b/spec/models/concerns/ci/maskable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Ci::Maskable do
+RSpec.describe Ci::Maskable do
let(:variable) { build(:ci_variable) }
describe 'masked value validations' do
diff --git a/spec/models/concerns/delete_with_limit_spec.rb b/spec/models/concerns/delete_with_limit_spec.rb
index 52085f970f3..0259a1ea4fb 100644
--- a/spec/models/concerns/delete_with_limit_spec.rb
+++ b/spec/models/concerns/delete_with_limit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeleteWithLimit do
+RSpec.describe DeleteWithLimit do
describe '.delete_with_limit' do
it 'deletes a limited amount of rows' do
create_list(:web_hook_log, 4)
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index 9164c3a75c5..2bb6aa27e21 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -2,12 +2,247 @@
require 'spec_helper'
-describe DeploymentPlatform do
+RSpec.describe DeploymentPlatform do
let(:project) { create(:project) }
describe '#deployment_platform' do
subject { project.deployment_platform }
+ context 'multiple clusters' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+
+ shared_examples 'matching environment scope' do
+ it 'returns environment specific cluster' do
+ is_expected.to eq(cluster.platform_kubernetes)
+ end
+ end
+
+ shared_examples 'not matching environment scope' do
+ it 'returns default cluster' do
+ is_expected.to eq(default_cluster.platform_kubernetes)
+ end
+ end
+
+ context 'multiple clusters use the same management project' do
+ let(:management_project) { create(:project, group: group) }
+
+ let!(:default_cluster) do
+ create(:cluster_for_group, groups: [group], environment_scope: '*', management_project: management_project)
+ end
+
+ let!(:cluster) do
+ create(:cluster_for_group, groups: [group], environment_scope: 'review/*', management_project: management_project)
+ end
+
+ let(:environment) { 'review/name' }
+
+ subject { management_project.deployment_platform(environment: environment) }
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when project does not have a cluster but has group clusters' do
+ let!(:default_cluster) do
+ create(:cluster, :provided_by_user,
+ cluster_type: :group_type, groups: [group], environment_scope: '*')
+ end
+
+ let!(:cluster) do
+ create(:cluster, :provided_by_user,
+ cluster_type: :group_type, environment_scope: 'review/*', groups: [group])
+ end
+
+ let(:environment) { 'review/name' }
+
+ subject { project.deployment_platform(environment: environment) }
+
+ context 'when environment scope is exactly matched' do
+ before do
+ cluster.update!(environment_scope: 'review/name')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ before do
+ cluster.update!(environment_scope: 'review/*')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope does not match' do
+ before do
+ cluster.update!(environment_scope: 'review/*/special')
+ end
+
+ it_behaves_like 'not matching environment scope'
+ end
+
+ context 'when group belongs to a parent group' do
+ let(:parent_group) { create(:group) }
+ let(:group) { create(:group, parent: parent_group) }
+
+ context 'when parent_group has a cluster with default scope' do
+ let!(:parent_group_cluster) do
+ create(:cluster, :provided_by_user,
+ cluster_type: :group_type, environment_scope: '*', groups: [parent_group])
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when parent_group has a cluster that is an exact match' do
+ let!(:parent_group_cluster) do
+ create(:cluster, :provided_by_user,
+ cluster_type: :group_type, environment_scope: 'review/name', groups: [parent_group])
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+ end
+ end
+
+ context 'with instance clusters' do
+ let!(:default_cluster) do
+ create(:cluster, :provided_by_user, :instance, environment_scope: '*')
+ end
+
+ let!(:cluster) do
+ create(:cluster, :provided_by_user, :instance, environment_scope: 'review/*')
+ end
+
+ let(:environment) { 'review/name' }
+
+ subject { project.deployment_platform(environment: environment) }
+
+ context 'when environment scope is exactly matched' do
+ before do
+ cluster.update!(environment_scope: 'review/name')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ before do
+ cluster.update!(environment_scope: 'review/*')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope does not match' do
+ before do
+ cluster.update!(environment_scope: 'review/*/special')
+ end
+
+ it_behaves_like 'not matching environment scope'
+ end
+ end
+
+ context 'when environment is specified' do
+ let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') }
+ let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) }
+
+ let!(:group_default_cluster) do
+ create(:cluster, :provided_by_user,
+ cluster_type: :group_type, groups: [group], environment_scope: '*')
+ end
+
+ let(:environment) { 'review/name' }
+
+ subject { project.deployment_platform(environment: environment) }
+
+ context 'when environment scope is exactly matched' do
+ before do
+ cluster.update!(environment_scope: 'review/name')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope is matched by wildcard' do
+ before do
+ cluster.update!(environment_scope: 'review/*')
+ end
+
+ it_behaves_like 'matching environment scope'
+ end
+
+ context 'when environment scope does not match' do
+ before do
+ cluster.update!(environment_scope: 'review/*/special')
+ end
+
+ it_behaves_like 'not matching environment scope'
+ end
+
+ context 'when environment scope has _' do
+ it 'does not treat it as wildcard' do
+ cluster.update!(environment_scope: 'foo_bar/*')
+
+ is_expected.to eq(default_cluster.platform_kubernetes)
+ end
+
+ context 'when environment name contains an underscore' do
+ let(:environment) { 'foo_bar/test' }
+
+ it 'matches literally for _' do
+ cluster.update!(environment_scope: 'foo_bar/*')
+
+ is_expected.to eq(cluster.platform_kubernetes)
+ end
+ end
+ end
+
+ # The environment name and scope cannot have % at the moment,
+ # but we're considering relaxing it and we should also make sure
+ # it doesn't break in case some data sneaked in somehow as we're
+ # not checking this integrity in database level.
+ context 'when environment scope has %' do
+ it 'does not treat it as wildcard' do
+ cluster.update_attribute(:environment_scope, '*%*')
+
+ is_expected.to eq(default_cluster.platform_kubernetes)
+ end
+
+ context 'when environment name contains a percent char' do
+ let(:environment) { 'foo%bar/test' }
+
+ it 'matches literally for %' do
+ cluster.update_attribute(:environment_scope, 'foo%bar/*')
+
+ is_expected.to eq(cluster.platform_kubernetes)
+ end
+ end
+ end
+
+ context 'when perfectly matched cluster exists' do
+ let!(:perfectly_matched_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'review/name') }
+
+ it 'returns perfectly matched cluster as highest precedence' do
+ is_expected.to eq(perfectly_matched_cluster.platform_kubernetes)
+ end
+ end
+ end
+
+ context 'with multiple clusters and multiple environments' do
+ let!(:cluster_1) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'staging/*') }
+ let!(:cluster_2) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'test/*') }
+
+ let(:environment_1) { 'staging/name' }
+ let(:environment_2) { 'test/name' }
+
+ it 'returns the appropriate cluster' do
+ expect(project.deployment_platform(environment: environment_1)).to eq(cluster_1.platform_kubernetes)
+ expect(project.deployment_platform(environment: environment_2)).to eq(cluster_2.platform_kubernetes)
+ end
+ end
+ end
+
context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do
it { is_expected.to be_nil }
end
diff --git a/spec/models/concerns/deprecated_assignee_spec.rb b/spec/models/concerns/deprecated_assignee_spec.rb
index e394de0aa34..630d9ea601f 100644
--- a/spec/models/concerns/deprecated_assignee_spec.rb
+++ b/spec/models/concerns/deprecated_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeprecatedAssignee do
+RSpec.describe DeprecatedAssignee do
let(:user) { create(:user) }
describe '#assignee_id=' do
diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb
index f091861bd41..dd5d422f12d 100644
--- a/spec/models/concerns/discussion_on_diff_spec.rb
+++ b/spec/models/concerns/discussion_on_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiscussionOnDiff do
+RSpec.describe DiscussionOnDiff do
subject { create(:diff_note_on_merge_request, line_number: 18).to_discussion }
describe "#truncated_diff_lines" do
diff --git a/spec/models/concerns/each_batch_spec.rb b/spec/models/concerns/each_batch_spec.rb
index ee3d9aea505..3c93c8a7a79 100644
--- a/spec/models/concerns/each_batch_spec.rb
+++ b/spec/models/concerns/each_batch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe EachBatch do
+RSpec.describe EachBatch do
describe '.each_batch' do
let(:model) do
Class.new(ActiveRecord::Base) do
diff --git a/spec/models/concerns/editable_spec.rb b/spec/models/concerns/editable_spec.rb
index 4a4a3ca5687..1d26629d0aa 100644
--- a/spec/models/concerns/editable_spec.rb
+++ b/spec/models/concerns/editable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Editable do
+RSpec.describe Editable do
describe '#edited?' do
let(:issue) { create(:issue, last_edited_at: nil) }
let(:edited_issue) { create(:issue, created_at: 3.days.ago, last_edited_at: 2.days.ago) }
diff --git a/spec/models/concerns/expirable_spec.rb b/spec/models/concerns/expirable_spec.rb
index f4f5eab5b86..b20d759fc3f 100644
--- a/spec/models/concerns/expirable_spec.rb
+++ b/spec/models/concerns/expirable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Expirable do
+RSpec.describe Expirable do
describe 'ProjectMember' do
let(:no_expire) { create(:project_member) }
let(:expire_later) { create(:project_member, expires_at: Time.current + 6.days) }
diff --git a/spec/models/concerns/faster_cache_keys_spec.rb b/spec/models/concerns/faster_cache_keys_spec.rb
index 7830acbae3d..ab6e809b3f7 100644
--- a/spec/models/concerns/faster_cache_keys_spec.rb
+++ b/spec/models/concerns/faster_cache_keys_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe FasterCacheKeys do
+RSpec.describe FasterCacheKeys do
describe '#cache_key' do
it 'returns a String' do
# We're using a fixed string here so it's easier to set an expectation for
diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb
index 89720e3652c..cc01820cc97 100644
--- a/spec/models/concerns/featurable_spec.rb
+++ b/spec/models/concerns/featurable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Featurable do
+RSpec.describe Featurable do
let_it_be(:user) { create(:user) }
let(:project) { create(:project) }
let(:feature_class) { subject.class }
diff --git a/spec/models/concerns/feature_gate_spec.rb b/spec/models/concerns/feature_gate_spec.rb
index 276d3d9e1d5..6106708a32d 100644
--- a/spec/models/concerns/feature_gate_spec.rb
+++ b/spec/models/concerns/feature_gate_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe FeatureGate do
+RSpec.describe FeatureGate do
describe 'User' do
describe '#flipper_id' do
context 'when user is not persisted' do
diff --git a/spec/models/concerns/from_union_spec.rb b/spec/models/concerns/from_union_spec.rb
index 735e14b47ec..9819a6ec3de 100644
--- a/spec/models/concerns/from_union_spec.rb
+++ b/spec/models/concerns/from_union_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe FromUnion do
+RSpec.describe FromUnion do
describe '.from_union' do
let(:model) do
Class.new(ActiveRecord::Base) do
diff --git a/spec/models/concerns/group_descendant_spec.rb b/spec/models/concerns/group_descendant_spec.rb
index 47419770d0f..b29fa910ee6 100644
--- a/spec/models/concerns/group_descendant_spec.rb
+++ b/spec/models/concerns/group_descendant_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupDescendant do
+RSpec.describe GroupDescendant do
let(:parent) { create(:group) }
let(:subgroup) { create(:group, parent: parent) }
let(:subsub_group) { create(:group, parent: subgroup) }
diff --git a/spec/models/concerns/has_environment_scope_spec.rb b/spec/models/concerns/has_environment_scope_spec.rb
index a6e1ba59263..0cc997709c9 100644
--- a/spec/models/concerns/has_environment_scope_spec.rb
+++ b/spec/models/concerns/has_environment_scope_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe HasEnvironmentScope do
+RSpec.describe HasEnvironmentScope do
subject { build(:ci_variable) }
it { is_expected.to allow_value('*').for(:environment_scope) }
diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb
index f12eee414f9..9496bb57b8b 100644
--- a/spec/models/concerns/has_user_type_spec.rb
+++ b/spec/models/concerns/has_user_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe User do
+RSpec.describe User do
specify 'types consistency checks', :aggregate_failures do
expect(described_class::USER_TYPES.keys)
.to match_array(%w[human ghost alert_bot project_bot support_bot service_user visual_review_bot migration_bot])
diff --git a/spec/models/concerns/ignorable_columns_spec.rb b/spec/models/concerns/ignorable_columns_spec.rb
index 018b1296c62..a5eff154a0b 100644
--- a/spec/models/concerns/ignorable_columns_spec.rb
+++ b/spec/models/concerns/ignorable_columns_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IgnorableColumns do
+RSpec.describe IgnorableColumns do
let(:record_class) do
Class.new(ApplicationRecord) do
include IgnorableColumns
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 74ee7a87b7b..96d3e2b7b1b 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Issuable do
+RSpec.describe Issuable do
include ProjectForksHelper
let(:issuable_class) { Issue }
@@ -416,6 +416,27 @@ describe Issuable do
describe '#to_hook_data' do
let(:builder) { double }
+ context 'when old_associations is empty' do
+ let(:label) { create(:label) }
+
+ before do
+ issue.update!(labels: [label])
+ issue.assignees << user
+ issue.spend_time(duration: 2, user_id: user.id, spent_at: Time.current)
+ expect(Gitlab::HookData::IssuableBuilder)
+ .to receive(:new).with(issue).and_return(builder)
+ end
+
+ it 'delegates to Gitlab::HookData::IssuableBuilder#build and does not set labels, assignees, nor total_time_spent' do
+ expect(builder).to receive(:build).with(
+ user: user,
+ changes: {})
+
+ # In some cases, old_associations is empty, e.g. on a close event
+ issue.to_hook_data(user)
+ end
+ end
+
context 'labels are updated' do
let(:labels) { create_list(:label, 2) }
diff --git a/spec/models/concerns/limitable_spec.rb b/spec/models/concerns/limitable_spec.rb
index ca0a257be7a..753e2a8ee5e 100644
--- a/spec/models/concerns/limitable_spec.rb
+++ b/spec/models/concerns/limitable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Limitable do
+RSpec.describe Limitable do
let(:minimal_test_class) do
Class.new do
include ActiveModel::Model
diff --git a/spec/models/concerns/loaded_in_group_list_spec.rb b/spec/models/concerns/loaded_in_group_list_spec.rb
index 509811822e0..c37943022ba 100644
--- a/spec/models/concerns/loaded_in_group_list_spec.rb
+++ b/spec/models/concerns/loaded_in_group_list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LoadedInGroupList do
+RSpec.describe LoadedInGroupList do
let(:parent) { create(:group) }
subject(:found_group) { Group.with_selects_for_list.find_by(id: parent.id) }
diff --git a/spec/models/concerns/manual_inverse_association_spec.rb b/spec/models/concerns/manual_inverse_association_spec.rb
index ee32e3b165b..1349d2cc680 100644
--- a/spec/models/concerns/manual_inverse_association_spec.rb
+++ b/spec/models/concerns/manual_inverse_association_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ManualInverseAssociation do
+RSpec.describe ManualInverseAssociation do
let(:model) do
Class.new(MergeRequest) do
belongs_to :manual_association, class_name: 'MergeRequestDiff', foreign_key: :latest_merge_request_diff_id
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 03fd1c69654..758b5aa2ce4 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mentionable do
+RSpec.describe Mentionable do
before do
stub_const('Example', Class.new)
Example.class_eval do
@@ -67,7 +67,7 @@ describe Mentionable do
end
end
-describe Issue, "Mentionable" do
+RSpec.describe Issue, "Mentionable" do
describe '#mentioned_users' do
let!(:user) { create(:user, username: 'stranger') }
let!(:user2) { create(:user, username: 'john') }
@@ -222,7 +222,7 @@ describe Issue, "Mentionable" do
end
end
-describe Commit, 'Mentionable' do
+RSpec.describe Commit, 'Mentionable' do
let(:project) { create(:project, :public, :repository) }
let(:commit) { project.commit }
@@ -291,7 +291,7 @@ describe Commit, 'Mentionable' do
end
end
-describe MergeRequest, 'Mentionable' do
+RSpec.describe MergeRequest, 'Mentionable' do
describe '#store_mentions!' do
it_behaves_like 'mentions in description', :merge_request
it_behaves_like 'mentions in notes', :merge_request do
@@ -312,7 +312,7 @@ describe MergeRequest, 'Mentionable' do
end
end
-describe Snippet, 'Mentionable' do
+RSpec.describe Snippet, 'Mentionable' do
describe '#store_mentions!' do
it_behaves_like 'mentions in description', :project_snippet
it_behaves_like 'mentions in notes', :project_snippet do
@@ -329,7 +329,7 @@ describe Snippet, 'Mentionable' do
end
end
-describe PersonalSnippet, 'Mentionable' do
+RSpec.describe PersonalSnippet, 'Mentionable' do
describe '#store_mentions!' do
it_behaves_like 'mentions in description', :personal_snippet
it_behaves_like 'mentions in notes', :personal_snippet do
@@ -346,7 +346,7 @@ describe PersonalSnippet, 'Mentionable' do
end
end
-describe DesignManagement::Design do
+RSpec.describe DesignManagement::Design do
describe '#store_mentions!' do
it_behaves_like 'mentions in notes', :design do
let(:note) { create(:diff_note_on_design) }
diff --git a/spec/models/concerns/milestoneable_spec.rb b/spec/models/concerns/milestoneable_spec.rb
index 0b19c0542ee..15352a1453c 100644
--- a/spec/models/concerns/milestoneable_spec.rb
+++ b/spec/models/concerns/milestoneable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Milestoneable do
+RSpec.describe Milestoneable do
let(:user) { create(:user) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 8c43a12aa15..58cd054efd5 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Milestone, 'Milestoneish' do
+RSpec.describe Milestone, 'Milestoneish' do
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb
index 5c8c5425ca7..bb7374bf46c 100644
--- a/spec/models/concerns/noteable_spec.rb
+++ b/spec/models/concerns/noteable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Noteable do
+RSpec.describe Noteable do
let!(:active_diff_note1) { create(:diff_note_on_merge_request) }
let(:project) { active_diff_note1.project }
subject { active_diff_note1.noteable }
@@ -262,4 +262,44 @@ describe Noteable do
end
end
end
+
+ describe "#has_any_diff_note_positions?" do
+ let(:source_branch) { "compare-with-merge-head-source" }
+ let(:target_branch) { "compare-with-merge-head-target" }
+ let(:merge_request) { create(:merge_request, source_branch: source_branch, target_branch: target_branch) }
+
+ let!(:note) do
+ path = "files/markdown/ruby-style-guide.md"
+
+ position = Gitlab::Diff::Position.new(
+ old_path: path,
+ new_path: path,
+ new_line: 508,
+ diff_refs: merge_request.diff_refs
+ )
+
+ create(:diff_note_on_merge_request, project: merge_request.project, position: position, noteable: merge_request)
+ end
+
+ before do
+ MergeRequests::MergeToRefService.new(merge_request.project, merge_request.author).execute(merge_request)
+ Discussions::CaptureDiffNotePositionsService.new(merge_request).execute
+ end
+
+ it "returns true when it has diff note positions" do
+ expect(merge_request.has_any_diff_note_positions?).to be(true)
+ end
+
+ it "returns false when it has notes but no diff note positions" do
+ DiffNotePosition.where(note: note).find_each(&:delete)
+
+ expect(merge_request.has_any_diff_note_positions?).to be(false)
+ end
+
+ it "returns false when it has no notes" do
+ merge_request.notes.find_each(&:destroy)
+
+ expect(merge_request.has_any_diff_note_positions?).to be(false)
+ end
+ end
end
diff --git a/spec/models/concerns/optionally_search_spec.rb b/spec/models/concerns/optionally_search_spec.rb
index e1eb4cf8cd2..c8e2e6da51f 100644
--- a/spec/models/concerns/optionally_search_spec.rb
+++ b/spec/models/concerns/optionally_search_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OptionallySearch do
+RSpec.describe OptionallySearch do
describe '.search' do
let(:model) do
Class.new do
diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb
index 3d5937c4fc6..3376e337dc9 100644
--- a/spec/models/concerns/participable_spec.rb
+++ b/spec/models/concerns/participable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Participable do
+RSpec.describe Participable do
let(:model) do
Class.new do
include Participable
diff --git a/spec/models/concerns/partitioned_table_spec.rb b/spec/models/concerns/partitioned_table_spec.rb
new file mode 100644
index 00000000000..3343b273ba2
--- /dev/null
+++ b/spec/models/concerns/partitioned_table_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe PartitionedTable do
+ describe '.partitioned_by' do
+ subject { my_class.partitioned_by(key, strategy: :monthly) }
+
+ let(:key) { :foo }
+
+ let(:my_class) do
+ Class.new do
+ include PartitionedTable
+ end
+ end
+
+ it 'assigns the MonthlyStrategy as the partitioning strategy' do
+ subject
+
+ expect(my_class.partitioning_strategy).to be_a(Gitlab::Database::Partitioning::MonthlyStrategy)
+ end
+
+ it 'passes the partitioning key to the strategy instance' do
+ subject
+
+ expect(my_class.partitioning_strategy.partitioning_key).to eq(key)
+ end
+
+ it 'registers itself with the PartitionCreator' do
+ expect(Gitlab::Database::Partitioning::PartitionCreator).to receive(:register).with(my_class)
+
+ subject
+ end
+ end
+end
diff --git a/spec/models/concerns/presentable_spec.rb b/spec/models/concerns/presentable_spec.rb
index 9db868dd348..871e122e409 100644
--- a/spec/models/concerns/presentable_spec.rb
+++ b/spec/models/concerns/presentable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Presentable do
+RSpec.describe Presentable do
let(:build) { Ci::Build.new }
describe '#present' do
diff --git a/spec/models/concerns/project_api_compatibility_spec.rb b/spec/models/concerns/project_api_compatibility_spec.rb
index f5722f88aac..7a69406cb71 100644
--- a/spec/models/concerns/project_api_compatibility_spec.rb
+++ b/spec/models/concerns/project_api_compatibility_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectAPICompatibility do
+RSpec.describe ProjectAPICompatibility do
let(:project) { create(:project) }
# git_strategy
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index 8346c4ad4cc..ba70ff563a8 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectFeaturesCompatibility do
+RSpec.describe ProjectFeaturesCompatibility do
let(:project) { create(:project) }
let(:features_enabled) { %w(issues wiki builds merge_requests snippets) }
let(:features) { features_enabled + %w(repository pages) }
diff --git a/spec/models/concerns/prometheus_adapter_spec.rb b/spec/models/concerns/prometheus_adapter_spec.rb
index fdc98ba74b8..e795e2b06cb 100644
--- a/spec/models/concerns/prometheus_adapter_spec.rb
+++ b/spec/models/concerns/prometheus_adapter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
+RSpec.describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
include PrometheusHelpers
include ReactiveCachingHelpers
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
index f63ad958ed3..750a5eba303 100644
--- a/spec/models/concerns/protected_ref_access_spec.rb
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProtectedRefAccess do
+RSpec.describe ProtectedRefAccess do
include ExternalAuthorizationServiceHelpers
subject(:protected_ref_access) do
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index cfca383e0b0..b12ad82920f 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ReactiveCaching, :use_clean_rails_memory_store_caching do
+RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do
include ExclusiveLeaseHelpers
include ReactiveCachingHelpers
@@ -285,38 +285,30 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
go!
end
- context 'when calculated object size exceeds default reactive_cache_hard_limit' do
- let(:calculation) { -> { 'a' * 2 * 1.megabyte } }
+ context 'when reactive_cache_hard_limit is set' do
+ let(:test_class) { Class.new(cache_class_test) { self.reactive_cache_hard_limit = 1.megabyte } }
+ let(:instance) { test_class.new(666, &calculation) }
+
+ context 'when cache size is over the overridden limit' do
+ let(:calculation) { -> { 'a' * 2 * 1.megabyte } }
- shared_examples 'ExceededReactiveCacheLimit' do
it 'raises ExceededReactiveCacheLimit exception and does not cache new data' do
expect { go! }.to raise_exception(ReactiveCaching::ExceededReactiveCacheLimit)
expect(read_reactive_cache(instance)).not_to eq(calculation.call)
end
- end
- context 'when reactive_cache_hard_limit feature flag is enabled' do
- it_behaves_like 'ExceededReactiveCacheLimit'
-
- context 'when reactive_cache_hard_limit is overridden' do
- let(:test_class) { Class.new(cache_class_test) { self.reactive_cache_hard_limit = 3.megabytes } }
- let(:instance) { test_class.new(666, &calculation) }
+ context 'when reactive_cache_limit_enabled? is overridden to return false' do
+ before do
+ allow(instance).to receive(:reactive_cache_limit_enabled?).and_return(false)
+ end
it_behaves_like 'successful cache'
-
- context 'when cache size is over the overridden limit' do
- let(:calculation) { -> { 'a' * 4 * 1.megabyte } }
-
- it_behaves_like 'ExceededReactiveCacheLimit'
- end
end
end
- context 'when reactive_cache_limit feature flag is disabled' do
- before do
- stub_feature_flags(reactive_cache_limit: false)
- end
+ context 'when cache size is within the overridden limit' do
+ let(:calculation) { -> { 'Smaller than 1Mb reactive_cache_hard_limit' } }
it_behaves_like 'successful cache'
end
@@ -377,7 +369,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
it { expect(subject.reactive_cache_refresh_interval).to be_a(ActiveSupport::Duration) }
it { expect(subject.reactive_cache_lifetime).to be_a(ActiveSupport::Duration) }
it { expect(subject.reactive_cache_key).to respond_to(:call) }
- it { expect(subject.reactive_cache_hard_limit).to be_a(Integer) }
+ it { expect(subject.reactive_cache_hard_limit).to be_nil }
it { expect(subject.reactive_cache_worker_finder).to respond_to(:call) }
end
end
diff --git a/spec/models/concerns/redactable_spec.rb b/spec/models/concerns/redactable_spec.rb
index 3f6a2e2410c..bb59e04adf1 100644
--- a/spec/models/concerns/redactable_spec.rb
+++ b/spec/models/concerns/redactable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Redactable do
+RSpec.describe Redactable do
before do
stub_commonmark_sourcepos_disabled
end
diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb
index 1cf6afcc167..c270f23defb 100644
--- a/spec/models/concerns/redis_cacheable_spec.rb
+++ b/spec/models/concerns/redis_cacheable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RedisCacheable do
+RSpec.describe RedisCacheable do
let(:model) do
Struct.new(:id, :attributes) do
def read_attribute(attribute)
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 95553fb13a6..c91ddfee944 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Discussion, ResolvableDiscussion do
+RSpec.describe Discussion, ResolvableDiscussion do
subject { described_class.new([first_note, second_note, third_note]) }
let(:first_note) { create(:discussion_note_on_merge_request) }
diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb
index 12e50ac807e..69c58a5cfe5 100644
--- a/spec/models/concerns/resolvable_note_spec.rb
+++ b/spec/models/concerns/resolvable_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Note, ResolvableNote do
+RSpec.describe Note, ResolvableNote do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index c891fdcb6b5..15d754861b2 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Group, 'Routable' do
+RSpec.describe Group, 'Routable' do
let!(:group) { create(:group, name: 'foo') }
describe 'Validations' do
@@ -164,7 +164,7 @@ describe Group, 'Routable' do
end
end
-describe Project, 'Routable' do
+RSpec.describe Project, 'Routable' do
describe '#full_path' do
let(:project) { build_stubbed(:project) }
diff --git a/spec/models/concerns/safe_url_spec.rb b/spec/models/concerns/safe_url_spec.rb
index e523e6a15e4..3d38c05bf11 100644
--- a/spec/models/concerns/safe_url_spec.rb
+++ b/spec/models/concerns/safe_url_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SafeUrl do
+RSpec.describe SafeUrl do
describe '#safe_url' do
let(:safe_url_test_class) do
Class.new do
diff --git a/spec/models/concerns/schedulable_spec.rb b/spec/models/concerns/schedulable_spec.rb
index 38ae2112e01..875c2d80e55 100644
--- a/spec/models/concerns/schedulable_spec.rb
+++ b/spec/models/concerns/schedulable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Schedulable do
+RSpec.describe Schedulable do
shared_examples 'before_save callback' do
it 'updates next_run_at' do
expect { object.save! }.to change { object.next_run_at }
diff --git a/spec/models/concerns/sha256_attribute_spec.rb b/spec/models/concerns/sha256_attribute_spec.rb
index 213723c2dcb..c247865d77f 100644
--- a/spec/models/concerns/sha256_attribute_spec.rb
+++ b/spec/models/concerns/sha256_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Sha256Attribute do
+RSpec.describe Sha256Attribute do
let(:model) { Class.new { include Sha256Attribute } }
before do
diff --git a/spec/models/concerns/sha_attribute_spec.rb b/spec/models/concerns/sha_attribute_spec.rb
index 0d4dbfb215e..50748efcda4 100644
--- a/spec/models/concerns/sha_attribute_spec.rb
+++ b/spec/models/concerns/sha_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ShaAttribute do
+RSpec.describe ShaAttribute do
let(:model) { Class.new { include ShaAttribute } }
before do
diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb
index a1fe5c0928d..bbfdaeec64c 100644
--- a/spec/models/concerns/sortable_spec.rb
+++ b/spec/models/concerns/sortable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Sortable do
+RSpec.describe Sortable do
describe '.order_by' do
let(:arel_table) { Group.arel_table }
let(:relation) { Group.all }
diff --git a/spec/models/concerns/spammable_spec.rb b/spec/models/concerns/spammable_spec.rb
index a8d27e174b7..d4fcb2e99eb 100644
--- a/spec/models/concerns/spammable_spec.rb
+++ b/spec/models/concerns/spammable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Spammable do
+RSpec.describe Spammable do
let(:issue) { create(:issue, description: 'Test Desc.') }
describe 'Associations' do
diff --git a/spec/models/concerns/stepable_spec.rb b/spec/models/concerns/stepable_spec.rb
index 51356c3eaf6..e442e4f0664 100644
--- a/spec/models/concerns/stepable_spec.rb
+++ b/spec/models/concerns/stepable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Stepable do
+RSpec.describe Stepable do
let(:described_class) do
Class.new do
include Stepable
diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb
index 5c0d1042e06..812f0a015f7 100644
--- a/spec/models/concerns/strip_attribute_spec.rb
+++ b/spec/models/concerns/strip_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe StripAttribute do
+RSpec.describe StripAttribute do
let(:milestone) { create(:milestone) }
describe ".strip_attributes" do
diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb
index f189cd7633c..2a43e748e58 100644
--- a/spec/models/concerns/subscribable_spec.rb
+++ b/spec/models/concerns/subscribable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Subscribable, 'Subscribable' do
+RSpec.describe Subscribable, 'Subscribable' do
let(:project) { create(:project) }
let(:resource) { create(:issue, project: project) }
let(:user_1) { create(:user) }
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 36eb8fdaba4..e0e764fc63c 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-shared_examples 'TokenAuthenticatable' do
+RSpec.shared_examples 'TokenAuthenticatable' do
describe 'dynamically defined methods' do
it { expect(described_class).to respond_to("find_by_#{token_field}") }
it { is_expected.to respond_to("ensure_#{token_field}") }
@@ -11,7 +11,7 @@ shared_examples 'TokenAuthenticatable' do
end
end
-describe User, 'TokenAuthenticatable' do
+RSpec.describe User, 'TokenAuthenticatable' do
let(:token_field) { :feed_token }
it_behaves_like 'TokenAuthenticatable'
@@ -23,7 +23,7 @@ describe User, 'TokenAuthenticatable' do
end
end
-describe ApplicationSetting, 'TokenAuthenticatable' do
+RSpec.describe ApplicationSetting, 'TokenAuthenticatable' do
let(:token_field) { :runners_registration_token }
let(:settings) { described_class.new }
@@ -100,7 +100,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
end
-describe PersonalAccessToken, 'TokenAuthenticatable' do
+RSpec.describe PersonalAccessToken, 'TokenAuthenticatable' do
shared_examples 'changes personal access token' do
it 'sets new token' do
subject
@@ -205,7 +205,7 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do
end
end
-describe Ci::Build, 'TokenAuthenticatable' do
+RSpec.describe Ci::Build, 'TokenAuthenticatable' do
let(:token_field) { :token }
let(:build) { FactoryBot.build(:ci_build) }
diff --git a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
index 7332da309d5..bccef9b9554 100644
--- a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe TokenAuthenticatableStrategies::Base do
+RSpec.describe TokenAuthenticatableStrategies::Base do
let(:instance) { double(:instance) }
let(:field) { double(:field) }
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index 70f41981b3b..f6b8cf7def4 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe TokenAuthenticatableStrategies::Encrypted do
+RSpec.describe TokenAuthenticatableStrategies::Encrypted do
let(:model) { double(:model) }
let(:instance) { double(:instance) }
diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/models/concerns/uniquify_spec.rb
index 9ba35702ba6..9b79e4d4154 100644
--- a/spec/models/concerns/uniquify_spec.rb
+++ b/spec/models/concerns/uniquify_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Uniquify do
+RSpec.describe Uniquify do
let(:uniquify) { described_class.new }
describe "#string" do
diff --git a/spec/models/concerns/usage_statistics_spec.rb b/spec/models/concerns/usage_statistics_spec.rb
index f99f0a13317..15ccd08eda9 100644
--- a/spec/models/concerns/usage_statistics_spec.rb
+++ b/spec/models/concerns/usage_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UsageStatistics do
+RSpec.describe UsageStatistics do
describe '.distinct_count_by' do
let_it_be(:issue_1) { create(:issue) }
let_it_be(:issue_2) { create(:issue) }
diff --git a/spec/models/concerns/where_composite_spec.rb b/spec/models/concerns/where_composite_spec.rb
index 1c0951d90d0..fb23e6bfe1d 100644
--- a/spec/models/concerns/where_composite_spec.rb
+++ b/spec/models/concerns/where_composite_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WhereComposite do
+RSpec.describe WhereComposite do
describe '.where_composite' do
let_it_be(:test_table_name) { "test_table_#{SecureRandom.hex(10)}" }
diff --git a/spec/models/concerns/x509_serial_number_attribute_spec.rb b/spec/models/concerns/x509_serial_number_attribute_spec.rb
index 18a1d85204c..88550823748 100644
--- a/spec/models/concerns/x509_serial_number_attribute_spec.rb
+++ b/spec/models/concerns/x509_serial_number_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe X509SerialNumberAttribute do
+RSpec.describe X509SerialNumberAttribute do
let(:model) { Class.new { include X509SerialNumberAttribute } }
before do
diff --git a/spec/models/container_registry/event_spec.rb b/spec/models/container_registry/event_spec.rb
index 54ff218f2a8..21a3ab5363a 100644
--- a/spec/models/container_registry/event_spec.rb
+++ b/spec/models/container_registry/event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ContainerRegistry::Event do
+RSpec.describe ContainerRegistry::Event do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create(:group, name: 'group') }
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index 4f23a905e93..953f92d103b 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ContainerRepository do
+RSpec.describe ContainerRepository do
let(:group) { create(:group, name: 'group') }
let(:project) { create(:project, path: 'test', group: group) }
diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
new file mode 100644
index 00000000000..2b569b6097d
--- /dev/null
+++ b/spec/models/custom_emoji_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe CustomEmoji do
+ describe 'Associations' do
+ it { is_expected.to belong_to(:namespace) }
+ it { is_expected.to have_db_column(:file) }
+ it { is_expected.to validate_length_of(:name).is_at_most(36) }
+ it { is_expected.to validate_presence_of(:name) }
+ end
+
+ describe 'exclusion of duplicated emoji' do
+ let(:emoji_name) { Gitlab::Emoji.emojis_names.sample }
+
+ it 'disallows emoji names of built-in emoji' do
+ new_emoji = build(:custom_emoji, name: emoji_name)
+
+ expect(new_emoji).not_to be_valid
+ expect(new_emoji.errors.messages).to eq(name: ["#{emoji_name} is already being used for another emoji"])
+ end
+
+ it 'disallows duplicate custom emoji names within namespace' do
+ old_emoji = create(:custom_emoji)
+ new_emoji = build(:custom_emoji, name: old_emoji.name, namespace: old_emoji.namespace)
+
+ expect(new_emoji).not_to be_valid
+ expect(new_emoji.errors.messages).to eq(name: ["has already been taken"])
+ end
+ end
+end
diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb
index f6ab8e0ece6..8900c49a662 100644
--- a/spec/models/cycle_analytics/code_spec.rb
+++ b/spec/models/cycle_analytics/code_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#code' do
+RSpec.describe 'CycleAnalytics#code' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb
index b4ab763e0e6..9372ef5f0e6 100644
--- a/spec/models/cycle_analytics/issue_spec.rb
+++ b/spec/models/cycle_analytics/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#issue' do
+RSpec.describe 'CycleAnalytics#issue' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb
index 6765b2e2cbc..364694a11e1 100644
--- a/spec/models/cycle_analytics/plan_spec.rb
+++ b/spec/models/cycle_analytics/plan_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#plan' do
+RSpec.describe 'CycleAnalytics#plan' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb
index 2f2bcd63acd..cf4d57d6b73 100644
--- a/spec/models/cycle_analytics/production_spec.rb
+++ b/spec/models/cycle_analytics/production_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#production' do
+RSpec.describe 'CycleAnalytics#production' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/project_level_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb
index bb296351a29..c2d421c03d8 100644
--- a/spec/models/cycle_analytics/project_level_spec.rb
+++ b/spec/models/cycle_analytics/project_level_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CycleAnalytics::ProjectLevel do
+RSpec.describe CycleAnalytics::ProjectLevel do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:from_date) { 10.days.ago }
let_it_be(:user) { project.owner }
diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb
index 25e8f1441d3..6ebbcebd71d 100644
--- a/spec/models/cycle_analytics/review_spec.rb
+++ b/spec/models/cycle_analytics/review_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#review' do
+RSpec.describe 'CycleAnalytics#review' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb
index effbc7056cc..024625d229f 100644
--- a/spec/models/cycle_analytics/staging_spec.rb
+++ b/spec/models/cycle_analytics/staging_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#staging' do
+RSpec.describe 'CycleAnalytics#staging' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb
index 7e7ba4d9994..7010d69f8a4 100644
--- a/spec/models/cycle_analytics/test_spec.rb
+++ b/spec/models/cycle_analytics/test_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'CycleAnalytics#test' do
+RSpec.describe 'CycleAnalytics#test' do
extend CycleAnalyticsHelpers::TestGeneration
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index ec6cfb6b826..00114a94b56 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeployKey, :mailer do
+RSpec.describe DeployKey, :mailer do
describe "Associations" do
it { is_expected.to have_many(:deploy_keys_projects) }
it { is_expected.to have_many(:projects) }
diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb
index 1dbae78a01d..7dd4d3129de 100644
--- a/spec/models/deploy_keys_project_spec.rb
+++ b/spec/models/deploy_keys_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeployKeysProject do
+RSpec.describe DeployKeysProject do
describe "Associations" do
it { is_expected.to belong_to(:deploy_key) }
it { is_expected.to belong_to(:project) }
@@ -13,6 +13,21 @@ describe DeployKeysProject do
it { is_expected.to validate_presence_of(:deploy_key) }
end
+ describe '.with_deploy_keys' do
+ subject(:scoped_query) { described_class.with_deploy_keys.last }
+
+ it 'includes deploy_keys in query' do
+ project = create(:project)
+ create(:deploy_keys_project, project: project, deploy_key: create(:deploy_key))
+
+ includes_query_count = ActiveRecord::QueryRecorder.new { scoped_query }.count
+ deploy_key_query_count = ActiveRecord::QueryRecorder.new { scoped_query.deploy_key }.count
+
+ expect(includes_query_count).to eq(2)
+ expect(deploy_key_query_count).to eq(0)
+ end
+ end
+
describe "Destroying" do
let(:project) { create(:project) }
subject { create(:deploy_keys_project, project: project) }
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 819e2850644..9fd3751be13 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeployToken do
+RSpec.describe DeployToken do
subject(:deploy_token) { create(:deploy_token) }
it { is_expected.to have_many :project_deploy_tokens }
diff --git a/spec/models/deployment_cluster_spec.rb b/spec/models/deployment_cluster_spec.rb
index 8bb09e9a510..dc9cbe4b082 100644
--- a/spec/models/deployment_cluster_spec.rb
+++ b/spec/models/deployment_cluster_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeploymentCluster do
+RSpec.describe DeploymentCluster do
let(:cluster) { create(:cluster) }
let(:deployment) { create(:deployment) }
let(:kubernetes_namespace) { 'an-example-namespace' }
diff --git a/spec/models/deployment_merge_request_spec.rb b/spec/models/deployment_merge_request_spec.rb
index fd5be52d47c..29834691fa4 100644
--- a/spec/models/deployment_merge_request_spec.rb
+++ b/spec/models/deployment_merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeploymentMergeRequest do
+RSpec.describe DeploymentMergeRequest do
let(:mr) { create(:merge_request, :merged) }
let(:deployment) { create(:deployment, :success, project: project) }
let(:project) { mr.project }
diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb
index 5a4ae0bbe79..d0474777eb7 100644
--- a/spec/models/deployment_metrics_spec.rb
+++ b/spec/models/deployment_metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DeploymentMetrics do
+RSpec.describe DeploymentMetrics do
describe '#has_metrics?' do
subject { described_class.new(deployment.project, deployment).has_metrics? }
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index ac2a4c9877d..b320390711e 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Deployment do
+RSpec.describe Deployment do
subject { build(:deployment) }
it { is_expected.to belong_to(:project).required }
diff --git a/spec/models/description_version_spec.rb b/spec/models/description_version_spec.rb
index 5ec34c0cde4..7c094f7a0a0 100644
--- a/spec/models/description_version_spec.rb
+++ b/spec/models/description_version_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DescriptionVersion do
+RSpec.describe DescriptionVersion do
describe 'associations' do
it { is_expected.to belong_to :issue }
it { is_expected.to belong_to :merge_request }
diff --git a/spec/models/design_management/action_spec.rb b/spec/models/design_management/action_spec.rb
index 753c31b1549..59c58191718 100644
--- a/spec/models/design_management/action_spec.rb
+++ b/spec/models/design_management/action_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe DesignManagement::Action do
+RSpec.describe DesignManagement::Action do
describe 'relations' do
it { is_expected.to belong_to(:design) }
it { is_expected.to belong_to(:version) }
diff --git a/spec/models/design_management/design_action_spec.rb b/spec/models/design_management/design_action_spec.rb
index da4ad41dfcb..958b1dd9124 100644
--- a/spec/models/design_management/design_action_spec.rb
+++ b/spec/models/design_management/design_action_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe DesignManagement::DesignAction do
+RSpec.describe DesignManagement::DesignAction do
describe 'validations' do
describe 'the design' do
let(:fail_validation) { raise_error(/design/i) }
diff --git a/spec/models/design_management/design_at_version_spec.rb b/spec/models/design_management/design_at_version_spec.rb
index f6fa8df243c..2c640ee5c2c 100644
--- a/spec/models/design_management/design_at_version_spec.rb
+++ b/spec/models/design_management/design_at_version_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DesignManagement::DesignAtVersion do
+RSpec.describe DesignManagement::DesignAtVersion do
include DesignManagementTestHelpers
let_it_be(:issue, reload: true) { create(:issue) }
diff --git a/spec/models/design_management/design_collection_spec.rb b/spec/models/design_management/design_collection_spec.rb
index bd48f742042..c5e290da759 100644
--- a/spec/models/design_management/design_collection_spec.rb
+++ b/spec/models/design_management/design_collection_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe DesignManagement::DesignCollection do
+RSpec.describe DesignManagement::DesignCollection do
include DesignManagementTestHelpers
let_it_be(:issue, reload: true) { create(:issue) }
diff --git a/spec/models/design_management/design_spec.rb b/spec/models/design_management/design_spec.rb
index bc1f54f057e..345147390c0 100644
--- a/spec/models/design_management/design_spec.rb
+++ b/spec/models/design_management/design_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DesignManagement::Design do
+RSpec.describe DesignManagement::Design do
include DesignManagementTestHelpers
let_it_be(:issue) { create(:issue) }
diff --git a/spec/models/design_management/repository_spec.rb b/spec/models/design_management/repository_spec.rb
index 996316eeec9..0115e0c139c 100644
--- a/spec/models/design_management/repository_spec.rb
+++ b/spec/models/design_management/repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DesignManagement::Repository do
+RSpec.describe DesignManagement::Repository do
let(:project) { create(:project) }
let(:repository) { described_class.new(project) }
diff --git a/spec/models/design_management/version_spec.rb b/spec/models/design_management/version_spec.rb
index ab6958ea94a..cd52f4129dc 100644
--- a/spec/models/design_management/version_spec.rb
+++ b/spec/models/design_management/version_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe DesignManagement::Version do
+RSpec.describe DesignManagement::Version do
let_it_be(:issue) { create(:issue) }
describe 'relations' do
diff --git a/spec/models/design_user_mention_spec.rb b/spec/models/design_user_mention_spec.rb
index 03c77c73c8d..944a82c5edf 100644
--- a/spec/models/design_user_mention_spec.rb
+++ b/spec/models/design_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DesignUserMention do
+RSpec.describe DesignUserMention do
describe 'associations' do
it { is_expected.to belong_to(:design) }
it { is_expected.to belong_to(:note) }
diff --git a/spec/models/dev_ops_score/metric_spec.rb b/spec/models/dev_ops_score/metric_spec.rb
index 89212d5ca26..60001d0667d 100644
--- a/spec/models/dev_ops_score/metric_spec.rb
+++ b/spec/models/dev_ops_score/metric_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DevOpsScore::Metric do
+RSpec.describe DevOpsScore::Metric do
let(:conv_dev_index) { create(:dev_ops_score_metric) }
describe '#percentage_score' do
diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb
index cfeb4382927..26b311fe629 100644
--- a/spec/models/diff_discussion_spec.rb
+++ b/spec/models/diff_discussion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiffDiscussion do
+RSpec.describe DiffDiscussion do
include RepoHelpers
subject { described_class.new([diff_note]) }
diff --git a/spec/models/diff_note_position_spec.rb b/spec/models/diff_note_position_spec.rb
index d93e0af5526..aa9e6b4e824 100644
--- a/spec/models/diff_note_position_spec.rb
+++ b/spec/models/diff_note_position_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiffNotePosition, type: :model do
+RSpec.describe DiffNotePosition, type: :model do
describe '.create_or_update_by' do
context 'when a diff note' do
let(:note) { create(:diff_note_on_merge_request) }
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 8bfe2ac7a6c..8a6176bf045 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiffNote do
+RSpec.describe DiffNote do
include RepoHelpers
let_it_be(:merge_request) { create(:merge_request) }
diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb
index 0a1c4c5560e..57c62788ee9 100644
--- a/spec/models/diff_viewer/base_spec.rb
+++ b/spec/models/diff_viewer/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiffViewer::Base do
+RSpec.describe DiffViewer::Base do
include FakeBlobHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb
index 0a14eae26f3..686dd1249be 100644
--- a/spec/models/diff_viewer/server_side_spec.rb
+++ b/spec/models/diff_viewer/server_side_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DiffViewer::ServerSide do
+RSpec.describe DiffViewer::ServerSide do
let_it_be(:project) { create(:project, :repository) }
let(:commit) { project.commit_by(oid: '570e7b2abdd848b95f2f578043fc23bd6f6fd24d') }
let!(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') }
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 950bdec4d00..021940be0c2 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Discussion do
+RSpec.describe Discussion do
subject { described_class.new([first_note, second_note, third_note]) }
let(:first_note) { create(:diff_note_on_merge_request) }
diff --git a/spec/models/draft_note_spec.rb b/spec/models/draft_note_spec.rb
index b880d3c5b97..64b06bf5c8f 100644
--- a/spec/models/draft_note_spec.rb
+++ b/spec/models/draft_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DraftNote do
+RSpec.describe DraftNote do
include RepoHelpers
let(:project) { create(:project, :repository) }
diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb
index f7b194abcee..ffdc621dd4c 100644
--- a/spec/models/email_spec.rb
+++ b/spec/models/email_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Email do
+RSpec.describe Email do
describe 'modules' do
subject { described_class }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index b93da518b68..c449a3c3c47 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Environment, :use_clean_rails_memory_store_caching do
+RSpec.describe Environment, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
using RSpec::Parameterized::TableSyntax
include RepoHelpers
@@ -18,6 +18,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to belong_to(:project).required }
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:metrics_dashboard_annotations) }
+ it { is_expected.to have_many(:alert_management_alerts) }
it { is_expected.to delegate_method(:stop_action).to(:last_deployment) }
it { is_expected.to delegate_method(:manual_actions).to(:last_deployment) }
@@ -847,6 +848,20 @@ describe Environment, :use_clean_rails_memory_store_caching do
subject { environment.calculate_reactive_cache }
+ it 'overrides default reactive_cache_hard_limit to 10 Mb' do
+ expect(described_class.reactive_cache_hard_limit).to eq(10.megabyte)
+ end
+
+ it 'overrides reactive_cache_limit_enabled? with a FF' do
+ environment_with_enabled_ff = FactoryBot.build(:environment)
+ environment_with_disabled_ff = FactoryBot.build(:environment)
+
+ stub_feature_flags(reactive_caching_limit_environment: environment_with_enabled_ff.project)
+
+ expect(environment_with_enabled_ff.send(:reactive_cache_limit_enabled?)).to be_truthy
+ expect(environment_with_disabled_ff.send(:reactive_cache_limit_enabled?)).to be_falsey
+ end
+
it 'returns cache data from the deployment platform' do
expect(environment.deployment_platform).to receive(:calculate_reactive_cache_for)
.with(environment).and_return(pods: %w(pod1 pod2))
diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb
index 10283b54796..7eefb8f714a 100644
--- a/spec/models/environment_status_spec.rb
+++ b/spec/models/environment_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe EnvironmentStatus do
+RSpec.describe EnvironmentStatus do
include ProjectForksHelper
let(:deployment) { create(:deployment, :succeed, :review_app) }
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index b564c48a9c1..72ed11f6c74 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ErrorTracking::ProjectErrorTrackingSetting do
+RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
include ReactiveCachingHelpers
include Gitlab::Routing
diff --git a/spec/models/event_collection_spec.rb b/spec/models/event_collection_spec.rb
index 6d1954700bf..aca2a8c3a2f 100644
--- a/spec/models/event_collection_spec.rb
+++ b/spec/models/event_collection_spec.rb
@@ -2,7 +2,9 @@
require 'spec_helper'
-describe EventCollection do
+RSpec.describe EventCollection do
+ include DesignManagementTestHelpers
+
describe '#to_a' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project_empty_repo, group: group) }
@@ -10,6 +12,10 @@ describe EventCollection do
let_it_be(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request) }
+ before do
+ enable_design_management
+ end
+
context 'with project events' do
let_it_be(:push_event_payloads) do
Array.new(9) do
@@ -21,11 +27,13 @@ describe EventCollection do
let_it_be(:merge_request_events) { create_list(:event, 10, :commented, project: project, target: merge_request) }
let_it_be(:closed_issue_event) { create(:closed_issue_event, project: project, author: user) }
let_it_be(:wiki_page_event) { create(:wiki_page_event, project: project) }
+ let_it_be(:design_event) { create(:design_event, project: project) }
let(:push_events) { push_event_payloads.map(&:event) }
it 'returns an Array of events', :aggregate_failures do
most_recent_20_events = [
wiki_page_event,
+ design_event,
closed_issue_event,
*push_events,
*merge_request_events
@@ -36,40 +44,23 @@ describe EventCollection do
expect(events).to match_array(most_recent_20_events)
end
- context 'the wiki_events feature flag is disabled' do
- before do
- stub_feature_flags(wiki_events: false)
- end
-
- it 'omits the wiki page events when using to_a' do
- events = described_class.new(projects).to_a
-
- expect(events).not_to include(wiki_page_event)
- end
-
- it 'omits the wiki page events when using all_project_events' do
- events = described_class.new(projects).all_project_events
+ it 'includes the wiki page events when using to_a' do
+ events = described_class.new(projects).to_a
- expect(events).not_to include(wiki_page_event)
- end
+ expect(events).to include(wiki_page_event)
end
- context 'the wiki_events feature flag is enabled' do
- before do
- stub_feature_flags(wiki_events: true)
- end
-
- it 'includes the wiki page events when using to_a' do
- events = described_class.new(projects).to_a
+ it 'includes the design events' do
+ collection = described_class.new(projects)
- expect(events).to include(wiki_page_event)
- end
+ expect(collection.to_a).to include(design_event)
+ expect(collection.all_project_events).to include(design_event)
+ end
- it 'includes the wiki page events when using all_project_events' do
- events = described_class.new(projects).all_project_events
+ it 'includes the wiki page events when using all_project_events' do
+ events = described_class.new(projects).all_project_events
- expect(events).to include(wiki_page_event)
- end
+ expect(events).to include(wiki_page_event)
end
it 'applies a limit to the number of events' do
@@ -81,7 +72,7 @@ describe EventCollection do
it 'can paginate through events' do
events = described_class.new(projects, offset: 20).to_a
- expect(events.length).to eq(1)
+ expect(events.length).to eq(2)
end
it 'returns an empty Array when crossing the maximum page number' do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 14066b1e9d2..96baeab6809 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Event do
+RSpec.describe Event do
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:target) }
@@ -643,15 +643,6 @@ describe Event do
end
end
- describe '.not_design' do
- it 'does not contain the design events' do
- non_design_events = events.reject(&:design?)
-
- expect(events).not_to match_array(non_design_events)
- expect(described_class.not_design).to match_array(non_design_events)
- end
- end
-
describe '.for_wiki_page' do
it 'only contains the wiki page events' do
wiki_events = events.select(&:wiki_page?)
@@ -661,15 +652,6 @@ describe Event do
end
end
- describe '.not_wiki_page' do
- it 'does not contain the wiki page events' do
- non_wiki_events = events.reject(&:wiki_page?)
-
- expect(events).not_to match_array(non_wiki_events)
- expect(described_class.not_wiki_page).to match_array(non_wiki_events)
- end
- end
-
describe '.for_wiki_meta' do
it 'finds events for a given wiki page metadata object' do
event = events.select(&:wiki_page?).first
diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb
index b8d85d49b07..47b13ff50cf 100644
--- a/spec/models/external_issue_spec.rb
+++ b/spec/models/external_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ExternalIssue do
+RSpec.describe ExternalIssue do
let(:project) { double('project', id: 1, to_reference: 'namespace1/project1') }
let(:issue) { described_class.new('EXT-1234', project) }
diff --git a/spec/models/external_pull_request_spec.rb b/spec/models/external_pull_request_spec.rb
index e85d5b2f6c7..e0822fc177a 100644
--- a/spec/models/external_pull_request_spec.rb
+++ b/spec/models/external_pull_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ExternalPullRequest do
+RSpec.describe ExternalPullRequest do
let(:project) { create(:project) }
let(:source_branch) { 'the-branch' }
let(:status) { :open }
diff --git a/spec/models/fork_network_member_spec.rb b/spec/models/fork_network_member_spec.rb
index d7a0dd5be65..b34eb7964ca 100644
--- a/spec/models/fork_network_member_spec.rb
+++ b/spec/models/fork_network_member_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ForkNetworkMember do
+RSpec.describe ForkNetworkMember do
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:fork_network) }
diff --git a/spec/models/fork_network_spec.rb b/spec/models/fork_network_spec.rb
index 5ec0f8d6b02..c2ef1fdcb5f 100644
--- a/spec/models/fork_network_spec.rb
+++ b/spec/models/fork_network_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ForkNetwork do
+RSpec.describe ForkNetwork do
include ProjectForksHelper
describe '#add_root_as_member' do
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index c8ed898122b..6fe5a1407a9 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GenericCommitStatus do
+RSpec.describe GenericCommitStatus do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:external_url) { 'http://example.gitlab.com/status' }
diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb
index b9c914e2506..7ecde04e3df 100644
--- a/spec/models/gpg_key_spec.rb
+++ b/spec/models/gpg_key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GpgKey do
+RSpec.describe GpgKey do
describe "associations" do
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:subkeys) }
diff --git a/spec/models/gpg_key_subkey_spec.rb b/spec/models/gpg_key_subkey_spec.rb
index 5f80cc02924..c1d9e2bde43 100644
--- a/spec/models/gpg_key_subkey_spec.rb
+++ b/spec/models/gpg_key_subkey_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GpgKeySubkey do
+RSpec.describe GpgKeySubkey do
subject { build(:gpg_key_subkey) }
describe 'associations' do
diff --git a/spec/models/grafana_integration_spec.rb b/spec/models/grafana_integration_spec.rb
index 662e8b1dd61..79f102919ac 100644
--- a/spec/models/grafana_integration_spec.rb
+++ b/spec/models/grafana_integration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GrafanaIntegration do
+RSpec.describe GrafanaIntegration do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/group_custom_attribute_spec.rb b/spec/models/group_custom_attribute_spec.rb
index 7d60c74b62b..1d8afa71377 100644
--- a/spec/models/group_custom_attribute_spec.rb
+++ b/spec/models/group_custom_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupCustomAttribute do
+RSpec.describe GroupCustomAttribute do
describe 'assocations' do
it { is_expected.to belong_to(:group) }
end
diff --git a/spec/models/group_deploy_key_spec.rb b/spec/models/group_deploy_key_spec.rb
index 3ba56c7e504..3fe71cc4699 100644
--- a/spec/models/group_deploy_key_spec.rb
+++ b/spec/models/group_deploy_key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupDeployKey do
+RSpec.describe GroupDeployKey do
it { is_expected.to validate_presence_of(:user) }
it 'is of type DeployKey' do
diff --git a/spec/models/group_group_link_spec.rb b/spec/models/group_group_link_spec.rb
index 54e622b2f22..03cc9d7e64c 100644
--- a/spec/models/group_group_link_spec.rb
+++ b/spec/models/group_group_link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupGroupLink do
+RSpec.describe GroupGroupLink do
let_it_be(:group) { create(:group) }
let_it_be(:shared_group) { create(:group) }
let_it_be(:group_group_link) do
diff --git a/spec/models/group_import_state_spec.rb b/spec/models/group_import_state_spec.rb
index 9d9cb1e8391..4404ef64966 100644
--- a/spec/models/group_import_state_spec.rb
+++ b/spec/models/group_import_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupImportState do
+RSpec.describe GroupImportState do
describe 'validations' do
let_it_be(:group) { create(:group) }
diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb
index a3a5c631c3d..ec9244d5eb5 100644
--- a/spec/models/group_label_spec.rb
+++ b/spec/models/group_label_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupLabel do
+RSpec.describe GroupLabel do
describe 'relationships' do
it { is_expected.to belong_to(:group) }
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 93cb6d83489..4184f2d07cc 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Group do
+RSpec.describe Group do
let!(:group) { create(:group) }
describe 'associations' do
@@ -1313,4 +1313,231 @@ describe Group do
expect(groups).to contain_exactly(parent_group1, parent_group2, child_group1, child_group2, child_group3)
end
end
+
+ describe '#shared_runners_allowed?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:shared_runners_enabled, :allow_descendants_override, :expected_shared_runners_allowed) do
+ true | false | true
+ true | true | true
+ false | false | false
+ false | true | true
+ end
+
+ with_them do
+ let!(:group) { create(:group, shared_runners_enabled: shared_runners_enabled, allow_descendants_override_disabled_shared_runners: allow_descendants_override) }
+
+ it 'returns the expected result' do
+ expect(group.shared_runners_allowed?).to eq(expected_shared_runners_allowed)
+ end
+ end
+ end
+
+ describe '#parent_allows_shared_runners?' do
+ context 'when parent group is present' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:shared_runners_enabled, :allow_descendants_override, :expected_shared_runners_allowed) do
+ true | false | true
+ true | true | true
+ false | false | false
+ false | true | true
+ end
+
+ with_them do
+ let!(:parent_group) { create(:group, shared_runners_enabled: shared_runners_enabled, allow_descendants_override_disabled_shared_runners: allow_descendants_override) }
+ let!(:group) { create(:group, parent: parent_group) }
+
+ it 'returns the expected result' do
+ expect(group.parent_allows_shared_runners?).to eq(expected_shared_runners_allowed)
+ end
+ end
+ end
+
+ context 'when parent group is missing' do
+ let!(:group) { create(:group) }
+
+ it 'returns true' do
+ expect(group.parent_allows_shared_runners?).to be_truthy
+ end
+ end
+ end
+
+ describe '#parent_enabled_shared_runners?' do
+ subject { group.parent_enabled_shared_runners? }
+
+ context 'when parent group is present' do
+ context 'When shared Runners are disabled' do
+ let!(:parent_group) { create(:group, :shared_runners_disabled) }
+ let!(:group) { create(:group, parent: parent_group) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'When shared Runners are enabled' do
+ let!(:parent_group) { create(:group) }
+ let!(:group) { create(:group, parent: parent_group) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'when parent group is missing' do
+ let!(:group) { create(:group) }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe '#enable_shared_runners!' do
+ subject { group.enable_shared_runners! }
+
+ context 'group that its ancestors have shared runners disabled' do
+ let_it_be(:parent) { create(:group, :shared_runners_disabled) }
+ let_it_be(:group) { create(:group, :shared_runners_disabled, parent: parent) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: group) }
+
+ it 'raises error and does not enable shared Runners' do
+ expect { subject }
+ .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners disabled for the parent group')
+ .and not_change { parent.reload.shared_runners_enabled }
+ .and not_change { group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+
+ context 'root group with shared runners disabled' do
+ let_it_be(:group) { create(:group, :shared_runners_disabled) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }
+
+ it 'enables shared Runners only for itself' do
+ expect { subject }
+ .to change { group.reload.shared_runners_enabled }.from(false).to(true)
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+ end
+
+ describe '#disable_shared_runners!' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent: group) }
+ let_it_be(:sub_group_2) { create(:group, parent: group) }
+ let_it_be(:project) { create(:project, group: group, shared_runners_enabled: true) }
+ let_it_be(:project_2) { create(:project, group: sub_group_2, shared_runners_enabled: true) }
+
+ subject { group.disable_shared_runners! }
+
+ it 'disables shared Runners for all descendant groups and projects' do
+ expect { subject }
+ .to change { group.reload.shared_runners_enabled }.from(true).to(false)
+ .and not_change { group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners }
+ .and change { sub_group_2.reload.shared_runners_enabled }.from(true).to(false)
+ .and not_change { sub_group_2.reload.allow_descendants_override_disabled_shared_runners }
+ .and change { project.reload.shared_runners_enabled }.from(true).to(false)
+ .and change { project_2.reload.shared_runners_enabled }.from(true).to(false)
+ end
+ end
+
+ describe '#allow_descendants_override_disabled_shared_runners!' do
+ subject { group.allow_descendants_override_disabled_shared_runners! }
+
+ context 'top level group' do
+ let_it_be(:group) { create(:group, :shared_runners_disabled) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }
+
+ it 'enables allow descendants to override only for itself' do
+ expect { subject }
+ .to change { group.reload.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
+ .and not_change { group.reload.shared_runners_enabled }
+ .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+
+ context 'group that its ancestors have shared Runners disabled but allows to override' do
+ let_it_be(:parent) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners) }
+ let_it_be(:group) { create(:group, :shared_runners_disabled, parent: parent) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: group) }
+
+ it 'enables allow descendants to override' do
+ expect { subject }
+ .to not_change { parent.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { parent.reload.shared_runners_enabled }
+ .and change { group.reload.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
+ .and not_change { group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+
+ context 'when parent does not allow' do
+ let_it_be(:parent) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false ) }
+ let_it_be(:group) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent: parent) }
+
+ it 'raises error and does not allow descendants to override' do
+ expect { subject }
+ .to raise_error(described_class::UpdateSharedRunnersError, 'Group level shared Runners not allowed')
+ .and not_change { parent.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { parent.reload.shared_runners_enabled }
+ .and not_change { group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { group.reload.shared_runners_enabled }
+ end
+ end
+
+ context 'top level group that has shared Runners enabled' do
+ let_it_be(:group) { create(:group, shared_runners_enabled: true) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }
+
+ it 'raises error and does not change config' do
+ expect { subject }
+ .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners enabled')
+ .and not_change { group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { group.reload.shared_runners_enabled }
+ .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+ end
+
+ describe '#disallow_descendants_override_disabled_shared_runners!' do
+ subject { group.disallow_descendants_override_disabled_shared_runners! }
+
+ context 'top level group' do
+ let_it_be(:group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners ) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners, parent: group) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: true, group: sub_group) }
+
+ it 'disables allow project to override for descendants and disables project shared Runners' do
+ expect { subject }
+ .to not_change { group.reload.shared_runners_enabled }
+ .and change { group.reload.allow_descendants_override_disabled_shared_runners }.from(true).to(false)
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and change { sub_group.reload.allow_descendants_override_disabled_shared_runners }.from(true).to(false)
+ .and change { project.reload.shared_runners_enabled }.from(true).to(false)
+ end
+ end
+
+ context 'top level group that has shared Runners enabled' do
+ let_it_be(:group) { create(:group, shared_runners_enabled: true) }
+ let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }
+
+ it 'results error and does not change config' do
+ expect { subject }
+ .to raise_error(described_class::UpdateSharedRunnersError, 'Shared Runners enabled')
+ .and not_change { group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { group.reload.shared_runners_enabled }
+ .and not_change { sub_group.reload.allow_descendants_override_disabled_shared_runners }
+ .and not_change { sub_group.reload.shared_runners_enabled }
+ .and not_change { project.reload.shared_runners_enabled }
+ end
+ end
+ end
end
diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb
index 57eb077031c..975b64cb855 100644
--- a/spec/models/guest_spec.rb
+++ b/spec/models/guest_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Guest do
+RSpec.describe Guest do
let_it_be(:public_project, reload: true) { create(:project, :public) }
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:internal_project) { create(:project, :internal) }
diff --git a/spec/models/hooks/active_hook_filter_spec.rb b/spec/models/hooks/active_hook_filter_spec.rb
index 1249c793f7f..1f693ce9fde 100644
--- a/spec/models/hooks/active_hook_filter_spec.rb
+++ b/spec/models/hooks/active_hook_filter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ActiveHookFilter do
+RSpec.describe ActiveHookFilter do
subject(:filter) { described_class.new(hook) }
describe '#matches?' do
diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index ccf8171049d..69fbc4c3b4f 100644
--- a/spec/models/hooks/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectHook do
+RSpec.describe ProjectHook do
describe 'associations' do
it { is_expected.to belong_to :project }
end
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 936c2fbad27..f7045d7ac5e 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ServiceHook do
+RSpec.describe ServiceHook do
describe 'associations' do
it { is_expected.to belong_to :service }
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 2e836c19e3c..e56d08c1847 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe SystemHook do
+RSpec.describe SystemHook do
context 'default attributes' do
let(:system_hook) { build(:system_hook) }
diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb
index 128601794cf..8dd9cf9e84a 100644
--- a/spec/models/hooks/web_hook_log_spec.rb
+++ b/spec/models/hooks/web_hook_log_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WebHookLog do
+RSpec.describe WebHookLog do
it { is_expected.to belong_to(:web_hook) }
it { is_expected.to serialize(:request_headers).as(Hash) }
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 025c11d6407..3fc1ad6eb0d 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WebHook do
+RSpec.describe WebHook do
let(:hook) { build(:project_hook) }
describe 'associations' do
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 9f120775a3c..696d33b7beb 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Identity do
+RSpec.describe Identity do
describe 'relations' do
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/import_export_upload_spec.rb b/spec/models/import_export_upload_spec.rb
index 18a714f4d98..46a611852ab 100644
--- a/spec/models/import_export_upload_spec.rb
+++ b/spec/models/import_export_upload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ImportExportUpload do
+RSpec.describe ImportExportUpload do
subject { described_class.new(project: create(:project)) }
shared_examples 'stores the Import/Export file' do |method|
diff --git a/spec/models/import_failure_spec.rb b/spec/models/import_failure_spec.rb
index d286a4ad314..cdef125e890 100644
--- a/spec/models/import_failure_spec.rb
+++ b/spec/models/import_failure_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ImportFailure do
+RSpec.describe ImportFailure do
describe 'Scopes' do
let_it_be(:project) { create(:project) }
let_it_be(:correlation_id) { 'ABC' }
diff --git a/spec/models/incident_management/project_incident_management_setting_spec.rb b/spec/models/incident_management/project_incident_management_setting_spec.rb
index ac3f97e2d89..effd89e970c 100644
--- a/spec/models/incident_management/project_incident_management_setting_spec.rb
+++ b/spec/models/incident_management/project_incident_management_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IncidentManagement::ProjectIncidentManagementSetting do
+RSpec.describe IncidentManagement::ProjectIncidentManagementSetting do
let_it_be(:project) { create(:project, :repository, create_templates: :issue) }
describe 'Associations' do
@@ -108,4 +108,42 @@ describe IncidentManagement::ProjectIncidentManagementSetting do
it_behaves_like 'no content'
end
end
+
+ describe '#pagerduty_token' do
+ let(:active) { true }
+
+ subject do
+ create(:project_incident_management_setting, project: project, pagerduty_active: active, pagerduty_token: token)
+ end
+
+ context 'when token already set' do
+ let(:token) { SecureRandom.hex }
+
+ it 'reads the token' do
+ expect(subject.pagerduty_token).to eq(token)
+ expect(subject.encrypted_pagerduty_token).not_to be_nil
+ expect(subject.encrypted_pagerduty_token_iv).not_to be_nil
+ end
+ end
+
+ context 'when not set' do
+ let(:token) { nil }
+
+ context 'when PagerDuty webhook is active' do
+ it 'generates a token before validation' do
+ expect(subject).to be_valid
+ expect(subject.pagerduty_token).to match(/\A\h{32}\z/)
+ end
+ end
+
+ context 'when PagerDuty webhook is not active' do
+ let(:active) { false }
+
+ it 'does not generate a token before validation' do
+ expect(subject).to be_valid
+ expect(subject.pagerduty_token).to be_nil
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 747e9dc2faa..383e548c324 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe InstanceConfiguration do
+RSpec.describe InstanceConfiguration do
context 'without cache' do
describe '#settings' do
describe '#ssh_algorithms_hashes' do
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index 3042fd15a7b..87ba0f3f7e6 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -3,8 +3,9 @@
require 'spec_helper'
RSpec.describe Integration do
- let(:project_1) { create(:project) }
- let(:project_2) { create(:project) }
+ let!(:project_1) { create(:project) }
+ let!(:project_2) { create(:project) }
+ let!(:project_3) { create(:project) }
let(:instance_integration) { create(:jira_service, :instance) }
before do
@@ -18,4 +19,10 @@ RSpec.describe Integration do
expect(Project.with_custom_integration_for(instance_integration)).to contain_exactly(project_2)
end
end
+
+ describe '#ids_without_integration' do
+ it 'returns projects ids without an integration' do
+ expect(Project.ids_without_integration(instance_integration, 100)).to contain_exactly(project_3.id)
+ end
+ end
end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 0dfb59cf43a..751e8724872 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe InternalId do
+RSpec.describe InternalId do
let(:project) { create(:project) }
let(:usage) { :issues }
let(:issue) { build(:issue, project: project) }
diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb
index dc22d26e2f9..966e4321378 100644
--- a/spec/models/issue/metrics_spec.rb
+++ b/spec/models/issue/metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Issue::Metrics do
+RSpec.describe Issue::Metrics do
let(:project) { create(:project) }
subject { create(:issue, project: project) }
diff --git a/spec/models/issue_assignee_spec.rb b/spec/models/issue_assignee_spec.rb
index 2d59ba15101..df8e91cd133 100644
--- a/spec/models/issue_assignee_spec.rb
+++ b/spec/models/issue_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IssueAssignee do
+RSpec.describe IssueAssignee do
let(:issue) { create(:issue) }
subject { issue.issue_assignees.build(assignee: create(:user)) }
@@ -15,4 +15,37 @@ describe IssueAssignee do
describe 'validations' do
it { is_expected.to validate_uniqueness_of(:assignee).scoped_to(:issue_id) }
end
+
+ describe 'scopes' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:project_issue) { create(:issue, project: project, assignee_ids: [user.id]) }
+
+ before do
+ issue.update!(assignee_ids: [user.id])
+ end
+
+ context 'in_projects' do
+ it 'returns issue assignees for given project' do
+ expect(IssueAssignee.count).to eq 2
+
+ assignees = IssueAssignee.in_projects([project])
+
+ expect(assignees.count).to eq 1
+ expect(assignees.first.user_id).to eq project_issue.issue_assignees.first.user_id
+ expect(assignees.first.issue_id).to eq project_issue.issue_assignees.first.issue_id
+ end
+ end
+
+ context 'on_issues' do
+ it 'returns issue assignees for given issues' do
+ expect(IssueAssignee.count).to eq 2
+
+ assignees = IssueAssignee.on_issues([project_issue])
+
+ expect(assignees.count).to eq 1
+ expect(assignees.first.issue_id).to eq project_issue.issue_assignees.first.issue_id
+ end
+ end
+ end
end
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
index 7fc635f100f..d67bd8debce 100644
--- a/spec/models/issue_collection_spec.rb
+++ b/spec/models/issue_collection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IssueCollection do
+RSpec.describe IssueCollection do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:issue1) { create(:issue, project: project) }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 291cccd72db..80041d2e859 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Issue do
+RSpec.describe Issue do
include ExternalAuthorizationServiceHelpers
describe "Associations" do
@@ -95,29 +95,6 @@ describe Issue do
end
end
- describe 'locking' do
- using RSpec::Parameterized::TableSyntax
-
- where(:lock_version) do
- [
- [0],
- ["0"]
- ]
- end
-
- with_them do
- it 'works when an issue has a NULL lock_version' do
- issue = create(:issue)
-
- described_class.where(id: issue.id).update_all('lock_version = NULL')
-
- issue.update!(lock_version: lock_version, title: 'locking test')
-
- expect(issue.reload.title).to eq('locking test')
- end
- end
- end
-
describe '.simple_sorts' do
it 'includes all keys' do
expect(described_class.simple_sorts.keys).to include(
@@ -406,6 +383,22 @@ describe Issue do
end
end
+ describe '#from_service_desk?' do
+ subject { issue.from_service_desk? }
+
+ context 'when issue author is support bot' do
+ let(:issue) { create(:issue, author: ::User.support_bot) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when issue author is not support bot' do
+ let(:issue) { create(:issue) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
describe '#suggested_branch_name' do
let(:repository) { double }
@@ -1002,6 +995,16 @@ describe Issue do
end
end
+ describe '.service_desk' do
+ it 'returns the service desk issue' do
+ service_desk_issue = create(:issue, author: ::User.support_bot)
+ regular_issue = create(:issue)
+
+ expect(described_class.service_desk).to include(service_desk_issue)
+ expect(described_class.service_desk).not_to include(regular_issue)
+ end
+ end
+
it_behaves_like 'throttled touch' do
subject { create(:issue, updated_at: 1.hour.ago) }
end
diff --git a/spec/models/iteration_spec.rb b/spec/models/iteration_spec.rb
index ae14adf9106..ef638330208 100644
--- a/spec/models/iteration_spec.rb
+++ b/spec/models/iteration_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Iteration do
+RSpec.describe Iteration do
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
@@ -45,6 +45,14 @@ describe Iteration do
it { is_expected.to be_valid }
end
+ context 'when updated iteration dates overlap with its own dates' do
+ it 'is valid' do
+ existing_iteration.start_date = 5.days.from_now
+
+ expect(existing_iteration).to be_valid
+ end
+ end
+
context 'when dates overlap' do
context 'same group' do
context 'when start_date is in range' do
@@ -138,6 +146,25 @@ describe Iteration do
end
end
+ context 'time scopes' do
+ let_it_be(:project) { create(:project, :empty_repo) }
+ let_it_be(:iteration_1) { create(:iteration, :skip_future_date_validation, project: project, start_date: 3.days.ago, due_date: 1.day.from_now) }
+ let_it_be(:iteration_2) { create(:iteration, :skip_future_date_validation, project: project, start_date: 10.days.ago, due_date: 4.days.ago) }
+ let_it_be(:iteration_3) { create(:iteration, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) }
+
+ describe 'start_date_passed' do
+ it 'returns iterations where start_date is in the past but due_date is in the future' do
+ expect(described_class.start_date_passed).to contain_exactly(iteration_1)
+ end
+ end
+
+ describe 'due_date_passed' do
+ it 'returns iterations where due date is in the past' do
+ expect(described_class.due_date_passed).to contain_exactly(iteration_2)
+ end
+ end
+ end
+
describe '.within_timeframe' do
let_it_be(:now) { Time.current }
let_it_be(:project) { create(:project, :empty_repo) }
diff --git a/spec/models/jira_import_state_spec.rb b/spec/models/jira_import_state_spec.rb
index d2535636c63..e982b7353ba 100644
--- a/spec/models/jira_import_state_spec.rb
+++ b/spec/models/jira_import_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe JiraImportState do
+RSpec.describe JiraImportState do
describe "associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 8cdedbcdedf..1e14864676c 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Key, :mailer do
+RSpec.describe Key, :mailer do
describe "Associations" do
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb
index 7a179dcb419..a95481f3083 100644
--- a/spec/models/label_link_spec.rb
+++ b/spec/models/label_link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LabelLink do
+RSpec.describe LabelLink do
it { expect(build(:label_link)).to be_valid }
it { is_expected.to belong_to(:label) }
diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb
index 34560acfa9e..0bf202ce2b1 100644
--- a/spec/models/label_note_spec.rb
+++ b/spec/models/label_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LabelNote do
+RSpec.describe LabelNote do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:label) { create(:label, project: project) }
diff --git a/spec/models/label_priority_spec.rb b/spec/models/label_priority_spec.rb
index 1a93468290f..db961d5a4e6 100644
--- a/spec/models/label_priority_spec.rb
+++ b/spec/models/label_priority_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LabelPriority do
+RSpec.describe LabelPriority do
describe 'relationships' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:label) }
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index dc878c2d3c0..e1abfd9d8e5 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Label do
+RSpec.describe Label do
describe 'modules' do
it { is_expected.to include_module(Referable) }
it { is_expected.to include_module(Subscribable) }
diff --git a/spec/models/legacy_diff_discussion_spec.rb b/spec/models/legacy_diff_discussion_spec.rb
index 49ea319fbd1..4f90fe7d7f0 100644
--- a/spec/models/legacy_diff_discussion_spec.rb
+++ b/spec/models/legacy_diff_discussion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LegacyDiffDiscussion do
+RSpec.describe LegacyDiffDiscussion do
subject { create(:legacy_diff_note_on_merge_request).to_discussion }
describe '#reply_attributes' do
diff --git a/spec/models/lfs_download_object_spec.rb b/spec/models/lfs_download_object_spec.rb
index d7522fbb969..d1c323cd177 100644
--- a/spec/models/lfs_download_object_spec.rb
+++ b/spec/models/lfs_download_object_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LfsDownloadObject do
+RSpec.describe LfsDownloadObject do
let(:oid) { 'cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411' }
let(:link) { 'http://www.example.com' }
let(:size) { 1 }
diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb
index 0a47ded43fb..d3f79c7c7cf 100644
--- a/spec/models/lfs_file_lock_spec.rb
+++ b/spec/models/lfs_file_lock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LfsFileLock do
+RSpec.describe LfsFileLock do
let_it_be(:lfs_file_lock, reload: true) { create(:lfs_file_lock) }
subject { lfs_file_lock }
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index 09a64dabb08..36d45f17392 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LfsObject do
+RSpec.describe LfsObject do
context 'scopes' do
describe '.not_existing_in_project' do
it 'contains only lfs objects not linked to the project' do
diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb
index 31300828a43..71009a6f28f 100644
--- a/spec/models/lfs_objects_project_spec.rb
+++ b/spec/models/lfs_objects_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LfsObjectsProject do
+RSpec.describe LfsObjectsProject do
let_it_be(:project) { create(:project) }
subject do
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
index 7037277e580..515f728f515 100644
--- a/spec/models/license_template_spec.rb
+++ b/spec/models/license_template_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe LicenseTemplate do
+RSpec.describe LicenseTemplate do
describe '#content' do
it 'calls a proc exactly once if provided' do
content_proc = -> { 'bar' }
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index bc9124e73af..37158584062 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe List do
+RSpec.describe List do
it_behaves_like 'having unique enum values'
describe 'relationships' do
diff --git a/spec/models/list_user_preference_spec.rb b/spec/models/list_user_preference_spec.rb
index 10a7bf41f4e..fde0481e301 100644
--- a/spec/models/list_user_preference_spec.rb
+++ b/spec/models/list_user_preference_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ListUserPreference do
+RSpec.describe ListUserPreference do
let_it_be(:user) { create(:user) }
let_it_be(:list) { create(:list) }
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 7c40bb24b56..f155c240fb2 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Member do
+RSpec.describe Member do
include ExclusiveLeaseHelpers
using RSpec::Parameterized::TableSyntax
@@ -88,6 +88,28 @@ describe Member do
expect(child_member).to be_valid
end
end
+
+ context 'project bots' do
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+ let(:new_member) { build(:project_member, user_id: project_bot.id) }
+
+ context 'not a member of any group or project' do
+ it 'is valid' do
+ expect(new_member).to be_valid
+ end
+ end
+
+ context 'already member of a project' do
+ before do
+ unrelated_project = create(:project)
+ unrelated_project.add_maintainer(project_bot)
+ end
+
+ it 'is not valid' do
+ expect(new_member).not_to be_valid
+ end
+ end
+ end
end
describe 'Scopes & finders' do
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index fdb71b7ec7d..9af620e70a5 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -2,52 +2,20 @@
require 'spec_helper'
-describe GroupMember do
+RSpec.describe GroupMember do
context 'scopes' do
- shared_examples '.count_users_by_group_id' do
- it 'counts users by group ID' do
- user_1 = create(:user)
- user_2 = create(:user)
- group_1 = create(:group)
- group_2 = create(:group)
-
- group_1.add_owner(user_1)
- group_1.add_owner(user_2)
- group_2.add_owner(user_1)
-
- expect(described_class.count_users_by_group_id).to eq(group_1.id => 2,
- group_2.id => 1)
- end
- end
-
- describe '.count_users_by_group_id with optimized_count_users_by_group_id feature flag on' do
- before do
- stub_feature_flags(optimized_count_users_by_group_id: true)
- end
-
- it_behaves_like '.count_users_by_group_id'
-
- it 'does not JOIN users' do
- scope = described_class.all
- expect(scope).not_to receive(:joins).with(:user)
-
- scope.count_users_by_group_id
- end
- end
-
- describe '.count_users_by_group_id with optimized_count_users_by_group_id feature flag off' do
- before do
- stub_feature_flags(optimized_count_users_by_group_id: false)
- end
-
- it_behaves_like '.count_users_by_group_id'
-
- it 'does JOIN users' do
- scope = described_class.all
- expect(scope).to receive(:joins).with(:user).and_call_original
-
- scope.count_users_by_group_id
- end
+ it 'counts users by group ID' do
+ user_1 = create(:user)
+ user_2 = create(:user)
+ group_1 = create(:group)
+ group_2 = create(:group)
+
+ group_1.add_owner(user_1)
+ group_1.add_owner(user_2)
+ group_2.add_owner(user_1)
+
+ expect(described_class.count_users_by_group_id).to eq(group_1.id => 2,
+ group_2.id => 1)
end
describe '.of_ldap_type' do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index fdb9457b211..f25f8933184 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectMember do
+RSpec.describe ProjectMember do
describe 'associations' do
it { is_expected.to belong_to(:project).with_foreign_key(:source_id) }
end
diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb
index bd97cabc11e..4d9e768ecc6 100644
--- a/spec/models/merge_request/metrics_spec.rb
+++ b/spec/models/merge_request/metrics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequest::Metrics do
+RSpec.describe MergeRequest::Metrics do
describe 'associations' do
it { is_expected.to belong_to(:merge_request) }
it { is_expected.to belong_to(:latest_closed_by).class_name('User') }
diff --git a/spec/models/merge_request_assignee_spec.rb b/spec/models/merge_request_assignee_spec.rb
index d6aab15d990..d287392bf7f 100644
--- a/spec/models/merge_request_assignee_spec.rb
+++ b/spec/models/merge_request_assignee_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestAssignee do
+RSpec.describe MergeRequestAssignee do
let(:merge_request) { create(:merge_request) }
subject { merge_request.merge_request_assignees.build(assignee: create(:user)) }
@@ -15,4 +15,26 @@ describe MergeRequestAssignee do
describe 'validations' do
it { is_expected.to validate_uniqueness_of(:assignee).scoped_to(:merge_request_id) }
end
+
+ describe 'scopes' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project_merge_request) { create(:merge_request, target_project: project, source_project: project, assignee_ids: [user.id]) }
+
+ before do
+ merge_request.update!(assignee_ids: [user.id])
+ end
+
+ context 'in_projects' do
+ it 'returns issue assignees for given project' do
+ expect(MergeRequestAssignee.count).to eq 2
+
+ assignees = MergeRequestAssignee.in_projects([project])
+
+ expect(assignees.count).to eq 1
+ expect(assignees.first.user_id).to eq project_merge_request.merge_request_assignees.first.user_id
+ expect(assignees.first.merge_request_id).to eq project_merge_request.merge_request_assignees.first.merge_request_id
+ end
+ end
+ end
end
diff --git a/spec/models/merge_request_context_commit_diff_file_spec.rb b/spec/models/merge_request_context_commit_diff_file_spec.rb
index 37d44662326..7a098639b57 100644
--- a/spec/models/merge_request_context_commit_diff_file_spec.rb
+++ b/spec/models/merge_request_context_commit_diff_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestContextCommitDiffFile do
+RSpec.describe MergeRequestContextCommitDiffFile do
describe 'associations' do
it { is_expected.to belong_to(:merge_request_context_commit) }
end
diff --git a/spec/models/merge_request_context_commit_spec.rb b/spec/models/merge_request_context_commit_spec.rb
index 5a1bf9874ac..29ef2fab9ad 100644
--- a/spec/models/merge_request_context_commit_spec.rb
+++ b/spec/models/merge_request_context_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestContextCommit do
+RSpec.describe MergeRequestContextCommit do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
let(:raw_repository) { project.repository.raw_repository }
diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb
index 62430b08c5c..5ea0145e60f 100644
--- a/spec/models/merge_request_diff_commit_spec.rb
+++ b/spec/models/merge_request_diff_commit_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestDiffCommit do
+RSpec.describe MergeRequestDiffCommit do
let(:merge_request) { create(:merge_request) }
let(:project) { merge_request.project }
diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb
index 40f7be5dc8f..25971f63338 100644
--- a/spec/models/merge_request_diff_file_spec.rb
+++ b/spec/models/merge_request_diff_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestDiffFile do
+RSpec.describe MergeRequestDiffFile do
it_behaves_like 'a BulkInsertSafe model', MergeRequestDiffFile do
let(:valid_items_for_bulk_insertion) { build_list(:merge_request_diff_file, 10) }
let(:invalid_items_for_bulk_insertion) { [] } # class does not have any validations defined
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 0839dde696a..d153ccedf8c 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestDiff do
+RSpec.describe MergeRequestDiff do
using RSpec::Parameterized::TableSyntax
include RepoHelpers
@@ -162,7 +162,8 @@ describe MergeRequestDiff do
let(:uploader) { ExternalDiffUploader }
let(:file_store) { uploader::Store::LOCAL }
let(:remote_store) { uploader::Store::REMOTE }
- let(:diff) { create(:merge_request).merge_request_diff }
+ let(:merge_request) { create(:merge_request) }
+ let(:diff) { merge_request.merge_request_diff }
it 'converts from in-database to external file storage' do
expect(diff).not_to be_stored_externally
@@ -177,6 +178,30 @@ describe MergeRequestDiff do
expect(diff.external_diff_store).to eq(file_store)
end
+ it 'safely handles a transaction error when migrating to external storage' do
+ expect(diff).not_to be_stored_externally
+ expect(diff.external_diff).not_to be_exists
+
+ stub_external_diffs_setting(enabled: true)
+
+ expect(diff).not_to receive(:save!)
+ expect(Gitlab::Database)
+ .to receive(:bulk_insert)
+ .with('merge_request_diff_files', anything)
+ .and_raise(ActiveRecord::Rollback)
+
+ expect { diff.migrate_files_to_external_storage! }.not_to change(diff, :merge_request_diff_files)
+
+ diff.reload
+
+ expect(diff).not_to be_stored_externally
+
+ # The diff is written outside of the transaction, which is desirable to
+ # avoid long transaction times when migrating, but it does mean we can
+ # leave the file dangling on failure
+ expect(diff.external_diff).to be_exists
+ end
+
it 'converts from in-database to external object storage' do
expect(diff).not_to be_stored_externally
@@ -209,6 +234,33 @@ describe MergeRequestDiff do
diff.migrate_files_to_external_storage!
end
+
+ context 'diff adds an empty file' do
+ let(:project) { create(:project, :test_repo) }
+ let(:merge_request) do
+ create(
+ :merge_request,
+ source_project: project,
+ target_project: project,
+ source_branch: 'empty-file',
+ target_branch: 'master'
+ )
+ end
+
+ it 'migrates the diff to object storage' do
+ create_file_in_repo(project, 'master', 'empty-file', 'empty-file', '')
+
+ expect(diff).not_to be_stored_externally
+
+ stub_external_diffs_setting(enabled: true)
+ stub_external_diffs_object_storage(uploader, direct_upload: true)
+
+ diff.migrate_files_to_external_storage!
+
+ expect(diff).to be_stored_externally
+ expect(diff.external_diff_store).to eq(remote_store)
+ end
+ end
end
describe '#migrate_files_to_database!' do
@@ -476,7 +528,7 @@ describe MergeRequestDiff do
include_examples 'merge request diffs'
end
- describe 'external diffs always enabled' do
+ describe 'external diffs on disk always enabled' do
before do
stub_external_diffs_setting(enabled: true, when: 'always')
end
@@ -484,6 +536,63 @@ describe MergeRequestDiff do
include_examples 'merge request diffs'
end
+ describe 'external diffs in object storage always enabled' do
+ let(:uploader) { ExternalDiffUploader }
+ let(:remote_store) { uploader::Store::REMOTE }
+
+ subject(:diff) { merge_request.merge_request_diff }
+
+ before do
+ stub_external_diffs_setting(enabled: true, when: 'always')
+ stub_external_diffs_object_storage(uploader, direct_upload: true)
+ end
+
+ # We can't use the full merge request diffs shared examples here because
+ # reading from the fake object store isn't implemented yet
+
+ context 'empty diff' do
+ let(:merge_request) { create(:merge_request, :without_diffs) }
+
+ it 'creates an empty diff' do
+ expect(diff.state).to eq('empty')
+ expect(diff).not_to be_stored_externally
+ end
+ end
+
+ context 'normal diff' do
+ let(:merge_request) { create(:merge_request) }
+
+ it 'creates a diff in object storage' do
+ expect(diff).to be_stored_externally
+ expect(diff.state).to eq('collected')
+ expect(diff.external_diff_store).to eq(remote_store)
+ end
+ end
+
+ context 'diff adding an empty file' do
+ let(:project) { create(:project, :test_repo) }
+ let(:merge_request) do
+ create(
+ :merge_request,
+ source_project: project,
+ target_project: project,
+ source_branch: 'empty-file',
+ target_branch: 'master'
+ )
+ end
+
+ it 'creates a diff in object storage' do
+ create_file_in_repo(project, 'master', 'empty-file', 'empty-file', '')
+
+ diff.reload
+
+ expect(diff).to be_stored_externally
+ expect(diff.state).to eq('collected')
+ expect(diff.external_diff_store).to eq(remote_store)
+ end
+ end
+ end
+
describe 'exernal diffs enabled for outdated diffs' do
before do
stub_external_diffs_setting(enabled: true, when: 'outdated')
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 582cdc7b419..06febddef0c 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequest do
+RSpec.describe MergeRequest do
include RepoHelpers
include ProjectForksHelper
include ReactiveCachingHelpers
@@ -12,6 +12,8 @@ describe MergeRequest do
subject { create(:merge_request) }
describe 'associations' do
+ subject { build_stubbed(:merge_request) }
+
it { is_expected.to belong_to(:target_project).class_name('Project') }
it { is_expected.to belong_to(:source_project).class_name('Project') }
it { is_expected.to belong_to(:merge_user).class_name("User") }
@@ -55,29 +57,6 @@ describe MergeRequest do
end
end
- describe 'locking' do
- using RSpec::Parameterized::TableSyntax
-
- where(:lock_version) do
- [
- [0],
- ["0"]
- ]
- end
-
- with_them do
- it 'works when a merge request has a NULL lock_version' do
- merge_request = create(:merge_request)
-
- described_class.where(id: merge_request.id).update_all('lock_version = NULL')
-
- merge_request.update!(lock_version: lock_version, title: 'locking test')
-
- expect(merge_request.reload.title).to eq('locking test')
- end
- end
- end
-
describe '#squash_in_progress?' do
let(:repo_path) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
@@ -195,6 +174,8 @@ describe MergeRequest do
end
describe 'validation' do
+ subject { build_stubbed(:merge_request) }
+
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
@@ -280,6 +261,21 @@ describe MergeRequest do
expect(MergeRequest::Metrics.count).to eq(1)
end
+
+ it 'does not create duplicated metrics records when MR is concurrently updated' do
+ merge_request = create(:merge_request)
+
+ merge_request.metrics.destroy
+
+ instance1 = MergeRequest.find(merge_request.id)
+ instance2 = MergeRequest.find(merge_request.id)
+
+ instance1.ensure_metrics
+ instance2.ensure_metrics
+
+ metrics_records = MergeRequest::Metrics.where(merge_request_id: merge_request.id)
+ expect(metrics_records.size).to eq(1)
+ end
end
end
@@ -1092,13 +1088,43 @@ describe MergeRequest do
end
describe "#work_in_progress?" do
- ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
+ subject { build_stubbed(:merge_request) }
+
+ [
+ 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP: [WIP] WIP:',
+ 'draft:', 'Draft: ', '[Draft]', '[DRAFT] ', 'Draft - '
+ ].each do |wip_prefix|
it "detects the '#{wip_prefix}' prefix" do
subject.title = "#{wip_prefix}#{subject.title}"
+
expect(subject.work_in_progress?).to eq true
end
end
+ it "detects merge request title just saying 'wip'" do
+ subject.title = "wip"
+
+ expect(subject.work_in_progress?).to eq true
+ end
+
+ it "detects merge request title just saying 'draft'" do
+ subject.title = "draft"
+
+ expect(subject.work_in_progress?).to eq true
+ end
+
+ it 'does not detect WIP in the middle of the title' do
+ subject.title = 'Something with WIP in the middle'
+
+ expect(subject.work_in_progress?).to eq false
+ end
+
+ it 'does not detect Draft in the middle of the title' do
+ subject.title = 'Something with Draft in the middle'
+
+ expect(subject.work_in_progress?).to eq false
+ end
+
it "doesn't detect WIP for words starting with WIP" do
subject.title = "Wipwap #{subject.title}"
expect(subject.work_in_progress?).to eq false
@@ -1115,7 +1141,12 @@ describe MergeRequest do
end
describe "#wipless_title" do
- ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix|
+ subject { build_stubbed(:merge_request) }
+
+ [
+ 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP: [WIP] WIP:',
+ 'draft:', 'Draft: ', '[Draft]', '[DRAFT] ', 'Draft - '
+ ].each do |wip_prefix|
it "removes the '#{wip_prefix}' prefix" do
wipless_title = subject.title
subject.title = "#{wip_prefix}#{subject.title}"
@@ -1133,14 +1164,14 @@ describe MergeRequest do
end
describe "#wip_title" do
- it "adds the WIP: prefix to the title" do
- wip_title = "WIP: #{subject.title}"
+ it "adds the Draft: prefix to the title" do
+ wip_title = "Draft: #{subject.title}"
expect(subject.wip_title).to eq wip_title
end
- it "does not add the WIP: prefix multiple times" do
- wip_title = "WIP: #{subject.title}"
+ it "does not add the Draft: prefix multiple times" do
+ wip_title = "Draft: #{subject.title}"
subject.title = subject.wip_title
subject.title = subject.wip_title
@@ -1170,6 +1201,12 @@ describe MergeRequest do
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
+ it "can't be removed because source project has been deleted" do
+ subject.source_project = nil
+
+ expect(subject.can_remove_source_branch?(user)).to be_falsey
+ end
+
it "can't remove a root ref" do
subject.update(source_branch: 'master', target_branch: 'feature')
@@ -1196,6 +1233,29 @@ describe MergeRequest do
end
end
+ describe "#source_branch_exists?" do
+ let(:merge_request) { subject }
+ let(:repository) { merge_request.source_project.repository }
+
+ context 'when the source project is set' do
+ it 'memoizes the value and returns the result' do
+ expect(repository).to receive(:branch_exists?).once.with(merge_request.source_branch).and_return(true)
+
+ 2.times { expect(merge_request.source_branch_exists?).to eq(true) }
+ end
+ end
+
+ context 'when the source project is not set' do
+ before do
+ merge_request.source_project = nil
+ end
+
+ it 'returns false' do
+ expect(merge_request.source_branch_exists?).to eq(false)
+ end
+ end
+ end
+
describe '#default_merge_commit_message' do
it 'includes merge information as the title' do
request = build(:merge_request, source_branch: 'source', target_branch: 'target')
@@ -2426,7 +2486,7 @@ describe MergeRequest do
context 'when working in progress' do
before do
- subject.title = 'WIP MR'
+ subject.title = '[Draft] MR'
end
it 'returns false' do
diff --git a/spec/models/metrics/dashboard/annotation_spec.rb b/spec/models/metrics/dashboard/annotation_spec.rb
index 3cba31ffdfe..bd4baeb8851 100644
--- a/spec/models/metrics/dashboard/annotation_spec.rb
+++ b/spec/models/metrics/dashboard/annotation_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Metrics::Dashboard::Annotation do
+RSpec.describe Metrics::Dashboard::Annotation do
using RSpec::Parameterized::TableSyntax
describe 'associations' do
diff --git a/spec/models/metrics/users_starred_dashboard_spec.rb b/spec/models/metrics/users_starred_dashboard_spec.rb
index 6cb14ae569e..c89344c0a1c 100644
--- a/spec/models/metrics/users_starred_dashboard_spec.rb
+++ b/spec/models/metrics/users_starred_dashboard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Metrics::UsersStarredDashboard do
+RSpec.describe Metrics::UsersStarredDashboard do
describe 'associations' do
it { is_expected.to belong_to(:project).inverse_of(:metrics_users_starred_dashboards) }
it { is_expected.to belong_to(:user).inverse_of(:metrics_users_starred_dashboards) }
diff --git a/spec/models/milestone_note_spec.rb b/spec/models/milestone_note_spec.rb
index aad65cf0346..db1a7ca05f8 100644
--- a/spec/models/milestone_note_spec.rb
+++ b/spec/models/milestone_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MilestoneNote do
+RSpec.describe MilestoneNote do
describe '.from_event' do
let(:author) { create(:user) }
let(:project) { create(:project, :repository) }
@@ -11,9 +11,7 @@ describe MilestoneNote do
subject { described_class.from_event(event, resource: noteable, resource_parent: project) }
- it_behaves_like 'a system note', exclude_project: true do
- let(:action) { 'milestone' }
- end
+ it_behaves_like 'a synthetic note', 'milestone'
context 'with a remove milestone event' do
let(:milestone) { create(:milestone) }
diff --git a/spec/models/milestone_release_spec.rb b/spec/models/milestone_release_spec.rb
index 28cec7bbc17..3c781545d8a 100644
--- a/spec/models/milestone_release_spec.rb
+++ b/spec/models/milestone_release_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MilestoneRelease do
+RSpec.describe MilestoneRelease do
let(:project) { create(:project) }
let(:release) { create(:release, project: project) }
let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 33f84da27f6..b52b035e130 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Milestone do
+RSpec.describe Milestone do
it_behaves_like 'a timebox', :milestone
describe 'MilestoneStruct#serializable_hash' do
diff --git a/spec/models/namespace/root_storage_size_spec.rb b/spec/models/namespace/root_storage_size_spec.rb
deleted file mode 100644
index a8048b7f637..00000000000
--- a/spec/models/namespace/root_storage_size_spec.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Namespace::RootStorageSize, type: :model do
- let(:namespace) { create(:namespace) }
- let(:current_size) { 50.megabytes }
- let(:limit) { 100 }
- let(:model) { described_class.new(namespace) }
- let(:create_statistics) { create(:namespace_root_storage_statistics, namespace: namespace, storage_size: current_size)}
-
- before do
- create_statistics
-
- stub_application_setting(namespace_storage_size_limit: limit)
- end
-
- describe '#above_size_limit?' do
- subject { model.above_size_limit? }
-
- context 'when limit is 0' do
- let(:limit) { 0 }
-
- it { is_expected.to eq(false) }
- end
-
- context 'when below limit' do
- it { is_expected.to eq(false) }
- end
-
- context 'when above limit' do
- let(:current_size) { 101.megabytes }
-
- it { is_expected.to eq(true) }
- end
- end
-
- describe '#usage_ratio' do
- subject { model.usage_ratio }
-
- it { is_expected.to eq(0.5) }
-
- context 'when limit is 0' do
- let(:limit) { 0 }
-
- it { is_expected.to eq(0) }
- end
-
- context 'when there are no root_storage_statistics' do
- let(:create_statistics) { nil }
-
- it { is_expected.to eq(0) }
- end
- end
-
- describe '#current_size' do
- subject { model.current_size }
-
- it { is_expected.to eq(current_size) }
- end
-
- describe '#limit' do
- subject { model.limit }
-
- it { is_expected.to eq(limit.megabytes) }
- end
-end
diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb
index 9e12831a704..ce6f875ee09 100644
--- a/spec/models/namespace/root_storage_statistics_spec.rb
+++ b/spec/models/namespace/root_storage_statistics_spec.rb
@@ -43,6 +43,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
total_build_artifacts_size = stat1.build_artifacts_size + stat2.build_artifacts_size
total_packages_size = stat1.packages_size + stat2.packages_size
total_storage_size = stat1.storage_size + stat2.storage_size
+ total_snippets_size = stat1.snippets_size + stat2.snippets_size
expect(root_storage_statistics.repository_size).to eq(total_repository_size)
expect(root_storage_statistics.wiki_size).to eq(total_wiki_size)
@@ -50,6 +51,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
expect(root_storage_statistics.build_artifacts_size).to eq(total_build_artifacts_size)
expect(root_storage_statistics.packages_size).to eq(total_packages_size)
expect(root_storage_statistics.storage_size).to eq(total_storage_size)
+ expect(root_storage_statistics.snippets_size).to eq(total_snippets_size)
end
it 'works when there are no projects' do
@@ -64,10 +66,20 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
expect(root_storage_statistics.build_artifacts_size).to eq(0)
expect(root_storage_statistics.packages_size).to eq(0)
expect(root_storage_statistics.storage_size).to eq(0)
+ expect(root_storage_statistics.snippets_size).to eq(0)
+ end
+ end
+
+ shared_examples 'does not include personal snippets' do
+ specify do
+ expect(root_storage_statistics).not_to receive(:from_personal_snippets)
+
+ root_storage_statistics.recalculate!
end
end
it_behaves_like 'data refresh'
+ it_behaves_like 'does not include personal snippets'
context 'with subgroups' do
let(:subgroup1) { create(:group, parent: namespace)}
@@ -77,12 +89,45 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
let(:project2) { create(:project, namespace: subgroup2) }
it_behaves_like 'data refresh'
+ it_behaves_like 'does not include personal snippets'
end
context 'with a personal namespace' do
- let(:namespace) { create(:user).namespace }
+ let_it_be(:user) { create(:user) }
+ let(:namespace) { user.namespace }
it_behaves_like 'data refresh'
+
+ context 'when user has personal snippets' do
+ let(:total_project_snippets_size) { stat1.snippets_size + stat2.snippets_size }
+
+ it 'aggregates personal and project snippets size' do
+ # This is just a a snippet authored by other user
+ # to ensure we only pick snippets from the namespace
+ # user
+ create(:personal_snippet, :repository).statistics.refresh!
+
+ snippets = create_list(:personal_snippet, 3, :repository, author: user)
+ snippets.each { |s| s.statistics.refresh! }
+
+ total_personal_snippets_size = snippets.map { |s| s.statistics.repository_size }.sum
+
+ root_storage_statistics.recalculate!
+
+ expect(root_storage_statistics.snippets_size).to eq(total_personal_snippets_size + total_project_snippets_size)
+ end
+
+ context 'when personal snippets do not have statistics' do
+ it 'does not raise any error' do
+ snippets = create_list(:personal_snippet, 2, :repository, author: user)
+ snippets.last.statistics.refresh!
+
+ root_storage_statistics.recalculate!
+
+ expect(root_storage_statistics.snippets_size).to eq(total_project_snippets_size + snippets.last.statistics.repository_size)
+ end
+ end
+ end
end
end
end
diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb
new file mode 100644
index 00000000000..71b0e974106
--- /dev/null
+++ b/spec/models/namespace/traversal_hierarchy_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Namespace::TraversalHierarchy, type: :model do
+ let_it_be(:root, reload: true) { create(:namespace, :with_hierarchy) }
+
+ describe '.for_namespace' do
+ let(:hierarchy) { described_class.for_namespace(namespace) }
+
+ context 'with root group' do
+ let(:namespace) { root }
+
+ it { expect(hierarchy.root).to eq root }
+ end
+
+ context 'with child group' do
+ let(:namespace) { root.children.first.children.first }
+
+ it { expect(hierarchy.root).to eq root }
+ end
+
+ context 'with group outside of hierarchy' do
+ let(:namespace) { create(:namespace) }
+
+ it { expect(hierarchy.root).not_to eq root }
+ end
+ end
+
+ describe '.new' do
+ let(:hierarchy) { described_class.new(namespace) }
+
+ context 'with root group' do
+ let(:namespace) { root }
+
+ it { expect(hierarchy.root).to eq root }
+ end
+
+ context 'with child group' do
+ let(:namespace) { root.children.first }
+
+ it { expect { hierarchy }.to raise_error(StandardError, 'Must specify a root node') }
+ end
+ end
+
+ describe '#incorrect_traversal_ids' do
+ subject { described_class.new(root).incorrect_traversal_ids }
+
+ it { is_expected.to match_array Namespace.all }
+ end
+
+ describe '#sync_traversal_ids!' do
+ let(:hierarchy) { described_class.new(root) }
+
+ before do
+ hierarchy.sync_traversal_ids!
+ root.reload
+ end
+
+ it_behaves_like 'hierarchy with traversal_ids'
+ it { expect(hierarchy.incorrect_traversal_ids).to be_empty }
+ end
+end
diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb
new file mode 100644
index 00000000000..257d78dfa2c
--- /dev/null
+++ b/spec/models/namespace_setting_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe NamespaceSetting, type: :model do
+ it { is_expected.to belong_to(:namespace) }
+end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index ed7ef8b2b8e..ad4c8448745 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Namespace do
+RSpec.describe Namespace do
include ProjectForksHelper
include GitHelpers
@@ -17,6 +17,8 @@ describe Namespace do
it { is_expected.to have_many :children }
it { is_expected.to have_one :root_storage_statistics }
it { is_expected.to have_one :aggregation_schedule }
+ it { is_expected.to have_one :namespace_settings }
+ it { is_expected.to have_many :custom_emoji }
end
describe 'validations' do
@@ -64,6 +66,36 @@ describe Namespace do
it { expect(group).to be_valid }
end
end
+
+ describe '1 char path length' do
+ it 'does not allow to create one' do
+ namespace = build(:namespace, path: 'j')
+
+ expect(namespace).not_to be_valid
+ expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)')
+ end
+
+ it 'does not allow to update one' do
+ namespace = create(:namespace)
+ namespace.update(path: 'j')
+
+ expect(namespace).not_to be_valid
+ expect(namespace.errors[:path].first).to eq('is too short (minimum is 2 characters)')
+ end
+
+ it 'allows updating other attributes for existing record' do
+ namespace = build(:namespace, path: 'j')
+ namespace.save(validate: false)
+ namespace.reload
+
+ expect(namespace.path).to eq('j')
+
+ namespace.update(name: 'something new')
+
+ expect(namespace).to be_valid
+ expect(namespace.name).to eq('something new')
+ end
+ end
end
describe 'delegate' do
@@ -153,7 +185,8 @@ describe Namespace do
wiki_size: 505,
lfs_objects_size: 202,
build_artifacts_size: 303,
- packages_size: 404))
+ packages_size: 404,
+ snippets_size: 605))
end
let(:project2) do
@@ -164,7 +197,8 @@ describe Namespace do
wiki_size: 50,
lfs_objects_size: 20,
build_artifacts_size: 30,
- packages_size: 40))
+ packages_size: 40,
+ snippets_size: 60))
end
it "sums all project storage counters in the namespace" do
@@ -172,12 +206,13 @@ describe Namespace do
project2
statistics = described_class.with_statistics.find(namespace.id)
- expect(statistics.storage_size).to eq 1665
+ expect(statistics.storage_size).to eq 2330
expect(statistics.repository_size).to eq 111
expect(statistics.wiki_size).to eq 555
expect(statistics.lfs_objects_size).to eq 222
expect(statistics.build_artifacts_size).to eq 333
expect(statistics.packages_size).to eq 444
+ expect(statistics.snippets_size).to eq 665
end
it "correctly handles namespaces without projects" do
@@ -189,6 +224,7 @@ describe Namespace do
expect(statistics.lfs_objects_size).to eq 0
expect(statistics.build_artifacts_size).to eq 0
expect(statistics.packages_size).to eq 0
+ expect(statistics.snippets_size).to eq 0
end
end
@@ -849,8 +885,13 @@ describe Namespace do
end
describe '#root_ancestor' do
+ let!(:root_group) { create(:group) }
+
+ it 'returns root_ancestor for root group without a query' do
+ expect { root_group.root_ancestor }.not_to exceed_query_limit(0)
+ end
+
it 'returns the top most ancestor' do
- root_group = create(:group)
nested_group = create(:group, parent: root_group)
deep_nested_group = create(:group, parent: nested_group)
very_deep_nested_group = create(:group, parent: deep_nested_group)
diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb
index 232172fde76..a393aace39c 100644
--- a/spec/models/network/graph_spec.rb
+++ b/spec/models/network/graph_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Network::Graph do
+RSpec.describe Network::Graph do
let(:project) { create(:project, :repository) }
let!(:note_on_commit) { create(:note_on_commit, project: project) }
diff --git a/spec/models/note_diff_file_spec.rb b/spec/models/note_diff_file_spec.rb
index 11108016b8e..1ece1dfea59 100644
--- a/spec/models/note_diff_file_spec.rb
+++ b/spec/models/note_diff_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe NoteDiffFile do
+RSpec.describe NoteDiffFile do
describe 'associations' do
it { is_expected.to belong_to(:diff_note) }
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index af3fdcfaa2e..e6e6a8c35cf 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Note do
+RSpec.describe Note do
include RepoHelpers
describe 'associations' do
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index 05aeafaa4d4..8429f577dc6 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe NotificationRecipient do
+RSpec.describe NotificationRecipient do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:target) { create(:issue, project: project) }
diff --git a/spec/models/oauth_access_grant_spec.rb b/spec/models/oauth_access_grant_spec.rb
index 955dae906f3..ca67944752d 100644
--- a/spec/models/oauth_access_grant_spec.rb
+++ b/spec/models/oauth_access_grant_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OauthAccessGrant do
+RSpec.describe OauthAccessGrant do
let(:user) { create(:user) }
let(:application) { create(:oauth_application, owner: user) }
diff --git a/spec/models/oauth_access_token_spec.rb b/spec/models/oauth_access_token_spec.rb
index 0a1c576a5e7..65a7f6410cf 100644
--- a/spec/models/oauth_access_token_spec.rb
+++ b/spec/models/oauth_access_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OauthAccessToken do
+RSpec.describe OauthAccessToken do
let(:user) { create(:user) }
let(:app_one) { create(:oauth_application) }
let(:app_two) { create(:oauth_application) }
diff --git a/spec/models/packages/composer/metadatum_spec.rb b/spec/models/packages/composer/metadatum_spec.rb
new file mode 100644
index 00000000000..ae53532696b
--- /dev/null
+++ b/spec/models/packages/composer/metadatum_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Composer::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+ it { is_expected.to validate_presence_of(:target_sha) }
+ it { is_expected.to validate_presence_of(:composer_json) }
+ end
+end
diff --git a/spec/models/packages/conan/file_metadatum_spec.rb b/spec/models/packages/conan/file_metadatum_spec.rb
new file mode 100644
index 00000000000..a66a2813196
--- /dev/null
+++ b/spec/models/packages/conan/file_metadatum_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Conan::FileMetadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package_file) }
+ end
+
+ describe 'validations' do
+ let(:package_file) { create(:conan_package_file, :conan_recipe_file) }
+
+ it { is_expected.to validate_presence_of(:package_file) }
+ it { is_expected.to validate_presence_of(:recipe_revision) }
+
+ describe '#recipe_revision' do
+ it { is_expected.to allow_value("0").for(:recipe_revision) }
+ it { is_expected.not_to allow_value(nil).for(:recipe_revision) }
+ end
+
+ describe '#package_revision_for_package_file' do
+ context 'recipe file' do
+ let(:conan_file_metadatum) { build(:conan_file_metadatum, :recipe_file, package_file: package_file) }
+
+ it 'is valid with empty value' do
+ conan_file_metadatum.package_revision = nil
+
+ expect(conan_file_metadatum).to be_valid
+ end
+
+ it 'is invalid with value' do
+ conan_file_metadatum.package_revision = '0'
+
+ expect(conan_file_metadatum).to be_invalid
+ end
+ end
+
+ context 'package file' do
+ let(:conan_file_metadatum) { build(:conan_file_metadatum, :package_file, package_file: package_file) }
+
+ it 'is valid with default value' do
+ conan_file_metadatum.package_revision = '0'
+
+ expect(conan_file_metadatum).to be_valid
+ end
+
+ it 'is invalid with non-default value' do
+ conan_file_metadatum.package_revision = 'foo'
+
+ expect(conan_file_metadatum).to be_invalid
+ end
+ end
+ end
+
+ describe '#conan_package_reference_for_package_file' do
+ context 'recipe file' do
+ let(:conan_file_metadatum) { build(:conan_file_metadatum, :recipe_file, package_file: package_file) }
+
+ it 'is valid with empty value' do
+ conan_file_metadatum.conan_package_reference = nil
+
+ expect(conan_file_metadatum).to be_valid
+ end
+
+ it 'is invalid with value' do
+ conan_file_metadatum.conan_package_reference = '123456789'
+
+ expect(conan_file_metadatum).to be_invalid
+ end
+ end
+
+ context 'package file' do
+ let(:conan_file_metadatum) { build(:conan_file_metadatum, :package_file, package_file: package_file) }
+
+ it 'is valid with acceptable value' do
+ conan_file_metadatum.conan_package_reference = '123456asdf'
+
+ expect(conan_file_metadatum).to be_valid
+ end
+
+ it 'is invalid with invalid value' do
+ conan_file_metadatum.conan_package_reference = 'foo@bar'
+
+ expect(conan_file_metadatum).to be_invalid
+ end
+
+ it 'is invalid when nil' do
+ conan_file_metadatum.conan_package_reference = nil
+
+ expect(conan_file_metadatum).to be_invalid
+ end
+ end
+ end
+
+ describe '#conan_package_type' do
+ it 'validates package of type conan' do
+ package = build('package')
+ package_file = build('package_file', package: package)
+ conan_file_metadatum = build('conan_file_metadatum', package_file: package_file)
+
+ expect(conan_file_metadatum).not_to be_valid
+ expect(conan_file_metadatum.errors.to_a).to contain_exactly('Package type must be Conan')
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/conan/metadatum_spec.rb b/spec/models/packages/conan/metadatum_spec.rb
new file mode 100644
index 00000000000..112f395818b
--- /dev/null
+++ b/spec/models/packages/conan/metadatum_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Conan::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ end
+
+ describe 'validations' do
+ let(:fifty_one_characters) { 'f_a' * 17}
+
+ it { is_expected.to validate_presence_of(:package) }
+ it { is_expected.to validate_presence_of(:package_username) }
+ it { is_expected.to validate_presence_of(:package_channel) }
+
+ describe '#package_username' do
+ it { is_expected.to allow_value("my-package+username").for(:package_username) }
+ it { is_expected.to allow_value("my_package.username").for(:package_username) }
+ it { is_expected.to allow_value("_my-package.username123").for(:package_username) }
+ it { is_expected.to allow_value("my").for(:package_username) }
+ it { is_expected.not_to allow_value('+my_package').for(:package_username) }
+ it { is_expected.not_to allow_value('.my_package').for(:package_username) }
+ it { is_expected.not_to allow_value('-my_package').for(:package_username) }
+ it { is_expected.not_to allow_value('m').for(:package_username) }
+ it { is_expected.not_to allow_value(fifty_one_characters).for(:package_username) }
+ it { is_expected.not_to allow_value("my/package").for(:package_username) }
+ it { is_expected.not_to allow_value("my(package)").for(:package_username) }
+ it { is_expected.not_to allow_value("my@package").for(:package_username) }
+ end
+
+ describe '#package_channel' do
+ it { is_expected.to allow_value("beta").for(:package_channel) }
+ it { is_expected.to allow_value("stable+1.0").for(:package_channel) }
+ it { is_expected.to allow_value("my").for(:package_channel) }
+ it { is_expected.to allow_value("my_channel.beta").for(:package_channel) }
+ it { is_expected.to allow_value("_my-channel.beta123").for(:package_channel) }
+ it { is_expected.not_to allow_value('+my_channel').for(:package_channel) }
+ it { is_expected.not_to allow_value('.my_channel').for(:package_channel) }
+ it { is_expected.not_to allow_value('-my_channel').for(:package_channel) }
+ it { is_expected.not_to allow_value('m').for(:package_channel) }
+ it { is_expected.not_to allow_value(fifty_one_characters).for(:package_channel) }
+ it { is_expected.not_to allow_value("my/channel").for(:package_channel) }
+ it { is_expected.not_to allow_value("my(channel)").for(:package_channel) }
+ it { is_expected.not_to allow_value("my@channel").for(:package_channel) }
+ end
+
+ describe '#conan_package_type' do
+ it 'will not allow a package with a different package_type' do
+ package = build('package')
+ conan_metadatum = build('conan_metadatum', package: package)
+
+ expect(conan_metadatum).not_to be_valid
+ expect(conan_metadatum.errors.to_a).to include('Package type must be Conan')
+ end
+ end
+ end
+
+ describe '#recipe' do
+ let(:package) { create(:conan_package) }
+
+ it 'returns the recipe' do
+ expect(package.conan_recipe).to eq("#{package.name}/#{package.version}@#{package.conan_metadatum.package_username}/#{package.conan_metadatum.package_channel}")
+ end
+ end
+
+ describe '#recipe_url' do
+ let(:package) { create(:conan_package) }
+
+ it 'returns the recipe url' do
+ expect(package.conan_recipe_path).to eq("#{package.name}/#{package.version}/#{package.conan_metadatum.package_username}/#{package.conan_metadatum.package_channel}")
+ end
+ end
+
+ describe '.package_username_from' do
+ let(:full_path) { 'foo/bar/baz-buz' }
+
+ it 'returns the username formatted package path' do
+ expect(described_class.package_username_from(full_path: full_path)).to eq('foo+bar+baz-buz')
+ end
+ end
+
+ describe '.full_path_from' do
+ let(:username) { 'foo+bar+baz-buz' }
+
+ it 'returns the username formatted package path' do
+ expect(described_class.full_path_from(package_username: username)).to eq('foo/bar/baz-buz')
+ end
+ end
+end
diff --git a/spec/models/packages/dependency_link_spec.rb b/spec/models/packages/dependency_link_spec.rb
new file mode 100644
index 00000000000..d8fde8f5eb3
--- /dev/null
+++ b/spec/models/packages/dependency_link_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::DependencyLink, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package).inverse_of(:dependency_links) }
+ it { is_expected.to belong_to(:dependency).inverse_of(:dependency_links) }
+ it { is_expected.to have_one(:nuget_metadatum).inverse_of(:dependency_link) }
+ end
+
+ describe 'validations' do
+ subject { create(:packages_dependency_link) }
+
+ it { is_expected.to validate_presence_of(:package) }
+ it { is_expected.to validate_presence_of(:dependency) }
+
+ context 'package_id and package_dependency_id uniqueness for dependency_type' do
+ it 'is not valid' do
+ exisiting_link = subject
+ link = build(
+ :packages_dependency_link,
+ package: exisiting_link.package,
+ dependency: exisiting_link.dependency,
+ dependency_type: exisiting_link.dependency_type
+ )
+
+ expect(link).not_to be_valid
+ expect(link.errors.to_a).to include("Dependency type has already been taken")
+ end
+ end
+ end
+
+ context 'with multiple links' do
+ let_it_be(:link1) { create(:packages_dependency_link) }
+ let_it_be(:link2) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :devDependencies) }
+ let_it_be(:link3) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :bundleDependencies) }
+
+ subject { described_class }
+
+ describe '.with_dependency_type' do
+ it 'returns links of the given type' do
+ expect(subject.with_dependency_type(:bundleDependencies)).to eq([link3])
+ end
+ end
+
+ describe '.for_package' do
+ let_it_be(:link1) { create(:packages_dependency_link) }
+ let_it_be(:link2) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :devDependencies) }
+ let_it_be(:link3) { create(:packages_dependency_link, dependency: link1.dependency, dependency_type: :bundleDependencies) }
+
+ it 'returns the link for the given package' do
+ expect(subject.for_package(link1.package)).to eq([link1])
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/dependency_spec.rb b/spec/models/packages/dependency_spec.rb
new file mode 100644
index 00000000000..fa6b0fd1848
--- /dev/null
+++ b/spec/models/packages/dependency_spec.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Dependency, type: :model do
+ describe 'relationships' do
+ it { is_expected.to have_many(:dependency_links) }
+ end
+
+ describe 'validations' do
+ subject { create(:packages_dependency) }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_presence_of(:version_pattern) }
+ it { is_expected.to validate_uniqueness_of(:name).scoped_to(:version_pattern) }
+ end
+
+ describe '.ids_for_package_names_and_version_patterns' do
+ let_it_be(:package_dependency1) { create(:packages_dependency, name: 'foo', version_pattern: '~1.0.0') }
+ let_it_be(:package_dependency2) { create(:packages_dependency, name: 'bar', version_pattern: '~2.5.0') }
+ let_it_be(:expected_ids) { [package_dependency1.id, package_dependency2.id] }
+ let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2) }
+ let(:chunk_size) { 50 }
+ let(:rows_limit) { 50 }
+
+ subject { Packages::Dependency.ids_for_package_names_and_version_patterns(names_and_version_patterns, chunk_size, rows_limit) }
+
+ it { is_expected.to match_array(expected_ids) }
+
+ context 'with unknown names' do
+ let(:names_and_version_patterns) { { unknown: '~1.0.0' } }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'with unknown version patterns' do
+ let(:names_and_version_patterns) { { 'foo' => '~1.0.0beta' } }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'with a name bigger than column size' do
+ let_it_be(:big_name) { 'a' * (Packages::Dependency::MAX_STRING_LENGTH + 1) }
+ let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2).merge(big_name => '~1.0.0') }
+
+ it { is_expected.to match_array(expected_ids) }
+ end
+
+ context 'with a version pattern bigger than column size' do
+ let_it_be(:big_version_pattern) { 'a' * (Packages::Dependency::MAX_STRING_LENGTH + 1) }
+ let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2).merge('test' => big_version_pattern) }
+
+ it { is_expected.to match_array(expected_ids) }
+ end
+
+ context 'with too big parameter' do
+ let(:size) { (Packages::Dependency::MAX_CHUNKED_QUERIES_COUNT * chunk_size) + 1 }
+ let(:names_and_version_patterns) { Hash[(1..size).map { |v| [v, v] }] }
+
+ it { expect { subject }.to raise_error(ArgumentError, 'Too many names_and_version_patterns') }
+ end
+
+ context 'with parameters size' do
+ let_it_be(:package_dependency3) { create(:packages_dependency, name: 'foo3', version_pattern: '~1.5.3') }
+ let_it_be(:package_dependency4) { create(:packages_dependency, name: 'foo4', version_pattern: '~1.5.4') }
+ let_it_be(:package_dependency5) { create(:packages_dependency, name: 'foo5', version_pattern: '~1.5.5') }
+ let_it_be(:package_dependency6) { create(:packages_dependency, name: 'foo6', version_pattern: '~1.5.6') }
+ let_it_be(:package_dependency7) { create(:packages_dependency, name: 'foo7', version_pattern: '~1.5.7') }
+ let(:expected_ids) { [package_dependency1.id, package_dependency2.id, package_dependency3.id, package_dependency4.id, package_dependency5.id, package_dependency6.id, package_dependency7.id] }
+ let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2, package_dependency3, package_dependency4, package_dependency5, package_dependency6, package_dependency7) }
+
+ context 'above the chunk size' do
+ let(:chunk_size) { 2 }
+
+ it { is_expected.to match_array(expected_ids) }
+ end
+
+ context 'selecting too many rows' do
+ let(:rows_limit) { 2 }
+
+ it { expect { subject }.to raise_error(ArgumentError, 'Too many Dependencies selected') }
+ end
+ end
+ end
+
+ describe '.for_package_names_and_version_patterns' do
+ let_it_be(:package_dependency1) { create(:packages_dependency, name: 'foo', version_pattern: '~1.0.0') }
+ let_it_be(:package_dependency2) { create(:packages_dependency, name: 'bar', version_pattern: '~2.5.0') }
+ let_it_be(:expected_array) { [package_dependency1, package_dependency2] }
+ let(:names_and_version_patterns) { build_names_and_version_patterns(package_dependency1, package_dependency2) }
+
+ subject { Packages::Dependency.for_package_names_and_version_patterns(names_and_version_patterns) }
+
+ it { is_expected.to match_array(expected_array) }
+
+ context 'with unknown names' do
+ let(:names_and_version_patterns) { { unknown: '~1.0.0' } }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'with unknown version patterns' do
+ let(:names_and_version_patterns) { { 'foo' => '~1.0.0beta' } }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ def build_names_and_version_patterns(*package_dependencies)
+ result = Hash.new { |h, dependency| h[dependency.name] = dependency.version_pattern }
+ package_dependencies.each { |dependency| result[dependency] }
+ result
+ end
+end
diff --git a/spec/models/packages/go/module_spec.rb b/spec/models/packages/go/module_spec.rb
new file mode 100644
index 00000000000..03af4cf4b70
--- /dev/null
+++ b/spec/models/packages/go/module_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Go::Module, type: :model do
+ before do
+ stub_feature_flags(go_proxy_disable_gomod_validation: false)
+ end
+
+ describe '#path_valid?' do
+ context 'with root path' do
+ let_it_be(:package) { create(:go_module) }
+
+ context 'with major version 0' do
+ it('returns true') { expect(package.path_valid?(0)).to eq(true) }
+ end
+
+ context 'with major version 1' do
+ it('returns true') { expect(package.path_valid?(1)).to eq(true) }
+ end
+
+ context 'with major version 2' do
+ it('returns false') { expect(package.path_valid?(2)).to eq(false) }
+ end
+ end
+
+ context 'with path ./v2' do
+ let_it_be(:package) { create(:go_module, path: '/v2') }
+
+ context 'with major version 0' do
+ it('returns false') { expect(package.path_valid?(0)).to eq(false) }
+ end
+
+ context 'with major version 1' do
+ it('returns false') { expect(package.path_valid?(1)).to eq(false) }
+ end
+
+ context 'with major version 2' do
+ it('returns true') { expect(package.path_valid?(2)).to eq(true) }
+ end
+ end
+ end
+
+ describe '#gomod_valid?' do
+ let_it_be(:package) { create(:go_module) }
+
+ context 'with good gomod' do
+ it('returns true') { expect(package.gomod_valid?("module #{package.name}")).to eq(true) }
+ end
+
+ context 'with bad gomod' do
+ it('returns false') { expect(package.gomod_valid?("module #{package.name}/v2")).to eq(false) }
+ end
+
+ context 'with empty gomod' do
+ it('returns false') { expect(package.gomod_valid?("")).to eq(false) }
+ end
+ end
+end
diff --git a/spec/models/packages/go/module_version_spec.rb b/spec/models/packages/go/module_version_spec.rb
new file mode 100644
index 00000000000..c4c6a07d9e9
--- /dev/null
+++ b/spec/models/packages/go/module_version_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Go::ModuleVersion, type: :model do
+ let_it_be(:user) { create :user }
+ let_it_be(:project) { create :project_empty_repo, creator: user, path: 'my-go-lib' }
+ let_it_be(:mod) { create :go_module, project: project }
+
+ before :all do
+ create :go_module_commit, :files, project: project, tag: 'v1.0.0', files: { 'README.md' => 'Hi' }
+ create :go_module_commit, :module, project: project, tag: 'v1.0.1'
+ create :go_module_commit, :package, project: project, tag: 'v1.0.2', path: 'pkg'
+ create :go_module_commit, :module, project: project, tag: 'v1.0.3', name: 'mod'
+ create :go_module_commit, :files, project: project, files: { 'y.go' => "package a\n" }
+ create :go_module_commit, :module, project: project, name: 'v2'
+ create :go_module_commit, :files, project: project, tag: 'v2.0.0', files: { 'v2/x.go' => "package a\n" }
+ end
+
+ shared_examples '#files' do |desc, *entries|
+ it "returns #{desc}" do
+ actual = version.files.map { |x| x }.to_set
+ expect(actual).to eq(entries.to_set)
+ end
+ end
+
+ shared_examples '#archive' do |desc, *entries|
+ it "returns an archive of #{desc}" do
+ expected = entries.map { |e| "#{version.full_name}/#{e}" }.to_set
+
+ actual = Set[]
+ Zip::InputStream.open(StringIO.new(version.archive.string)) do |zip|
+ while (entry = zip.get_next_entry)
+ actual.add(entry.name)
+ end
+ end
+
+ expect(actual).to eq(expected)
+ end
+ end
+
+ describe '#name' do
+ context 'with ref and name specified' do
+ let_it_be(:version) { create :go_module_version, mod: mod, name: 'foobar', commit: project.repository.head_commit, ref: project.repository.find_tag('v1.0.0') }
+ it('returns that name') { expect(version.name).to eq('foobar') }
+ end
+
+ context 'with ref specified and name unspecified' do
+ let_it_be(:version) { create :go_module_version, mod: mod, commit: project.repository.head_commit, ref: project.repository.find_tag('v1.0.0') }
+ it('returns the name of the ref') { expect(version.name).to eq('v1.0.0') }
+ end
+
+ context 'with ref and name unspecified' do
+ let_it_be(:version) { create :go_module_version, mod: mod, commit: project.repository.head_commit }
+ it('returns nil') { expect(version.name).to eq(nil) }
+ end
+ end
+
+ describe '#gomod' do
+ context 'with go.mod missing' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.0' }
+ it('returns nil') { expect(version.gomod).to eq(nil) }
+ end
+
+ context 'with go.mod present' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.1' }
+ it('returns the contents of go.mod') { expect(version.gomod).to eq("module #{mod.name}\n") }
+ end
+ end
+
+ describe '#files' do
+ context 'with a root module' do
+ context 'with an empty module path' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.2' }
+ it_behaves_like '#files', 'all the files', 'README.md', 'go.mod', 'a.go', 'pkg/b.go'
+ end
+ end
+
+ context 'with a root module and a submodule' do
+ context 'with an empty module path' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' }
+ it_behaves_like '#files', 'files excluding the submodule', 'README.md', 'go.mod', 'a.go', 'pkg/b.go'
+ end
+
+ context 'with the submodule\'s path' do
+ let_it_be(:mod) { create :go_module, project: project, path: 'mod' }
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' }
+ it_behaves_like '#files', 'the submodule\'s files', 'mod/go.mod', 'mod/a.go'
+ end
+ end
+ end
+
+ describe '#archive' do
+ context 'with a root module' do
+ context 'with an empty module path' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.2' }
+ it_behaves_like '#archive', 'all the files', 'README.md', 'go.mod', 'a.go', 'pkg/b.go'
+ end
+ end
+
+ context 'with a root module and a submodule' do
+ context 'with an empty module path' do
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' }
+ it_behaves_like '#archive', 'files excluding the submodule', 'README.md', 'go.mod', 'a.go', 'pkg/b.go'
+ end
+
+ context 'with the submodule\'s path' do
+ let_it_be(:mod) { create :go_module, project: project, path: 'mod' }
+ let_it_be(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.3' }
+ it_behaves_like '#archive', 'the submodule\'s files', 'go.mod', 'a.go'
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/maven/metadatum_spec.rb b/spec/models/packages/maven/metadatum_spec.rb
new file mode 100644
index 00000000000..16f6929d710
--- /dev/null
+++ b/spec/models/packages/maven/metadatum_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Maven::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+
+ describe '#app_name' do
+ it { is_expected.to allow_value("my-app").for(:app_name) }
+ it { is_expected.not_to allow_value("my/app").for(:app_name) }
+ it { is_expected.not_to allow_value("my(app)").for(:app_name) }
+ end
+
+ describe '#app_group' do
+ it { is_expected.to allow_value("my.domain.com").for(:app_group) }
+ it { is_expected.not_to allow_value("my/domain/com").for(:app_group) }
+ it { is_expected.not_to allow_value("my(domain)").for(:app_group) }
+ end
+
+ describe '#path' do
+ it { is_expected.to allow_value("my/domain/com/my-app").for(:path) }
+ it { is_expected.to allow_value("my/domain/com/my-app/1.0-SNAPSHOT").for(:path) }
+ it { is_expected.not_to allow_value("my(domain)com.my-app").for(:path) }
+ end
+
+ describe '#maven_package_type' do
+ it 'will not allow a package with a different package_type' do
+ package = build('conan_package')
+ maven_metadatum = build('maven_metadatum', package: package)
+
+ expect(maven_metadatum).not_to be_valid
+ expect(maven_metadatum.errors.to_a).to include('Package type must be Maven')
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/nuget/dependency_link_metadatum_spec.rb b/spec/models/packages/nuget/dependency_link_metadatum_spec.rb
new file mode 100644
index 00000000000..0c03c65028e
--- /dev/null
+++ b/spec/models/packages/nuget/dependency_link_metadatum_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Nuget::DependencyLinkMetadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:dependency_link) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:dependency_link) }
+ it { is_expected.to validate_presence_of(:target_framework) }
+
+ describe '#ensure_nuget_package_type' do
+ it 'validates package of type nuget' do
+ package = build('conan_package')
+ dependency_link = build('packages_dependency_link', package: package)
+ nuget_metadatum = build('nuget_dependency_link_metadatum', dependency_link: dependency_link)
+
+ expect(nuget_metadatum).not_to be_valid
+ expect(nuget_metadatum.errors.to_a).to contain_exactly('Package type must be NuGet')
+ end
+
+ it 'validates package of type nuget with nil dependency_link' do
+ nuget_metadatum = build('nuget_dependency_link_metadatum', dependency_link: nil)
+
+ expect(nuget_metadatum).not_to be_valid
+ expect(nuget_metadatum.errors.to_a).to contain_exactly("Dependency link can't be blank", 'Package type must be NuGet')
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/nuget/metadatum_spec.rb b/spec/models/packages/nuget/metadatum_spec.rb
new file mode 100644
index 00000000000..c1bc5429500
--- /dev/null
+++ b/spec/models/packages/nuget/metadatum_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Nuget::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package).inverse_of(:nuget_metadatum) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+
+ %i[license_url project_url icon_url].each do |url|
+ describe "##{url}" do
+ it { is_expected.to allow_value('http://sandbox.com').for(url) }
+ it { is_expected.to allow_value('https://sandbox.com').for(url) }
+ it { is_expected.not_to allow_value('123').for(url) }
+ it { is_expected.not_to allow_value('sandbox.com').for(url) }
+ end
+
+ describe '#ensure_at_least_one_field_supplied' do
+ subject { build(:nuget_metadatum) }
+
+ it 'rejects unfilled metadatum' do
+ subject.attributes = { license_url: nil, project_url: nil, icon_url: nil }
+
+ expect(subject).not_to be_valid
+ expect(subject.errors).to contain_exactly('Nuget metadatum must have at least license_url, project_url or icon_url set')
+ end
+ end
+
+ describe '#ensure_nuget_package_type' do
+ subject { build(:nuget_metadatum) }
+
+ it 'rejects if not linked to a nuget package' do
+ subject.package = build(:npm_package)
+
+ expect(subject).not_to be_valid
+ expect(subject.errors).to contain_exactly('Package type must be NuGet')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/package_file_spec.rb b/spec/models/packages/package_file_spec.rb
new file mode 100644
index 00000000000..7758ed4a500
--- /dev/null
+++ b/spec/models/packages/package_file_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::PackageFile, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ it { is_expected.to have_one(:conan_file_metadatum) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+ end
+
+ context 'with package filenames' do
+ let_it_be(:package_file1) { create(:package_file, :xml, file_name: 'FooBar') }
+ let_it_be(:package_file2) { create(:package_file, :xml, file_name: 'ThisIsATest') }
+
+ describe '.with_file_name' do
+ let(:filename) { 'FooBar' }
+
+ subject { described_class.with_file_name(filename) }
+
+ it { is_expected.to match_array([package_file1]) }
+ end
+
+ describe '.with_file_name_like' do
+ let(:filename) { 'foobar' }
+
+ subject { described_class.with_file_name_like(filename) }
+
+ it { is_expected.to match_array([package_file1]) }
+ end
+ end
+
+ it_behaves_like 'UpdateProjectStatistics' do
+ subject { build(:package_file, :jar, size: 42) }
+
+ before do
+ allow_any_instance_of(Packages::PackageFileUploader).to receive(:size).and_return(42)
+ end
+ end
+
+ describe '.with_conan_package_reference' do
+ let_it_be(:non_matching_package_file) { create(:package_file, :nuget) }
+ let_it_be(:metadatum) { create(:conan_file_metadatum, :package_file) }
+ let_it_be(:reference) { metadatum.conan_package_reference}
+
+ it 'returns matching packages' do
+ expect(described_class.with_conan_package_reference(reference))
+ .to eq([metadatum.package_file])
+ end
+ end
+
+ describe '#update_file_metadata callback' do
+ let_it_be(:package_file) { build(:package_file, :nuget, file_store: nil, size: nil) }
+
+ subject { package_file.save! }
+
+ it 'updates metadata columns' do
+ expect(package_file)
+ .to receive(:update_file_metadata)
+ .and_call_original
+
+ expect { subject }
+ .to change { package_file.file_store }.from(nil).to(::Packages::PackageFileUploader::Store::LOCAL)
+ .and change { package_file.size }.from(nil).to(3513)
+ end
+ end
+end
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
new file mode 100644
index 00000000000..4170bf595f0
--- /dev/null
+++ b/spec/models/packages/package_spec.rb
@@ -0,0 +1,485 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Package, type: :model do
+ include SortingHelper
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:package_files).dependent(:destroy) }
+ it { is_expected.to have_many(:dependency_links).inverse_of(:package) }
+ it { is_expected.to have_many(:tags).inverse_of(:package) }
+ it { is_expected.to have_one(:conan_metadatum).inverse_of(:package) }
+ it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) }
+ it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
+ end
+
+ describe '.with_composer_target' do
+ let!(:package1) { create(:composer_package, :with_metadatum, sha: '123') }
+ let!(:package2) { create(:composer_package, :with_metadatum, sha: '123') }
+ let!(:package3) { create(:composer_package, :with_metadatum, sha: '234') }
+
+ subject { described_class.with_composer_target('123').to_a }
+
+ it 'selects packages with the specified sha' do
+ expect(subject).to include(package1)
+ expect(subject).to include(package2)
+ expect(subject).not_to include(package3)
+ end
+ end
+
+ describe '.sort_by_attribute' do
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') }
+ let!(:package1) { create(:npm_package, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
+ let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') }
+ let(:package3) { create(:maven_package, project: project, version: '1.1.1', name: 'zzz') }
+
+ before do
+ travel_to(1.day.ago) do
+ package3
+ end
+ end
+
+ RSpec.shared_examples 'package sorting by attribute' do |order_by|
+ subject { described_class.where(id: packages.map(&:id)).sort_by_attribute("#{order_by}_#{sort}").to_a }
+
+ context "sorting by #{order_by}" do
+ context 'ascending order' do
+ let(:sort) { 'asc' }
+
+ it { is_expected.to eq packages }
+ end
+
+ context 'descending order' do
+ let(:sort) { 'desc' }
+
+ it { is_expected.to eq packages.reverse }
+ end
+ end
+ end
+
+ it_behaves_like 'package sorting by attribute', 'name' do
+ let(:packages) { [package1, package2, package3] }
+ end
+
+ it_behaves_like 'package sorting by attribute', 'created_at' do
+ let(:packages) { [package3, package1, package2] }
+ end
+
+ it_behaves_like 'package sorting by attribute', 'version' do
+ let(:packages) { [package3, package2, package1] }
+ end
+
+ it_behaves_like 'package sorting by attribute', 'type' do
+ let(:packages) { [package3, package1, package2] }
+ end
+
+ it_behaves_like 'package sorting by attribute', 'project_path' do
+ let(:another_project) { create(:project, :public, namespace: group, name: 'project B') }
+ let!(:package4) { create(:npm_package, project: another_project, version: '3.1.0', name: "@#{project.root_namespace.path}/bar") }
+
+ let(:packages) { [package1, package2, package3, package4] }
+ end
+ end
+
+ describe 'validations' do
+ subject { create(:package) }
+
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id, :version, :package_type) }
+
+ describe '#name' do
+ it { is_expected.to allow_value("my/domain/com/my-app").for(:name) }
+ it { is_expected.to allow_value("my.app-11.07.2018").for(:name) }
+ it { is_expected.not_to allow_value("my(dom$$$ain)com.my-app").for(:name) }
+
+ context 'conan package' do
+ subject { create(:conan_package) }
+
+ let(:fifty_one_characters) {'f_b' * 17}
+
+ it { is_expected.to allow_value('foo+bar').for(:name) }
+ it { is_expected.to allow_value('foo_bar').for(:name) }
+ it { is_expected.to allow_value('foo.bar').for(:name) }
+ it { is_expected.not_to allow_value(fifty_one_characters).for(:name) }
+ it { is_expected.not_to allow_value('+foobar').for(:name) }
+ it { is_expected.not_to allow_value('.foobar').for(:name) }
+ it { is_expected.not_to allow_value('%foo%bar').for(:name) }
+ end
+ end
+
+ describe '#version' do
+ RSpec.shared_examples 'validating version to be SemVer compliant for' do |factory_name|
+ context "for #{factory_name}" do
+ subject { create(factory_name) }
+
+ it { is_expected.to allow_value('1.2.3').for(:version) }
+ it { is_expected.to allow_value('1.2.3-beta').for(:version) }
+ it { is_expected.to allow_value('1.2.3-alpha.3').for(:version) }
+ it { is_expected.not_to allow_value('1').for(:version) }
+ it { is_expected.not_to allow_value('1.2').for(:version) }
+ it { is_expected.not_to allow_value('1./2.3').for(:version) }
+ it { is_expected.not_to allow_value('../../../../../1.2.3').for(:version) }
+ it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) }
+ end
+ end
+
+ context 'conan package' do
+ subject { create(:conan_package) }
+
+ let(:fifty_one_characters) {'1.2' * 17}
+
+ it { is_expected.to allow_value('1.2').for(:version) }
+ it { is_expected.to allow_value('1.2.3-beta').for(:version) }
+ it { is_expected.to allow_value('1.2.3-pre1+build2').for(:version) }
+ it { is_expected.not_to allow_value('1').for(:version) }
+ it { is_expected.not_to allow_value(fifty_one_characters).for(:version) }
+ it { is_expected.not_to allow_value('1./2.3').for(:version) }
+ it { is_expected.not_to allow_value('.1.2.3').for(:version) }
+ it { is_expected.not_to allow_value('+1.2.3').for(:version) }
+ it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) }
+ end
+
+ context 'maven package' do
+ subject { create(:maven_package) }
+
+ it { is_expected.to allow_value('0').for(:version) }
+ it { is_expected.to allow_value('1').for(:version) }
+ it { is_expected.to allow_value('10').for(:version) }
+ it { is_expected.to allow_value('1.0').for(:version) }
+ it { is_expected.to allow_value('1.3.350.v20200505-1744').for(:version) }
+ it { is_expected.to allow_value('1.1-beta-2').for(:version) }
+ it { is_expected.to allow_value('1.2-SNAPSHOT').for(:version) }
+ it { is_expected.to allow_value('12.1.2-2-1').for(:version) }
+ it { is_expected.to allow_value('1.2.3..beta').for(:version) }
+ it { is_expected.to allow_value('1.2.3-beta').for(:version) }
+ it { is_expected.to allow_value('10.2.3-beta').for(:version) }
+ it { is_expected.to allow_value('2.0.0.v200706041905-7C78EK9E_EkMNfNOd2d8qq').for(:version) }
+ it { is_expected.to allow_value('1.2-alpha-1-20050205.060708-1').for(:version) }
+ it { is_expected.to allow_value('703220b4e2cea9592caeb9f3013f6b1e5335c293').for(:version) }
+ it { is_expected.to allow_value('RELEASE').for(:version) }
+ it { is_expected.not_to allow_value('..1.2.3').for(:version) }
+ it { is_expected.not_to allow_value(' 1.2.3').for(:version) }
+ it { is_expected.not_to allow_value("1.2.3 \r\t").for(:version) }
+ it { is_expected.not_to allow_value("\r\t 1.2.3").for(:version) }
+ it { is_expected.not_to allow_value('1.2.3-4/../../').for(:version) }
+ it { is_expected.not_to allow_value('1.2.3-4%2e%2e%').for(:version) }
+ it { is_expected.not_to allow_value('../../../../../1.2.3').for(:version) }
+ it { is_expected.not_to allow_value('%2e%2e%2f1.2.3').for(:version) }
+ end
+
+ it_behaves_like 'validating version to be SemVer compliant for', :npm_package
+ it_behaves_like 'validating version to be SemVer compliant for', :nuget_package
+ end
+
+ describe '#package_already_taken' do
+ context 'npm package' do
+ let!(:package) { create(:npm_package) }
+
+ it 'will not allow a package of the same name' do
+ new_package = build(:npm_package, name: package.name)
+
+ expect(new_package).not_to be_valid
+ end
+ end
+
+ context 'maven package' do
+ let!(:package) { create(:maven_package) }
+
+ it 'will allow a package of the same name' do
+ new_package = build(:maven_package, name: package.name)
+
+ expect(new_package).to be_valid
+ end
+ end
+ end
+
+ context "recipe uniqueness for conan packages" do
+ let!(:package) { create('conan_package') }
+
+ it "will allow a conan package with same project, name, version and package_type" do
+ new_package = build('conan_package', project: package.project, name: package.name, version: package.version)
+ new_package.conan_metadatum.package_channel = 'beta'
+ expect(new_package).to be_valid
+ end
+
+ it "will not allow a conan package with same recipe (name, version, metadatum.package_channel, metadatum.package_username, and package_type)" do
+ new_package = build('conan_package', project: package.project, name: package.name, version: package.version)
+ expect(new_package).not_to be_valid
+ expect(new_package.errors.to_a).to include("Package recipe already exists")
+ end
+ end
+
+ Packages::Package.package_types.keys.without('conan').each do |pt|
+ context "project id, name, version and package type uniqueness for package type #{pt}" do
+ let(:package) { create("#{pt}_package") }
+
+ it "will not allow a #{pt} package with same project, name, version and package_type" do
+ new_package = build("#{pt}_package", project: package.project, name: package.name, version: package.version)
+ expect(new_package).not_to be_valid
+ expect(new_package.errors.to_a).to include("Name has already been taken")
+ end
+ end
+ end
+ end
+
+ describe '#destroy' do
+ let(:package) { create(:npm_package) }
+ let(:package_file) { package.package_files.first }
+ let(:project_statistics) { ProjectStatistics.for_project_ids(package.project.id).first }
+
+ it 'affects project statistics' do
+ expect { package.destroy! }
+ .to change { project_statistics.reload.packages_size }
+ .from(package_file.size).to(0)
+ end
+ end
+
+ describe '.by_name_and_file_name' do
+ let!(:package) { create(:npm_package) }
+ let!(:package_file) { package.package_files.first }
+
+ subject { described_class }
+
+ it 'finds a package with correct arguiments' do
+ expect(subject.by_name_and_file_name(package.name, package_file.file_name)).to eq(package)
+ end
+
+ it 'will raise error if not found' do
+ expect { subject.by_name_and_file_name('foo', 'foo-5.5.5.tgz') }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'version scopes' do
+ let!(:package1) { create(:npm_package, version: '1.0.0') }
+ let!(:package2) { create(:npm_package, version: '1.0.1') }
+ let!(:package3) { create(:npm_package, version: '1.0.1') }
+
+ describe '.last_of_each_version' do
+ subject { described_class.last_of_each_version }
+
+ it 'includes only latest package per version' do
+ is_expected.to include(package1, package3)
+ is_expected.not_to include(package2)
+ end
+ end
+
+ describe '.has_version' do
+ subject { described_class.has_version }
+
+ before do
+ create(:maven_metadatum).package.update!(version: nil)
+ end
+
+ it 'includes only packages with version attribute' do
+ is_expected.to match_array([package1, package2, package3])
+ end
+ end
+
+ describe '.with_version' do
+ subject { described_class.with_version('1.0.1') }
+
+ it 'includes only packages with specified version' do
+ is_expected.to match_array([package2, package3])
+ end
+ end
+
+ describe '.without_version_like' do
+ let(:version_pattern) { '%.0.0%' }
+
+ subject { described_class.without_version_like(version_pattern) }
+
+ it 'includes packages without the version pattern' do
+ is_expected.to match_array([package2, package3])
+ end
+ end
+ end
+
+ context 'conan scopes' do
+ let!(:package) { create(:conan_package) }
+
+ describe '.with_conan_channel' do
+ subject { described_class.with_conan_channel('stable') }
+
+ it 'includes only packages with specified version' do
+ is_expected.to include(package)
+ end
+ end
+
+ describe '.with_conan_username' do
+ subject do
+ described_class.with_conan_username(
+ Packages::Conan::Metadatum.package_username_from(full_path: package.project.full_path)
+ )
+ end
+
+ it 'includes only packages with specified version' do
+ is_expected.to match_array([package])
+ end
+ end
+ end
+
+ describe '.without_nuget_temporary_name' do
+ let!(:package1) { create(:nuget_package) }
+ let!(:package2) { create(:nuget_package, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
+
+ subject { described_class.without_nuget_temporary_name }
+
+ it 'does not include nuget temporary packages' do
+ expect(subject).to eq([package1])
+ end
+ end
+
+ describe '.processed' do
+ let!(:package1) { create(:nuget_package) }
+ let!(:package2) { create(:npm_package) }
+ let!(:package3) { create(:nuget_package) }
+
+ subject { described_class.processed }
+
+ it { is_expected.to match_array([package1, package2, package3]) }
+
+ context 'with temporary packages' do
+ let!(:package1) { create(:nuget_package, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
+
+ it { is_expected.to match_array([package2, package3]) }
+ end
+ end
+
+ describe '.limit_recent' do
+ let!(:package1) { create(:nuget_package) }
+ let!(:package2) { create(:nuget_package) }
+ let!(:package3) { create(:nuget_package) }
+
+ subject { described_class.limit_recent(2) }
+
+ it { is_expected.to match_array([package3, package2]) }
+ end
+
+ context 'with several packages' do
+ let_it_be(:package1) { create(:nuget_package, name: 'FooBar') }
+ let_it_be(:package2) { create(:nuget_package, name: 'foobar') }
+ let_it_be(:package3) { create(:npm_package) }
+ let_it_be(:package4) { create(:npm_package) }
+
+ describe '.pluck_names' do
+ subject { described_class.pluck_names }
+
+ it { is_expected.to match_array([package1, package2, package3, package4].map(&:name)) }
+ end
+
+ describe '.pluck_versions' do
+ subject { described_class.pluck_versions }
+
+ it { is_expected.to match_array([package1, package2, package3, package4].map(&:version)) }
+ end
+
+ describe '.with_name_like' do
+ subject { described_class.with_name_like(name_term) }
+
+ context 'with downcase name' do
+ let(:name_term) { 'foobar' }
+
+ it { is_expected.to match_array([package1, package2]) }
+ end
+
+ context 'with prefix wildcard' do
+ let(:name_term) { '%ar' }
+
+ it { is_expected.to match_array([package1, package2]) }
+ end
+
+ context 'with suffix wildcard' do
+ let(:name_term) { 'foo%' }
+
+ it { is_expected.to match_array([package1, package2]) }
+ end
+
+ context 'with surrounding wildcards' do
+ let(:name_term) { '%ooba%' }
+
+ it { is_expected.to match_array([package1, package2]) }
+ end
+ end
+
+ describe '.search_by_name' do
+ let(:query) { 'oba' }
+
+ subject { described_class.search_by_name(query) }
+
+ it { is_expected.to match_array([package1, package2]) }
+ end
+ end
+
+ describe '.select_distinct_name' do
+ let_it_be(:nuget_package) { create(:nuget_package) }
+ let_it_be(:nuget_packages) { create_list(:nuget_package, 3, name: nuget_package.name, project: nuget_package.project) }
+ let_it_be(:maven_package) { create(:maven_package) }
+ let_it_be(:maven_packages) { create_list(:maven_package, 3, name: maven_package.name, project: maven_package.project) }
+
+ subject { described_class.select_distinct_name }
+
+ it 'returns only distinct names' do
+ packages = subject
+
+ expect(packages.size).to eq(2)
+ expect(packages.pluck(:name)).to match_array([nuget_package.name, maven_package.name])
+ end
+ end
+
+ describe '#versions' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:package) { create(:maven_package, project: project) }
+ let_it_be(:package2) { create(:maven_package, project: project) }
+ let_it_be(:package3) { create(:maven_package, project: project, name: 'foo') }
+
+ it 'returns other package versions of the same package name belonging to the project' do
+ expect(package.versions).to contain_exactly(package2)
+ end
+
+ it 'does not return different packages' do
+ expect(package.versions).not_to include(package3)
+ end
+ end
+
+ describe '#pipeline' do
+ let_it_be(:package) { create(:maven_package) }
+
+ context 'package without pipeline' do
+ it 'returns nil if there is no pipeline' do
+ expect(package.pipeline).to be_nil
+ end
+ end
+
+ context 'package with pipeline' do
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
+ before do
+ package.create_build_info!(pipeline: pipeline)
+ end
+
+ it 'returns the pipeline' do
+ expect(package.pipeline).to eq(pipeline)
+ end
+ end
+ end
+
+ describe '#tag_names' do
+ let_it_be(:package) { create(:nuget_package) }
+
+ subject { package.tag_names }
+
+ it { is_expected.to eq([]) }
+
+ context 'with tags' do
+ let(:tags) { %w(tag1 tag2 tag3) }
+
+ before do
+ tags.each { |t| create(:packages_tag, name: t, package: package) }
+ end
+
+ it { is_expected.to contain_exactly(*tags) }
+ end
+ end
+end
diff --git a/spec/models/packages/pypi/metadatum_spec.rb b/spec/models/packages/pypi/metadatum_spec.rb
new file mode 100644
index 00000000000..2c9893ef8f3
--- /dev/null
+++ b/spec/models/packages/pypi/metadatum_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Pypi::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+
+ describe '#pypi_package_type' do
+ it 'will not allow a package with a different package_type' do
+ package = build('package')
+ pypi_metadatum = build('pypi_metadatum', package: package)
+
+ expect(pypi_metadatum).not_to be_valid
+ expect(pypi_metadatum.errors.to_a).to include('Package type must be PyPi')
+ end
+ end
+ end
+end
diff --git a/spec/models/packages/sem_ver_spec.rb b/spec/models/packages/sem_ver_spec.rb
new file mode 100644
index 00000000000..419653dca19
--- /dev/null
+++ b/spec/models/packages/sem_ver_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::SemVer, type: :model do
+ shared_examples '#parse with a valid semver' do |str, major, minor, patch, prerelease, build|
+ context "with #{str}" do
+ it "returns #{described_class.new(major, minor, patch, prerelease, build, prefixed: true)} with prefix" do
+ expected = described_class.new(major, minor, patch, prerelease, build, prefixed: true)
+ expect(described_class.parse('v' + str, prefixed: true)).to eq(expected)
+ end
+
+ it "returns #{described_class.new(major, minor, patch, prerelease, build)} without prefix" do
+ expected = described_class.new(major, minor, patch, prerelease, build)
+ expect(described_class.parse(str)).to eq(expected)
+ end
+ end
+ end
+
+ shared_examples '#parse with an invalid semver' do |str|
+ context "with #{str}" do
+ it 'returns nil with prefix' do
+ expect(described_class.parse('v' + str, prefixed: true)).to be_nil
+ end
+
+ it 'returns nil without prefix' do
+ expect(described_class.parse(str)).to be_nil
+ end
+ end
+ end
+
+ describe '#parse' do
+ it_behaves_like '#parse with a valid semver', '1.0.0', 1, 0, 0, nil, nil
+ it_behaves_like '#parse with a valid semver', '1.0.0-pre', 1, 0, 0, 'pre', nil
+ it_behaves_like '#parse with a valid semver', '1.0.0+build', 1, 0, 0, nil, 'build'
+ it_behaves_like '#parse with a valid semver', '1.0.0-pre+build', 1, 0, 0, 'pre', 'build'
+ it_behaves_like '#parse with an invalid semver', '01.0.0'
+ it_behaves_like '#parse with an invalid semver', '0.01.0'
+ it_behaves_like '#parse with an invalid semver', '0.0.01'
+ it_behaves_like '#parse with an invalid semver', '1.0.0asdf'
+ end
+end
diff --git a/spec/models/packages/tag_spec.rb b/spec/models/packages/tag_spec.rb
new file mode 100644
index 00000000000..18ec99c3d51
--- /dev/null
+++ b/spec/models/packages/tag_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Tag, type: :model do
+ let!(:project) { create(:project) }
+ let!(:package) { create(:npm_package, version: '1.0.2', project: project, updated_at: 3.days.ago) }
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package).inverse_of(:tags) }
+ end
+
+ describe 'validations' do
+ subject { create(:packages_tag) }
+
+ it { is_expected.to validate_presence_of(:package) }
+ it { is_expected.to validate_presence_of(:name) }
+ end
+
+ describe '.for_packages' do
+ let(:package2) { create(:package, project: project, updated_at: 2.days.ago) }
+ let(:package3) { create(:package, project: project, updated_at: 1.day.ago) }
+ let!(:tag1) { create(:packages_tag, package: package) }
+ let!(:tag2) { create(:packages_tag, package: package2) }
+ let!(:tag3) { create(:packages_tag, package: package3) }
+
+ subject { described_class.for_packages(project.packages) }
+
+ it { is_expected.to match_array([tag1, tag2, tag3]) }
+
+ context 'with too many tags' do
+ before do
+ stub_const('Packages::Tag::FOR_PACKAGES_TAGS_LIMIT', 2)
+ end
+
+ it { is_expected.to match_array([tag2, tag3]) }
+ end
+ end
+
+ describe '.with_name' do
+ let_it_be(:package) { create(:package) }
+ let_it_be(:tag1) { create(:packages_tag, package: package, name: 'tag1') }
+ let_it_be(:tag2) { create(:packages_tag, package: package, name: 'tag2') }
+ let_it_be(:tag3) { create(:packages_tag, package: package, name: 'tag3') }
+ let(:name) { 'tag1' }
+
+ subject { described_class.with_name(name) }
+
+ it { is_expected.to contain_exactly(tag1) }
+
+ context 'with nil name' do
+ let(:name) { nil }
+
+ it { is_expected.to eq([]) }
+ end
+
+ context 'with multiple names' do
+ let(:name) { %w(tag1 tag3) }
+
+ it { is_expected.to contain_exactly(tag1, tag3) }
+ end
+ end
+end
diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb
index c05d4c82634..38bd9b39a56 100644
--- a/spec/models/pages/lookup_path_spec.rb
+++ b/spec/models/pages/lookup_path_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Pages::LookupPath do
+RSpec.describe Pages::LookupPath do
let(:project) do
instance_double(Project,
id: 12345,
diff --git a/spec/models/pages/virtual_domain_spec.rb b/spec/models/pages/virtual_domain_spec.rb
index a5310738482..38f5f4d2538 100644
--- a/spec/models/pages/virtual_domain_spec.rb
+++ b/spec/models/pages/virtual_domain_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Pages::VirtualDomain do
+RSpec.describe Pages::VirtualDomain do
describe '#certificate and #key pair' do
let(:domain) { nil }
let(:project) { instance_double(Project) }
diff --git a/spec/models/pages_domain_acme_order_spec.rb b/spec/models/pages_domain_acme_order_spec.rb
index 4ffb4fc7389..4a104203e39 100644
--- a/spec/models/pages_domain_acme_order_spec.rb
+++ b/spec/models/pages_domain_acme_order_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PagesDomainAcmeOrder do
+RSpec.describe PagesDomainAcmeOrder do
using RSpec::Parameterized::TableSyntax
describe '.expired' do
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index fc7694530d0..d283389e29e 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PagesDomain do
+RSpec.describe PagesDomain do
using RSpec::Parameterized::TableSyntax
subject(:pages_domain) { described_class.new }
diff --git a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
index ef7298c3d8c..61174a7d0c5 100644
--- a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
+++ b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PerformanceMonitoring::PrometheusDashboard do
+RSpec.describe PerformanceMonitoring::PrometheusDashboard do
let(:json_content) do
{
"dashboard" => "Dashboard Title",
@@ -50,19 +50,19 @@ describe PerformanceMonitoring::PrometheusDashboard do
context 'dashboard content is missing' do
let(:json_content) { nil }
- it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
+ it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'dashboard content is NOT a hash' do
let(:json_content) { YAML.safe_load("'test'") }
- it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
+ it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'content is an array' do
let(:json_content) { [{ "dashboard" => "Dashboard Title" }] }
- it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
+ it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'dashboard definition is missing panels_groups and dashboard keys' do
@@ -72,7 +72,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
- it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
+ it_behaves_like 'validation failed', panel_groups: ["should be an array of panel_groups objects"], dashboard: ["can't be blank"]
end
context 'group definition is missing panels and group keys' do
@@ -88,7 +88,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
- it_behaves_like 'validation failed', panels: ["can't be blank"], group: ["can't be blank"]
+ it_behaves_like 'validation failed', panels: ["should be an array of panels objects"], group: ["can't be blank"]
end
context 'panel definition is missing metrics and title keys' do
@@ -110,7 +110,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
}
end
- it_behaves_like 'validation failed', metrics: ["can't be blank"], title: ["can't be blank"]
+ it_behaves_like 'validation failed', metrics: ["should be an array of metrics objects"], title: ["can't be blank"]
end
context 'metrics definition is missing unit, query and query_range keys' do
@@ -180,7 +180,7 @@ describe PerformanceMonitoring::PrometheusDashboard do
describe '.find_for' do
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user) }
- let(:environment) { build_stubbed(:environment) }
+ let(:environment) { build_stubbed(:environment, project: project) }
let(:path) { ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH }
context 'dashboard has been found' do
diff --git a/spec/models/performance_monitoring/prometheus_metric_spec.rb b/spec/models/performance_monitoring/prometheus_metric_spec.rb
index 83f687aa90e..b5b9cd58aa8 100644
--- a/spec/models/performance_monitoring/prometheus_metric_spec.rb
+++ b/spec/models/performance_monitoring/prometheus_metric_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PerformanceMonitoring::PrometheusMetric do
+RSpec.describe PerformanceMonitoring::PrometheusMetric do
let(:json_content) do
{
"id" => "metric_of_ages",
diff --git a/spec/models/performance_monitoring/prometheus_panel_group_spec.rb b/spec/models/performance_monitoring/prometheus_panel_group_spec.rb
index ecf7e13a9a3..9e92cb27954 100644
--- a/spec/models/performance_monitoring/prometheus_panel_group_spec.rb
+++ b/spec/models/performance_monitoring/prometheus_panel_group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PerformanceMonitoring::PrometheusPanelGroup do
+RSpec.describe PerformanceMonitoring::PrometheusPanelGroup do
let(:json_content) do
{
"group" => "Group Title",
diff --git a/spec/models/performance_monitoring/prometheus_panel_spec.rb b/spec/models/performance_monitoring/prometheus_panel_spec.rb
index 127b9e8183a..c5c6b1fdafd 100644
--- a/spec/models/performance_monitoring/prometheus_panel_spec.rb
+++ b/spec/models/performance_monitoring/prometheus_panel_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PerformanceMonitoring::PrometheusPanel do
+RSpec.describe PerformanceMonitoring::PrometheusPanel do
let(:json_content) do
{
"max_value" => 1,
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
index a3f5eb38511..a39a37b605f 100644
--- a/spec/models/personal_access_token_spec.rb
+++ b/spec/models/personal_access_token_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PersonalAccessToken do
+RSpec.describe PersonalAccessToken do
subject { described_class }
describe '.build' do
@@ -165,6 +165,7 @@ describe PersonalAccessToken do
let_it_be(:revoked_token) { create(:personal_access_token, revoked: true) }
let_it_be(:valid_token_and_notified) { create(:personal_access_token, expires_at: 2.days.from_now, expire_notification_delivered: true) }
let_it_be(:valid_token) { create(:personal_access_token, expires_at: 2.days.from_now) }
+ let_it_be(:long_expiry_token) { create(:personal_access_token, expires_at: '999999-12-31'.to_date) }
context 'in one day' do
it "doesn't have any tokens" do
@@ -187,10 +188,24 @@ describe PersonalAccessToken do
expect(described_class.without_impersonation).to contain_exactly(personal_access_token)
end
end
+
+ describe 'revoke scopes' do
+ let_it_be(:revoked_token) { create(:personal_access_token, :revoked) }
+ let_it_be(:non_revoked_token) { create(:personal_access_token, revoked: false) }
+ let_it_be(:non_revoked_token2) { create(:personal_access_token, revoked: nil) }
+
+ describe '.revoked' do
+ it { expect(described_class.revoked).to contain_exactly(revoked_token) }
+ end
+
+ describe '.not_revoked' do
+ it { expect(described_class.not_revoked).to contain_exactly(non_revoked_token, non_revoked_token2) }
+ end
+ end
end
describe '.simple_sorts' do
- it 'includes overriden keys' do
+ it 'includes overridden keys' do
expect(described_class.simple_sorts.keys).to include(*%w(expires_at_asc expires_at_desc))
end
end
diff --git a/spec/models/personal_snippet_spec.rb b/spec/models/personal_snippet_spec.rb
index fb96d6e8bc3..10d70fed1ee 100644
--- a/spec/models/personal_snippet_spec.rb
+++ b/spec/models/personal_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PersonalSnippet do
+RSpec.describe PersonalSnippet do
describe '#embeddable?' do
[
{ snippet: :public, embeddable: true },
diff --git a/spec/models/plan_limits_spec.rb b/spec/models/plan_limits_spec.rb
index 1366f088623..831fd0dcbc3 100644
--- a/spec/models/plan_limits_spec.rb
+++ b/spec/models/plan_limits_spec.rb
@@ -2,57 +2,217 @@
require 'spec_helper'
-describe PlanLimits do
- let(:plan_limits) { create(:plan_limits) }
- let(:model) { ProjectHook }
- let(:count) { model.count }
+RSpec.describe PlanLimits do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:plan_limits) { create(:plan_limits) }
+ let(:project_hooks_count) { 2 }
before do
- create(:project_hook)
+ create_list(:project_hook, project_hooks_count, project: project)
end
- context 'without plan limits configured' do
- describe '#exceeded?' do
- it 'does not exceed any relation offset' do
- expect(plan_limits.exceeded?(:project_hooks, model)).to be false
- expect(plan_limits.exceeded?(:project_hooks, count)).to be false
+ describe '#exceeded?' do
+ let(:alternate_limit) { double('an alternate limit value') }
+
+ subject(:exceeded_limit) { plan_limits.exceeded?(:project_hooks, limit_subject, alternate_limit: alternate_limit) }
+
+ before do
+ allow(plan_limits).to receive(:limit_for).with(:project_hooks, alternate_limit: alternate_limit).and_return(limit)
+ end
+
+ shared_examples_for 'comparing limits' do
+ context 'when limit for given name results to a disabled value' do
+ let(:limit) { nil }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when limit for given name results to a non-disabled value' do
+ context 'and given count is smaller than limit' do
+ let(:limit) { project_hooks_count + 1 }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'and given count is equal to the limit' do
+ let(:limit) { project_hooks_count }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'and given count is greater than the limit' do
+ let(:limit) { project_hooks_count - 1 }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+ end
+
+ context 'when given limit subject is an integer' do
+ let(:limit_subject) { project.hooks.count }
+
+ it_behaves_like 'comparing limits'
+ end
+
+ context 'when given limit subject is an ActiveRecord::Relation' do
+ let(:limit_subject) { project.hooks }
+
+ it_behaves_like 'comparing limits'
+ end
+
+ context 'when given limit subject is something else' do
+ let(:limit_subject) { ProjectHook }
+ let(:limit) { 100 }
+
+ it 'raises an error' do
+ expect { exceeded_limit }.to raise_error(ArgumentError)
end
end
end
- context 'with plan limits configured' do
- before do
- plan_limits.update!(project_hooks: 2)
+ describe '#limit_for' do
+ let(:alternate_limit) { nil }
+
+ subject(:limit) { plan_limits.limit_for(:project_hooks, alternate_limit: alternate_limit) }
+
+ context 'when given limit name does not exist' do
+ it 'raises an error' do
+ expect { plan_limits.limit_for(:project_foo) }.to raise_error(described_class::LimitUndefinedError)
+ end
end
- describe '#exceeded?' do
- it 'does not exceed the relation offset' do
- expect(plan_limits.exceeded?(:project_hooks, model)).to be false
- expect(plan_limits.exceeded?(:project_hooks, count)).to be false
+ context 'when given limit name is disabled' do
+ before do
+ plan_limits.update!(project_hooks: 0)
+ end
+
+ it { is_expected.to eq(nil) }
+
+ context 'and alternate_limit is a non-zero integer' do
+ let(:alternate_limit) { 1 }
+
+ it { is_expected.to eq(1) }
+ end
+
+ context 'and alternate_limit is zero' do
+ let(:alternate_limit) { 0 }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'and alternate_limit is a proc that returns non-zero integer' do
+ let(:alternate_limit) { -> { 1 } }
+
+ it { is_expected.to eq(1) }
+ end
+
+ context 'and alternate_limit is a proc that returns zero' do
+ let(:alternate_limit) { -> { 0 } }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'and alternate_limit is a proc that returns nil' do
+ let(:alternate_limit) { -> { nil } }
+
+ it { is_expected.to eq(nil) }
end
end
- context 'with boundary values' do
+ context 'when given limit name is enabled' do
+ let(:plan_limit_value) { 2 }
+
before do
- create(:project_hook)
+ plan_limits.update!(project_hooks: plan_limit_value)
end
- describe '#exceeded?' do
- it 'does exceed the relation offset' do
- expect(plan_limits.exceeded?(:project_hooks, model)).to be true
- expect(plan_limits.exceeded?(:project_hooks, count)).to be true
- end
+ context 'and alternate_limit is a non-zero integer that is bigger than the plan limit' do
+ let(:alternate_limit) { plan_limit_value + 1 }
+
+ it { is_expected.to eq(plan_limit_value) }
+ end
+
+ context 'and alternate_limit is a non-zero integer that is smaller than the plan limit' do
+ let(:alternate_limit) { plan_limit_value - 1 }
+
+ it { is_expected.to eq(alternate_limit) }
+ end
+
+ context 'and alternate_limit is zero' do
+ let(:alternate_limit) { 0 }
+
+ it { is_expected.to eq(plan_limit_value) }
+ end
+
+ context 'and alternate_limit is a proc that returns non-zero integer that is bigger than the plan limit' do
+ let(:alternate_limit) { -> { plan_limit_value + 1 } }
+
+ it { is_expected.to eq(plan_limit_value) }
+ end
+
+ context 'and alternate_limit is a proc that returns non-zero integer that is smaller than the plan limit' do
+ let(:alternate_limit) { -> { plan_limit_value - 1 } }
+
+ it { is_expected.to eq(alternate_limit.call) }
+ end
+
+ context 'and alternate_limit is a proc that returns zero' do
+ let(:alternate_limit) { -> { 0 } }
+
+ it { is_expected.to eq(plan_limit_value) }
+ end
+
+ context 'and alternate_limit is a proc that returns nil' do
+ let(:alternate_limit) { -> { nil } }
+
+ it { is_expected.to eq(plan_limit_value) }
end
end
end
context 'validates default values' do
+ # TODO: For now, these columns have default values set to 0.
+ # Each artifact type listed here have their own matching issues to determine
+ # the actual limit value. In each of those issues, the default value should also be updated to
+ # a non-zero value. Also update existing values of zero to whatever the default value will be.
+ # For a list of the issues, see: https://gitlab.com/gitlab-org/gitlab/-/issues/211378#note_355619970
+ let(:disabled_max_artifact_size_columns) do
+ %w[
+ ci_max_artifact_size_archive
+ ci_max_artifact_size_metadata
+ ci_max_artifact_size_trace
+ ci_max_artifact_size_junit
+ ci_max_artifact_size_sast
+ ci_max_artifact_size_dependency_scanning
+ ci_max_artifact_size_container_scanning
+ ci_max_artifact_size_dast
+ ci_max_artifact_size_codequality
+ ci_max_artifact_size_license_management
+ ci_max_artifact_size_license_scanning
+ ci_max_artifact_size_performance
+ ci_max_artifact_size_browser_performance
+ ci_max_artifact_size_load_performance
+ ci_max_artifact_size_metrics
+ ci_max_artifact_size_metrics_referee
+ ci_max_artifact_size_network_referee
+ ci_max_artifact_size_dotenv
+ ci_max_artifact_size_cobertura
+ ci_max_artifact_size_terraform
+ ci_max_artifact_size_accessibility
+ ci_max_artifact_size_cluster_applications
+ ci_max_artifact_size_secret_detection
+ ci_max_artifact_size_requirements
+ ci_max_artifact_size_coverage_fuzzing
+ ]
+ end
+
let(:columns_with_zero) do
%w[
ci_active_pipelines
ci_pipeline_size
ci_active_jobs
- ]
+ storage_size_limit
+ ] + disabled_max_artifact_size_columns
end
it "has positive values for enabled limits" do
diff --git a/spec/models/plan_spec.rb b/spec/models/plan_spec.rb
index 3f3b8046232..490c6b1bbf7 100644
--- a/spec/models/plan_spec.rb
+++ b/spec/models/plan_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Plan do
+RSpec.describe Plan do
describe '#default?' do
subject { plan.default? }
@@ -14,4 +14,16 @@ describe Plan do
end
end
end
+
+ context 'when updating plan limits' do
+ let(:plan) { described_class.default }
+
+ it { expect(plan).to be_persisted }
+
+ it { expect(plan.actual_limits).not_to be_persisted }
+
+ it 'successfully updates the limits' do
+ expect(plan.actual_limits.update!(ci_instance_level_variables: 100)).to be_truthy
+ end
+ end
end
diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb
index ae00f9df89e..92b3e41cd18 100644
--- a/spec/models/pool_repository_spec.rb
+++ b/spec/models/pool_repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PoolRepository do
+RSpec.describe PoolRepository do
describe 'associations' do
it { is_expected.to belong_to(:shard) }
it { is_expected.to belong_to(:source_project) }
diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb
index d435fccc09a..02a4d783b84 100644
--- a/spec/models/postgresql/replication_slot_spec.rb
+++ b/spec/models/postgresql/replication_slot_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Postgresql::ReplicationSlot do
+RSpec.describe Postgresql::ReplicationSlot do
describe '.in_use?' do
it 'returns true when replication slots are present' do
expect(described_class).to receive(:exists?).and_return(true)
diff --git a/spec/models/product_analytics_event_spec.rb b/spec/models/product_analytics_event_spec.rb
new file mode 100644
index 00000000000..6058df9fa13
--- /dev/null
+++ b/spec/models/product_analytics_event_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe ProductAnalyticsEvent, type: :model do
+ it { is_expected.to belong_to(:project) }
+ it { expect(described_class).to respond_to(:order_by_time) }
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:project_id) }
+ it { is_expected.to validate_presence_of(:event_id) }
+ it { is_expected.to validate_presence_of(:v_collector) }
+ it { is_expected.to validate_presence_of(:v_etl) }
+ end
+
+ describe '.timerange' do
+ let_it_be(:event_1) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 1.day) }
+ let_it_be(:event_2) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 5.days) }
+ let_it_be(:event_3) { create(:product_analytics_event, collector_tstamp: Time.zone.now - 15.days) }
+
+ it { expect(described_class.timerange(3.days)).to match_array([event_1]) }
+ it { expect(described_class.timerange(7.days)).to match_array([event_1, event_2]) }
+ it { expect(described_class.timerange(30.days)).to match_array([event_1, event_2, event_3]) }
+ end
+end
diff --git a/spec/models/programming_language_spec.rb b/spec/models/programming_language_spec.rb
index b327d360461..f2201eabd1c 100644
--- a/spec/models/programming_language_spec.rb
+++ b/spec/models/programming_language_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProgrammingLanguage do
+RSpec.describe ProgrammingLanguage do
it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:color) }
diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb
index 6f06fe4e55a..c517fc8be55 100644
--- a/spec/models/project_authorization_spec.rb
+++ b/spec/models/project_authorization_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectAuthorization do
+RSpec.describe ProjectAuthorization do
let(:user) { create(:user) }
let(:project1) { create(:project) }
let(:project2) { create(:project) }
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 5af25ac1437..8313879114f 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectAutoDevops do
+RSpec.describe ProjectAutoDevops do
let_it_be(:project) { build(:project) }
it_behaves_like 'having unique enum values'
diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb
index ecca371ce4e..698465e854a 100644
--- a/spec/models/project_ci_cd_setting_spec.rb
+++ b/spec/models/project_ci_cd_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectCiCdSetting do
+RSpec.describe ProjectCiCdSetting do
describe 'validations' do
it 'validates default_git_depth is between 0 and 1000 or nil' do
expect(subject).to validate_numericality_of(:default_git_depth)
diff --git a/spec/models/project_custom_attribute_spec.rb b/spec/models/project_custom_attribute_spec.rb
index 80638676b49..25ee1e60819 100644
--- a/spec/models/project_custom_attribute_spec.rb
+++ b/spec/models/project_custom_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectCustomAttribute do
+RSpec.describe ProjectCustomAttribute do
describe 'assocations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_daily_statistic_spec.rb b/spec/models/project_daily_statistic_spec.rb
index 86210af15d8..8dbabdb3829 100644
--- a/spec/models/project_daily_statistic_spec.rb
+++ b/spec/models/project_daily_statistic_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe ProjectDailyStatistic do
+RSpec.describe ProjectDailyStatistic do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_export_job_spec.rb b/spec/models/project_export_job_spec.rb
index dc39d0e401d..5a2b1443f8b 100644
--- a/spec/models/project_export_job_spec.rb
+++ b/spec/models/project_export_job_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectExportJob, type: :model do
+RSpec.describe ProjectExportJob, type: :model do
let(:project) { create(:project) }
let!(:job1) { create(:project_export_job, project: project, status: 0) }
let!(:job2) { create(:project_export_job, project: project, status: 2) }
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index e33ea75bc5d..c927c8fd1f9 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectFeature do
+RSpec.describe ProjectFeature do
using RSpec::Parameterized::TableSyntax
let(:project) { create(:project) }
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 8ef29e8a876..c925d87170c 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectGroupLink do
+RSpec.describe ProjectGroupLink do
describe "Associations" do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/project_import_data_spec.rb b/spec/models/project_import_data_spec.rb
index fe47811f074..50a2ee42084 100644
--- a/spec/models/project_import_data_spec.rb
+++ b/spec/models/project_import_data_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectImportData do
+RSpec.describe ProjectImportData do
describe '#merge_data' do
it 'writes the Hash to the attribute if it is nil' do
row = described_class.new
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index f3b83c036b5..6a0402d43a8 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectImportState, type: :model do
+RSpec.describe ProjectImportState, type: :model do
let_it_be(:correlation_id) { 'cid' }
let_it_be(:import_state, refind: true) { create(:import_state, correlation_id_value: correlation_id) }
diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb
index 330aab9f856..f451c2905e6 100644
--- a/spec/models/project_label_spec.rb
+++ b/spec/models/project_label_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectLabel do
+RSpec.describe ProjectLabel do
describe 'relationships' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_metrics_setting_spec.rb b/spec/models/project_metrics_setting_spec.rb
index adfbbbc3a45..6639f9cb208 100644
--- a/spec/models/project_metrics_setting_spec.rb
+++ b/spec/models/project_metrics_setting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectMetricsSetting do
+RSpec.describe ProjectMetricsSetting do
describe 'Associations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_repository_spec.rb b/spec/models/project_repository_spec.rb
index c966447fedc..6852ca0097d 100644
--- a/spec/models/project_repository_spec.rb
+++ b/spec/models/project_repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectRepository do
+RSpec.describe ProjectRepository do
describe 'associations' do
it { is_expected.to belong_to(:shard) }
it { is_expected.to belong_to(:project) }
diff --git a/spec/models/project_services/alerts_service_spec.rb b/spec/models/project_services/alerts_service_spec.rb
index 4e63ece26d8..db25885c76a 100644
--- a/spec/models/project_services/alerts_service_spec.rb
+++ b/spec/models/project_services/alerts_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AlertsService do
+RSpec.describe AlertsService do
let_it_be(:project) { create(:project) }
let(:service_params) { { project: project, active: active } }
let(:active) { true }
diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb
index 8b6f2888c0a..7a6fe4b1537 100644
--- a/spec/models/project_services/asana_service_spec.rb
+++ b/spec/models/project_services/asana_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AsanaService do
+RSpec.describe AsanaService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
index 2c86c0ec7be..207add6f090 100644
--- a/spec/models/project_services/assembla_service_spec.rb
+++ b/spec/models/project_services/assembla_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe AssemblaService do
+RSpec.describe AssemblaService do
include StubRequests
describe "Associations" do
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index c1efa3a4348..4d2474cc56a 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BambooService, :use_clean_rails_memory_store_caching do
+RSpec.describe BambooService, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
include StubRequests
diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb
index ab939e0d2f8..560c7c3ee83 100644
--- a/spec/models/project_services/bugzilla_service_spec.rb
+++ b/spec/models/project_services/bugzilla_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BugzillaService do
+RSpec.describe BugzillaService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -32,49 +32,4 @@ describe BugzillaService do
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
-
- context 'overriding properties' do
- let(:url) { 'http://bugzilla.example.com' }
- let(:access_params) do
- { project_url: url, issues_url: url, new_issue_url: url }
- end
-
- # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- context 'when data are stored in properties' do
- let(:properties) { access_params.merge(title: title, description: description) }
- let(:service) do
- create(:bugzilla_service, :without_properties_callback, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in separated fields' do
- let(:service) do
- create(:bugzilla_service, title: title, description: description, properties: access_params)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in both properties and separated fields' do
- let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
- let(:service) do
- create(:bugzilla_service, :without_properties_callback, title: title, description: description, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:bugzilla_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('Bugzilla')
- expect(service.description).to eq('Bugzilla issue tracker')
- end
- end
- end
end
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 8b6fa36eaa5..ff717a59e7b 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe BuildkiteService, :use_clean_rails_memory_store_caching do
+RSpec.describe BuildkiteService, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
include StubRequests
diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb
index 0d3dd89e93b..ea3990b339b 100644
--- a/spec/models/project_services/campfire_service_spec.rb
+++ b/spec/models/project_services/campfire_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CampfireService do
+RSpec.describe CampfireService do
include StubRequests
describe 'Associations' do
diff --git a/spec/models/project_services/chat_message/alert_message_spec.rb b/spec/models/project_services/chat_message/alert_message_spec.rb
index a1dd332c005..927c5dffe77 100644
--- a/spec/models/project_services/chat_message/alert_message_spec.rb
+++ b/spec/models/project_services/chat_message/alert_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::AlertMessage do
+RSpec.describe ChatMessage::AlertMessage do
subject { described_class.new(args) }
let_it_be(:start_time) { Time.current }
diff --git a/spec/models/project_services/chat_message/base_message_spec.rb b/spec/models/project_services/chat_message/base_message_spec.rb
index 8f80cf0b074..a7ddf230758 100644
--- a/spec/models/project_services/chat_message/base_message_spec.rb
+++ b/spec/models/project_services/chat_message/base_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::BaseMessage do
+RSpec.describe ChatMessage::BaseMessage do
let(:base_message) { described_class.new(args) }
let(:args) { { project_url: 'https://gitlab-domain.com' } }
diff --git a/spec/models/project_services/chat_message/deployment_message_spec.rb b/spec/models/project_services/chat_message/deployment_message_spec.rb
index 42c1689db3d..9c361f90ae0 100644
--- a/spec/models/project_services/chat_message/deployment_message_spec.rb
+++ b/spec/models/project_services/chat_message/deployment_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::DeploymentMessage do
+RSpec.describe ChatMessage::DeploymentMessage do
describe '#pretext' do
it 'returns a message with the data returned by the deployment data builder' do
environment = create(:environment, name: "myenvironment")
diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb
index c4d10be8331..051f4780ba4 100644
--- a/spec/models/project_services/chat_message/issue_message_spec.rb
+++ b/spec/models/project_services/chat_message/issue_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::IssueMessage do
+RSpec.describe ChatMessage::IssueMessage do
subject { described_class.new(args) }
let(:args) do
diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb
index 6063ef4ecb3..45be5212508 100644
--- a/spec/models/project_services/chat_message/merge_message_spec.rb
+++ b/spec/models/project_services/chat_message/merge_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::MergeMessage do
+RSpec.describe ChatMessage::MergeMessage do
subject { described_class.new(args) }
let(:args) do
diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb
index 5e7987dc0f6..6a741365d55 100644
--- a/spec/models/project_services/chat_message/note_message_spec.rb
+++ b/spec/models/project_services/chat_message/note_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::NoteMessage do
+RSpec.describe ChatMessage::NoteMessage do
subject { described_class.new(args) }
let(:color) { '#345' }
diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb
index a7171577063..4eb2f57315b 100644
--- a/spec/models/project_services/chat_message/pipeline_message_spec.rb
+++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe ChatMessage::PipelineMessage do
+RSpec.describe ChatMessage::PipelineMessage do
subject { described_class.new(args) }
let(:args) do
diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb
index 9d990508ab2..e3ba4c2aefe 100644
--- a/spec/models/project_services/chat_message/push_message_spec.rb
+++ b/spec/models/project_services/chat_message/push_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::PushMessage do
+RSpec.describe ChatMessage::PushMessage do
subject { described_class.new(args) }
let(:args) do
diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb
index 1346a43335e..04c9e5934be 100644
--- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb
+++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatMessage::WikiPageMessage do
+RSpec.describe ChatMessage::WikiPageMessage do
subject { described_class.new(args) }
let(:args) do
diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb
index 1caec5c6eb7..77a1377c138 100644
--- a/spec/models/project_services/chat_notification_service_spec.rb
+++ b/spec/models/project_services/chat_notification_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ChatNotificationService do
+RSpec.describe ChatNotificationService do
describe 'Associations' do
before do
allow(subject).to receive(:activated?).and_return(true)
diff --git a/spec/models/project_services/confluence_service_spec.rb b/spec/models/project_services/confluence_service_spec.rb
new file mode 100644
index 00000000000..5d153b17070
--- /dev/null
+++ b/spec/models/project_services/confluence_service_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ConfluenceService do
+ describe 'Associations' do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe 'Validations' do
+ before do
+ subject.active = active
+ end
+
+ context 'when service is active' do
+ let(:active) { true }
+
+ it { is_expected.not_to allow_value('https://example.com').for(:confluence_url) }
+ it { is_expected.not_to allow_value('example.com').for(:confluence_url) }
+ it { is_expected.not_to allow_value('foo').for(:confluence_url) }
+ it { is_expected.not_to allow_value('ftp://example.atlassian.net/wiki').for(:confluence_url) }
+ it { is_expected.not_to allow_value('https://example.atlassian.net').for(:confluence_url) }
+ it { is_expected.not_to allow_value('https://.atlassian.net/wiki').for(:confluence_url) }
+ it { is_expected.not_to allow_value('https://example.atlassian.net/wikifoo').for(:confluence_url) }
+ it { is_expected.not_to allow_value('').for(:confluence_url) }
+ it { is_expected.not_to allow_value(nil).for(:confluence_url) }
+ it { is_expected.not_to allow_value('😊').for(:confluence_url) }
+ it { is_expected.to allow_value('https://example.atlassian.net/wiki').for(:confluence_url) }
+ it { is_expected.to allow_value('http://example.atlassian.net/wiki').for(:confluence_url) }
+ it { is_expected.to allow_value('https://example.atlassian.net/wiki/').for(:confluence_url) }
+ it { is_expected.to allow_value('http://example.atlassian.net/wiki/').for(:confluence_url) }
+ it { is_expected.to allow_value('https://example.atlassian.net/wiki/foo').for(:confluence_url) }
+
+ it { is_expected.to validate_presence_of(:confluence_url) }
+ end
+
+ context 'when service is inactive' do
+ let(:active) { false }
+
+ it { is_expected.not_to validate_presence_of(:confluence_url) }
+ it { is_expected.to allow_value('foo').for(:confluence_url) }
+ end
+ end
+
+ describe '#detailed_description' do
+ it 'can correctly return a link to the project wiki when active' do
+ project = create(:project)
+ subject.project = project
+ subject.active = true
+
+ expect(subject.detailed_description).to include(Gitlab::Routing.url_helpers.project_wikis_url(project))
+ end
+
+ context 'when the project wiki is not enabled' do
+ it 'returns nil when both active or inactive', :aggregate_failures do
+ project = create(:project, :wiki_disabled)
+ subject.project = project
+
+ [true, false].each do |active|
+ subject.active = active
+
+ expect(subject.detailed_description).to be_nil
+ end
+ end
+ end
+ end
+
+ describe 'Caching has_confluence on project_settings' do
+ let(:project) { create(:project) }
+
+ subject { project.project_setting.has_confluence? }
+
+ it 'sets the property to true when service is active' do
+ create(:confluence_service, project: project, active: true)
+
+ is_expected.to be(true)
+ end
+
+ it 'sets the property to false when service is not active' do
+ create(:confluence_service, project: project, active: false)
+
+ is_expected.to be(false)
+ end
+
+ it 'creates a project_setting record if one was not already created' do
+ expect { create(:confluence_service) }.to change { ProjectSetting.count }.by(1)
+ end
+ end
+end
diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb
index e749ea6eacc..881ae60a680 100644
--- a/spec/models/project_services/custom_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CustomIssueTrackerService do
+RSpec.describe CustomIssueTrackerService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -31,66 +31,5 @@ describe CustomIssueTrackerService do
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
-
- context 'title' do
- let(:issue_tracker) { described_class.new(properties: {}) }
-
- it 'sets a default title' do
- issue_tracker.title = nil
-
- expect(issue_tracker.title).to eq('Custom Issue Tracker')
- end
-
- it 'sets the custom title' do
- issue_tracker.title = 'test title'
-
- expect(issue_tracker.title).to eq('test title')
- end
- end
- end
-
- context 'overriding properties' do
- let(:url) { 'http://custom.example.com' }
- let(:access_params) do
- { project_url: url, issues_url: url, new_issue_url: url }
- end
-
- # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- context 'when data are stored in properties' do
- let(:properties) { access_params.merge(title: title, description: description) }
- let(:service) do
- create(:custom_issue_tracker_service, :without_properties_callback, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in separated fields' do
- let(:service) do
- create(:custom_issue_tracker_service, title: title, description: description, properties: access_params)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in both properties and separated fields' do
- let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
- let(:service) do
- create(:custom_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:custom_issue_tracker_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('Custom Issue Tracker')
- expect(service.description).to eq('Custom issue tracker')
- end
- end
end
end
diff --git a/spec/models/project_services/data_fields_spec.rb b/spec/models/project_services/data_fields_spec.rb
index 6b388a7222b..9a3042f9f8d 100644
--- a/spec/models/project_services/data_fields_spec.rb
+++ b/spec/models/project_services/data_fields_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DataFields do
+RSpec.describe DataFields do
let(:url) { 'http://url.com' }
let(:username) { 'username_one' }
let(:properties) do
diff --git a/spec/models/project_services/discord_service_spec.rb b/spec/models/project_services/discord_service_spec.rb
index b5a54676dd7..d4bd08ddeb6 100644
--- a/spec/models/project_services/discord_service_spec.rb
+++ b/spec/models/project_services/discord_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe DiscordService do
+RSpec.describe DiscordService do
it_behaves_like "chat service", "Discord notifications" do
let(:client) { Discordrb::Webhooks::Client }
let(:client_arguments) { { url: webhook_url } }
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index 1ee9c5f90c6..9aaf4f7a644 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe DroneCiService, :use_clean_rails_memory_store_caching do
+RSpec.describe DroneCiService, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
describe 'associations' do
diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb
index 44db95afc57..6954a72f9c1 100644
--- a/spec/models/project_services/emails_on_push_service_spec.rb
+++ b/spec/models/project_services/emails_on_push_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe EmailsOnPushService do
+RSpec.describe EmailsOnPushService do
describe 'Validations' do
context 'when service is active' do
before do
diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb
index f8d88a944a5..c6891401a0f 100644
--- a/spec/models/project_services/external_wiki_service_spec.rb
+++ b/spec/models/project_services/external_wiki_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ExternalWikiService do
+RSpec.describe ExternalWikiService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index c1ebe69ee66..94a49fb3080 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe FlowdockService do
+RSpec.describe FlowdockService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index 7f1c6224b7d..a6b7cb05836 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GitlabIssueTrackerService do
+RSpec.describe GitlabIssueTrackerService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -51,49 +51,4 @@ describe GitlabIssueTrackerService do
end
end
end
-
- context 'overriding properties' do
- let(:url) { 'http://gitlab.example.com' }
- let(:access_params) do
- { project_url: url, issues_url: url, new_issue_url: url }
- end
-
- # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- context 'when data are stored in properties' do
- let(:properties) { access_params.merge(title: title, description: description) }
- let(:service) do
- create(:gitlab_issue_tracker_service, :without_properties_callback, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in separated fields' do
- let(:service) do
- create(:gitlab_issue_tracker_service, title: title, description: description, properties: access_params)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in both properties and separated fields' do
- let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
- let(:service) do
- create(:gitlab_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:gitlab_issue_tracker_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('GitLab')
- expect(service.description).to eq('GitLab issue tracker')
- end
- end
- end
end
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb
index 0505ac9b49c..042e32439d1 100644
--- a/spec/models/project_services/hangouts_chat_service_spec.rb
+++ b/spec/models/project_services/hangouts_chat_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe HangoutsChatService do
+RSpec.describe HangoutsChatService do
it_behaves_like "chat service", "Hangouts Chat" do
let(:client) { HangoutsChat::Sender }
let(:client_arguments) { webhook_url }
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index c25edf81352..667e6cc85ab 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe HipchatService do
+RSpec.describe HipchatService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index 88a93eef214..07963947de8 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require 'socket'
require 'json'
-describe IrkerService do
+RSpec.describe IrkerService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/issue_tracker_data_spec.rb b/spec/models/project_services/issue_tracker_data_spec.rb
index db617cf0abb..3ddb7d9250f 100644
--- a/spec/models/project_services/issue_tracker_data_spec.rb
+++ b/spec/models/project_services/issue_tracker_data_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IssueTrackerData do
+RSpec.describe IssueTrackerData do
let(:service) { create(:custom_issue_tracker_service, active: false, properties: {}) }
describe 'Associations' do
diff --git a/spec/models/project_services/issue_tracker_service_spec.rb b/spec/models/project_services/issue_tracker_service_spec.rb
index f1cdee5c4a3..5b12c7330b8 100644
--- a/spec/models/project_services/issue_tracker_service_spec.rb
+++ b/spec/models/project_services/issue_tracker_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IssueTrackerService do
+RSpec.describe IssueTrackerService do
describe 'Validations' do
let(:project) { create :project }
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 20e85f0fd4b..cfc2c920cd2 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -2,11 +2,9 @@
require 'spec_helper'
-describe JiraService do
+RSpec.describe JiraService do
include AssetsHelpers
- let(:title) { 'custom title' }
- let(:description) { 'custom description' }
let(:url) { 'http://jira.example.com' }
let(:api_url) { 'http://api-jira.example.com' }
let(:username) { 'jira-username' }
@@ -93,7 +91,6 @@ describe JiraService do
let(:params) do
{
project: create(:project),
- title: 'custom title', description: 'custom description',
url: url, api_url: api_url,
username: username, password: password,
jira_issue_transition_id: transition_id
@@ -106,19 +103,6 @@ describe JiraService do
expect(subject.properties).to be_nil
end
- it 'sets title correctly' do
- service = subject
-
- expect(service.title).to eq('custom title')
- end
-
- it 'sets service data correctly' do
- service = subject
-
- expect(service.title).to eq('custom title')
- expect(service.description).to eq('custom description')
- end
-
it 'stores data in data_fields correcty' do
service = subject
@@ -209,7 +193,6 @@ describe JiraService do
end
it 'does not reset password if url "changed" to the same url as before' do
- service.title = 'aaaaaa'
service.url = 'http://jira.example.com'
service.save
@@ -318,46 +301,32 @@ describe JiraService do
# this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
context 'when data are stored in properties' do
- let(:properties) { data_params.merge(title: title, description: description) }
+ let(:properties) { data_params }
let!(:service) do
create(:jira_service, :without_properties_callback, properties: properties.merge(additional: 'something'))
end
- it_behaves_like 'issue tracker fields'
it_behaves_like 'handles jira fields'
end
context 'when data are stored in separated fields' do
let(:service) do
- create(:jira_service, data_params.merge(properties: {}, title: title, description: description))
+ create(:jira_service, data_params.merge(properties: {}))
end
- it_behaves_like 'issue tracker fields'
it_behaves_like 'handles jira fields'
end
context 'when data are stored in both properties and separated fields' do
- let(:properties) { data_params.merge(title: title, description: description) }
+ let(:properties) { data_params }
let(:service) do
create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |service|
create(:jira_tracker_data, data_params.merge(service: service))
end
end
- it_behaves_like 'issue tracker fields'
it_behaves_like 'handles jira fields'
end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:jira_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('Jira')
- expect(service.description).to eq(s_('JiraService|Jira issue tracker'))
- end
- end
end
describe '#close_issue' do
@@ -704,59 +673,6 @@ describe JiraService do
end
end
- describe 'description and title' do
- let(:title) { 'Jira One' }
- let(:description) { 'Jira One issue tracker' }
- let(:properties) do
- {
- url: 'http://jira.example.com/web',
- username: 'mic',
- password: 'password',
- title: title,
- description: description
- }
- end
-
- context 'when it is not set' do
- it 'default values are returned' do
- service = create(:jira_service)
-
- expect(service.title).to eq('Jira')
- expect(service.description).to eq(s_('JiraService|Jira issue tracker'))
- end
- end
-
- context 'when it is set in properties' do
- it 'values from properties are returned' do
- service = create(:jira_service, :without_properties_callback, properties: properties)
-
- expect(service.title).to eq(title)
- expect(service.description).to eq(description)
- end
- end
-
- context 'when it is in title & description fields' do
- it 'values from title and description fields are returned' do
- service = create(:jira_service, title: title, description: description)
-
- expect(service.title).to eq(title)
- expect(service.description).to eq(description)
- end
- end
-
- context 'when it is in both properites & title & description fields' do
- it 'values from title and description fields are returned' do
- title2 = 'Jira 2'
- description2 = 'Jira description 2'
-
- service = create(:jira_service, title: title2, description: description2, properties: properties)
-
- expect(service.title).to eq(title2)
- expect(service.description).to eq(description2)
- end
- end
- end
-
describe 'project and issue urls' do
context 'when gitlab.yml was initialized' do
it 'is prepopulated with the settings' do
@@ -808,7 +724,7 @@ describe JiraService do
describe '#new_issue_url' do
it 'handles trailing slashes' do
- expect(service.new_issue_url).to eq('http://jira.test.com/path/secure/CreateIssue.jspa')
+ expect(service.new_issue_url).to eq('http://jira.test.com/path/secure/CreateIssue!default.jspa')
end
end
end
diff --git a/spec/models/project_services/jira_tracker_data_spec.rb b/spec/models/project_services/jira_tracker_data_spec.rb
index 12f6b99e8a7..9e38bced46c 100644
--- a/spec/models/project_services/jira_tracker_data_spec.rb
+++ b/spec/models/project_services/jira_tracker_data_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-describe JiraTrackerData do
- let(:service) { create(:jira_service, active: false) }
+RSpec.describe JiraTrackerData do
+ let(:service) { build(:jira_service) }
describe 'Associations' do
it { is_expected.to belong_to(:service) }
diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/project_services/mattermost_service_spec.rb
index 5b974985706..af1944ea77d 100644
--- a/spec/models/project_services/mattermost_service_spec.rb
+++ b/spec/models/project_services/mattermost_service_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe MattermostService do
+RSpec.describe MattermostService do
it_behaves_like "slack or mattermost notifications", "Mattermost"
end
diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
index 836181929e3..4fff3bc56cc 100644
--- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb
+++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MattermostSlashCommandsService do
+RSpec.describe MattermostSlashCommandsService do
it_behaves_like "chat slash commands service"
context 'Mattermost API' do
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index 425599c73d4..610feb52827 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MicrosoftTeamsService do
+RSpec.describe MicrosoftTeamsService do
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
diff --git a/spec/models/project_services/open_project_service_spec.rb b/spec/models/project_services/open_project_service_spec.rb
index 8e373a31e62..1abaab0ceff 100644
--- a/spec/models/project_services/open_project_service_spec.rb
+++ b/spec/models/project_services/open_project_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OpenProjectService do
+RSpec.describe OpenProjectService do
describe 'Validations' do
context 'when service is active' do
before do
diff --git a/spec/models/project_services/open_project_tracker_data_spec.rb b/spec/models/project_services/open_project_tracker_data_spec.rb
index 0d387bbf69b..e6a3963ba87 100644
--- a/spec/models/project_services/open_project_tracker_data_spec.rb
+++ b/spec/models/project_services/open_project_tracker_data_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe OpenProjectTrackerData do
+RSpec.describe OpenProjectTrackerData do
describe 'Associations' do
it { is_expected.to belong_to(:service) }
end
diff --git a/spec/models/project_services/packagist_service_spec.rb b/spec/models/project_services/packagist_service_spec.rb
index 53f18a1bdd9..f710385b6e2 100644
--- a/spec/models/project_services/packagist_service_spec.rb
+++ b/spec/models/project_services/packagist_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PackagistService do
+RSpec.describe PackagistService do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/project_services/pipelines_email_service_spec.rb b/spec/models/project_services/pipelines_email_service_spec.rb
index de1edf2099a..9a8386c619e 100644
--- a/spec/models/project_services/pipelines_email_service_spec.rb
+++ b/spec/models/project_services/pipelines_email_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PipelinesEmailService, :mailer do
+RSpec.describe PipelinesEmailService, :mailer do
let(:pipeline) do
create(:ci_pipeline, :failed,
project: project,
diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb
index dde46c82df6..8de85cc7fa5 100644
--- a/spec/models/project_services/pivotaltracker_service_spec.rb
+++ b/spec/models/project_services/pivotaltracker_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PivotaltrackerService do
+RSpec.describe PivotaltrackerService do
include StubRequests
describe 'Associations' do
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index db3cbe23ad3..16837e2b93a 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PrometheusService, :use_clean_rails_memory_store_caching do
+RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching do
include PrometheusHelpers
include ReactiveCachingHelpers
@@ -23,7 +23,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
# result = { success: false, result: error }
expect(result[:success]).to be_falsy
- expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::Error)
+ expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::UnexpectedResponseError)
expect(redirect_req_stub).to have_been_requested
expect(redirected_req_stub).not_to have_been_requested
@@ -262,8 +262,6 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
service.google_iap_audience_client_id = "IAP_CLIENT_ID.apps.googleusercontent.com"
stub_request(:post, "https://oauth2.googleapis.com/token").to_return(status: 200, body: '{"id_token": "FOO"}', headers: { 'Content-Type': 'application/json; charset=UTF-8' })
-
- stub_feature_flags(prometheus_service_iap_auth: true)
end
it 'includes the authorization header' do
@@ -474,11 +472,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
title: 'API URL',
placeholder: s_('PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/'),
required: true
- }
- ]
- end
- let(:feature_flagged_fields) do
- [
+ },
{
type: 'text',
name: 'google_iap_audience_client_id',
@@ -498,13 +492,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end
it 'returns fields' do
- stub_feature_flags(prometheus_service_iap_auth: false)
expect(service.fields).to eq(expected_fields)
end
-
- it 'returns fields with feature flag on' do
- stub_feature_flags(prometheus_service_iap_auth: true)
- expect(service.fields).to eq(expected_fields + feature_flagged_fields)
- end
end
end
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index 380f02739bc..b7d3b8987b8 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PushoverService do
+RSpec.describe PushoverService do
include StubRequests
describe 'Associations' do
diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb
index 6220d7b1fac..b9be3940d34 100644
--- a/spec/models/project_services/redmine_service_spec.rb
+++ b/spec/models/project_services/redmine_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RedmineService do
+RSpec.describe RedmineService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -50,49 +50,4 @@ describe RedmineService do
expect(described_class.reference_pattern.match('#123')[:issue]).to eq('123')
end
end
-
- context 'overriding properties' do
- let(:url) { 'http://redmine.example.com' }
- let(:access_params) do
- { project_url: url, issues_url: url, new_issue_url: url }
- end
-
- # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- context 'when data are stored in properties' do
- let(:properties) { access_params.merge(title: title, description: description) }
- let(:service) do
- create(:redmine_service, :without_properties_callback, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in separated fields' do
- let(:service) do
- create(:redmine_service, title: title, description: description, properties: access_params)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in both properties and separated fields' do
- let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
- let(:service) do
- create(:redmine_service, :without_properties_callback, title: title, description: description, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:redmine_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('Redmine')
- expect(service.description).to eq('Redmine issue tracker')
- end
- end
- end
end
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 93036ac7ec4..0b35b9e7b30 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe SlackService do
+RSpec.describe SlackService do
it_behaves_like "slack or mattermost notifications", 'Slack'
end
diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb
index 8c57907d064..95c87ef01bc 100644
--- a/spec/models/project_services/slack_slash_commands_service_spec.rb
+++ b/spec/models/project_services/slack_slash_commands_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SlackSlashCommandsService do
+RSpec.describe SlackSlashCommandsService do
it_behaves_like "chat slash commands service"
describe '#trigger' do
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index 0dd77b68588..a3fda33664a 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe TeamcityService, :use_clean_rails_memory_store_caching do
+RSpec.describe TeamcityService, :use_clean_rails_memory_store_caching do
include ReactiveCachingHelpers
include StubRequests
diff --git a/spec/models/project_services/unify_circuit_service_spec.rb b/spec/models/project_services/unify_circuit_service_spec.rb
index 51079ea5395..73702aa8471 100644
--- a/spec/models/project_services/unify_circuit_service_spec.rb
+++ b/spec/models/project_services/unify_circuit_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe UnifyCircuitService do
+RSpec.describe UnifyCircuitService do
it_behaves_like "chat service", "Unify Circuit" do
let(:client_arguments) { webhook_url }
let(:content_key) { :subject }
diff --git a/spec/models/project_services/webex_teams_service_spec.rb b/spec/models/project_services/webex_teams_service_spec.rb
index 38977ef3b7d..bd73d0c93b8 100644
--- a/spec/models/project_services/webex_teams_service_spec.rb
+++ b/spec/models/project_services/webex_teams_service_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe WebexTeamsService do
+RSpec.describe WebexTeamsService do
it_behaves_like "chat service", "Webex Teams" do
let(:client_arguments) { webhook_url }
let(:content_key) { :markdown }
diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb
index b8fff635e99..4339b44e1de 100644
--- a/spec/models/project_services/youtrack_service_spec.rb
+++ b/spec/models/project_services/youtrack_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe YoutrackService do
+RSpec.describe YoutrackService do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -42,49 +42,4 @@ describe YoutrackService do
expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123')
end
end
-
- context 'overriding properties' do
- let(:url) { 'http://youtrack.example.com' }
- let(:access_params) do
- { project_url: url, issues_url: url, new_issue_url: url }
- end
-
- # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- context 'when data are stored in properties' do
- let(:properties) { access_params.merge(title: title, description: description) }
- let(:service) do
- create(:youtrack_service, :without_properties_callback, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in separated fields' do
- let(:service) do
- create(:youtrack_service, title: title, description: description, properties: access_params)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when data are stored in both properties and separated fields' do
- let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
- let(:service) do
- create(:youtrack_service, :without_properties_callback, title: title, description: description, properties: properties)
- end
-
- it_behaves_like 'issue tracker fields'
- end
-
- context 'when no title & description are set' do
- let(:service) do
- create(:youtrack_service, properties: access_params)
- end
-
- it 'returns default values' do
- expect(service.title).to eq('YouTrack')
- expect(service.description).to eq(s_('IssueTracker|YouTrack issue tracker'))
- end
- end
- end
end
diff --git a/spec/models/project_setting_spec.rb b/spec/models/project_setting_spec.rb
index 5cfb932eb2a..5572304d666 100644
--- a/spec/models/project_setting_spec.rb
+++ b/spec/models/project_setting_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe ProjectSetting, type: :model do
+RSpec.describe ProjectSetting, type: :model do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb
index c17a24dc7cf..464b9b1da84 100644
--- a/spec/models/project_snippet_spec.rb
+++ b/spec/models/project_snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectSnippet do
+RSpec.describe ProjectSnippet do
describe "Associations" do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 9ec306d297e..8fdda241719 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Project do
+RSpec.describe Project do
include ProjectForksHelper
include GitHelpers
include ExternalAuthorizationServiceHelpers
@@ -63,6 +63,7 @@ describe Project do
it { is_expected.to have_one(:bugzilla_service) }
it { is_expected.to have_one(:gitlab_issue_tracker_service) }
it { is_expected.to have_one(:external_wiki_service) }
+ it { is_expected.to have_one(:confluence_service) }
it { is_expected.to have_one(:project_feature) }
it { is_expected.to have_one(:project_repository) }
it { is_expected.to have_one(:container_expiration_policy) }
@@ -119,6 +120,8 @@ describe Project do
it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:project) }
it { is_expected.to have_many(:repository_storage_moves) }
it { is_expected.to have_many(:reviews).inverse_of(:project) }
+ it { is_expected.to have_many(:packages).class_name('Packages::Package') }
+ it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') }
it_behaves_like 'model with repository' do
let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
@@ -1378,6 +1381,62 @@ describe Project do
end
end
+ describe '.service_desk_enabled' do
+ it 'returns the correct project' do
+ project_with_service_desk_enabled = create(:project)
+ project_with_service_desk_disabled = create(:project, :service_desk_disabled)
+
+ expect(described_class.service_desk_enabled).to include(project_with_service_desk_enabled)
+ expect(described_class.service_desk_enabled).not_to include(project_with_service_desk_disabled)
+ end
+ end
+
+ describe '#service_desk_enabled?' do
+ let_it_be(:namespace) { create(:namespace) }
+
+ subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) }
+
+ before do
+ allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
+ allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
+ end
+
+ it 'is enabled' do
+ expect(project.service_desk_enabled?).to be_truthy
+ expect(project.service_desk_enabled).to be_truthy
+ end
+ end
+
+ describe '#service_desk_address' do
+ let_it_be(:project) { create(:project, service_desk_enabled: true) }
+
+ before do
+ allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true)
+ allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com")
+ end
+
+ it 'uses project full path as service desk address key' do
+ expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com")
+ end
+ end
+
+ describe '.find_by_service_desk_project_key' do
+ it 'returns the correct project' do
+ project1 = create(:project)
+ project2 = create(:project)
+ create(:service_desk_setting, project: project1, project_key: 'key1')
+ create(:service_desk_setting, project: project2, project_key: 'key2')
+
+ expect(Project.find_by_service_desk_project_key('key1')).to eq(project1)
+ expect(Project.find_by_service_desk_project_key('key2')).to eq(project2)
+ end
+
+ it 'returns nil if there is no project with the key' do
+ expect(Project.find_by_service_desk_project_key('some_key')).to be_nil
+ end
+ end
+
context 'repository storage by default' do
let(:project) { build(:project) }
@@ -1651,6 +1710,14 @@ describe Project do
let(:project_name) { 'group.example.com' }
it { is_expected.to eq("http://group.example.com") }
+
+ context 'mixed case path' do
+ before do
+ project.update!(path: 'Group.example.com')
+ end
+
+ it { is_expected.to eq("http://group.example.com") }
+ end
end
context 'project page' do
@@ -1658,6 +1725,14 @@ describe Project do
let(:project_name) { 'Project' }
it { is_expected.to eq("http://group.example.com/project") }
+
+ context 'mixed case path' do
+ before do
+ project.update!(path: 'Project')
+ end
+
+ it { is_expected.to eq("http://group.example.com/Project") }
+ end
end
end
@@ -2897,28 +2972,73 @@ describe Project do
subject { project.deployment_variables(environment: environment, kubernetes_namespace: namespace) }
- before do
- expect(project).to receive(:deployment_platform).with(environment: environment)
- .and_return(deployment_platform)
- end
+ context 'when the deployment platform is stubbed' do
+ before do
+ expect(project).to receive(:deployment_platform).with(environment: environment)
+ .and_return(deployment_platform)
+ end
+
+ context 'when project has a deployment platform' do
+ let(:platform_variables) { %w(platform variables) }
+ let(:deployment_platform) { double }
+
+ before do
+ expect(deployment_platform).to receive(:predefined_variables)
+ .with(project: project, environment_name: environment, kubernetes_namespace: namespace)
+ .and_return(platform_variables)
+ end
+
+ it { is_expected.to eq platform_variables }
+ end
- context 'when project has no deployment platform' do
- let(:deployment_platform) { nil }
+ context 'when project has no deployment platform' do
+ let(:deployment_platform) { nil }
- it { is_expected.to eq [] }
+ it { is_expected.to eq [] }
+ end
end
- context 'when project has a deployment platform' do
- let(:platform_variables) { %w(platform variables) }
- let(:deployment_platform) { double }
+ context 'when project has a deployment platforms' do
+ let(:project) { create(:project) }
+
+ let!(:default_cluster) do
+ create(:cluster,
+ :not_managed,
+ platform_type: :kubernetes,
+ projects: [project],
+ environment_scope: '*',
+ platform_kubernetes: default_cluster_kubernetes)
+ end
- before do
- expect(deployment_platform).to receive(:predefined_variables)
- .with(project: project, environment_name: environment, kubernetes_namespace: namespace)
- .and_return(platform_variables)
+ let!(:review_env_cluster) do
+ create(:cluster,
+ :not_managed,
+ platform_type: :kubernetes,
+ projects: [project],
+ environment_scope: 'review/*',
+ platform_kubernetes: review_env_cluster_kubernetes)
end
- it { is_expected.to eq platform_variables }
+ let(:default_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'default-AAA') }
+ let(:review_env_cluster_kubernetes) { create(:cluster_platform_kubernetes, token: 'review-AAA') }
+
+ context 'when environment name is review/name' do
+ let!(:environment) { create(:environment, project: project, name: 'review/name') }
+
+ it 'returns variables from this service' do
+ expect(project.deployment_variables(environment: 'review/name'))
+ .to include(key: 'KUBE_TOKEN', value: 'review-AAA', public: false, masked: true)
+ end
+ end
+
+ context 'when environment name is other' do
+ let!(:environment) { create(:environment, project: project, name: 'staging/name') }
+
+ it 'returns variables from this service' do
+ expect(project.deployment_variables(environment: 'staging/name'))
+ .to include(key: 'KUBE_TOKEN', value: 'default-AAA', public: false, masked: true)
+ end
+ end
end
end
@@ -3999,7 +4119,7 @@ describe Project do
it 'returns the number of forks' do
project = build(:project)
- expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1)
+ expect_any_instance_of(::Projects::BatchForksCountService).to receive(:refresh_cache_and_retrieve_data).and_return({ project => 1 })
expect(project.forks_count).to eq(1)
end
@@ -4655,6 +4775,7 @@ describe Project do
expect(project).to receive(:refresh_markdown_cache!)
expect(InternalId).to receive(:flush_records!).with(project: project)
expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
+ expect(project).to receive(:write_repository_config)
project.after_import
end
@@ -4743,6 +4864,36 @@ describe Project do
end
end
+ describe "#default_branch" do
+ context "with an empty repository" do
+ let_it_be(:project) { create(:project_empty_repo) }
+
+ context "Gitlab::CurrentSettings.default_branch_name is unavailable" do
+ before do
+ expect(Gitlab::CurrentSettings)
+ .to receive(:default_branch_name)
+ .and_return(nil)
+ end
+
+ it "returns that value" do
+ expect(project.default_branch).to be_nil
+ end
+ end
+
+ context "Gitlab::CurrentSettings.default_branch_name is available" do
+ before do
+ expect(Gitlab::CurrentSettings)
+ .to receive(:default_branch_name)
+ .and_return('example_branch')
+ end
+
+ it "returns that value" do
+ expect(project.default_branch).to eq("example_branch")
+ end
+ end
+ end
+ end
+
describe '#to_ability_name' do
it 'returns project' do
project = build(:project_empty_repo)
@@ -5886,6 +6037,30 @@ describe Project do
end
end
+ describe '#prometheus_service_active?' do
+ let(:project) { create(:project) }
+
+ subject { project.prometheus_service_active? }
+
+ before do
+ create(:prometheus_service, project: project, manual_configuration: manual_configuration)
+ end
+
+ context 'when project has an activated prometheus service' do
+ let(:manual_configuration) { true }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when project has an inactive prometheus service' do
+ let(:manual_configuration) { false }
+
+ it 'the service is marked as inactive' do
+ expect(subject).to be_falsey
+ end
+ end
+ end
+
describe '#self_monitoring?' do
let_it_be(:project) { create(:project) }
@@ -6025,6 +6200,39 @@ describe Project do
end
end
+ describe '#packages_enabled' do
+ subject { create(:project).packages_enabled }
+
+ it { is_expected.to be true }
+ end
+
+ describe '#package_already_taken?' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :public, namespace: namespace) }
+ let!(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") }
+
+ context 'no package exists with the same name' do
+ it 'returns false' do
+ result = project.package_already_taken?("@#{namespace.path}/bar")
+ expect(result).to be false
+ end
+
+ it 'returns false if it is the project that the package belongs to' do
+ result = project.package_already_taken?("@#{namespace.path}/foo")
+ expect(result).to be false
+ end
+ end
+
+ context 'a package already exists with the same name' do
+ let(:alt_project) { create(:project, :public, namespace: namespace) }
+
+ it 'returns true' do
+ result = alt_project.package_already_taken?("@#{namespace.path}/foo")
+ expect(result).to be true
+ end
+ end
+ end
+
describe '#design_management_enabled?' do
let(:project) { build(:project) }
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 4bc6130387a..3659e6b973e 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectStatistics do
+RSpec.describe ProjectStatistics do
let(:project) { create :project }
let(:statistics) { project.statistics }
@@ -32,7 +32,8 @@ describe ProjectStatistics do
repository_size: 2.exabytes,
wiki_size: 1.exabytes,
lfs_objects_size: 2.exabytes,
- build_artifacts_size: 3.exabytes - 1
+ build_artifacts_size: 2.exabytes - 1,
+ snippets_size: 1.exabyte
)
statistics.reload
@@ -41,8 +42,9 @@ describe ProjectStatistics do
expect(statistics.repository_size).to eq(2.exabytes)
expect(statistics.wiki_size).to eq(1.exabytes)
expect(statistics.lfs_objects_size).to eq(2.exabytes)
- expect(statistics.build_artifacts_size).to eq(3.exabytes - 1)
+ expect(statistics.build_artifacts_size).to eq(2.exabytes - 1)
expect(statistics.storage_size).to eq(8.exabytes - 1)
+ expect(statistics.snippets_size).to eq(1.exabyte)
end
end
@@ -52,23 +54,47 @@ describe ProjectStatistics do
statistics.wiki_size = 6
statistics.lfs_objects_size = 3
statistics.build_artifacts_size = 4
+ statistics.snippets_size = 5
expect(statistics.total_repository_size).to eq 5
end
end
describe '#wiki_size' do
- it "is initialized with not null value" do
+ it 'is initialized with not null value' do
+ expect(statistics.attributes['wiki_size']).to be_zero
+ expect(statistics.wiki_size).to be_zero
+ end
+
+ it 'coerces any nil value to 0' do
+ statistics.update!(wiki_size: nil)
+
+ expect(statistics.attributes['wiki_size']).to be_nil
expect(statistics.wiki_size).to eq 0
end
end
+ describe '#snippets_size' do
+ it 'is initialized with not null value' do
+ expect(statistics.attributes['snippets_size']).to be_zero
+ expect(statistics.snippets_size).to be_zero
+ end
+
+ it 'coerces any nil value to 0' do
+ statistics.update!(snippets_size: nil)
+
+ expect(statistics.attributes['snippets_size']).to be_nil
+ expect(statistics.snippets_size).to eq 0
+ end
+ end
+
describe '#refresh!' do
before do
allow(statistics).to receive(:update_commit_count)
allow(statistics).to receive(:update_repository_size)
allow(statistics).to receive(:update_wiki_size)
allow(statistics).to receive(:update_lfs_objects_size)
+ allow(statistics).to receive(:update_snippets_size)
allow(statistics).to receive(:update_storage_size)
end
@@ -82,6 +108,7 @@ describe ProjectStatistics do
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
expect(statistics).to have_received(:update_lfs_objects_size)
+ expect(statistics).to have_received(:update_snippets_size)
end
end
@@ -95,6 +122,7 @@ describe ProjectStatistics do
expect(statistics).not_to have_received(:update_commit_count)
expect(statistics).not_to have_received(:update_repository_size)
expect(statistics).not_to have_received(:update_wiki_size)
+ expect(statistics).not_to have_received(:update_snippets_size)
end
end
@@ -108,9 +136,11 @@ describe ProjectStatistics do
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
+ expect(statistics).to have_received(:update_snippets_size)
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
+ expect(statistics.snippets_size).to eq(0)
end
end
@@ -130,9 +160,11 @@ describe ProjectStatistics do
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
+ expect(statistics).to have_received(:update_snippets_size)
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
+ expect(statistics.snippets_size).to eq(0)
end
end
@@ -202,6 +234,33 @@ describe ProjectStatistics do
end
end
+ describe '#update_snippets_size' do
+ before do
+ create_list(:project_snippet, 2, project: project)
+ SnippetStatistics.update_all(repository_size: 10)
+ end
+
+ it 'stores the size of snippets' do
+ # Snippet not associated with the project
+ snippet = create(:project_snippet)
+ snippet.statistics.update!(repository_size: 40)
+
+ statistics.update_snippets_size
+
+ expect(statistics.update_snippets_size).to eq 20
+ end
+
+ context 'when not all snippets has statistics' do
+ it 'stores the size of snippets with statistics' do
+ SnippetStatistics.last.delete
+
+ statistics.update_snippets_size
+
+ expect(statistics.update_snippets_size).to eq 10
+ end
+ end
+ end
+
describe '#update_lfs_objects_size' do
let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
@@ -222,12 +281,13 @@ describe ProjectStatistics do
statistics.update!(
repository_size: 2,
wiki_size: 4,
- lfs_objects_size: 3
+ lfs_objects_size: 3,
+ snippets_size: 2
)
statistics.reload
- expect(statistics.storage_size).to eq 9
+ expect(statistics.storage_size).to eq 11
end
it 'works during wiki_size backfill' do
@@ -241,6 +301,21 @@ describe ProjectStatistics do
expect(statistics.storage_size).to eq 5
end
+
+ context 'when nullable columns are nil' do
+ it 'does not raise any error' do
+ expect do
+ statistics.update!(
+ repository_size: 2,
+ wiki_size: nil,
+ lfs_objects_size: 3,
+ snippets_size: nil
+ )
+ end.not_to raise_error
+
+ expect(statistics.storage_size).to eq 5
+ end
+ end
end
describe '.increment_statistic' do
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index 24652a1d706..34ec856459c 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe ProjectTeam do
+RSpec.describe ProjectTeam do
let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index aff2b248642..d9c5fed542e 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProjectWiki do
+RSpec.describe ProjectWiki do
it_behaves_like 'wiki model' do
let(:wiki_container) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_container_without_repo) { create(:project, namespace: user.namespace) }
diff --git a/spec/models/prometheus_alert_event_spec.rb b/spec/models/prometheus_alert_event_spec.rb
index 85e57cb08c3..913ca7db0be 100644
--- a/spec/models/prometheus_alert_event_spec.rb
+++ b/spec/models/prometheus_alert_event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PrometheusAlertEvent do
+RSpec.describe PrometheusAlertEvent do
subject { build(:prometheus_alert_event) }
let(:alert) { subject.prometheus_alert }
diff --git a/spec/models/prometheus_alert_spec.rb b/spec/models/prometheus_alert_spec.rb
index 1409cf65fee..7169a34d96f 100644
--- a/spec/models/prometheus_alert_spec.rb
+++ b/spec/models/prometheus_alert_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PrometheusAlert do
+RSpec.describe PrometheusAlert do
let_it_be(:project) { build(:project) }
let(:metric) { build(:prometheus_metric) }
@@ -33,6 +33,10 @@ describe PrometheusAlert do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:environment) }
+ it { is_expected.to belong_to(:prometheus_metric) }
+ it { is_expected.to have_many(:prometheus_alert_events) }
+ it { is_expected.to have_many(:related_issues) }
+ it { is_expected.to have_many(:alert_management_alerts) }
end
describe 'project validations' do
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
index 93abef063cb..f284102b4a9 100644
--- a/spec/models/prometheus_metric_spec.rb
+++ b/spec/models/prometheus_metric_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PrometheusMetric do
+RSpec.describe PrometheusMetric do
subject { build(:prometheus_metric) }
it_behaves_like 'having unique enum values'
@@ -11,6 +11,7 @@ describe PrometheusMetric do
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:query) }
it { is_expected.to validate_presence_of(:group) }
+ it { is_expected.to validate_uniqueness_of(:identifier).scoped_to(:project_id).allow_nil }
describe 'common metrics' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/protectable_dropdown_spec.rb b/spec/models/protectable_dropdown_spec.rb
index aca3df9fdde..c51197234ca 100644
--- a/spec/models/protectable_dropdown_spec.rb
+++ b/spec/models/protectable_dropdown_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProtectableDropdown do
+RSpec.describe ProtectableDropdown do
let(:project) { create(:project, :repository) }
let(:subject) { described_class.new(project, :branches) }
diff --git a/spec/models/protected_branch/merge_access_level_spec.rb b/spec/models/protected_branch/merge_access_level_spec.rb
index 39dd586b157..b6c2d527d1b 100644
--- a/spec/models/protected_branch/merge_access_level_spec.rb
+++ b/spec/models/protected_branch/merge_access_level_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe ProtectedBranch::MergeAccessLevel do
+RSpec.describe ProtectedBranch::MergeAccessLevel do
it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb
index 628c8d29ecd..77fe9814c86 100644
--- a/spec/models/protected_branch/push_access_level_spec.rb
+++ b/spec/models/protected_branch/push_access_level_spec.rb
@@ -2,6 +2,6 @@
require 'spec_helper'
-describe ProtectedBranch::PushAccessLevel do
+RSpec.describe ProtectedBranch::PushAccessLevel do
it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MAINTAINER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) }
end
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index 30fce1cd5c4..a89f8778780 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProtectedBranch do
+RSpec.describe ProtectedBranch do
subject { build_stubbed(:protected_branch) }
describe 'Associations' do
diff --git a/spec/models/protected_tag_spec.rb b/spec/models/protected_tag_spec.rb
index 79120d17d39..7bc62b1d0e7 100644
--- a/spec/models/protected_tag_spec.rb
+++ b/spec/models/protected_tag_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ProtectedTag do
+RSpec.describe ProtectedTag do
describe 'Associations' do
it { is_expected.to belong_to(:project) }
end
diff --git a/spec/models/push_event_payload_spec.rb b/spec/models/push_event_payload_spec.rb
index 6b59ee5ee57..32415ef4719 100644
--- a/spec/models/push_event_payload_spec.rb
+++ b/spec/models/push_event_payload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PushEventPayload do
+RSpec.describe PushEventPayload do
it_behaves_like 'having unique enum values'
describe 'saving payloads' do
diff --git a/spec/models/push_event_spec.rb b/spec/models/push_event_spec.rb
index 5c1802669c1..61e31e7c4e3 100644
--- a/spec/models/push_event_spec.rb
+++ b/spec/models/push_event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe PushEvent do
+RSpec.describe PushEvent do
let(:payload) { PushEventPayload.new }
let(:event) do
diff --git a/spec/models/readme_blob_spec.rb b/spec/models/readme_blob_spec.rb
index 34182fa413f..95622d55254 100644
--- a/spec/models/readme_blob_spec.rb
+++ b/spec/models/readme_blob_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ReadmeBlob do
+RSpec.describe ReadmeBlob do
include FakeBlobHelpers
describe 'policy' do
diff --git a/spec/models/redirect_route_spec.rb b/spec/models/redirect_route_spec.rb
index b9b2873f8f2..c6e35923b89 100644
--- a/spec/models/redirect_route_spec.rb
+++ b/spec/models/redirect_route_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RedirectRoute do
+RSpec.describe RedirectRoute do
let(:group) { create(:group) }
let!(:redirect_route) { group.redirect_routes.create(path: 'gitlabb') }
diff --git a/spec/models/releases/evidence_spec.rb b/spec/models/releases/evidence_spec.rb
index 927e2e9bbe6..ca5d4b67b59 100644
--- a/spec/models/releases/evidence_spec.rb
+++ b/spec/models/releases/evidence_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Releases::Evidence do
+RSpec.describe Releases::Evidence do
let_it_be(:project) { create(:project) }
let(:release) { create(:release, project: project) }
diff --git a/spec/models/releases/link_spec.rb b/spec/models/releases/link_spec.rb
index 7533d1e6e5c..4dc1e53d59e 100644
--- a/spec/models/releases/link_spec.rb
+++ b/spec/models/releases/link_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Releases::Link do
+RSpec.describe Releases::Link do
let(:release) { create(:release, project: project) }
let(:project) { create(:project) }
diff --git a/spec/models/releases/source_spec.rb b/spec/models/releases/source_spec.rb
index d7af6fd90a6..d10b2140550 100644
--- a/spec/models/releases/source_spec.rb
+++ b/spec/models/releases/source_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Releases::Source do
+RSpec.describe Releases::Source do
let_it_be(:project) { create(:project, :repository, name: 'finance-cal') }
let(:tag_name) { 'v1.0' }
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 6d163a16e63..ebc9760ab14 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RemoteMirror, :mailer do
+RSpec.describe RemoteMirror, :mailer do
include GitHelpers
describe 'URL validation' do
diff --git a/spec/models/repository_language_spec.rb b/spec/models/repository_language_spec.rb
index 13a4cd1e7cf..8cf5e17086d 100644
--- a/spec/models/repository_language_spec.rb
+++ b/spec/models/repository_language_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe RepositoryLanguage do
+RSpec.describe RepositoryLanguage do
let(:repository_language) { build(:repository_language) }
describe 'associations' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c698b40a4c0..964cc5a13ca 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Repository do
+RSpec.describe Repository do
include RepoHelpers
include GitHelpers
@@ -252,6 +252,21 @@ describe Repository do
end
end
end
+
+ context 'with filename with pathspec characters' do
+ let(:filename) { ':wq' }
+ let(:newrev) { project.repository.commit('master').sha }
+
+ before do
+ create_file_in_repo(project, 'master', 'master', filename, 'Test file')
+ end
+
+ subject { repository.last_commit_for_path('master', filename, literal_pathspec: true).id }
+
+ it 'returns a commit SHA' do
+ expect(subject).to eq(newrev)
+ end
+ end
end
describe '#last_commit_id_for_path' do
@@ -276,6 +291,21 @@ describe Repository do
end
end
end
+
+ context 'with filename with pathspec characters' do
+ let(:filename) { ':wq' }
+ let(:newrev) { project.repository.commit('master').sha }
+
+ before do
+ create_file_in_repo(project, 'master', 'master', filename, 'Test file')
+ end
+
+ subject { repository.last_commit_id_for_path('master', filename, literal_pathspec: true) }
+
+ it 'returns a commit SHA' do
+ expect(subject).to eq(newrev)
+ end
+ end
end
describe '#commits' do
@@ -2865,6 +2895,29 @@ describe Repository do
end
end
+ describe '#project' do
+ it 'returns the project for a project snippet' do
+ snippet = create(:project_snippet)
+
+ expect(snippet.repository.project).to be(snippet.project)
+ end
+
+ it 'returns nil for a personal snippet' do
+ snippet = create(:personal_snippet)
+
+ expect(snippet.repository.project).to be_nil
+ end
+
+ it 'returns the container if it is a project' do
+ expect(repository.project).to be(project)
+ end
+
+ it 'returns nil if the container is not a project' do
+ expect(repository).to receive(:container).and_return(Group.new)
+ expect(repository.project).to be_nil
+ end
+ end
+
describe '#submodule_links' do
it 'returns an instance of Gitlab::SubmoduleLinks' do
expect(repository.submodule_links).to be_a(Gitlab::SubmoduleLinks)
diff --git a/spec/models/resource_milestone_event_spec.rb b/spec/models/resource_milestone_event_spec.rb
index 66686ec77d0..76ffb358d80 100644
--- a/spec/models/resource_milestone_event_spec.rb
+++ b/spec/models/resource_milestone_event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ResourceMilestoneEvent, type: :model do
+RSpec.describe ResourceMilestoneEvent, type: :model do
it_behaves_like 'a resource event'
it_behaves_like 'a resource event for issues'
it_behaves_like 'a resource event for merge requests'
diff --git a/spec/models/resource_state_event_spec.rb b/spec/models/resource_state_event_spec.rb
index 986a13cbd0d..1381b45cf9e 100644
--- a/spec/models/resource_state_event_spec.rb
+++ b/spec/models/resource_state_event_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ResourceStateEvent, type: :model do
+RSpec.describe ResourceStateEvent, type: :model do
subject { build(:resource_state_event, issue: issue) }
let(:issue) { create(:issue) }
diff --git a/spec/models/review_spec.rb b/spec/models/review_spec.rb
index 9dd8b90feee..2683dc93a4b 100644
--- a/spec/models/review_spec.rb
+++ b/spec/models/review_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Review do
+RSpec.describe Review do
describe 'associations' do
it { is_expected.to belong_to(:author).class_name('User').with_foreign_key(:author_id).inverse_of(:reviews) }
it { is_expected.to belong_to(:merge_request).inverse_of(:reviews).touch(false) }
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 20289afbeb5..0f1637016d6 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Route do
+RSpec.describe Route do
let(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
let(:route) { group.route }
diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb
index 087bc957373..aeafb49f8b5 100644
--- a/spec/models/sent_notification_spec.rb
+++ b/spec/models/sent_notification_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SentNotification do
+RSpec.describe SentNotification do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
diff --git a/spec/models/sentry_issue_spec.rb b/spec/models/sentry_issue_spec.rb
index b4c1cf57761..33654bf5e1a 100644
--- a/spec/models/sentry_issue_spec.rb
+++ b/spec/models/sentry_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SentryIssue do
+RSpec.describe SentryIssue do
describe 'associations' do
it { is_expected.to belong_to(:issue) }
end
diff --git a/spec/models/serverless/domain_cluster_spec.rb b/spec/models/serverless/domain_cluster_spec.rb
index f5e1eb304a1..fdae0483c19 100644
--- a/spec/models/serverless/domain_cluster_spec.rb
+++ b/spec/models/serverless/domain_cluster_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ::Serverless::DomainCluster do
+RSpec.describe ::Serverless::DomainCluster do
subject { create(:serverless_domain_cluster) }
describe 'validations' do
diff --git a/spec/models/serverless/domain_spec.rb b/spec/models/serverless/domain_spec.rb
index ba54e05b4e3..f997b28b149 100644
--- a/spec/models/serverless/domain_spec.rb
+++ b/spec/models/serverless/domain_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ::Serverless::Domain do
+RSpec.describe ::Serverless::Domain do
let(:function_name) { 'test-function' }
let(:pages_domain_name) { 'serverless.gitlab.io' }
let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: pages_domain_name) }
diff --git a/spec/models/serverless/function_spec.rb b/spec/models/serverless/function_spec.rb
index 810d4409a34..632f5eba5c3 100644
--- a/spec/models/serverless/function_spec.rb
+++ b/spec/models/serverless/function_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ::Serverless::Function do
+RSpec.describe ::Serverless::Function do
let(:project) { create(:project) }
let(:func) { described_class.new(project, 'test', 'test-ns') }
diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb
new file mode 100644
index 00000000000..ca57a5d4087
--- /dev/null
+++ b/spec/models/service_desk_setting_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ServiceDeskSetting do
+ describe 'validations' do
+ subject(:service_desk_setting) { create(:service_desk_setting) }
+
+ it { is_expected.to validate_presence_of(:project_id) }
+ it { is_expected.to validate_length_of(:outgoing_name).is_at_most(255) }
+ it { is_expected.to validate_length_of(:project_key).is_at_most(255) }
+ it { is_expected.to allow_value('abc123_').for(:project_key) }
+ it { is_expected.not_to allow_value('abc 12').for(:project_key) }
+ it { is_expected.not_to allow_value('Big val').for(:project_key) }
+
+ describe '.valid_issue_template' do
+ let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/issue_templates/service_desk.md' => 'template' }) }
+
+ it 'is not valid if template does not exist' do
+ settings = build(:service_desk_setting, project: project, issue_template_key: 'invalid key')
+
+ expect(settings).not_to be_valid
+ expect(settings.errors[:issue_template_key].first).to eq('is empty or does not exist')
+ end
+
+ it 'is valid if template exists' do
+ settings = build(:service_desk_setting, project: project, issue_template_key: 'service_desk')
+
+ expect(settings).to be_valid
+ end
+ end
+ end
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ end
+end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 8698a6cf3d3..75bbb074526 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Service do
+RSpec.describe Service do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
@@ -304,8 +304,6 @@ describe Service do
end
describe 'build issue tracker from an integration' do
- let(:title) { 'custom title' }
- let(:description) { 'custom description' }
let(:url) { 'http://jira.example.com' }
let(:api_url) { 'http://api-jira.example.com' }
let(:username) { 'jira-username' }
@@ -322,8 +320,6 @@ describe Service do
service = described_class.build_from_integration(project.id, integration)
expect(service).to be_active
- expect(service.title).to eq(title)
- expect(service.description).to eq(description)
expect(service.url).to eq(url)
expect(service.api_url).to eq(api_url)
expect(service.username).to eq(username)
@@ -335,7 +331,7 @@ describe Service do
# this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
context 'when data are stored in properties' do
- let(:properties) { data_params.merge(title: title, description: description) }
+ let(:properties) { data_params }
let!(:integration) do
create(:jira_service, :without_properties_callback, template: true, properties: properties.merge(additional: 'something'))
end
@@ -345,14 +341,14 @@ describe Service do
context 'when data are stored in separated fields' do
let(:integration) do
- create(:jira_service, :template, data_params.merge(properties: {}, title: title, description: description))
+ create(:jira_service, :template, data_params.merge(properties: {}))
end
it_behaves_like 'service creation from an integration'
end
context 'when data are stored in both properties and separated fields' do
- let(:properties) { data_params.merge(title: title, description: description) }
+ let(:properties) { data_params }
let(:integration) do
create(:jira_service, :without_properties_callback, active: true, template: true, properties: properties).tap do |service|
create(:jira_tracker_data, data_params.merge(service: service))
@@ -390,6 +386,33 @@ describe Service do
end
end
+ describe 'instance' do
+ describe '.instance_for' do
+ let_it_be(:jira_service) { create(:jira_service, :instance) }
+ let_it_be(:slack_service) { create(:slack_service, :instance) }
+
+ subject { described_class.instance_for(type) }
+
+ context 'Hipchat serivce' do
+ let(:type) { 'HipchatService' }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'Jira serivce' do
+ let(:type) { 'JiraService' }
+
+ it { is_expected.to eq(jira_service) }
+ end
+
+ context 'Slack serivce' do
+ let(:type) { 'SlackService' }
+
+ it { is_expected.to eq(slack_service) }
+ end
+ end
+ end
+
describe "{property}_changed?" do
let(:service) do
BambooService.create(
@@ -514,7 +537,6 @@ describe Service do
let(:service) do
GitlabIssueTrackerService.create(
project: create(:project),
- title: 'random title',
project_url: 'http://gitlab.example.com'
)
end
@@ -523,10 +545,6 @@ describe Service do
expect { service }.not_to raise_error
end
- it 'sets title correctly' do
- expect(service.title).to eq('random title')
- end
-
it 'sets data correctly' do
expect(service.data_fields.project_url).to eq('http://gitlab.example.com')
end
diff --git a/spec/models/shard_spec.rb b/spec/models/shard_spec.rb
index 4da86858b54..a9d11f4290c 100644
--- a/spec/models/shard_spec.rb
+++ b/spec/models/shard_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Shard do
+RSpec.describe Shard do
describe '.populate!' do
it 'creates shards based on the config file' do
expect(described_class.all).to be_empty
diff --git a/spec/models/snippet_blob_spec.rb b/spec/models/snippet_blob_spec.rb
index 88441e39d45..19b985f66ee 100644
--- a/spec/models/snippet_blob_spec.rb
+++ b/spec/models/snippet_blob_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SnippetBlob do
+RSpec.describe SnippetBlob do
let(:snippet) { create(:snippet) }
subject { described_class.new(snippet) }
diff --git a/spec/models/snippet_input_action_collection_spec.rb b/spec/models/snippet_input_action_collection_spec.rb
index ef18ab5a810..3ec206bd031 100644
--- a/spec/models/snippet_input_action_collection_spec.rb
+++ b/spec/models/snippet_input_action_collection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SnippetInputActionCollection do
+RSpec.describe SnippetInputActionCollection do
let(:action_name) { 'create' }
let(:action) { { action: action_name, file_path: 'foo', content: 'bar', previous_path: 'foobar' } }
let(:data) { [action, action] }
diff --git a/spec/models/snippet_input_action_spec.rb b/spec/models/snippet_input_action_spec.rb
index 5e379a48171..ca61b80df4c 100644
--- a/spec/models/snippet_input_action_spec.rb
+++ b/spec/models/snippet_input_action_spec.rb
@@ -2,18 +2,18 @@
require 'spec_helper'
-describe SnippetInputAction do
+RSpec.describe SnippetInputAction do
describe 'validations' do
using RSpec::Parameterized::TableSyntax
where(:action, :file_path, :content, :previous_path, :allowed_actions, :is_valid, :invalid_field) do
:create | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
- :move | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
+ :move | 'foobar' | 'foobar' | 'foo1' | nil | true | nil
:delete | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
:update | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
:foo | 'foobar' | 'foobar' | 'foobar' | nil | false | :action
'create' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
- 'move' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
+ 'move' | 'foobar' | 'foobar' | 'foo1' | nil | true | nil
'delete' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
'update' | 'foobar' | 'foobar' | 'foobar' | nil | true | nil
'foo' | 'foobar' | 'foobar' | 'foobar' | nil | false | :action
@@ -21,10 +21,16 @@ describe SnippetInputAction do
'' | 'foobar' | 'foobar' | 'foobar' | nil | false | :action
:move | 'foobar' | 'foobar' | nil | nil | false | :previous_path
:move | 'foobar' | 'foobar' | '' | nil | false | :previous_path
+ :move | 'foobar' | 'foobar' | 'foobar' | nil | false | :file_path
+ :move | nil | 'foobar' | 'foobar' | nil | false | :file_path
+ :move | '' | 'foobar' | 'foobar' | nil | false | :file_path
+ :move | nil | 'foobar' | 'foo1' | nil | false | :file_path
+ :move | 'foobar' | nil | 'foo1' | nil | true | nil
+ :move | 'foobar' | '' | 'foo1' | nil | true | nil
:create | 'foobar' | nil | 'foobar' | nil | false | :content
:create | 'foobar' | '' | 'foobar' | nil | false | :content
- :create | nil | 'foobar' | 'foobar' | nil | false | :file_path
- :create | '' | 'foobar' | 'foobar' | nil | false | :file_path
+ :create | nil | 'foobar' | 'foobar' | nil | true | nil
+ :create | '' | 'foobar' | 'foobar' | nil | true | nil
:update | 'foobar' | nil | 'foobar' | nil | false | :content
:update | 'foobar' | '' | 'foobar' | nil | false | :content
:update | 'other' | 'foobar' | 'foobar' | nil | false | :file_path
diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb
index b86a6f82f07..8c25d713c0a 100644
--- a/spec/models/snippet_repository_spec.rb
+++ b/spec/models/snippet_repository_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SnippetRepository do
+RSpec.describe SnippetRepository do
let_it_be(:user) { create(:user) }
let(:snippet) { create(:personal_snippet, :repository, author: user) }
let(:snippet_repository) { snippet.snippet_repository }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 4d6586c1df4..3f9c6981de1 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Snippet do
+RSpec.describe Snippet do
describe 'modules' do
subject { described_class }
@@ -20,6 +20,7 @@ describe Snippet do
it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
it { is_expected.to have_many(:user_mentions).class_name("SnippetUserMention") }
it { is_expected.to have_one(:snippet_repository) }
+ it { is_expected.to have_one(:statistics).class_name('SnippetStatistics').dependent(:destroy) }
end
describe 'validation' do
@@ -91,6 +92,17 @@ describe Snippet do
end
end
+ describe 'callbacks' do
+ it 'creates snippet statistics when the snippet is created' do
+ snippet = build(:snippet)
+ expect(snippet.statistics).to be_nil
+
+ snippet.save
+
+ expect(snippet.statistics).to be_persisted
+ end
+ end
+
describe '#to_reference' do
context 'when snippet belongs to a project' do
let(:project) { build(:project, name: 'sample-project') }
@@ -750,4 +762,29 @@ describe Snippet do
end
end
end
+
+ describe '#list_files' do
+ let_it_be(:snippet) { create(:snippet, :repository) }
+ let(:ref) { 'test-ref' }
+
+ subject { snippet.list_files(ref) }
+
+ context 'when snippet has a repository' do
+ it 'lists files from the repository with the ref' do
+ expect(snippet.repository).to receive(:ls_files).with(ref)
+
+ subject
+ end
+ end
+
+ context 'when snippet does not have a repository' do
+ before do
+ allow(snippet.repository).to receive(:empty?).and_return(true)
+ end
+
+ it 'returns an empty array' do
+ expect(subject).to eq []
+ end
+ end
+ end
end
diff --git a/spec/models/snippet_statistics_spec.rb b/spec/models/snippet_statistics_spec.rb
new file mode 100644
index 00000000000..ad25bd7b3be
--- /dev/null
+++ b/spec/models/snippet_statistics_spec.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SnippetStatistics do
+ let_it_be(:snippet_without_repo) { create(:snippet) }
+ let_it_be(:snippet_with_repo) { create(:snippet, :repository) }
+
+ let(:statistics) { snippet_with_repo.statistics }
+
+ it { is_expected.to belong_to(:snippet) }
+ it { is_expected.to validate_presence_of(:snippet) }
+
+ describe '#update_commit_count' do
+ subject { statistics.update_commit_count }
+
+ it 'updates the count of commits' do
+ commit_count = snippet_with_repo.repository.commit_count
+
+ subject
+
+ expect(statistics.commit_count).to eq commit_count
+ end
+
+ context 'when the snippet does not have a repository' do
+ let(:statistics) { snippet_without_repo.statistics }
+
+ it 'returns 0' do
+ expect(subject).to eq 0
+ expect(statistics.commit_count).to eq 0
+ end
+ end
+ end
+
+ describe '#update_file_count' do
+ subject { statistics.update_file_count }
+
+ it 'updates the count of files' do
+ file_count = snippet_with_repo.repository.ls_files(nil).count
+
+ subject
+
+ expect(statistics.file_count).to eq file_count
+ end
+
+ context 'when the snippet does not have a repository' do
+ let(:statistics) { snippet_without_repo.statistics }
+
+ it 'returns 0' do
+ expect(subject).to eq 0
+ expect(statistics.file_count).to eq 0
+ end
+ end
+ end
+
+ describe '#update_repository_size' do
+ subject { statistics.update_repository_size }
+
+ it 'updates the repository_size' do
+ repository_size = snippet_with_repo.repository.size.megabytes.to_i
+
+ subject
+
+ expect(statistics.repository_size).to eq repository_size
+ end
+
+ context 'when the snippet does not have a repository' do
+ let(:statistics) { snippet_without_repo.statistics }
+
+ it 'returns 0' do
+ expect(subject).to eq 0
+ expect(statistics.repository_size).to eq 0
+ end
+ end
+ end
+
+ describe '#refresh!' do
+ subject { statistics.refresh! }
+
+ it 'retrieves and saves statistic data from repository' do
+ expect(statistics).to receive(:update_commit_count)
+ expect(statistics).to receive(:update_file_count)
+ expect(statistics).to receive(:update_repository_size)
+ expect(statistics).to receive(:save!)
+
+ subject
+ end
+ end
+
+ context 'with a PersonalSnippet' do
+ let!(:snippet) { create(:personal_snippet, :repository) }
+
+ shared_examples 'personal snippet statistics updates' do
+ it 'schedules a namespace statistics worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async).once
+
+ statistics.save!
+ end
+
+ it 'does not try to update project stats' do
+ expect(statistics).not_to receive(:schedule_update_project_statistic)
+
+ statistics.save!
+ end
+ end
+
+ context 'when creating' do
+ let(:statistics) { build(:snippet_statistics, snippet_id: snippet.id, with_data: true) }
+
+ before do
+ snippet.statistics.delete
+ end
+
+ it_behaves_like 'personal snippet statistics updates'
+ end
+
+ context 'when updating' do
+ let(:statistics) { snippet.statistics }
+
+ before do
+ snippet.statistics.repository_size = 123
+ end
+
+ it_behaves_like 'personal snippet statistics updates'
+ end
+ end
+
+ context 'with a ProjectSnippet' do
+ let!(:snippet) { create(:project_snippet) }
+
+ it_behaves_like 'UpdateProjectStatistics' do
+ subject { build(:snippet_statistics, snippet: snippet, id: snippet.id, with_data: true) }
+
+ before do
+ # The shared examples requires the snippet statistics not to be present
+ snippet.statistics.delete
+ snippet.reload
+ end
+ end
+
+ it 'does not call personal snippet callbacks' do
+ expect(snippet.statistics).not_to receive(:update_author_root_storage_statistics)
+ expect(snippet.statistics).to receive(:schedule_update_project_statistic)
+
+ snippet.statistics.update!(repository_size: 123)
+ end
+ end
+end
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 8d0f247b5d6..97a0dc27f17 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SpamLog do
+RSpec.describe SpamLog do
let_it_be(:admin) { create(:admin) }
describe 'associations' do
diff --git a/spec/models/ssh_host_key_spec.rb b/spec/models/ssh_host_key_spec.rb
index a17cd8ba345..4d729d5585f 100644
--- a/spec/models/ssh_host_key_spec.rb
+++ b/spec/models/ssh_host_key_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SshHostKey do
+RSpec.describe SshHostKey do
using RSpec::Parameterized::TableSyntax
include ReactiveCachingHelpers
diff --git a/spec/models/state_note_spec.rb b/spec/models/state_note_spec.rb
index d3409315e41..bd07af7ceca 100644
--- a/spec/models/state_note_spec.rb
+++ b/spec/models/state_note_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe StateNote do
+RSpec.describe StateNote do
describe '.from_event' do
let_it_be(:author) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -10,18 +10,62 @@ describe StateNote do
ResourceStateEvent.states.each do |state, _value|
context "with event state #{state}" do
- let_it_be(:event) { create(:resource_state_event, issue: noteable, state: state, created_at: '2020-02-05') }
+ let(:event) { create(:resource_state_event, issue: noteable, state: state, created_at: '2020-02-05') }
subject { described_class.from_event(event, resource: noteable, resource_parent: project) }
- it_behaves_like 'a system note', exclude_project: true do
- let(:action) { state.to_s }
+ it_behaves_like 'a synthetic note', state == 'reopened' ? 'opened' : state
+
+ it 'contains the expected values' do
+ expect(subject.author).to eq(author)
+ expect(subject.created_at).to eq(event.created_at)
+ expect(subject.note).to eq(state)
+ end
+ end
+ end
+
+ context 'with a mentionable source' do
+ subject { described_class.from_event(event, resource: noteable, resource_parent: project) }
+
+ context 'with a commit' do
+ let(:commit) { create(:commit, project: project) }
+ let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', source_commit: commit.id) }
+
+ it 'contains the expected values' do
+ expect(subject.author).to eq(author)
+ expect(subject.created_at).to eq(subject.created_at)
+ expect(subject.note).to eq("closed via commit #{commit.id}")
+ end
+ end
+
+ context 'with a merge request' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', source_merge_request: merge_request) }
+
+ it 'contains the expected values' do
+ expect(subject.author).to eq(author)
+ expect(subject.created_at).to eq(event.created_at)
+ expect(subject.note).to eq("closed via merge request !#{merge_request.iid}")
+ end
+ end
+
+ context 'when closed by error tracking' do
+ let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', close_after_error_tracking_resolve: true) }
+
+ it 'contains the expected values' do
+ expect(subject.author).to eq(author)
+ expect(subject.created_at).to eq(event.created_at)
+ expect(subject.note).to eq('resolved the corresponding error and closed the issue.')
end
+ end
+
+ context 'when closed by promotheus alert' do
+ let(:event) { create(:resource_state_event, issue: noteable, state: :closed, created_at: '2020-02-05', close_auto_resolve_prometheus_alert: true) }
it 'contains the expected values' do
expect(subject.author).to eq(author)
expect(subject.created_at).to eq(event.created_at)
- expect(subject.note_html).to eq("<p dir=\"auto\">#{state}</p>")
+ expect(subject.note).to eq('automatically closed this issue because the alert resolved.')
end
end
end
diff --git a/spec/models/subscription_spec.rb b/spec/models/subscription_spec.rb
index 41bd48810b2..be85e6e10f4 100644
--- a/spec/models/subscription_spec.rb
+++ b/spec/models/subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Subscription do
+RSpec.describe Subscription do
describe 'relationships' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:subscribable) }
diff --git a/spec/models/suggestion_spec.rb b/spec/models/suggestion_spec.rb
index 2ac3ae0a5ad..6c30bc39c1d 100644
--- a/spec/models/suggestion_spec.rb
+++ b/spec/models/suggestion_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Suggestion do
+RSpec.describe Suggestion do
let(:suggestion) { create(:suggestion) }
describe 'associations' do
@@ -38,26 +38,106 @@ describe Suggestion do
end
describe '#appliable?' do
- context 'when patch is already applied' do
- let(:suggestion) { create(:suggestion, :applied) }
+ let(:suggestion) { build(:suggestion) }
- it 'returns false' do
- expect(suggestion).not_to be_appliable
+ subject(:appliable) { suggestion.appliable? }
+
+ before do
+ allow(suggestion).to receive(:inapplicable_reason).and_return(inapplicable_reason)
+ end
+
+ context 'when inapplicable_reason is nil' do
+ let(:inapplicable_reason) { nil }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when inapplicable_reason is not nil' do
+ let(:inapplicable_reason) { :applied }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#inapplicable_reason' do
+ let(:merge_request) { create(:merge_request) }
+
+ let!(:note) do
+ create(
+ :diff_note_on_merge_request,
+ project: merge_request.project,
+ noteable: merge_request
+ )
+ end
+
+ let(:suggestion) { build(:suggestion, note: note) }
+
+ subject(:inapplicable_reason) { suggestion.inapplicable_reason }
+
+ context 'when suggestion is already applied' do
+ let(:suggestion) { build(:suggestion, :applied, note: note) }
+
+ it { is_expected.to eq(:applied) }
+ end
+
+ context 'when merge request was merged' do
+ before do
+ merge_request.mark_as_merged!
+ end
+
+ it { is_expected.to eq(:merge_request_merged) }
+ end
+
+ context 'when merge request is closed' do
+ before do
+ merge_request.close!
end
+
+ it { is_expected.to eq(:merge_request_closed) }
end
- context 'when merge request is not opened' do
- let(:merge_request) { create(:merge_request, :merged) }
- let(:note) do
- create(:diff_note_on_merge_request, project: merge_request.project,
- noteable: merge_request)
+ context 'when source branch is deleted' do
+ before do
+ merge_request.project.repository.rm_branch(merge_request.author, merge_request.source_branch)
end
- let(:suggestion) { create(:suggestion, note: note) }
+ it { is_expected.to eq(:source_branch_deleted) }
+ end
- it 'returns false' do
- expect(suggestion).not_to be_appliable
+ context 'when content is outdated' do
+ before do
+ allow(suggestion).to receive(:outdated?).and_return(true)
+ end
+
+ it { is_expected.to eq(:outdated) }
+ end
+
+ context 'when note is outdated' do
+ before do
+ allow(note).to receive(:active?).and_return(false)
end
+
+ it { is_expected.to eq(:outdated) }
+ end
+
+ context 'when applicable' do
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#single_line?' do
+ subject(:single_line) { suggestion.single_line? }
+
+ context 'when suggestion is for a single line' do
+ let(:suggestion) { build(:suggestion, lines_above: 0, lines_below: 0) }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when suggestion is for multiple lines' do
+ let(:suggestion) { build(:suggestion, lines_above: 2, lines_below: 0) }
+
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb
index 801f139355b..9a6b57afb97 100644
--- a/spec/models/system_note_metadata_spec.rb
+++ b/spec/models/system_note_metadata_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SystemNoteMetadata do
+RSpec.describe SystemNoteMetadata do
describe 'associations' do
it { is_expected.to belong_to(:note) }
it { is_expected.to belong_to(:description_version) }
diff --git a/spec/models/term_agreement_spec.rb b/spec/models/term_agreement_spec.rb
index 42a48048b67..98c7a2daadd 100644
--- a/spec/models/term_agreement_spec.rb
+++ b/spec/models/term_agreement_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe TermAgreement do
+RSpec.describe TermAgreement do
describe 'validations' do
it { is_expected.to validate_presence_of(:term) }
it { is_expected.to validate_presence_of(:user) }
diff --git a/spec/models/terraform/state_spec.rb b/spec/models/terraform/state_spec.rb
index 3cd15e23ee2..00e67ad70db 100644
--- a/spec/models/terraform/state_spec.rb
+++ b/spec/models/terraform/state_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Terraform::State do
+RSpec.describe Terraform::State do
subject { create(:terraform_state, :with_file) }
let(:terraform_state_file) { fixture_file('terraform/terraform.tfstate') }
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index bda89fc01f3..44e81455a67 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Todo do
+RSpec.describe Todo do
let(:issue) { create(:issue) }
describe 'relationships' do
diff --git a/spec/models/tree_spec.rb b/spec/models/tree_spec.rb
index 7dde8459f9a..1522d836f76 100644
--- a/spec/models/tree_spec.rb
+++ b/spec/models/tree_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Tree do
+RSpec.describe Tree do
let(:repository) { create(:project, :repository).repository }
let(:sha) { repository.root_ref }
diff --git a/spec/models/trending_project_spec.rb b/spec/models/trending_project_spec.rb
index 39f5d686eb4..802f8befbcd 100644
--- a/spec/models/trending_project_spec.rb
+++ b/spec/models/trending_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe TrendingProject do
+RSpec.describe TrendingProject do
let(:user) { create(:user) }
let(:public_project1) { create(:project, :public, :repository) }
let(:public_project2) { create(:project, :public, :repository) }
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 8a64948d570..18388b4cd83 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Upload do
+RSpec.describe Upload do
describe 'associations' do
it { is_expected.to belong_to(:model) }
end
diff --git a/spec/models/uploads/fog_spec.rb b/spec/models/uploads/fog_spec.rb
index 72a169280af..899e6f2064c 100644
--- a/spec/models/uploads/fog_spec.rb
+++ b/spec/models/uploads/fog_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Uploads::Fog do
+RSpec.describe Uploads::Fog do
let(:data_store) { described_class.new }
before do
diff --git a/spec/models/uploads/local_spec.rb b/spec/models/uploads/local_spec.rb
index 374c3019edc..d354b252b39 100644
--- a/spec/models/uploads/local_spec.rb
+++ b/spec/models/uploads/local_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Uploads::Local do
+RSpec.describe Uploads::Local do
let(:data_store) { described_class.new }
before do
diff --git a/spec/models/user_agent_detail_spec.rb b/spec/models/user_agent_detail_spec.rb
index 5c28511b446..e3f3d9c342b 100644
--- a/spec/models/user_agent_detail_spec.rb
+++ b/spec/models/user_agent_detail_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserAgentDetail do
+RSpec.describe UserAgentDetail do
describe '.submittable?' do
it 'is submittable when not already submitted' do
detail = build(:user_agent_detail)
diff --git a/spec/models/user_callout_spec.rb b/spec/models/user_callout_spec.rb
index a084b1ac662..cdf70dd5190 100644
--- a/spec/models/user_callout_spec.rb
+++ b/spec/models/user_callout_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserCallout do
+RSpec.describe UserCallout do
let!(:callout) { create(:user_callout) }
it_behaves_like 'having unique enum values'
diff --git a/spec/models/user_canonical_email_spec.rb b/spec/models/user_canonical_email_spec.rb
index 54a4e968033..8e26f68c09b 100644
--- a/spec/models/user_canonical_email_spec.rb
+++ b/spec/models/user_canonical_email_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserCanonicalEmail do
+RSpec.describe UserCanonicalEmail do
it { is_expected.to belong_to(:user) }
describe 'validations' do
diff --git a/spec/models/user_custom_attribute_spec.rb b/spec/models/user_custom_attribute_spec.rb
index d0981b2d771..1a51ad662b0 100644
--- a/spec/models/user_custom_attribute_spec.rb
+++ b/spec/models/user_custom_attribute_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserCustomAttribute do
+RSpec.describe UserCustomAttribute do
describe 'assocations' do
it { is_expected.to belong_to(:user) }
end
diff --git a/spec/models/user_detail_spec.rb b/spec/models/user_detail_spec.rb
index 2b2bfff7be2..041af5b9c31 100644
--- a/spec/models/user_detail_spec.rb
+++ b/spec/models/user_detail_spec.rb
@@ -2,13 +2,42 @@
require 'spec_helper'
-describe UserDetail do
+RSpec.describe UserDetail do
it { is_expected.to belong_to(:user) }
describe 'validations' do
- describe 'job_title' do
+ describe '#job_title' do
it { is_expected.not_to validate_presence_of(:job_title) }
it { is_expected.to validate_length_of(:job_title).is_at_most(200) }
end
+
+ describe '#bio' do
+ it { is_expected.to validate_length_of(:bio).is_at_most(255) }
+ end
+ end
+
+ describe '#bio_html' do
+ let(:user) { create(:user, bio: 'some **bio**') }
+
+ subject { user.user_detail.bio_html }
+
+ it 'falls back to #bio when the html representation is missing' do
+ user.user_detail.update!(bio_html: nil)
+
+ expect(subject).to eq(user.user_detail.bio)
+ end
+
+ it 'stores rendered html' do
+ expect(subject).to include('some <strong>bio</strong>')
+ end
+
+ it 'does not try to set the value when the column is not there' do
+ without_bio_html_column = UserDetail.column_names - ['bio_html']
+
+ expect(described_class).to receive(:column_names).at_least(:once).and_return(without_bio_html_column)
+ expect(user.user_detail).not_to receive(:bio_html=)
+
+ subject
+ end
end
end
diff --git a/spec/models/user_highest_role_spec.rb b/spec/models/user_highest_role_spec.rb
index b3c795f6623..3ae672cf7f7 100644
--- a/spec/models/user_highest_role_spec.rb
+++ b/spec/models/user_highest_role_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserHighestRole do
+RSpec.describe UserHighestRole do
describe 'associations' do
it { is_expected.to belong_to(:user).required }
end
diff --git a/spec/models/user_interacted_project_spec.rb b/spec/models/user_interacted_project_spec.rb
index 83c66bf1969..2fec8be76e8 100644
--- a/spec/models/user_interacted_project_spec.rb
+++ b/spec/models/user_interacted_project_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserInteractedProject do
+RSpec.describe UserInteractedProject do
describe '.track' do
subject { described_class.track(event) }
diff --git a/spec/models/user_mentions/commit_user_mention_spec.rb b/spec/models/user_mentions/commit_user_mention_spec.rb
index ebad3902d6b..91d28241650 100644
--- a/spec/models/user_mentions/commit_user_mention_spec.rb
+++ b/spec/models/user_mentions/commit_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CommitUserMention do
+RSpec.describe CommitUserMention do
describe 'associations' do
it { is_expected.to belong_to(:note) }
end
diff --git a/spec/models/user_mentions/issue_user_mention_spec.rb b/spec/models/user_mentions/issue_user_mention_spec.rb
index ac29f3084b4..6faf598ee36 100644
--- a/spec/models/user_mentions/issue_user_mention_spec.rb
+++ b/spec/models/user_mentions/issue_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe IssueUserMention do
+RSpec.describe IssueUserMention do
describe 'associations' do
it { is_expected.to belong_to(:issue) }
it { is_expected.to belong_to(:note) }
diff --git a/spec/models/user_mentions/merge_request_user_mention_spec.rb b/spec/models/user_mentions/merge_request_user_mention_spec.rb
index c5c7cebfaa5..10fcb126965 100644
--- a/spec/models/user_mentions/merge_request_user_mention_spec.rb
+++ b/spec/models/user_mentions/merge_request_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe MergeRequestUserMention do
+RSpec.describe MergeRequestUserMention do
describe 'associations' do
it { is_expected.to belong_to(:merge_request) }
it { is_expected.to belong_to(:note) }
diff --git a/spec/models/user_mentions/snippet_user_mention_spec.rb b/spec/models/user_mentions/snippet_user_mention_spec.rb
index 0e34a2dd5a1..0762e731a53 100644
--- a/spec/models/user_mentions/snippet_user_mention_spec.rb
+++ b/spec/models/user_mentions/snippet_user_mention_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SnippetUserMention do
+RSpec.describe SnippetUserMention do
describe 'associations' do
it { is_expected.to belong_to(:snippet) }
it { is_expected.to belong_to(:note) }
diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb
index cf32d4eeca7..27ddaea763d 100644
--- a/spec/models/user_preference_spec.rb
+++ b/spec/models/user_preference_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserPreference do
+RSpec.describe UserPreference do
let(:user_preference) { create(:user_preference) }
describe 'notes filters global keys' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index dd4b174a38f..fa2e4b63648 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe User do
+RSpec.describe User do
include ProjectForksHelper
include TermsHelper
include ExclusiveLeaseHelpers
@@ -58,6 +58,10 @@ describe User do
it { is_expected.to delegate_method(:job_title).to(:user_detail).allow_nil }
it { is_expected.to delegate_method(:job_title=).to(:user_detail).with_arguments(:args).allow_nil }
+
+ it { is_expected.to delegate_method(:bio).to(:user_detail).allow_nil }
+ it { is_expected.to delegate_method(:bio=).to(:user_detail).with_arguments(:args).allow_nil }
+ it { is_expected.to delegate_method(:bio_html).to(:user_detail).allow_nil }
end
describe 'associations' do
@@ -91,64 +95,28 @@ describe User do
it { is_expected.to have_many(:metrics_users_starred_dashboards).inverse_of(:user) }
it { is_expected.to have_many(:reviews).inverse_of(:author) }
- describe "#bio" do
- it 'syncs bio with `user_details.bio` on create' do
- user = create(:user, bio: 'my bio')
-
- expect(user.bio).to eq(user.user_detail.bio)
+ describe "#user_detail" do
+ it 'does not persist `user_detail` by default' do
+ expect(create(:user).user_detail).not_to be_persisted
end
- context 'when `migrate_bio_to_user_details` feature flag is off' do
- before do
- stub_feature_flags(migrate_bio_to_user_details: false)
- end
-
- it 'does not sync bio with `user_details.bio`' do
- user = create(:user, bio: 'my bio')
+ it 'creates `user_detail` when `bio` is given' do
+ user = create(:user, bio: 'my bio')
- expect(user.bio).to eq('my bio')
- expect(user.user_detail.bio).to eq('')
- end
+ expect(user.user_detail).to be_persisted
+ expect(user.user_detail.bio).to eq('my bio')
end
- it 'syncs bio with `user_details.bio` on update' do
- user = create(:user)
-
- user.update!(bio: 'my bio')
+ it 'delegates `bio` to `user_detail`' do
+ user = create(:user, bio: 'my bio')
expect(user.bio).to eq(user.user_detail.bio)
end
- context 'when `user_details` association already exists' do
- let(:user) { create(:user) }
-
- before do
- create(:user_detail, user: user)
- end
-
- it 'syncs bio with `user_details.bio`' do
- user.update!(bio: 'my bio')
-
- expect(user.bio).to eq(user.user_detail.bio)
- end
-
- it 'falls back to "" when nil is given' do
- user.update!(bio: nil)
-
- expect(user.bio).to eq(nil)
- expect(user.user_detail.bio).to eq('')
- end
-
- # very unlikely scenario
- it 'truncates long bio when syncing to user_details' do
- invalid_bio = 'a' * 256
- truncated_bio = 'a' * 255
-
- user.bio = invalid_bio
- user.save(validate: false)
+ it 'creates `user_detail` when `bio` is first updated' do
+ user = create(:user)
- expect(user.user_detail.bio).to eq(truncated_bio)
- end
+ expect { user.update(bio: 'my bio') }.to change { user.user_detail.persisted? }.from(false).to(true)
end
end
@@ -214,7 +182,7 @@ describe User do
describe 'validations' do
describe 'password' do
- let!(:user) { create(:user) }
+ let!(:user) { build_stubbed(:user) }
before do
allow(Devise).to receive(:password_length).and_return(8..128)
@@ -337,8 +305,6 @@ describe User do
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
it { is_expected.not_to allow_value(Gitlab::Database::MAX_INT_VALUE + 1).for(:projects_limit) }
- it { is_expected.to validate_length_of(:bio).is_at_most(255) }
-
it_behaves_like 'an object with email-formated attributes', :email do
subject { build(:user) }
end
@@ -3745,6 +3711,12 @@ describe User do
expect(user.namespace).not_to be_nil
end
+
+ it 'creates the namespace setting' do
+ user.save!
+
+ expect(user.namespace.namespace_settings).to be_persisted
+ end
end
context 'for an existing user' do
@@ -4634,7 +4606,8 @@ describe User do
[
{ state: 'blocked' },
{ user_type: :ghost },
- { user_type: :alert_bot }
+ { user_type: :alert_bot },
+ { user_type: :support_bot }
]
end
@@ -4688,6 +4661,7 @@ describe User do
where(:user_type, :expected_result) do
'human' | true
'alert_bot' | false
+ 'support_bot' | false
end
with_them do
@@ -4756,19 +4730,44 @@ describe User do
end
end
- describe '#migration_bot' do
- it 'creates the user if it does not exist' do
- expect do
- described_class.migration_bot
- end.to change { User.where(user_type: :migration_bot).count }.by(1)
+ context 'bot users' do
+ shared_examples 'bot users' do |bot_type|
+ it 'creates the user if it does not exist' do
+ expect do
+ described_class.public_send(bot_type)
+ end.to change { User.where(user_type: bot_type).count }.by(1)
+ end
+
+ it 'creates a route for the namespace of the created user' do
+ bot_user = described_class.public_send(bot_type)
+
+ expect(bot_user.namespace.route).to be_present
+ end
+
+ it 'does not create a new user if it already exists' do
+ described_class.public_send(bot_type)
+
+ expect do
+ described_class.public_send(bot_type)
+ end.not_to change { User.count }
+ end
end
- it 'does not create a new user if it already exists' do
- described_class.migration_bot
+ shared_examples 'bot user avatars' do |bot_type, avatar_filename|
+ it 'sets the custom avatar for the created bot' do
+ bot_user = described_class.public_send(bot_type)
- expect do
- described_class.migration_bot
- end.not_to change { User.count }
+ expect(bot_user.avatar.url).to be_present
+ expect(bot_user.avatar.filename).to eq(avatar_filename)
+ end
end
+
+ it_behaves_like 'bot users', :alert_bot
+ it_behaves_like 'bot users', :support_bot
+ it_behaves_like 'bot users', :migration_bot
+ it_behaves_like 'bot users', :ghost
+
+ it_behaves_like 'bot user avatars', :alert_bot, 'alert-bot.png'
+ it_behaves_like 'bot user avatars', :support_bot, 'support-bot.png'
end
end
diff --git a/spec/models/user_status_spec.rb b/spec/models/user_status_spec.rb
index fcc01cdae3d..2c0664bd165 100644
--- a/spec/models/user_status_spec.rb
+++ b/spec/models/user_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UserStatus do
+RSpec.describe UserStatus do
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to allow_value('smirk').for(:emoji) }
diff --git a/spec/models/users_statistics_spec.rb b/spec/models/users_statistics_spec.rb
index 4437a5469c6..b4b7ddb7c63 100644
--- a/spec/models/users_statistics_spec.rb
+++ b/spec/models/users_statistics_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe UsersStatistics do
+RSpec.describe UsersStatistics do
let(:users_statistics) { build(:users_statistics) }
describe 'scopes' do
diff --git a/spec/models/web_ide_terminal_spec.rb b/spec/models/web_ide_terminal_spec.rb
index 4103a26c75a..149fce33f43 100644
--- a/spec/models/web_ide_terminal_spec.rb
+++ b/spec/models/web_ide_terminal_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WebIdeTerminal do
+RSpec.describe WebIdeTerminal do
let(:build) { create(:ci_build) }
subject { described_class.new(build) }
diff --git a/spec/models/wiki_page/meta_spec.rb b/spec/models/wiki_page/meta_spec.rb
index 0255dd802cf..aaac72cbc68 100644
--- a/spec/models/wiki_page/meta_spec.rb
+++ b/spec/models/wiki_page/meta_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WikiPage::Meta do
+RSpec.describe WikiPage::Meta do
let_it_be(:project) { create(:project, :wiki_repo) }
let_it_be(:other_project) { create(:project) }
diff --git a/spec/models/wiki_page/slug_spec.rb b/spec/models/wiki_page/slug_spec.rb
index 324dea6b320..cf256c67277 100644
--- a/spec/models/wiki_page/slug_spec.rb
+++ b/spec/models/wiki_page/slug_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe WikiPage::Slug do
+RSpec.describe WikiPage::Slug do
let_it_be(:meta) { create(:wiki_page_meta) }
describe 'Associations' do
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 8f2da8ff9a1..a2ca6441f28 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-describe WikiPage do
+RSpec.describe WikiPage do
let_it_be(:user) { create(:user) }
let(:container) { create(:project, :wiki_repo) }
let(:wiki) { Wiki.for_container(container, user) }
@@ -864,6 +864,24 @@ describe WikiPage do
end
end
+ describe '#diffs' do
+ subject { existing_page }
+
+ it 'returns a diff instance' do
+ diffs = subject.diffs(foo: 'bar')
+
+ expect(diffs).to be_a(Gitlab::Diff::FileCollection::WikiPage)
+ expect(diffs.diffable).to be_a(Commit)
+ expect(diffs.diffable.id).to eq(subject.version.id)
+ expect(diffs.project).to be(subject.wiki)
+ expect(diffs.diff_options).to include(
+ expanded: true,
+ paths: [subject.path],
+ foo: 'bar'
+ )
+ end
+ end
+
private
def get_slugs(page_or_dir)
diff --git a/spec/models/zoom_meeting_spec.rb b/spec/models/zoom_meeting_spec.rb
index 3dad957a1ce..00a0f92e848 100644
--- a/spec/models/zoom_meeting_spec.rb
+++ b/spec/models/zoom_meeting_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ZoomMeeting do
+RSpec.describe ZoomMeeting do
let(:project) { build(:project) }
describe 'Factory' do