diff options
Diffstat (limited to 'spec/finders')
18 files changed, 711 insertions, 118 deletions
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index 004629eda95..3d80ed19eb6 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -3,8 +3,9 @@ require 'spec_helper' RSpec.describe BranchesFinder, feature_category: :source_code_management do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + let(:repository) { project.repository } let(:branch_finder) { described_class.new(repository, params) } @@ -344,6 +345,60 @@ RSpec.describe BranchesFinder, feature_category: :source_code_management do end end + describe '#next_cursor' do + subject { branch_finder.next_cursor } + + it 'always nil before #execute call' do + is_expected.to be_nil + end + + context 'after #execute' do + context 'with gitaly pagination' do + before do + branch_finder.execute(gitaly_pagination: true) + end + + context 'without pagination params' do + it { is_expected.to be_nil } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_present } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to be_present } + end + end + end + + context 'without gitaly pagination' do + before do + branch_finder.execute(gitaly_pagination: false) + end + + context 'without pagination params' do + it { is_expected.to be_nil } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_nil } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to be_nil } + end + end + end + end + end + describe '#total' do subject { branch_finder.total } diff --git a/spec/finders/ci/catalog/resources/versions_finder_spec.rb b/spec/finders/ci/catalog/resources/versions_finder_spec.rb index b2418aa45dd..b541b84f198 100644 --- a/spec/finders/ci/catalog/resources/versions_finder_spec.rb +++ b/spec/finders/ci/catalog/resources/versions_finder_spec.rb @@ -22,13 +22,13 @@ RSpec.describe Ci::Catalog::Resources::VersionsFinder, feature_category: :pipeli end.not_to exceed_query_limit(control_count) end - context 'when the user is not authorized for any catalog resource' do + context 'when the user is not authorized' do it 'returns empty response' do is_expected.to be_empty end end - describe 'versions' do + context 'when the user is authorized' do before_all do resource1.project.add_guest(current_user) end @@ -74,7 +74,7 @@ RSpec.describe Ci::Catalog::Resources::VersionsFinder, feature_category: :pipeli end end - describe 'latest versions' do + context 'when `latest` parameter is true' do before_all do resource1.project.add_guest(current_user) resource2.project.add_guest(current_user) @@ -85,22 +85,5 @@ RSpec.describe Ci::Catalog::Resources::VersionsFinder, feature_category: :pipeli it 'returns the latest version for each authorized catalog resource' do expect(execute).to match_array([v1_1, v2_1]) end - - context 'when one catalog resource does not have versions' do - it 'returns the latest version of only the catalog resource with versions' do - resource1.versions.delete_all(:delete_all) - - is_expected.to match_array([v2_1]) - end - end - - context 'when no catalog resource has versions' do - it 'returns empty response' do - resource1.versions.delete_all(:delete_all) - resource2.versions.delete_all(:delete_all) - - is_expected.to be_empty - end - end end end diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb index 7f680f50297..fbe44244dba 100644 --- a/spec/finders/ci/runners_finder_spec.rb +++ b/spec/finders/ci/runners_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::RunnersFinder, feature_category: :runner_fleet do +RSpec.describe Ci::RunnersFinder, feature_category: :fleet_visibility do context 'admin' do let_it_be(:admin) { create(:user, :admin) } diff --git a/spec/finders/concerns/packages/finder_helper_spec.rb b/spec/finders/concerns/packages/finder_helper_spec.rb index f81e940c7ed..4145e1e2a54 100644 --- a/spec/finders/concerns/packages/finder_helper_spec.rb +++ b/spec/finders/concerns/packages/finder_helper_spec.rb @@ -27,6 +27,115 @@ RSpec.describe ::Packages::FinderHelper, feature_category: :package_registry do it { is_expected.to eq [package1] } end + describe '#packages_for' do + using RSpec::Parameterized::TableSyntax + + let_it_be_with_reload(:group) { create(:group) } + let_it_be_with_reload(:subgroup) { create(:group, parent: group) } + let_it_be(:project) { create(:project, namespace: group) } + let_it_be(:project2) { create(:project, namespace: subgroup) } + let_it_be(:package1) { create(:package, project: project) } + let_it_be(:package2) { create(:package, project: project2) } + let_it_be(:package3) { create(:package, :error, project: project2) } + + let(:finder_class) do + Class.new do + include ::Packages::FinderHelper + + def initialize(user) + @current_user = user + end + + def execute(group) + packages_for(@current_user, within_group: group) + end + end + end + + let(:finder) { finder_class.new(user) } + + subject { finder.execute(group) } + + shared_examples 'returning both packages' do + it { is_expected.to contain_exactly(package1, package2) } + end + + shared_examples 'returning no packages' do + it { is_expected.to be_empty } + end + + shared_examples 'returning package2' do + it { is_expected.to contain_exactly(package2) } + end + + context 'with an user' do + let_it_be(:user) { create(:user) } + + where(:group_visibility, :subgroup_visibility, :shared_example_name) do + 'public' | 'public' | 'returning both packages' + # All packages are returned because of the parent group visibility set to `public` + # and all users will have `read_group` permission. + 'public' | 'private' | 'returning both packages' + # No packages are returned because of the parent group visibility set to `private` + # and non-members won't have `read_group` permission. + 'private' | 'private' | 'returning no packages' + end + + with_them do + before do + subgroup.update!(visibility: subgroup_visibility) + group.update!(visibility: group_visibility) + end + + it_behaves_like params[:shared_example_name] + end + + context 'without a group' do + subject { finder.execute(nil) } + + it_behaves_like 'returning no packages' + end + + context 'with a subgroup' do + subject { finder.execute(subgroup) } + + it_behaves_like 'returning package2' + end + end + + context 'with a deploy token' do + let_it_be(:user) { create(:deploy_token, :group, read_package_registry: true) } + let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: user, group: group) } + + where(:group_visibility, :subgroup_visibility, :shared_example_name) do + 'public' | 'public' | 'returning both packages' + 'public' | 'private' | 'returning both packages' + 'private' | 'private' | 'returning both packages' + end + + with_them do + before do + subgroup.update!(visibility: subgroup_visibility) + group.update!(visibility: group_visibility) + end + + it_behaves_like params[:shared_example_name] + end + + context 'without a group' do + subject { finder.execute(nil) } + + it_behaves_like 'returning no packages' + end + + context 'with a subgroup' do + subject { finder.execute(subgroup) } + + it_behaves_like 'returning both packages' + end + end + end + describe '#packages_visible_to_user' do using RSpec::Parameterized::TableSyntax diff --git a/spec/finders/deploy_keys/deploy_keys_finder_spec.rb b/spec/finders/deploy_keys/deploy_keys_finder_spec.rb new file mode 100644 index 00000000000..f0d3935cc95 --- /dev/null +++ b/spec/finders/deploy_keys/deploy_keys_finder_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DeployKeys::DeployKeysFinder, feature_category: :continuous_delivery do + describe '#execute' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + + let_it_be(:accessible_project) { create(:project, :internal).tap { |p| p.add_developer(user) } } + let_it_be(:inaccessible_project) { create(:project, :internal) } + let_it_be(:project_private) { create(:project, :private) } + + let_it_be(:deploy_key_for_target_project) do + create(:deploy_keys_project, project: project, deploy_key: create(:deploy_key)) + end + + let_it_be(:deploy_key_for_accessible_project) do + create(:deploy_keys_project, project: accessible_project, deploy_key: create(:deploy_key)) + end + + let_it_be(:deploy_key_for_inaccessible_project) do + create(:deploy_keys_project, project: inaccessible_project, deploy_key: create(:deploy_key)) + end + + let_it_be(:deploy_keys_project_private) do + create(:deploy_keys_project, project: project_private, deploy_key: create(:another_deploy_key)) + end + + let_it_be(:deploy_key_public) { create(:deploy_key, public: true) } + + let(:params) { {} } + + subject(:result) { described_class.new(project, user, params).execute } + + context 'with access' do + before_all do + project.add_maintainer(user) + end + + context 'when filtering for enabled_keys' do + let(:params) { { filter: :enabled_keys } } + + it 'returns the correct result' do + expect(result.map(&:id)).to match_array([deploy_key_for_target_project.deploy_key_id]) + end + end + + context 'when filtering for available project keys' do + let(:params) { { filter: :available_project_keys } } + + it 'returns the correct result' do + expect(result.map(&:id)).to match_array([deploy_key_for_accessible_project.deploy_key_id]) + end + end + + context 'when filtering for available public keys' do + let(:params) { { filter: :available_public_keys } } + + it 'returns the correct result' do + expect(result.map(&:id)).to match_array([deploy_key_public.id]) + end + end + + context 'when there are no set filters' do + it 'returns an empty collection' do + expect(result).to eq DeployKey.none + end + end + end + + context 'without access' do + it 'returns an empty collection' do + expect(result).to eq DeployKey.none + end + end + end +end diff --git a/spec/finders/groups/custom_emoji_finder_spec.rb b/spec/finders/groups/custom_emoji_finder_spec.rb new file mode 100644 index 00000000000..f1044997d4f --- /dev/null +++ b/spec/finders/groups/custom_emoji_finder_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Groups::CustomEmojiFinder, feature_category: :code_review_workflow do + describe '#execute' do + let(:params) { {} } + + subject(:execute) { described_class.new(group, params).execute } + + context 'when inside a group' do + let_it_be(:group) { create(:group) } + let_it_be(:custom_emoji) { create(:custom_emoji, group: group) } + + it 'returns custom emoji from group' do + expect(execute).to contain_exactly(custom_emoji) + end + end + + context 'when group is nil' do + let_it_be(:group) { nil } + + it 'returns nil' do + expect(execute).to be_empty + end + end + + context 'when group is a subgroup' do + let_it_be(:parent) { create(:group) } + let_it_be(:group) { create(:group, parent: parent) } + let_it_be(:custom_emoji) { create(:custom_emoji, group: group) } + + it 'returns custom emoji' do + expect(described_class.new(group, params).execute).to contain_exactly(custom_emoji) + end + end + + describe 'when custom emoji is in parent group' do + let_it_be(:parent) { create(:group) } + let_it_be(:group) { create(:group, parent: parent) } + let_it_be(:custom_emoji) { create(:custom_emoji, group: parent) } + let(:params) { { include_ancestor_groups: true } } + + it 'returns custom emoji' do + expect(execute).to contain_exactly(custom_emoji) + end + + context 'when params is empty' do + let(:params) { {} } + + it 'returns empty record' do + expect(execute).to eq([]) + end + end + + context 'when include_ancestor_groups is false' do + let(:params) { { include_ancestor_groups: false } } + + it 'returns empty record' do + expect(execute).to eq([]) + end + end + end + end +end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index f20c03c9658..5d69988a761 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -274,6 +274,38 @@ RSpec.describe GroupsFinder, feature_category: :groups_and_projects do end end + context 'with organization' do + let_it_be(:organization_user) { create(:organization_user) } + let_it_be(:organization) { organization_user.organization } + let_it_be(:user) { organization_user.user } + let_it_be(:public_group) { create(:group, name: 'public-group', organization: organization) } + let_it_be(:outside_organization_group) { create(:group) } + let_it_be(:private_group) { create(:group, :private, name: 'private-group', organization: organization) } + let_it_be(:no_access_group_in_org) { create(:group, :private, name: 'no-access', organization: organization) } + + let(:current_user) { user } + let(:params) { { organization: organization } } + let(:finder) { described_class.new(current_user, params) } + + subject(:result) { finder.execute.to_a } + + before_all do + private_group.add_developer(user) + public_group.add_developer(user) + outside_organization_group.add_developer(user) + end + + context 'when user is only authorized to read the public group' do + let(:current_user) { create(:user) } + + it { is_expected.to contain_exactly(public_group) } + end + + it 'return all groups inside the organization' do + expect(result).to contain_exactly(public_group, private_group) + end + end + context 'with include_ancestors' do let_it_be(:user) { create(:user) } diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 4df6197e3b0..e0fc494d033 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -161,6 +161,27 @@ RSpec.describe MembersFinder, feature_category: :groups_and_projects do expect(result).to eq([member3, member2, member1]) end + it 'avoids N+1 database queries on accessing user records' do + project.add_maintainer(user2) + + # warm up + # We need this warm up because there is 1 query being fired in one of the policies, + # and policy results are cached. Without a warm up, the control_count will be X queries + # but the test phase will only fire X-1 queries, due the fact that the + # result of the policy is already available in the cache. + described_class.new(project, user2).execute.map(&:user) + + control_count = ActiveRecord::QueryRecorder.new do + described_class.new(project, user2).execute.map(&:user) + end + + create_list(:project_member, 3, project: project) + + expect do + described_class.new(project, user2).execute.map(&:user) + end.to issue_same_number_of_queries_as(control_count) + end + context 'with :shared_into_ancestors' do let_it_be(:invited_group) do create(:group).tap do |invited_group| diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb index 118679a4911..dee73625cb8 100644 --- a/spec/finders/milestones_finder_spec.rb +++ b/spec/finders/milestones_finder_spec.rb @@ -101,6 +101,13 @@ RSpec.describe MilestonesFinder do expect(result).to contain_exactly(milestone_1, milestone_2) end + it 'filters by id or title' do + params[:ids] = [milestone_2.id] + params[:title] = [milestone_1.title] + + expect(result).to contain_exactly(milestone_1, milestone_2) + end + it 'filters by active state' do params[:state] = 'active' @@ -182,9 +189,9 @@ RSpec.describe MilestonesFinder do expect(result).to contain_exactly(milestone_2, milestone_3, milestone_4) end - context 'when include_parent_milestones is true' do + context 'when include_ancestors is true' do it 'ignores the iid filter' do - params[:include_parent_milestones] = true + params[:include_ancestors] = true expect(result).to contain_exactly(milestone_1, milestone_2, milestone_3, milestone_4) end diff --git a/spec/finders/organizations/groups_finder_spec.rb b/spec/finders/organizations/groups_finder_spec.rb deleted file mode 100644 index 08c5604149b..00000000000 --- a/spec/finders/organizations/groups_finder_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Organizations::GroupsFinder, feature_category: :cell do - let_it_be(:organization_user) { create(:organization_user) } - let_it_be(:organization) { organization_user.organization } - let_it_be(:user) { organization_user.user } - let_it_be(:public_group) { create(:group, name: 'public-group', organization: organization) } - let_it_be(:other_group) { create(:group, name: 'other-group', organization: organization) } - let_it_be(:outside_organization_group) { create(:group) } - let_it_be(:private_group) do - create(:group, :private, name: 'private-group', organization: organization) - end - - let_it_be(:no_access_group_in_org) do - create(:group, :private, name: 'no-access', organization: organization) - end - - let(:current_user) { user } - let(:params) { {} } - let(:finder) { described_class.new(organization: organization, current_user: current_user, params: params) } - - before_all do - private_group.add_developer(user) - public_group.add_developer(user) - other_group.add_developer(user) - outside_organization_group.add_developer(user) - end - - subject(:result) { finder.execute.to_a } - - describe '#execute' do - context 'when user is not authorized to read the organization' do - let(:current_user) { create(:user) } - - it { is_expected.to be_empty } - end - - context 'when organization is nil' do - let(:organization) { nil } - - it { is_expected.to be_empty } - end - - context 'when user is authorized to read the organization' do - it 'return all accessible groups' do - expect(result).to contain_exactly(public_group, private_group, other_group) - end - - context 'when search param is passed' do - let(:params) { { search: 'the' } } - - it 'filters the groups by search' do - expect(result).to contain_exactly(other_group) - end - end - - context 'when sort param is not passed' do - it 'return groups sorted by name in ascending order by default' do - expect(result).to eq([other_group, private_group, public_group]) - end - end - - context 'when sort param is passed' do - using RSpec::Parameterized::TableSyntax - - where(:field, :direction, :sorted_groups) do - 'name' | 'asc' | lazy { [other_group, private_group, public_group] } - 'name' | 'desc' | lazy { [public_group, private_group, other_group] } - 'path' | 'asc' | lazy { [other_group, private_group, public_group] } - 'path' | 'desc' | lazy { [public_group, private_group, other_group] } - end - - with_them do - let(:params) { { sort: { field: field, direction: direction } } } - it 'sorts the groups' do - expect(result).to eq(sorted_groups) - end - end - end - end - end -end diff --git a/spec/finders/packages/maven/package_finder_spec.rb b/spec/finders/packages/maven/package_finder_spec.rb index f769471fcc7..e5ece42baaa 100644 --- a/spec/finders/packages/maven/package_finder_spec.rb +++ b/spec/finders/packages/maven/package_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Packages::Maven::PackageFinder do +RSpec.describe ::Packages::Maven::PackageFinder, feature_category: :package_registry do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, namespace: group) } @@ -13,10 +13,6 @@ RSpec.describe ::Packages::Maven::PackageFinder do let(:param_order_by_package_file) { false } let(:finder) { described_class.new(user, project_or_group, path: param_path, order_by_package_file: param_order_by_package_file) } - before do - group.add_developer(user) - end - describe '#execute' do subject { finder.execute } @@ -58,6 +54,24 @@ RSpec.describe ::Packages::Maven::PackageFinder do let(:project_or_group) { group } it_behaves_like 'handling valid and invalid paths' + + context 'when the FF maven_remove_permissions_check_from_finder disabled' do + before do + stub_feature_flags(maven_remove_permissions_check_from_finder: false) + end + + it 'returns an empty array' do + is_expected.to be_empty + end + + context 'when an user assigned the developer role' do + before do + group.add_developer(user) + end + + it_behaves_like 'handling valid and invalid paths' + end + end end context 'across all projects' do diff --git a/spec/finders/packages/pypi/packages_finder_spec.rb b/spec/finders/packages/pypi/packages_finder_spec.rb index 26cfaa29a0c..bf0f56c2fb2 100644 --- a/spec/finders/packages/pypi/packages_finder_spec.rb +++ b/spec/finders/packages/pypi/packages_finder_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Packages::Pypi::PackagesFinder do context 'when package registry is disabled for one project' do before do - project2.project_feature.update!(package_registry_access_level: ProjectFeature::DISABLED) + project2.update!(package_registry_access_level: 'disabled', packages_enabled: false) end it 'filters the packages from the disabled project' do diff --git a/spec/finders/projects/ml/model_finder_spec.rb b/spec/finders/projects/ml/model_finder_spec.rb index a2c2836a63d..0395e387c8f 100644 --- a/spec/finders/projects/ml/model_finder_spec.rb +++ b/spec/finders/projects/ml/model_finder_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Projects::Ml::ModelFinder, feature_category: :mlops do let_it_be(:project) { create(:project) } let_it_be(:model1) { create(:ml_models, :with_versions, project: project) } let_it_be(:model2) { create(:ml_models, :with_versions, project: project) } - let_it_be(:model3) { create(:ml_models, name: "#{model1.name}_1", project: project) } + let_it_be(:model3) { create(:ml_models, name: "#{model1.name}_1", project: project, updated_at: 1.week.ago) } let_it_be(:other_model) { create(:ml_models) } let_it_be(:project_models) { [model1, model2, model3] } @@ -52,6 +52,7 @@ RSpec.describe Projects::Ml::ModelFinder, feature_category: :mlops do 'by column' | 'name' | 'ASC' | [0, 2, 1] 'invalid sort' | nil | 'UP' | [2, 1, 0] 'invalid order by' | 'INVALID' | nil | [2, 1, 0] + 'order by updated_at' | 'updated_at' | nil | [1, 0, 2] end with_them do let(:params) { { order_by: order_by, sort: direction } } diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index f7afd96fa09..e570b49e1da 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -482,6 +482,19 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do it { is_expected.to match_array([internal_project]) } end + describe 'filter by organization_id' do + let_it_be(:organization) { create(:organization) } + let_it_be(:organization_project) { create(:project, organization: organization) } + + let(:params) { { organization_id: organization.id } } + + before do + organization_project.add_maintainer(current_user) + end + + it { is_expected.to match_array([organization_project]) } + end + describe 'when with_issues_enabled is true' do let(:params) { { with_issues_enabled: true } } diff --git a/spec/finders/releases_finder_spec.rb b/spec/finders/releases_finder_spec.rb index bee0ae0d5c1..2603a205c42 100644 --- a/spec/finders/releases_finder_spec.rb +++ b/spec/finders/releases_finder_spec.rb @@ -26,6 +26,18 @@ RSpec.describe ReleasesFinder, feature_category: :release_orchestration do end end + shared_examples_for 'when a release is tagless' do + # There shouldn't be tags in this state, but because some exist in production and cause page loading errors, this + # test exists. We can test empty string but not the nil value since there is a not null constraint at the database + # level. + it 'does not return the tagless release' do + empty_string_tag = create(:release, project: project, tag: 'v99.0.0') + empty_string_tag.update_column(:tag, '') + + expect(subject).not_to include(empty_string_tag) + end + end + shared_examples_for 'preload' do it 'preloads associations' do expect(Release).to receive(:preloaded).once.and_call_original @@ -89,6 +101,7 @@ RSpec.describe ReleasesFinder, feature_category: :release_orchestration do it_behaves_like 'preload' it_behaves_like 'when a tag parameter is passed' + it_behaves_like 'when a release is tagless' end end @@ -132,6 +145,7 @@ RSpec.describe ReleasesFinder, feature_category: :release_orchestration do it_behaves_like 'preload' it_behaves_like 'when a tag parameter is passed' + it_behaves_like 'when a release is tagless' context 'with sorting parameters' do it 'sorted by released_at in descending order by default' do @@ -223,6 +237,7 @@ RSpec.describe ReleasesFinder, feature_category: :release_orchestration do end it_behaves_like 'preload' + it_behaves_like 'when a release is tagless' end end end diff --git a/spec/finders/repositories/tree_finder_spec.rb b/spec/finders/repositories/tree_finder_spec.rb index 42b4047c4e8..7c81572d13c 100644 --- a/spec/finders/repositories/tree_finder_spec.rb +++ b/spec/finders/repositories/tree_finder_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Repositories::TreeFinder do +RSpec.describe Repositories::TreeFinder, feature_category: :source_code_management do include RepoHelpers let_it_be(:user) { create(:user) } @@ -61,6 +61,60 @@ RSpec.describe Repositories::TreeFinder do end end + describe '#next_cursor' do + subject { tree_finder.next_cursor } + + it 'always nil before #execute call' do + is_expected.to be_nil + end + + context 'after #execute' do + context 'with gitaly pagination' do + before do + tree_finder.execute(gitaly_pagination: true) + end + + context 'without pagination params' do + it { is_expected.to be_present } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_present } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to eq('') } + end + end + end + + context 'without gitaly pagination' do + before do + tree_finder.execute(gitaly_pagination: false) + end + + context 'without pagination params' do + it { is_expected.to be_nil } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_nil } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to be_nil } + end + end + end + end + end + describe "#total", :use_clean_rails_memory_store_caching do subject { tree_finder.total } diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb index 525c19ba137..378acc67a50 100644 --- a/spec/finders/tags_finder_spec.rb +++ b/spec/finders/tags_finder_spec.rb @@ -2,11 +2,15 @@ require 'spec_helper' -RSpec.describe TagsFinder do +RSpec.describe TagsFinder, feature_category: :source_code_management do + subject(:tags_finder) { described_class.new(repository, params) } + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository) } let_it_be(:repository) { project.repository } + let(:params) { {} } + def load_tags(params, gitaly_pagination: false) described_class.new(repository, params).execute(gitaly_pagination: gitaly_pagination) end @@ -210,4 +214,58 @@ RSpec.describe TagsFinder do end end end + + describe '#next_cursor' do + subject { tags_finder.next_cursor } + + it 'always nil before #execute call' do + is_expected.to be_nil + end + + context 'after #execute' do + context 'with gitaly pagination' do + before do + tags_finder.execute(gitaly_pagination: true) + end + + context 'without pagination params' do + it { is_expected.to be_nil } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_present } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to be_present } + end + end + end + + context 'without gitaly pagination' do + before do + tags_finder.execute(gitaly_pagination: false) + end + + context 'without pagination params' do + it { is_expected.to be_nil } + end + + context 'with pagination params' do + let(:params) { { per_page: 5 } } + + it { is_expected.to be_nil } + + context 'when all objects can be returned on the same page' do + let(:params) { { per_page: 100 } } + + it { is_expected.to be_nil } + end + end + end + end + end end diff --git a/spec/finders/timelogs/timelogs_finder_spec.rb b/spec/finders/timelogs/timelogs_finder_spec.rb new file mode 100644 index 00000000000..35691a46e23 --- /dev/null +++ b/spec/finders/timelogs/timelogs_finder_spec.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Timelogs::TimelogsFinder, feature_category: :team_planning do + let_it_be(:current_user) { create(:user) } + let_it_be(:group_a) { create(:group) } + let_it_be(:group_b) { create(:group) } + let_it_be(:project_a) { create(:project, :empty_repo, :public, group: group_a) } + let_it_be(:project_b) { create(:project, :empty_repo, :public, group: group_a) } + let_it_be(:project_c) { create(:project, :empty_repo, :public, group: group_b) } + + let_it_be(:issue_a) { create(:issue, project: project_a) } + let_it_be(:issue_b) { create(:issue, project: project_b) } + let_it_be(:issue_c) { create(:issue, project: project_c) } + let_it_be(:merge_request) { create(:merge_request, source_project: project_a) } + + let_it_be(:timelog1) do + create(:issue_timelog, issue: issue_a, user: current_user, spent_at: 2.days.ago.beginning_of_day, time_spent: 3000) + end + + let_it_be(:timelog2) do + create(:issue_timelog, issue: issue_a, user: create(:user), spent_at: 2.days.ago.end_of_day, time_spent: 4000) + end + + let_it_be(:timelog3) do + create(:merge_request_timelog, + merge_request: merge_request, + user: current_user, + spent_at: 10.days.ago, + time_spent: 2000) + end + + let_it_be(:timelog4) do + create(:issue_timelog, issue: issue_b, user: current_user, spent_at: 1.hour.ago, time_spent: 500) + end + + let_it_be(:timelog5) do + create(:issue_timelog, issue: issue_c, user: create(:user), spent_at: 7.days.ago.end_of_day, time_spent: 6000) + end + + subject(:finder_results) { described_class.new(issuable, params).execute } + + describe '#execute' do + let(:issuable) { nil } + let(:params) { {} } + + context 'when params is empty' do + it 'returns all timelogs' do + expect(finder_results).to contain_exactly(timelog1, timelog2, timelog3, timelog4, timelog5) + end + end + + context 'when an issuable is provided' do + let(:issuable) { issue_a } + + it 'returns the issuable timelogs' do + expect(finder_results).to contain_exactly(timelog1, timelog2) + end + end + + context 'when a username is provided' do + let(:params) { { username: current_user.username } } + + it 'returns all timelogs created by the user' do + expect(finder_results).to contain_exactly(timelog1, timelog3, timelog4) + end + end + + context 'when a group is provided' do + let(:params) { { group_id: group_a.id } } + + it 'returns all timelogs of issuables inside that group' do + expect(finder_results).to contain_exactly(timelog1, timelog2, timelog3, timelog4) + end + + context 'when the group does not exist' do + let(:params) { { group_id: non_existing_record_id } } + + it 'raises an exception' do + expect { finder_results }.to raise_error( + ActiveRecord::RecordNotFound, /Group with id '\d+' could not be found/) + end + end + end + + context 'when a project is provided' do + let(:params) { { project_id: project_a.id } } + + it 'returns all timelogs of issuables inside that project' do + expect(finder_results).to contain_exactly(timelog1, timelog2, timelog3) + end + + context 'when the project does not exist' do + let(:params) { { project_id: non_existing_record_id } } + + it 'returns an empty list and does not raise an exception' do + expect(finder_results).to be_empty + expect { finder_results }.not_to raise_error + end + end + end + + context 'when a start datetime is provided' do + let(:params) { { start_time: 3.days.ago.beginning_of_day } } + + it 'returns all timelogs created after that date' do + expect(finder_results).to contain_exactly(timelog1, timelog2, timelog4) + end + end + + context 'when an end datetime is provided' do + let(:params) { { end_time: 3.days.ago.beginning_of_day } } + + it 'returns all timelogs created before that date' do + expect(finder_results).to contain_exactly(timelog3, timelog5) + end + end + + context 'when both a start and an end datetime are provided' do + let(:params) { { start_time: 2.days.ago.beginning_of_day, end_time: 1.day.ago.beginning_of_day } } + + it 'returns all timelogs created between those dates' do + expect(finder_results).to contain_exactly(timelog1, timelog2) + end + + context 'when start time is after end time' do + let(:params) { { start_time: 1.day.ago.beginning_of_day, end_time: 2.days.ago.beginning_of_day } } + + it 'raises an exception' do + expect { finder_results }.to raise_error(ArgumentError, /Start argument must be before End argument/) + end + end + end + + context 'when sort is provided' do + let(:params) { { sort: sort_value } } + + context 'when sorting by spent_at desc' do + let(:sort_value) { :spent_at_desc } + + it 'returns timelogs sorted accordingly' do + expect(finder_results).to eq([timelog4, timelog2, timelog1, timelog5, timelog3]) + end + end + + context 'when sorting by spent_at asc' do + let(:sort_value) { :spent_at_asc } + + it 'returns timelogs sorted accordingly' do + expect(finder_results).to eq([timelog3, timelog5, timelog1, timelog2, timelog4]) + end + end + + context 'when sorting by time_spent desc' do + let(:sort_value) { :time_spent_desc } + + it 'returns timelogs sorted accordingly' do + expect(finder_results).to eq([timelog5, timelog2, timelog1, timelog3, timelog4]) + end + end + + context 'when sorting by time_spent asc' do + let(:sort_value) { :time_spent_asc } + + it 'returns timelogs sorted accordingly' do + expect(finder_results).to eq([timelog4, timelog3, timelog1, timelog2, timelog5]) + end + end + end + end +end |