Skip to content

RSpec: Ensure that finders' `execute` always returns AR relation

Peter Leitzen requested to merge pl-rspec-support-finder-exection-relation into master

What does this MR do and why?

This MR ensures that all finders' execute method return ActiveRecord::Relation.

All current offenses are tracked in an allow list YAML which can be used to either exclude finders temporarily or permanently. These entries are not checked.

Closes #298771 (closed)

Failure example

When a finder method execute does not return a ActiveRecord::Relation users see the following example failure:

Failures:

  1) TagsFinder#execute filter only filters tags by name
     Failure/Error:
                 raise <<~MESSAGE
                   #{self.class}#execute returned `#{result.class}` instead of `ActiveRecord::Relation`.
                   All finder classes are expected to return `ActiveRecord::Relation`.

                   Read more at https://docs.gitlab.com/ee/development/reusing_abstractions.html#finders
                 MESSAGE

     RuntimeError:
       TagsFinder#execute returned `Array` instead of `ActiveRecord::Relation`.
       All finder classes are expected to return `ActiveRecord::Relation`.

       Read more at https://docs.gitlab.com/ee/development/reusing_abstractions.html#finders
     # ./spec/support/finder_collection.rb:30:in `execute'
     # ./spec/finders/tags_finder_spec.rb:11:in `load_tags'
     # ./spec/finders/tags_finder_spec.rb:47:in `block (4 levels) in <top (required)>'
     # ./spec/spec_helper.rb:405:in `block (3 levels) in <top (required)>'
     # ./spec/support/sidekiq_middleware.rb:9:in `with_sidekiq_server_middleware'
     # ./spec/spec_helper.rb:396:in `block (2 levels) in <top (required)>'
     # ./spec/spec_helper.rb:392:in `block (3 levels) in <top (required)>'
     # ./lib/gitlab/application_context.rb:52:in `with_raw_context'
     # ./spec/spec_helper.rb:392:in `block (2 levels) in <top (required)>'
     # ./spec/spec_helper.rb:261:in `block (2 levels) in <top (required)>'
     # ./spec/support/system_exit_detected.rb:7:in `block (2 levels) in <main>'
     # ./spec/support/database/prevent_cross_joins.rb:106:in `block (3 levels) in <main>'
     # ./spec/support/database/prevent_cross_joins.rb:60:in `with_cross_joins_prevented'
     # ./spec/support/database/prevent_cross_joins.rb:106:in `block (2 levels) in <main>'
Offenses extracted from pipeline log

Via

