diff --git a/.rubocop_todo/qa/fabricate_usage.yml b/.rubocop_todo/qa/fabricate_usage.yml new file mode 100644 index 0000000000000000000000000000000000000000..4560204b84c9e08d41700375d4253ee91247f191 --- /dev/null +++ b/.rubocop_todo/qa/fabricate_usage.yml @@ -0,0 +1,36 @@ +--- +QA/FabricateUsage: + Include: + - 'qa/qa/specs/**/*_spec.rb' + Details: grace period + Exclude: + - 'qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb' + - 'qa/qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_ci_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/upgrade_group_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/saas_user_limit_experience_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/utilization/free_namespace_storage_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/attachment_replication_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/database_delete_replication_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_ci_job_log_artifacts_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_maven_package_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/geo_replication_npm_registry_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/http_push_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/http_push_to_secondary_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/rename_replication_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/ssh_push_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/ssh_push_to_secondary_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/3_create/remote_development/workspace_actions_spec.rb' diff --git a/rubocop/cop/qa/fabricate_usage.rb b/rubocop/cop/qa/fabricate_usage.rb new file mode 100644 index 0000000000000000000000000000000000000000..1787df0e7dfe0781ee68e729b6da3dbc8bf67ac2 --- /dev/null +++ b/rubocop/cop/qa/fabricate_usage.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require_relative '../../qa_helpers' + +module RuboCop + module Cop + module QA + # This cop checks for the usages of API fabrications and suggests using + # the corresponding QA factories instead. + # @example + # Good: + # create(:project) + # Bad: + # Resource::Project.fabricate_via_api! + # @example + # Good: + # create(:group) + # Bad: + # Resource::Group.fabricate_via_api! + # @example + # Good: + # create(:user, username: 'username', password: 'password') + # Bad: + # Resource::User.fabricate_via_api! do |user| + # user.username = 'username' + # user.password = 'password' + # end + class FabricateUsage < RuboCop::Cop::Base + MESSAGE = "Prefer create(:%{factory}[, ...]) here." + RESOURCES_TO_CHECK = { + 'Resource::Project' => :project, + 'Resource::Group' => :group, + 'Resource::Issue' => :issue, + 'Resource::User' => :user, + 'Resource::Pipeline' => :pipeline, + 'Resource::Job' => :job, + 'Resource::File' => :file, + 'Resource::GroupAccessToken' => :group_access_token, + 'Resource::ProjectAccessToken' => :project_access_token, + 'Resource::GroupLabel' => :group_label, + 'Resource::ProjectLabel' => :project_label, + 'Resource::GroupRunner' => :group_runner, + 'Resource::ProjectRunner' => :project_runner, + 'Resource::GroupMilestone' => :group_milestone, + 'Resource::ProjectMilestone' => :project_milestone, + 'Resource::Snippet' => :snippet, + 'Resource::ProjectSnippet' => :project_snippet + }.freeze + + RESTRICT_ON_SEND = %i[fabricate_via_api!].freeze + + def_node_matcher :const_receiver, <<~PATTERN + (send $const ...) + PATTERN + + def on_send(node) + factory = RESOURCES_TO_CHECK[const_receiver(node)&.const_name] + return unless factory + + add_offense(node, message: format(MESSAGE, factory: factory)) + end + end + end + end +end diff --git a/spec/rubocop/cop/qa/fabricate_usage_spec.rb b/spec/rubocop/cop/qa/fabricate_usage_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..7c4b42b91e0e7dae7eb4e241aeb607d80b988846 --- /dev/null +++ b/spec/rubocop/cop/qa/fabricate_usage_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'rubocop_spec_helper' + +require_relative '../../../../rubocop/cop/qa/fabricate_usage' + +RSpec.describe RuboCop::Cop::QA::FabricateUsage, feature_category: :quality_management do + let(:source_file) { 'qa/qa/specs/spec.rb' } + + it 'registers an offense when using fabricate_via_api! for a valid resource' do + expect_offense(<<~RUBY) + Resource::Project.fabricate_via_api! do |project| + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer create(:project[, ...]) here. + project.name = 'test' + end + RUBY + end + + it 'registers an offense for groups' do + expect_offense(<<~RUBY) + Resource::Group.fabricate_via_api! do |group| + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer create(:group[, ...]) here. + group.path = 'test' + end + RUBY + end + + it 'does not register an offense when using fabricate_via_api! for an unenforced resource' do + expect_no_offenses(<<~RUBY) + Resource::Invalid.fabricate_via_api! do |project| + project.name = 'test' + end + RUBY + end +end