diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
commit | 0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch) | |
tree | 4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/finders | |
parent | 744144d28e3e7fddc117924fef88de5d9674fe4c (diff) | |
download | gitlab-ce-0653e08efd039a5905f3fa4f6e9cef9f5d2f799c.tar.gz |
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/finders')
-rw-r--r-- | spec/finders/branches_finder_spec.rb | 7 | ||||
-rw-r--r-- | spec/finders/ci/pipelines_finder_spec.rb | 37 | ||||
-rw-r--r-- | spec/finders/ci/runners_finder_spec.rb | 126 | ||||
-rw-r--r-- | spec/finders/error_tracking/errors_finder_spec.rb | 18 | ||||
-rw-r--r-- | spec/finders/feature_flags_finder_spec.rb | 8 | ||||
-rw-r--r-- | spec/finders/groups/user_groups_finder_spec.rb | 106 | ||||
-rw-r--r-- | spec/finders/issues_finder/params_spec.rb | 49 | ||||
-rw-r--r-- | spec/finders/issues_finder_spec.rb | 429 | ||||
-rw-r--r-- | spec/finders/merge_requests_finder_spec.rb | 96 | ||||
-rw-r--r-- | spec/finders/packages/helm/package_files_finder_spec.rb | 35 | ||||
-rw-r--r-- | spec/finders/packages/helm/packages_finder_spec.rb | 74 | ||||
-rw-r--r-- | spec/finders/packages/npm/package_finder_spec.rb | 61 | ||||
-rw-r--r-- | spec/finders/projects_finder_spec.rb | 28 | ||||
-rw-r--r-- | spec/finders/repositories/tree_finder_spec.rb | 95 |
14 files changed, 773 insertions, 396 deletions
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index a62dd3842db..f9d525c33a4 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -259,4 +259,11 @@ RSpec.describe BranchesFinder do end end end + + describe '#total' do + subject { branch_finder.total } + + it { is_expected.to be_an(Integer) } + it { is_expected.to eq(repository.branch_count) } + end end diff --git a/spec/finders/ci/pipelines_finder_spec.rb b/spec/finders/ci/pipelines_finder_spec.rb index c7bd52576e8..908210e0296 100644 --- a/spec/finders/ci/pipelines_finder_spec.rb +++ b/spec/finders/ci/pipelines_finder_spec.rb @@ -113,27 +113,6 @@ RSpec.describe Ci::PipelinesFinder do end end - context 'when name is specified' do - let(:user) { create(:user) } - let!(:pipeline) { create(:ci_pipeline, project: project, user: user) } - - context 'when name exists' do - let(:params) { { name: user.name } } - - it 'returns matched pipelines' do - is_expected.to eq([pipeline]) - end - end - - context 'when name does not exist' do - let(:params) { { name: 'invalid-name' } } - - it 'returns empty' do - is_expected.to be_empty - end - end - end - context 'when username is specified' do let(:user) { create(:user) } let!(:pipeline) { create(:ci_pipeline, project: project, user: user) } @@ -258,20 +237,8 @@ RSpec.describe Ci::PipelinesFinder do let!(:push_pipeline) { create(:ci_pipeline, project: project, source: 'push') } let!(:api_pipeline) { create(:ci_pipeline, project: project, source: 'api') } - context 'when `pipeline_source_filter` feature flag is disabled' do - before do - stub_feature_flags(pipeline_source_filter: false) - end - - it 'returns all the pipelines' do - is_expected.to contain_exactly(web_pipeline, push_pipeline, api_pipeline) - end - end - - context 'when `pipeline_source_filter` feature flag is enabled' do - it 'returns only the matched pipeline' do - is_expected.to eq([web_pipeline]) - end + it 'returns only the matched pipeline' do + is_expected.to eq([web_pipeline]) end end diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb index 599b4ffb804..10d3f641e02 100644 --- a/spec/finders/ci/runners_finder_spec.rb +++ b/spec/finders/ci/runners_finder_spec.rb @@ -18,6 +18,13 @@ RSpec.describe Ci::RunnersFinder do end end + context 'with nil group' do + it 'returns all runners' do + expect(Ci::Runner).to receive(:with_tags).and_call_original + expect(described_class.new(current_user: admin, params: { group: nil }).execute).to match_array [runner1, runner2] + end + end + context 'with preload param set to :tag_name true' do it 'requests tags' do expect(Ci::Runner).to receive(:with_tags).and_call_original @@ -158,6 +165,7 @@ RSpec.describe Ci::RunnersFinder do let_it_be(:project_4) { create(:project, group: sub_group_2) } let_it_be(:project_5) { create(:project, group: sub_group_3) } let_it_be(:project_6) { create(:project, group: sub_group_4) } + let_it_be(:runner_instance) { create(:ci_runner, :instance, contacted_at: 13.minutes.ago) } let_it_be(:runner_group) { create(:ci_runner, :group, contacted_at: 12.minutes.ago) } let_it_be(:runner_sub_group_1) { create(:ci_runner, :group, active: false, contacted_at: 11.minutes.ago) } let_it_be(:runner_sub_group_2) { create(:ci_runner, :group, contacted_at: 10.minutes.ago) } @@ -171,7 +179,10 @@ RSpec.describe Ci::RunnersFinder do let_it_be(:runner_project_6) { create(:ci_runner, :project, contacted_at: 2.minutes.ago, projects: [project_5])} let_it_be(:runner_project_7) { create(:ci_runner, :project, contacted_at: 1.minute.ago, projects: [project_6])} - let(:params) { {} } + let(:target_group) { nil } + let(:membership) { nil } + let(:extra_params) { {} } + let(:params) { { group: target_group, membership: membership }.merge(extra_params).reject { |_, v| v.nil? } } before do group.runners << runner_group @@ -182,65 +193,104 @@ RSpec.describe Ci::RunnersFinder do end describe '#execute' do - subject { described_class.new(current_user: user, group: group, params: params).execute } + subject { described_class.new(current_user: user, params: params).execute } + + shared_examples 'membership equal to :descendants' do + it 'returns all descendant runners' do + expect(subject).to eq([runner_project_7, runner_project_6, runner_project_5, + runner_project_4, runner_project_3, runner_project_2, + runner_project_1, runner_sub_group_4, runner_sub_group_3, + runner_sub_group_2, runner_sub_group_1, runner_group]) + end + end context 'with user as group owner' do before do group.add_owner(user) end - context 'passing no params' do - it 'returns all descendant runners' do - expect(subject).to eq([runner_project_7, runner_project_6, runner_project_5, - runner_project_4, runner_project_3, runner_project_2, - runner_project_1, runner_sub_group_4, runner_sub_group_3, - runner_sub_group_2, runner_sub_group_1, runner_group]) + context 'with :group as target group' do + let(:target_group) { group } + + context 'passing no params' do + it_behaves_like 'membership equal to :descendants' end - end - context 'with sort param' do - let(:params) { { sort: 'contacted_asc' } } + context 'with :descendants membership' do + let(:membership) { :descendants } - it 'sorts by specified attribute' do - expect(subject).to eq([runner_group, runner_sub_group_1, runner_sub_group_2, - runner_sub_group_3, runner_sub_group_4, runner_project_1, - runner_project_2, runner_project_3, runner_project_4, - runner_project_5, runner_project_6, runner_project_7]) + it_behaves_like 'membership equal to :descendants' end - end - context 'filtering' do - context 'by search term' do - let(:params) { { search: 'runner_project_search' } } + context 'with :direct membership' do + let(:membership) { :direct } + + it 'returns runners belonging to group' do + expect(subject).to eq([runner_group]) + end + end + + context 'with unknown membership' do + let(:membership) { :unsupported } - it 'returns correct runner' do - expect(subject).to eq([runner_project_3]) + it 'raises an error' do + expect { subject }.to raise_error(ArgumentError, 'Invalid membership filter') end end - context 'by status' do - let(:params) { { status_status: 'paused' } } + context 'with nil group' do + let(:target_group) { nil } - it 'returns correct runner' do - expect(subject).to eq([runner_sub_group_1]) + it 'returns no runners' do + # Query should run against all runners, however since user is not admin, query returns no results + expect(subject).to eq([]) end end - context 'by tag_name' do - let(:params) { { tag_name: %w[runner_tag] } } + context 'with sort param' do + let(:extra_params) { { sort: 'contacted_asc' } } - it 'returns correct runner' do - expect(subject).to eq([runner_project_5]) + it 'sorts by specified attribute' do + expect(subject).to eq([runner_group, runner_sub_group_1, runner_sub_group_2, + runner_sub_group_3, runner_sub_group_4, runner_project_1, + runner_project_2, runner_project_3, runner_project_4, + runner_project_5, runner_project_6, runner_project_7]) end end - context 'by runner type' do - let(:params) { { type_type: 'project_type' } } + context 'filtering' do + context 'by search term' do + let(:extra_params) { { search: 'runner_project_search' } } + + it 'returns correct runner' do + expect(subject).to eq([runner_project_3]) + end + end + + context 'by status' do + let(:extra_params) { { status_status: 'paused' } } + + it 'returns correct runner' do + expect(subject).to eq([runner_sub_group_1]) + end + end + + context 'by tag_name' do + let(:extra_params) { { tag_name: %w[runner_tag] } } + + it 'returns correct runner' do + expect(subject).to eq([runner_project_5]) + end + end + + context 'by runner type' do + let(:extra_params) { { type_type: 'project_type' } } - it 'returns correct runners' do - expect(subject).to eq([runner_project_7, runner_project_6, - runner_project_5, runner_project_4, - runner_project_3, runner_project_2, runner_project_1]) + it 'returns correct runners' do + expect(subject).to eq([runner_project_7, runner_project_6, + runner_project_5, runner_project_4, + runner_project_3, runner_project_2, runner_project_1]) + end end end end @@ -278,7 +328,7 @@ RSpec.describe Ci::RunnersFinder do end describe '#sort_key' do - subject { described_class.new(current_user: user, group: group, params: params).sort_key } + subject { described_class.new(current_user: user, params: params.merge(group: group)).sort_key } context 'without params' do it 'returns created_at_desc' do @@ -287,7 +337,7 @@ RSpec.describe Ci::RunnersFinder do end context 'with params' do - let(:params) { { sort: 'contacted_asc' } } + let(:extra_params) { { sort: 'contacted_asc' } } it 'returns contacted_asc' do expect(subject).to eq('contacted_asc') diff --git a/spec/finders/error_tracking/errors_finder_spec.rb b/spec/finders/error_tracking/errors_finder_spec.rb index 2df5f1653e0..29053054f9d 100644 --- a/spec/finders/error_tracking/errors_finder_spec.rb +++ b/spec/finders/error_tracking/errors_finder_spec.rb @@ -6,7 +6,8 @@ RSpec.describe ErrorTracking::ErrorsFinder do let_it_be(:project) { create(:project) } let_it_be(:user) { project.creator } let_it_be(:error) { create(:error_tracking_error, project: project) } - let_it_be(:error_resolved) { create(:error_tracking_error, :resolved, project: project) } + let_it_be(:error_resolved) { create(:error_tracking_error, :resolved, project: project, first_seen_at: 2.hours.ago) } + let_it_be(:error_yesterday) { create(:error_tracking_error, project: project, first_seen_at: Time.zone.now.yesterday) } before do project.add_maintainer(user) @@ -17,12 +18,25 @@ RSpec.describe ErrorTracking::ErrorsFinder do subject { described_class.new(user, project, params).execute } - it { is_expected.to contain_exactly(error, error_resolved) } + it { is_expected.to contain_exactly(error, error_resolved, error_yesterday) } context 'with status parameter' do let(:params) { { status: 'resolved' } } it { is_expected.to contain_exactly(error_resolved) } end + + context 'with sort parameter' do + let(:params) { { status: 'unresolved', sort: 'first_seen' } } + + it { is_expected.to eq([error, error_yesterday]) } + end + + context 'with limit parameter' do + let(:params) { { limit: '1', sort: 'first_seen' } } + + # Sort by first_seen is DESC by default, so the most recent error is `error` + it { is_expected.to contain_exactly(error) } + end end end diff --git a/spec/finders/feature_flags_finder_spec.rb b/spec/finders/feature_flags_finder_spec.rb index 4faa6a62a1f..1b3c71b143f 100644 --- a/spec/finders/feature_flags_finder_spec.rb +++ b/spec/finders/feature_flags_finder_spec.rb @@ -72,13 +72,5 @@ RSpec.describe FeatureFlagsFinder do subject end end - - context 'with a legacy flag' do - let!(:feature_flag_3) { create(:operations_feature_flag, :legacy_flag, name: 'flag-c', project: project) } - - it 'returns new flags' do - is_expected.to eq([feature_flag_1, feature_flag_2]) - end - end end end diff --git a/spec/finders/groups/user_groups_finder_spec.rb b/spec/finders/groups/user_groups_finder_spec.rb new file mode 100644 index 00000000000..4cce3ab72eb --- /dev/null +++ b/spec/finders/groups/user_groups_finder_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Groups::UserGroupsFinder do + describe '#execute' do + let_it_be(:user) { create(:user) } + let_it_be(:guest_group) { create(:group, name: 'public guest', path: 'public-guest') } + let_it_be(:private_maintainer_group) { create(:group, :private, name: 'b private maintainer', path: 'b-private-maintainer') } + let_it_be(:public_developer_group) { create(:group, project_creation_level: nil, name: 'c public developer', path: 'c-public-developer') } + let_it_be(:public_maintainer_group) { create(:group, name: 'a public maintainer', path: 'a-public-maintainer') } + + subject { described_class.new(current_user, target_user, arguments).execute } + + let(:arguments) { {} } + let(:current_user) { user } + let(:target_user) { user } + + before_all do + guest_group.add_guest(user) + private_maintainer_group.add_maintainer(user) + public_developer_group.add_developer(user) + public_maintainer_group.add_maintainer(user) + end + + it 'returns all groups where the user is a direct member' do + is_expected.to match( + [ + public_maintainer_group, + private_maintainer_group, + public_developer_group, + guest_group + ] + ) + end + + context 'when target_user is nil' do + let(:target_user) { nil } + + it { is_expected.to be_empty } + end + + context 'when current_user is nil' do + let(:current_user) { nil } + + it { is_expected.to be_empty } + end + + context 'when permission is :create_projects' do + let(:arguments) { { permission_scope: :create_projects } } + + specify do + is_expected.to match( + [ + public_maintainer_group, + private_maintainer_group, + public_developer_group + ] + ) + end + + context 'when paginatable_namespace_drop_down_for_project_creation feature flag is disabled' do + before do + stub_feature_flags(paginatable_namespace_drop_down_for_project_creation: false) + end + + it 'ignores project creation scope and returns all groups where the user is a direct member' do + is_expected.to match( + [ + public_maintainer_group, + private_maintainer_group, + public_developer_group, + guest_group + ] + ) + end + end + + context 'when search is provided' do + let(:arguments) { { permission_scope: :create_projects, search: 'maintainer' } } + + specify do + is_expected.to match( + [ + public_maintainer_group, + private_maintainer_group + ] + ) + end + end + end + + context 'when search is provided' do + let(:arguments) { { search: 'maintainer' } } + + specify do + is_expected.to match( + [ + public_maintainer_group, + private_maintainer_group + ] + ) + end + end + end +end diff --git a/spec/finders/issues_finder/params_spec.rb b/spec/finders/issues_finder/params_spec.rb new file mode 100644 index 00000000000..879ecc364a2 --- /dev/null +++ b/spec/finders/issues_finder/params_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IssuesFinder::Params do + describe '#include_hidden' do + subject { described_class.new(params, user, IssuesFinder) } + + context 'when param is not set' do + let(:params) { {} } + + context 'with an admin', :enable_admin_mode do + let(:user) { create(:user, :admin) } + + it 'returns true' do + expect(subject.include_hidden?).to be_truthy + end + end + + context 'with a regular user' do + let(:user) { create(:user) } + + it 'returns false' do + expect(subject.include_hidden?).to be_falsey + end + end + end + + context 'when param is set' do + let(:params) { { include_hidden: true } } + + context 'with an admin', :enable_admin_mode do + let(:user) { create(:user, :admin) } + + it 'returns true' do + expect(subject.include_hidden?).to be_truthy + end + end + + context 'with a regular user' do + let(:user) { create(:user) } + + it 'returns false' do + expect(subject.include_hidden?).to be_falsey + end + end + end + end +end diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 0cb73f3da6d..ed35d75720c 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -12,8 +12,52 @@ RSpec.describe IssuesFinder do context 'scope: all' do let(:scope) { 'all' } - it 'returns all issues' do - expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5) + context 'include_hidden and public_only params' do + let_it_be(:banned_user) { create(:user, :banned) } + let_it_be(:hidden_issue) { create(:issue, project: project1, author: banned_user) } + let_it_be(:confidential_issue) { create(:issue, project: project1, confidential: true) } + + context 'when user is an admin', :enable_admin_mode do + let(:user) { create(:user, :admin) } + + it 'returns all issues' do + expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5, hidden_issue, confidential_issue) + end + end + + context 'when user is not an admin' do + context 'when public_only is true' do + let(:params) { { public_only: true } } + + it 'returns public issues' do + expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5) + end + end + + context 'when public_only is false' do + let(:params) { { public_only: false } } + + it 'returns public and confidential issues' do + expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5, confidential_issue) + end + end + + context 'when public_only is not set' do + it 'returns public and confidential issue' do + expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5, confidential_issue) + end + end + + context 'when ban_user_feature_flag is false' do + before do + stub_feature_flags(ban_user_feature_flag: false) + end + + it 'returns all issues' do + expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue5, hidden_issue, confidential_issue) + end + end + end end context 'user does not have read permissions' do @@ -426,139 +470,121 @@ RSpec.describe IssuesFinder do end end - shared_examples ':label_name parameter' do - context 'filtering by label' do - let(:params) { { label_name: label.title } } + context 'filtering by label' do + let(:params) { { label_name: label.title } } - it 'returns issues with that label' do - expect(issues).to contain_exactly(issue2) - end + it 'returns issues with that label' do + expect(issues).to contain_exactly(issue2) + end - context 'using NOT' do - let(:params) { { not: { label_name: label.title } } } + context 'using NOT' do + let(:params) { { not: { label_name: label.title } } } - it 'returns issues that do not have that label' do - expect(issues).to contain_exactly(issue1, issue3, issue4, issue5) - end + it 'returns issues that do not have that label' do + expect(issues).to contain_exactly(issue1, issue3, issue4, issue5) + end - # IssuableFinder first filters using the outer params (the ones not inside the `not` key.) - # Afterwards, it applies the `not` params to that resultset. This means that things inside the `not` param - # do not take precedence over the outer params with the same name. - context 'shadowing the same outside param' do - let(:params) { { label_name: label2.title, not: { label_name: label.title } } } + # IssuableFinder first filters using the outer params (the ones not inside the `not` key.) + # Afterwards, it applies the `not` params to that resultset. This means that things inside the `not` param + # do not take precedence over the outer params with the same name. + context 'shadowing the same outside param' do + let(:params) { { label_name: label2.title, not: { label_name: label.title } } } - it 'does not take precedence over labels outside NOT' do - expect(issues).to contain_exactly(issue3) - end + it 'does not take precedence over labels outside NOT' do + expect(issues).to contain_exactly(issue3) end + end - context 'further filtering outside params' do - let(:params) { { label_name: label2.title, not: { assignee_username: user2.username } } } + context 'further filtering outside params' do + let(:params) { { label_name: label2.title, not: { assignee_username: user2.username } } } - it 'further filters on the returned resultset' do - expect(issues).to be_empty - end + it 'further filters on the returned resultset' do + expect(issues).to be_empty end end end + end - context 'filtering by multiple labels' do - let(:params) { { label_name: [label.title, label2.title].join(',') } } - let(:label2) { create(:label, project: project2) } - - before do - create(:label_link, label: label2, target: issue2) - end + context 'filtering by multiple labels' do + let(:params) { { label_name: [label.title, label2.title].join(',') } } + let(:label2) { create(:label, project: project2) } - it 'returns the unique issues with all those labels' do - expect(issues).to contain_exactly(issue2) - end - - context 'using NOT' do - let(:params) { { not: { label_name: [label.title, label2.title].join(',') } } } + before do + create(:label_link, label: label2, target: issue2) + end - it 'returns issues that do not have any of the labels provided' do - expect(issues).to contain_exactly(issue1, issue4, issue5) - end - end + it 'returns the unique issues with all those labels' do + expect(issues).to contain_exactly(issue2) end - context 'filtering by a label that includes any or none in the title' do - let(:params) { { label_name: [label.title, label2.title].join(',') } } - let(:label) { create(:label, title: 'any foo', project: project2) } - let(:label2) { create(:label, title: 'bar none', project: project2) } + context 'using NOT' do + let(:params) { { not: { label_name: [label.title, label2.title].join(',') } } } - before do - create(:label_link, label: label2, target: issue2) + it 'returns issues that do not have any of the labels provided' do + expect(issues).to contain_exactly(issue1, issue4, issue5) end + end + end - it 'returns the unique issues with all those labels' do - expect(issues).to contain_exactly(issue2) - end + context 'filtering by a label that includes any or none in the title' do + let(:params) { { label_name: [label.title, label2.title].join(',') } } + let(:label) { create(:label, title: 'any foo', project: project2) } + let(:label2) { create(:label, title: 'bar none', project: project2) } - context 'using NOT' do - let(:params) { { not: { label_name: [label.title, label2.title].join(',') } } } + before do + create(:label_link, label: label2, target: issue2) + end - it 'returns issues that do not have ANY ONE of the labels provided' do - expect(issues).to contain_exactly(issue1, issue4, issue5) - end - end + it 'returns the unique issues with all those labels' do + expect(issues).to contain_exactly(issue2) end - context 'filtering by no label' do - let(:params) { { label_name: described_class::Params::FILTER_NONE } } + context 'using NOT' do + let(:params) { { not: { label_name: [label.title, label2.title].join(',') } } } - it 'returns issues with no labels' do + it 'returns issues that do not have ANY ONE of the labels provided' do expect(issues).to contain_exactly(issue1, issue4, issue5) end end + end - context 'filtering by any label' do - let(:params) { { label_name: described_class::Params::FILTER_ANY } } - - it 'returns issues that have one or more label' do - create_list(:label_link, 2, label: create(:label, project: project2), target: issue3) + context 'filtering by no label' do + let(:params) { { label_name: described_class::Params::FILTER_NONE } } - expect(issues).to contain_exactly(issue2, issue3) - end + it 'returns issues with no labels' do + expect(issues).to contain_exactly(issue1, issue4, issue5) end + end - context 'when the same label exists on project and group levels' do - let(:issue1) { create(:issue, project: project1) } - let(:issue2) { create(:issue, project: project1) } - - # Skipping validation to reproduce a "real-word" scenario. - # We still have legacy labels on PRD that have the same title on the group and project levels, example: `bug` - let(:project_label) { build(:label, title: 'somelabel', project: project1).tap { |r| r.save!(validate: false) } } - let(:group_label) { create(:group_label, title: 'somelabel', group: project1.group) } - - let(:params) { { label_name: 'somelabel' } } + context 'filtering by any label' do + let(:params) { { label_name: described_class::Params::FILTER_ANY } } - before do - create(:label_link, label: group_label, target: issue1) - create(:label_link, label: project_label, target: issue2) - end + it 'returns issues that have one or more label' do + create_list(:label_link, 2, label: create(:label, project: project2), target: issue3) - it 'finds both issue records' do - expect(issues).to contain_exactly(issue1, issue2) - end + expect(issues).to contain_exactly(issue2, issue3) end end - context 'when `optimized_issuable_label_filter` feature flag is off' do - before do - stub_feature_flags(optimized_issuable_label_filter: false) - end + context 'when the same label exists on project and group levels' do + let(:issue1) { create(:issue, project: project1) } + let(:issue2) { create(:issue, project: project1) } - it_behaves_like ':label_name parameter' - end + # Skipping validation to reproduce a "real-word" scenario. + # We still have legacy labels on PRD that have the same title on the group and project levels, example: `bug` + let(:project_label) { build(:label, title: 'somelabel', project: project1).tap { |r| r.save!(validate: false) } } + let(:group_label) { create(:group_label, title: 'somelabel', group: project1.group) } + + let(:params) { { label_name: 'somelabel' } } - context 'when `optimized_issuable_label_filter` feature flag is on' do before do - stub_feature_flags(optimized_issuable_label_filter: true) + create(:label_link, label: group_label, target: issue1) + create(:label_link, label: project_label, target: issue2) end - it_behaves_like ':label_name parameter' + it 'finds both issue records' do + expect(issues).to contain_exactly(issue1, issue2) + end end context 'filtering by issue term' do @@ -567,6 +593,35 @@ RSpec.describe IssuesFinder do it 'returns issues with title and description match for search term' do expect(issues).to contain_exactly(issue1, issue2) end + + context 'with anonymous user' do + let_it_be(:public_project) { create(:project, :public, group: subgroup) } + let_it_be(:issue6) { create(:issue, project: public_project, title: 'tanuki') } + let_it_be(:issue7) { create(:issue, project: public_project, title: 'ikunat') } + + let(:search_user) { nil } + let(:params) { { search: 'tanuki' } } + + context 'with disable_anonymous_search feature flag enabled' do + before do + stub_feature_flags(disable_anonymous_search: true) + end + + it 'does not perform search' do + expect(issues).to contain_exactly(issue6, issue7) + end + end + + context 'with disable_anonymous_search feature flag disabled' do + before do + stub_feature_flags(disable_anonymous_search: false) + end + + it 'finds one public issue' do + expect(issues).to contain_exactly(issue6) + end + end + end end context 'filtering by issue term in title' do @@ -1001,132 +1056,64 @@ RSpec.describe IssuesFinder do end describe '#with_confidentiality_access_check' do - let(:guest) { create(:user) } + let(:user) { create(:user) } let_it_be(:authorized_user) { create(:user) } - let_it_be(:banned_user) { create(:user, :banned) } let_it_be(:project) { create(:project, namespace: authorized_user.namespace) } let_it_be(:public_issue) { create(:issue, project: project) } let_it_be(:confidential_issue) { create(:issue, project: project, confidential: true) } - let_it_be(:hidden_issue) { create(:issue, project: project, author: banned_user) } - shared_examples 'returns public, does not return hidden or confidential' do + shared_examples 'returns public, does not return confidential' do it 'returns only public issues' do expect(subject).to include(public_issue) - expect(subject).not_to include(confidential_issue, hidden_issue) + expect(subject).not_to include(confidential_issue) end end - shared_examples 'returns public and confidential, does not return hidden' do - it 'returns only public and confidential issues' do + shared_examples 'returns public and confidential' do + it 'returns public and confidential issues' do expect(subject).to include(public_issue, confidential_issue) - expect(subject).not_to include(hidden_issue) - end - end - - shared_examples 'returns public and hidden, does not return confidential' do - it 'returns only public and hidden issues' do - expect(subject).to include(public_issue, hidden_issue) - expect(subject).not_to include(confidential_issue) end end - shared_examples 'returns public, confidential, and hidden' do - it 'returns all issues' do - expect(subject).to include(public_issue, confidential_issue, hidden_issue) - end - end + subject { described_class.new(user, params).with_confidentiality_access_check } context 'when no project filter is given' do let(:params) { {} } context 'for an anonymous user' do - subject { described_class.new(nil, params).with_confidentiality_access_check } - - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' end context 'for a user without project membership' do - subject { described_class.new(user, params).with_confidentiality_access_check } - - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' end context 'for a guest user' do - subject { described_class.new(guest, params).with_confidentiality_access_check } - before do - project.add_guest(guest) + project.add_guest(user) end - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' end context 'for a project member with access to view confidential issues' do - subject { described_class.new(authorized_user, params).with_confidentiality_access_check } - - it_behaves_like 'returns public and confidential, does not return hidden' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public, confidential, and hidden' + before do + project.add_reporter(user) end + + it_behaves_like 'returns public and confidential' end context 'for an admin' do - let(:admin_user) { create(:user, :admin) } - - subject { described_class.new(admin_user, params).with_confidentiality_access_check } + let(:user) { create(:user, :admin) } context 'when admin mode is enabled', :enable_admin_mode do - it_behaves_like 'returns public, confidential, and hidden' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public, confidential, and hidden' - end + it_behaves_like 'returns public and confidential' end context 'when admin mode is disabled' do - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' end end end @@ -1135,17 +1122,9 @@ RSpec.describe IssuesFinder do let(:params) { { project_id: project.id } } context 'for an anonymous user' do - subject { described_class.new(nil, params).with_confidentiality_access_check } - - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end + let(:user) { nil } - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' it 'does not filter by confidentiality' do expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything) @@ -1154,17 +1133,7 @@ RSpec.describe IssuesFinder do end context 'for a user without project membership' do - subject { described_class.new(user, params).with_confidentiality_access_check } - - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' it 'filters by confidentiality' do expect(subject.to_sql).to match("issues.confidential") @@ -1172,21 +1141,11 @@ RSpec.describe IssuesFinder do end context 'for a guest user' do - subject { described_class.new(guest, params).with_confidentiality_access_check } - before do - project.add_guest(guest) + project.add_guest(user) end - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end + it_behaves_like 'returns public, does not return confidential' it 'filters by confidentiality' do expect(subject.to_sql).to match("issues.confidential") @@ -1194,40 +1153,18 @@ RSpec.describe IssuesFinder do end context 'for a project member with access to view confidential issues' do - subject { described_class.new(authorized_user, params).with_confidentiality_access_check } - - it_behaves_like 'returns public and confidential, does not return hidden' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public, confidential, and hidden' + before do + project.add_reporter(user) end - it 'does not filter by confidentiality' do - expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything) - - subject - end + it_behaves_like 'returns public and confidential' end context 'for an admin' do - let(:admin_user) { create(:user, :admin) } - - subject { described_class.new(admin_user, params).with_confidentiality_access_check } + let(:user) { create(:user, :admin) } context 'when admin mode is enabled', :enable_admin_mode do - it_behaves_like 'returns public, confidential, and hidden' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public, confidential, and hidden' - end + it_behaves_like 'returns public and confidential' it 'does not filter by confidentiality' do expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything) @@ -1237,19 +1174,7 @@ RSpec.describe IssuesFinder do end context 'when admin mode is disabled' do - it_behaves_like 'returns public, does not return hidden or confidential' - - context 'when feature flag is disabled' do - before do - stub_feature_flags(ban_user_feature_flag: false) - end - - it_behaves_like 'returns public and hidden, does not return confidential' - end - - it 'filters by confidentiality' do - expect(subject.to_sql).to match("issues.confidential") - end + it_behaves_like 'returns public, does not return confidential' end end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 49b29cefb9b..42197a6b103 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -227,56 +227,38 @@ RSpec.describe MergeRequestsFinder do end end - shared_examples ':label_name parameter' do - describe ':label_name parameter' do - let(:common_labels) { create_list(:label, 3) } - let(:distinct_labels) { create_list(:label, 3) } - let(:merge_requests) do - common_attrs = { - source_project: project1, target_project: project1, author: user - } - distinct_labels.map do |label| - labels = [label, *common_labels] - create(:labeled_merge_request, :closed, labels: labels, **common_attrs) - end - end - - def find(label_name) - described_class.new(user, label_name: label_name).execute - end - - it 'accepts a single label' do - found = find(distinct_labels.first.title) - common = find(common_labels.first.title) - - expect(found).to contain_exactly(merge_requests.first) - expect(common).to match_array(merge_requests) - end - - it 'accepts an array of labels, all of which must match' do - all_distinct = find(distinct_labels.pluck(:title)) - all_common = find(common_labels.pluck(:title)) - - expect(all_distinct).to be_empty - expect(all_common).to match_array(merge_requests) + describe ':label_name parameter' do + let(:common_labels) { create_list(:label, 3) } + let(:distinct_labels) { create_list(:label, 3) } + let(:merge_requests) do + common_attrs = { + source_project: project1, target_project: project1, author: user + } + distinct_labels.map do |label| + labels = [label, *common_labels] + create(:labeled_merge_request, :closed, labels: labels, **common_attrs) end end - end - context 'when `optimized_issuable_label_filter` feature flag is off' do - before do - stub_feature_flags(optimized_issuable_label_filter: false) + def find(label_name) + described_class.new(user, label_name: label_name).execute end - it_behaves_like ':label_name parameter' - end + it 'accepts a single label' do + found = find(distinct_labels.first.title) + common = find(common_labels.first.title) - context 'when `optimized_issuable_label_filter` feature flag is on' do - before do - stub_feature_flags(optimized_issuable_label_filter: true) + expect(found).to contain_exactly(merge_requests.first) + expect(common).to match_array(merge_requests) end - it_behaves_like ':label_name parameter' + it 'accepts an array of labels, all of which must match' do + all_distinct = find(distinct_labels.pluck(:title)) + all_common = find(common_labels.pluck(:title)) + + expect(all_distinct).to be_empty + expect(all_common).to match_array(merge_requests) + end end it 'filters by source project id' do @@ -729,6 +711,36 @@ RSpec.describe MergeRequestsFinder do merge_requests = described_class.new(user, params).execute expect { merge_requests.load }.not_to raise_error end + + context 'filtering by search text' do + let!(:merge_request6) { create(:merge_request, source_project: project1, target_project: project1, source_branch: 'tanuki-branch', title: 'tanuki') } + + let(:params) { { project_id: project1.id, search: 'tanuki' } } + + context 'with anonymous user' do + let(:merge_requests) { described_class.new(nil, params).execute } + + context 'with disable_anonymous_search feature flag enabled' do + before do + stub_feature_flags(disable_anonymous_search: true) + end + + it 'does not perform search' do + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request6) + end + end + + context 'with disable_anonymous_search feature flag disabled' do + before do + stub_feature_flags(disable_anonymous_search: false) + end + + it 'returns matching merge requests' do + expect(merge_requests).to contain_exactly(merge_request6) + end + end + end + end end describe '#row_count', :request_store do diff --git a/spec/finders/packages/helm/package_files_finder_spec.rb b/spec/finders/packages/helm/package_files_finder_spec.rb index 2b84fd2b2d2..5f1378f837d 100644 --- a/spec/finders/packages/helm/package_files_finder_spec.rb +++ b/spec/finders/packages/helm/package_files_finder_spec.rb @@ -6,42 +6,51 @@ RSpec.describe ::Packages::Helm::PackageFilesFinder do let_it_be(:project1) { create(:project) } let_it_be(:project2) { create(:project) } let_it_be(:helm_package) { create(:helm_package, project: project1) } - let_it_be(:helm_package_file) { helm_package.package_files.first } + let_it_be(:helm_package_file1) { helm_package.package_files.first } + let_it_be(:helm_package_file2) { create(:helm_package_file, package: helm_package) } let_it_be(:debian_package) { create(:debian_package, project: project1) } - describe '#execute' do - let(:project) { project1 } - let(:channel) { 'stable' } - let(:params) { {} } + let(:project) { project1 } + let(:channel) { 'stable' } + let(:params) { {} } + + let(:service) { described_class.new(project, channel, params) } - subject { described_class.new(project, channel, params).execute } + describe '#execute' do + subject { service.execute } context 'with empty params' do - it { is_expected.to match_array([helm_package_file]) } + it { is_expected.to eq([helm_package_file2, helm_package_file1]) } end context 'with another project' do let(:project) { project2 } - it { is_expected.to match_array([]) } + it { is_expected.to eq([]) } end context 'with another channel' do let(:channel) { 'staging' } - it { is_expected.to match_array([]) } + it { is_expected.to eq([]) } end - context 'with file_name' do - let(:params) { { file_name: helm_package_file.file_name } } + context 'with matching file_name' do + let(:params) { { file_name: helm_package_file1.file_name } } - it { is_expected.to match_array([helm_package_file]) } + it { is_expected.to eq([helm_package_file2, helm_package_file1]) } end context 'with another file_name' do let(:params) { { file_name: 'foobar.tgz' } } - it { is_expected.to match_array([]) } + it { is_expected.to eq([]) } end end + + describe '#most_recent!' do + subject { service.most_recent! } + + it { is_expected.to eq(helm_package_file2) } + end end diff --git a/spec/finders/packages/helm/packages_finder_spec.rb b/spec/finders/packages/helm/packages_finder_spec.rb new file mode 100644 index 00000000000..5037a9e6205 --- /dev/null +++ b/spec/finders/packages/helm/packages_finder_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::Helm::PackagesFinder do + let_it_be(:project1) { create(:project) } + let_it_be(:project2) { create(:project) } + let_it_be(:helm_package) { create(:helm_package, project: project1) } + let_it_be(:npm_package) { create(:npm_package, project: project1) } + let_it_be(:npm_package) { create(:npm_package, project: project2) } + + let(:project) { project1 } + let(:channel) { 'stable' } + let(:finder) { described_class.new(project, channel) } + + describe '#execute' do + subject { finder.execute } + + context 'with project' do + context 'with channel' do + it { is_expected.to eq([helm_package]) } + + context 'ignores duplicate package files' do + let_it_be(:package_file1) { create(:helm_package_file, package: helm_package) } + let_it_be(:package_file2) { create(:helm_package_file, package: helm_package) } + + it { is_expected.to eq([helm_package]) } + + context 'let clients use select id' do + subject { finder.execute.pluck_primary_key } + + it { is_expected.to eq([helm_package.id]) } + end + end + end + + context 'with not existing channel' do + let(:channel) { 'alpha' } + + it { is_expected.to be_empty } + end + + context 'with no channel' do + let(:channel) { nil } + + it { is_expected.to be_empty } + end + + context 'with no helm packages' do + let(:project) { project2 } + + it { is_expected.to be_empty } + end + end + + context 'with no project' do + let(:project) { nil } + + it { is_expected.to be_empty } + end + + context 'when the limit is hit' do + let_it_be(:helm_package2) { create(:helm_package, project: project1) } + let_it_be(:helm_package3) { create(:helm_package, project: project1) } + let_it_be(:helm_package4) { create(:helm_package, project: project1) } + + before do + stub_const("#{described_class}::MAX_PACKAGES_COUNT", 2) + end + + it { is_expected.to eq([helm_package4, helm_package3]) } + end + end +end diff --git a/spec/finders/packages/npm/package_finder_spec.rb b/spec/finders/packages/npm/package_finder_spec.rb index a995f3b96c4..230d267e508 100644 --- a/spec/finders/packages/npm/package_finder_spec.rb +++ b/spec/finders/packages/npm/package_finder_spec.rb @@ -7,6 +7,7 @@ RSpec.describe ::Packages::Npm::PackageFinder do let(:project) { package.project } let(:package_name) { package.name } + let(:last_of_each_version) { true } shared_examples 'accepting a namespace for' do |example_name| before do @@ -38,6 +39,8 @@ RSpec.describe ::Packages::Npm::PackageFinder do end describe '#execute' do + subject { finder.execute } + shared_examples 'finding packages by name' do it { is_expected.to eq([package]) } @@ -56,13 +59,27 @@ RSpec.describe ::Packages::Npm::PackageFinder do end end - subject { finder.execute } + shared_examples 'handling last_of_each_version' do + include_context 'last_of_each_version setup context' + + context 'disabled' do + let(:last_of_each_version) { false } + + it { is_expected.to contain_exactly(package1, package2) } + end + + context 'enabled' do + it { is_expected.to contain_exactly(package2) } + end + end context 'with a project' do - let(:finder) { described_class.new(package_name, project: project) } + let(:finder) { described_class.new(package_name, project: project, last_of_each_version: last_of_each_version) } it_behaves_like 'finding packages by name' + it_behaves_like 'handling last_of_each_version' + context 'set to nil' do let(:project) { nil } @@ -71,10 +88,12 @@ RSpec.describe ::Packages::Npm::PackageFinder do end context 'with a namespace' do - let(:finder) { described_class.new(package_name, namespace: namespace) } + let(:finder) { described_class.new(package_name, namespace: namespace, last_of_each_version: last_of_each_version) } it_behaves_like 'accepting a namespace for', 'finding packages by name' + it_behaves_like 'accepting a namespace for', 'handling last_of_each_version' + context 'set to nil' do let_it_be(:namespace) { nil } @@ -98,16 +117,28 @@ RSpec.describe ::Packages::Npm::PackageFinder do end end + shared_examples 'handling last_of_each_version' do + include_context 'last_of_each_version setup context' + + context 'enabled' do + it { is_expected.to eq(package2) } + end + end + context 'with a project' do - let(:finder) { described_class.new(package_name, project: project) } + let(:finder) { described_class.new(package_name, project: project, last_of_each_version: last_of_each_version) } it_behaves_like 'finding packages by version' + + it_behaves_like 'handling last_of_each_version' end context 'with a namespace' do - let(:finder) { described_class.new(package_name, namespace: namespace) } + let(:finder) { described_class.new(package_name, namespace: namespace, last_of_each_version: last_of_each_version) } it_behaves_like 'accepting a namespace for', 'finding packages by version' + + it_behaves_like 'accepting a namespace for', 'handling last_of_each_version' end end @@ -118,10 +149,26 @@ RSpec.describe ::Packages::Npm::PackageFinder do it { is_expected.to eq(package) } end + shared_examples 'handling last_of_each_version' do + include_context 'last_of_each_version setup context' + + context 'disabled' do + let(:last_of_each_version) { false } + + it { is_expected.to eq(package2) } + end + + context 'enabled' do + it { is_expected.to eq(package2) } + end + end + context 'with a project' do - let(:finder) { described_class.new(package_name, project: project) } + let(:finder) { described_class.new(package_name, project: project, last_of_each_version: last_of_each_version) } it_behaves_like 'finding package by last' + + it_behaves_like 'handling last_of_each_version' end context 'with a namespace' do @@ -129,6 +176,8 @@ RSpec.describe ::Packages::Npm::PackageFinder do it_behaves_like 'accepting a namespace for', 'finding package by last' + it_behaves_like 'accepting a namespace for', 'handling last_of_each_version' + context 'with duplicate packages' do let_it_be(:namespace) { create(:group) } let_it_be(:subgroup1) { create(:group, parent: namespace) } diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index 21b5b2f6130..d26180bbf94 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -135,6 +135,7 @@ RSpec.describe ProjectsFinder do describe 'filter by tags (deprecated)' do before do + public_project.reload public_project.topic_list = 'foo' public_project.save! end @@ -146,6 +147,7 @@ RSpec.describe ProjectsFinder do describe 'filter by topics' do before do + public_project.reload public_project.topic_list = 'foo, bar' public_project.save! end @@ -188,6 +190,32 @@ RSpec.describe ProjectsFinder do it { is_expected.to eq([public_project]) } end + context 'with anonymous user' do + let(:public_project_2) { create(:project, :public, group: group, name: 'E', path: 'E') } + let(:current_user) { nil } + let(:params) { { search: 'C' } } + + context 'with disable_anonymous_project_search feature flag enabled' do + before do + stub_feature_flags(disable_anonymous_project_search: true) + end + + it 'does not perform search' do + is_expected.to eq([public_project_2, public_project]) + end + end + + context 'with disable_anonymous_project_search feature flag disabled' do + before do + stub_feature_flags(disable_anonymous_project_search: false) + end + + it 'finds one public project' do + is_expected.to eq([public_project]) + end + end + end + describe 'filter by name for backward compatibility' do let(:params) { { name: 'C' } } diff --git a/spec/finders/repositories/tree_finder_spec.rb b/spec/finders/repositories/tree_finder_spec.rb new file mode 100644 index 00000000000..0d70d5f92d3 --- /dev/null +++ b/spec/finders/repositories/tree_finder_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Repositories::TreeFinder do + include RepoHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, creator: user) } + + let(:repository) { project.repository } + let(:tree_finder) { described_class.new(project, params) } + let(:params) { {} } + let(:first_page_ids) { tree_finder.execute.map(&:id) } + let(:second_page_token) { first_page_ids.last } + + describe "#execute" do + subject { tree_finder.execute(gitaly_pagination: true) } + + it "returns an array" do + is_expected.to be_an(Array) + end + + it "includes 20 items by default" do + expect(subject.size).to eq(20) + end + + it "accepts a gitaly_pagination argument" do + expect(repository).to receive(:tree).with(anything, anything, recursive: nil, pagination_params: { limit: 20, page_token: nil }).and_call_original + expect(tree_finder.execute(gitaly_pagination: true)).to be_an(Array) + + expect(repository).to receive(:tree).with(anything, anything, recursive: nil).and_call_original + expect(tree_finder.execute(gitaly_pagination: false)).to be_an(Array) + end + + context "commit doesn't exist" do + let(:params) do + { ref: "nonesuchref" } + end + + it "raises an error" do + expect { subject }.to raise_error(described_class::CommitMissingError) + end + end + + describe "pagination_params" do + let(:params) do + { per_page: 5, page_token: nil } + end + + it "has the per_page number of items" do + expect(subject.size).to eq(5) + end + + it "doesn't include any of the first page records" do + first_page_ids = subject.map(&:id) + second_page = described_class.new(project, { per_page: 5, page_token: first_page_ids.last }).execute(gitaly_pagination: true) + + expect(second_page.map(&:id)).not_to include(*first_page_ids) + end + end + end + + describe "#total", :use_clean_rails_memory_store_caching do + subject { tree_finder.total } + + it { is_expected.to be_an(Integer) } + + it "only calculates the total once" do + expect(repository).to receive(:tree).once.and_call_original + + 2.times { tree_finder.total } + end + end + + describe "#commit_exists?" do + subject { tree_finder.commit_exists? } + + context "ref exists" do + let(:params) do + { ref: project.default_branch } + end + + it { is_expected.to be(true) } + end + + context "ref is missing" do + let(:params) do + { ref: "nonesuchref" } + end + + it { is_expected.to be(false) } + end + end +end |