ruby ~/devel/gitlab/gitlab-tools/bin/ci_logs gitlab-org/gitlab 573031599 rspec | grep -E "^\S+#execute returned" | sort | uniq > .local/finder_collection.txt
AccessRequestsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Admin::PlansFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Admin::PlansFinder#execute returned `Plan` instead of `ActiveRecord::Relation`
Analytics::CycleAnalytics::StageFinder#execute returned `Analytics::CycleAnalytics::GroupStage` instead of `ActiveRecord::Relation`
Analytics::CycleAnalytics::StageFinder#execute returned `Analytics::CycleAnalytics::ProjectStage` instead of `ActiveRecord::Relation`
ApplicationsFinder#execute returned `Doorkeeper::Application` instead of `ActiveRecord::Relation`
ApplicationsFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Autocomplete::GroupFinder#execute returned `Group` instead of `ActiveRecord::Relation`
Autocomplete::GroupFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Autocomplete::ProjectFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Autocomplete::ProjectFinder#execute returned `Project` instead of `ActiveRecord::Relation`
Autocomplete::UsersFinder#execute returned `Array` instead of `ActiveRecord::Relation`
BilledUsersFinder#execute returned `Hash` instead of `ActiveRecord::Relation`
Boards::BoardsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Boards::VisitsFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
BranchesFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Ci::AuthJobFinder#execute returned `Ci::Build` instead of `ActiveRecord::Relation`
Ci::AuthJobFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Ci::CommitStatusesFinder#execute returned `Hash` instead of `ActiveRecord::Relation`
Ci::DailyBuildGroupReportResultsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
ClusterAncestorsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Clusters::AgentAuthorizationsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Clusters::KubernetesNamespaceFinder#execute returned `Clusters::KubernetesNamespace` instead of `ActiveRecord::Relation`
Clusters::KubernetesNamespaceFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
ComplianceManagement::MergeRequests::ComplianceViolationsFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
ContainerRepositoriesFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
ContextCommitsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
ContextCommitsFinder#execute returned `CommitCollection` instead of `ActiveRecord::Relation`
Environments::EnvironmentNamesFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Environments::EnvironmentsByDeploymentsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
EventsFinder#execute returned `Kaminari::PaginatableArray` instead of `ActiveRecord::Relation`
GroupDescendantsFinder#execute returned `Kaminari::PaginatableArray` instead of `ActiveRecord::Relation`
Groups::ProjectsRequiringAuthorizationsRefresh::OnDirectMembershipFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Groups::ProjectsRequiringAuthorizationsRefresh::OnTransferFinder#execute returned `Array` instead of `ActiveRecord::Relation`
KeysFinder#execute returned `DeployKey` instead of `ActiveRecord::Relation`
KeysFinder#execute returned `Key` instead of `ActiveRecord::Relation`
KeysFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
LfsPointersFinder#execute returned `Array` instead of `ActiveRecord::Relation`
LicenseTemplateFinder#execute returned `Array` instead of `ActiveRecord::Relation`
LicenseTemplateFinder#execute returned `LicenseTemplate` instead of `ActiveRecord::Relation`
MergeRequests::OldestPerCommitFinder#execute returned `Hash` instead of `ActiveRecord::Relation`
NotesFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Packages::BuildInfosFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Packages::Conan::PackageFileFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Packages::Conan::PackageFileFinder#execute returned `Packages::PackageFile` instead of `ActiveRecord::Relation`
Packages::Go::ModuleFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Packages::Go::ModuleFinder#execute returned `Packages::Go::Module` instead of `ActiveRecord::Relation`
Packages::Go::PackageFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Packages::Go::PackageFinder#execute returned `Packages::Package` instead of `ActiveRecord::Relation`
Packages::Go::VersionFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Packages::PackageFileFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Packages::PackageFileFinder#execute returned `Packages::PackageFile` instead of `ActiveRecord::Relation`
Packages::PackageFinder#execute returned `Packages::Package` instead of `ActiveRecord::Relation`
Packages::Pypi::PackageFinder#execute returned `Packages::Package` instead of `ActiveRecord::Relation`
Projects::Integrations::Jira::ByIdsFinder#execute returned `Hash` instead of `ActiveRecord::Relation`
Projects::Integrations::Jira::ByIdsFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Projects::Integrations::Jira::IssuesFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Releases::EvidencePipelineFinder#execute returned `Ci::Pipeline` instead of `ActiveRecord::Relation`
Releases::EvidencePipelineFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Repositories::BranchNamesFinder#execute returned `Enumerator::Lazy` instead of `ActiveRecord::Relation`
Repositories::ChangelogTagFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Repositories::ChangelogTagFinder#execute returned `RSpec::Mocks::Double` instead of `ActiveRecord::Relation`
Repositories::TreeFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Security::FindingsFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Security::FindingsFinder#execute returned `Security::FindingsFinder::ResultSet` instead of `ActiveRecord::Relation`
Security::PipelineVulnerabilitiesFinder#execute returned `Gitlab::Ci::Reports::Security::AggregatedReport` instead of `ActiveRecord::Relation`
Security::ScanExecutionPoliciesFinder#execute returned `Array` instead of `ActiveRecord::Relation`
Security::TrainingProviders::BaseUrlFinder#execute returned `Hash` instead of `ActiveRecord::Relation`
Security::TrainingProviders::BaseUrlFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
Security::TrainingUrlsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
SentryIssueFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
SentryIssueFinder#execute returned `SentryIssue` instead of `ActiveRecord::Relation`
ServerlessDomainFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
ServerlessDomainFinder#execute returned `Serverless::Domain` instead of `ActiveRecord::Relation`
TagsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Array` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::DockerfileTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::GitignoreTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::GitlabCiYmlTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::IssueTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::MergeRequestTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `Gitlab::Template::MetricsDashboardTemplate` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
TemplateFinder#execute returned `OpenStruct` instead of `ActiveRecord::Relation`
UploaderFinder#execute returned `FileUploader` instead of `ActiveRecord::Relation`
UploaderFinder#execute returned `NilClass` instead of `ActiveRecord::Relation`
UserGroupNotificationSettingsFinder#execute returned `Array` instead of `ActiveRecord::Relation`
UserGroupsCounter#execute returned `Hash` instead of `ActiveRecord::Relation`

How to set up and validate locally

bin/rspec $(find spec ee/spec -name "*_finder_spec.rb")

echo "[]" > spec/support/finder_collection_allowlist.yml

bin/rspec $(find spec ee/spec -name "*_finder_spec.rb") 💥

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Peter Leitzen

Merge request reports